이미지를 직접 만드는 법을 정리한다.
Dockerfile의 핵심 명령어와 작성 순서, 빌드와 실행,
그리고 완성한 이미지를 Docker Hub에 올려 공유하는 것까지
Python, Nginx 같은 이미지는 받아서 쓰면 된다.
그런데 진짜 필요한 건 내 코드가 담긴 이미지다.
내 모델, 내 서버 코드, 실행에 필요한 환경이 함께 담긴 이미지.
그건 어디에도 없어서 직접 만들어야 한다.
그 설계도를 쓰는 파일이 Dockerfile이다.
Docker에서 머리를 쓰는 구간은 사실상 여기부터라고 느꼈다.
Dockerfile은 이미지를 만드는 설명서다
전체 흐름은 이렇게 이어진다.
Dockerfile 작성 → docker build → 이미지 완성 → docker run → 컨테이너 실행
Dockerfile은 "이 환경을 어떻게 만드는지"를 순서대로 적은 텍스트 파일이다.
어떤 Python 이미지를 기반으로 하고, 라이브러리는 뭘 설치하고, 코드는 어디에 두고, 마지막에 어떤 명령으로 실행하는지
환경 설치 과정 전체를 코드로 적어두는 것이다.
이렇게 적어두면 좋은 점은 명확하다.
사람이 손으로 설치하면 매번 결과가 조금씩 다르지만, 파일로 적어두면 몇 번을 빌드해도 똑같은 이미지가 나온다.
프로젝트 폴더 준비
Dockerfile을 쓰기 전에, 폴더 구조부터 잡는 게 순서다. 기본형은 이렇다.
my-app/
├── Dockerfile ← 이미지 설계도
├── .dockerignore ← 이미지에 넣지 않을 파일 목록
├── requirements.txt ← 파이썬 라이브러리 목록
└── src/
└── main.py ← 실제 코드
.dockerignore는 Git의 .gitignore와 같은 역할이다.
가상환경 폴더, 캐시, 데이터처럼 이미지에 들어갈 필요 없는 것들을 적어두면 빌드할 때 제외된다.
Dockerfile 한 줄씩 뜯어보기
가장 기본적인 Dockerfile 완성본을 먼저 보고, 한 줄씩 뜯어보자.
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY src ./src
CMD ["python", "-m", "src.main"]
Docker를 로봇이라고 생각하면 이해가 쉽다.
로봇이 빈 방에서 이 설명서를 위에서부터 한 줄씩 실행하며 이미지를 조립해 나간다.
FROM ─ 바닥에 깔 베이스 이미지. 여기서는 경량 Python 이미지를 기반으로 삼았다. 항상 첫 줄이다.
WORKDIR ─ 로봇의 작업 위치 지정. "이제부터 /app 폴더에서 일해"라는 뜻이다.
COPY ─ 내 컴퓨터의 파일을 이미지 안으로 복사. 헷갈리기 쉬운 부분인데,COPY의 앞과 뒤에서 앞은 내 컴퓨터 쪽 경로, 뒤는 이미지 안쪽 경로다
RUN ─ 이미지 안에서 명령어 실행. 여기서는 라이브러리 설치.
CMD ─ 이 이미지로 컨테이너를 시작할 때 실행할 명령. 빌드 중에 실행되는 RUN과 달리, CMD는 나중에 컨테이너가 켜질 때 실행된다.

왜 requirements를 코드보다 먼저 복사할까
위 Dockerfile에서 이상한 점이 하나 있다.
requirements.txt만 먼저 복사해서 설치하고, 코드는 그 뒤에 따로 복사한다.
왜 굳이 두 번에 나눌까?
이유는 캐시다.
Dockerfile의 한 줄이 이미지의 한 층(레이어)이 되고,
다시 빌드할 때 바뀌지 않은 층은 캐시에서 재사용된다.
대신 어떤 층이 바뀌면 그 아래 줄들은 전부 다시 실행된다.
코드는 매일 고치지만 라이브러리 목록은 가끔 바뀐다.
그래서 라이브러리 설치를 코드 복사보다 위에 두면, 코드만 고쳤을 때 설치 단계는 캐시로 건너뛴다.
코드만 수정하고 재빌드하면:
FROM python:3.12-slim → 캐시 재사용
WORKDIR /app → 캐시 재사용
COPY requirements.txt . → 캐시 재사용
RUN pip install -r ... → 캐시 재사용 (몇 분 절약!)
COPY src ./src → 여기부터만 다시 실행
CMD [...]
AI 프로젝트라면 이 차이가 특히 크다.
PyTorch 같은 라이브러리는 설치에 몇 분씩 걸리는데,
순서를 잘못 잡으면 코드 한 줄 고칠 때마다 그 시간을 다시 기다리게 된다.
원칙은 하나다. 자주 바뀌는 줄일수록 아래로.
빌드하고 실행하기
Dockerfile을 다 썼으면 빌드한다.
# 이미지 빌드 (-t: 이미지 이름 붙이기, 마지막 점: 현재 폴더 기준)
docker build -t my-app .
# 만든 이미지로 컨테이너 실행
docker run my-app
빌드 로그를 보면 Dockerfile의 각 줄이 순서대로 실행되는 게 그대로 보인다.
첫 빌드는 베이스 이미지를 받고 라이브러리를 까느라 몇 분 걸리지만,
코드만 고치고 다시 빌드하면 캐시 덕에 몇 초 만에 끝난다.
이 빌드-실행이 잘 되면, 내 코드가 담긴 이미지가 완성된 것이다.
알아두면 좋은 명령어들
기본형 외에 자주 만나는 명령어가 몇 개 있다.
ENV와 ARG
둘 다 변수를 설정하는데 수명이 다르다. ENV는 빌드할 때와 컨테이너 실행 중에도 유지되는 환경 변수고, ARG는 빌드하는 동안만 쓰이고 사라진다.
여기서 주의할 점 하나.
API 키나 비밀번호를 ENV에 직접 적으면 안 된다.
docker inspect 한 번이면 이미지의 환경 변수가 전부 노출되기 때문이다.
비밀값은 Dockerfile 안에 직접 적지 말고, .env 파일이나 배포 환경의 Secret 설정으로 분리해서 실행할 때 주입하는 게 안전하다.
EXPOSE
"이 이미지는 이 포트를 사용한다"는 표시. 문서 역할일 뿐 실제로 포트를 열지는 않는다.
실제 연결은 실행할 때 -p 옵션으로 한다.
ENTRYPOINT
CMD와 비슷하게 시작 명령을 정하는데, 차이가 있다.
CMD는 실행할 때 다른 명령으로 덮어쓸 수 있는 기본값이고, ENTRYPOINT는 고정 명령이다.
보통은 CMD만으로 충분하다.
Docker Hub에 올리기 ─ 이미지 공유
이미지를 만들었으면 공유할 차례다.
코드를 GitHub에 올리듯, 이미지는 Docker Hub에 올린다.
절차는 세 단계다. 로그인 → 태그 달기 → 푸시
1) Docker Hub 로그인
docker login
2) 태그 달기 (계정명/이미지명:버전)
docker tag my-app 내계정명/my-app:v1.0.0
3) 업로드
docker push 내계정명/my-app:v1.0.0
여기서 태그를 먼저 달아야 하는 이유가 있다.
Docker Hub는 계정명/이미지명 형식으로 저장소를 구분하기 때문에, 그 형식의 이름표를 붙여야 올라간다.
올리고 나면 hub.docker.com의 내 저장소에 이미지가 생기고, 이렇게 받을 수 있다.
docker pull 내계정명/my-app:v1.0.0
권한 설정에 따라 공개 이미지로 공유할 수도 있고, 제한된 사람만 받게 관리할 수도 있다.
이제 코드가 아니라 실행에 필요한 환경을 함께 주고받는 것이다.

버전 태그 규칙 ─ 숫자 세 개의 의미
태그의 v1.0.0 같은 숫자에는 관례가 있다. 세 자리가 각각 변화의 크기를 뜻한다.
v 1 . 2 . 3
│ │ └─ patch : 버그 수정, 작은 개선
│ └───── minor : 기능 추가 (기존 사용법은 그대로)
└───────── major : 구조가 바뀌는 큰 변화 (호환 안 될 수 있음)
AI 서비스 기준으로 예를 들면 감이 온다.
- 모델 자체를 다른 모델로 교체했다 → major를 올린다 (v2.0.0)
- 전처리 단계를 하나 추가했다 → minor를 올린다 (v1.1.0)
- 모델 로드할 때 한글 깨지던 버그를 고쳤다 → patch를 올린다 (v1.0.1)
이미지는 한번 만들면 수정할 수 없으니, 수정 = 재빌드 + 새 태그 + 푸시가 한 세트다.
이렇게 버전이 쌓이면 문제가 생겼을 때 이전 버전으로 바로 되돌아갈 수 있다.
정리
- Dockerfile은 이미지를 만드는 설명서다. 환경 설치 과정을 코드로 적어두면 몇 번을 빌드해도 같은 이미지가 나온다.
- 기본 골격은 FROM(베이스) → WORKDIR(작업 위치) → COPY+RUN(의존성 설치) → COPY(코드) → CMD(실행 명령) 순서다.
- COPY는 앞이 내 컴퓨터, 뒤가 이미지 안쪽 경로다.
- 한 줄이 한 레이어다. 자주 바뀌는 줄(코드)을 아래에 둬야 캐시를 살려 빌드가 빨라진다.
- 비밀값은 ENV에 직접 적지 말고 .env 파일이나 배포 환경의 Secret 설정으로 분리한다. docker inspect로 노출될 수 있다.
- 이미지 공유는 login → tag → push 세 단계. 받을 때는 pull이다.
- 버전 태그는 major(큰 구조 변화). minor(기능 추가). patch(버그 수정) 규칙으로 올린다.
빈 폴더에서 시작해 Dockerfile을 쓰고, 빌드하고, Docker Hub에 올리기까지가 한 사이클이다.
이 사이클이 손에 익으면, "실행에 필요한 환경을 함께 공유한다"는 말이 더 이상 추상적으로 들리지 않는다.
'[MLOps] 모델 서빙과 배포 > Docker와 컨테이너' 카테고리의 다른 글
| Docker Compose와 쿠버네티스 ─ 컨테이너 여러 개를 한 번에 관리하기 (0) | 2026.07.12 |
|---|---|
| Docker 컨테이너 사용법 ─ 생명주기, 볼륨, 포트, 네트워크 (0) | 2026.07.11 |
| Docker 이미지 구조 ─ 레이어, 태그, 캐시의 원리 (0) | 2026.07.11 |
| Docker란? ─ "내 컴퓨터에서는 되는데요"를 해결하는 기술 (0) | 2026.07.11 |