야 Docker 이리나와!! [4] - Dockerfile 설명집

김진성·2021년 10월 10일
1

Docker

목록 보기
4/6
post-thumbnail

지금까지 터미널에서 Docker를 다루는 명령어에 대해서 정리를 하고 공부를 했다. 이제는 Docker Image를 생성하는데 많이 사용되는 Dockerfile에 대해서 공부를 할 예정이다. 사실 글을 적기 전에 Docker로 Django나 Spring Framework를 연동할 때 Dockerfile로 올린 적이 있었다. 그 때는 Dockerfile의 구성 요소가 어떤 요소인지 모르고 다른 글이나 Docs를 따라 작성했었기 때문에 알지 못하는 점이 많았다.

Dockerfile이란?

기본적으로 Dockerfile은 이미지를 생성하는데 필요한 모든 명령어 모음집이라고 할 수 있다.
예를 들면, Dockerfile은 아래와 같이 기본적인 text file로 구성이 되어 있다.

FROM ubuntu:180.04
COPY . /app
RUN make /app
CMD python /app/app.py

각 명령어에 대해서 설명해보자면 주로 4가지로 구성되어 있다.

  • FROM: ubuntu:18.04 도커 이미지로부터 새로운 레이어를 생성
  • COPY: 도커 현재 목록으로부터 파일들을 추가
  • RUN: make로 나의 어플을 실행함
  • CMD: 컨테이너 내에서 어떤 명령어를 실행할지 구체화하여 적는 곳

이 파일은 우분투 내에서 파이썬 파일을 실행하고자 하는 Dockerfile로 볼 수 있다.

Dockerfile 작성 Tip!

.dockerignore

  • .gitignore가 깃에 불필요한 파일을 올리지 않을 때 쓰는 것처럼 우리가 도커를 실행함에 있어 필요하지 않은 구성요소가 존재할 수 있다. .dockerignore가 그런 역할을 하며 도커 허브에 올리는 용량을 줄이는데 도움이 된다.
  • .dockerignore 파일은 우리가 올리는 파일 목록의 Root 목록에 위치하여야 한다.

Multi-Stage builds

  • Multi-Stage builds는 Dockerfile들을 최적화하는 과정에서 읽기 쉽고 유지보수하기 좋게 만들기 위해 사용된다.
# syntax=docker/dockerfile:1
FROM golang:1.16-alpine AS build

# Install tools required for project
# Run `docker build --no-cache .` to update dependencies
RUN apk add --no-cache git
RUN go get github.com/golang/dep/cmd/dep

# List project dependencies with Gopkg.toml and Gopkg.lock
# These layers are only re-built when Gopkg files are updated
COPY Gopkg.lock Gopkg.toml /go/src/project/
WORKDIR /go/src/project/
# Install library dependencies
RUN dep ensure -vendor-only

# Copy the entire project and build it
# This layer is rebuilt when a file changes in the project directory
COPY . /go/src/project/
RUN go build -o /bin/project

# This results in a single layer image
FROM scratch
COPY --from=build /bin/project /bin/project
ENTRYPOINT ["/bin/project"]
CMD ["--help"]
  • 프로젝트에 필요한 tool과 Library를 먼저 설치하고 하나의 이미지로 만들어 Docker 구성을 작게 유지할 수 있게 한다.

Dockerfile에 불필요한 패키지들을 설치하지 말고 프론트/백/DB 등 여러 구성 요소들을 한번에 묶어서 적어놓아 설치하지 않게 해야 한다.

Layer 수를 최소화하라

  • 오직 RUN, COPY, ADD 명령어만 Layer들을 생성하고 다른 명령어들은 임시 이미지들만 생성하기에 빌드 용량을 증가시키지 않는다.
  • 따라서, Multi-Stage 빌드 방법을 통해 오로지 마지막 이미지(final image)만 COPY해 사용하는 것을 권장하고 있다.

multi-line arguments(여러 명령어 구분하기)

  • RUN 명령어에 다양한 시스템 명령어를 적어야 할 때는 백슬래시()로 구분해야 한다.
RUN apt-get update && apt-get install -y \
  bzr \
  cvs \
  git \
  mercurial \
  subversion \
  && rm -rf /var/lib/apt/lists/*

Dockerfile 명령어

1. FROM

FROM [--platform=<platform>] <image> [AS <name>]
FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]
FROM [--platform=<platform>] <image>[@<digest>] [AS <name>]
  • Dockerfile은 기본적으로 Basic Image 즉, 빌드의 바탕이 되는 요소가 필요한데 FROM 명령어를 통해 생성되기에 FROM 명령어가 반드시 먼저 실행되어야 한다.
  • FROM에서 가져오는 이미지는 보통 Docker Hub에서 제공해주는 오픈소스나 Public Repository을 통해 온다.
  • FROM 명령어는 제일 먼저 시작해야되기는 하지만 여러 번 사용할 수 있다.

2. LABEL

  • LABEL은 우리가 많은 이미지들을 구성할 때 라벨을 붙여 구분을 할 수 있게 해준다.
  • key-value 쌍으로 LABEL을 적어야 한다.
LABEL com.example.version="0.0.1-beta"
LABEL vendor1="ACME Incorporated"
LABEL vendor2=ZENITH\ Incorporated
LABEL com.example.release-date="2015-02-12"
LABEL com.example.version.is-production=""

만약 하나의 이미지에 여러 라벨을 붙인다면!?
LABEL com.example.version="0.0.1-beta" com.example.release-date="2015-02-12"
or 백슬래시를 넣을 수 있음
LABEL vendor=ACME\ Incorporated \
      com.example.is-beta= \
      com.example.is-production="" \
      com.example.version="0.0.1-beta" \
      com.example.release-date="2015-02-12"
  • LABEL은 이미지/컨테이너/로컬데몬/용량/네트워크/Swarm nodes/Swarm Service에 적용할 수 있다. 각각 요소에 LABEL을 붙이는 방법은 나중에 공부할 시간이 생기면 해보겠다.

3. RUN

  • RUN은 Dockerfile에서 서버 명령어 등을 관리하기 때문에 백슬래시() 등을 통해 읽기 쉽고 이해하기 좋게 잘 관리해야 한다.
  • RUN은 shell 형태와 exec 형태 2가지로 구성되어 있다.
  1. shell : /bin/sh -c or cmd /S /C
  2. exec : ["executable", "param1", "param2"]

예시) shell과 exec 각각의 형태

1. shell
RUN /bin/bash -c 'source $HOME/.bashrc; \
echo $HOME'

2. exec
RUN ["/bin/bash", "-c", "echo hello"]

apt-get

  • RUN에서 가장 많이 사용하는 케이스에는 apt-get이 있다.
  • apt-get 명령어를 통해 update나 install 등을 실행할 수 있다.
RUN apt-get update && apt-get install -y \
    package-bar \
    package-baz \
    package-foo  \
    && rm -rf /var/lib/apt/lists/*
    
FROM ubuntu:18.04
RUN apt-get update
RUN apt-get install -y curl nginx

4. CMD

  • CMD 명령어는 컨테이너화된 이미지 내 소프트웨어를 실행시키는데 사용되어야 한다.
  • RUN과 다르게 CMD는 exec 형태로 많이 사용된다.
1. shell 형태
FROM ubuntu
CMD echo "This is a test." | wc -

2. exec 형태
FROM ubuntu
CMD ["/usr/bin/wc","--help"]

5. EXPOSE

  • EXPOSE 명령어는 주로 컨테이너가 사용하는 연결 포트를 가리키는 경우가 있다.
  • 예를 들어, Apache 웹 서버가 EXPOSE 80을 사용하고 MongoDB는 EXPOSE 27017를 사용해 구분하는데 필요한 역할 이다.
  • 기본 포트는 TCP 포트로 사용되지만 직접 UDP를 사용할 때는 명시할 수 있다.
EXPOSE <port> [<port>/<protocol>...]
EXPOSE 80/udp

TCP와 UDP 둘다 expose 할 경우
EXPOSE 80/tcp
EXPOSE 80/udp
  • EXPOSE를 통해 dockerfile에서 명시를 할 수 있지만 docker run 명령어에서 지정해줄 수 있다.
docker run -p 80:80/tcp -p 80:80/udp ...

6. ENV

  • 실행하기 쉬운 새로운 소프트웨어를 만들기 위해 ENV를 사용해 환경 경로 및 설치 컨테이너의 PATH를 업데이트할 수 있다.
  • 기본적으로 Key-Value 쌍으로 구성되어 있으며 환경 경로 설정을 통해 Dockerfile 명령어 작성 시 참고하며 적을 수 있다.
ENV <key>=<value> ...

예시) 3줄로 작성하기
ENV MY_NAME="John Doe"
ENV MY_DOG=Rex\ The\ Dog
ENV MY_CAT=fluffy

예시) 한 줄로 작성하기
ENV MY_NAME="John Doe" MY_DOG=Rex\ The\ Dog \
    MY_CAT=fluffy
    
예시) 환경 경로 참고하기
FROM alpine
ENV ADMIN_USER="mark"
RUN echo $ADMIN_USER > ./mark
RUN unset ADMIN_USER

$ docker run --rm test sh -c 'echo $ADMIN_USER'

만약 환경 변수가 많이 존재하지 않는다면, 명령어 안에 환경 경로를 설정하는 것이 낫다
RUN DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install -y ...

7. ADD or COPY

  • ADD와 COPY는 기능적으로는 매우 비슷하지만, COPY 사용 빈도가 높다.
  • COPY는 로컬 파일을 컨테이너로 복사하는 것만을 지원한다. 하지만, ADD는 로컬에서 추출가능한 tar 파일과 URL 지원과 같은 요소들에 적용할 때 유용하다.

예시 1) COPY

COPY requirements.txt /tmp/
RUN pip install --requirement /tmp/requirements.txt
COPY . /tmp/

예시 2) ADD

ADD https://example.com/big.tar.xz /usr/src/things/
RUN tar -xJf /usr/src/things/big.tar.xz -C /usr/src/things
RUN make -C /usr/src/things all

지금까지 Dockerfile을 작성하는 팁 및 여러가지 구성 요소에 대해서 정리를 해봤다. Dockerfile 구성 요소에 ENRTYPOINT나 VOLUME, USER, WORKDIR, ONBUILD 도 있지만 자주 사용하는 요소들에 대해서 적어봤다. 추가적으로 더 정보가 필요하다면 아래의 Docker Docs에서 찾아볼 수 있다.

https://docs.docker.com/develop/develop-images/dockerfile_best-practices/

profile
https://medium.com/@jinsung1048 미디엄으로 이전하였습니다.

0개의 댓글