주요글: 도커 시작하기

컨테이너 시작과 중지

도커를 사용한다는 것은 결국 이미지를 이용해서 컨테이너를 생성하고 실행한다는 것이다. docker run 명령어를 사용하면 컨테이너를 실행할 수 있다. 다음 명령어를 보자.

$ docker run -p 8080:80 nginx:latest

이 명령어를 실행하면 hello-world를 실행했을 때와 다르게 nginx 이미지를 실행하면 컨테이너가 구동된 채로 대기한다. 여기서 -p 옵션은 도커를 실행하는 호스트의 8080 포트를 컨테이너의 80 포트로 연결하도록 설정한다. nginx 이미지는 내부적으로 80 포트를 사용해서 웹 서버를 구동하는데 호스트의 8080 포트와 컨테이너의 80 포트를 연결한 것이다.

컨테이너가 실행 중인 상태에서 웹 브라우저를 띄워 http://호스트:8080에 연결해보자. 다음과 같이 nginx 웹 서버의 응답 화면을 볼 수 있다.

docker 명령어를 실행한 콘솔에는 아래와 같이 접근 로그가 출력될 것이다.

vagrant@ubuntu-bionic:~$ docker run -p 8080:80 nginx:latest 
192.168.1.1 - - [21/Sep/2019:14:32:33 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36" "-"

docker 명령어를 실행한 콘솔에서 Ctrl+C키를 누르면 컨테이너를 종료한다. nginx 이미지는 "nginx" 서버를 포그라운드로 실행하는데 Ctrl+C를 누르면 nginx 서버가 종료된다. 컨테이너를 구동할 때 실행한 프로그램이 종료되면 컨테이너도 함께 종료된다. nginx 이미지의 경우 실행하는 프로그램이 nginx 서버이므로 nginx 서버가 종료되면 컨테이너도 함께 종료된다.

이제 컨테이너를 백그라운드로 실행해보자. 아래와 같이 -d 옵션을 사용한다. -d 옵션은 컨테이너의 프로그램을 실행할 때 터미널에 연결하지 않는다.

vagrant@ubuntu-bionic:~$ docker run -d -p 8080:80 nginx:latest
7d78d9bf30ad21ad021a737886f517cb6cae98cd7b0535307dc583c32bd53e54
vagrant@ubuntu-bionic:~$

명령어 실행 결과로 7d78d9bf로 시작하는 문자열을 출력했는데 이 문자열은 컨테이너를 구분할 때 사용할 고유 ID이다.

컨테이너가 실행중이므로 웹 브라우저에서 http://호스트:8080에 연결하면 응답이 표시된다. docker container -ls 명령어를 사용해서 실제 실행중인 컨테이너 목록을 확인할 수 있다. (docker container 대신 docker ps 명령어를 사용해도 된다.)

vagrant@ubuntu-bionic:/vagrant$ docker container ls
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                  NAMES
7d78d9bf30ad        nginx:latest        "nginx -g 'daemon of…"   4 minutes ago       Up 4 minutes        0.0.0.0:8080->80/tcp   goofy_chaplygin

CONTAINER ID에 표시된 값이 앞서 출력된 컨테이너 ID의 앞 부분과 같은 것을 알 수 있다. STATUS는 Up인데 이는 컨테이너가 실행 중임을 뜻한다.

실행 중인 컨테이너를 중지하려면 docker stop 명령어를 사용하면 된다.

docker stop 7d78d9

docker stop 명령어 뒤에는 컨테이너를 구분할 수 있는 값을 입력한다. 여기서는 7d78d9를 입력했는데 고유 ID 전체가 아니라 다른 컨테이너와 구분되는 앞 글자 일부만 입력해도 된다.

컨테이너 이름

docker container ls -a 명령어를 입력해보자. ls 명령어에 -a 옵션을 붙이면 실행 중인 컨테이너뿐만 아니라 종료된 컨테이너도 모두 표시한다. 결과를 보면 nginx:latest 이미지를 이용해 여러 컨테이너가 생성된 것을 알 수 있다. 여기서 NAMES 값은 도커가 임의로 생성한 컨테이너 이름이다.(비슷하게 docker ps -a 옵션을 사용해도 모든 컨테이너 목록을 보여준다.)

vagrant@ubuntu-bionic:~$ docker container ls -a
CONTAINER ID        IMAGE               COMMAND                  ...생략 NAMES
7d78d9bf30ad        nginx:latest        "nginx -g 'daemon of…"   ...생략 goofy_chaplygin
7cf417035656        nginx:latest        "nginx -g 'daemon of…"   ...생략 condescending_albattani
346023f2afbc        nginx:latest        "nginx -g 'daemon of…"   ...생략 focused_heyrovsky
bb62c718f7a1        nginx:latest        "nginx -g 'daemon of…"   ...생략 peaceful_cerf
f2033c7f6be9        nginx:latest        "nginx -g 'daemon of…"   ...생략 unruffled_greider
dc5e650a1d70        centos:7            "/bin/bash"              ...생략 fervent_cartwright
420592ad5d1e        hello-world         "/hello"                 ...생략 youthful_lovelace
6441f60ab2e2        hello-world         "/hello"                 ...생략 youthful_lamarr

--name 옵션을 사용하면 컨테이너 이름을 직접 지정할 수 있다. 다음은 --name 옵션의 사용 예를 보여준다. --name 값이 web인 컨테이너를 실행한 뒤에 컨테이너 목록을 보면 NAMES 값이 web인 것을 알 수 있다.

vagrant@ubuntu-bionic:~$ docker run -d --name web -p 8080:80 nginx:latest
b91561be1280c8601efab3aa1edb4373b6a3beafc2507df1fc7f274fba341905
vagrant@ubuntu-bionic:~$ docker ps
CONTAINER ID        IMAGE               ...생략  NAMES
b91561be1280        nginx:latest        ...생략  web

컨테이너를 중지할 때는 컨테이너 ID뿐만 아니라 이름을 사용해도 된다.

vagrant@ubuntu-bionic:~$ docker stop web
web

이름이 web인 컨테이너를 주징했다면 다시 같은 이름의 컨테이너를 생성해보자. 다음과 같이 생성에 실패한다.

vagrant@ubuntu-bionic:~$ docker run -d --name web -p 8080:80 nginx:latest
docker: Error response from daemon: Conflict. The container name "/web" is already in use
by container "b91561be1280c8601efab3aa1edb4373b6a3beafc2507df1fc7f274fba341905". 
You have to remove (or rename) that container to be able to reuse that name.
See 'docker run --help'.

컨테이너 ID와 마찬가지로 컨테이너 이름도 고유하기 때문에 같은 이름의 컨테이너를 중복해서 생성할 수 없다.

컨테이너 삭제

매번 동일한 이름으로 컨테이너를 생성해야 한다면 컨테이너를 삭제하고 다시 생성하면 된다. docker rm 명령어를 사용하면 된다. docker rm 명령어에 컨테이너 ID(구분되는 일부)나 이름을 지정하면 해당 컨테이너를 삭제한다.

vagrant@ubuntu-bionic:~$ docker rm web
web

또는 컨테이너를 실행할 때 --rm 옵션을 줄 수도 있다. 이 옵션을 사용하면 컨테이너가 종료될 때 컨테이너를 자동으로 삭제한다.

vagrant@ubuntu-bionic:~$ docker run -d --rm --name web -p 8080:80 nginx:latest

 

컨테이너 상태

컨테이너는 크게 다음의 상태를 가진다.

docker run 명령어는 실제로 다음의 세 명령어를 한 번에 실행하는 것과 같다.

  • docker pull : 이미지를 다운로드한다. docker run 명령어는 이미지가 로컬에 없으면 이미지를 다운로드한다.
  • docker create : 이미지로부터 컨테이너를 생성한다.
  • docker start : 컨테이너를 시작한다.

인터랙티브 컨테이너

컨테이너를 구동하고 컨테이너를 위한 가상 터미널을 할당할 수도 있다. 다음 명령어를 실행해보자.

vagrant@ubuntu-bionic:~$ docker run -it centos:7 /bin/bash
[root@ebe53b326516 /]#

이 명령은 centos:7 이미지를 이용한 컨테이너를 생성하고 컨테이너의 /bin/bash 명령을 실행한다(이미지 이름 뒤에 실행할 명령어를 지정하지 않으면 이미지를 생성할 때 지정한 명령어를 기본으로 실행한다). 여기서 -it 옵션이 중요하다. -i 옵션은 --interactive와 같은 옵션으로 컨테이너의 표준입력을 연결한다. -t는 -tty와 같은 옵션으로 컨테이너를 위한 가상 터미널을 할당한다. 즉 -it 옵션을 이용하면 해당 프로그램을 실행하고 컨테이너에 터미널로 연결한다. 이제 컨테이너 내부에서 ls나 ps와 같은 명령을 사용해서 컨테이너 내부를 확인할 수 있다.

exit 명령어를 bash를 종료하므로 컨테이너가 종료된다.

docker exec는 실행 중인 컨테이너에서 특정 명령을 실행할 때 사용하는데 이를 사용하면 실행 중인 컨테이너에 터미널로 붙을 수도 있다. 다음은 실행 예이다.

vagrant@ubuntu-bionic:~$ docker exec -it web /bin/bash
root@1227ce888767:/#

관련 글

도커는 소프트웨어를 빌드하고 실행하기 위한 소프트웨어다. 도커를 사용하면 웹 서버, 명령행 프로그램 등의 소프트웨어를 설치하고, 출시하고, 실행하고, 삭제하는 과정을 단순화할 수 있다. 이를 위해 도커는 OS의 컨테이너 기술을 사용한다.

docker run hello-world 실행 과정 보기

도커 시작하기 0 : 우분투에 도커 설치하기에서 docker run hello-workd 명령어를 실행했는데 이 명령어를 처음 실행할 때 표시되는 메시지는 다음과 같다.

vagrant@ubuntu-bionic:~$ docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
1b930d010525: Pull complete
Digest: sha256:b8ba256769a0ac28dd126d584e0a2011cd2877f3f76e093a7ae560f2a5301c00
Status: Downloaded newer image for hello-world:latest

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

...생략

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

첫 번째 실행하면 "Hello form docker!" 문장이 출력되기 이전에 'hello-world:latest' 이미지를 로컬에서 찾을 수 없어 다운로드했다는 메시지를 볼 수 있다.

docker run hello-world 명령어를 다시 실행해보자.

vagrant@ubuntu-bionic:~$ docker run hello-world

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

이번에는 이미지를 다운로드하지 않는다. hello-world는 도커가 소프트웨어를 배포할 때 사용하는 단위인 이미지의 이름으로 docker run 명령을 이용해서 이미지를 실행하면 도커는 다음 과정을 거친다.

docker run 명령어 실행 과정

도커는 실행할 이미지가 로컬에 존재하는지 확인한다. 존재하지 않으면 이미지를 먼저 다운로드한다. 이미지 파일은 도커 허브라는 곳에 위치하며 도커 허브에서 해당 이미지 파일을 다운로드한다. 다운로드가 끝나면 이미지에서 컨테이너를 생성하고 실행한다. 프로그램 파일이 있고 그 프로그램을 실행하면 프로세스가 생기는 것처럼 이미지 파일이 있고 이 이미지를 실행하면 컨테이너가 생성된다.

docker images 명령어를 실행하면 로컬에 설치된 이미지를 표시한다. 실행하면 다음과 같이 hello-world:latest 이미지가 존재하는 것을 확인할 수 있다.

vagrant@ubuntu-bionic:~$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
hello-world         latest              fce289e99eb9        8 months ago        1.84kB

이번에는 docker container ls -a 명령어로 컨테이너 목록을 확인하자. 아래와 같이 두 개의 컨테이너가 표시되는 것을 알 수 있다. docker run 명령어는 실행할 때마다 컨테이너를 생성하므로 docker run hello-world 명령어를 실행한 횟수만큼 컨테이너가 만들어졌다.

vagrant@ubuntu-bionic:~$ docker container ls -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                      PORTS               NAMES
420592ad5d1e        hello-world         "/hello"            24 minutes ago      Exited (0) 24 minutes ago                       youthful_lovelace
6441f60ab2e2        hello-world         "/hello"            39 minutes ago      Exited (0) 39 minutes ago                       youthful_lamarr

 

컨테이너

컨테이너 대한 소개는 https://www.docker.com/resources/what-container 문서를 참고한다. 이 문서는 컨테이너를 다음과 같이 설명하고 있다.

Containers are an abstraction at the app layer that packages code and dependencies together. Multiple containers can run on the same machine and share the OS kernel with other containers, each running as isolated processes in user space. Containers take up less space than VMs (container images are typically tens of MBs in size), can handle more applications and require fewer VMs and Operating systems.

 

컨테이너와 VM 차이

어플리케이션을 구동하는데 필요한 의존은 컨테이너 안에 포함된다. 한 컨테이너에 포함된 의존은 다른 컨테이너에 영향을 주지 않는다. 예를 들어 app 1은 lib 1 버전이 필요하고 app 2는 lib 2 버전이 필요하다고 하자. 한 OS에서 app 1과 app 2를 함께 실행하려면 lib 1과 lib 2를 모두 설치해야 한다. 만약 lib 2를 설치하면 lib 1이 비정상 동작한다면 한 OS에서 app 1과 app 2를 함께 구동할 수 없게 된다. 컨테이너를 사용하면 이런 문제가 발생하지 않는다. 컨테이너는 서로 격리된 환경에서 구동되므로 라이브러리 버전 충돌이 발생하지 않는다.

컨테이너는 격리된 환경에서 돌아가므로 한 컨테이너의 어플리케이션에 문제가 발생하더라도 OS나 다른 컨테이너에 주는 영향을 최소화할 수 있다.

도커, 컨테이너

도커는 이 컨테이너를 사용하는데 필요한 도구를 제공한다. 도커는 cgroup과 네임스페이스에 대한 자세한 이해가 없어도 컨테이너를 사용할 수 있게 만들어 주었다. 이런 이유는 도커는 출시 이후 빠르게 컨테이너를 위한 대세 기반 기술로 자리 잡았다.

관련 글

+ Recent posts