
Docker에서 이미지와 컨테이너의 개념을 이해했다면 다음으로 봐야 할 것이 Dockerfile이다.
앞에서 정리한 흐름을 다시 보면 다음과 같다.
Dockerfile
↓
docker build
↓
Docker Image
↓
docker run
↓
Docker Container
즉, Dockerfile은 Docker 이미지를 어떻게 만들 것인지 작성해두는 파일이다.
가장 간단하게 표현하면 다음과 같다.
Dockerfile = Docker 이미지를 만들기 위한 설계서
이미지가 완성된 결과물이라면, Dockerfile은 그 이미지를 만드는 방법을 적어둔 레시피에 가깝다.
1. Dockerfile은 왜 필요할까?
예를 들어 Python으로 만든 간단한 프로그램이 있다고 해보자.
project/
│
├─ app.py
└─ requirements.txt
이 프로그램을 실행하려면 보통 다음과 같은 과정이 필요하다.
1. Python 설치
2. 필요한 라이브러리 설치
3. 소스 코드 복사
4. 프로그램 실행
직접 서버에 접속해서 하나씩 설정할 수도 있다.
하지만 서버가 여러 대라면 매번 같은 작업을 반복해야 한다.
서버 A
Python 설치
라이브러리 설치
코드 복사
서버 B
Python 설치
라이브러리 설치
코드 복사
서버 C
Python 설치
라이브러리 설치
코드 복사
이렇게 하면 서버마다 설정이 조금씩 달라질 수도 있고, 실수도 발생하기 쉽다.
Dockerfile을 사용하면 이런 과정을 코드로 정리할 수 있다.
FROM python:3.12
WORKDIR /app
COPY . .
RUN pip install -r requirements.txt
CMD ["python", "app.py"]
이제 이 Dockerfile만 있으면 같은 Docker 이미지를 반복해서 만들 수 있다.
2. Dockerfile의 기본 구조
Dockerfile에는 여러 가지 명령어가 들어간다.
대표적으로 많이 사용하는 명령어는 다음과 같다.
FROM
WORKDIR
COPY
RUN
EXPOSE
ENV
CMD
ENTRYPOINT
기본적으로는 다음 흐름만 이해하면 된다.
어떤 이미지를 기반으로 할지 정하고
↓
작업할 폴더를 지정하고
↓
파일을 복사하고
↓
필요한 프로그램을 설치하고
↓
컨테이너가 시작될 때 실행할 명령을 지정
이 흐름을 Dockerfile로 표현한 것이 다음과 같다.
FROM python:3.12
WORKDIR /app
COPY . .
RUN pip install -r requirements.txt
CMD ["python", "app.py"]
하나씩 살펴보자.
3. FROM
FROM python:3.12
FROM은 어떤 이미지를 기반으로 새로운 이미지를 만들 것인지 지정한다.
Docker 이미지를 완전히 아무것도 없는 상태에서 직접 만드는 경우는 드물다.
보통 기존에 만들어져 있는 이미지를 기반으로 시작한다.
예를 들어
FROM python:3.12
이라고 작성하면 Python 3.12가 설치된 이미지를 기반으로 한다는 뜻이다.
구조로 보면 다음과 같다.
Python 3.12 Image
│
↓
내 애플리케이션 추가
│
↓
새로운 Docker Image
Node.js라면 다음과 같이 사용할 수 있다.
FROM node:22
Ubuntu를 기반으로 하고 싶다면 다음과 같이 작성할 수도 있다.
FROM ubuntu:24.04
즉 FROM은 Dockerfile의 출발점이다.
4. WORKDIR
WORKDIR /app
WORKDIR은 컨테이너 내부에서 작업할 기본 디렉터리를 지정한다.
여기서 중요한 점은 /app이 내 컴퓨터의 폴더 이름을 의미하는 것이 아니라는 점이다.
예를 들어 내 프로젝트가 다음처럼 되어 있다고 해보자.
내 컴퓨터
was/
│
├─ app.py
└─ Dockerfile
Dockerfile에 다음과 같이 작성해도 된다.
WORKDIR /app
내 컴퓨터의 폴더 이름이 was라고 해서
WORKDIR /was
로 작성해야 하는 것은 아니다.
WORKDIR은 컨테이너 내부에서 사용할 경로를 직접 정하는 것이다.
즉 다음과 같은 관계다.
내 컴퓨터
was/
├─ app.py
└─ Dockerfile
↓ Docker Image 생성
Container
/app
└─ app.py
이후 RUN, COPY, CMD 같은 명령어들은 이 작업 디렉터리를 기준으로 동작할 수 있다.
5. COPY
COPY . .
COPY는 호스트에 있는 파일을 Docker 이미지 안으로 복사하는 명령어다.
형식은 다음과 같다.
COPY 원본경로 대상경로
예를 들어
COPY app.py /app/app.py
라고 하면
내 컴퓨터
app.py
↓ COPY
Docker Image
/app/app.py
가 된다.
그런데 자주 사용하는 형태가 다음이다.
COPY . .
처음 보면 점이 두 개라 헷갈릴 수 있다.
앞의 .은
현재 Docker build를 실행한 폴더
를 의미하고,
뒤의 .은 현재 WORKDIR을 의미한다.
예를 들어
WORKDIR /app
COPY . .
라면 다음과 같이 이해할 수 있다.
현재 프로젝트 폴더
↓
Container의 /app
6. RUN
RUN pip install -r requirements.txt
RUN은 이미지를 만드는 과정에서 명령어를 실행한다.
예를 들어 Python 프로젝트에서 필요한 라이브러리를 설치하려면 다음처럼 작성할 수 있다.
RUN pip install -r requirements.txt
Node.js 프로젝트라면 다음처럼 사용할 수도 있다.
RUN npm install
Ubuntu 기반 이미지에서는 Linux 명령어도 사용할 수 있다.
RUN apt update
중요한 점은 RUN이 컨테이너 실행 시점이 아니라 이미지를 만드는 시점에 실행된다는 것이다.
docker build
│
├─ FROM
├─ COPY
├─ RUN ← 여기서 실행
│
↓
Docker Image 완성
7. CMD
CMD ["python", "app.py"]
CMD는 컨테이너가 시작될 때 기본적으로 실행할 명령어를 지정한다.
앞에서 본 RUN과 가장 많이 헷갈리는 부분이다.
둘의 차이는 다음과 같다.
RUN
→ 이미지 만드는 중에 실행
CMD
→ 컨테이너가 시작될 때 실행
예를 들어 다음 Dockerfile이 있다고 해보자.
FROM python:3.12
WORKDIR /app
COPY . .
RUN pip install -r requirements.txt
CMD ["python", "app.py"]
docker build를 실행할 때는
Python 이미지 준비
파일 복사
라이브러리 설치
까지 진행된다.
그리고
docker run my-app
을 실행하면 마지막 CMD가 실행된다.
python app.py
즉 프로그램이 실제로 시작된다.
8. RUN과 CMD 차이
이 둘은 반드시 구분해두는 것이 좋다.
| 구분 | RUN | CMD |
| 실행 시점 | 이미지 빌드 중 | 컨테이너 시작 시 |
| 사용 목적 | 설치, 설정 | 프로그램 실행 |
| 실행 횟수 | 이미지 생성 시 | 컨테이너 실행할 때마다 |
예를 들어
RUN pip install flask
는 Flask를 이미지 안에 설치한다.
반면
CMD ["python", "app.py"]
는 컨테이너가 실행될 때 Python 프로그램을 시작한다.
흐름으로 보면 더 쉽게 이해할 수 있다.
Dockerfile
RUN pip install ...
↓
docker build
↓
Image 완성
CMD python app.py
↓
docker run
↓
Container 실행
9. EXPOSE
웹 애플리케이션을 만들다 보면 다음 명령어도 자주 보게 된다.
EXPOSE 8000
EXPOSE는 이 컨테이너가 어떤 포트를 사용할 예정인지 표시하는 역할을 한다.
예를 들어 FastAPI 애플리케이션이 8000번 포트에서 실행된다면 다음처럼 작성할 수 있다.
EXPOSE 8000
하지만 여기서 주의할 점이 있다.
EXPOSE를 작성했다고 해서 내 PC에서 바로 8000번 포트로 접근할 수 있는 것은 아니다.
실제로 호스트와 컨테이너의 포트를 연결하려면 docker run에서 포트 매핑을 해줘야 한다.
docker run -p 8000:8000 my-app
구조는 다음과 같다.
내 PC
localhost:8000
│
↓
Container
8000
즉,
EXPOSE
→ 이 컨테이너는 이 포트를 사용한다는 정보
-p
→ 실제로 호스트 포트와 컨테이너 포트를 연결
이라고 이해하면 된다.
10. ENV
환경 변수를 이미지나 컨테이너에서 사용하고 싶을 때는 ENV를 사용할 수 있다.
ENV APP_ENV=production
그러면 컨테이너 내부에서 다음 환경 변수를 사용할 수 있다.
APP_ENV=production
예를 들어 Python에서는 다음처럼 접근할 수 있다.
import os
env = os.getenv("APP_ENV")
다만 비밀번호나 API Key 같은 민감한 값은 Dockerfile에 직접 작성하지 않는 것이 좋다.
Dockerfile은 보통 Git에 올라가기 때문이다.
ENV PASSWORD=1234
이런 식으로 민감 정보를 직접 넣는 것은 피하는 것이 좋다.
11. ENTRYPOINT는 뭐가 다를까?
CMD와 비슷한 명령어로 ENTRYPOINT도 있다.
예를 들어
ENTRYPOINT ["python", "app.py"]
처럼 사용할 수 있다.
둘 다 컨테이너가 시작될 때 실행되는 명령어를 지정하지만 차이가 있다.
간단하게 이해하면
CMD
→ 기본 실행 명령
ENTRYPOINT
→ 반드시 실행할 메인 명령
이라고 볼 수 있다.
처음 Docker를 공부할 때는 CMD부터 익숙해지는 것이 좋다.
실제로 간단한 애플리케이션에서는 CMD만 사용해도 충분한 경우가 많다.
12. Dockerfile로 이미지 만들기
이제 실제 Dockerfile이 있다고 해보자.
FROM python:3.12
WORKDIR /app
COPY . .
CMD ["python", "app.py"]
프로젝트 구조가 다음과 같다고 하자.
was/
│
├─ app.py
└─ Dockerfile
터미널에서 was 폴더로 이동한 뒤 다음 명령어를 실행한다.
docker build -t was-app .
여기서
docker build
은 Docker 이미지를 만들라는 명령어다.
-t was-app
은 이미지의 이름을 was-app으로 지정한다는 뜻이다.
마지막 .은 현재 디렉터리를 Build Context로 사용한다는 의미다.
was/
│
├─ app.py
└─ Dockerfile
↑
현재 위치에서 docker build 실행
Docker는 이 폴더의 파일을 기준으로 이미지를 만든다.
13. 만들어진 이미지 확인하기
이미지가 제대로 생성되었는지 확인하려면 다음 명령어를 사용한다.
docker images
또는
docker image ls
예를 들어 다음과 같이 나타날 수 있다.
REPOSITORY TAG IMAGE ID
was-app latest abcd1234
python 3.12 efgh5678
여기서 was-app이 방금 Dockerfile로 만든 이미지다.
14. 이미지 실행하기
이제 만든 이미지를 컨테이너로 실행해보자.
docker run was-app
그러면 Docker가 다음 과정을 수행한다.
was-app Image
│
│ docker run
↓
Container 생성
│
↓
CMD 실행
│
↓
python app.py
즉 Dockerfile에서 작성했던
CMD ["python", "app.py"]
가 실제로 실행된다.
15. Dockerfile은 한 줄씩 Layer를 만든다
Docker 이미지를 이해할 때 알아두면 좋은 개념이 Layer다.
Dockerfile의 주요 명령어들은 이미지에 층을 하나씩 추가한다.
예를 들어
FROM python:3.12
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["python", "app.py"]
라고 작성하면 개념적으로 다음과 같이 쌓인다.
┌──────────────────────────┐
│ CMD python app.py │
├──────────────────────────┤
│ COPY source code │
├──────────────────────────┤
│ RUN pip install │
├──────────────────────────┤
│ COPY requirements.txt │
├──────────────────────────┤
│ WORKDIR /app │
├──────────────────────────┤
│ Python 3.12 Base Image │
└──────────────────────────┘
Docker는 이 Layer들을 캐시해서 이미지 빌드 속도를 줄인다.
그래서 Dockerfile의 작성 순서도 중요하다.
16. COPY 순서가 중요한 이유
예를 들어 다음과 같이 작성했다고 해보자.
COPY . .
RUN pip install -r requirements.txt
소스 코드 하나만 수정해도 COPY . . 단계가 변경된다.
그러면 그 아래에 있는
RUN pip install -r requirements.txt
도 다시 실행될 수 있다.
하지만 다음처럼 작성하면 더 효율적이다.
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
이 경우 requirements.txt가 바뀌지 않았다면 라이브러리 설치 과정은 캐시를 사용할 수 있다.
requirements.txt 변경 없음
↓
pip install Layer 재사용
소스 코드만 변경
↓
마지막 COPY만 다시 실행
그래서 실제 프로젝트에서는 이런 식으로 Dockerfile을 작성하는 경우가 많다.
17. .dockerignore도 같이 알아두자
프로젝트 폴더에는 Docker 이미지에 굳이 들어갈 필요가 없는 파일도 많다.
예를 들어
.git/
venv/
__pycache__/
.env
node_modules/
같은 파일들이다.
이런 파일까지 COPY . .로 복사하면 이미지가 불필요하게 커질 수 있다.
이를 막기 위해 .dockerignore 파일을 사용한다.
예를 들어
.git
.env
__pycache__
venv
처럼 작성한다.
그러면 Docker는 해당 파일을 Build Context에서 제외한다.
개념적으로는 .gitignore와 비슷하다.
18. Dockerfile 전체 흐름으로 보기
지금까지 내용을 한 번에 정리하면 다음과 같다.
내 프로젝트
was/
│
├─ app.py
├─ requirements.txt
├─ .dockerignore
└─ Dockerfile
│
│
↓ docker build
┌──────────────────────────┐
│ Docker Image │
│ │
│ Python │
│ 필요한 라이브러리 │
│ app.py │
│ 실행 명령 │
└──────────────────────────┘
│
│ docker run
↓
┌──────────────────────────┐
│ Docker Container │
│ │
│ python app.py 실행 │
└──────────────────────────┘
Dockerfile은 결국 애플리케이션을 어떤 환경에서 어떻게 실행할지를 코드로 작성한 파일이다.
19. 자주 사용하는 Dockerfile 명령어 정리
명령어역할
| FROM | 기반 이미지 지정 |
| WORKDIR | 컨테이너 내부 작업 디렉터리 지정 |
| COPY | 호스트 파일을 이미지로 복사 |
| RUN | 이미지 빌드 중 명령 실행 |
| CMD | 컨테이너 실행 시 기본 명령 |
| ENTRYPOINT | 컨테이너의 메인 실행 명령 |
| EXPOSE | 사용할 포트 정보 명시 |
| ENV | 환경 변수 설정 |
처음 Dockerfile을 공부한다면 우선 다음 다섯 개부터 익히면 된다.
FROM
WORKDIR
COPY
RUN
CMD
이 다섯 개만 알아도 대부분의 간단한 Docker 이미지는 직접 만들 수 있다.
20. 핵심 정리
Dockerfile의 역할을 한 문장으로 정리하면 다음과 같다.
Dockerfile은 Docker 이미지를 어떤 순서와 설정으로 만들 것인지 작성한 파일이다.
그리고 전체 흐름은 다음과 같다.
Dockerfile
│
│ docker build
↓
Docker Image
│
│ docker run
↓
Docker Container
각 개념의 관계를 다시 정리하면 다음과 같다.
Dockerfile
= 만드는 방법
Docker Image
= 만들어진 실행 환경
Docker Container
= 이미지를 실제로 실행한 상태
처음에는 Dockerfile의 명령어를 하나씩 외우려고 하기보다는,
기반 이미지 선택
→ 작업 폴더 지정
→ 파일 복사
→ 필요한 것 설치
→ 실행 명령 지정
이라는 흐름을 이해하는 것이 더 중요하다.
이 흐름이 익숙해지면 Python, Spring Boot, Node.js 같은 애플리케이션도 같은 방식으로 Docker 이미지로 만들 수 있다.
'개발 > Docker' 카테고리의 다른 글
| [Docker] Docker로 CRUD 게시판 만들어보기 2편 - Volume과 Bind Mount 적용하기 (0) | 2026.08.22 |
|---|---|
| [Docker] Docker로 CRUD 게시판 만들어보기 1편 - Flask 게시판을 컨테이너로 실행하기 (0) | 2026.08.22 |
| [Docker] Docker 볼륨과 바인드 마운트의 차이 (0) | 2026.08.20 |
| [Docker] Docker 이미지와 컨테이너 개념 정리 (0) | 2026.08.20 |
| [Docker] Docker는 무엇일까 (3) | 2025.08.16 |