[Docker 이해하기] 5. Dockerfile을 사용한 코드에 의한 서버 구축

Titu·2021년 7월 22일
4

Docker

목록 보기
5/6
post-thumbnail

Docker에서는 인프라의 구성 관리를 'Dockerfile'로 기술한다. Dockerfile에는 베이스가 되는 이미지에 각종 미들웨어를 설치 및 설정하고, 개발한 애플리케이션의 실행 모듈을 전개하기 위한 애플리케이션 실행 기반의 모든 구성 정보를 기술한다.

이 장에서는 Dockerfile을 사용한 서버 구축 및 구성 관리의 방법에 대해 살펴본다.

5.1 Dockerfile을 사용한 구성관리

Dockerfile이란?

  • Dockerfile이란 인프라 구성을 기술한 파일, 즉, Docker 상에서 작동시킬 컨테이너의 구성 정보를 기술하기 위한 파일이다.

Dockerfile의 기본 구문

  • Dockerfile은 텍스트 형식의 파일로, 에디터 등을 사용하여 작성한다. 확장자는 필요 없으며, 'Dockerfile'이라는 이름의 파일에 인프라의 구성 정보를 기술한다.
  • 명령은 관례적으로 대문자로 통일하여 쓴다.
  • Dockerfile의 명령
    • FROM: 베이스 이미지 지정
    • RUN: 명령 실행
    • CMD: 컨테이너 실행 명령
    • LABEL: 라벨 설정
    • EXPOSE: 포트 익스포트
    • ENV:환경 변수
    • ADD: 파일/디렉토리 추가
    • COPY: 파일 보갓
    • ENTRYPOINT: 컨테이너 실행 명령
    • VOLUME: 볼륨 마운트
    • USER: 사용자 지정
    • WORKDIR: 작업 디렉토리
    • ARG: Dockerfile 안의 변수
    • ONBUILD: 빌드 완료 후 실행되는 명령
    • STOPSIGNAL: 시스템 콜 시그널 설정
    • HEALTHCHECK: 컨테이너의 헬스 체크
    • SHELL: 기본 쉘 설정

Dockerfile 작성

Dockerfile에는 'Docker 컨테이너를 어떤 Docker 이미지로부터 생성할지'라는 정보를 반드시 기술해야 한다. 이 이미지를 베이스 이미지라고 한다.

  • 베이스 이미지를 기술하는 FROM 명령
    • FROM [이미지명]
    • FROM [이미지명]:[태그명]
    • FROM [이미지명]@[다이제스트]
# CentOS를 베이스 이미지로 한 Dockerfile
FROM centos:centos7

# 다이제스트(Docker Hub에 업로드하면 자동으로 부여되는 식별자)를 지정한 Dockerfile
From tensorflow/tensorflow@sha256:788c345613ff6cfe617b911dda22b1a900558c28c75afe6c05f8fa0d02bd9811

# 다이제스트는 아래 명령어로 확인할 수 있다
$ docker image ls --digests tensorflow/tensorflow
REPOSITORY              TAG                 DIGEST                                                                    IMAGE ID            CREATED             SIZE
tensorflow/tensorflow   latest              sha256:788c345613ff6cfe617b911dda22b1a900558c28c75afe6c05f8fa0d02bd9811   1d932048a281        2 months ago        1.3GB

5.2 Dockerfile의 빌드와 이미지 레이어

Dockerfile을 빌드하면 Dockerfile에 정의된 구성을 바탕으로 Docker 이미지를 작성할 수 있다.

Dockerfile로부터 Docker 이미지 만들기

  • docker build -t [생성할 이미지명]:[태그명] [Dockerfile의 위치]
# Dockerfile 작성
$ mkdir sample && cd $_
$ touch Dockerfile

$ ls
Dockerfile
# Dockerfile 내용
FROM centos:centos7
# Dockerfile로부터 Docker 이미지 빌드
  # Dockerfile로부터 sample이라는 이미지를 작성
  # Dockerfile의 위치를 상대경로로 지정
$ docker build -t sample:1.0 ./
Sending build context to Docker daemon  2.048kB
Step 1/1 : FROM centos:centos7
centos7: Pulling from library/centos
2d473b07cdd5: Pull complete 
Digest: sha256:0f4ec88e21daf75124b8a9e5ca03c37a5e937e0e108a255d890492430789b60e
Status: Downloaded newer image for centos:centos7
 ---> 8652b9f0cb4c
Successfully built 8652b9f0cb4c
Successfully tagged sample:1.0

# 절대 경로 확인을 통해 Dockerfile의 위치를 절대 경로로 지정할 수도 있음
# $ pwd
# /root/sample
# $ docker build -t sample:1.0 /root/sample

# 베이스 이미지인 centos:centos7와 새로 생성된 sample:1.0 이미지가 만들어져 있는 것 확인
$ docker image ls
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
sample              1.0                 8652b9f0cb4c        8 months ago        204MB
centos              centos7             8652b9f0cb4c        8 months ago        204MB

# 베이스 이미지인 centos:centos7가 로컬 환경에 다운로드된 상태에서 새로운 이미지 작성
$ docker build -t sample:2.0 .
Sending build context to Docker daemon  2.048kB
Step 1/1 : FROM centos:centos7
 ---> 8652b9f0cb4c
Successfully built 8652b9f0cb4c
Successfully tagged sample:2.0

# 베이스 이미지와 새롭게 생성된 이미지의 IMAGE ID가 같은 것을 확인할 수 있다.
  # 이는 이미지로서는 각각 다른 이름이 붙어있지만 그 실체는 동일한 이미지라는 것을 뜻한다
$ docker image ls
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
centos              centos7             8652b9f0cb4c        8 months ago        204MB
sample              1.0                 8652b9f0cb4c        8 months ago        204MB
sample              2.0                 8652b9f0cb4c        8 months ago        204MB
  • Dockerfile에는 임의의 파일명을 붙일 수도 있다.
    • 단, 파일명이 Dockerfile 이외의 이름인 경우는 Docker Hub에서 이미지의 자동 생성 기능을 사용할 수 없다.
# Dockerfile.base라는 이름의 파일을 지정하여 docker build 명령 실행
$ docker build -t sample -f Dockerfile.base
  • 또한, 표준 입력을 통해 Dockerfile을 지정하여 빌드를 할 수도 있다.
    • 단, 이 경우는 ADD 명령으로 이미지 안에 파일을 추가할 수 없기 때문에, Dockerfile과 빌드에 필요한 파일을 tar로 모아두고 표준입력에서 지정한다.
# 표준 입력에서의 빌드
$ docker build - < Dockerfile

# 압축 아카이브로부터 이미지를 빌드
# 이미지 안에 추가하고 싶은 dummyfile과 Dockerfile을 하나로 모아놓은 압축 아카이브
$ tar tvfz docker.tar.gz
-rw-r--r-- root/root         0 2021-07-22 02:55 dummyfile
-rw-r--r-- root/root        21 2021-07-22 02:09 Dockerfile

$ docker build - < docker.tar.gz
Sending build context to Docker daemon     166B
Step 1/1 : FROM centos:centos7
 ---> 8652b9f0cb4c
Successfully built 8652b9f0cb4c

Docker 이미지의 레이어 구조

Dockerfile을 빌드하여 Docker 이미지를 작성하면 Dockerfile의 명령별로 이미지를 작성한다. 이렇게 작성된 여러 개의 이미지는 레이어 구조로 되어 있다. 작성된 이미지는 다른 이미지와도 공유되어, Docker에서는 디스크의 용량을 효율적으로 이용한다.


$ vim Dockerfile
# 4개의 명령을 갖고 있는 Dockerfile
# STEP:1 Ubuntu (베이스 이미지)
FROM ubuntu:latest

# STEP:2 Nginx 설치
RUN apt-get update && apt-get install -y -q nginx

# STEP:3 파일 복사
COPY index.html /usr/share/nginx/html/

# STEP:4 Nginx 시작
CMD ["nginx", "-g", "daemon off;"]
$ touch index.html

# 이미지의 레이어 구조를 확인할 수 있다.
$ docker build -t webap .
Sending build context to Docker daemon   12.8kB
Step 1/4 : FROM ubuntu:latest
 ---> c29284518f49			# >> 첫 번째 이미지 작성 
Step 2/4 : RUN apt-get update && apt-get install -y -q nginx
 ---> Using cache
 ---> 9bc4d211a191			# >> 두 번째 이미지 작성 
Step 3/4 : COPY index.html /usr/share/nginx/html/
 ---> 290bd588e421			# >> 세 번째 이미지 작성 
Step 4/4 : CMD ["nginx", "-g", "daemon off;"]
 ---> Running in cbfa49771587
Removing intermediate container cbfa49771587
 ---> 3354484cda91			# >> 네 번째 이미지 작성 
Successfully built 3354484cda91
Successfully tagged webap:latest

Dockerfile의 저장 위치

Dockerfile로부터 이미지를 만들 때 docker build 명령은 Dockerfile을 포함하는 디렉토리(서브디렉토리 포함)를 모두 Docker 데몬으로 전송한다.
예를 들어, 시스템의 루트 디렉토리를 소스 레포지토리로 사용한 경우에는 루트 디렉토리의 모든 콘텐츠를 Docker 데몬으로 전송하기 때문에 처리가 느려진다.
그래서 빈 디렉토리를 만들고 거기에 Dockerfile을 저장한 후 이미지를 작성하는 방법을 권장한다.
Docker의 빌드에 필요 없는 파일은 Dockerfile과 똑같은 디렉토리에 두지 않도록 주의하는 것이 좋다.

빌드에 불필요한 파일 제외(.dockerignore)

Docker에서 빌드하면 빌드를 실행한 디렉토리 아래에 있는 모든 파일이 Docker 데몬으로 전송된다. 그렇기 때문에 빌드에서 제외하고 싶은 파일이 있는 경우는 '.dockerignore'이라는 이름의 파일 안에 해당 파일명을 기술한다.
.dockerignore 파일을 설정해두면 빌드에 불필요한 파일이 전송되지 않기 때문에 처리 속도가 빨라진다. 따라서, 불필요한 파일은 Docker의 빌드 대상에서 제외하는 설정을 해 두는 것이 좋다.

$ mkdir sample
$ cd sample/
$ vim Dockerfile
  
  # Dockefile작성
  FROM ubuntu:latest
  Add dummyfile /tmp/dummyfile
  
$ touch Dummyfile
$ vim .dockerignore

  # .dockerignore 작성
  dummyfile

$ ls -la
total 16
drwxr-xr-x 2 root root 4096 Jul 22 11:37 .
drwx------ 5 root root 4096 Jul 22 11:37 ..
-rw-r--r-- 1 root root   49 Jul 22 11:36 Dockerfile
-rw-r--r-- 1 root root   10 Jul 22 11:37 .dockerignore
-rw-r--r-- 1 root root    0 Jul 22 11:35 Dummyfile

# sample이라는 이미지를 만든다.
  # 로그를 확인하면 .dockerignore 설정으로 dummyfile이 존재하지 않기 때문에 오류가 발생하는 것을 확인할 수 있다.
$ docker build -t sample .
Sending build context to Docker daemon  3.584kB
Step 1/2 : FROM ubuntu:latest
latest: Pulling from library/ubuntu
a31c7b29f4ad: Pull complete 
Digest: sha256:b3e2e47d016c08b3396b5ebe06ab0b711c34e7f37b98c9d37abe794b71cea0a2
Status: Downloaded newer image for ubuntu:latest
 ---> c29284518f49
Step 2/2 : Add dummyfile /tmp/dummyfile
ADD failed: stat /var/lib/docker/tmp/docker-builder197312635/dummyfile: no such file or directory

5.3 멀티스테이지 빌드를 사용한 애플리케이션 개발

애플리케이션 개발 시에 개발 환경에서 사용한 라이브러리나 개발 지원 툴이 제품 환경에서 반드시 사용되는 것은 아니다. 컴퓨팅 리소스의 효율적 관리 측면이나 보안 관점에서는, 애플리케이션을 실행하기 위해 최소한으로 필요한 실행 모듈만 제품 환경에 배치하는 것이 바람직하다.

  • 예제를 통해 멀티스테이지 빌드 기능을 사용하여 샘플 애플리케이션을 빌드해보기로 한다.

Dockerfile 작성

# 샘플 코드 다운로드
$ git clone https://github.com/asashiho/dockertext2
$ cd dockertext2/chap05/multi-stage/

$ cat Dockerfile 
# 1. Build Image (개발 환경용 Docker 이미지)
  # 개발용 언어 Go의 버전 1.13을 베이스 이미지로 하여 작성하고 builder라는 이름을 붙인다. 
FROM golang:1.13 AS builder

# Install dependencies
  # 개발에 필요한 버전을 설치한다.
WORKDIR /go/src/github.com/asashiho/dockertext-greet
RUN go get -d -v github.com/urfave/cli

# Build modules
  # 로컬 환경에 있는 소스 코드를 컨테이너 안으로 복사한다.
  # 이 소스코드를 go build 명령으로 빌드하여 greet이라는 이름의 실행 가능 바이너리 파일을 작성한다.
COPY main.go .
RUN GOOS=linux go build -a -o greet .

# ------------------------------
# 2. Production Image (제품 환경용 Docker 이미지)
  # 제품 환경용 Docker 이미지의 베이스 이미지로는 busybox를 사용한다.
  # (Busybox는 기본적인 Linux 명령들을 하나의 파일로 모아놓은 것으로, 최소한으로 필요한 Linux 쉘 환경을 제공하는 경우 이용한다.)
FROM busybox  
WORKDIR /opt/greet/bin

# Deploy modules
  # 개발용 환경의 Dokcer 이미지로 빌드한 greet이라는 이름의 실행 가능한 바이너리 파일을 제품 환경용 Docker 이미지로 복사한다.
  # 복사한 바이너리파일을 실행한다.
COPY --from=builder /go/src/github.com/asashiho/dockertext-greet/ .
ENTRYPOINT ["./greet"]

Docker 이미지의 빌드

# 작성한 Dockerfile을 바탕으로 Docker 이미지를 빌드한다.
$ docker build -t greet .
Sending build context to Docker daemon  3.584kB
Step 1/9 : FROM golang:1.13 AS builder
1.13: Pulling from library/golang
d6ff36c9ec48: Pull complete 
c958d65b3090: Pull complete 
edaf0a6b092f: Pull complete 
80931cf68816: Pull complete 
813643441356: Pull complete 
799f41bb59c9: Pull complete 
16b5038bccc8: Pull complete 
Digest: sha256:8ebb6d5a48deef738381b56b1d4cd33d99a5d608e0d03c5fe8dfa3f68d41a1f8
Status: Downloaded newer image for golang:1.13
 ---> d6f3656320fe
Step 2/9 : WORKDIR /go/src/github.com/asashiho/dockertext-greet
Removing intermediate container 138eec593243
 ---> ea0d83ac2224
Step 3/9 : RUN go get -d -v github.com/urfave/cli
 ---> Running in 5ed829250e46
github.com/urfave/cli (download)
github.com/cpuguy83/go-md2man (download)
Removing intermediate container 5ed829250e46
 ---> 6de782b5c20b
Step 4/9 : COPY main.go .
 ---> 4722e9861498
Step 5/9 : RUN GOOS=linux go build -a -o greet .
 ---> Running in eb1af930dfe2
Removing intermediate container eb1af930dfe2
 ---> 20e7307af3cb
Step 6/9 : FROM busybox
latest: Pulling from library/busybox
b71f96345d44: Pull complete 
Digest: sha256:0f354ec1728d9ff32edcd7d1b8bbdfc798277ad36120dc3dc683be44524c8b60
Status: Downloaded newer image for busybox:latest
 ---> 69593048aa3a
Step 7/9 : WORKDIR /opt/greet/bin
Removing intermediate container 8a43367b4076
 ---> 167d31e42934
Step 8/9 : COPY --from=builder /go/src/github.com/asashiho/dockertext-greet/ .
 ---> 90173ba17164
Step 9/9 : ENTRYPOINT ["./greet"]
 ---> Running in 96585b622bc2
Removing intermediate container 96585b622bc2
 ---> cfff5e33b6ec
Successfully built cfff5e33b6ec
Successfully tagged greet:latest

# 개발 환경용 이미지인 golang은 803MB이지만, 제품 환경용 이미지인 greet는 6.11MB라는 것을 알 수 있다.
$ docker image ls
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
greet               latest              cfff5e33b6ec        37 seconds ago      6.11MB
busybox             latest              69593048aa3a        6 weeks ago         1.24MB
golang              1.13                d6f3656320fe        11 months ago       803MB
  • 제품 환경에서는 부하에 따라 작동하는 컨테이너의 수가 바뀐다. 따라서 가능한 한 용량이 적은 이미지를 사용하면 시스템 전체의 컴퓨팅 리소스를 효율적으로 활용할 수 있다.

Docker 컨테이너의 시작

# 제품 환경용 Docker 이미지인 greet을 사용하여 컨테이너를 실행
$ docker container run -it --rm greet asa
Hello asa

$ docker container run -it --rm greet --lang=es asa
Hola asa

5.4 명령 및 데몬 실행

Docker 이미지를 만드려면 필요한 미들웨어를 설치하고 사용자 계정이나 디렉토리를 작성하는 등의 명령을 실행한다. 또한 이미지로부터 컨테이너를 생성했을 때 서버 프로세스 등을 데몬으로서 작동시킬 필요도 있다.

명령 실행(RUN)

FROM 명령에서 지정한 베이스 이미지에 대해 애플리케이션/미들웨어를 설치하거나, 환경 구축을 위한 명령을 실행할 때는 RUN 명령을 사용한다. 즉, RUN 명령은 이미지를 작성하기 위해 실행하는 명령을 기술한다.

  • RUN [실행하고 싶은 명령]
    • Shell 형식으로 기술
      • 도커 컨테이너 안에서 /bin/sh -c를 사용하여 명령을 실행했을 때와 동일하게 작동한다.
    • Exec 형식으로 기술
      • 쉘을 통하지 않고 직접 실행된다.
      • 다른 쉘을 이용하고 싶은 경우는 쉘의 경로를 지정한 후 명령을 지정한다.
# Shell 형식의 RUN 명령
RUN apt-get install -y nginx

# Exec 형식의 RUN 명령
RUN ["/bin/bash", "-c", "apt-get install -y nginx"]
$ vim Dockerfile

  # Dockerfile 작성
  FROM ubuntu:latest

  # RUN 명령 실행
  RUN echo 쉘 형식
  RUN ["echo", "Exec 형식"]
  RUN ["/bin/bash", "-c", "echo 'Exec 형식에서 bash 사용' "]

# Dockerfile로부터 이미지 생성
$ docker build -t run-sample .
Sending build context to Docker daemon  12.29kB
Step 1/4 : FROM ubuntu:latest
latest: Pulling from library/ubuntu
a31c7b29f4ad: Pull complete 
Digest: sha256:b3e2e47d016c08b3396b5ebe06ab0b711c34e7f37b98c9d37abe794b71cea0a2
Status: Downloaded newer image for ubuntu:latest
 ---> c29284518f49
Step 2/4 : RUN echo 쉘 형식
 ---> Running in eec307de2e22
쉘 형식
Removing intermediate container eec307de2e22
 ---> 03000f9fbbc6
Step 3/4 : RUN ["echo", "Exec 형식"]
 ---> Running in 07cdb29fac7c
Exec 형식
Removing intermediate container 07cdb29fac7c
 ---> 4bdfe8f6fbd9
Step 4/4 : RUN ["/bin/bash", "-c", "echo 'Exec 형식에서 bash 사용' "]
 ---> Running in 43cf04dc1715
Exec 형식에서 bash 사용
Removing intermediate container 43cf04dc1715
 ---> 6f616ac8c59d
Successfully built 6f616ac8c59d
Successfully tagged run-sample:latest

# 이미지를 생성할 때 어떤 명령이 실행되는지 확인
  # 실행 결과를 확인하면 shell 형식으로 작성한 명령은 /bin/sh, exec 형식으로 작성한 명령은 쉘을 통하지 않고 실행되는 것을 알 수 있다.
$ docker history run-sample
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
6f616ac8c59d        50 seconds ago      /bin/bash -c echo 'Exec 형식에서 bash 사용'           0B                  
4bdfe8f6fbd9        51 seconds ago      echo Exec 형식                                    0B                  
03000f9fbbc6        52 seconds ago      /bin/sh -c echo 쉘 형식                            0B                  

데몬 실행(CMD)

이미지를 바탕으로 생성된 컨테이너 안에서 명령을 실행하려면 CMD 명령을 사용한다.

  • CMD [실행하고 싶은 명령]
    • Shell 형식으로 기술
      • 도커 컨테이너 안에서 /bin/sh -c를 사용하여 명령을 실행했을 때와 동일하게 작동한다.
    • Exec 형식으로 기술
      • 쉘을 호출하지 않고 직접 실행된다.
    • ENTRYPOINT 명령의 파라미터로 기술
# Shell 형식의 CMD 명령
CMD nginx -g 'daemon off;'

# Exec 형식의 CMD 명령
CMD ["nginx", "-g", "daemon off;"]
$ vim Dockerfile

  # Dockerfile 작성
  FROM ubuntu:16.04

  # Nginx 설치
  RUN apt-get -y update && apt-get -y upgrade
  RUN apt-get -y install nginx

  # 포트 지정
  EXPOSE 80

  # 서버 실행
  CMD ["nginx", "-g", "daemon off;"]

# Dockerfile로부터 이미지 생성
$ docker build -t cmd-sample .

# CMD 명령을 사용하여 Nginx 데몬을 시작하도록 설정되어 있다.
# 따라서 아래 명령을 통해 80번 포트를 호스트 OS에 전송하면 컨테이너가 웹 서버로서 작동하고 있다는 것을 알 수 있다.
$ docker container run -p 80:80 -d cmd-sample                

데몬 실행(ENTRYPOINT)

ENTRYPOINT 명령에서 지정한 명령은 docker container run 명령을 실행했을 때 반드시 실행된다. (CMD 명령은 컨테이너 시작 시에 실행하고 싶은 명령을 정의해도 docker container run 명령 실행 시 파라미터로 새로운 명령을 지정한다면, 이것을 먼저 실행한다.) ENTRYPOINT 명령 시, 명령의 파라미터를 지정하고 싶다면 CMD 명령과 조합한다.

  • ENTRYPOINT [실행하고 싶은 명령]
    • Shell 형식으로 기술
      • 도커 컨테이너 안에서 /bin/sh -c를 사용하여 명령을 실행했을 때와 동일하게 작동한다.
    • Exec 형식으로 기술
      • 쉘을 호출하지 않고 직접 실행된다.
# Shell 형식의 ENTRYPOINT 명령
ENTRYPOINT nginx -g 'daemon off;'

# Exec 형식의 ENTRYPOINT 명령
ENTRYPOINT ["nginx", "-g", "daemon off;"]
$ vim Dockerfile

  # Dockerfile 작성
  FROM ubuntu:16.04

  # top 실행
  ENTRYPOINT ["top"]
  CMD ["-d", "10"]

$ docker build -t cmd-sample .

# CMD 명령에서 지정한 10초 간격으로 갱신하는 경우
$ docker container run -it sample

# 2초 간격으로 갱신하는 경우
$ docker container run -it sample -d 2

빌드 완료 후에 실행되는 명령(ONBUILD)

ONBUILD 명령은 자신의 Dockerfile로부터 생성한 이미지를 베이스 이미지로 한 다른 Dockerfile을 빌드할 때 실행하고 싶은 명령을 기술한다. 예를 들어, 웹 시스템을 구축할 때 OS 설치 및 환경 설정이나 웹 서버 설치 및 각종 플러그인 설치 등과 같은 인프라 환경 구축과 관련된 부분을 베이스 이미지로 작성하고, ONBUILD 명령으로 이미지 안에 개발한 프로그램을 전개하는 명령을 지정할 수 있다.

  • ONBUILD [실행하고 싶은 명령]

예제를 통해 ONBUILD 명령을 사용하여 웹 애플리케이션의 실행 환경을 구축해보기로 한다.

# 샘플 코드 다운로드
$ git clone https://github.com/asashiho/dockertext2
$ cd dockertext2/chap05/onbuild

$ cat Dockerfile.base
# 베이스 이미지 설정
FROM ubuntu:16.04

# Nginx 설치
RUN apt-get -y update && apt-get -y upgrade
RUN apt-get -y install nginx

# 포트 지정
EXPOSE 80

# 웹 콘텐츠 배치
ONBUILD ADD website.tar /var/www/html

# Nginx 실행
CMD ["nginx", "-g", "daemon off;"]

# 베이스 이미지 작성
$ docker build -t web-base -f Dockerfile.base .

$ cat Dockerfile
# Docker 이미지 취득
FROM web-base

# 웹 서버용 이미지 작성
$ docker build -t photoview-image .
Sending build context to Docker daemon  715.3kB
Step 1/1 : FROM web-base
# Executing 1 build trigger
 ---> ae929aa3d9b5
Successfully built ae929aa3d9b5
Successfully tagged photoview-image:latest

# 웹 서버용 컨테이너 시작
$ docker container run -d -p 80:80 photoview-image

# 브라우저에서 http://localhost/로 접속해서 로컬에 있는 애플리케이션이 전개되는 것을 확인한다.
# 아래 명령을 통해 이미지에 ONBUILD 명령이 설정되어 있는지 확인할 수 있다.
$ docker image inspect --format="{{.Config.OnBuild}}" web-base
[ADD website.tar /var/www/html/]

예제를 통해 인프라 구축과 관련된 이미지 작성과 애플리케이션 전개와 관련된 이미지 생성을 나누는 방법을 연습해보았다.

시스템 콜 시그널의 설정(STOPSIGNAL)

컨테이너를 종료할 때에 송신하는 시그널을 설정하는 명령이다.

  • STOPSIGNAL [실행하고 싶은 명령]
    • STOPSIGNAL 명령에는 시그널 번호(9 등) 또는 시그널명(SIGKILL 등)을 지정할 수 있다.

컨테이너의 헬스 체크 명령(HEALTHCHECK)

컨테이너 안의 프로세스가 정상적으로 작동하고 있는지를 체크하고 싶을 때 사용하는 명령이다.

  • HEALTHCHECK [옵션] CMD [실행하고 싶은 명령]
    • --interval=n 옵션을 지정하여 헬스 체크 간격을 설정할 수 있다. (기본값 30초)
    • --timeout=n 옵션을 지정하여 헬스 체크 타임아웃을 설정할 수 있다. (기본값 30초)
    • --retries=N 옵션을 지정하여 타임아웃 횟수를 설정할 수 있다. (기본값 3초)
# 웹 서버의 메인페이지(http://localhost/)를 3초 안에 표시할 수 있는지 없는지를 1분마다 확인하려면 Dockerfile에 아래 명령어를 지정해준다.
HEALTHCHECK --interval=1m --timeout=3s CMD Curl -f http://localhost/ || exit 1

# 헬스 체크의 결과는 docker container inspect 명령으로 확인한다.
$ docker container inspect web-server

5.5 환경 및 네트워크 설정

Dockerfile 안에서 환경변수와 컨테이너 안에서의 작업 디렉토리를 설정할 수 있다.

환경 변수 설정(ENV)

  • ENV [key] [value]

    • 단일 환경변수에 하나의 값을 설정. 첫번째 공백 앞이 key이면 그 뒤는 공백이나 따옴표와 같은 문자도 함께 문자열로 취급
  • ENV [key]=[value]

    • 한 번에 여러 개의 값을 설정
    • 변수 앞에 \를 추가하면 이스케이프 처리를 할 수 있다.
# key value로 지정하는 경우의 ENV 명령
ENV myName "daisy"
ENV myOrder cherry tangerine mango
ENV myNickname titu

# key=value로 지정하는 경우의 ENV 명령
ENV myName="daisy" \
	myOrder=cherry\ tangerine\ mango \
	myNickname=titu

ENV 명령으로 지정한 환경변수는 컨테이너 실행 시의 docker containe run 명령의 --env 옵션을 사용하여 변경할 수 있다.

작업 디렉토리 지정(WORKDIR)

Dockerfile에서 정의한 명령을 실행하기 위한 작업용 디렉토리를 지정하는 명령어다.

  • WORKDIR [작업 디렉토리 경로]
    • 지정한 디렉토리가 존재하지 않으면 새로 작성한다.
    • 상대 경로를 지정한 경우는 이전 WORKDIR 명령의 경로에 대한 상대 경로가 된다.
# 절대경로/상대 경로를 사용한 WORKDIR 명령
WORKDIR /first
WORKDIR second
WORKDIR third
RUN ["pwd"]		# /first/second/third
# WORKDIR 명령으로 환경변수를 사용한 예
ENV DIRPATH /first
ENV DIRNAME second
WORKDIR $DIRPATH/$DIRNAME
RUN ["pwd"]		# /first/second

사용자 지정(USER)

이미지 실행이나 Dockerfile의 명령을 실행하기 위한 사용자를 지정할 때 사용하는 명령이다.

  • USER [사용자명/UID]
    • USER 명령에서 지정하는 사용자는 RUN 명령에서 미리 작성해 놓을 필요가 있다.
# RUN 명령으로 사용자 작성 후, USER 명령으로 사용자 설정
RUN ["adduser", "titu"]
RUN ["whoami"]		# root
USER titu
RUN ["whoami"]		# titu

라벨 지정(LABEL)

이미지에 버전 정보나 작성자 정보, 코멘트 등과 같은 정보를 제공할 때 사용하는 명령이다.

  • LABEL <키 명>=<값>
# 이미지에 정보 설정
LABEL maintainer "titu"
LABEL title="webAp"
LABEL version="1.0"
LABEL description="This image is web application server"

# 위 명령을 바탕으로 Dockerfile을 빌드하여 생성한 label-sample이라는 이미지의 상세 정보를 확인하는 명령
$ docker image inspect --format="{{.Config.Labels}}" label-sample

포트 지정(EXPOSE)

컨테이너의 공개 포트 번호를 지정할 떄 사용하는 명령이다.

  • EXPOSE <포트 번호>
    • EXPOSE 명령은 도커에게 실행 중인 컨테이너가 listen하고 있는 네트워크를 알려준다. 또한 docker container run -p 옵션을 사용할 때 어떤 포트를 호스트에 공개할지 정의한다.
# 8080 포트 공개
EXPOSE 8080

Dockerfile 내 변수의 설정(ARG)

Dockerfile 안에서 사용할 변수를 정의할 때 사용하는 명령어다. ARG 명령을 사용하면 변수의 값에 따라 생성되는 이미지의 내용을 바꿀 수 있다.

  • ARG <이름>[=기본값]
    • 환경변수인 ENV와는 달리 ARG 변수는 Dockerfile 내에서만 사용할 수 있다.
# 변수 정의
ARG YOURNAME="titu"
RUN echo $YOURNAME

# Dockerfile을 빌드할 때 --build-arg 옵션을 지정하면 ARG 명령에서 지정한 변수에 값을 설정할 수 있다.
$ docker build . --build-arg YOURNAME=daisy

# 옵션을 지정하지 않을 경우에는 Dockerfile내에서 지정한 기본 값으로 전개된다.
$ docker build .

기본 쉘 설정(SHELL)

쉘 형식으로 명령을 실행 할 때 기본 쉘을 설정하는 명령이다.

  • SHELL ["쉘의 경로", "파라미터"]
    • SHELL 명령을 지정하지 않았을 때 Linux의 기본 쉘은 ["/bin/sh", "-c"]이다.
# 기본 쉘 지정
SHELL ["/bin/bash", "-c"]

# RUN 명령 실행
RUN echo hello

5.6 파일 설정

Dockerfile에서 파일을 다룰 때 사용하는 명령들이다.

파일 및 디렉토리 추가(ADD)

호스트상의 파일이나 디렉토리, 원격 파일을 Docker 이미지 안으로 복사할 때 사용하는 명령이다.

  • ADD <호스트의 파일경로> <Docker 이미지의 파일 경로>
  • ADD ["<호스트의 파일경로>" "<Docker 이미지의 파일 경로>"]
    • 호스트
      • 호스트의 파일 경로에는 와일드카드와 Go 언어의 filepath.Match 룰과 일치하는 패턴을 사용할 수 있다.
      • 호스트의 파일이 압축파일일 때는 디렉토리로 압축을 푼다.
    • 원격 파일
      • 이미지에 추가하고 싶은 파일이 원격 파일 url인 경우, 추가한 파일은 퍼미션이 600(사용자만 읽기 쓰기 가능)이 된다.
      • ADD 명령은 인증을 지원하지 않기 때문에 원격 파일의 다운로드에 인증이 필요한 경우는 RUN 명령에서 wget이나 curl 명령을 사용해야 한다.
      • 이미지 안의 파일 지정이 파일(마지막이 슬래시가 아님)일 때는 URL로부터 파일을 다운로드하여 지정한 파일명을 추가한다.
      • 이미지 안의 파일 지정이 디렉토리(마지막이 슬래시)일 때는 파일명은 URL로 지정한 것이 된다.
      • 원격 URL로부터 다운로드한 리소스는 압축이 풀리지 않으므로 주의해야 한다.
# 호스트상의 host.html파일을 이미지 안의 /docker_dir/에 추가하고 싶을 때
ADD host.html /docker_dir/

# [hos]로 시작하는 모든 파일 추가
ADD hos* /docker_dir/

# [hos]+임의의 한 문자 룰에 해당하는 파일 추가
ADD hos?.txt /docker_dir/

# /docker_dir/web에 host.html 복사
WORKDIR /docker_dir
ADD host.html web/

# 원격 파일 추가
ADD http://www.wings.msn.to/index.php /docker_dir/web

파일 복사(COPY)

이미지에 호스트상의 파일이나 디렉토리를 복사할 때 사용하는 명령이다. ADD 명령과의 차이점은 ADD 명령은 원격 파일의 다운로드나 아카이브의 압축 해제 등과 같은 기능을 갖고 있지만, COPY 명령은 단순히 파일을 복사하는 처리만 한다는데 있다.

  • COPY <호스트의 파일경로> <Docker 이미지의 파일 경로>
  • COPY ["<호스트의 파일경로>" "<Docker 이미지의 파일 경로>"]

볼륨 마운트(VOLUME)

이미지에 볼륨을 할당하는 명령이다. 컨테이너는 영구 데이터를 저장하는 데는 적합하지 않기 때문에, 영구 저장이 필요한 데이터는 컨테이너 밖의 스토리지에 저장하는 것이 적합하다. 영구 데이터는 Docker의 호스트 머신상의 볼륨에 마운트하거나 공유 스토리지를 볼륨으로 마운트 하는 것이 가능하다.

  • VOLUME ["/마운트 포인트"]
    • 지정한 이름의 마운트 포인트를 작성하고, 호스트나 그 외 다른 컨테이너로부터 볼륨의 외부 마운트를 수행한다.
# VOLUME 명령 예제
VOLUME /myvol
VOLUME /var/log /var/db
VOLUME ["/data"]
VOLUME ["/var/www", "/var/log/apache2", "/etc/apache2"]

본문은 'Asa Shijo, <완벽한 IT 인프라 구축을 위한 Docker>, 정보문화사(2020)' 를 참고하여 정리한 글입니다.

[참고: 완벽한 IT 인프라 구축을 위한 Docker]

profile
This is titu

1개의 댓글

comment-user-thumbnail
2022년 2월 26일

이렇게 완벽한 글이 있다니요..?! 괜찮으시다면 개인적으로 정리할 때 참고 자료로 써도 될까요?

답글 달기