[Docker] Dockerfile (1)
1. Dockerfile
1) Dockerfile
베이스가 될 도커 이미지, 도커 컨테이너에서 수행한 명령, 환경 변수 등의 설정, 도커 컨테이너 안에서 작동시켜둘 데몬을 도커상에서 작동시킬 컨테이너 구성 정보를 기술한 파일이다. 도커 빌드 명령은 Dockerfile에 기술된 정보를 바탕으로 도커 이미지를 생성한다.
2) 기본 문법
Dockerfile은 텍스트 형식의 파일로, 에디터 등을 사용하여 작성한다. 확장자는 필요없고 'Dockerfile'이라는 이름의 파일에 인프라 구성 정보를 기술한다. Dockerfile에서 이미지 빌드 시 파일명을 명시적으로 지정하여 다른 파일명으로도 동작할 수 있도록 할 수 있다.
명령 | 설명 |
FROM | 베이스 이미지를 지정 |
RUN | 명령 실행 |
CMD | 컨테이너 실행 명령 |
LABEL | 라벨 설정 |
EXPOSE | 포트 설정 |
ENV | 환경 변수 설정 |
ADD | 디렉토리 및 파일 추가 |
COPY | 파일 복사 |
ENTRYPOINT | 컨테이너 실행 명령 |
VOLUME | 볼륨 마운트 |
USER | 사용자 지정 |
WORKDIR | 작업 디렉토리 |
ARG | Dockerfile 파일 내의 변수 |
ONBUILD | 빌드 완료 후 실행되는 명령 |
STOPSIGNAL | 시스템 콜 시그널 설정 |
HEALTHCHECK | 컨테이너 헬스 체크 |
SHELL | 기본 쉘 설정 |
# | 주석 |
3) Dockerfile 작성
# 베이스 이미지 설정 (태그 생략 시 최신 버전 적용)
FROM [이미지명:태그명]
# 베이스 이미지 설정 (Docker Hub 식별자 적용)
# FROM [이미지명@[docker image ls --digest [다이제스트 이미지] 결과]]
# 베이스 이미지 설정 (태그 생략 시 최신 버전 적용)
FROM centos:centos7
# 베이스 이미지 설정 (Docker Hub 식별자 적용)
# FROM tensorflow/tensorflow@[docker image ls --digest tensorflow/tensorflow 결과]
4) Dockerfile로 도커 이미지 생성
이미지 생성 시 도커 리포지토리에서 베이스 이미지를 다운로드하기에 시간이 걸린다. 한번 다운 받은 베이스가 될 이미지는 다시 다운받지 않으므로 시간 제한 없이 바로 사용할 수 있다. (베이스 이미지와 새로 작성한 이미지의 IMAGE ID가 동일)
sudo docker build -t [생성할 이미지:태그명] [Dockerfile 위치]
Dockerfile이 아닌 다른 파일명으로 이미지를 생성할 경우 -f 옵션으로 명시적으로 지정할 수 있다. 하지만 Dockerfile 이외의 이름은 Docker Hub에서 이미지의 자동 생성 기능을 사용할 수 없다.
sudo docker build -t [생성할 이미지:태그명] -f [Dockerfile 위치]
표준 입력을 경유하여 Dockerfile을 지정하여 빌드할 경우 Dockerfile 내용을 인수로 전달하기에 '-'을 지정해야한다. 단, 빌드에 필요한 파일을 포함시킬 수 없기에 이미지 안에 파일을 추가할 수 없다. 따라서 빌드에 필요한 파일을 tar로 모아두고 표준 입력에서 지정해야한다.
sudo docker build - < Dockerfile
※ 도커는 이미지 빌드할 때 자동으로 중간 이미지를 생성한다. 그리고 다른 이미지를 빌드할 때 중간 이미지를 내부적으로 재이용하여 빌드 속도를 향상시킨다. 이미지를 재이용하고 있을 때는 빌드 로그에 'Using Cache'라고 출력이된다.
5) 도커 이미지 레이어 구조
Dockerfile을 빌드하여 도커 이미지를 작성하면 Dockerfile 명령별로 이미지를 작성한다. 작성된 여러 개의 이미지는 레이어 구조로 되어있다. 예를 들어 4개의 명령을 가진 Dockerfile로 이미지를 작성할 경우, 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;"]
작성된 이미지는 공통의 베이스 이미지를 바탕으로 여러 개의 이미지를 작성한 경우에는 베이스 이미지의 레이어가 공유되어 디스크의 용량을 효율적으로 이용한다.
2. 멀티스테이지 빌드
1) 샘플 앱 복제
애플리케이션 개발 환경에서 사용한 라이브러리나 개발 지원 툴이 제품 환경에서 반드시 사용되지는 않는다. 따라서 제품 환경에는 애플리케이션을 실행하기 위한 최소한의 실행 모듈만 배치하는 것이 보안 관점이나 리소스 활용점에서 좋다. 깃허브에 샘플 애플리케이션을 복제하여 구성한다.
git clone https://github.com/asashiho/dockertext2
cd dockertext2/chap05/multi-stage/
2) Dockerfile
깃허브에 등록된 Dockerfile은 두 개의 부분으로 나뉜다. 첫 번째는 개발 환경용 도커 이미지를 Go 1.8.4 베이스로 'builder'라는 이름을 붙여 생성한다. 개발에 필요한 버전을 설치하여 클론 파일에 있는 소스코드를 컨테이너 안으로 복사한다.
# 1. Build Image
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
COPY main.go .
RUN GOOS=linux go build -a -o greet .
두 번째로, 도커 이미지의 베이스 이미지로는 기본 Linux 명령들을 파일로 모아놓은 'busybox'를 사용한다. 'busybox'는 최소한의 Linux 쉘 환경을 제공하는 경우 이용한다. 이 후 도커 이미지로 빌드한 'greet'라는 이름의 실행 가능 바이너리 파일을 제품 환경용 도커 이미지로 복사한다.
# 2. Production Image
FROM busybox
WORKDIR /opt/greet/bin
# Deploy modules
COPY --from=builder /go/src/github.com/asashiho/dockertext-greet/ .
ENTRYPOINT ["./greet"]
3) 도커 이미지 빌드
'/docker/home/ubuntu/dockertext2/chap05/multi-stage' 위치에서 Dockerfile을 이용하여 greet라는 이름으로 이미지를 빌드한다.
sudo docker build -t greet .
이미지를 확인한 결과, 개발 환경 이미지인 'golang'의 경우 803MB, 제품 환경 이미지인 'greet'는 6.03MB('busybox' 1.23MB에 애플리케이션의 실행에 필요한 모듈만을 추가한 정도)임을 확인할 수 있다.
4) 도커 컨테이너 시작
제품 환경으로 만든 저용량 도커 이미지를 이용하여 컨테이너를 실행한다. (샘플 앱은 인수를 이용하여 인사를 반환하는 간단한 커맨드라인 툴)
sudo docker container run -it --rm greet test
sudo docker container run -it --rm greet --lang=es test
3. Dockerfile 명령 및 데몬 실행
1) 명령 실행
FROM 명령에서 지정한 베이스 이미지에서 애플리케이션과 미들웨어의 설치 및 설정을 할 때는 RUN 명령을 사용한다. Dockerfile을 작성 시 가장 많이 사용하는 명령이다. RUN 명령에는 Shell 형식으로 기술하거나 Exec 형식으로 기술할 수 있다. Exec 형식은 JSON 배열로 지정하며 쉘을 경우하지 않고 직접 실행한다.
RUN 명령은 '\'로 줄을 바꾸어 가독성을 향상시켜 한줄로 작성하는 것이 좋다.
# Shell 형식 Nginx 설치
RUN apt-get install -y install
# Exec 형식 Nginx 설치
RUN ["/bin/bash", "-c", "apt-get install -y nginx"]
# 베이스 이미지 설정
FROM ubuntu:latest
# RUN 명령
RUN echo Shell 형식입니다.
RUN ["echo", " Exec 형식입니다."]
RUN ["/bin/bash", "-c", "echo 'Exec bash형식입니다.'"]
2) 명령 실행 확인
sudo docker history [이미지명]
3) 데몬 실행
RUN 명령으로 생성한 이미지를 바탕으로 컨테이너 내부에 명령을 실행하기 위해 CMD 명령을 사용한다. Dockerfile에는 하나의 CMD 명령을 기술할 수 있다. 두 개 이상의 CMD 명령을 작성할 경우, 가장 마지막에 작성된 명령만이 수행된다. CMD도 Shell 형식으로 기술하는 방법, Exec 형식으로 기술하는 방법, ENTRYPOINT 명령의 파리미터로 기술하는 방법이 있다.
# Shell 형식 Nginx 실행
CMD nginx -g 'daemon off;'
# Exec 형식 Nginx 실행
CMD ["nginx", "-g", "daemon off;"]
예시로 웹 서버 80번 포트를 액세스하도록 지정하고 이미지를 생성한 뒤에 컨테이너를 시작할 때, CMD 명령을 사용하여 Nginx 데몬을 시작하도록 설정되어 있기에 80번 포트를 지정하여 가동시켜야한다.
FROM ubuntu:16.04
RUN apt-get -y update && apt-get -y upgrade
RUN apt-get -y install nginx
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
sudo docker container run -p 80:80 -d [이미지명]
ENTRYPOINT 형식에도 Shell과 Exec 형식으로 분리된다.
# ENTRYPOINT Shell 형식
ENTRYPOINT nginx -g 'daemon off;'
# ENTRYPOINT Exec 형식
ENTRYPOINT ["nginx", "-g", "daemon off;"]
CMD의 경우는 컨테이너 가동 시에 실행하고 싶은 명령을 정의해도 컨테이너 가동 시에 인수로 새로운 명령을 우선시한다. ENTRYPOINT 명령에서 지정한 명령은 반드시 컨테이너에서 실행되기에 명령 인수를 지정하고 싶을 때는 CMD 명령과 조합하여 사용한다. ENTRYPOINT 명령으로는 실행하고 싶은 명령 자체를 지정하고, CMD 명령으로는 그 명령의 인수를 지정하면 컨테이너 실행 시 기본 작동을 결정할 수 있다.
예시로, ENTRYPOINT로 top 명령을 실행할 경우 갱신 간격을 10초로 지정하면 컨테이너는 top 명령어를 반드시 실행한다. 하지만 여기서 CMD 명령에 top 명령을 수행 시 인수를 지정하여 실행이 가능하다. 즉, ENTRYPOINT는 기본값 설정이 가능하다고 생각하면 쉽다.
FROM ubuntu:16.04
ENTRYPOINT ["top"]
CMD ["-d", "10"]
// 기본값으로 명령 실행 (10초)
sudo docker container run -it [이미지명]
// 인수를 지정한 명령 실행 (2초)
sudo docker container run -it [이미지명] -d 2
4) 빌드 완료 후 실행되는 명령
ONBUILD 명령을 이용하여 그 다음 빌드에서 실행할 명령을 이미지 안에 설정할 수 있다. 예시로, 베이스 이미지를 만들고, 만든 베이스 이미지에서 ONBUILD 명령을 수행한 새로운 이미지를 작성할 수 있다. 즉, Dockerfile로 생성한 이미지를 베이스 이미지로 또 다른 Dockerfile을 빌드할 때 기술한다.
일반적으로 웹 시스템 구축 시 OS 설치 및 환경 설정 및 플러그인 설치 인프라가 구축된 베이스 이미지에서 이미지 안에 개발한 프로그램을 전개하는 명령(ADD, COPY)을 이용하여 ONBUILD 명령을 사용한다.
가장 먼저 웹 서버 실행 환경을 위한 베이스 이미지(Dockerfile.base)를 작성한다. ONBUILD 명령에는 웹 콘텐츠를 압축한 website.tar 파일을 추가하고 Dockerfile.base 파일로 베이스 이미지(baseImage)를 빌드한다.
FROM ubuntu:16.04
RUN apt-get -y update && apt-get -y upgrade
RUN apt-get -y install nginx
EXPOSE 80
ONBUILD ADD website.tar /var/www/html/
CMD ["nginx", "-g", "daemon off;"]
sudo docker build -t baseimage -f Dockerfile.base .
이 후 웹 서버용 이미지(Dockerfile)를 작성하고 빌드하여 웹 서버 실행 환경 이미지에 웹 콘텐츠가 추가된 새로운 이미지를 만들고 가동시켜준다.
FROM baseimage
sudo docker build -t webimage .
sudo docker container run -d -p 80:80 webimage
sudo docker image inspect --format={{ .Config.OnBuild }}" webimage
※ 개발 환경에서는 여러명이 작업하기에 한 사람이 Dockerfile을 작성하여 운영한다.
5) 시스템 콜 시그널 설정
컨테이너를 종료할 때에 송신하는 시그널을 설정하기 위해 STOPSIGNAL 명령을 사용한다. STOPSIGNAL 명령에는 시그널 번호 혹은 시그널명을 지정하여 사용할 수 있다.
STOPSIGNAL 9
STOPSIGNAL SIGKILL
6) 컨테이너 헬스 체크 명령
컨테이너 안의 프로세스가 정상적으로 작동하고 있는지 확인할 때 HEALTHCHECK 명령을 사용한다. HEALTHCHECK 명령을 통해 컨테이너의 상태를 테스트 및 확인 결과는 inspect 명령어로 확인할 수 있다.
HEALTHCHECK [옵션] CMD [명령]
옵션 | 설명 |
--interval=n | 헬스 체크 간격 (기본 30s) |
--timeout=n | 헬스 체크 타임아웃 (기본 30s) |
--retries=n | 타임아웃 횟수 (기본 3) |
// 5분 주기로 가동 중인 웹 서버의 메인 페이지를 3초안에 표시할 수 있는지 없는지를 확인할 경우
HEALTHCHECK --inteval=5m --timeout=3s CMD curl -f http://[URL] || exit 1
// 결과 확인
sudo docker container inspect [컨테이너명]
[참고] 완벽한 IT 인프라 구축을 위한 Docker (2판)