Rust製マイクロサービスをDocker+Kubernetesで本番投入するまで

概要

Rustで書かれたHTTP APIをコンテナ化し、Kubernetesクラスタへデプロイするまでの実践手順を解説します。マルチステージビルドによる最小イメージの作り方、非特権ユーザでの実行、水平オートスケール設定までカバーします。

Rustプロジェクトの準備

以下のディレクトリ構成で新規プロジェクトを作成します。

.
├── Cargo.toml
├── src
│   └── main.rs
├── Dockerfile
└── k8s
    ├── deployment.yaml
    └── service.yaml

Cargo.toml(抜粋)

[package]
name = "rust-api"
version = "0.1.0"
edition = "2021"

[dependencies]
tokio = { version = "1", features = ["full"] }
axum = "0.7"
serde = { version = "1.0", features = ["derive"] }
anyhow = "1.0"

最小構成のDockerfile

ランタイムに不要なビルドツールを含まない、distrolessベースのマルチステージビルドを採用します。

# ---------- ビルドステージ ----------
FROM rust:1.76-slim AS builder

WORKDIR /app
COPY Cargo.toml Cargo.lock ./
RUN mkdir src && echo "fn main(){}" > src/main.rs \
    && cargo build --release \
    && rm -rf src

# 依存キャッシュを活かして再ビルド
COPY src ./src
RUN touch src/main.rs \
    && cargo build --release

# ---------- ランタイムステージ ----------
FROM gcr.io/distroless/cc-debian12

# 非ルートユーザを追加
COPY --from=builder /etc/passwd /etc/passwd
USER 65534:65534

COPY --from=builder /app/target/release/rust-api /usr/local/bin/app
EXPOSE 8080
ENTRYPOINT ["/usr/local/bin/app"]

イメージサイズは約20 MBにまで削減されます。

コンテナレジストリへプッシュ

docker build -t ghcr.io/your-org/rust-api:1.0.0 .
docker push ghcr.io/your-org/rust-api:1.0.0

Kubernetesマニフェスト

Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: rust-api
spec:
  replicas: 2
  selector:
    matchLabels:
      app: rust-api
  template:
    metadata:
      labels:
        app: rust-api
    spec:
      securityContext:
        runAsNonRoot: true
        runAsUser: 65534
        fsGroup: 65534
      containers:
      - name: app
        image: ghcr.io/your-org/rust-api:1.0.0
        ports:
        - containerPort: 8080
        env:
        - name: RUST_LOG
          value: "info"
        resources:
          requests:
            cpu: 50m
            memory: 32Mi
          limits:
            cpu: 200m
            memory: 128Mi
        readinessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 3
          periodSeconds: 5
        livenessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 10
          periodSeconds: 10

Serviceベースの水平オートスケーラー

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: rust-api-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: rust-api
  minReplicas: 2
  maxReplicas: 20
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70

Service & Ingress

---
apiVersion: v1
kind: Service
metadata:
  name: rust-api-svc
spec:
  selector:
    app: rust-api
  ports:
  - port: 80
    targetPort: 8080
  type: ClusterIP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: rust-api-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx
  rules:
  - host: api.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: rust-api-svc
            port:
              number: 80

CI/CD(GitHub Actions)例

name: build-and-deploy
on:
  push:
    tags: [ 'v*.*.*' ]

jobs:
  docker:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - name: Set up Docker Buildx
      uses: docker/setup-buildx-action@v3
    - name: Login to GHCR
      uses: docker/login-action@v3
      with:
        registry: ghcr.io
        username: ${{ github.actor }}
        password: ${{ secrets.GITHUB_TOKEN }}
    - name: Build and push
      uses: docker/build-push-action@v5
      with:
        push: true
        tags: ghcr.io/your-org/rust-api:${{ github.ref_name }}

  deploy:
    needs: docker
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - name: Set image tag in manifest
      run: |
        sed -i "s|:latest|:${{ github.ref_name }}|" k8s/deployment.yaml
    - name: Deploy to cluster
      uses: azure/k8s-deploy@v4
      with:
        manifests: |
          k8s/deployment.yaml
          k8s/service.yaml
          k8s/hpa.yaml
        kubeconfig: ${{ secrets.KUBECONFIG }}

トラブルシューティングTips

  • Out-of-memory:メモリリークを疑う場合はkubectl top podで使用量を監視し、limits.memoryを段階的に増やす。
  • 起動遅延scratchイメージだと/etc/ssl/certsが欠落しHTTPS通信が失敗する場合がある。必要に応じてca-certificates.crtをコピーする。
  • 非特権ポート:1024番以下を使う場合はsecurityContext.capabilitiesNET_BIND_SERVICEを付与するか、8080番などに変更。

次のステップ

本番投入後はPrometheus+Grafanaでメトリクスを可視化し、ServiceMonitorを追加して自動的にスクラップ設定を行うと、Rustアプリのパフォーマンスを継続的に追跡できます。

タグ: rust Docker Kubernetes Distroless hpa

7月17日 21:23 投稿