42서울/Inception

Dockerfiles이란? [42 inception 과제 개념 9]

뜨거운 개발자 2023. 6. 24. 17:58

이제 도커 이미지를 Dockerfile을 이용해 만들어보겠습니다.

이전에 도커 이미지 게시글에서 잠깐 다뤘던 그림을 가져오겠습니다.

도커파일의 특징

👨🏻‍💻
도커파일이란 도커 이미지를 만들기 위한 설계도입니다. 도커 이미지를 빌드하려면 해당 어플리케이션의 콘텐츠들을 도커파일에 복사해야만 합니다.
👨🏻‍💻
도커파일은 반드시 Dockerfile이라는 파일명을 가져야합니다.(대소문자 중요)
👨🏻‍💻
도커 파일에서 하나의 명령은 하나의 layer를 생성합니다.

도커 파일의 문법

👨🏻‍💻
도커 파일에서 있는 모든 대문자는 다 Dockerfile의 문법입니다.
👨🏻‍💻
모든 도커파일의 첫번째 줄은 기반 이미지(base image)를 가져오는 것으로 시작합니다.

첫번째 줄은 이 이미지의 기반 이미지가 무엇인지에 대해서 적어주는 것 입니다.

예를 들어 백엔드가 없는 자바스크립트 어플을 만드는 상황을 가정해보겠습니다.

이미지의 nodejsBase이미지인 이미지의 구조는 다음과 같습니다.

우리는 Linux Alpine 또는 다른 lower level 이미지가 필요합니다. 이후 우리는 node를 설치해야만 합니다. app의 기반이미지는 nodejs엔진입니다. 노드엔진의 기반 운영체제도 있습니다.

실제로 우리의 이미지가 NODE 이미지에 기반으로 한다면, 우리의 이미지 안에 노드를 설치해야한다는 것을 의미합니다. 따라서 우리가 컨테이너 쉘을 열면 node 명령이 사용가능한 것을 볼 수가 있습니다.

🚨
경고 : 이 도커파일 내용은 어렵습니다. 최대한 이해해 보시고, 나중에 나오는 도커파일 문법들까지 정리를 한 파일에 했기 때문에 한번에 이해가 잘 안돼도, 일단 넘어가시고 도커파일 작성시에 다시 돌아오시면 좋을 것 같습니다.(그래도 한번 읽어보세요. 안 보는 것과 차이는 큽니다.)
🚨
이 경고를 보고 겁먹고, 밑부분을 안 읽으시면 피눈물 흘립니다. PID1 문제를 해결하는 기초 내용도 나오니 꼭 읽어보세요!!

도커파일 예시 문법

예시를 들어보겠습니다.

기본적으로 도커파일은 쉘에서 실행할 수 있는 명령을 전부 실행할 수 있다고 했습니다.

따라서 쉘명령을 도커파일로 변경한 모습을 보도록 하겠습니다.

쉘 명령

install node

set MONGO_DB_USERNAME=addmin
set MONGO_DB_PWD=password

create /home/app folder

copy current foler file to /home/app

start the app with: "node server.js"

도커 파일

FROM node
ENV MONGO_DB_USERNAME=admin \
		MONGO_DB_PWD=password

RUN mkdir -p /home/app

COPY . /home/app

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

간단하게 노드를 기반으로 하는 웹서버를 실행하는 상황을 도커파일로 만들었습니다.

위 내용을 기반으로 도커파일의 문법에 대해서 설명하도록 해보겠습니다.

도커파일 문법

FROM

FROM node : 노드 이미지를 기반으로 합니다.

위에서 이야기 했듯 모든 도커파일은 FROM으로 시작합니다.

FROM 명령문은 기본적으로 사용할 Base 이미지를 지정합니다.

Docker 이미지는 여러 계층으로 구성되며, FROM 명령문은 이러한 계층 중 가장 첫 번째 계층을 정의합니다.

RUN

run mkdir -p /home/app : 이 부분은 /home/app 이라는 폴더가 컨테이너 내부에 생성되게 된다는 것을 의미하게 됩니다. (로컬에 폴더가 생기는 것이 아닙니다.)

이 폴더 내부에 이미지나 도커파일 등 설정파일과 다양한 것들이 들어가게 됩니다.

RUN 명령을 사용하면 기본적으로 모든 종류의 Linux 명령을 실행할 수 있습니다.
🚨
도커파일의 중요한 특징으로 도커파일에 있는 모든 명령은 컨테이너 내부에 적용이 됩니다!!

COPY

💡
도커 파일은 전부 containor 내부에서 실행되는 것이 기본적인 규칙인데 COPY 명령은 살짝 예외입니다. 실제로 COPY명령은 호스트에서 실행이 됩니다.
COPY 호스트_파일경로 , 도커컨테이너_내부경로 

만약 COPY를 가장 마지막에 두는 경우 도커 컨테이너 내부에 있는 파일을 복사 할 수가 있습니다.

COPY . /home/app : 첫번째 매개변수는 source(.)이고 두번째 매개변수는 target(/home/app)입니다. 도커파일이 있는 위치에서, 있는 모든 파일을 컨테이너 위에 /home/app으로 복사가 됩니다!!

👨🏻‍💻
다만 . 으로 사용하게 되면, 원하지 현재 폴더에 원치 않는 파일들도 전부 복사가 되기 때문에 폴더를 만들어서 ./app 으로 해주는 것이 더 권장되는 방법입니다.

CMD

CMD 명령의 3가지 양식

1. CMD ["executable","param1","param2"](실행 양식, 이 양식이 기본 양식입니다.)

2. CMD ["param1","param2"](ENTRYPOINT의 기본 파라미터로 사용)

3. CMD command param1 param2( 형태)

👨🏻‍💻
CMD 명령은 기본적으로 sh 위에서 실행됩니다. 즉 /bin/sh -c 로 실행이 됩니다. (sh는 본쉘) (양식 3번)
👨🏻‍💻
그렇게 하지 않고 바로 프로그램을 실행하려면, ENTRYPOINT를 사용하거나 명령어를 실행할 파일을 직접적으로 정의해주는 양식을 사용하면 됩니다. (양식 1, 2)

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

우리가 노드로 서버를 시작하는 명령을 실행하는 예시입니다.

실제로 우리가 커맨드라인에서 서버를 실행할 때 node server.js라고 입력합니다.

node server.js 를 컨테이너 내부에서 실행하도록 하는 의미입니다.

CMD 의 기본 목적은 실행중인 컨테이너에 기본값을 제공하는 것 입니다.

환경변수 대체

CMD [ "echo", "$HOME" ] 형식은 환경변수 대체를 해주지 않습니다. 왜냐하면 양식 1,2번 같은 경우 기본적으로 쉘 위에서 실행되는게 아니기 때문에 그렇습니다.

만약 환경변수를 대체하고 싶다면, CMD [ "sh", "-c", "echo $HOME" ] 로 쉘을 이용해 환경변수 대체를 해줄 수 있습니다.

RUNCMD 차이점.

RUNCMD 는 비슷해 보이지만, 큰 차이가 있는데, CMDentrypoint command를 의미합니다.

즉, RUN 명령은 여러 번 실행할 수 있지만 CMD 명령은 오직 한번만 파일에 있을 수 있습니다.

CMD 명령은 오직 도커파일에서 한번만 쓰는게 원칙이지만 만약 여러개가 들어갔다면 마지막 CMD를 사용합니다.

그리고 CMDENTRYPOINT 와 결합해서 진입점을 정합니다.

ENTRYPOINT

엔트리 포인트는 2개의 형식이 있습니다.

  • ENTRYPOINT ["executable", "param1", "param2"] : 기본 형식
  • ENTRYPOINT command param1 param2 : 셸 형식

ENTRYPOINT 를 통해 실행 파일로 실행할 컨테이너를 구성할 수 있습니다.

기본형식

👨🏻‍💻
ENTRYPOINTdocker run 을 했을 때, 실행되는 모든 명령의 진입 명령입니다.

다만 ENTRYPOINT 를 잠깐 바꾸고 싶다면 docker run --entrypoint flag를 사용하면 그 RUN명령에서는 overriding 됩니다.

🚨
(docker run을 실행할 때 그 RUN앞에 실행된다는 뜻이 아니라 CMD 앞에 실행된다는 의미입니다. 여기서 rundocker run 이지 RUN이 아닙니다.)

예시

FROM ubuntu
ENTRYPOINT ["top", "-b"]
CMD ["-c"]

다음과 같은 실행결과가 나옵니다.

💡
docker run -it --rm --name test top -Hdocker exec -it test ps aux USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 1 2.6 0.1 19752 2352 ? Ss+ 08:24 0:00 top -b -H root 7 0.0 0.1 15572 2164 ? R+ 08:25 0:00 ps aux

즉, 이걸 보면, PID 1번으로 실행하는 프로그램이 ENTRYPOINT로 지정해준 프로그램임을 볼 수 있습니다.

아파치의 엔트리 포인트 예시

FROM debian:stable
RUN apt-get update && apt-get install -y --force-yes apache2
EXPOSE 80 443
VOLUME ["/var/www", "/var/log/apache2", "/etc/apache2"]
ENTRYPOINT ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]

ENTRYPOINT를 지정해줌으로써, 그 컨테이너에서 실행할 때 그곳으로 들어가서 실행을 합니다.

즉 컨테이너에서 첫번째로 실행하는 프로세스를 지정할 때 유용하게 사용할 수도 있습니다.

쉘형식

쉘 형식 같은 경우에는 ENTRYPOINT/bin/sh -c 로 실행이 됩니다.

따라서, 첫번째 PID1이 쉘이 되고 그 다음으로 실행하는 명령으로 될 것이기 때문에 컨테이너가 유닉스 시그널을 받지 않습니다.

그래서 시그널을 받아도 실행한 것이 stop하지 않고 쉘이 중단한다는 특징이 있습니다.

저희 과제에서는 금지된 방법이라고 생각하셔도 좋습니다.

CMD와 ENTRYPOINT의상호 작용 방식 이해

CMDENTRYPOINT 규칙

1. 도커 파일은CMD또는ENTRYPOINT 를 하나 이상을 지정해서 명령을 실행해야만 합니다
2. ENTRYPOINT 는 컨테이너를 실행 파일로 사용할 때 정의해야 합니다. (즉 컨테이너 자체를 실행파일로 정의할 때)
3. CMDENTRYPOINTdefault 인자를 정의하는데 사용하거나, 컨테이너에서 에드훅 명령 (딱 한번 쓰거나 간단한 명령)을 실행하기 위해서 사용합니다.
4. CMD는 컨테이너를 실행할 때 다른 선택 인수를 사용할 때 지정합니다

ENTRYPOINT 와 CMD 사용 조합표

다음 표는 ENTRYPOINTCMD 사이의 조합을 보여주는 표입니다.

각각 ENTRYPOINT 와 CMD 모두 쉘 버전으로 실행하면 ([] 대괄호가 없는 버전) 전부 /bin/sh(본쉘) 를 이용해서 실행하는 모습을 볼 수가 있습니다.

그 외 특징은 언제나 ENTRYPOINT가 먼저 실행된다는 점과, 엔트리포인트가 쉘 형식이면 CMD가 무시된다는 특징을 볼 수 있습니다.

만약 CMD와 ENTRYPOINT 설명이 이해가 안가면 나중에 실습하다 궁금증이 생길 때

Dockerfile reference
Find all the available commands you can use in a Dockerfile and learn how to use them, including COPY, ARG, ENTRYPOINT, and more.
https://docs.docker.com/engine/reference/builder/

여기서 CMDENTRYPOINT에 대해서 더 이해를 하고 넘어가기를 바랍니다.

EXPOSE

컨테이너에게 반드시 Expose 해야만 하는 포트번호를 적어줍니다.(컨테이너 내부 포트를 의미합니다.)

expose:
  - "3000"
  - "8000"

컨테이너 내부의 포트번호만을 지정해줄 수 있습니다.

즉 컨테이너 내부에서 통신할 포트 번호를 적어준다라고 생각해주시면 좋을 것 같습니다.

publish하려면 -p 플래그를 Docker run 할 때 줘야만 합니다.

ARG

FROMARG 앞에 올수 있는 유일한 지침입니다.

또한 FROM같은 경우 as name으로 기반 이미지의 이름을 적을 수 있습니다.

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

다음과 같은 방법으로 도커파일로 컨테이너에게 환경변수를 만들어 전달 할 수 있습니다.

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

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

$ docker build --build-arg port=8080 . : 예를 들어, Dockerfile에 다음과 같이 ARG 명령문으로 port를 인자로 선언해주면, docker build 커맨드에 --build-arg 옵션에 port 값을 넘길 수가 있습니다.

ARG port=8080 처럼, 인자의 디폴트값을 지정해주면, --build-arg 옵션으로 해당 인자가 넘어오지 않았을 때 사용됩니다.

CMD start.sh -h 127.0.0.1 -p ${port} : 설정된 인자 값은 다음과 같이 ${인자명} 형태로 읽어서 사용할 수 있습니다.

도커파일의 문제 및 환경변수 설정

도커파일은 도커파일이 수정될 때마다 수정된 부분만 바꾸는게 아니라 이미지를 다시 빌드해야만 합니다.

환경변수 설정은 도커 컴포즈에서도 할 수 있습니다. 사실 이것은 둘 중 어디서 설정해주던 다 설정이 가능합니다. 이는 선택에 따라 달라집니다.

👨🏻‍💻
하지만 환경 변수를 설정하는 것은 일반적으로 Docker Compose 파일에서 하는 것이 더 좋습니다. Docker Compose는 여러 컨테이너로 구성된 애플리케이션을 정의하고 관리하는 도구이기 때문에, Compose 파일에서 환경 변수를 설정하면 모든 컨테이너에 일관된 방식으로 전달됩니다.

도커파일 빌드해보기 docker build

👨🏻‍💻
도커파일을 이용해서 이미지를 빌드하려면, 두개의 인자를 줘야만 합니다.
docker build [OPTIONS] PATH | URL | -
docker build -t my-app:1.0 .

첫번째 인자는 이미지에 이름을 지정하도록 이미지 이름을 인자로 사용합니다.

두번째 인자는 도커파일의 위치입니다.

위의 명령의 의미는 이미지의 이름을 my-app으로 하고 버전을 1.0으로 합니다. 또, 도커파일의 위치는 현재 폴더와 같은 위치에 있다는 의미입니다.

다음과 같은 명령으로 도커이미지를 지울 수 있습니다.

  • docker stop
  • docker rm
  • docker rmi

이전 게시물에서 다뤘기 때문에 이미지로 대체합니다.


Uploaded by N2T

728x90