목차
1. 이미지 생성 방법 비교
2. Dockerfile 작성
3. Dockerfile 빌드
4. 기타 Dockerfile 명령어
5. Dockerfile로 빌드할 때 주의할 점
1. 이미지 생성 방법 비교
작업한 컨테이너로 이미지 만들기
- 아무것도 존재하지 않는 이미지(우분투, Cent OS 등)로 컨테이너 생성
- 애플리케이션을 위한 환경을 설치하고 코드를 복사해 동작 확인
- 컨테이너를 이미지로 커밋
장점 : 이미지 동작 보장
단점 : 수작업으로 패키지를 설치하고 코드를 복제해야함
도커 파일 활용하기
완성된 이미지를 생성하기 위한 명령어와 정보를 담은 파일을 작성한다.
빌드 명령어는 Dockerfile을 읽어 이미지를 생성한다.
도커 파일을 사용하면 수작업 번거로움을 덜 수 있고, 깃과 같은 개발 도구를 통해 애플리케이션 빌드 및 배포를 자동화할 수 있다.
패키지 설치 및 이미지 생성을 자동화하여 쉬운 배포가 가능하다.
도커 파일에는 다음 내용이 포함되어야 한다.
- 컨테이너에 생성해야하는 패키지
- 추가해야하는 소스코드
- 실행해야하는 명령어와 셸 스크립트
2. Dockerfile 작성
간단한 웹 서버 이미지 생성을 위한 도커 파일을 작성해야 한다.
우분투 기본 이미지에 아파치 웹 서버를 설치하고, 로컬의 test.html 파일을 웹 서버로 접근할 수 있는 컨테이너 디렉터리인 /var/www/html에 복사한다.
FROM ubuntu:14.04
LABEL "purpose"="practice"
LABEL maintainer "mingadinga <email>"
RUN apt-get update
RUN apt-get install apache2 -y
ADD test.html /var/www/html
WORKDIR /var/www/html
RUN ["/bin/bash", "-c", "echo hello >> test2.html"]
EXPOSE 80
CMD apachectl -DFOREGROUND # 컨테이너 생성시 아파치 웹서버 실행
도커 명령어 알아보기
- FROM : 베이스 이미지
- LABEL : 메타데이터, 키 밸류
- RUN : 이미지를 만들기 위해 컨테이너 내부에서 실행할 명령어
- WORKDIR : 디렉터리로 cd
- ADD : 도커파일이 위치한 디렉터리인 컨텍스트에서 파일을 가져와 이미지의 디렉터리에 추가
- EXPOSE : 노출 포트 설정. 호스트 포트와 바인딩하는 것 아님.
- CMD : 컨테이너가 시작될 때 한번 실행하는 명령어. docker run 커맨드 인자보다 우선순위 낮음
3. Dockerfile 빌드
기본 빌드
docker build -t mybuild:0.0 ./ # 이미지, 도커파일이 저장된 경로(컨텍스트)
[+] Building 26.6s (11/11) FINISHED
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 316B 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for docker.io/library/ubuntu:14.04 0.0s
=> [1/6] FROM docker.io/library/ubuntu:14.04 0.0s
=> [internal] load build context 0.0s
=> => transferring context: 41B 0.0s
=> [2/6] RUN apt-get update 17.7s
=> [3/6] RUN apt-get install apache2 -y 8.5s
=> [4/6] ADD test.html /var/www/html 0.0s
=> [5/6] WORKDIR /var/www/html 0.0s
=> [6/6] RUN ["/bin/bash", "-c", "echo hello >> test2.html"] 0.2s
=> exporting to image 0.1s
=> => exporting layers 0.1s
=> => writing image sha256:423ea3c59b5c5ed4d7de52540132733c79454c2347d69 0.0s
=> => naming to docker.io/library/mybuild:0.0 0.0s
위에서 만든 이미지로 컨테이너를 실행하자.
-P 옵션은 EXPOSE 명령어로 노출된 포트를 호스트에서 사용 가능한 포트에 차례로 연결한다.
docker run -d -P --name myserver mybuild:0.0
docker port myserver
# 80/tcp -> 0.0.0.0:32768
빌드 컨텍스트
빌드 컨텍스트는 이미지를 생성하는데 필요한 각종 파일, 소스코드, 메타데이터 등을 담고 있는 디렉터리이다.
Dockerfile이 위치한 디렉터리가 빌드 컨텍스트이다.
이미지 빌드를 시작하면 도커는 가장 먼저 빌드 컨텍스트를 읽는다.
컨텍스트에는 이미지 빌드에 필요한 파일만 있는 것이 바람직하다.
컨텍스트에서 특정 파일을 이미지에서 제외하기 위해 .dockerignore 파일을 작성한다.
minhwi@minhwiui-MacBookPro ~/dockerfile vi .dockerignore
test2.html
*.html
*/*.html
test.htm?
!test*.html # test로 시작하는 html은 포함
Dockerfile로 컨테이너 생성하고 커밋하기
build 명령어는 Dockerfile에 기록된 대로 컨테이너를 실행한 뒤 완성된 이미지를 만든다.
도커 파일에 기록된 명령어를 실행할 때마다 새로운 컨테이너가 하나씩 생성되며 이를 이미지로 커밋한다.
그래서 이미지의 빌드가 완료되면 dockerfile의 명령어 줄 수만큼 레이어가 존재하고, 중간에 컨테이너도 같은 수만큼 생성되고 삭제된다.
FROM ubuntu:14.04
LABEL maintainer "mingadinga <email>"
RUN apt-get update
RUN apt-get install apache2 -y
이런 도커 파일이 있을 때 어떤 과정을 거쳐 이미지가 빌드되는지 살펴보자.
- 베이스 이미지인 우분투를 풀 받아서 가져온다.
- 베이스 이미지의 컨테이너 생성, LABEL 명령어를 실행하고 변경사항을 이미지로 커밋한다.
- 2번 이미지의 컨테이너 생성, RUN 명령어를 실행하고 변경사항을 이미지로 커밋한다.
- 3번 이미지의 컨테이너 생성, RUN 명령어를 실행하고 변경사항을 이미지로 커밋한다.
총 4개의 레이어가 존재한다.
캐시를 이용한 이미지 빌드
이미지 빌드를 마치고 난 뒤 다시 같은 빌드를 진행하면 이미지 빌드에서 사용했던 캐시를 사용한다.
즉, 도커파일의 동일한 명령어에 대해 이전에 생성했던 이미지 레이어를 재활용한다.
# Dockerfile
FROM ubuntu:14.04
LABEL "purpose"="practice"
LABEL maintainer "mingadinga <email>"
RUN apt-get update
RUN apt-get install apache2 -y
ADD test.html /var/www/html
WORKDIR /var/www/html
RUN ["/bin/bash", "-c", "echo hello >> test2.html"]
EXPOSE 80
CMD apachectl -DFOREGROUND # 컨테이너 생성시 아파치 웹서버 실행
# Dockerfile로 빌드
docker build -t mybuild:0.0 ./
# Dockerfile2
FROM ubuntu:14.04
LABEL "purpose"="practice"
LABEL maintainer "mingadinga <email>"
RUN apt-get update
# Dockerfile2로 빌드
docker build -f Dockerfile2 -t mycache:0.0 ./
[+] Building 0.1s (6/6) FINISHED
=> [internal] load build definition from Dockerfile2 0.0s
=> => transferring dockerfile: 145B 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 89B 0.0s
=> [internal] load metadata for docker.io/library/ubuntu:14.04 0.0s
=> [1/2] FROM docker.io/library/ubuntu:14.04 0.0s
=> **CACHED** [2/2] RUN apt-get update 0.0s
=> exporting to image 0.0s
=> => exporting layers 0.0s
=> => writing image sha256:aa2de0581394d74cfb86d5acfc3facbcedb2c16de527c 0.0s
=> => naming to docker.io/library/mycache:0.0 0.0s
캐시의 기준은 명령어 변경 여부이므로, git clone 명령어로 소스코드를 복사해오는 경우
캐시 옵션을 사용하면 푸시하더라도 새로운 소스코드를 받아오지 않는다.
따라서 캐시를 사용하지 않으려면 build 명령어에 —no-cache 옵션을 추가한다.
docker build --nocache -t mybuild:0.0 ./
멀티 스테이지를 이용한 빌드
실제 실행 파일은 작지만, 빌드에 필요한 패키지와 라이브러리가 불필요하게 이미지의 크기를 차지하고 있을 때
이미지의 크기를 줄이기 위해 사용하는 빌드 방법이다.
하나의 Dockerfile 안에 여러개의 FROM 이미지를 정의하여 최종 생성되는 이미지의 크기를 줄일 수 있다.
프로그램 실행을 위해 필요한 요소만 포함하는 리눅스 배포판인 alpine이나 busybox와 같은 이미지를 사용한다.
경량화된 애플리케이션 이미지를 간단하게 생성할 수 있다.
베이스 이미지를 하나만 사용하여 Hello World을 출력하는 Go 프로그램 이미지를 만드는 Dockerfile을 작성했다. 이미지의 크기가 773mb이다.
FROM golang
ADD main.go /root
WORKDIR /root
RUN go build -o /root/mainApp /root/main.go
CMD ["./mainApp"]
minhwi@minhwiui-MacBookPro ~/hello_go docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
go_helloworld latest 3ae5f3348a5c 12 seconds ago **773MB**
베이스 이미지를 여러개 사용해 동일한 기능을 하는 경량 이미지를 만들 수 있다. 이미지가 훨씬 가벼워졌다.
FROM golang
ADD main.go /root
WORKDIR /root
RUN go build -o /root/mainApp /root/main.go
FROM alpine:latest
WORKDIR /root
COPY --from=0 /root/mainApp .
CMD ["./mainApp"]
docker build . -t go_helloworld
REPOSITORY TAG IMAGE ID CREATED SIZE
go_helloworld multi-stage 0caa710d5b34 8 seconds ago **9.3MB**
go_helloworld latest 3ae5f3348a5c 2 minutes ago 773MB
4. 기타 Dockerfile 명령어
- ENV : 환경변수 지정. 이미지에도 저장됨
- VOLUME : 컨테이너 생성시 호스트와 공유할 컨테이너 내부 디렉터리 설정
- ARG : build 명령어의 인자를 받아 Dockefile 내에서 사용할 변수 값 설정
- USER : 컨테이너 내부 실행의 사용자 권한 설정
- ONBUILD : 하위 이미지가 Dockerfile로 생성될 때 실행할 명령어 추가. 명령어를 상속
- STOPSIGNAL : 컨테이너가 정지될 때 사용될 시스템 콜의 종류
- HEALTHCHECK : 컨테이너의 애플리케이션 상태 체크
- SHELL : 도커파일에서 사용하는 셸 지정
- COPY : 컨텍스트로부터 로컬 파일만 이미지에 추가
- ADD : 외부 URL 및 tar 파일에서도 파일 추가
- CMD : 컨테이너가 시작될 때 실행할 명령어 설정
- ENTRYPOINT : 커맨드를 인자로 받아 이미지 내의 스크립트 파일을 실행
5. Dockerfile로 빌드할 때 주의할 점
Dockerfile을 작성하는 좋은 습관
- 하나의 명령어는 \로 나눠서 가독성을 높인다.
- .dockerignore로 불필요한 파일은 빌드 컨텍스트에서 제외한다.
- 빌드 캐시로 이미지 레이어를 재사용한다.
도커 이미지 구조와 Dockerfile 관계
RUN이 하나의 이미지 레이어가 된다.
여러개의 RUN 명령어가 하나로 묶일 수 있다면 이미지 레이어의 개수와 전체 용량을 줄일 수 있다.
여러개의 RUN 명령어를 하나의 RUN으로 줄이자.
FROM ubuntu:14.04
#RUN mkdir /test
#RUN fallocate -l 100m /test/dummy
#RUN rm /test/dummy
RUN mkdir /test && \\
fallocate -l 100m /test/dummy && \\
rm /test/dummy
이미지의 레이어 구조 활용해 저장공간 절약하기
애플리케이션들이 동일한 라이브러리를 사용한다면, 애플리케이션 이미지에 라이브러리를 각기 설치하기보다는
라이브러리 이미지를 미리 만들고 이 이미지로 애플리케이션 이미지를 생성해 공유할 수 있다.
또 도커 이미지를 배포할 때 애플리케이션에 해당하는 변경된 부분만 내려받으면 되므로 이미지를 빠르게 전송할 수 있다.
만약 다른 사람이 빌드한 이미지에 불필요한 이미지 레이어가 있다면 컨테이너를 생성해 단일 이미지를 만들어 크기를 줄일 수 있다. 하지만 이전 이미지에 저장된 이미지 설정 정보는 잃는다.