Step 03: Docker 컨테이너화 및 배포
advanced 60 min

Docker 컨테이너화 및 배포

Docker로 애플리케이션을 패키징하고 배포합니다.

Execute this step

Run from project root:
docker-compose up

Step 3: Docker 컨테이너화 및 배포

학습 목표

  • Rust 애플리케이션을 Docker 이미지로 만들기
  • docker-compose로 전체 스택 실행하기
  • 프로덕션 환경 설정하기

Dockerfile 작성

Dockerfile (멀티 스테이지 빌드):

1# 빌드 스테이지
2FROM rust:1.75 as builder
3
4WORKDIR /app
5
6# 의존성만 먼저 빌드 (캐싱)
7COPY Cargo.toml Cargo.lock ./
8RUN mkdir src && \
9    echo "fn main() {}" > src/main.rs && \
10    cargo build --release && \
11    rm -rf src
12
13# 실제 소스 빌드
14COPY . .
15RUN touch src/main.rs && \
16    cargo build --release --bin web
17
18# 런타임 스테이지
19FROM debian:bookworm-slim
20
21RUN apt-get update && \
22    apt-get install -y libpq5 ca-certificates && \
23    rm -rf /var/lib/apt/lists/*
24
25WORKDIR /app
26
27COPY --from=builder /app/target/release/web /app/web
28COPY --from=builder /app/.env /app/.env
29
30EXPOSE 8080
31
32CMD ["./web"]

docker-compose.yml

1version: '3.8'
2
3services:
4  postgres:
5    image: postgres:15
6    container_name: library-postgres
7    environment:
8      POSTGRES_USER: postgres
9      POSTGRES_PASSWORD: password
10      POSTGRES_DB: library_db
11    ports:
12      - "5432:5432"
13    volumes:
14      - postgres_data:/var/lib/postgresql/data
15    healthcheck:
16      test: ["CMD-SHELL", "pg_isready -U postgres"]
17      interval: 10s
18      timeout: 5s
19      retries: 5
20
21  web:
22    build: .
23    container_name: library-web
24    environment:
25      DATABASE_URL: postgresql://postgres:password@postgres:5432/library_db
26      JWT_SECRET: your-secret-key-change-in-production
27      RUST_LOG: info
28    ports:
29      - "8080:8080"
30    depends_on:
31      postgres:
32        condition: service_healthy
33    command: >
34      sh -c "sleep 5 && ./web"
35
36volumes:
37  postgres_data:

.dockerignore

1target/
2.git/
3.env
4*.log
5Dockerfile
6docker-compose.yml
7README.md

프로덕션 환경 변수

.env.production:

1DATABASE_URL=postgresql://user:password@db-host:5432/library_db
2JWT_SECRET=your-very-secret-key-min-32-characters
3RUST_LOG=info
4BIND_ADDRESS=0.0.0.0:8080

빌드 및 실행

로컬 빌드

1# 이미지 빌드
2docker build -t library-manager .
3
4# 컨테이너 실행
5docker run -p 8080:8080 --env-file .env library-manager

docker-compose 사용

1# 전체 스택 시작
2docker-compose up -d
3
4# 로그 확인
5docker-compose logs -f web
6
7# 마이그레이션 실행
8docker-compose exec web sqlx migrate run
9
10# 종료
11docker-compose down
12
13# 볼륨까지 삭제
14docker-compose down -v

헬스 체크 추가

src/bin/web.rs에 헬스 체크 개선:

1#[derive(Serialize)]
2struct HealthResponse {
3    status: String,
4    database: String,
5    version: String,
6}
7
8async fn health_check(data: web::Data<AppState>) -> HttpResponse {
9    let db_status = match data.db.check_connection().await {
10        Ok(_) => "healthy",
11        Err(_) => "unhealthy",
12    };
13    
14    HttpResponse::Ok().json(HealthResponse {
15        status: "ok".to_string(),
16        database: db_status.to_string(),
17        version: env!("CARGO_PKG_VERSION").to_string(),
18    })
19}

src/db.rs에 추가:

1impl Database {
2    pub async fn check_connection(&self) -> Result<()> {
3        sqlx::query("SELECT 1")
4            .execute(&self.pool)
5            .await
6            .map(|_| ())
7            .map_err(|e| LibraryError::DatabaseError(e.to_string()))
8    }
9}

프로덕션 최적화

1. 로깅 설정

Cargo.toml에 추가:

[dependencies]
env_logger = "0.11"
log = "0.4"

src/bin/web.rs:

1use log::{info, error};
2
3#[actix_web::main]
4async fn main() -> std::io::Result<()> {
5    env_logger::init();
6    
7    info!("Starting library management system");
8    
9    // ...
10    
11    info!("Server listening on http://{}:{}", host, port);
12    
13    HttpServer::new(move || {
14        App::new()
15            .wrap(actix_web::middleware::Logger::default())
16            // ...
17    })
18    .bind((host, port))?
19    .run()
20    .await
21}

2. CORS 설정

[dependencies]
actix-cors = "0.7"
1use actix_cors::Cors;
2
3HttpServer::new(move || {
4    let cors = Cors::default()
5        .allowed_origin("https://your-frontend.com")
6        .allowed_methods(vec!["GET", "POST", "PUT", "DELETE"])
7        .allowed_headers(vec![header::AUTHORIZATION, header::CONTENT_TYPE])
8        .max_age(3600);
9    
10    App::new()
11        .wrap(cors)
12        .wrap(actix_web::middleware::Logger::default())
13        // ...
14})

3. Rate Limiting

[dependencies]
actix-limitation = "0.5"

배포

AWS ECS 배포 예시

  1. ECR에 이미지 푸시:
1# ECR 로그인
2aws ecr get-login-password --region us-east-1 | \
3  docker login --username AWS --password-stdin <account-id>.dkr.ecr.us-east-1.amazonaws.com
4
5# 이미지 태그
6docker tag library-manager:latest <account-id>.dkr.ecr.us-east-1.amazonaws.com/library-manager:latest
7
8# 푸시
9docker push <account-id>.dkr.ecr.us-east-1.amazonaws.com/library-manager:latest
  1. RDS PostgreSQL 설정

  2. ECS Task Definition 생성

  3. ECS Service 배포

간단한 서버 배포

1# 서버에서
2git clone <repo>
3cd library-manager
4docker-compose -f docker-compose.prod.yml up -d
5
6# Nginx 리버스 프록시 설정
7# /etc/nginx/sites-available/library
8server {
9    listen 80;
10    server_name library.example.com;
11    
12    location / {
13        proxy_pass http://localhost:8080;
14        proxy_set_header Host $host;
15        proxy_set_header X-Real-IP $remote_addr;
16    }
17}

모니터링

1# docker-compose.monitoring.yml
2services:
3  prometheus:
4    image: prom/prometheus
5    ports:
6      - "9090:9090"
7    volumes:
8      - ./prometheus.yml:/etc/prometheus/prometheus.yml
9  
10  grafana:
11    image: grafana/grafana
12    ports:
13      - "3000:3000"
14    environment:
15      - GF_SECURITY_ADMIN_PASSWORD=admin

체크리스트

  • [ ] Dockerfile을 작성했습니다
  • [ ] docker-compose로 전체 스택을 실행했습니다
  • [ ] 멀티 스테이지 빌드로 이미지 크기를 최적화했습니다
  • [ ] 헬스 체크를 구현했습니다
  • [ ] 프로덕션 환경 변수를 설정했습니다
  • [ ] 로깅을 추가했습니다

축하합니다! 🎉

초급부터 고급까지 Rust 도서 관리 시스템을 완성했습니다!

배운 내용:

  • ✅ Rust 기초 문법 (소유권, 차용, 구조체, 열거형)
  • ✅ 비동기 프로그래밍 (async/await, tokio)
  • ✅ 웹 개발 (Actix-web, REST API)
  • ✅ 데이터베이스 (PostgreSQL, SQLx)
  • ✅ 인증/인가 (JWT, bcrypt)
  • ✅ 배포 (Docker, docker-compose)

이제 실전 Rust 프로젝트를 만들 준비가 되었습니다!

Did you find this helpful? Give it a cheer!