Docker #2. 도커 이미지와 컨테이너(images and containers)
Prog. Langs & Tools/Docker

Docker #2. 도커 이미지와 컨테이너(images and containers)

오늘은 도커 이미지와 컨테이너에 대해서 정리해 보고자 한다.

이번 내용은 도커 공식 사이트에서 도커를 설치하고 난 후 진행한다. 회원가입 및 설치는 그렇게 어렵지 않으니 아마 쉽게 할 수 있을 것이다. 설치를 마치고 나서 커맨드에 $ docker version을 입력후 엔터를 누르면 아래와 같이 나온다.

도커 이미지와 컨테이너는 도커를 이해하는데 가장 기본적인 개념이므로 확실하게 짚고 넘어가야 한다. 각각의 역할을 간단하게 정리하면 다음과 같다.

  • 도커 이미지 : 도커 컨테이너를 구성하는 파일 시스템과 실행할 어플리케이션 설정을 하나로 합친 것으로, 컨테이너를 생성하는 템플릿 역할을 한다.
  • 도커 컨테이너 : 도커 이미지를 기반으로 생성되며, 파일 시스템과 어플리케이션이 구체화되어 실행되는 상태이다.


간단한 실습을 하나 하면서 살펴보려고 한다. 간단한 웹 서버를 도커 컨테이너에서 실행해 보자. 디렉토리에 main.go 파일과 Dockerfile 파일을 하나씩 만들어 본다.

./main.go

./Dockerfile

방금 만든 서버 어플리케이션은 다음과 같은 기능을 갖는다.

모든 HTTP 요청에 대해 "Hello Docker!"라는 응답을 보낸다.

포트 8080으로 요청을 받는 서버 어플리케이션으로 동작한다.

클라이언트로 요청을 받으면 received request라는 메시지를 표준으로 출력한다.

방금 만든 Dockerfile을 통해서 main.go가 포함된 도커 이미지 파일을 만들 수 있다. 도커 이미지를 빌드하기 위해서 아래와 같은 명령어를 입력한다. -t 옵션은 이미지명을 지정할 때 사용하고, 여기서는 'example/echo'라는 이미지명을 사용했다.

$ docker image build -t example/echo:latest .

빌드를 실행하면 베이스 이미지를 내려받고 Dockerfile의 RUN, COPY 등의 인스트럭션이 순차적으로 실행됨을 알 수 있다. 이미지가 성공적으로 빌드되면 다음 명령어를 통해 빌드 된 이미지 목록을 출력할 수 있다.

$ docker image ls


이미지를 생성했으니 docker container run 명령으로 컨테이너를 실행해 보려고 한다.

$ docker container run example/echo:latest

이와 같이 명령을 입력하면 start server라는 로그가 출력될 것이다. 만약 여기에 옵션값으로 -d를 추가하면 백그라운드로 컨테이너를 실행하게 되어 해시값 형태의 컨테이너 id값이 출력되게 된다.

이미지와 마찬가지로 컨테이너도 현재 실행 중인 목록을 보고 싶다면 다음 명령어를 입력하면 된다.

$ docker container ls

또는

$ docker ps


앞서 설명한 포트 포워딩에 대해서 조금 더 설명해 보려고 한다. 도커 컨테이너는 가상환경이지만, 외부에서 보았을 때 독립된 하나의 머신처럼 다룰 수 있다는 특징이 있다. echo 어플리케이션은 8080 포트를 리스닝 하고 있지만, 이 포트는 컨테이너 포트라고 해서 컨테이너 안에 한정된 포트이다. 따라서 컨테이너 밖에서는 컨테이너 포트를 사용할 수 없다. 

이와 같이 HTTP 요청을 받는 어플리케이션을 사용하려면 컨테이너 밖에서 온 요청을 컨테이너 안에 있는 어플리케이션에 전달해 주어야 하는데, 이 역할을 담당하는 것이 바로 포트 포워딩(Port Forwarding)이다. 포트 포워딩이란 호스트 머신의 포트를 컨테이너 포트와 연결해 컨테이너 밖에서 온 통신을 컨테이너 포트로 전달한다. 이 덕분에 컨테이너 포트를 컨테이너 외부에서도 이용할 수 있다.

앞에서 실행한 컨테이너를 먼저 지우고 포트를 지정해서 컨테이너를 다시 실행시켜 본다

$ docker container stop $(docker container ls --filter "ancestor=example/echo" -q)

$ docker container run -d -p 9000:8080 example/echo:latest 

이제는 다음과 같이 요청을 보내면 'Hello Docker!'라는 응답이 정상적으로 나타날 것이다.

$ curl http://localhost:9000/


간단한 예제를 통해서 어플리케이션에서 이미지와 컨테이너를 어떻게 빌드하는지 알아보았다. 이제 이미지에 대해서 본격적으로 정리해 보려고 한다. 도커 이미지는 한 마디로 표현하면 도커 컨테이너를 만들기 위한 템플릿으로 볼 수 있다.

도커 이미지는 우분투와 같은 운영 체제로 구성된 파일 시스템은 물론, 컨테이너 위에서 실행하기 위한 어플리케이션이나 그 의존 라이브러리, 도구에 어떤 프로세스를 실행할지 등의 실행 환경에 설정 정보까지 포함하는 아카이브다. 이렇게 이미지를 빌드하기 위한 파일이 바로 Dockerfile이다. 이미지는 모든 OS를 포함하고 있지는 않다. 그리고 만들어진 이미지는 도커 엔진 이미지 캐시에 저장되어서 언제든지 빠르게 꺼내서 쓸 수 있다. 

이미지 관련해서 자주 사용되는 명령어들을 살펴본다.

  • docker image pull : 이미지 내려받기
    • 도커 레지스트리에서 도커 이미지를 내려 받으러면 docker image pull을 사용한다.
    • $ docker image pull [options] 레포지토리명[:태그명]
  • docker image push : 이미지를 외부에 공개하기
    • docker image push 명령은 현재 저장된 도커 이미지를 도커 허브 등의 레지스트리에 등록하기 위해 사용한다.
    • $ docker image push [options] 레포지토리명[:태그명]
  • docker image rm : 이미지를 지우기
    • docker image rm 명령은 현재 빌드된 이미지를 지울 때 사용한다. 아이디나 이미지명으로 지울 수 있다.
    • $ docker image rm [id 또는 이미지명]

이번에는 도커 컨테이너에 대해서 알아보고자 한다. 도커 컨테이너는 파일 시스템과 어플리케이션이 함께 담겨 있는 박스라고 보면 된다.  컨테이너가 어떻게 동작하는지를 알기 위해서는 먼저 생애 주기를 이해해야 한다. 컨테이너는 실행 중, 정지, 파기의 3가지 상태를 가진다. 이미지는 상태를 갖지 않는다는 점과 대조적이다. 방금 위해서 실습해본 docker container run 명령이가 컨테이너를 실행 중인 상태로 만드는 명령어이며, 정지시키는 명령어는 docker container stop, 파기는 docker container rm이다.

도커 컨테이너를 시작하는 명령어를 입력했을 때, 다음의 절차가 순서대로 진행되는 것으로 이해를 하면 된다.

  1. 이미지 캐시에서 로컬 이미지를 찾으려고 시도
  2. 로컬에서 찾을 수 없는 경우, 리모트 이미지 레포지토리(Docker Hub)를 바라봄
  3. 가장 최신 버전의 이미지를 다운 받고, 해당 이미지 기반 컨테이너를 생성
  4. 도커 엔진 내의 사설 IP에 가상 IP를 부여
  5. 호스트의 80 포트를 열고 컨테이너의 80 포트로 포워딩
  6. 이미지 Dockerfile의 CMD를 통해 컨테이너 시작

도커 컨테이너 실행 명령에서 자주 사용하는 옵션은 다음과 같다.

  • -i : 컨테이너를 실행할 때 컨테이너 쪽 표준 입력과의 연결을 그대로 유지
  • -t : 유사 터미널 기능 활성화. 보통 -i 옵션과 함께 사용하여 -it로 쓰는 경우가 많음
  • --rm : 컨테이너를 종료할 때 컨테이너를 파기하는 옵션
  • -v : 호스트와 컨테이너 간에 디렉터리나 파일을 공유하기 위해 사용하는 옵션

지금까지 이미지와 컨테이너의 기본개념 및 간단한 사용법을 살펴보았다. 방금까지는 단일 컨테이너를 다루었지만, 실제로 현업에서는 하나보다 많은 컨테이너를 사용하여 시스템을 구축한다. 그렇게 되면 컨테이너 간에 의존 관계가 생기며, 단일 컨테이너에서는 없었던 문제들이 생기기 시작한다. 따라서 이러한 설정 파일이나 환경 변수 등을 올바르게 전달하는 것이 중요해 지는데, 이럴 때 필요한 것이 docker-compose이다.

docker-compose는 yaml 포맷으로 기술된 설정 파일로, 여러 컨테이너의 실행을 한 번에 관리할 수 있게 해준다. 앞에서 살펴본 example/echo 예제를 docker-compose.yml 파일로 구성하면 다음과 같다.

./docker-compose.yml

이와 같이 설정을 하고 다음 명령어를 실행하면 컨테이너를 생성할 수 있다. 단일 컨테이너를 docker container run 했을 때와 같은 결과가 나온다.

$ docker-compose up

실행한 컨테이너를 정지하고 싶으면 아래 명령어를 입력하면 된다

$ docker-compose down

이와 같이 docker-compose를 사용하면 여러개의 컨테이너를 관리하는 것도 쉽고 빠르게 할 수 있게 된다.


참고자료

  1. <도커/쿠버네티스를 활용한 컨테이너 개발 실전 입문> 야마다 아키노리 저
  2. <Docker Mastery with Kubernetes and Swarm> Udemy course