도커를 사용하면 mysql이나 nginx처럼 이미 제공하는 이미지를 사용해서 소프트웨어를 쉽게 실행할 수 있지만 단지 이것만이 도커를 사용하는 아니다. 도커를 사용하는 또 다른 이유는 직접 개발한 소프트웨어를 도커 이미지로 만들어 배포하고 실행하기 위함이다. 즉 도커를 잘 활용하려면 이미지에 대한 이해가 필요하다.

이미지 이름

도커 이미지를 이용해서 컨테이너를 생성할 때 이미지 이름을 사용한다.

docker run -it --rm alpine:3.10 sh

위 코드는 alpine:3.10을 이미지 이름으로 사용했다. alpine은 리포지토리 이름이고 3.10은 태그이다. docker images 명령어를 실행하면 로컬에 존재하는 이미지 목록을 표시한다. 이미지 목록을 보면 REPOSITORY 칼럼과 TAG 칼럼을 통해서 이미지의 리포지토리와 태그 값을 확인할 수 있다.

vagrant@ubuntu-bionic:~$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
adminer             latest              09a06e7c3196        2 days ago          87.6MB
nginx               1.17.4              f949e7d76d63        4 days ago          126MB
nginx               latest              f949e7d76d63        4 days ago          126MB
mysql               5.7                 383867b75fd2        2 weeks ago         373MB
mysql               latest              b8fd9553f1f0        2 weeks ago         445MB
centos              7                   67fa590cfc1c        5 weeks ago         202MB
alpine              3.10                961769676411        5 weeks ago         5.58MB
openjdk             8u212-jdk-alpine    a3562aa0b991        4 months ago        105MB
hello-world         latest              fce289e99eb9        9 months ago        1.84kB

위 결과에서 mysql 리포지토리는 태그가 5.7인 이미지와 태그가 latest인 이미지가 존재한다. 여기에 표시된 리포지토리 명은 완전한 리포지토리 명을 짧게 표시한 것이다. 실제 리포토리명은 다음의 세 가지 요소를 가진다.

  • 리포지토리호스트/조직(계정)이름/짧은이름

alpine이나 nginx와 같이 도커가 제공하는 공식 이미지는 짧은 이름으로 이미지를 사용할 수 있다. 실제 공식 이미지의 리포지토리 이름은 다음 형식을 갖는다.

  • docker.io/library/nginx

도커 허브에 개인 계정을 만들어 리포지토리를 만들 수도 있다. 예를 들어 필자는 도커 허브에 madvirus라는 이름으로 가입했는데 이 경우 리포지토리 이름은 madvirus/openjdk-pinpoint와 같은 형태를 갖는다.

도커 허브가 아닌 사설 도커 리포지토리를 사용할 경우 호스트명을 포함한 완전한 리포지토리 이름을 사용해야 한다.

보통 태그 값으로는 버전을 사용한다. 한 이미지에 여러 태그를 붙일 수 있는데 보통 마지막 버전에 해당하는 이미지에는 latest 태그를 함께 붙인다. 이 글을 쓰는 시점에서 nginx:latest 이미지는 nginx:1.17.4 이미지와 같은 이미지이다. docker images 결과를 보면 nginx의 latest 태그와 1.17.4 태그의 이미지ID가 같은 것을 알 수 있다.

참고로 컨테이너를 생성할 때 태그를 지정하지 않으면 latest를 기본 값으로 사용한다.

이미지 레이어

기존 이미지를 이용해서 새로운 이미지를 만들어보자.

vagrant@ubuntu-bionic:~$ docker run --name alpine_custom alpine:3.10 touch /mycustom.txt

vagrant@ubuntu-bionic:~$ docker commit alpine_custom myimage
sha256:422d23665db418c26a463a3aeb4d92b43a9c51c056aae34dea26a3a4067c1f9a

위 코드는 alpine:3.10 이미지를 이용해서 alpine_custom 컨테이너를 생성한다. 컨테이너를 실행할 때 touch 명령어를 사용해서 루트에 mycustom.txt 파일을 생성한다.

docker commit 명령어는 컨테이너를 이용해서 새로운 이미지를 생성한다. 위 코드는 alpine_custom 컨테이너를 이용해서 myimage라는 이미지를 생성한다. 태그를 지정하지 않았으므로 latest를 태그로 사용한다.

이제 생성한 alpine_custom 컨테이너를 삭제하고 새로 생성한 myimage 이미지를 이용해서 새로운 컨테이너를 사용해보자.

vagrant@ubuntu-bionic:~$ docker rm -v alpine_custom
alpine_custom
vagrant@ubuntu-bionic:~$ docker run --rm myimage ls -la /
total 64
drwxr-xr-x    1 root     root          4096 Sep 29 11:28 .
drwxr-xr-x    1 root     root          4096 Sep 29 11:28 ..
-rwxr-xr-x    1 root     root             0 Sep 29 11:28 .dockerenv
...생략
-rw-r--r--    1 root     root             0 Sep 29 11:15 mycustom.txt
...생략
drwxr-xr-x   11 root     root          4096 Aug 20 10:30 var

myimage 이미지로 생성한 컨테이너에서 ls 명령어를 실행하면 mycustom.txt 파일이 표시된 것을 알 수 있다.

myimage를 조금 더 살펴보자. docker image history 명령어를 실행하면 이미지의 내역을 볼 수 있다. 다음은 실행 결과이다.

vagrant@ubuntu-bionic:~$ docker image history myimage:latest
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
422d23665db4        7 minutes ago       touch /mycustom.txt                             0B
961769676411        5 weeks ago         /bin/sh -c #(nop)  CMD ["/bin/sh"]              0B
<missing>           5 weeks ago         /bin/sh -c #(nop) ADD file:fe64057fbb83dccb9…   5.58MB

위 결과에서 IMAGE 칼럼에 표시된 값과 docker images 명령어의 IMAGE ID 칼럼 값을 비교해보자. 결과를 보면 docker image history에서 출력한 9617961769676411가 alpine:3.10 IMAGE ID와 같다.

vagrant@ubuntu-bionic:~$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
myimage             latest              422d23665db4        12 minutes ago      5.58MB
...생략
alpine              3.10                961769676411        5 weeks ago         5.58MB
...생략

 

이번에는 alpine:3.10 이미지의 내역을 보자. myimage 이미지의 내역에서 9617961769676411 부분부터 <missing>의 내용이 완전 동일하다.

vagrant@ubuntu-bionic:~$ docker image history alpine:3.10
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
961769676411        5 weeks ago         /bin/sh -c #(nop)  CMD ["/bin/sh"]              0B
<missing>           5 weeks ago         /bin/sh -c #(nop) ADD file:fe64057fbb83dccb9…   5.58MB

도커 이미지 내역은 도커 이미지를 생성할 때 사용한 변경 내역을 보여준다. 도커는 이미지를 생성할 때 전체 파일을 새로 만들지 않고 변경한 파일만 이용해서 새로운 레이어를 생성한다. 예를 들어 myimage:latest 이미지는 alpine:3.10 이미지에서 변경한 내용만 새로운 레이어로 생성한다. 비슷하게 alpine:3.10 이미지는 <missing>으로 표시된 이미지에서 변경한 내용만 레이어로 생성한다. 즉 myimage:latest 이미지는 alpine 이미지로 생성한 컨테이너에서 변경한 파일인 mycustom.txt 파일만 포함한다.

이런 특징을 잘 활용하면 다운로드 받을 이미지의 크기를 줄일 수 있다. 예를 들어 10개의 자바 어플리케이션을 이미지로 만들 때 openjdk:8u212-jdk-alpine 이미지를 하위 레이어로 사용하면 openjdk:8u212-jdk-alpine 이미지와 관련된 파일은 한 번만 다운로드 하고 10개 자바 어플리케이션의 변경 부분만 다운로드하므로 어플리케이션을 구동하기 위해 다운로드해야 하는 이미지의 크기가 줄어든다.

docker commit으로 생성한 이미지 파일은 도커 허브나 별도로 구성한 도커 레지스트리를 이용해서 다른 사람과 공유할 수 있다. 그런데 docker commit을 이용해서 도커 이미지를 만드는 과정은 수작업으로 이루어지므로 불편하고 실수하기 좋다. Dockerfile을 사용하면 이미지 생성 과정을 쉽게 자동화할 수 있는데 다음 글에서 Dockerfile을 이용한 이미지 생성 방법을 살펴보자.

관련 글

+ Recent posts