1. 아무것도 없는 이미지(ubuntu, CentOS etc.)로 컨테이너 생성
2. Application을 위한 환경을 설치하고 소스코드 등을 복사해 잘 동작하는 것을 확인
3. Container를 이미지로 Commit
이 방법을 사용하면 Application이 동작하는 환경을 구성하기 위해 일일이 수작업으로 패키지를 설치하고 소스코드를 Git에서 Clone하거나 Host에서 복사해야 한다.
Docker는 위와 같이 일련의 과정을 손쉽게 기록하고 수행할 수 있는 Build 명령어를 제공한다.
완성된 Image를 생성하기 위해 Container에 설치해야 하는 패키지, 추가해야 하는 소스코드, 실행해야 하는 명령어와 Shell Script 등을 하나의 파일에 기롷해 두면 도커는 이 파일을 읽어 컨테이너에서 작업을 수행한 뒤 Image로 만든다.
이러한 작업을 기록한 파일을 Dockerfile
이라고 한다.
Dockerfile은 한 줄이 하나의 명령어가 되고, 명령어를 명시한 뒤에 옵션을 추가하는 방식이다.
명령어를 소문자로 표기해도 상관은 없지만 일반적으로 대문자
로 표기한다.
생성할 Image의 Base가 될 Image를 선언한다. From 명령어는 Dockerfile을 작성할 때 반드시 한 번 이상 사용된다.
FROM ubuntu:22.04
...
Image에 metadata를 추가한다. Metadata는 key:value
형태로 저장되며, 여러 개의 Metadata가 저장될 수 있다.
LABEL maintaner "chris han"
Image를 만들기 위해 Container 내부에서 명령어를 싱행한다.
이때 주의할 점은 Dockerfile을 Image로 빌드하는 과정에서는 별도의 입력이 불가능하기 때문에 apt-get install apache2와 같은 명령어를 사용할 때 Y/N을 Yes로 설정해야 한다. Image를 빌드할 때 별도의 입력을 받아야 하는 RUN이 있다면 build 명령어는 이를 오류로 간주하고 빌드를 종료한다.
파일을 Image에 추가한다. 추가하는 파일은 Dockerfile이 위치한 디렉터리인 Conext에서 가져온다.
ADD 명령어는 JSON 배열의 형태로 ["source", ... "dest"]
와 같이 사용할 수 있다. 추가할 파일명은 여러 개를 지정할 수 있으며 배열의 마지막 원소가 컨테이너에 추가될 위치이다.
...
ADD file1 /var/www/html/
ADD file2 /var/www/html/
...
OR
ADD ["file1", "file2", ..., "/var/www/html/"]
...
COPY는 Local Directory에서 읽어 들인 Context로부터 이미지에 파일을 복사한다. 사용방법은 ADD와 같다.
COPY와 ADD의 다른 점은, COPY는 Local의 파일만 Image에 추가할 수 있지만 ADD는 외부 URL 및 tar 파일에서도 파일을 추가할 수 있다.
명령어를 실행할 Directory를 선언한다. Bash shell에서 cd
명령어를 사용하는 것과 같은 기능이다.
예를 들어 WORKDIR을 /var/www/html로 선언하고 RUN touch test를 실행하면 /var/www/html/ Directory에 test파일이 생성된다. WORKDIR을 여러번 사용하면 cd명령어를 여러번 사용한 것과 같다.
WORKDIR /var
WORKDIR www/html
위 내용은 다음 내용과 같다.
WORKDIR /var/www/html
Dockerfile의 빌드로 생성된 이미지에서 노출할 포트를 선언한다. EXPOSE를 설정한 이미지로 컨테이너를 생성했다고 해서 반드시 이 포트가 Host의 포트와 바인딩되는 것은 아니며 단지 컨테이너의 80번 포트를 사용할 것임을 나타내는 것 뿐이다.
Dockerfile에서 사용될 환경변수를 지정한다. 설정한 환경변수는 ${ENV_NAME} 또는 $ENV_NAME 형태로 사용할 수 있다. 이 환경변수는 Dockerfile뿐 아니라 Image에도 저장되므로 빌드된 이미지로 컨테이너를 생성하면 환경변수를 사용할 수 있다.
# 다음은 con_str이라는 환경변수에 "Server=..." 값을 설정한다.
ENV con_str "Server=..."
build 명령어를 실행할 때 추가로 입력을 받아 Dockerfile 내에서 사용될 변수의 값을 설정한다.
# vi Dockerfile
FROM ubuntu:22.04
ARG my_arg
RUN touch ${my_arg}/my_touch
위 내용을 Dockerfile로 저장한 뒤 이미지를 빌드한다. build 명령어를 실행할 때 --build-arg
옵션을 사용해 Dockerfile의 ARG에 값을 입력할 수 있다.
$ docker build --build-arg my_arg=/home -t myarg:1.0 ./
ARG
와 ENV
의 값을 사용하는 방법은 ${}
로 같으므로 Dockerfile에서 ARG로 설정한 변수를 ENV에서 같은 이름으로 다시 정의하면 --build-arg 옵션에서 설정하는 값은 ENV에 의해 덮어쓰여진다.
USER로 컨테이너 내에서 사용될 사용자 계정의 이름이나 UID를 설정하면 그 아래의 명령어는 해당 사용자 권한으로 실행된다. 일반적으로 RUN으로 사용자의 그룹과 계정을 생성한 뒤 사용한다. root 권한이 필요하지 않다면 USER를 사용하는 것을 권장한다.
RUN groupadd -r author && useradd -r -g aothor chris
USER chris
CMD는 컨테이너가 시작될 때 시랳ㅇ할 명령어를 설정한다.
CMD ["/bin/bash"]
ENTRYPOINT도 CMD와 동일하게 컨테이너가 시작될 때 수행할 명령을 지정한다.
다른점은 ENTRYPOINT는 CMD를 인자로 받아 사용할 수 있는 스크립트의 역할을 할 수 있는 것이다.
ENTRYPOINT ["dotnet", "main.dll"]
JSON 배열 형태가 아닌 CMD와 ENTRYPOINT를 사용하면 실제로 이미지를 생성할 때 cmd와 entrypoint에 /bin/sh -c
가 앞에 추가된다.
CMD echo test
# -> /bin/sh -c echo test
ENTRYPOINT /entrypoint.sh
# -> /bin/sh -c /entrypoint.sh
반면에 JSON 배열 형태를 사용하면 다음과 같다.
CMD ["echo", "test"]
# -> echo test
ENTRYPOINT ["/bin/bash", "/entrypoint.sh"]
# -> /bin/bash /entrypoint.sh
일반적으로 Application을 빌드 할 때는 많은 의존성 패키지와 라이브러리를 필요로 한다.
예를 들어, Go로 작성된 소스코드를 빌드하기 위해서는 Go와 관련된 빌드 툴과 라이브러리가 미리 설치되어 있어야 한다.
하지만 이는 단지 빌드에 필요한 자원일 뿐 Application을 배포해서 사용하는데는 사용되지 않는다.
최종 이미지 사이즈를 줄이기 위해서 Multistage build 방법을 사용할 수 있다.
FROM golang
COPY 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"]
--from=0
은 첫 번째 FROM에서 빌드된 이미지의 최종 상태를 의미한다.