Profile picture

[Dockerfile] Dockerfile에서 자주 쓰이는 지시어

JaehyoJJAng2023년 04월 07일

▶︎ Dockerfile

Docker 이미지가 어떤 단계를 거쳐 빌드되야 하는지를 담고있는 텍스트 파일임.

Docker는 Dockerfile에 나열된 명령문을 차례대로 수행하여 이미지를 생성해준다.


‣ 문법

하나의 도커파일은 기본적으로 다음과 같은 구조를 가진 여러 개의 명령문으로 구성되어 있음

# 주석
지시어(INSTRUCTION) 인자(arguments)

지시어는 시작하고 여러 개의 인자가 따라올 수 있으며, 지시어가 인자와 구분이 될 수 있도록 지시어는 대문자로 작성하는 것이 관례


▶︎ 지시어

‣ FROM

FROM <이미지>
FROM <이미지:태그>

base 이미지를 지정해주기 위해서 사용된다. 보통 Dockerfile 최상단에 위치함


python 3.8을 base 이미지로 사용하는 경우

FROM python3:3.8-slim-buster

‣ WORKDIR

쉘의 cd 명령문처럼 컨테이너 상에서 작업 디렉토리로 전환을 위해서 사용됨.

WORKDIR 명령문으로 작업 디렉토리를 전환하면 그 이후에 등장하는 모든 RUN,CMD,ENTRYPOINT,COPY 명령문은 해당 디렉토리를 기준으로 수행된다.


WORKDIR <이동할경로>

/app으로 작업 디렉토리 전환

WORKDIR /app

‣ RUN

  • 이미지 빌드 과정에서 필요한 커맨드를 실행하기 위해서 사용됨.
  • 특정 소프트웨어를 설치하기 위해서 많이 사용된다

pip 패키지 설치

RUN pip install --upgrade pip

‣ ENTRYPOINT

이미지를 컨테이너로 띄울 때 항상 실행되야 하는 커맨드를 지정할 때 사용함

해당 명령문은 Docker 이미지를 하나의 실행 파일처럼 사용할 때 유용하다.

왜냐하면 컨테이너가 실행될 때 ENTRYPOINT 명령문으로 지정된 커맨드가 실행되고, 이 커맨드로 실행된 프로세스가 죽을 때 컨테이너도 따라서 죽기 때문


Dockerfile

FROM node:14-alpine

RUN apk add --no-cache python3 g++ make

WORKDIR /app

COPY ./ ./

ENTRYPOINT [ "docker-entrypoint.sh" ]

CMD [ "node" ]

docker-entrypoint.sh 에 node 인자를 넘기게 된다
docker-entrypoint.sh node


docker run

# bash가 아닌 sh 쉘로 동작함
$ docker run -d -it --name ubuntu --entrypoint sh ubuntu:focal 
$ docker inspect ubuntu

"Config": {
  ... 생략 ...
    "Entrypoint": [
        "sh"
    ],
  ... 생략 ...
},

‣ CMD

해당 이미지를 컨테이너로 띄울 때 디폴트로 실행할 커맨드나,ENTRYPOINT 명령문으로 지정된 커맨드에 디폴트로 넘길 파라미터를 지정할 때 사용된다

CMD 명령문은 많은 경우 , ENTRYPOINT 명령문과 함께 사용되는데, ENTRYPOINT 명령문으로는 커맨드를 지정하고 CMD 명령문으로 디폴트 파라미터를 지정해주면 매우 유연하게 이미지 실행이 가능하다

CMD 명령문은 이미지를 컨테이너로 띄울 때 딱 한번 실행 하는 커맨드이고 이마저도 docker run 커맨드에 인자를 넘길 경우 상실하게 됨


‣ EXPOSE

네트워크 상에서 컨테이너로 들어오는 트래픽을 리스닝하는 포트와 프로토콜을 지정하기 위해서 사용된다.

프로토콜은 TCP와 UDP중 선택 가능하지만 디폴트로 TCP가 선택된다

EXPOSE 명령문으로 지정된 포트는 해당 컨테이너의 내부에서만 유효하며, 호스트 컴퓨터에서 바로 접근할 수 있는 것이 아니다.

호스트 컴퓨터로부터 해당 포트로의 접근을 허용하려면 docker run 커맨드를 통해 -p 옵션을 줘서 호스트 컴퓨터의 특정 포트를 컨테이너와 포워딩 시켜줘야 한다.


8080/TCP 포트로 리스닝 후 포트 포워딩

EXPOSE 8080
docker run -d --name test -p 80:8080 yshrim12/web:latest 

‣ COPY

호스트 컴퓨터에 있는 디렉토리나 파일을 Docker 이미지의 파일 시스템으로 복사하기 위해서 사용된다.

절대 경로와 상대 경로 모두를 지원하고, 상대 경로를 사용할 때에는 이전에 사용해봤던 WORKDIR 명령문으로 작업 디렉토리를 어디로 전환해놨는지 고려해야 한다.


현재 경로에 존재하는 모든 파일 복사

COPY . /app

‣ ENV

환경 변수를 설정하기 위해서 사용된다.

ENV 명령문으로 설정된 환경 변수는 이미지 빌드 시에도 사용됨은 물론이고 해당 컨테이너에서 돌아가는 애플리케이션도 접근할 수 있다.


MYSQL_ROOT_PASSWORD 환경 변수를 root으로 설정

ENV MYSQL_ROOT_PASSWORD root

정의한 환경변수를 도커파일 내에서 호출해보기

FROM node:14

...(생략)...

ENV PORT 80

...(생략)...

ENV 옵션에서 정의한 PORT 변수를 EXPOSE 옵션에서 호출해 사용이 가능하다


아니면 이런 방법도 있다

$ docker run -d -it --name bitch --env-file .env yshrim12/bitch:0.0

--env-file .env 옵션 처럼 환경 변수 파일을 지정해줄 수도 있다.


위 옵션은 Docker Compose의 YAML 파일에서도 비슷하게 사용할 수 있는데 잠깐 예시를 들자면 아래와 같다.

services:
  db:
    env_file:
      - ".env"    

.env 파일의 내용은 아래와 같슈

MYSQL_ROOT_PASSWORD="fuck you"

당연하겠지만 민감한 환경 변수를 위의 방식처럼 파일로 관리하는 경우 해당 환경 변수 파일이 커밋되지 않도록 .gitignore 파일에 해당 환경 변수 파일을 기입하는 것은 필수다.


‣ ARG

ARG <이름>
ARG <이름>=<기본값>

ARG 명령문은 docker build 커맨드로 이미지를 빌드 시, --build-arg 옵션을 통해 넘길 수 있는 인자를 정의하기 위해 사용한다


예를 들어, Dockerfile에 다음과 같이 ARG 명령문으로 port를 인자로 선언해주면,

ARG port

다음과 같이 docker build 커맨드에 --build-arg 옵션에 port 값을 넘길 수가 있다.


인자의 디폴트 값을 지정해주면, --build-arg 옵션으로 해당 인자가 넘어오지 않았을 때 사용된다

ARG port=8000

설정된 인자 값은 다음과 같이 ${인자명} 형태로 읽어서 사용이 가능하다

CMD build.sh -h 127.0.0.1 -p ${port}

ENV와 달리 ARG로 설정한 값은 이미지가 빌드되는 동안에만 유효하니 주의하자!

.dockerignore

Docker 이미지 빌드할 때 제외 시키고 싶은 파일이 있는 경우 .dockerignore 파일에 추가해주면 된다

dev/
*.sh

이렇게 설정을 해주면 Docker는 프로젝트 최상위 디렉터리에 위치하고 있는 dev/,*.sh 파일들을 무사하게 되므로, RUN과 CMD, COPY와 같은 명령문이 해당 파일을 사용할 수 없게 된다


▶︎ Dockerfile 실습

‣ Dockerfile

~/dockerTests/Dockerfile

FROM node:16-alpine

LABEL maintainer="yshrim12@naver.com"
LABEL description="Simple server with node.js"

# Create App directory
WORKDIR /app

# Install app dependencies
# A wildcard is used to ensure both package.json AND package-lock.json are copied
# where available (npm@5+)
COPY package*.json ./
RUN npm install

# Bundle app source
COPY ./ ./

# Set variable
ARG PORT=8080

# Set EXPOSE (conatainer port)
EXPOSE ${PORT}

CMD [ "node", "server.js" ]

이미지 빌드하기

$ docker build --tag yshrim12/nodejs-server -f ./Dockerfile .

컨테이너 실행하기

$ docker run -d -it --name nodejs-server -p 80:8080 --rm yshrim12/nodejs-server

‣ 멀티 스테이지 빌드

~/dockerTests/Dockerfile.multi

FROM node:16-alpine AS base

LABEL maintainer="yshrim12@naver.com"
LABEL description="Simple server with node.js"

# Create App directory
WORKDIR /app

# Install app dependencies
# A wildcard is used to ensure both package.json AND package-lock.json are copied
# where available (npm@5+)
COPY package*.json ./

FROM base AS build
RUN npm install

FROM base AS release
COPY --from=build /app/node_modules ./node_modules

# Bundle app source
COPY ./ ./

ARG PORT=8080

EXPOSE ${PORT}

CMD [ "node", "server.js" ]

이미지 빌드하기

$ docker build --tag yshrim12/nodejs-server -f ./Dockerfile.multi .

컨테이너 실행하기

$ docker run -d -it --name nodejs-server -p 80:8080 --rm yshrim12/nodejs-server

‣ Slim 이미지 빌드

~/dockerTests/Dockerfile.slim


FROM node:16-slim
LABEL maintainer="FastCampus Park <fastcampus@fastcampus.com>"
LABEL description="Simple server with Node.js"

# Create app directory
WORKDIR /app

# Install app dependencies
# A wildcard is used to ensure both package.json AND package-lock.json are copied
# where available (npm@5+)
COPY package*.json ./

RUN npm install
# If you are building your code for production
# RUN npm ci --only=production

# Bundle app source
COPY . .

EXPOSE 8080
CMD [ "node", "server.js" ]

이미지 빌드하기

$ docker build --tag yshrim12/nodejs-server -f ./Dockerfile.slim .

컨테이너 실행하기

$ docker run -d -it --name nodejs-server -p 80:8080 --rm yshrim12/nodejs-server

Loading script...