1단계
1단계 — Docker 기초
35 분
1단계 — Docker 기초
"내 컴퓨터에선 되는데" 가 사라진 이유 = Docker. 코드 · 런타임 · OS 의존성을 하나의 이미지 로 묶어 어디서나 똑같이 동작하게.
1. 핵심 세 단어
- Image — 실행 가능한 템플릿 (read-only). 설계도 + 부품
- Container — Image 를 띄운 실행 인스턴스. 실제 프로세스
- Volume — 컨테이너 외부의 영속 저장소. DB 데이터 등
Image 하나 → Container 여러 개 (동시에 같은 이미지 여러 인스턴스).
2. Docker vs VM
| 가상머신 (VM) | 컨테이너 (Docker) | |
|---|---|---|
| OS | 각자 전체 OS | 호스트 OS 커널 공유 |
| 크기 | 수 GB | 수십 MB |
| 시작 시간 | 분 단위 | 초 단위 |
| 격리 | 강함 | 프로세스 수준 |
| 밀도 | 낮음 | 높음 (한 서버 수백) |
3. 설치
# macOS / Windows
# Docker Desktop 공식 다운로드 (https://www.docker.com/products/docker-desktop/)
# Linux
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER # sudo 없이 쓰려면
# 확인
docker --version
docker run hello-world
hello-world 가 뜨면 설치 완료.
4. 첫 컨테이너
# 한 번만 실행 후 종료
docker run -it --rm alpine sh
# alpine 이미지 자동 다운로드 → 컨테이너 → 셸 진입
# 종료하면 자동 삭제 (--rm)
# 백그라운드 + 포트 노출
docker run -d --name my-postgres \
-e POSTGRES_PASSWORD=secret \
-p 5432:5432 \
postgres:17
# 확인
docker ps # 실행 중 컨테이너
docker ps -a # 종료된 것 포함
docker logs my-postgres # 로그
docker logs -f my-postgres # 로그 실시간
# 컨테이너 안으로
docker exec -it my-postgres psql -U postgres
# 중지 · 제거
docker stop my-postgres
docker rm my-postgres
5. 자주 쓰는 명령 10 개
docker images # 이미지 목록
docker pull node:20-alpine # 이미지 다운로드만
docker rmi 이미지이름 # 이미지 제거
docker ps -a # 모든 컨테이너
docker exec -it 이름 sh # 컨테이너 안 셸
docker logs -f 이름 # 로그 실시간
docker inspect 이름 # 상세 정보 (JSON)
docker stats # 실시간 CPU · 메모리
docker network ls # 네트워크 목록
docker system prune # 사용 안 하는 리소스 정리
6. Dockerfile — 내 앱을 이미지로
FROM node:20-alpine
WORKDIR /app
# 의존성 먼저 복사 (캐시 활용)
COPY package.json pnpm-lock.yaml ./
RUN corepack enable && pnpm install --frozen-lockfile
# 소스 복사 + 빌드
COPY . .
RUN pnpm build
EXPOSE 3000
CMD ["pnpm", "start"]
핵심 명령
| 명령 | 역할 |
|---|---|
FROM |
베이스 이미지 |
WORKDIR |
작업 디렉터리 |
COPY |
파일 복사 |
RUN |
빌드 시점 명령 |
CMD |
컨테이너 시작 시 명령 |
EXPOSE |
포트 문서화 (실제 노출은 -p) |
ENV |
환경변수 |
ARG |
빌드 인자 |
빌드 + 실행
docker build -t my-app .
docker run -d -p 3000:3000 --name my-app-c my-app
curl http://localhost:3000
7. 멀티스테이지 빌드 — 이미지 1/3
# ---- 빌드 스테이지 ----
FROM node:20-alpine AS builder
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN corepack enable && pnpm install --frozen-lockfile
COPY . .
RUN pnpm build
# ---- 실행 스테이지 ----
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/public ./public
EXPOSE 3000
CMD ["node", "server.js"]
빌드 도구 · TypeScript · eslint 가 최종 이미지에 안 들어감. 이미지 1 GB → 300 MB 수준.
8. .dockerignore
package.json 옆에 .dockerignore:
node_modules
.next
.git
.env
.env.local
*.log
README.md
.github
.vscode
빌드 컨텍스트에서 제외. 빌드 속도 · 이미지 크기 · 보안 모두 개선.
9. 볼륨 — 데이터 영속
컨테이너는 일회성. 재시작하면 데이터 소실.
# 명명 볼륨
docker volume create pg-data
docker run -d --name pg \
-v pg-data:/var/lib/postgresql/data \
-e POSTGRES_PASSWORD=secret \
postgres:17
# 컨테이너 삭제 후 재생성해도 데이터 유지
docker rm -f pg
docker run -d --name pg \
-v pg-data:/var/lib/postgresql/data \
-e POSTGRES_PASSWORD=secret \
postgres:17
# → 이전 데이터 그대로
# 호스트 경로 마운트
docker run -v $(pwd)/data:/app/data my-app
10. 네트워크
# 브리지 네트워크 생성
docker network create my-net
# 같은 네트워크의 컨테이너끼리 이름으로 통신 가능
docker run -d --name db --network my-net postgres:17
docker run -d --name app --network my-net \
-e DATABASE_URL=postgres://db:5432/... \
my-app
# app 에서 "db" 라는 호스트명으로 접근 가능
docker-compose 가 이 패턴을 자동화.
11. 이미지 최적화 팁
- Alpine 베이스 — 5~10MB · 단 일부 NPM 네이티브 모듈 빌드 이슈
- Distroless (Google) — OS 셸조차 없음 · 가장 작음
- 레이어 최소화 —
RUN apt-get update && apt-get install ...한 줄로 .dockerignore철저히- 의존성 먼저 COPY — 소스 변경만으로 full rebuild 방지
12. 보안 기본
- root 로 실행 금지:
RUN addgroup -g 1001 app && adduser -D -u 1001 -G app app USER app - 비밀번호 · 키 — 이미지에 절대 포함 금지. 런타임
-e또는 secret - 이미지 스캔 —
docker scout cves my-app - 최소 권한 — read-only filesystem (
--read-only)
13. 자주 걸리는 자리
- 포트 충돌 —
-p 3000:3000호스트 포트 이미 사용 중 - 이미지 빌드 매우 느림 —
.dockerignore없거나 COPY 순서 잘못 - 컨테이너 금방 종료 — CMD 가 foreground 가 아님 ·
docker logs확인 - 권한 문제 — 볼륨 마운트 시 host user UID 와 컨테이너 user UID 불일치
- 디스크 꽉 참 — 오래된 이미지 · 컨테이너 누적.
docker system prune -a
14. 직접 해 보기
위 Dockerfile 로 작은 Next.js 앱:
pnpm create next-app@latest my-test
cd my-test
# Dockerfile 복사 + 빌드
docker build -t my-test .
docker run -d -p 3000:3000 --name my-test-c my-test
curl http://localhost:3000
# 정리
docker rm -f my-test-c
docker rmi my-test
더 깊이
다음 단계
2단계에서는 여러 컨테이너를 한 번에 띄우는 docker-compose 패턴을 배워요.