학습 목표
Dockerfile로 이미지를 정의할 수 있다.
이미지 레이어와 빌드 캐시가 속도·크기에 미치는 영향을 설명할 수 있다.
docker build / docker run의 주요 옵션을 사용할 수 있다.
볼륨과 bind mount로 데이터를 유지할 수 있다.
포트 매핑으로 호스트에서 컨테이너 서비스에 접근할 수 있다.
문제 상황
- 로컬에선 되는데 서버에 올리면 Python 버전·라이브러리가 다르다
- "내 머신에선 됐는데"를 줄이려면 이미지로 환경을 고정해야 한다
- Dockerfile을 고칠 때마다
pip install이 10분씩 돈다- 캐시를 왜 못 타는지 모른다
- 컨테이너를 지우면 DB 데이터가 사라진다
- 볼륨 없이 실행한 탓이다
docker run했는데localhost:8080이 안 열린다-p매핑을 안 했거나 앱이0.0.0.0이 아닌127.0.0.1만 listen
컨테이너 개념·VM 비교를 봤다. 이번 편은 Docker CLI로 이미지를 만들고 실행하는 실무 흐름이다.
1. 이미지와 Dockerfile
이미지 — 읽기 전용 레이어 스택 + 메타데이터. 컨테이너는 그 위에 쓰기 가능 레이어를 얹은 실행 인스턴스.
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
| 지시어 | 역할 |
|---|---|
FROM |
베이스 이미지 |
WORKDIR |
이후 명령의 작업 디렉터리 |
COPY / ADD |
파일 복사 (ADD는 URL·tar 자동 해제 — 보통 COPY 선호) |
RUN |
빌드 시 명령 (레이어 생성) |
EXPOSE |
문서화용 포트 (실제 publish는 run -p) |
CMD / ENTRYPOINT |
컨테이너 시작 시 기본 명령 |
docker build -t my-app:1.0 .
docker images
2. 레이어와 빌드 캐시
각 Dockerfile instruction은 (대부분) 새 레이어를 만든다. 변경되지 않은 상위 레이어는 캐시 재사용.

- 자주 바뀌는 것(
COPY . .)은 아래가 아니라 위에 둔다 - 의존성 설치(
requirements.txt+RUN pip)는 소스보다 먼저 — 소스만 바뀌면 pip 캐시 hit .dockerignore로node_modules,.git제외 — 불필요한 캐시 무효화 방지
# 나쁜 예: 소스 변경마다 pip 전체 재실행
COPY . .
RUN pip install -r requirements.txt
# 좋은 예
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
3. docker run
docker run -d --name api \
-p 8080:8000 \
-v app-data:/var/lib/app \
-e DATABASE_URL=postgres://... \
my-app:1.0

| 옵션 | 의미 |
|---|---|
-d |
detached — 백그라운드 |
--name |
컨테이너 이름 |
-p host:container |
포트 publish |
-v name:/path |
named volume |
-v /host/path:/container/path |
bind mount |
-e |
환경 변수 |
--rm |
종료 시 컨테이너 삭제 |
docker ps # 실행 중
docker logs api # stdout/stderr
docker exec -it api sh # 셸 진입
docker stop api && docker rm api
4. 볼륨과 데이터 유지
컨테이너 쓰기 레이어는 컨테이너 삭제 시 사라진다. DB·업로드 파일은 볼륨 또는 bind mount로 분리한다.
| 방식 | 특징 |
|---|---|
| Named volume | Docker가 경로 관리, 이식·백업 용이 |
| Bind mount | 호스트 디렉터리 직접 연결, 개발 시 핫 리로드 |
| tmpfs | 메모리 — 영속 불필요·민감 데이터 |
docker volume create pgdata
docker run -d -v pgdata:/var/lib/postgresql/data postgres:16
- 프로덕션 DB는 보통 관리형 RDS 등; 컨테이너 볼륨은 개발·CI·stateful 워크로드에 한정
- 권한: 호스트 UID와 컨테이너 user mismatch 시 쓰기 실패 —
USER·chown조정
5. 포트와 네트워크 기초
- 컨테이너 내부
EXPOSE 8000만으로는 호스트에서 접근 불가 -p 8080:8000— 호스트 8080 → 컨테이너 8000- 앱은
0.0.0.0에 bind (컨테이너 내부127.0.0.1만 listen하면 외부에서 unreachable) - 기본 bridge 네트워크: 컨테이너끼리는 이름으로 통신하려면 user-defined network +
--network
docker network create appnet
docker run -d --network appnet --name db postgres:16
docker run -d --network appnet -p 8080:8000 --name api my-app:1.0
# api → db:5432 (서비스 이름 = DNS)
6. 정리
- Dockerfile로 재현 가능한 이미지를 만든다
- 레이어 순서가 빌드 시간을 좌우한다
run은 포트·볼륨·환경을 붙여 실행 환경을 완성한다- 데이터는 컨테이너 밖(볼륨)에 둔다
다음에 다룰 것
- 컨테이너 런타임과 보안
- namespace, cgroup, capabilities, rootless
해당 내용은 Docker 공식 문서(https://docs.docker.com) 및 컨테이너 실무 관행을 기반으로 합니다.
'플랫폼, 데브옵스와 클라우드' 카테고리의 다른 글
| Pod와 Deployment (0) | 2026.06.23 |
|---|---|
| Kubernetes 개요 (0) | 2026.06.22 |
| 컨테이너 런타임과 보안 (0) | 2026.06.21 |
| 컨테이너란 무엇인가 (0) | 2026.06.19 |
| 플랫폼 오리엔테이션 (0) | 2026.06.18 |