본문으로 건너뛰기

🐳 도커란?

📖 정의

Docker는 애플리케이션을 컨테이너(Container)라는 격리된 환경에서 실행하는 플랫폼입니다. 컨테이너는 애플리케이션과 필요한 모든 의존성을 하나로 묶어 어디서든 동일하게 실행할 수 있게 해줍니다. "내 컴퓨터에서는 잘 되는데..."라는 문제를 해결하며, 개발, 테스트, 배포를 간단하고 일관되게 만들어줍니다.

🎯 비유로 이해하기

배송 컨테이너

Docker를 물류 컨테이너에 비유하면:

전통적인 배송 (VM)
├─ 물건마다 다른 포장
├─ 크기/무게 제각각
├─ 운송 방법 다름
└─ 비효율적, 복잡함

컨테이너 배송 (Docker)
├─ 표준화된 컨테이너
├─ 어떤 물건이든 담을 수 있음
├─ 배, 트럭, 기차 어디든 동일하게 운송
└─ 효율적, 간단함

Docker Container = 표준화된 소프트웨어 패키지

아파트 vs 단독 주택

Virtual Machine (VM) = 단독 주택
├─ 각 집마다 완전한 인프라 필요
├─ 땅, 건물, 전기, 수도 모두 별도
├─ 비용 많이 듦
└─ 시작 느림

Docker Container = 아파트
├─ 공통 인프라 공유 (땅, 건물)
├─ 각 세대는 독립적
├─ 효율적
└─ 빠른 입주

⚙️ 작동 원리

1. Docker 아키텍처

┌─────────────────────────────────────┐
│ Docker Client (CLI) │
│ docker run, docker build, etc. │
└───────────────┬─────────────────────┘
│ API 호출
┌───────────────▼─────────────────────┐
│ Docker Daemon │
│ 컨테이너/이미지 관리 │
└───────────────┬─────────────────────┘

┌───────────────▼─────────────────────┐
│ 컨테이너 (실행 중인 앱들) │
│ ┌─────┐ ┌─────┐ ┌─────┐ │
│ │App 1│ │App 2│ │App 3│ │
│ └─────┘ └─────┘ └─────┘ │
└─────────────────────────────────────┘

┌───────────────▼─────────────────────┐
│ Host OS (Linux Kernel) │
└─────────────────────────────────────┘

2. 이미지 vs 컨테이너

이미지 (Image) = 설계도, 클래스
├─ 읽기 전용
├─ 레이어로 구성
├─ 재사용 가능
└─ 예: ubuntu, node, nginx

컨테이너 (Container) = 실행 인스턴스, 객체
├─ 이미지로부터 생성
├─ 실행 가능
├─ 격리된 환경
└─ 여러 개 생성 가능

관계:
Image → Container 1
→ Container 2
→ Container 3

3. Dockerfile → 이미지 → 컨테이너

1. Dockerfile 작성 (레시피)
├─ FROM node:18
├─ COPY package.json .
├─ RUN npm install
└─ CMD ["npm", "start"]

2. 이미지 빌드 (요리)
docker build -t my-app .

3. 컨테이너 실행 (서빙)
docker run -p 3000:3000 my-app

💡 실제 예시

Dockerfile 작성

# Node.js 애플리케이션 Dockerfile

# 베이스 이미지
FROM node:18-alpine

# 작업 디렉토리 설정
WORKDIR /app

# package.json 복사
COPY package*.json ./

# 의존성 설치
RUN npm ci --only=production

# 소스 코드 복사
COPY . .

# 포트 노출
EXPOSE 3000

# 환경변수
ENV NODE_ENV=production

# 컨테이너 시작 시 실행할 명령
CMD ["node", "server.js"]

이미지 빌드 및 실행

# 1. 이미지 빌드
docker build -t my-node-app:1.0 .
# -t: 태그 (이름:버전)
# .: 현재 디렉토리의 Dockerfile 사용

# 2. 이미지 확인
docker images
# REPOSITORY TAG IMAGE ID CREATED
# my-node-app 1.0 abc123 2 minutes ago

# 3. 컨테이너 실행
docker run -d \
--name my-app \
-p 3000:3000 \
-e NODE_ENV=production \
my-node-app:1.0

# -d: 백그라운드 실행
# --name: 컨테이너 이름
# -p: 포트 매핑 (호스트:컨테이너)
# -e: 환경변수

# 4. 실행 중인 컨테이너 확인
docker ps

# 5. 로그 확인
docker logs my-app

# 6. 컨테이너 내부 접속
docker exec -it my-app sh

# 7. 컨테이너 중지
docker stop my-app

# 8. 컨테이너 삭제
docker rm my-app

# 9. 이미지 삭제
docker rmi my-node-app:1.0

Docker Compose (여러 컨테이너 관리)

# docker-compose.yml

version: '3.8'

services:
# 웹 애플리케이션
web:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DB_HOST=database
depends_on:
- database
- redis
volumes:
- ./logs:/app/logs

# 데이터베이스
database:
image: mysql:8.0
environment:
- MYSQL_ROOT_PASSWORD=secret
- MYSQL_DATABASE=myapp
volumes:
- db-data:/var/lib/mysql
ports:
- "3306:3306"

# 캐시
redis:
image: redis:7-alpine
ports:
- "6379:6379"

# Nginx (리버스 프록시)
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
depends_on:
- web

volumes:
db-data:
# Docker Compose 명령어

# 모든 서비스 시작
docker-compose up -d

# 로그 확인
docker-compose logs -f web

# 특정 서비스 재시작
docker-compose restart web

# 서비스 스케일링
docker-compose up -d --scale web=3

# 모든 서비스 중지 및 삭제
docker-compose down

# 볼륨까지 삭제
docker-compose down -v

실전 예시: React + Node.js + MongoDB

# frontend/Dockerfile
FROM node:18-alpine AS build

WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# Nginx로 정적 파일 서빙
FROM nginx:alpine
COPY --from=build /app/build /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
# backend/Dockerfile
FROM node:18-alpine

WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .

EXPOSE 5000
CMD ["node", "server.js"]
# docker-compose.yml
version: '3.8'

services:
frontend:
build:
context: ./frontend
ports:
- "80:80"
depends_on:
- backend

backend:
build:
context: ./backend
ports:
- "5000:5000"
environment:
- MONGO_URL=mongodb://mongodb:27017/myapp
depends_on:
- mongodb

mongodb:
image: mongo:6
volumes:
- mongo-data:/data/db
ports:
- "27017:27017"

volumes:
mongo-data:

데이터 볼륨 관리

# 볼륨 생성
docker volume create my-data

# 볼륨 목록
docker volume ls

# 볼륨을 컨테이너에 마운트
docker run -v my-data:/app/data my-app

# 호스트 디렉토리 마운트 (개발 시 유용)
docker run -v $(pwd):/app my-app

# 읽기 전용 마운트
docker run -v $(pwd):/app:ro my-app

# 볼륨 삭제
docker volume rm my-data

# 사용하지 않는 볼륨 모두 삭제
docker volume prune

.dockerignore 파일

# .dockerignore

# Git
.git
.gitignore

# Node
node_modules
npm-debug.log

# 환경 설정
.env
.env.local

# 빌드 결과물
dist
build

# IDE
.vscode
.idea

# 테스트
coverage
*.test.js

# 문서
README.md
docs/

🤔 자주 묻는 질문

Q1. Docker vs Virtual Machine?

A:

Virtual Machine (VM)
┌─────────────────┐ ┌─────────────────┐
│ App A │ │ App B │
│ Libs │ │ Libs │
│ Guest OS │ │ Guest OS │
│ (Linux) │ │ (Ubuntu) │
└─────────────────┘ └─────────────────┘
┌─────────────────────────────────────┐
│ Hypervisor (VMware 등) │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ Host OS │
└─────────────────────────────────────┘

Docker Container
┌───────┐ ┌───────┐ ┌───────┐
│ App A │ │ App B │ │ App C │
│ Libs │ │ Libs │ │ Libs │
└───────┘ └───────┘ └───────┘
┌─────────────────────────────┐
│ Docker Engine │
└─────────────────────────────┘
┌─────────────────────────────┐
│ Host OS (Linux Kernel) │
└─────────────────────────────┘

비교:
VM Docker
크기 GB MB
시작 분 초
격리 완전 프로세스 레벨
성능 오버헤드 큼 네이티브에 가까움
이식성 낮음 높음

Q2. Docker 이미지 레이어란?

A:

FROM node:18          # Layer 1: 베이스 이미지
WORKDIR /app # Layer 2: 작업 디렉토리
COPY package.json . # Layer 3: package.json
RUN npm install # Layer 4: 의존성 설치
COPY . . # Layer 5: 소스 코드
CMD ["node", "app.js"]# Layer 6: 시작 명령

# 레이어 캐싱
# - 변경되지 않은 레이어는 재사용
# - 빌드 속도 향상

# ✅ 좋은 예: 자주 변경되는 것을 나중에
COPY package.json .
RUN npm install # 캐시됨 (package.json 안 바뀌면)
COPY . . # 코드만 변경

# ❌ 나쁜 예: 자주 변경되는 것을 먼저
COPY . . # 코드 변경마다
RUN npm install # npm install 다시 실행!

Q3. 컨테이너 오케스트레이션이란?

A:

단일 서버
┌─────────────────────┐
│ ┌───┐ ┌───┐ ┌───┐ │
│ │C1 │ │C2 │ │C3 │ │
│ └───┘ └───┘ └───┘ │
└─────────────────────┘

오케스트레이션 (Kubernetes)
┌─────────┐ ┌─────────┐ ┌─────────┐
│ Server1 │ │ Server2 │ │ Server3 │
│ ┌───┐ │ │ ┌───┐ │ │ ┌───┐ │
│ │C1 │ │ │ │C2 │ │ │ │C3 │ │
│ └───┘ │ │ └───┘ │ │ └───┘ │
└─────────┘ └─────────┘ └─────────┘

기능:
- 자동 배포
- 스케일링 (자동 확장/축소)
- 로드 밸런싱
- 자동 복구 (컨테이너 죽으면 재시작)
- 롤링 업데이트

도구:
- Kubernetes (가장 인기)
- Docker Swarm
- Amazon ECS

Q4. 개발 환경에서 Docker 활용은?

A:

# 시나리오: 팀원마다 다른 환경

# 팀원 A: macOS, Node 16
# 팀원 B: Windows, Node 18
# 팀원 C: Linux, Node 14

# 문제: "내 컴퓨터에서는 되는데..."

# 해결: Docker로 환경 통일

# docker-compose.yml
version: '3.8'
services:
app:
image: node:18
volumes:
- .:/app
working_dir: /app
command: npm run dev
ports:
- "3000:3000"

# 모든 팀원이 동일한 환경
docker-compose up

# 장점:
# 1. Node.js 설치 불필요
# 2. 버전 통일
# 3. 의존성 격리
# 4. 환경 설정 공유

Q5. Docker 보안은?

A:

# ✅ 보안 모범 사례

# 1. 최소 권한 베이스 이미지
FROM node:18-alpine # ✅ 작고 안전
FROM node:18 # ❌ 불필요한 도구 많음

# 2. 루트가 아닌 사용자로 실행
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nodejs -u 1001
USER nodejs # ✅ 권한 최소화

# 3. 민감 정보 제외
# .dockerignore에 추가
.env
*.key
secrets/

# 4. 다단계 빌드 (불필요한 도구 제거)
FROM node:18 AS builder
WORKDIR /app
COPY . .
RUN npm run build

FROM node:18-alpine # 작은 이미지
COPY --from=builder /app/dist ./dist
CMD ["node", "dist/main.js"]

# 5. 이미지 스캔
docker scan my-image

# 6. 읽기 전용 루트 파일시스템
docker run --read-only my-app

# 7. 리소스 제한
docker run --memory="512m" --cpus="1" my-app

🎓 다음 단계

Docker를 이해했다면, 다음을 학습해보세요:

  1. Git이란? (문서 작성 예정) - 버전 관리와 배포
  2. Node.js란? (문서 작성 예정) - Docker로 Node 앱 배포
  3. TDD란? - Docker에서 테스트 실행

실습해보기

# 1. Docker 설치 확인
docker --version

# 2. Hello World
docker run hello-world

# 3. Nginx 실행
docker run -d -p 8080:80 nginx
# http://localhost:8080 접속

# 4. 컨테이너 확인
docker ps

# 5. 중지 및 삭제
docker stop <container-id>
docker rm <container-id>

# 6. 자신의 앱 컨테이너화
# - Dockerfile 작성
# - docker build
# - docker run

유용한 명령어

# 이미지 관리
docker pull nginx # 이미지 다운로드
docker images # 이미지 목록
docker rmi nginx # 이미지 삭제

# 컨테이너 관리
docker run # 컨테이너 실행
docker ps # 실행 중인 컨테이너
docker ps -a # 모든 컨테이너
docker stop <id> # 컨테이너 중지
docker start <id> # 컨테이너 시작
docker restart <id> # 컨테이너 재시작
docker rm <id> # 컨테이너 삭제

# 로그 및 디버깅
docker logs <id> # 로그 확인
docker logs -f <id> # 실시간 로그
docker exec -it <id> sh # 컨테이너 내부 접속
docker inspect <id> # 상세 정보

# 정리
docker system prune # 미사용 리소스 삭제
docker system prune -a # 모든 미사용 리소스
docker volume prune # 미사용 볼륨 삭제

🎬 마무리

Docker는 현대 개발의 필수 도구입니다:

  • 컨테이너: 격리된 실행 환경
  • 이미지: 애플리케이션 패키지
  • 이식성: 어디서든 동일하게 실행
  • 효율성: 가볍고 빠름

"내 컴퓨터에서는 되는데..." 문제를 Docker로 해결하세요! 🐳✨