
Dockerfile이란?
Dockerfile은 DockerImage를 생성하기 위한 레시피(텍스트 파일)이다.
이미지를 구성하는데 필요한 모든 명령어와 설정이 포함됩니다.
-> 도커파일(Dockerfile)은 도커 이미지(Docker image)가 어떤 단계를 거쳐 빌드(build)되야 하는지를 담고있는 텍스트 파일이다.
이 문서는 이미지 빌더에게 실행할 명령어, 복사할 파일, 시작 명령어 등을 지시합니다.
Dockerfile을 작성한 후 빌드하면 Docker는 Dockerfile에 나열된 명령문을 차례대로 수행하며 DockerImage를 생성해준다.
Docker을 읽을 줄 안다는 것은 해당 이미지가 어떻게 구성되어 있는지 알 수 있다는 의미이다.
Docker 이미지는 읽기 전용 계층(layer)들로 구성됩니다. 각 계층은 Dockerfile의 한 명령어를 대표하고, 이 계층들은 쌓여서 이미지를 형성합니다.
예를 들어, 위의 Dockerfile에서 각 명령어는 다음과 같은 계층을 생성합니다.
FROM ubuntu:22.04 # ubuntu:22.04 Docker 이미지에서 새 계층을 생성합니다.
COPY . /app # Docker 클라이언트의 현재 디렉토리에서 파일을 추가합니다.
RUN make /app # make 명령어를 사용하여 애플리케이션을 빌드합니다.
CMD python /app/app.py # 컨테이너 내에서 실행할 명령어를 지정합니다.
이미지를 실행하여 컨테이너를 생성 할 떄는 기존 계층들 위에 새로운 쓰기 가능 계층, 즉 컨테이너 계층을 추가합니다. 실행 중인 컨테이너에서 파일을 새로 작성하거나 기존 파일을 수정하고 삭제하는 모든 변경 사항은 이 쓰기 가능한 컨테이너 계층에 기록됩니다.
하나의 Dockerfile은 기본적으로 다음과 같은 구조를 가진 여러 개의 명령문으로 구성 되어있습니다.

각 명령문은 명령어로 시작하고 여러 개의 인자가 따라올 수 있으며, 해당 명령문에 대한 주석도 달 수 있습니다. 인자와 구분이 쉽도록 명령어는 모두 영문 대문자로 써주는 것이 관례입니다.

하나의 Docker 이미지는 base 이미지부터 시작해서 기존 이미지 위에 새로운 이미지를 중첩해서 여러 단계의 이미지 층(layer)을 쌓가며 만들어집니다
FROM명령문은 이 base 이미지를 지정해주기 위해서 사용되는데, 보통 Dockerfile 내에서 최상단에 위치합니다. base 이미지는 일반적으로 Docker Hub와 같은 Docker repository에 올려 놓은 잘 알려진 공개 이미지인 경우가 많습니다



쉽게 말해 FROM 명령어는 Docker 빌드의 새로운 시작점을 찍는 명령어로, 이 명령어가 실행되는 순간 이전 단계에서 작업했던 모든 파일과 상태는 깨끗이 초기화(리셋)되며, 오직 새롭게 지정한 베이스 이미지만을 바탕으로 다음 빌드 단계를 이어나가게 된다.
AS는 FROM으로 인해 이전 작업물이 날아가기 전에, 나중에 COPY 등으로 다시 불러와 쓰기 위해 빌드 단계에 이름표(별명)를 붙이는 명령어이다.
WORKDIR 명령문은 쉘(shell)의 cd명령문 처럼 컨테이너 상에서 작업 디렉터리로 전환을 위해서 사용됩니다. WORKDIR 명령문으로 작업 디텍터리를 전환하면 그 이후에 등장하는 모든 RUN,CMD,ENTRYPOINT,COPY,ADD 명령문은 해당 디렉터리를 기준으로 실행됩니다.


RUN명령문은 마치 쉘(shell)에서 커맨드를 실행하는 것 처럼 이미지 빌드 과정에서 필요한 커맨드를 실행하기 위해서 사용됩니다.쉘(shell)을 통해 거의 못하는 작업이 없는 것 처럼 RUN명령문으로 할수 있는 작업은 무궁무진하지만 보통 이미지 안에 특정 소프트웨어를 설치하기 위해서 많이 사용됩니다.




ENTRYPOINT명령문은 이미지를 컨테이너로 띄울 때 항상 실행되야 하는 커맨드를 지정할 때 사용합니다.ENTRYPOINT명령문은 이미지를 컨테이너로 띄울 때 항상 실행되야 하는 커맨드를 지정할 때 사용합니다.ENTRYPOINT명령문은 Docker이미지를 마치 하나의 실행 파일처럼 사용할때 유용합니다. 왜냐하면 컨테이너가 뜰 때 ENTRYPOINT명령문으로 지정된 커맨드가 실행되고, 이 커맨드로 실행된 프로세스가 죽을 때,컨테이너로 따라서 종료되기 때문입니다.
- npm start 스크립트 실행



CMD명령문은 컨테이너가 켜지자마자 실행할 기본 행동(디폴트 명령어)입니다.
도커 컨테이너를 하나의 작은 컴퓨터라고 하면 컴퓨터가 켜졌는데 아무 프로그램도 안 돌아가면 쓸모가 없습니다. 그럴때 CMD라는 컨테이너가 컴퓨터가 시작하자마자 자동으로 실행할 프로그램을 미리 정해두는 매크로같은 역할을 합니다.
RUN과 달리 CME는 이미지가 다 만들어진 후 컨테이너를 Docker run으로 켜는 시점에 실행됩니다.CMD를 여러개 적을 수는 있지만, 도커는 오직 가장 마지막에 적힌 하나의 CMD만 실행합니다CMD로 기본 행동을 정해놨더라도, 사용자가 컨테이너를 켤 때 맨뒤에 다른 명령어를 붙이면 원래 CMD 내용은 무시하고 사용자가 새로 입력한 명령어가 실행됩니다.
CMD는 단독으로도 자주 쓰이지만, ENTRYPOINT라는 명령문과 함께 결합할 때 진짜 진가를 발휘합니다.
두 명령어의 역할을 나누면 다음과 같습니다.
ENTRYPOINT: 절대 바뀌지 않는 고정 뼈대 명령어CMD: 그 뒤에 붙는 바뀔 수 있는 기본 옵션 (파라미터/인자)ENTRYPOINT ["node"]
CMD ["index.js"]

EXPOSE 명령문은 네트워크 상에서 컨테이너로 들어오는 트래픽(traffic)을 리스닝(listening)하는 포트와 프로토콜를 지정하기 위해서 사용됩니다. 프로토콜은 TCP와 UDP 중 선택할 수 있는데 지정하지 않으면 TCP가 기본값으로 사용됩니다.
여기서 주의할 점은 EXPOSE 명령문으로 지정된 포트는 해당 컨테이너의 내부에서만 유효하며, 호스트(host) 컴퓨터에서는 이 포트를 바로 접근을 할 수 있는 것은 아니라는 겁니다. 호스트 컴퓨터로부터 해당 포트로의 접근을 허용하려면, docker run 커맨드를 -p 옵션을 통해 호스트 컴퓨터의 특정 포트를 포워딩(forwarding)시켜줘야 합니다.
-> 쉽게 말해 EXPOSE 명령문은 컨테이너를 실행할때 특정 컨테이너가 특정 네트워크 포트를 사용하여 통신할거라고 알려주는 명령문입니다.
결론적으로, EXPOSE는 단순히 "이 컨테이너는 내부적으로 이 포트를 씁니다" 하고 알려주는 설명서 역할을 할 뿐이고, 실제로 외부와 통하는 길을 뚫어주는 것은 docker run 할 때 쓰는 -p 옵션입니다.
80/TCP 포트로 리스닝

9999/UDP 포트로 리스닝

COPY명령문은 내 컴퓨터 내에 있는 파일을 복사하는 명령문이다
** COPY [내 컴퓨터 경로][이미지 내부의 저장 경로}
# 내 컴퓨터에 있는 'app.py' 파일을 이미지 안의 '/app/' 폴더로 복사해 줘!
COPY app.py /app/app.py
ADD 명령문은 COPY 명령문과 비슷하지만 두가지의 능력이 더 있습니다.
ADD명령문은 COPY와 다른게 내 컴퓨터에 없는 파일이라도, URL을 적으면 도커가 알아서 다운로드해서 이미지 안에 넣어줍니다.
ADD [https://example.com/important-data.csv](https://example.com/important-data.csv) /app/data.csv
.tar나.tar.gz같은 압축 파일을 복사하면, 이미지 안에 들어갈 때 알아서 압축을 풀어서 넣어줍니다.
"ADD는 COPY의 복사 기능도 다 가지고 있으면서, 기능이 두 개나 더 있잖아, 그럼 그냥 ADD만 쓰면 되는 거 아냐?"
COPY와 ADD를 공부하다 보니 이런 생각이 들었습니다.
하지만 도커의 공식 권장 사항은 "웬만하면 무조건 COPY를 쓰세요" 입니다. 왜 기능이 더 좋은 ADD 말고 COPY를 쓰라고 하는걸까요?
1.명확한 게 최고입니다 (예측 불가능한 마법 방지)
ADD는 내부적으로 "파일이 압축 파일인가? -> 맞으면 압축 해제 / 아니면 일반 복사"라는 복잡한 판단을 도커 엔진이 알아서 처리합니다.
이 '알아서 해주는 마법'이 오히려 독이 될 때가 많습니다.
tar.gz) 자체를 이미지 안에 보관하고 싶어서 넣었는데, ADD 명령문 때문에 내 의도와 상관없이 압축이 풀려버리는 불상사가 생길 수 있습니다.ADD 한 줄만 보고는 이 파일이 안에서 압축이 풀린 상태인지 그냥 복사된 상태인지 한눈에 파악하기 어렵습니다. COPY는: 100% 정직하게 그대로 복사만 하므로, 코드가 훨씬 직관적이고 예측 가능해서 안전합니다.2.보안과 용량 문제가 발생하기 쉽습니다
인터넷 주소(URL)를 통해 다운로드받는 ADD 기능은 얼핏 편해 보이지만, 실제 협업 환경에서는 치명적인 문제를 일으킬 수 있습니다.
ADD https://...처럼 외부 주소를 적어두면 빌드할 때마다 매번 인터넷에서 파일을 새로 받아옵니다. 만약 그 사이에 해당 사이트가 해킹당해 악성코드가 심긴 파일로 바뀌어 있다면, 도커는 아무것도 모른 채 위험한 파일을 다운로드해 이미지로 만들어 버립니다. 또한 프라이빗한 주소라면 토큰이나 비밀번호가 Dockerfile에 노출되는 보안 사고가 날 수도 있습니다.ADD로 원격 압축 파일을 다운로드하면 압축 파일 자체가 이미지에 그대로 박제되어 용량이 쓸데없이 커집니다. (그래서 보통은 RUN curl이나 wget으로 파일을 다운받아 압축을 풀고, 그 즉시 원본 압축 파일을 지우는 방식을 씁니다.)압축 파일을 넣으면서 "동시에 압축까지 자동으로 풀고 싶을 때"라는 극히 예외적인 상황이 아니라면, 모든 파일 복사는 무조건
COPY를 쓰는 것이 전 세계 개발자들의 약속(Best Practice)입니다.
ENV명령문은 환경 변수를 설정하기 위해서 사용합니다. ENV명령문으로 설정된 환경 변수는 이미지 빌드 시에도 사용되고 해당 컨테이너에서 돌아가는 애플리케이션도 접근할수있습니다.
->ENV 명령문은 환경 변수를 설정하는 명령문이고
이미지를 빌드하는 과정이나 컨테이너가 실행되는 과정에서도 환경 변수를 가져와서 쓸수있다
ARG는 도커 이미지를 만드는(빌드하는) 동안에만 잠깐 쓰고 사라지는 임시 변수입니다.
도커파일 코드를 직접 수정하지 않고, 이미지를 만들 때 터미널 명령어 창(--build-arg)에서 원하는 값을 쏙 찔러 넣어 편리하게 설정을 바꿀 때 사용합니다.
"결국 둘 다 변수를 설정해서 가져다 쓰는 건데, 뭐가 다른가요?"
가장 핵심이 되는 차이점은 "이 변수가 언제까지 살아남아 있는가?" 즉, 생명 주기(Life Cycle)에 있습니다.
--build-arg)에서 원하는 값을 슥 채워 넣기 위해 사용합니다.| 비교 항목 | ARG (Argument) | ENV (Environment) |
|---|---|---|
| 변수의 수명 | 이미지가 완성(Build 끝)되면 사라짐 | 컨테이너가 켜져 있는 동안 계속 유지됨 |
| 소스코드 접근 | 내가 짠 프로그램(파이썬 등)에서 접근 불가능 | 내가 짠 프로그램에서 언제든 접근 가능 |
| 터미널에서 변경 | 이미지 만들 때 --build-arg로 변경 가능 | 컨테이너 켤 때 docker run -e로 변경 가능 |
| 추천 용도 | 패키지 버전 관리, 임시 폴더 경로 지정 등 | 데이터베이스 비번, API 토큰, 서버 환경 설정 등 |
.dockerignore은 명령문은 아니지만 Docker 이미지를 빌드할 때 제외 시키고 싶은 파일이 있다면,.dockerignore파일에 추가하면됩니다.
예를 들어, .git 디렉터리와 마크다운(markdown) 파일을 모두 제외 시키고 싶다면 다음과 같이 .dockerignore 파일을 작성해주면 됩니다.

| 명령어 | 실제 기능 (쉽게 이해하는 용도) | 왜 쓰나요? |
|---|---|---|
| FROM | 기본 환경(OS/언어) 선택 | 파이썬, NodeJS 등 내 프로그램이 돌아갈 베이스 컴퓨터 환경을 가져오려고 |
| WORKDIR | 기본 폴더 위치 변경 (cd) | 이후 경로가 꼬이지 않게 "이제부터 모든 작업은 이 폴더 안에서 해!"라고 지정하려고 |
| RUN | 필요한 프로그램 미리 설치 | 이미지를 만드는 도중에 프로그램 실행에 필요한 패키지나 라이브러리를 미리 다운받아 설치하려고 |
| ENTRYPOINT | 실행 시 무조건 켜질 메인 프로그램 | 컨테이너가 가동되는 순간 딴짓 안 하고 바로 작동할 주인공 프로그램을 강제로 고정하려고 |
| CMD | 실행 시 켜질 기본 명령어/옵션 | 별다른 명령이 없으면 기본값으로 실행하되, 사용자가 원하면 다른 명령어로 쉽게 바꿔 켤 수 있게 하려고 |
| EXPOSE | 외부 연결용 포트 번호 안내 | 다른 컴퓨터나 컨테이너가 접속할 수 있도록 네트워크 문(포트)을 열어두고 알려주려고 |
| COPY / ADD | 내 파일 가상 환경 안으로 복사 | 내 컴퓨터에 있는 소스코드나 설정 파일을 격리된 도커 환경 내부로 집어넣으려고 |
| ENV | 프로그램 실행 내내 쓸 변수 저장 | 컨테이너가 켜져 있는 동안 프로그램이 계속 읽어서 사용할 데이터(비밀번호, 주소 등)를 등록하려고 |
| ARG | 이미지 빌드 중에만 쓸 변수 저장 | 이미지를 만드는 과정(빌드)에서만 잠깐 필요하고, 실행될 때는 굳이 안 남아있어도 되는 임시 변수를 쓰려고 |