도커 이미지를 빌드할 내용을 하나의 파일로 정의 해놓는 형태
- 사용시 명확히 어떤 프로그램의 어떤 버전을 썼는지 명시할 수 있어 추후 유지보수 / 재설치 할 때 편하다.
사용법
빈 디렉토리 안에 Dockerfile 이라는 이름의 파일을 만들어 내용을 작성하고
docker build -t {name}:{version} ./
위의 명령어를 통해 빌드하면 로컬에 이미지가 생성된다.
Dockerfile을 특정 파일로 명시하고 싶다면 -f {Dockerfile Name} 옵션을 추가하면 가능하다.
- 도커를 빌드하는 과정에서 컨텍스트가 맨위의 디렉토리로부터 하위디렉토리까지 모든 파일을 읽어들이는데, 이과정에서 메모리 초과나 권한 에러가 뜰 수 있으니 위치와 도커파일은 하나씩 하는게 좋아보인다. 아니면 gitignore 처럼 .dockerignore 파일을 최상단 디렉토리에 작성하여 읽어들이지 않도록 할 수 있다.
# .dockerignore
test2.html # 특정파일 선택
*.html # *-> .html로 끝나는 모든 파일
*/*.html # 아무디렉토리로 접근한 후 그안에 있는 html 파일 전부 제회
test.html? # ?->한글자
- 도커파일을 빌드할때 캐시정보를 남기는데, 이 캐시정보를 활용하면 빠르게 빌드가 가능하다. 캐시정보는 변경된 Dockerfile 지점부터 빌드를 시작할 수 있도록한다. 사용하고 싶지 않으면 --no-cache 옵션을 통해 빌드하면 처음부터 빌드를 한다.
- 도커파일은 빌드할 때 매 명령어 라인마다(백슬래시로 구분하면 하나의 명령어로 침) 새로운 컨테이너를 다시 생성하고 빌드하고 삭제하고를 반복하는데 이러한 특성은 리소스를 많이 소모하거나 실수를 유발하는 단점으로 작용할 수도 있지만, 잘 활용하면 장점이 된다. 예를 들어 A, B 애플리케이션이 500MB의 크기의 같은 라이브러리를 사용해야 한다면 각 애플리케이션의 이미지에 라이브러리를 각기 설치하기보다 500MB 크기의 라이브러리 이미지를 미리 만들어 놓고 활용하면 용량을 절약할 수 있다. 또, 변경된 부분만 빌드하면 되므로 빠르게 빌드가 가능하다.
Dockerfile 작성법
Dockerfile에는 다양한 명령어들을 집어넣어 실행할 수 있다.
명령어 | 기능 |
FROM | 생성할 이미지의 베이스가 될 이미지를 뜻한다. Dockerfile을 작성할 때 반드시 한번 이상 입력해야 하며, 이름은 docker run 당시의 이미지 이름 포맷과 똑같이 입력하면 된다.(ex> ubuntu:14.04) 이미지가 없다면 자동으로 pull해서 가져온다. |
MAINTAINER | 이미지를 생성한 개발자의 정보에 대해서 작성하는 칸이다. 하지만 지금은 보통 잘 사용하지않고 LABEL로 전부 통합한다고 한다. |
LABEL | 이미지에 대한 메타데이터를 추가하는 주석같은 존재 |
RUN | 이미지를 만들때 컨테이너 내부에서 실행할 명령어를 작성하는 칸 (shell 명령어 작성과 똑같이 하면 됨 |
WORKDIR | 명령어를 실행할 디렉토리를 변경할 때 사용하는 명령어 cd 라고 생각하면 편하다. |
EXPOSE | 사용해야되는 포트를 나타내는 구간인데, 주의해야할 점은 실제로 포트가 바인딩 되는 것은 아니다. 실제로 포트를 바인딩 하려면 -p {port}를 하거나 -P 옵션을 사용하면 EXPOSE 포트가 전부 바인딩 된다. |
ENV | Dockerfile에서 사용될 환경변수를 지정한다. 실제로 docker run 됬을 때에도 유지가 된다. |
VOLUME | 빌드된 이미지로 컨테이너를 생성했을 때, 호스트와 공유할 컨테이너 내부의 디렉토리를 지정한다. |
ARG | Dockerfile에서 "빌드할 때" 사용할 환경변수를 지정한다. 빌드 이외에는 사용할 수 없다. |
USER | 이미지를 빌드할 때, 파일을 복사하거나 무언가 행위를 할 때의 유저를 변경한다. VOLUME을 연결할때나 특정 권한을 제한할때 꼭 사용해야한다. 만일 권한을 정하지 않으면 root로 기본 설정이 되므로 조금 위험할수도 있다. |
Onbuild | 이 이미지가 빌드되고 난 이후 다른 이미지가 빌드할 때 이 이미지를 참고할 때에만 실행되도록 명령어를 설정한다. |
Stopsignal | 컨테이너가 정지될 때 사용될 시스템 콜의 종류를 지정한다. |
Healthcheck | 이미지로부터 생성된 컨테이너에서 동작하는 애플리케이션의 상태를 체크하도록 설정한다. |
Shell | Dockerfile에서 기본적으로 사용하는 셸은 리눅스에서는 "/bin/sh -c", 윈도우에서는 "cmd /S /C"이지만, 다른 셸을 사용하고 싶다면 해당 명령어로 바꿀 수 있다. |
ADD | 특정 파일을 컨텍스트로부터 이미지에 추가할 때 사용하는 명령어 (COPY와 다른점은 외부 URL로 부터 파일을 추가할 수도 있고 tar파일에서도 파일을 추가할 수 있다. 압축이 해제되어서 전송된다. 도커의 컨테이너 레이어를 최대한 덜 사용하면서 하는게 리소스적인 측면에서 좋은데 압축을 해제해서 전송하므로 명령어 실행이 1번 줄어 좋은 점도 있지만 URL로 부터 파일을 추가한다는 것이 어떤 파일이 추가될지 모르는 위험성도 있어 조심히 써야한다. |
COPY | ADD와 마찬가지로 특정 파일을 컨텍스트로부터 이미지에 추가할 때 사용하는 명령어. 호스트의 파일만 추가가능 |
ENTRYPOINT | 컨테이너가 시작될 때(run) 실행되는 명령어로써 CMD와는 차이점은 특정 옵션으로 변경하지 않는한 Overwirting 되지 않는다. CMD와 함께 {명령어(entrypoint)} {인자(cmd)} 로 보통 쓰인다. |
CMD | ENTRYPOINT와 같지만 최종적으로 하나만 실행되며, 시작할 당시에 맨위에 인자로 작성하여 Overwrite할 수 있다. |
Dockerfile로 빌드할 때 주의할 점
- 도커파일이 변경되었을때 로컬에 빌드한 캐시가 남아있다면 변경지점부터 재빌드를 시작하는데 이를 활용하는것이 좋다. 하지만 조심해야할것이 git clone 같은 동작은 변경사항이 아니지만 실제 리포지토리에는 코드가 변경이 되었을 경우 캐시가 이미 빌드한것으로 착각해 원하는 결과가 나오지 않을 수 있다. 이럴때는 --no-cache 옵션을 통해 빌드시에 캐시를 쓰지 않거나 하는 방법을 사용해야한다.
- Dockerfile을 사용할 때, 예를들어 fallocate라는 명령어를 통해 tmp 디렉터리를 만들고 그다음에 추가 명령어로 제거할경우 도커는 매 명령어 마다 컨테이너를 생성삭제를 반복하기때문에 용량이 불어날 수 있다. 그러므로 이러한 경우에는 && 를 통해 한 컨테이너 안에서 연속적으로 명령어가 실행되게 하는 것이 바람직하다.
- Dockerfile을 작성할 때, 백슬래시로 apt-get을 사용하여 가독성이 좋게 작성하면 좋다.
...
RUN apt-get install package-1 \
package-2 \
package-3 \
...
- CMD 또는 ENTRYPOINT에 설정하려는 명령어를 일반 배열형태로 작성하거나 JSON 형태로 작성할 수 있는데 둘의 차이점은 일반배열의 경우 모든 명령어 앞에 셸 실행 명령어가 붙고 JSON 형태의 경우 맨앞에 한번만 붙는다.
- 이전에 구성해놓은 이미지를 컨테이너로 생성하고 해당 컨테이너를 export 하여 이미지를 import 하게 되면 용량적인 측면에서 이로울 수 있다. 다만, 이전에 이미지에 저장되어있던 각종 이미지 설정은 잃어버리게 된다. (이미지 커맨드 명령어, ENTRYPOINT 등)
멀티스테이지
- 용량적인 측면에서 크게 이득을 볼 수 있는 기법인데, 예를 들어 go 언어 파일을 빌드하기위해 빌드파일들을 설치하는데, golang 이미지를 그대로 가져오게 되면 빌드 이외의 불필요한 파일들이 너무 많이 깔리게 된다. 이럴때 멀티스테이징을 하여 용량을 축소시킬 수 있다.
FROM golang
ADD main.go /root
WORKDIR /root
RUN go build -o /root/mainApp /root/main.go
FROM golang
ADD main2.go /root
WORKDIR /root
RUN go build -o /root/mainApp2 /root/main2.go
FROM alpine:latest
WORKDIR /root
COPY --from=0 /root/mainApp . # 파일을 0번째로 빌드된 이미지로부터 가져오기
COPY --from=1 /root/mainApp2 . # 파일을 1번째로 빌드된 이미지로부터 가져오기
# 특정 단계를 명시해서 파일을 복사해오기
# FROM golang as builder
# ADD main.go /root
# WORKDIR /root
# RUN go build -o /root/mainApp /root/main.go
# FROM alpine:latest
# WORKDIR /root
# COPY --from=builder /root/mainApp .
# CMD ["./mainApp"]
'Infra > Docker' 카테고리의 다른 글
Docker Swarm (도커 스웜 모드) (0) | 2022.05.22 |
---|---|
Docker Daemon (도커 데몬) - 1 [구조/-H/Remote Docker API] (1) | 2022.05.08 |