• Home
  • About
    • lahuman photo

      lahuman

      열심히 사는 아저씨

    • Learn More
    • Facebook
    • LinkedIn
    • Github
  • Posts
    • All Posts
    • All Tags
  • Projects

#1 컨테이너 격리

13 Jan 2022

Reading time ~17 minutes

컨테이너 격리 & 도커 네트워크

Kubernetes Advanced Networking Study(이하 KANG) 첫번째 시간에 내용을 정리 하였습니다.

도커(Docker)란?

도커(Docker)는 가상실행 환경을 제공해주는 오픈소스 플랫폼입니다. 도커에서는 이 가상실행 환경을 컨테이너(Container)라고 부릅니다.

  • 정확히 표현하는 용어는 ‘컨테이너화된 프로세스(Containerized Process)’
  • 도커 플랫폼이 설치된 곳이라면 컨테이너로 묶인 애플리케이션을 어디서든 실행할 수 있음

[https://kubernetes.io/docs/concepts/overview/what-is-kubernetes/](https://kubernetes.io/docs/concepts/overview/what-is-kubernetes/)

컨테이너 VS Virtual Machine(VM)

hypervisor를 이용한 Virtual Machine 과 프로세스를 이용한 컨테이너의 차이를 비교 합니다.

출처 : KANG

컨테이너는 운영체제를 제외한 나머지 애플리케이션 실행에 필요한 모든 파일을 패키징한다는 점에서 OS레벨 가상화를 지원하는 1개 이상의 프로세스 세트입니다 컨테이너는 프로세스가 지정된 리소스 요청에만 액세스할 수 있도록 허용합니다. 이러한 리소스 제한은 컨테이너가 용량이 충분한 노드에서 구동될 수 있도록 합니다.

  • 컨테이너는 호스트의 커널을 공유하지만, 개별적인 ‘사용자 공간(user space)’ 를 가집니다.
  • 가상화된 공간을 생성하기 위해 리눅스 기능 pivot-root, 네임스페이스(namespace), cgroup 를 사용함으로써 프로세스 단위의 격리 환경과 리소스을 제공합니다.

출처 : KANG

VM은 자체 운영 체제(Operating System, OS)를 포함하고 있어 여러 리소스 집약적인 기능을 한 번에 수행할 수 있습니다. VM에서 사용할 수 있는 리소스가 늘어남에 따라 전체 서버, OS, 데스크톱, 데이터베이스, 네트워크를 추상화, 분할, 복제, 에뮬레이션할 수 있습니다.

  • 개별 VM은 독립된 OS를 사용하여 컨테이너 방식에 비해 고립성(보안)은 더 좋지만, 오버헤드가 크고 무겁고 느리다는 단점을 가짐

컨테이너와 VM은 유사하다고 볼 수 있습니다. 중요한 차이점은 확장 방식과 이식성입니다. 컨테이너는 게스트OS 와 하이버파이저가 없기 때문에 이로 인한 오버헤드를 줄임으로써 훨씬 더 가볍게 프로세스를 실행할 수 있고 컨테이너에 대한 복제와 배포가 더 용이합니다.

docker 아키텍처

[docker-architecture](https://docs.docker.com/get-started/overview/#docker-architecture)

#1 vagrant로 docker 사용해보기

작업을 시작하려고 하는 빈 디렉토리에서 아래 파일을 생성합니다.

  • Vagrantfile 파일
Vagrant.configure("2") do |config|
  config.vm.boot_timeout = 1800
  config.vm.box = "ubuntu/focal64"
  config.vm.box_version = "20211026.0.0"
  config.vm.provision "shell", inline: "echo 'sudo su -' >> .bashrc"
  config.vm.provision "shell", inline: "apt update"
  config.vm.provision "shell", inline: "apt install -y conntrack bridge-utils net-tools resolvconf jq tree"
  config.vm.provision "shell", inline: "echo 'nameserver 8.8.8.8' > /etc/resolvconf/resolv.conf.d/head"
  config.vm.provision "shell", inline: "resolvconf -u"
  config.vm.network "private_network", ip: "192.168.50.10"
  config.vm.network "forwarded_port", guest: 22, host: 60510, auto_correct: true, id: "ssh"
  config.vm.synced_folder "./", "/vagrant", disabled: true
  config.vm.provider "virtualbox" do |spec|
    spec.memory = "2048"
    spec.cpus = "2"
    spec.linked_clone = true
    spec.customize [ "modifyvm", :id, "--uartmode1", "disconnected" ]
  end
end

준비가 되었으면, vagrant 명령어로 VM을 실행합니다.

# vagrant 프로비저닝
$ vagrant up

# Ubuntu SSH 접속
$ vagrant ssh

-----------
# Ubuntu Shell 에서 Ubuntu 종료
$ poweroff
-----------

# Ubuntu 재시작 후 Ubuntu SSH 접속
$ vagrant reload
$ vagrant ssh

프로비저닝(provisioning)은 사용자의 요구에 맞게 시스템 자원을 할당, 배치, 배포해 두었다가 필요 시 시스템을 즉시 사용할 수 있는 상태로 미리 준비해 두는 것을 말한다. 서버 자원 프로비저닝, OS 프로비저닝, 소프트웨어 프로비저닝, 스토리지 프로비저닝, 계정 프로비저닝 등이 있다. 수동으로 처리하는 ‘수동 프로비저닝’과 자동화 툴을 이용해 처리하는 ‘자동 프로비저닝’이 있다

추가 TIP

lahuman@HP:/mnt/c/Users/lahum/projects/KANS/1week/1.docker$ vagrant reload
==> default: Checking if box 'ubuntu/focal64' version '20211026.0.0' is up to date...
==> default: Clearing any previously set forwarded ports...
==> default: Clearing any previously set network interfaces...
==> default: Preparing network interfaces based on configuration...
    default: Adapter 1: nat
    default: Adapter 2: hostonly
==> default: Forwarding ports...
    default: 22 (guest) => 60510 (host) (adapter 1)
==> default: Running 'pre-boot' VM customizations...
==> default: Booting VM...
==> default: Waiting for machine to boot. This may take a few minutes...
    default: SSH address: 172.29.0.1:60510
    default: SSH username: vagrant
    default: SSH auth method: private key
Timed out while waiting for the machine to boot. This means that
Vagrant was unable to communicate with the guest machine within
the configured ("config.vm.boot_timeout" value) time period.

If you look above, you should be able to see the error(s) that
Vagrant had when attempting to connect to the machine. These errors
are usually good hints as to what may be wrong.

If you're using a custom box, make sure that networking is properly
working and you're able to connect to the machine. It is a common
problem that networking isn't setup properly in these boxes.
Verify that authentication configurations are also setup properly,
as well.

If the box appears to be booting properly, you may want to increase
the timeout ("config.vm.boot_timeout") value.

위와 같이 timout이 발생하여서 다시 기동시에 reload를 하지 말고 up으로 요청하면 VM 서버를 재기동 하지 않고 연결합니다.

# VM이 재기동 되어 오랜 시간이 걸림
lahuman@HP:/mnt/c/Users/lahum/projects/KANS/1week/1.docker$ vagrant reload
==> default: Attempting graceful shutdown of VM...
==> default: Checking if box 'ubuntu/focal64' version '20211026.0.0' is up to date...
==> default: Clearing any previously set forwarded ports...
==> default: Clearing any previously set network interfaces...
==> default: Preparing network interfaces based on configuration...
    default: Adapter 1: nat
    default: Adapter 2: hostonly
==> default: Forwarding ports...
    default: 22 (guest) => 60510 (host) (adapter 1)
==> default: Running 'pre-boot' VM customizations...
==> default: Booting VM...
==> default: Waiting for machine to boot. This may take a few minutes...
    default: SSH address: 172.29.0.1:60510
    default: SSH username: vagrant
    default: SSH auth method: private key
    default: Warning: Connection reset. Retrying...
==> default: Machine booted and ready!
==> default: Checking for guest additions in VM...
==> default: Configuring and enabling network interfaces...
==> default: Machine already provisioned. Run `vagrant provision` or use the `--provision`
==> default: flag to force provisioning. Provisioners marked to run always will still run.

# VM 기동 완료되어서 바로 시작
lahuman@HP:/mnt/c/Users/lahum/projects/KANS/1week/1.docker$ vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Checking if box 'ubuntu/focal64' version '20211026.0.0' is up to date...
==> default: Machine already provisioned. Run `vagrant provision` or use the `--provision`
==> default: flag to force provisioning. Provisioners marked to run always will still run.

Virtualbox 네트워크 구성 정보

Host PC에서 ssh 접근시

출처 : KANG

vagrant ssh 접속 시 기본적으로 호스트에 127.0.0.1(60510)를 목적지로 접속 → 이후 포트포워딩(SNAT + DNAT)을 통해서 내부에 VM로 SSH 연결됨

vm network 설정 정보

Ubuntu에서 외부 인터넷 접속시

출처 : KANG

docker 설치

다음 명령어로 docker를 설치합니다.

# 도커 설치
$ curl -fsSL https://get.docker.com | sh

# 도커 정보 확인
$ docker info
$ docker version

# 도커 서비스 상태 확인
$ systemctl status docker
# 'q' 입력으로 빠져나오기

# 모든 서비스의 상태 표시 - 링크
$ systemctl list-units --type=service

# 도커 루트 디렉터리 확인
$ tree -L 3 /var/lib/docker
# 결과
/var/lib/docker
├── buildkit
│   ├── cache.db
│   ├── containerdmeta.db
│   ├── content
│   │   └── ingest
│   ├── executor
│   ├── metadata_v2.db
│   └── snapshots.db
├── containers
├── image
│   └── overlay2
│       ├── distribution
│       ├── imagedb
│       ├── layerdb
│       └── repositories.json
├── network
│   └── files
│       └── local-kv.db
├── overlay2
│   └── l
├── plugins
│   ├── storage
│   │   └── ingest
│   └── tmp
├── runtimes
├── swarm
├── tmp
├── trust
└── volumes
    ├── backingFsBlockDev
    └── metadata.db

23 directories, 8 files

docker 설치 후 기본 정보 확인

# 프로세스 확인 - 셸변수
$ ps -ef
$ pstree -p

# 시스템에 (마운트 된) disk free 디스크 여유 공간 확인
$ df -hT

# 네트워크 정보 확인 >> docker0 네트워크 인터페이스가 추가됨, 현재는 DOWN 상태
$ ip -br -c addr
$ ip -c addr
$ ip -c link
$ ip -br -c link
$ ip -c route

# 이더넷 브리지 정보 확인
$ brctl show

# iptables 정책 확인
$ iptables -t filter -S
$ iptables -t nat -S

iptables 명령어의 결과를 자세히 보면, docker를 설치하기 전과 후가 다릅니다.

# filter 에 FORWARD 가 기존 ACCEPT 에서 DROP 로 변경됨
# filter 에 FORWARD 에 docker0 에서 docker0 혹은 외부로 전달 허용 정책이 추가됨
$ iptables -t filter -S
-P INPUT ACCEPT
-P FORWARD DROP
-P OUTPUT ACCEPT
-N DOCKER
-N DOCKER-ISOLATION-STAGE-1
-N DOCKER-ISOLATION-STAGE-2
-N DOCKER-USER
-A FORWARD -j DOCKER-USER
-A FORWARD -j DOCKER-ISOLATION-STAGE-1
-A FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -o docker0 -j DOCKER
-A FORWARD -i docker0 ! -o docker0 -j ACCEPT
-A FORWARD -i docker0 -o docker0 -j ACCEPT
-A DOCKER-ISOLATION-STAGE-1 -i docker0 ! -o docker0 -j DOCKER-ISOLATION-STAGE-2
-A DOCKER-ISOLATION-STAGE-1 -j RETURN
-A DOCKER-ISOLATION-STAGE-2 -o docker0 -j DROP
-A DOCKER-ISOLATION-STAGE-2 -j RETURN
-A DOCKER-USER -j RETURN

# nat POSTROUTING 에 172.17.0.0/16 에서 외부로 전달 시 매스커레이딩(SNAT) 정책이 추가됨
iptables -t nat -S
-P PREROUTING ACCEPT
-P INPUT ACCEPT
-P OUTPUT ACCEPT
-P POSTROUTING ACCEPT
-N DOCKER
-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
-A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
-A DOCKER -i docker0 -j RETURN

#2 컨테이너 실행 및 확인

Ubuntu 호스트 VM 에서 동작하는 컨테이너의 네트워크 정보 샘플

[생활코딩 Docker 입문수업 - 5. 네트워크](https://www.youtube.com/watch?v=SJFO2w5Q2HI)

# nginx 이미지를 컨테이너 백그라운드로 실행
# -d 는 Detached 모드로 컨테이너를 실행. 컨테이너를 백그라운드에서 동작하는 애플리케이션으로써 실행하도록 설정.
# Detached 모드인 컨테이너는 반드시 컨테이너에서 프로그램이 실행돼야 하며 프로그램이 실행되지 않으면 컨테이너는 종료됩니다.
$ docker run -d nginx
$ docker ps

# 실행중인 컨테이너의 ID만 확인
$ docker ps -q

# 컨테이너 상세 정보 확인
# docker inspect '<NAME> 혹은 <ID>'
$ docker inspect $(docker ps -q)

# 컨테이너(=Instance)의 IP 정보 확인(JSON) - 링크
# docker inspect --format='' $INSTANCE_ID
$ docker inspect -f '' $(docker ps -q)
$ ping 172.17.0.2

# 호스트 네트워크 인터페이스 정보 확인
$ ip -c a
$ brctl show

# curl 로 http 접속 테스트 - 링크 링크2
$ curl `docker inspect -f '' $(docker ps -q)` | grep -o '<title>.*</title>'
$ curl -s 172.17.0.2 | grep -o '<title>.*</title>'

# 컨테이너의 log 확인
$ docker logs -f $(docker ps -q)

# 컨테이너에 명령 실행
# nginx 컨테이너의 index.html 파일의 위치는 /usr/share/nginx/html/index.html 입니다
$ docker exec $(docker ps -q) ls /usr/share/nginx/html
$ docker exec $(docker ps -q) cat /usr/share/nginx/html/index.html

# 실행 및 종료된 컨테이너까지 전부삭제
$ docker rm -f $(docker ps -a -q)

ubuntu:16.04 이미지에 bash 쉘 실행

# 호스트 Ubuntu 버전 확인
$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 20.04.3 LTS
Release:        20.04
Codename:       focal

# ubuntu 16.04 컨테이너 실행하면서 bash 쉘 실행 → 컨테이너 내부로 진입
$ docker run --rm -it ubuntu:16.04 bash

$ cat /etc/os-release
NAME="Ubuntu"
VERSION="16.04.7 LTS (Xenial Xerus)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 16.04.7 LTS"
VERSION_ID="16.04"
HOME_URL="http://www.ubuntu.com/"
SUPPORT_URL="http://help.ubuntu.com/"
BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"
VERSION_CODENAME=xenial
UBUNTU_CODENAME=xenial

$ ps -ef
UID          PID    PPID  C STIME TTY          TIME CMD
root           1       0  0 09:56 pts/0    00:00:00 bash
root          12       1  0 09:56 pts/0    00:00:00 ps -ef

$ exit

# 컨테이너 상태 확인
docker ps

Host 리눅스 배포판과 다른 centos 이미지를 실행하며 bash 쉘 실행!

$ docker run --rm -it centos bash

$ cat /etc/redhat-release
CentOS Linux release 8.4.2105

$ exit

TIP 도커 호스트의 ‘리눅스 배포판’과 컨테이너의 ‘커널 버전’이 달라도 컨테이너가 동작하는 이유는?

  • LBS(Linux Base Standart)는 소스 코드를 컴파일한 시점에서 호환성 있는 머신 코드를 생성하도록 ISO 규격으로 표준화되어 있다.
  • 리눅스 ABI(Application Binary Interface)로 인해 리눅스 커널의 버전이 올라가도 유저 공간에서 동작하는 바이너리(머신 코드) 레벨의 호환성은 유지된다.
  • 참고 링크
    • 응용 프로그램 이진 인터페이스
    • Application binary interface
    • 리눅스 기본 규격

스터디에서 토론한 내용을 추가 합니다.

[홀스]님 : 컨테이너가 다른버전 다른 os가 동작하는게 지금까지 알아본 정보는 CPU이 기계어 명령어 셋인 ISA가 각각 아키텍쳐 마다 다르고, OS에는 이 ISA와 연동되는 ABI가 있는데 이 부분이 호환되는거라 가능한걸로 알고 있습니다. 이 ABI는 제조사에서 커널에 개발해서 넣는 경우가 많다고 하고요. 이 커널의 경우는 linux foudation에서만 개발하기에 대부분 리눅스는 호환되고요. 그리고 ABI가 레드햇이라던가에서 따로 개발해서 넣는 경우도 있습니다. 만약 라이브러리나 어플리케이션이 이러한 ABI를 사용하게 될 경우 버전에 따라 동작 안할 수도 있습니다.

[가우]님 : linux syscall r3->r0 (calling convention, asm 호환) 호환 및 아키텍쳐가 동일하다면 라이브러리에서 호환성을 가져갈수 있을것 같네요. 음 그런데 호스트 커널 버전이 낮고 도커이미지 버전이 높으면 일부 동작안하는 기능이 있을 것도 같군요

단, 호스트와 CPU 아키텍처가 다른 컨테이너 이미지는 동작 불가합니다.

# Host가 AMD CPU 일때
$ cat /proc/cpuinfo
processor       : 0
vendor_id       : AuthenticAMD
cpu family      : 23
model           : 96
model name      : AMD Ryzen 5 4500U with Radeon Graphics
stepping        : 1
microcode       : 0xffffffff
cpu MHz         : 2370.554
cache size      : 512 KB
...

# riscv64로 CPU 아키텍처로 실행하려고 하면 아래와 같이 오류가 발생합니다.
$ docker run --rm -it riscv64/ubuntu bash
Unable to find image 'riscv64/ubuntu:latest' locally
latest: Pulling from riscv64/ubuntu
8c703baf60b6: Pull complete
Digest: sha256:bc700522fdbb6d8fa43a6800954e3f12c6990df7e344649c05eafac375aa4dcf
Status: Downloaded newer image for riscv64/ubuntu:latest
WARNING: The requested image's platform (linux/riscv64) does not match the detected host platform (linux/amd64) and no specific platform was requested
standard_init_linux.go:228: exec user process caused: exec format error

#3 컨테이너 격리에 대하여

컨테이너의 시작은 1979년 chroot에서로 부터 시작했습니다.

[https://netpple.github.io/docs/make-container-without-docker/container-internal-1](https://netpple.github.io/docs/make-container-without-docker/container-internal-1)

리눅스의 파일 시스템은 root(/)로 부터 시작합니다. 따라서 특정 디렉토리를 root(/)로 지정할수 있으면 해당 경로에 프로세스를 가두는 점에서 착안되었습니다. FTP(원격 유저)를 특정 디렉토리 경로에 가두기 위한 용도로 사용됩니다.

chroot를 사용하기 위해서는 필요한 프로그램들을 하나하나 복사해서 새로운 root(/) 디렉토리 밑으로 넣어두어야 합니다.

[https://netpple.github.io/docs/make-container-without-docker/container-internal-1](https://netpple.github.io/docs/make-container-without-docker/container-internal-1)

이렇게 필요한 파일들을 미리 넣어두어서 만들어두는 것을 이미지라고 하며, 흔히 도커 이미지라고 합니다

하지만 chroot는 큰 취약점이 있습니다. 바로 탈옥이 가능하다는 것입니다. 이외에도 호스트와 격리 문제, 호스트 리소스를 제한 없이 사용이 가능하고, root 권한 및 다양한 보안 문제를 가지고 있습니다.

탈옥은 root(/) 디렉토리를 벗어나는 행위를 이야기 합니다. 이를 해결한 방법이 pivot_root와 Namespace 입니다. 또한 리소스에 대한 제한을 위해서는 Cgroup을 사용합니다. 더욱 자세한 내용은 도커 없이 컨테이너 만들기를 참조해주세요. :)

[https://wizardzines.com/comics/namespaces](https://wizardzines.com/comics/namespaces)

[https://wizardzines.com/comics/cgroups](https://wizardzines.com/comics/cgroups)

Docker를 이용할 경우 위의 문제점을 해결한 독립된 리눅스 환경을 보장받는 프로세스가 생성됩니다. 단, Host 프로세스에서 컨테이너(프로세스)를 종료할 수 있습니다. VM 환경에서는 Host에서 프로세스에 접근이 기본적으로 어렵습니다.

아래는 Host에서 컨테이너의 파일에 접근하고 삭제하는 실습입니다.

# 터미널1 (Ubuntu 컨테이너)
## Ubuntu 컨테이너 bash 접속 후 명령어 실행
docker run --rm -it ubuntu bash
## echo $$ : 현재 bash 쉘의 프로세스 ID - 링크
$ echo $$
1

$ ps -ef
UID          PID    PPID  C STIME TTY          TIME CMD
root           1       0  0 14:03 pts/0    00:00:00 bash
root          11       1  0 14:03 pts/0    00:00:00 ps -ef

$ ps afxuwww
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root           1  0.0  0.1   4108  3680 pts/0    Ss   14:03   0:00 bash
root          12  0.0  0.1   5896  2948 pts/0    R+   14:03   0:00 ps afxuwww

## 현재 프로세스의 네임스페이스 정보 : 호스트와 컨테이너의 Namespace 중 ipc, mnt, net, pid, uts 가 다르다
$ ls -l /proc/$$/ns
total 0
lrwxrwxrwx 1 root root 0 Jan 13 14:04 cgroup -> 'cgroup:[4026531835]'
lrwxrwxrwx 1 root root 0 Jan 13 14:04 ipc -> 'ipc:[4026532258]'
lrwxrwxrwx 1 root root 0 Jan 13 14:04 mnt -> 'mnt:[4026532256]'
lrwxrwxrwx 1 root root 0 Jan 13 14:04 net -> 'net:[4026532261]'
lrwxrwxrwx 1 root root 0 Jan 13 14:04 pid -> 'pid:[4026532259]'
lrwxrwxrwx 1 root root 0 Jan 13 14:04 pid_for_children -> 'pid:[4026532259]'
lrwxrwxrwx 1 root root 0 Jan 13 14:04 user -> 'user:[4026531837]'
lrwxrwxrwx 1 root root 0 Jan 13 14:04 uts -> 'uts:[4026532257]'

# 터미널2 (호스트 Shell)
$ echo $$
1424

$ ps -ef | grep bash
vagrant     1412    1411  0 08:46 pts/0    00:00:00 -bash
root        1424    1423  0 08:46 pts/0    00:00:00 -bash
root       12330    1424  0 14:00 pts/0    00:00:00 grep --color=auto bash

# 모든 프로세스 확인
$ ps afxuwww
# 전체 프로세스가 표출됨

# 네임 스페이스 확인
$ ls -l /proc/$$/ns
total 0
lrwxrwxrwx 1 root root 0 Jan 13 14:01 cgroup -> 'cgroup:[4026531835]'
lrwxrwxrwx 1 root root 0 Jan 13 14:01 ipc -> 'ipc:[4026531839]'
lrwxrwxrwx 1 root root 0 Jan 13 14:01 mnt -> 'mnt:[4026531840]'
lrwxrwxrwx 1 root root 0 Jan 13 14:01 net -> 'net:[4026531992]'
lrwxrwxrwx 1 root root 0 Jan 13 14:01 pid -> 'pid:[4026531836]'
lrwxrwxrwx 1 root root 0 Jan 13 14:01 pid_for_children -> 'pid:[4026531836]'
lrwxrwxrwx 1 root root 0 Jan 13 14:01 user -> 'user:[4026531837]'
lrwxrwxrwx 1 root root 0 Jan 13 14:01 uts -> 'uts:[4026531838]'

# UTS(호스트네임, 도메인네임)이 다르다!
## 터미널1 (Ubuntu 컨테이너)
$ hostname
$ cat /etc/hostname
a1c2d8316afb

## 터미널2 (호스트 Shell)
$ hostname
$ cat /etc/hostname
ubuntu-focal

# PID(프로세스ID)가 다르다!
## 터미널1 (Ubuntu 컨테이너)
$ ps -eaf
UID          PID    PPID  C STIME TTY          TIME CMD
root           1       0  0 14:03 pts/0    00:00:00 bash
root          16       1  0 14:05 pts/0    00:00:00 ps -eaf

$ ps afxuwww
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root           1  0.0  0.1   4108  3680 pts/0    Ss   14:03   0:00 bash
root          17  0.0  0.1   5896  3128 pts/0    R+   14:05   0:00 ps afxuwww


## 터미널2 (호스트 Shell)
$ ps -eaf
$ ps afxuwww
# 모든 프로세스 표출

# NET(네트워크 환경)이 다르다!
## 터미널1 (Ubuntu 컨테이너)
$ sed -i 's/archive.ubuntu.com/ftp.daum.net/g' /etc/apt/sources.list
$ apt update && apt -y install net-tools
# 설치 됨

$ ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.17.0.3  netmask 255.255.0.0  broadcast 172.17.255.255
        ether 02:42:ac:11:00:03  txqueuelen 0  (Ethernet)
        RX packets 7736  bytes 21137301 (21.1 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 3899  bytes 215690 (215.6 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

$ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         172.17.0.1      0.0.0.0         UG    0      0        0 eth0
172.17.0.0      0.0.0.0         255.255.0.0     U     0      0        0 eth0

## 터미널2 (호스트 Shell)
$ ifconfig
docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.17.0.1  netmask 255.255.0.0  broadcast 172.17.255.255
        inet6 fe80::42:9ff:fe51:ba05  prefixlen 64  scopeid 0x20<link>
        ether 02:42:09:51:ba:05  txqueuelen 0  (Ethernet)
        RX packets 3913  bytes 163570 (163.5 KB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 7747  bytes 21138259 (21.1 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

enp0s3: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 10.0.2.15  netmask 255.255.255.0  broadcast 10.0.2.255
        inet6 fe80::31:14ff:fe45:3203  prefixlen 64  scopeid 0x20<link>
        ether 02:31:14:45:32:03  txqueuelen 1000  (Ethernet)
        RX packets 251806  bytes 373931304 (373.9 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 52954  bytes 3428495 (3.4 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

enp0s8: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.50.10  netmask 255.255.255.0  broadcast 192.168.50.255
        inet6 fe80::a00:27ff:fe41:3ff2  prefixlen 64  scopeid 0x20<link>
        ether 08:00:27:41:3f:f2  txqueuelen 1000  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 33  bytes 2486 (2.4 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 2  bytes 124 (124.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 2  bytes 124 (124.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

veth2e41a0a: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet6 fe80::2811:75ff:feb8:4ab  prefixlen 64  scopeid 0x20<link>
        ether 2a:11:75:b8:04:ab  txqueuelen 0  (Ethernet)
        RX packets 14  bytes 2662 (2.6 KB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 40  bytes 3152 (3.1 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

$ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         10.0.2.2        0.0.0.0         UG    100    0        0 enp0s3
10.0.2.0        0.0.0.0         255.255.255.0   U     0      0        0 enp0s3
10.0.2.2        0.0.0.0         255.255.255.255 UH    100    0        0 enp0s3
172.17.0.0      0.0.0.0         255.255.0.0     U     0      0        0 docker0
192.168.50.0    0.0.0.0         255.255.255.0   U     0      0        0 enp0s8

# MNT(마운트 파일시스템 구조)가 다르다!
## 터미널1 (Ubuntu 컨테이너) 파일 생성
$ ls /
$ for i in {1..100}; do touch /deleteme.$i; done;
$ echo '1q2w3e' > /secret.txt
$ ls /

## 터미널2 (호스트 Shell)
$ tree -L 1 /
/
├── bin -> usr/bin
├── boot
├── dev
├── etc
├── home
├── lib -> usr/lib
├── lib32 -> usr/lib32
├── lib64 -> usr/lib64
├── libx32 -> usr/libx32
├── lost+found
├── media
├── mnt
├── opt
├── proc
├── root
├── run
├── sbin -> usr/sbin
├── snap
├── srv
├── sys
├── tmp
├── usr
└── var

$ findmnt -t overlay
TARGET                                                                                           SOURCE  FSTYPE OPTIONS
/var/lib/docker/overlay2/05bb70b4b6bb1c325a01ce9d9bf4c11053b2c73e136556717b1a65caa113d688/merged overlay overla rw,relat

$ ls -a $(findmnt -t overlay | grep -v TARGET | awk '{print $1}')
/var/lib/docker/overlay2/05bb70b4b6bb1c325a01ce9d9bf4c11053b2c73e136556717b1a65caa113d688/merged:
.             deleteme.17  deleteme.3   deleteme.42  deleteme.55  deleteme.68  deleteme.80  deleteme.93  media
..            deleteme.18  deleteme.30  deleteme.43  deleteme.56  deleteme.69  deleteme.81  deleteme.94  mnt
.dockerenv    deleteme.19  deleteme.31  deleteme.44  deleteme.57  deleteme.7   deleteme.82  deleteme.95  opt
bin           deleteme.2   deleteme.32  deleteme.45  deleteme.58  deleteme.70  deleteme.83  deleteme.96  proc
boot          deleteme.20  deleteme.33  deleteme.46  deleteme.59  deleteme.71  deleteme.84  deleteme.97  root
deleteme.1    deleteme.21  deleteme.34  deleteme.47  deleteme.6   deleteme.72  deleteme.85  deleteme.98  run
deleteme.10   deleteme.22  deleteme.35  deleteme.48  deleteme.60  deleteme.73  deleteme.86  deleteme.99  sbin
deleteme.100  deleteme.23  deleteme.36  deleteme.49  deleteme.61  deleteme.74  deleteme.87  dev          secret.txt
deleteme.11   deleteme.24  deleteme.37  deleteme.5   deleteme.62  deleteme.75  deleteme.88  etc          srv
deleteme.12   deleteme.25  deleteme.38  deleteme.50  deleteme.63  deleteme.76  deleteme.89  home         sys
deleteme.13   deleteme.26  deleteme.39  deleteme.51  deleteme.64  deleteme.77  deleteme.9   lib          tmp
deleteme.14   deleteme.27  deleteme.4   deleteme.52  deleteme.65  deleteme.78  deleteme.90  lib32        usr
deleteme.15   deleteme.28  deleteme.40  deleteme.53  deleteme.66  deleteme.79  deleteme.91  lib64        var
deleteme.16   deleteme.29  deleteme.41  deleteme.54  deleteme.67  deleteme.8   deleteme.92  libx32

$ echo $(findmnt -t overlay | grep -v TARGET | awk '{print $1}')
/var/lib/docker/overlay2/05bb70b4b6bb1c325a01ce9d9bf4c11053b2c73e136556717b1a65caa113d688/merged

$ cat $(findmnt -t overlay | grep -v TARGET | awk '{print $1}')/secret.txt
1q2w3e

$ rm -rf $(findmnt -t overlay | grep -v TARGET | awk '{print $1}')/deleteme.*
$ ls -a $(findmnt -t overlay | grep -v TARGET | awk '{print $1}')
.   .dockerenv  boot  etc   lib    lib64   media  opt   root  sbin        srv  tmp  var
..  bin         dev   home  lib32  libx32  mnt    proc  run   secret.txt  sys  usr

참고 자료

  • https://kubernetes.io/docs/concepts/overview/what-is-kubernetes/
  • 하이퍼바이저란?
  • docker-architecture
  • 프로비저닝
  • 응용 프로그램 이진 인터페이스
  • Application binary interface
  • 리눅스 기본 규격
  • 1편.컨테이너 인터널(1)
  • https://netpple.github.io/docs/make-container-without-docker/container-internal-1
  • https://wizardzines.com/comics/cgroups


kangvagrantdocker Share Tweet +1