#배포 가이드

버전: 0.33.0 최종 업데이트: 2026-03-15 적용 대상: ranvier, ranvier-runtime, ranvier-http 카테고리: Operations


이 가이드는 Ranvier 애플리케이션의 컨테이너화된 배포를 다룹니다 — 빌드 전략, Docker 다단계 빌드, Kubernetes 매니페스트, 리버스 프록시, 환경 설정을 포함합니다.

#1. 빌드 전략

Ranvier 애플리케이션은 프로덕션 환경을 위해 완전한 정적 musl 바이너리로 컴파일됩니다. 이를 통해 OS 수준의 종속성이 제거되고 FROM scratch 런타임 이미지를 사용할 수 있습니다.

빌드 모드 타겟 사용 사례
네이티브 디버그 호스트 타겟 로컬 반복 개발
네이티브 릴리스 호스트 타겟 로컬 통합 테스트
musl 릴리스 x86_64-unknown-linux-musl 컨테이너 / 프로덕션
# musl 타겟 추가 (Linux)
rustup target add x86_64-unknown-linux-musl
cargo build --release --target x86_64-unknown-linux-musl

Windows/macOS에서는 musl 컴파일을 위해 Docker 빌더 스테이지를 사용하세요 (§2 참조).


#2. Docker 다단계 빌드

# ---- Builder ----
FROM clux/muslrust:stable AS builder
WORKDIR /app
COPY Cargo.toml Cargo.lock ./

# 종속성 캐싱: 먼저 더미 바이너리를 빌드
RUN mkdir -p src && echo 'fn main(){}' > src/main.rs
RUN cargo build --release --target x86_64-unknown-linux-musl && \
    rm -rf src target/x86_64-unknown-linux-musl/release/deps/my_app*

COPY src ./src
RUN cargo build --release --target x86_64-unknown-linux-musl

# ---- Runtime ----
FROM scratch
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=builder /app/target/x86_64-unknown-linux-musl/release/my-app /app
EXPOSE 3000
ENTRYPOINT ["/app"]

주요 결정 사항:

선택 근거
clux/muslrust:stable 전체 Rust + musl 툴체인
FROM scratch OS 표면적 제로, 최소 용량
rustls 전용 OpenSSL 종속성 없음 — scratch에서 작동
CA 인증서 복사 아웃바운드 HTTPS 연결에 필요

#3. Kubernetes 매니페스트

#Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: ranvier-app
spec:
  replicas: 2
  selector:
    matchLabels:
      app: ranvier-app
  template:
    metadata:
      labels:
        app: ranvier-app
    spec:
      containers:
        - name: app
          image: my-registry/ranvier-app:latest
          ports:
            - containerPort: 3000
          env:
            - name: RUST_LOG
              value: "info"
            - name: JWT_SECRET
              valueFrom:
                secretKeyRef:
                  name: ranvier-secrets
                  key: jwt-secret
          livenessProbe:
            httpGet:
              path: /health
              port: 3000
            initialDelaySeconds: 15
          readinessProbe:
            httpGet:
              path: /health
              port: 3000
            initialDelaySeconds: 5
          resources:
            requests:
              cpu: 100m
              memory: 64Mi
            limits:
              cpu: 500m
              memory: 256Mi

#Service

apiVersion: v1
kind: Service
metadata:
  name: ranvier-app
spec:
  selector:
    app: ranvier-app
  ports:
    - port: 80
      targetPort: 3000
  type: ClusterIP

#HPA (Horizontal Pod Autoscaler)

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: ranvier-app
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: ranvier-app
  minReplicas: 2
  maxReplicas: 10
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70

#4. 헬스 체크 엔드포인트

use ranvier::prelude::*;

#[transition]
async fn health_check(
    _state: (),
    _resources: &(),
    _bus: &mut Bus,
) -> Outcome<String, String> {
    Outcome::Next(r#"{"status":"ok"}"#.to_string())
}

// Axon 설정에 마운트
let health = Axon::simple::<String>("health")
    .then(health_check);

Ranvier::http()
    .bind("0.0.0.0:3000")
    .route("/health", health)
    .route("/api/hello", hello_axon)
    .run(())
    .await?;

#5. 리버스 프록시 (Nginx)

Nginx는 정적 프론트엔드 에셋을 제공하고 API 호출을 Ranvier 백엔드로 프록시합니다:

server {
    listen 80;

    # 정적 SPA
    root /usr/share/nginx/html;
    location / { try_files $uri $uri/ /index.html; }

    # API 프록시 → Ranvier 백엔드
    location /api/ {
        proxy_pass http://backend:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Ranvier::http()Ingress Builder이며 웹 서버가 아닙니다. Nginx를 엣지로 사용하면 Ranvier 애플리케이션의 순수 API 레이어 역할이 유지됩니다.


#6. Compose 토폴로지

┌─────────────────┐
│   Browser :8080 │
└────────┬────────┘

  ┌──────▼──────┐
  │   proxy     │  nginx:alpine — :80
  └──┬───────┬──┘
     │       │
┌────▼───┐ ┌─▼────────┐
│frontend│ │ backend   │  Ranvier — :3000
│(static)│ └──────┬────┘
└────────┘        │
           ┌──────▼──────┐
           │     db       │  postgres:16-alpine — :5432
           └──────────────┘
설정 개발 프로덕션
DB 포트 노출 예 (5432) 아니오
RUST_LOG debug info
재시작 정책 없음 unless-stopped
JWT_SECRET 테스트 값 env/secret

#7. 환경 변수

변수 필수 기본값 설명
JWT_SECRET 예 (인증 사용 시) JWT 서명 시크릿 (절대 하드코딩 금지)
DATABASE_URL 예 (영속성 사용 시) postgres://user:pass@host:5432/db
RUST_LOG 아니오 info 로그 필터 (debug, info, warn, error)
OTEL_EXPORTER_OTLP_ENDPOINT 아니오 OTLP 수집기 엔드포인트
PORT 아니오 3000 HTTP 바인딩 포트
  • 항상 .env.example을 기준 소스로 사용하세요
  • .env를 절대 버전 관리에 커밋하지 마세요
  • 프로덕션 환경에서는 Kubernetes Secrets 또는 vault를 사용하세요

#8. 릴리스 빌드 최적화

# Cargo.toml
[profile.release]
lto = true
strip = true
codegen-units = 1
opt-level = 3
cargo build --release --target x86_64-unknown-linux-musl

#9. CI 파이프라인 (musl 빌드)

name: CI
on: [push, pull_request]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
        with: { targets: x86_64-unknown-linux-musl }
      - uses: Swatinem/rust-cache@v2
      - run: cargo check --workspace
      - run: cargo test --workspace
      - run: cargo clippy --workspace -- -D warnings
      - run: cargo build --release --target x86_64-unknown-linux-musl

#10. 배포 체크리스트

#필수 항목

  • --release 프로파일에 LTO, strip, codegen-units=1 설정
  • Docker 다단계 빌드 (builder → scratch)
  • 헬스 체크 엔드포인트 (/health) 설정
  • 환경 변수 문서화 및 주입
  • 시크릿을 버전 관리에 커밋하지 않음
  • 프로덕션 환경에 RUST_LOG=info 설정

#권장 항목

  • Kubernetes readiness/liveness 프로브 설정
  • CPU 기반 자동 스케일링을 위한 HPA
  • 관측성을 위한 OpenTelemetry 익스포터
  • 정적 에셋 + API 라우팅을 위한 Nginx 리버스 프록시
  • musl 빌드 검증을 포함한 CI 파이프라인

#관련 문서

  • 프로덕션 준비 체크리스트 — 배포 전 체크리스트
  • 성능 튜닝 — 프로파일링, 커넥션 풀, 비동기 최적화
  • 보안 강화 — 인증, CORS, 에러 마스킹
  • OTel 운영 플레이북 — 관측성 설정