- 책 또는 웹사이트의 내용을 복제하여 다른 곳에 게시하는 것을 금지합니다.
- 책 또는 웹사이트의 내용을 발췌, 요약하여 강의 자료, 발표 자료, 블로그 포스팅 등으로 만드는 것을 금지합니다.
Docker 기본 사용법
이재홍 http://www.pyrasis.com 2014.7.7 ~ 2022.9.9
Docker는 리눅스 서버를 손쉽게 패키징하고, 배포할 수 있도록 해주는 플랫폼입니다. 이 문서는 Docker 기본 사용법 강좌입니다.
목차
가상 머신과 Docker
Docker는 지금까지 사용해왔던 VMware, Microsoft Hyper-V(Virtual PC), Xen 등의 같은 가상 머신과 비슷합니다.
가상 머신에 리눅스를 설치한 뒤 각종 서버와 DB를 설치하고, 개발한 어플리케이션이나 웹사이트를 실행했습니다. 이렇게 세팅한 가상 머신 이미지를 여러 서버에 복사해서 실행하면 이미지 하나로 서버를 계속 만들어낼 수 있었습니다.
보통 가상 머신 서버를 독자적으로 운영하거나, 서비스 형태로 제공되는 AWS, Microsoft Azure를 사용하기도 합니다.
가상 머신
출처: https://www.docker.com/resources/what-container/
가상 머신은 편하긴 하지만 성능이 좋지 못한 것이 단점이었습니다. 지금까지 CPU에 가상화를 위한 기능들이 많이 추가되었지만 아직도 가상 머신은 리얼 머신에 비해 속도가 느립니다.
느린 속도를 개선하기 위해 반가상화(Paravirtualization) 방식이 개발되었고, 현재 널리 쓰이고 있습니다.
그림에서 보는 것과 같이 가상 머신이기 때문에 항상 Guest OS를 설치해야 합니다. 그래서 이미지안에 OS가 포함되기 때문에 이미지 용량이 커집니다.
아무리 네트워크와 인터넷 속도가 빨라졌다 하더라도 가상화 이미지를 주고받는 것은 꽤 부담스럽습니다.
가상 머신은 OS를 가상화하는데 초점이 맞춰져 있습니다. 그래서 이미지를 생성하는 기능만 가지고 있을 뿐 배포와 관리 기능이 부족합니다(VMware의 비싼 제품을 사용해도 되지만...).
Docker
출처: https://www.docker.com/resources/what-container/
Docker는 반가상화보다 좀더 경량화된 방식입니다. 그림에서 보는 것과 같이 Geust OS를 설치하지 않습니다. Docker 이미지에 서버 운영에 필요한 프로그램과 라이브러리만 격리해서 설치할 수 있고, OS 자원은 호스트와 공유합니다. 이렇게 되면서 이미지 용량이 크게 줄어들었습니다. 그리고 가상화 레이어가 없기 때문에 파일시스템, 네트워크 속도도 가상 머신에 비해 월등히 빠릅니다(호스트와 거의 동일한 속도).
Docker는 가상 머신과는 달리 이미지 생성과 배포에 특화된 기능을 제공합니다.
Git에서 소스를 관리하는 것처럼 이미지 버전 관리 기능을 제공합니다. 그리고 중앙 관리를 위해 저장소에 이미지를 올리고, 받을 수 있습니다(Push/Pull). 게다가 GitHub처럼 Docker 이미지를 공유할 수 있는 Docker Hub도 제공합니다(GitHub처럼 개인 저장소도 제공합니다).
다양한 API를 제공하기 때문에 원하는 만큼 자동화를 할 수 있어 개발과 서버 운영에 매우 유용합니다.
리눅스 컨테이너
리눅스는 운영체제 레벨에서 가상화를 제공했습니다. OS를 통째로 가상화하는 것이 아닌 리눅스 커널 레벨에서 제공하는 일종의 격리(Isolate)된 가상 공간입니다.
OS가 설치되지 않기 때문에 가상 머신이라고 하지 않고, 컨테이너라고 부릅니다.
리눅스 컨테이너는 격리된 공간만 제공할 뿐 개발 및 서버 운영에 필요한 부가기능이 부족했습니다. Docker는 리눅스 컨테이너를 기반으로 하여 편리하게 이미지를 관리하고 배포할 수 있도록 해줍니다.
Docker 이미지와 컨테이너
Docker는 이미지와 컨테이너라는 개념이 있습니다.
이미지는 필요한 프로그램과 라이브러리, 소스를 설치한 뒤 파일로 만든 것입니다. 이 이미지를 저장소에 올리고, 받을 수 있습니다.
컨테이너는 이미지를 실행한 상태입니다. 이미지로 여러개의 컨테이너를 만들 수 있습니다. 운영체제로 보면 이미지는 실행파일이고 컨테이너는 프로세스입니다.
Docker는 특정 실행파일 또는 스크립트를 위한 실행 환경이라 보면 됩니다. 리눅스/유닉스 계열은 파일 실행에 필요한 모든 구성요소가 잘게 쪼개어져 있습니다. 시스템 구조가 단순해지고 명확해지는 장점이 있지만 의존성 관계를 해결하기가 어려워지는 단점이 있습니다. 그래서 리눅스 배포판 별로 미리 컴파일된 패키지(rpm, deb 등)라는 시스템이 나왔습니다. 하지만 서버를 실행할 때마다 일일이 소스를 컴파일하거나 패키지를 설치하고, 설정하려면 상당히 귀찮습니다.
서버가 한 두대라면 큰 어려움이 없겠지만 클라우드 환경에서는 서버를 몇 십, 몇 백개를 생성해야 합니다. 서버 구성을 미리 해놓은 Docker 이미지를 사용하면 실행할 서버가 몇개가 되든 손쉽게 해결할 수 있습니다.
설치하기
이제 Docker를 설치해보겠습니다. Docker는 소스를 컴파일해서 설치하려면 준비해야할 것이 많습니다. 따라서 이 문서에서는 자동 설치 스크립트와 각 리눅스 배포판의 패키지로 설치하는 방법을 설명하겠습니다.
리눅스
자동 설치 스크립트
Docker는 리눅스 배포판 종류를 자동으로 인식하여 패키지를 설치해주는 스크립트를 제공합니다.
$ sudo wget -qO- https://get.docker.com/ | sh
$ sudo curl -fsSL https://get.docker.com | sh
우분투
자동 설치 스크립트를 사용하지 않고 우분투에서 패키지로 직접 설치하는 방법입니다.
우분투 22.04 LTS 64비트를 기준으로 하겠습니다.
$ sudo apt update
$ sudo apt install -y \
ca-certificates \
curl \
gnupg \
lsb-release
$ sudo mkdir -p /etc/apt/keyrings
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
$ echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
$ sudo apt update
$ sudo apt install docker-ce docker-ce-cli containerd.io docker-compose-plugin
RedHat Enterprise Linux, CentOS, Amazon Linux
자동 설치 스크립트를 사용하지 않고, 레드햇 엔터프라이즈 리눅스(RHEL), CentOS, Amazon Linux에서 패키지로 직접 설치하는 방법입니다.
$ sudo yum install -y yum-utils
$ sudo yum-config-manager \
--add-repo \
https://download.docker.com/linux/rhel/docker-ce.repo
$ sudo yum install docker-ce docker-ce-cli containerd.io docker-compose-plugin
$ sudo yum install -y yum-utils
$ sudo yum-config-manager \
--add-repo \
https://download.docker.com/linux/centos/docker-ce.repo
$ sudo yum install docker-ce docker-ce-cli containerd.io docker-compose-plugin
sudo yum install docker
Docker 서비스 실행
$ sudo systemctl start docker
부팅했을 때 자동으로 실행하기
$ sudo chkconfig docker on
macOS
맥에서는 Docker Desktop을 이용하여 Docker를 사용할 수 있습니다.
https://docs.docker.com/desktop/install/mac-install/에서 Docker.dmg를 받은 뒤 설치합니다.
설치는 특별한 것이 없으므로 따로 설명하지 않겠습니다.
윈도우
윈도우에서는 Docker Desktop을 이용하여 Docker를 사용할 수 있습니다.
https://docs.docker.com/desktop/install/windows-install/에서 Docker Desktop Installer.exe를 받은 뒤 설치합니다.
설치는 특별한 것이 없으므로 따로 설명하지 않겠습니다.
사용하기
Docker의 명령은 docker run
, docker push
와 같이 docker <명령>
형식입니다. 그리고 항상 root 권한으로 실행해야 합니다.
먼저 Docker의 기본적인 사용법을 알아보기 위해 Docker Hub에서 제공하는 이미지를 받아서 실행해보겠습니다.
search
Docker는 Docker Hub를 통해 이미지를 공유하는 생태계가 구축되어 있습니다. search
명령으로 Docker Hub에서 이미지를 검색할 수 있습니다.
$ sudo docker search ubuntu
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
ubuntu Ubuntu is a Debian-based Linux operating sys… 14904 [OK]
websphere-liberty WebSphere Liberty multi-architecture images … 288 [OK]
ubuntu-upstart DEPRECATED, as is Upstart (find other proces… 112 [OK]
neurodebian NeuroDebian provides neuroscience research s… 92 [OK]
ubuntu/nginx Nginx, a high-performance reverse proxy & we… 58
open-liberty Open Liberty multi-architecture images based… 54 [OK]
ubuntu-debootstrap DEPRECATED; use "ubuntu" instead 46 [OK]
엄청나게 많은 이미지가 검색될 것입니다. 보통 ubuntu, centos, redis 등 OS나 프로그램 이름을 가진 이미지가 공식 이미지입니다. 나머지는 사용자들이 만들어 공개한 이미지입니다.
Docker Hub에서 이미지를 검색한 뒤 해당 이미지의 Tags 탭을 보면 현재 사용할 수 있는 이미지의 버전을 볼 수 있습니다.
Official Ubuntu base image Tags
pull
우분투 이미지를 받아보겠습니다.
$ sudo docker pull ubuntu:latest
docker pull <이미지 이름>:<태그>
형식입니다. latest를 설정하면 최신 버전을 받습니다. ubuntu:22.04, ubuntu:20.10처럼 태그를 지정해 줄 수도 있습니다.
이미지 이름에서 pyrasis/ubuntu처럼 / 앞에 사용자명을 지정하면 Docker Hub에서 해당 사용자가 올린 이미지를 받습니다. 공식 이미지는 사용자명이 붙지 않습니다.
참고
호스트에 설치된 리눅스 배포판과 Docker 이미지의 배포판의 종류가 달라도 됩니다. CentOS 위에서 우분투 컨테이너를 실행할 수 있습니다.
images
이제 받은 이미지의 목록을 출력해보겠습니다.
$ sudo docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu latest 2dc39ba059dc 7 days ago 77.8MB
docker images
는 모든 이미지 목록을 출력합니다. docker images ubuntu
처럼 이미지 이름을 설정하면 이름은 같지만 태그가 다른 이미지가 출력됩니다.
run
이미지를 컨테이너로 생성한 뒤 Bash Shell을 실행해보겠습니다.
$ sudo docker run -i -t --name hello ubuntu /bin/bash
docker run <옵션> <이미지 이름> <실행할 파일>
형식입니다. 여기서는 ubunbu 이미지를 컨테이너로 생성한 뒤 ubuntu 이미지 안의 /bin/bash를 실행합니다. 이미지 이름 대신 이미지 ID를 사용해도 됩니다.
-i
(interactive), -t
(Pseudo-tty) 옵션을 사용하면 실행된 Bash Shell에 입력 및 출력을 할 수 있습니다. 그리고 --name
옵션으로 컨테이너의 이름을 지정할 수 있습니다. 이름을 지정하지 않으면 Docker가 자동으로 이름을 생성하여 지정합니다.
이제 호스트 OS와 완전히 격리된 공간이 생성되었습니다. cd, ls 명령으로 컨테이너 내부를 한번 둘러봅니다. 호스트 OS와는 다르다는 것을 알 수 있습니다. exit를 입력하여 Bash Shell에서 빠져나옵니다. 우분투 이미지에서 /bin/bash 실행 파일을 직접 실행했기 때문에 여기서 빠져나오면 컨테이너가 정지(stop)됩니다.
ps
아래 명령을 입력하여 모든 컨테이너 목록을 출력합니다.
$ sudo docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f8b0bf04c340 ubuntu "/bin/bash" 5 seconds ago Exited (0) 3 seconds ago hello
docker ps
형식입니다. -a
옵션을 사용하면 정지된 컨테이너까지 모두 출력하고, 옵션을 사용하지 않으면 실행되고 있는 컨테이너만 출력합니다.
우리는 앞에서 컨테이너를 생성할 때 이름을 hello로 지정했으므로 컨테이너 목록에서도 hello로 표시됩니다.
start
방금 정지한 컨테이너를 다시금 시작해보겠습니다.
$ sudo docker start hello
docker start <컨테이너 이름>
형식입니다. 컨테이너 이름 대신 컨테이너 ID를 사용해도 됩니다.
아래 명령을 입력하여 실행된 컨테이너 목록을 출력합니다.
$ sudo docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f8b0bf04c340 ubuntu "/bin/bash" 40 seconds ago Up 4 seconds hello
hello 컨테이너가 시작되었습니다.
restart
OS 재부팅처럼 컨테이너를 재시작해보겠습니다.
$ sudo docker restart hello
docker restart <컨테이너 이름>
형식입니다. 컨테이너 이름 대신 컨테이너 ID를 사용해도 됩니다.
attach
이제 시작한 컨테이너에 접속해보겠습니다. 아래 명령을 실행한 뒤 엔터를 한번 더 입력하면 Bash Shell이 표시됩니다.
$ sudo docker attach hello
root@f8b0bf04c340:/#
docker attach <컨테이너 이름>
형식입니다. 컨테이너 이름 대신 컨테이너 ID를 사용해도 됩니다.
우리는 /bin/bash를 실행했기 때문에 명령을 자유롭게 입력할 수 있지만, DB나 서버 어플리케이션을 실행하면 입력은 할 수 없고 출력만 보게 됩니다.
Bash Shell에서 exit 또는 Ctrl+D를 입력하면 컨테이너가 정지됩니다. 여기서는 Ctrl+P, Ctrl+Q를 차례대로 입력하여 컨테이너를 정지하지 않고, 컨테이너에서 빠져나옵니다.
exec
현재 컨테이너가 /bin/bash로 실행된 상태입니다. 이번에는 /bin/bash를 통하지 않고 외부에서 컨테이너 안의 명령을 실행해보겠습니다.
$ sudo docker exec hello echo "Hello World"
Hello World
docker exec <컨테이너 이름> <명령> <매개 변수>
형식입니다. 컨테이너 이름 대신 컨테이너 ID를 사용해도 됩니다. 컨테이너가 실행되고 있는 상태에서만 사용할 수 있으며 정지된 상태에서는 사용할 수 없습니다.
컨테이너 안의 echo 명령을 실행하고 매개 변수로 "Hello World"를 지정했기 때문에 Hello World가 출력됩니다. docker exec
명령은 이미 실행된 컨테이너에 apt, yum 명령으로 패키지를 설치하거나, 각종 데몬을 실행할 때 활용할 수 있습니다.
stop
이번에는 컨테이너를 정지해보겠습니다. 먼저 실행된 컨테이너 목록을 출력합니다.
$ sudo docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f8b0bf04c340 ubuntu "/bin/bash" 2 minutes ago Up About a minute hello
아래 명령으로 컨테이너를 정지합니다.
$ sudo docker stop hello
docker stop <컨테이너 이름>
형식입니다. 컨테이너 이름 대신 컨테이너 ID를 사용해도 됩니다.
실행된 컨테이너 목록을 출력합니다.
$ sudo docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
hello 컨테이너를 정지했기 때문에 아무 것도 안나옵니다.
rm
생성된 컨테이너를 삭제해보겠습니다.
$ sudo docker rm hello
docker rm <컨테이너 이름>
형식입니다. 컨테이너 이름 대신 컨테이너 ID를 사용해도 됩니다.
모든 컨테이너 목록을 출력합니다.
$ sudo docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
hello 컨테이너를 삭제했기 때문에 아무 것도 안 나옵니다.
rmi
이번에는 이미지를 삭제해보겠습니다.
$ sudo docker rmi ubuntu:latest
docker rmi <이미지 이름>:<태그>
형식입니다. 이미지 이름 대신 이미지 ID를 사용해도 됩니다. docker rmi ubuntu
처럼 이미지 이름만 지정하면 태그는 다르지만 ubuntu 이름을 가진 모든 이미지가 삭제됩니다.
이미지 목록을 출력합니다.
$ sudo docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu 이미지를 삭제했기 때문에 아무것도 안 나옵니다.
이미지 생성하기
기본적인 이미지와 컨테이너 사용 방법을 알아보았으니 이번에는 이미지를 생성해보겠습니다.
Dockerfile
Dockerfile은 Docker 이미지 설정 파일입니다. Dockerfile에 설정된 내용대로 이미지를 생성합니다.
먼저 example 디렉터리를 생성한 뒤 example 디렉터리로 이동합니다.
~$ mkdir example
~$ cd example
아래 내용을 Dockerfile로 저장합니다.
FROM ubuntu:22.04
MAINTAINER Foo Bar <foo@bar.com>
RUN apt update
RUN apt install -y nginx
RUN echo "\ndaemon off;" >> /etc/nginx/nginx.conf
RUN chown -R www-data:www-data /var/lib/nginx
VOLUME ["/data", "/etc/nginx/site-enabled", "/var/log/nginx"]
WORKDIR /etc/nginx
CMD ["nginx"]
EXPOSE 80
EXPOSE 443
우분투 22.04를 기반으로 nginx 서버를 설치한 Docker 이미지를 생성하는 예제입니다.
- FROM: 어떤 이미지를 기반으로 할지 설정합니다. Docker 이미지는 기존에 만들어진 이미지를 기반으로 생성합니다.
<이미지 이름>:<태그>
형식으로 설정합니다. - MAINTAINER: 메인테이너 정보입니다.
- RUN: Shell 스크립트 혹은 명령을 실행합니다.
- 이미지 생성 중에는 사용자 입력을 받을 수 없으므로 apt install 명령에서
-y
옵션을 사용합니다(yum install도 동일). - 나머지는 nginx 설정입니다.
- 이미지 생성 중에는 사용자 입력을 받을 수 없으므로 apt install 명령에서
- VOLUME: 호스트와 공유할 디렉터리 목록입니다.
docker run
명령에서-v
옵션으로 설정할 수 있습니다. 예)-v /root/data:/data
호스트의 /root/data 디렉터리를 Docker 컨테이너의 /data 디렉터리에 연결합니다. - CMD: 컨테이너가 시작되었을 때 실행할 실행 파일 또는 스크립트입니다.
- WORKDIR: CMD에서 설정한 실행 파일이 실행될 디렉터리입니다.
- EXPOSE: 호스트와 연결할 포트 번호입니다.
build
Dockerfile을 작성하였으면 이미지를 생성합니다. Dockerfile이 저장된 example 디렉터리에서 아래 명령을 실행합니다.
$ sudo docker build --tag hello:0.1 .
docker build <옵션> <Dockerfile 경로>
형식입니다. --tag
옵션으로 이미지 이름과 태그를 설정할 수 있습니다. 이미지 이름만 설정하면 태그는 latest로 설정됩니다.
잠시 기다리면 이미지 파일이 생성됩니다. 이미지 목록을 출력합니다.
$ sudo docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hello 0.1 8307ff628ec8 9 seconds ago 170MB
ubuntu 22.04 2dc39ba059dc 7 days ago 77.8MB
hello:0.1 이미지가 생성되었습니다. 이제 실행을 해봅니다.
$ sudo docker run --name hello-nginx -d -p 80:80 -v /root/data:/data hello:0.1
-d
옵션은 컨테이너를 백그라운드로 실행합니다.
-p 80:80
옵션으로 호스트의 80번 포트와 컨테이너의 80번 포트를 연결하고 외부에 노출합니다. 이렇게 설정한 뒤 http://<호스트 IP>:80에 접속하면 컨테이너의 80번 포트로 접속됩니다.
-v /root/data:/data
옵션으로 호스트의 /root/data 디렉터리를 컨테이너의 /data 디렉터리에 연결합니다. /root/data 디렉터리에 파일을 넣으면 컨테이너에서 해당 파일을 읽을 수 있습니다.
실행된 컨테이너 목록을 출력합니다.
$ sudo docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
9f4bb4c4a3d8 hello:0.1 "nginx" 11 seconds ago Up 10 seconds 0.0.0.0:80->80/tcp, :::80->80/tcp, 443/tcp hello-nginx
hello-nginx 컨테이너가 실행되었습니다.
웹 브라우저를 실행하고, http://<호스트 IP>:80으로 접속합니다. Welcome to nginx! 페이지가 표시될 것입니다.
macOS, Windows에서 Docker Desktop을 사용한다면?
웹 브라우저를 실행하고, http://127.0.0.1로 접속합니다. Welcome to nginx! 페이지가 표시될 것입니다.
기타 명령
history
앞에서 생성한 hello:0.1 이미지의 히스토리를 조회해보겠습니다.
$ sudo docker history hello:0.1
IMAGE CREATED CREATED BY SIZE COMMENT
8307ff628ec8 5 minutes ago /bin/sh -c #(nop) EXPOSE 443 0B
adbb8a7ef64d 5 minutes ago /bin/sh -c #(nop) EXPOSE 80 0B
a405f9d28415 5 minutes ago /bin/sh -c #(nop) CMD ["nginx"] 0B
a9726f7e681e 5 minutes ago /bin/sh -c #(nop) WORKDIR /etc/nginx 0B
78f354dbdbcc 5 minutes ago /bin/sh -c #(nop) VOLUME [/data /etc/nginx/… 0B
66f885084c19 5 minutes ago /bin/sh -c chown -R www-data:www-data /var/l… 0B
5c54eb835f57 5 minutes ago /bin/sh -c echo "\ndaemon off;" >> /etc/ngin… 1.46kB
9fc2f61ad2f4 5 minutes ago /bin/sh -c apt install -y nginx 55.6MB
9c80ad1c1c34 6 minutes ago /bin/sh -c apt update 36.2MB
69b49943b930 6 minutes ago /bin/sh -c #(nop) MAINTAINER Foo Bar <foo@b… 0B
2dc39ba059dc 7 days ago /bin/sh -c #(nop) CMD ["bash"] 0B
<missing> 7 days ago /bin/sh -c #(nop) ADD file:a7268f82a86219801… 77.8MB
docker history <이미지 이름>:<태그>
형식입니다. 이미지 이름 대신 이미지 ID를 사용해도 됩니다.
이렇게 Dockefile에 설정한 대로 히스토리가 생성됩니다.
cp
hello-nginx 컨테이너에서 파일을 꺼내보겠습니다.
$ sudo docker cp hello-nginx:/etc/nginx/nginx.conf ./
docker cp <컨테이너 이름>:<경로> <호스트 경로>
형식입니다.
현재 디렉터리에 nginx.conf 파일이 복사되었습니다.
commit
docker commit
명령은 컨테이너의 변경 사항을 이미지 파일로 생성합니다.
hello-nginx 컨테이너 안의 파일 내용이 바뀌었다고 치고, 컨테이너를 이미지 파일로 생성해보겠습니다.
$ sudo docker commit -a "Foo Bar <foo@bar.com>" -m "add hello.txt" hello-nginx hello:0.2
docker commit <옵션> <컨테이너 이름> <이미지 이름>:<태그>
형식입니다. 컨테이너 이름 대신 컨테이너 ID를 사용해도 됩니다.
-a "Foo Bar <foo@bar.com>"
과 -m "add hello.txt"
옵션으로 커밋한 사용자와 로그 메시지를 설정합니다. hello-nginx 컨테이너를 hello:0.2 이미지로 생성합니다.
이미지 목록을 출력합니다.
$ sudo docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hello 0.2 011ff3ff87e9 14 seconds ago 170MB
hello 0.1 8307ff628ec8 6 minutes ago 170MB
ubuntu 22.04 2dc39ba059dc 7 days ago 77.8MB
hello:0.2 이미지가 생성되었습니다.
diff
docker diff
명령은 컨테이너가 실행되면서 변경된 파일 목록을 출력합니다. 비교 기준은 컨테이너를 생성한 이미지 내용입니다.
$ sudo docker diff hello-nginx
C /etc
C /etc/nginx
A /etc/nginx/site-enabled
A /data
C /var
C /var/lib
C /var/lib/nginx
A /var/lib/nginx/body
A /var/lib/nginx/fastcgi
A /var/lib/nginx/proxy
A /var/lib/nginx/scgi
A /var/lib/nginx/uwsgi
C /run
A /run/nginx.pid
docker diff <컨테이너 이름>
형식입니다. 컨테이너 이름 대신 컨테이너 ID를 사용해도 됩니다.
A
는 추가된 파일, C
는 변경된 파일, D
는 삭제된 파일입니다.
inspect
docker inspect
명령은 이미지와 컨테이너의 세부 정보를 출력합니다.
$ sudo docker inspect hello-nginx
[
{
"Id": "9f4bb4c4a3d8698a31507febffba19a655caab5a0ee78452cc2c64ea406af7bd",
"Created": "2022-09-09T03:11:49.391803068Z",
"Path": "nginx",
"Args": [],
"State": {
"Status": "running",
... 생략 ...
},
"Image": "sha256:8307ff628ec8900bc7a80eecc54bf6f21b6019ee96d22ca3e4152a06f704756d",
... 생략 ...
"Name": "/hello-nginx",
"RestartCount": 0,
"Driver": "overlay2",
"Platform": "linux",
... 생략 ...
"HostConfig": {
... 생략 ...
"NetworkMode": "default",
"PortBindings": {
"80/tcp": [
{
"HostIp": "",
"HostPort": "80"
}
]
},
... 생략 ...
},
... 생략 ...
"Config": {
"Hostname": "9f4bb4c4a3d8",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"ExposedPorts": {
"443/tcp": {},
"80/tcp": {}
},
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
],
"Cmd": [
"nginx"
],
"Image": "hello:0.1",
"Volumes": {
"/data": {},
"/etc/nginx/site-enabled": {},
"/var/log/nginx": {}
},
"WorkingDir": "/etc/nginx",
"Entrypoint": null,
"OnBuild": null,
"Labels": {}
},
"NetworkSettings": {
"Bridge": "",
"SandboxID": "0adee39c61d22d570a50a3e9ba174dc7901ffea732f6aca75e5ea00a330f9fa3",
"HairpinMode": false,
"LinkLocalIPv6Address": "",
"LinkLocalIPv6PrefixLen": 0,
"Ports": {
"443/tcp": null,
"80/tcp": [
{
"HostIp": "0.0.0.0",
"HostPort": "80"
},
{
"HostIp": "::",
"HostPort": "80"
}
]
},
... 생략 ...
}
}
]
}]
docker inspect <이미지 또는 컨테이너 이름>
형식입니다. 이미지, 컨테이너 이름 대신 이미지 ID나, 컨테이너 ID를 사용해도 됩니다.
컨테이너 연결하기
웹 서버 컨테이너와 DB 컨테이너가 있을 때 웹 서버 컨테이너에서 DB 컨테이너에 접근할 수 있어야 합니다. 이 때에는 docker run
명령에서 --link
옵션을 사용하여 컨테이너를 연결합니다.
먼저 DB 이미지를 컨테이너로 실행합니다. 이번에는 MongoDB를 사용해보겠습니다.
$ sudo docker run --name db -d mongo
DB 컨테이너 이름은 db로 설정하였습니다.
그리고 앞에서 생성한 hello-nginx 컨테이너를 정지합니다.
$ sudo docker stop hello-nginx
이제 web 컨테이너를 생성하면서 db 컨테이너와 연결합니다. 웹 서버로 사용할 컨테이너는 nginx 이미지로 생성하겠습니다.
$ sudo docker run --name web -d -p 80:80 --link db:db nginx
--link <컨테이너 이름>:<별칭>
형식입니다.
컨테이너 목록을 출력합니다.
$ sudo docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a391f7cf7199 nginx "/docker-entrypoint.…" 9 seconds ago Up 7 seconds 0.0.0.0:80->80/tcp, :::80->80/tcp web
abe4e3ffe358 mongo "docker-entrypoint.s…" 2 minutes ago Up 2 minutes 27017/tcp db
db 컨테이너와 web 컨테이너가 연결되었습니다.
이제 web 컨테이너 안에서 db:27017
주소로 db 컨테이너의 MongoDB에 접속할 수 있습니다.
mongodb://db:27017/exampledb
<별칭>:<포트 번호>
형식입니다.
참고
다음과 같이 docker inspect 명령으로 web 컨테이너의 세부 정보에서 hosts 파일 경로를 구한 뒤 cat 명령으로 내용을 살펴봅니다(`는 탭키 위에 있는 문자입니다).
$ sudo cat `sudo docker inspect -f "{{ .HostsPath }}" web`
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.3 db abe4e3ffe358
172.17.0.2 a391f7cf7199
db는 --link db:db
에서 설정한 별칭이고, 172.17.0.3은 db 컨테이너의 IP 주소입니다.
컨테이너를 연결하면 이렇게 hosts 파일에 IP 주소가 자동으로 설정되기 때문에 별칭을 통해 연결한 컨테이너에 접속할 수 있습니다.
개인 저장소 구축하기
Docker 저장소 서버는 Docker 레지스트리 서버라고 부릅니다. 레지스트리 서버에 이미지를 올리거나 받을 수 있습니다.
먼저 --insecure-registry 옵션을 사용하여 Docker 데몬을 실행합니다.
$ sudo dockerd --insecure-registry localhost:5000
보통 Docker 데몬을 직접 실행하지 않고 서비스 형태로 실행합니다. 이때는 /etc/docker/daemon.json 파일을 생성하고 다음과 같이 저장합니다(이 파일은 root 권한으로 수정해야 합니다).
{
"insecure-registries": ["localhost:5000"]
}
/etc/docker/daemon.json 파일을 수정했으면 Docker 서비스를 재시작합니다.
$ sudo systemctl restart docker
로컬에 이미지 데이터 저장
Docker 레지스트리 서버도 Docker Hub를 통해 Docker 이미지로 제공됩니다. Docker 레지스트리 이미지를 받습니다.
$ sudo docker pull registry:latest
registry:latest 이미지를 컨테이너로 실행합니다.
$ sudo docker run -d -p 5000:5000 --name hello-registry \
-v /tmp/registry:/tmp/registry \
registry
이렇게 실행하면 이미지 파일은 호스트의 /tmp/registry 디렉터리에 저장됩니다.
push
앞에서 만든 hello:0.1 이미지를 개인 저장소에 올려보겠습니다.
$ sudo docker tag hello:0.1 localhost:5000/hello:0.1
$ sudo docker push localhost:5000/hello:0.1
docker tag <이미지 이름>:<태그> <Docker 레지스트리 URL>/<이미지 이름>:<태그>
형식입니다.
docker push <Docker 레지스트리 URL>/<이미지 이름>:<태그>
형식입니다.
먼저 docker tag
명령으로 hello:0.1 이미지를 localhost:5000/hello:0.1 태그로 생성합니다. 그리고 docker push
명령으로 localhost:5000/hello:0.1 이미지를 개인 저장소에 올립니다(태그를 생성했으므로 실제로는 hello:0.1 이미지가 올라갑니다).
이제 다른 서버에서 개인 저장소(Docker 레지스트리 서버)에 접속하여 이미지를 받아올 수 있습니다. 개인 저장소 서버 IP 주소가 172.31.23.145라면 아래와 같이 명령을 실행합니다.
$ sudo docker pull 172.31.23.145:5000/hello:0.1
참고로 명령을 실행하려면 /etc/docker/daemon.json 파일에서 insecure-registries에 172.31.23.145:5000이 들어있어야 합니다. 그리고 Docker 서비스를 재시작하는 것을 잊지마세요.
{
"insecure-registries": ["localhost:5000", "172.31.23.145:5000"]
}
이미지 목록을 출력합니다.
$ sudo docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hello 0.2 011ff3ff87e9 29 minutes ago 170MB
172.31.23.145:5000/hello 0.1 8307ff628ec8 35 minutes ago 170MB
hello 0.1 8307ff628ec8 35 minutes ago 170MB
localhost:5000/hello 0.1 8307ff628ec8 35 minutes ago 170MB
mongo latest d34d21a9eb5b 7 days ago 693MB
ubuntu 22.04 2dc39ba059dc 7 days ago 77.8MB
nginx latest 2b7d6430f78d 2 weeks ago 142MB
registry latest 3a0f7b0a13ef 4 weeks ago 24.1MB
개인 저장소에서 172.31.23.145:5000/hello 이미지를 받았습니다.
이미지를 삭제할 때에는 아래와 같이 실행합니다.
$ sudo docker rmi 172.31.23.145:5000/hello:0.1
Amazon S3에 이미지 데이터 저장
이번에는 AWS의 S3에 이미지 데이터를 저장하는 방법입니다.
먼저 앞에서 생성한 hello-registry 컨테이너를 정지합니다.
$ sudo docker stop hello-registry
Docker 레지스트리 이미지를 받습니다.
$ sudo docker pull registry:latest
registry:latest 이미지를 컨테이너로 실행합니다.
$ sudo docker run -d -p 5000:5000 --name s3-registry \
-e REGISTRY_STORAGE=s3 \
-e REGISTRY_STORAGE_S3_BUCKET=examplebucket10 \
-e REGISTRY_STORAGE_S3_REGION=ap-northeast-2 \
-e REGISTRY_STORAGE_S3_ACCESSKEY=AKIABCDEFGHIJKLMNOPQ \
-e REGISTRY_STORAGE_S3_SECRETKEY=sF4321ABCDEFGHIJKLMNOPqrstuvwxyz21345Afc \
registry
- REGISTRY_STORAGE: s3을 설정합니다.
- REGISTRY_STORAGE_S3_BUCKET: 이미지 데이터를 저장할 S3 버킷 이름을 설정합니다.
- REGISTRY_STORAGE_S3_REGION: S3 버킷의 리전을 설정합니다.
- REGISTRY_STORAGE_S3_ACCESSKEY: AWS 액세스 키를 설정합니다.
- REGISTRY_STORAGE_S3_SECRETKEY: AWS 시크릿 키를 설정합니다.
이제 s3-registry 저장소에 Docker 이미지를 push하면 S3 버킷에 저장됩니다.
기본 인증 사용하기
Docker 레지스트리에는 로그인 기능이 없습니다. 따라서 Nginx의 기본 인증(Basic Authentication) 기능을 사용해야 합니다. 또한, HTTP 프로토콜에서는 인증을 지원하지 않으므로 반드시 HTTPS 프로토콜을 사용해야 합니다.
먼저 /etc/hosts 파일을 편집하여 테스트용 도메인을 추가합니다. 이 파일은 root 권한으로 수정해야 합니다. 도메인을 구입하지 않았을 때는 이 부분을 반드시 설정해주어야 하며, 도메인 구입하여 DNS를 설정하였다면 이 부분은 건너뛰어도 됩니다.
127.0.0.1 localhost
<레지스트리 서버 IP 주소> registry.example.com
# The following lines are desirable for IPv6 capable hosts
::1 ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
ff02::3 ip6-allhosts
여러분의 레지스트리 서버 IP 주소를 registry.example.com으로 설정합니다. 이 문서에서는 registry.example.com을 기준으로 설명하겠습니다.
이제 SSL 사설 인증서(Self Signed)를 생성하겠습니다. SSL 공인 인증서를 구입하였다면 이 부분은 건너뛰어도 됩니다.
- /CN: Docker 레지스트리를 실행하는 서버의 도메인입니다. 이 부분을 정확하게 입력하지 않으면 인증서를 사용해도 로그인할 때 에러가 발생합니다. /etc/hosts 파일에 설정한대로 registry.example.com를 입력합니다.
- subjectAltName: SAN입니다. subjectAltName=DNS:registry.example.com를 입력합니다.
$ mkdir certs
$ cd certs
~/certs$ openssl genrsa -out ca.key 2048
~/certs$ openssl req -new -x509 -days 365 -key ca.key \
-subj "/C=CN/ST=GD/L=SZ/O=Acme, Inc./CN=Acme Root CA" \
-out ca.crt
~/certs$ openssl req -newkey rsa:2048 \
-nodes -keyout server.key \
-subj "/C=CN/ST=GD/L=SZ/O=Acme, Inc./CN=registry.example.com" -out server.csr
~/certs$ openssl x509 -req \
-extfile <(printf "subjectAltName=DNS:registry.example.com") \
-days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt
$ cd ..
이제 server.crt 인증서 파일을 시스템에 설치를 해주어야 합니다(인증서 파일을 설치하지 않으려면 --insecure-registry
옵션을 사용해야 합니다. 이 부분은 뒤에 따로 설명하겠습니다).
$ sudo mkdir -p /etc/docker/certs.d/registry.example.com
$ sudo cp certs/ca.crt /etc/docker/certs.d/registry.example.com/ca.crt
/etc/hosts에 도메인을 추가하고, 인증서 파일을 설치했으면 Docker 서비스를 재시작합니다. Docker 서비스를 재시작해야 추가된 도메인과 설치된 인증서가 적용됩니다.
$ sudo systemctl restart docker
Docker 레지스트리에 접속할 다른 시스템에도 server.crt 인증서 파일을 복사하여 같은 방식으로 설치를 하고 Docker 서비스를 재시작합니다. 그리고 도메인을 구입하지 않았다면 /etc/hosts에 레지스트리 서버(registry.example.com)의 IP 주소를 설정합니다.
--insecure-registry 옵션
server.crt 인증서 파일을 시스템에 설치하지 않으려면 Docker 데몬을 실행할 때 --insecure-registry
옵션을 사용해야 합니다.
$ sudo dockerd --insecure-registry registry.example.com
--insecure-registry
옵션에 Docker 레지스트리의 도메인을 설정합니다. 도메인 여러 개를 설정하려면 --insecure-registry 옵션을 여러 번 사용하면 됩니다.
보통 Docker 데몬을 직접 실행하지 않고 서비스 형태로 실행합니다. 이때는 /etc/docker/daemon.json 파일의 insecure-registries 부분을 다음과 같이 설정합니다(이 파일은 root 권한으로 수정해야 합니다).
{
"insecure-registries": ["registry.example.com"]
}
/etc/docker/daemon.json 파일을 수정했으면 Docker 서비스를 재시작합니다.
$ sudo systemctl restart docker
Docker 레지스트리에 접속할 다른 시스템에도 같은 방식으로 --insecure-registry 옵션을 사용하여 Docker 데몬을 실행합니다.
이제 사용자 계정과 비밀번호를 저장할 htpasswd 파일을 생성해야 합니다.
다음과 같이 auth/htpasswd 파일을 생성하고 hellouser라는 예제 사용자를 추가합니다. 비밀번호 입력 부분에는 사용할 비빌번호를 입력합니다.
$ mkdir auth
$ sudo docker run --entrypoint htpasswd --rm httpd:2 -Bbn hellouser hellopassword > auth/htpasswd
다음 명령을 실행하여 Docker 레지스트리 컨테이너를 생성합니다.
$ sudo docker run -d \
--name registry \
-v "$(pwd)"/auth:/auth \
-e REGISTRY_AUTH=htpasswd \
-e REGISTRY_AUTH_HTPASSWD_REALM="Registry Realm" \
-e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
-v "$(pwd)"/certs:/certs \
-e REGISTRY_HTTP_ADDR=0.0.0.0:443 \
-e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/server.crt \
-e REGISTRY_HTTP_TLS_KEY=/certs/server.key \
-p 443:443 \
registry:2
-v
옵션으로 auth, certs 디렉터리를 컨테이너의 /auth, /certs로 연결합니다.- REGISTRY_AUTH_HTPASSWD_PATH: 앞에서 생성한 htpasswd 파일을 설정합니다.
- REGISTRY_HTTP_TLS_CERTIFICATE, REGISTRY_HTTP_TLS_KEY: 앞에서 생성한 server.key, server.crt 파일을 설정합니다.
docker login
명령으로 https://registry.example.com에 로그인합니다. Username과 Password에는 htpasswd 명령으로 생성한 사용자(hellouser)와 비밀번호(hellopassword)를 입력합니다.
$ sudo docker login https://registry.example.com
Username: hellouser
Password: hellopassword
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Login Succeeded
docker login <Docker 레지스트리 URL>
형식입니다.
주의
도메인 설정이 귀찮다고 그냥 건너뛰고 IP 주소만 사용하면 로그인이 안 됩니다. 인증서에 설정한 도메인과 docker login 명령에 입력한 도메인이 반드시 일치해야 합니다. HTTPS 프로토콜은 IP 주소 접속을 허용하지 않으므로 구입하지 않은 도메인은 /etc/hosts 파일에 등록하여 사용합니다.
이제 앞에서 만든 hello:0.1 이미지를 개인 저장소에 올려보겠습니다.
$ sudo docker tag hello:0.1 registry.example.com/hello:0.1
$ sudo docker push registry.example.com/hello:0.1
The push refers to repository [registry.example.com/hello]
3fc4e1f2a517: Pushed
4d3d2ac7a313: Pushed
11fb43ff38a5: Pushed
7e6bc37fff8b: Pushed
7f5cbd8cc787: Pushed
0.1: digest: sha256:6b67420328f3527e518eccced8c24ac90e699b022545610f6e18bf873856eb8f size: 1367
이미지의 태그는 <Docker 레지스트리 URL>/<이미지 이름>:<태그>
형식으로 생성합니다. 우리는 registry.example.com으로 설정했으므로 registry.example.com/hello:0.1
이 됩니다.
다른 서버에서는 다음 명령을 실행하여 registry.example.com에 저장된 이미지를 받을 수 있습니다.
$ sudo docker pull registry.example.com/hello:0.1
저작권 안내
이 웹사이트에 게시된 모든 글의 무단 복제 및 도용을 금지합니다.- 블로그, 게시판 등에 퍼가는 것을 금지합니다.
- 비공개 포스트에 퍼가는 것을 금지합니다.
- 글 내용, 그림을 발췌 및 요약하는 것을 금지합니다.
- 링크 및 SNS 공유는 허용합니다.