[찍먹 Cloud Computing] 5. Docker Image, Container, Dockerfile
도커의 기본 사용법에 대해서 알아보자. 2021-08-02

Docker Basic

안녕하세요? JustKode 입니다. 오늘은 컨테이너 기술을 구현하기 위한 Docker의 사용법에 대해서 배워보는 시간을 가져 보도록 하겠습니다.

오리엔테이션에서 컨테이너 방식으로 프로세스를 격리하여 실행하는 방식이 대두 되기 시작 했다고 이야기 했었습니다. 커널을 공유함으로써 가상화에 많은 자원을 사용하지 않으며, 비슷한 종속성을 가진 컨테이너 끼리는 이미지 자원을 공유하여 최적화 합니다. 또한, 동일한 환경에서 프로세스가 실행 될 수 있도록 하면서 충돌이 일어나지 않도록 프로세스를 구성합니다.

출처: Docker 홈페이지

이러한 가상화 기술을 통해 우리는 프로세스 독립 실행을 통해, 어느 환경에서든 프로그램을 실행 할 수 있게 되었습니다. 하지만, 독립적인 OS를 실행 시킴으로써 오는 Overhead는 감안 해야 겠지만요. 그럼에도 우리는 컨테이너를 실행 함으로써, 로드 밸런싱, Health Check, 예측 가능한 실행 환경을 가져 갈 수 있다는 점은, 매우 큰 장점으로 작용 합니다.

Docker CLI

일단, Docker를 사용 하기 전, Docker CLI를 설치하여야 합니다. 운영 체제에 맞게, 아래 링크에 접속하여 설치 해 주세요.

Docker Image

먼저 Docker Image에 대해서 설명 드리겠습니다. Docker ImageDocker Container를 만들기 위한, 실행 파일 이라고 생각하면 됩니다. 우리는 Docker Image를 어디서 찾아 볼 수 있을까요? 우리는 이를 Docker Hub에서 찾아 볼 수 있습니다.

Docker Hub

저는 Nginx 이미지를 검색 하여 선택 해 보겠습니다. 그럼 다음과 같이 Nginx 이미지 관련 정보를 열람 할 수 있습니다.

Nginx 이미지

그 다음으로, 조금 밑에 내리다 보면 우리는 다음 이미지의 버전 정보를 확인 할 수 있습니다. 여기에 활성화 된 버전 정보를 이용하여, 우리는 이미지를 생성 할 수 있습니다.

Nginx 이미지 버전 정보

이제 Docker CLI에 접근 할 시간입니다. 터미널에서 docker --version을 입력 하여, 도커가 정상적으로 설치 되었는지 확인 합니다.

$ docker --version
Docker version 20.10.6, build 370c289

자, 우리는 일단 docker pull 명령어를 통해서 이미지를 가져 오도록 하겠습니다. 우리는 nginx의 특정 버전인 1.20을 Pull 해 보도록 하겠습니다.

$ docker pull nginx:1.20
1.20: Pulling from library/nginx
513c6babab2b: Pull complete 
91772348f9ab: Pull complete 
846cdab430d1: Pull complete 
a4a7291136de: Pull complete 
6b72e4ce657e: Pull complete 
e40ce1e39e7d: Pull complete 
Digest: sha256:cd108d06120a7c0d3d073ba4e3eee7b5e6759f0c8a80799e2dbb35d07388840c
Status: Downloaded newer image for nginx:1.20
docker.io/library/nginx:1.20

그 다음 Docker Image를 잘 가져 왔는지 확인 해 볼까요? docker image ls를 이용하여 현재 있는 Docker Image를 확인 할 수 있습니다.

$ docker image ls
REPOSITORY   TAG       IMAGE ID       CREATED       SIZE
nginx        1.20      807dc38084a8   11 days ago   126MB

Docker Container

이미지를 가져왔으니, 이제 이 이미지를 이용하여 Docker Container를 만들 차례 입니다. docker run [image]을 이용하여, 현재 가지고 있는 Docker Image를 이용하여, 컨테이너를 생성 할 수 있습니다.

$ docker run -d -p 8080:80 --name my_nginx nginx:1.20
5dfb45ecb86a8b4b51e76cda29c1dbfba3d0752a5fbf999f8fb1a1ae7ad37b1d

일단 주로 쓰이는 옵션에 대해서 설명 드리겠습니다.

  • run: 컨테이너를 실행 합니다.
  • -d: 컨테이너를 detached mode, 즉, 백그라운드에서 실행 한다는 뜻입니다.
  • -p 8080:80: 호스트의 8080번 포트를 컨테이너의 80번 포트에 mapping 한다는 뜻입니다.
  • --name my_nginx: --name 옵션을 이용해, 실행 할 컨테이너의 이름을 설정 합니다. 위 코드는 컨테이너의 이름my_nginx로 설정 합니다.
  • nginx:1.20: 저희가 사용할 이미지 입니다. 해당 이미지가 설치 되어 있지 않다면, 설치합니다.

우리는 이 과정을 통해 이미지를 간단하게 컨테이너로 실행 할 수 있었습니다. http://localhost:8080 에 접속하면 다음과 같은 화면을 볼 수 있습니다.

Nginx의 성공적 실행

docker ps를 입력 하면, 현재 실행 중인 컨테이너를 확인 할 수 있습니다.

CONTAINER ID   IMAGE        COMMAND                  CREATED         STATUS         PORTS                                   NAMES
5dfb45ecb86a   nginx:1.20   "/docker-entrypoint.…"   3 minutes ago   Up 3 minutes   0.0.0.0:8080->80/tcp, :::8080->80/tcp   my_nginx

만약 잠시 컨테이너의 프로세스는 종료 하면서, 스냅샷 (컨테이너 내 설정 및 파일 정버)을 남기고 싶을 땐, docker stop [컨테이너 명 | 컨테이너 아이디]을 이용 합니다.

$ docker stop my_nginx
my_nginx

docker ps -a를 이용하여, 모든 상태의 컨테이너를 확인 할 수 있습니다.

$ docker ps -a
CONTAINER ID   IMAGE        COMMAND                  CREATED          STATUS                          PORTS     NAMES
5dfb45ecb86a   nginx:1.20   "/docker-entrypoint.…"   15 minutes ago   Exited (0) About a minute ago             my_nginx

다시 실행 하기를 원하신다면, docker start [컨테이너 명 | 컨테이너 아이디]를 이용 합니다.

$ docker start my_nginx
my_nginx
$ docker ps
CONTAINER ID   IMAGE        COMMAND                  CREATED       STATUS          PORTS                                   NAMES
5dfb45ecb86a   nginx:1.20   "/docker-entrypoint.…"   5 hours ago   Up 12 seconds   0.0.0.0:8080->80/tcp, :::8080->80/tcp   my_nginx

만약, 프로세스 실행 상태를 저장하면서 잠깐 일시 정지, 다시 실행을 하고 싶을 때는 위의 방식과 비슷하게 각각 docker pause [컨테이너 명 | 컨테이너 아이디], docker unpause [컨테이너 명 | 컨테이너 아이디]를 입력 하면 됩니다.

더 이상 컨테이너가 필요 하지 않게 되었다면, docker kill [컨테이너 명 | 컨테이너 아이디]을 이용하여 제거 할 수 있습니다.

$ docker kill my_nginx
my_nginx
$ docker ps -a
CONTAINER ID   IMAGE        COMMAND                  CREATED        STATUS                       PORTS     NAMES

Docker Container Access

우리는 Docker Container에 어떻게 Access 할 수 있을까요? 우리는 이를 docker exec -it [컨테이너 명 | 컨테이너 아이디] [실행 할 파일]을 이용해서 컨테이너 내부 파일에 접근 할 수 있습니다.

다시 docker run -d -p 8080:80 --name my_nginx nginx:1.20 를 입력 해 주신 다음, docker exec -it my_nginx /bin/bash를 입력 해 주세요. 그럼 다음과 같이 실행 중인 컨테이너 내로 접속 할 수 있습니다.

$ docker exec -it my_nginx /bin/bash
root@5dfb45ecb86a:/#

DockerFile

이런 기존의 이미지로 만든 컨테이너를 직접 실행 한 다음, 컨테이너에 접속해서 환경 설정을 직접 해도 되지만, 이런 식으로 하게되면, 배포가 쉽다는 컨테이너의 장점을 이용하지 못하게 됩니다. 이때, 우리는 어떻게 할 수 있을까요?

이럴 때는, DockerFile 을 이용하면 됩니다. DockerFile은 어떻게 이미지를 구현 할 건지에 대한 내용들을 구성 할 수 있습니다. Django 서버를 여는 예제를 통해서 같이 보겠습니다. 일단 실습을 하기 전 아무 폴더나 만든 후에 다음을 작성해 주세요.

첫 번째로 Dockerfile을 만드는 예제 입니다. 만드신 폴더에 Dockerfile 이라는 이름으로 파일을 만든 후, 다음을 입력 해 주세요.

  • DockerFile
FROM python:3.9
EXPOSE 8000

ENV mode="development"
COPY /app /app
WORKDIR /app

RUN pip install -r requirements.txt \
    && django-admin startproject mysite . \
    && python manage.py makemigrations \
    && python manage.py migrate

CMD python manage.py runserver 0.0.0.0:8000

두 번째는 파이썬 의존성 정보를 담을 requirements.txt 입니다. 예시 이므로, app 폴더를 만들어, 해당 이름에 django만 넣도록 하게 하겠습니다.

  • app/requirements.txt
django

각 명령어의 역할 입니다.

  • FROM: DockerFileDocker Image로 빌드 시, 기반이 되는 Docker Image를 설정 합니다.
  • EXPOSE: 컨테이너의 어느 포트를 외부로 노출 시킬 것인지 설정 합니다.
  • ENV: 환경 변수를 설정 합니다. 키="값" 형태로 삽입 합니다.
  • COPY: 첫 번째 인자로 들어간 폴더를, 두 번째 인자로 들어간 위치에 복사 합니다.
  • WORKDIR: 현재 사용 폴더를 해당 폴더로 이동 시킵니다.
  • RUN: 빌드를 하기 위해 필요한 명령어를 작성합니다. 여기에는 apt-get, npm, pip 등의 명령어를 삽입하여, Docker Image를 만드는데 필요한 의존성 파일들을 설치하고, 사전 실행이 필요한 명령어를 사용 하는데 많이 사용합니다.
  • CMD: docker run을 통해 실행 했을때, 기본적으로 실행 될 명령어를 입력 합니다.

이렇게 입력 한 후, docker build를 통해, 해당 폴더에 있는 Dockerfile에 작성 한 내용대로, Docker Image Build를 실행 합니다. -t 옵션을 통해 이미지의 이름을 설정 해 줄 수 있습니다.

$ docker build -t mydjango .

그 다음 만들어진 이미지를 이용해, docker run 으로, 컨테이너를 생성 후 실행 합니다.

$ docker run -d -p 8080:8000 mydjango

http://localhost:8080 에 접속하면 다음과 같은 화면을 볼 수 있습니다.

Django 서버의 성공 적 실행

마치며

이렇게 Docker에 대해서 간단하게 다루어 보는 시간을 가졌습니다. 다음 시간에는, Container Orchestration을 위한 Kubernetes에 대해서 배워 보는 시간을 가져 보도록 하겠습니다!