Hello World

docker 는 “hello world” 를 제공하고 있다. 대부분 컴퓨팅 언어를 하면서 처음 접하게 되는 단순한 프로그램인데 “Hello World” 를 프린팅하는 예제이다.

docker run hello-world 명령어를 입력하는 것으로 실행된다.

$ docker run hello-world

Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
1b930d010525: Pull complete
Digest: sha256:9572f7cdcee8591948c2963463447a53466950b3fc15a247fcad1917ca215a2f
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/

Docker Image and Registry

위의 예를 설명하면 다음과 같다.

  • Unable to find image 'hello-world:latest' locally: 로컬 저장소에서 hello-world 이미지를 찾을 수 없음
  • latest: Pulling from library/hello-world: latest 버전을 다운로드 하고있음
  • 1b930d010525: Pull complete: 다운로드가 완료됨
  • Digest: sha256:9572f7cdcee8591948c2963463447a53466950b3fc15a247fcad1917ca215a2f: 다운로드 콘텐츠 체크
  • Status: Downloaded newer image for hello-world:latest: 다운로드가 완료되었음

로컬 저장소에 원하는 이미지가 없는 경우 외부에서 해당 이미지를 찾고 다운로드 하는 것을 볼 수 있다.

docker 는 docker HUB 라고 하는 registry 를 통하여 이미지를 로컬에 다운로드 한 후 이것을 활용하여 동작한다.

DOCKER REGISTRY

docker image 를 저장하는 중앙저장소 객체는 versioning, tagging 됨

위의 예를 계속 설명하면

  1. docker client 가 docker daemon 에 접속함
  2. docker daemon 은 hello world 이미지를 Docker Hub 를 통해 받음
  3. docker daemon 은 받은 이미지를 사용하여 컨테이너를 만들고 컨테이너에서 실행된 실행파일이 메시지 출력을 함
  4. docker daemon 은 출력을 docker client 에게 전달하였고 이것을 당신의 터미널로 보냄

Docker Ubuntu Image and Registry

  • docker run -it ubuntu bash 명령어로 실행
$ docker run -it ubuntu bash

Unable to find image 'ubuntu:latest' locally
latest: Pulling from library/ubuntu

5c939e3a4d10: Pulling fs layer
c63719cdbe7a: Downloading 10.17MB/26.69MB
19a861ea6baf: Pull complete
651c9d2d6c4f: Waiting
...
5c939e3a4d10: Extracting [=================================================> ]  26.25MB/26.69M
c63719cdbe7a: Extracting [=======================================>           ]  16.25MB/26.69M
19a861ea6baf: Pull complete
651c9d2d6c4f: Pull complete
...
5c939e3a4d10: Pull complete
c63719cdbe7a: Pull complete
19a861ea6baf: Pull complete
651c9d2d6c4f: Pull complete
Digest: sha256:8d31dad0c58f552e890d68bbfb735588b6b820a46e459672d96e585871acc110
Status: Downloaded newer image for ubuntu:latest
root@b7da2785780e:/#

ubuntu 컨테이너를 실행하는 위 예를 단계별로 설명하면 다음과 같다.

  • Pulling fs layer
  • Downloading
  • Extraction

1. Layered Image

큰 이미지인 ubuntu 를 다운로드 하는데 4 단계로 구성되어 있다. Pulling fs layer 라는 의미에서 알 수 있듯 docker 가 사용하는 disk image 는 계층적으로 구성되어 있다는 의미이다.

쉽게 말하자면 ubuntu 버전이 업데이트 됨에 따라 각 버전들이 레이어로 구성되어 있는 것이며 각 레이어는 업데이트 내용을 포함하고 있다는 것이다.

Docker Image

Docker Image 는 단계적으로 Layered Image

2. Execution

위의 예에서 docker run -it ubuntu bash 를 실행하면

$ docker run -it ubuntu bash
root@b7da2785780e:/#

와 같이 다른 출력이 없이 ubuntu 컨테이너의 root 프롬프트가 실행된 것을 확인할 수 있다.

이와 같은 것을 대화형 모드라고 하는데 이것은 -i, -t 옵션을 사용한 결과이다.

...
-i, --interactive                    Keep STDIN open even if not attached
-t, --tty                            Allocate a pseudo-TTY
...

기본적으로 컨테이너는 해당하는 명령을 수행하고 빠져온다. (이는 hello-world 예에서 확인할 수 있다.)

아래는 ubuntu 컨테이너를 실행하고 디렉토리 구조를 확인하는 명령을 실행시킨 예를 보여준다. (ls 명령을 실행하여 디렉토리 구조를 확인할 수 있다.)

$ docker run -it ubuntu ls -l

total 64
drwxr-xr-x   2 root root 4096 Jan 12 21:10 bin
drwxr-xr-x   2 root root 4096 Apr 24  2018 boot
drwxr-xr-x   5 root root  360 Feb 17 11:21 dev
drwxr-xr-x   1 root root 4096 Feb 17 11:21 etc
drwxr-xr-x   2 root root 4096 Apr 24  2018 home
drwxr-xr-x   8 root root 4096 May 23  2017 lib
drwxr-xr-x   2 root root 4096 Jan 12 21:10 lib64
drwxr-xr-x   2 root root 4096 Jan 12 21:09 media
drwxr-xr-x   2 root root 4096 Jan 12 21:09 mnt
drwxr-xr-x   2 root root 4096 Jan 12 21:09 opt
dr-xr-xr-x 109 root root    0 Feb 17 11:21 proc
drwx------   2 root root 4096 Jan 12 21:10 root
drwxr-xr-x   1 root root 4096 Jan 16 01:20 run
drwxr-xr-x   1 root root 4096 Jan 16 01:20 sbin
drwxr-xr-x   2 root root 4096 Jan 12 21:09 srv
dr-xr-xr-x  13 root root    0 Feb 17 11:21 sys
drwxrwxrwt   2 root root 4096 Jan 12 21:10 tmp
drwxr-xr-x   1 root root 4096 Jan 12 21:09 usr
drwxr-xr-x   1 root root 4096 Jan 12 21:10 var
$

컨테이너가 실행되고 명령을 수행한 뒤 컨테이너가 종료됨을 확인할 수 있다.

3. Namespace

컨테이너는 Namespace 를 활용하여 격리한다고 한다.

$ docker run -it ubuntu bash

root@810699d1fe17:/# ls
bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
root@810699d1fe17:/# pwd
/
root@810699d1fe17:/# du -h --max-depth=1 / 2> /dev/null | sort -hr
70M	/
43M	/usr
13M	/lib
5.3M	/var
4.8M	/bin
3.8M	/sbin
620K	/etc
20K	/run
12K	/root
4.0K	/tmp
4.0K	/srv
4.0K	/opt
4.0K	/mnt
4.0K	/media
4.0K	/lib64
4.0K	/home
4.0K	/boot
0	/sys
0	/proc
0	/dev
root@810699d1fe17:/#

디렉토리 구조, 루트 사용자, 디스크 사용량이 70M 임을 확인할 수 있다.

root@810699d1fe17:/# ps axu
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.1  18508  3280 pts/0    Ss   00:46   0:00 bash
root        13  0.0  0.1  34400  2728 pts/0    R+   00:50   0:00 ps axu
root@810699d1fe17:/# w
 00:50:52 up 5 min,  0 users,  load average: 0.00, 0.02, 0.00
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
root@810699d1fe17:/# ls /var/log
alternatives.log  apt  bootstrap.log  btmp  dpkg.log  faillog  lastlog  tallylog  wtmp
root@810699d1fe17:/# lastlog
Username         Port     From             Latest
root                                       **Never logged in**
daemon                                     **Never logged in**
bin                                        **Never logged in**
sys                                        **Never logged in**
sync                                       **Never logged in**
games                                      **Never logged in**
man                                        **Never logged in**
lp                                         **Never logged in**
mail                                       **Never logged in**
news                                       **Never logged in**
uucp                                       **Never logged in**
proxy                                      **Never logged in**
www-data                                   **Never logged in**
backup                                     **Never logged in**
list                                       **Never logged in**
irc                                        **Never logged in**
gnats                                      **Never logged in**
nobody                                     **Never logged in**
_apt                                       **Never logged in**
root@810699d1fe17:/#

컨테이너를 실행하면서 수행한 프로세스가 하나이기 때문에 사용자, 시스템 사용 흔적이 없음을 확인할 수 있다.

격리 상태를 확인하기 위해서는 호스트에서 확인하는 것이 가장 확실하다.

bash 를 실행중인 ubuntu 컨테이너가 호스트에서는 어떻게 진행되는지 살펴보기 위해 다음을 확인한다.

$ ps axfww
...
  961 ?        Ssl    0:00 /usr/bin/containerd
 2045 ?        Sl     0:00  \_ containerd-shim -namespace moby -workdir /var/lib/containerd/io.containerd.runtime.v1.linux/moby/810699d1fe173ef0341a8325f81696b3da935d5aefd43454ca573b07b1235d2e -address /run/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runc
 2071 pts/0    Ss     0:00      \_ bash
...

컨테이너에 bash 가 실행되는 것을 확인할 수 있다.

$ mount | grep docker
overlay on /var/lib/docker/overlay2/71a98749345978590390cab34127fe227316292e7f510ab8e8e3e000cfede33e/merged type overlay (rw,relatime,lowerdir=/var/lib/docker/overlay2/l/SCOELRQA7BW5JOXNR4IDAUNX2U:/var/lib/docker/overlay2/l/PAIOSR5EXBVFXYJIZ2BISHONKO:/var/lib/docker/overlay2/l/OLRJYWEFJXHHANEN274OLS34DQ:/var/lib/docker/overlay2/l/4NUC7DHCQWFW3LIGNAWKWTIHBF:/var/lib/docker/overlay2/l/VAIYUGOZSB3VHLXKXDWED6ZTGZ,upperdir=/var/lib/docker/overlay2/71a98749345978590390cab34127fe227316292e7f510ab8e8e3e000cfede33e/diff,workdir=/var/lib/docker/overlay2/71a98749345978590390cab34127fe227316292e7f510ab8e8e3e000cfede33e/work)
nsfs on /run/docker/netns/871995a295a4 type nsfs (rw)

위의 명령어로 확인할 수 있는 것은 첫 구문에서 overlay 로 마운트 된 파일 시스템이 컨테이너와 연관이 있음을 알 수 있다.

또한 첫 구문의 .../merged 에서 layered image 가 병합된 것으로 유추할 수 있다. (뒷 구문의 : 이 각 레이어 연결을 위한 것임을 유추할 수 있다.)

$ sudo ls /var/lib/docker/overlay2/71a98749345978590390cab34127fe227316292e7f510ab8e8e3e000cfede33e/merged
bin   dev  home  lib64	mnt  proc  run	 srv  tmp  var
boot  etc  lib	 media	opt  root  sbin  sys  usr

호스트에서 마운트된 컨테이너를 위와 같이 확인하면 컨테이너에서 확인한 ls 의 모습과 같다.

ubuntu 컨테이너에 파일을 하나 만들고

root@810699d1fe17:/# touch testfile
root@810699d1fe17:/# ls
bin   dev  home  lib64  mnt  proc  run   srv  _testfile_  usr
boot  etc  lib   media  opt  root  sbin  sys  tmp       var
root@810699d1fe17:/#

호스트에서 확인하면

$ sudo ls /var/lib/docker/overlay2/71a98749345978590390cab34127fe227316292e7f510ab8e8e3e000cfede33e/merged
bin   dev  home  lib64	mnt  proc  run	 srv  _testfile_	usr
boot  etc  lib	 media	opt  root  sbin  sys  tmp	var

반대로 호스트에서 생성된 파일을 삭제한 후 컨테이너에 반영이 되었는지 확인해보면 양쪽이 연관이 있음을 알 수 있다.

$ sudo rm /var/lib/docker/overlay2/71a98749345978590390cab34127fe227316292e7f510ab8e8e3e000cfede33e/merged/testfile
$ sudo ls /var/lib/docker/overlay2/71a98749345978590390cab34127fe227316292e7f510ab8e8e3e000cfede33e/merged
bin   dev  home  lib64	mnt  proc  run	 srv  tmp  var
boot  etc  lib	 media	opt  root  sbin  sys  usr

...

root@810699d1fe17:/# ls
bin   dev  home  lib64  mnt  proc  run   srv  tmp  var
boot  etc  lib   media  opt  root  sbin  sys  usr

컨테이너에서 생성한 testfile 을 호스트에서 어떻게 바인드 되어 있는지 확인할 수 있다.

이를 통해 호스트가 컨테이너의 OS 역할을 수행하는 것을 알 수 있다. 즉, VM 에서 이야기 하는 Disk OS 는 호스트에 의해 처리되는 것이다.