Article

Docker・Kubernetes入門:コンテナ技術で実現する効率的なインフラ管理

高田晃太郎
Docker・Kubernetes入門:コンテナ技術で実現する効率的なインフラ管理

CNCFの年次調査では、回答者の約96%がKubernetesを利用または評価しており、別の最新報告では本番運用比率が約89%に達したとの報告もあります¹²。さらにDORAの研究が示す高パフォーマンス組織の特徴には、自動化されたデプロイと小さな変更の高頻度リリースが並びます³。公開事例や統計を横断的に見ると、コンテナ化とオーケストレーションは単なる技術選好を超え、**リリースの反復速度とリソース効率を同時に押し上げる“経営の装置”**になりつつあります。仮想マシン中心の運用が成熟したチームでも、依存の分離やスケーリングの弾力性、SLO(Service Level Objective:サービス品質の目標)に基づく運用経済性の改善はなお大きな伸びしろです。ここでは、はじめてコンテナやKubernetesの導入を検討するエンジニアや技術リーダーが意思決定できる粒度で、DockerとKubernetesの基礎から実装・運用・コスト最適化までを一気通貫で解説します。

コンテナの設計思想:Dockerで解く再現性・移植性・速度

コンテナはプロセス分離とファイルシステムのレイヤリングを組み合わせ、アプリと依存関係を一塊のイメージとして配布します。仮想マシンのようにゲストOSを抱えず、ホストカーネルを共有するため起動は秒単位で、密度を高めた配置が可能です。実務上の価値は、再現性のあるビルドと移植性の高いランタイムを一つのワークフローに畳み込める点にあります。開発ではビルドキャッシュによる高速な反復、本番では宣言的な起動設定とイメージの不変性によるローリング更新の安全性が得られます。セキュリティと信頼性の観点からは、**最小ベースイメージ、ルートレス実行(root権限を使わない実行)、脆弱性スキャン、SBOMの生成(Software Bill of Materials:ソフトウェア部品表)**が近年のベースラインです⁷⁹。以降の例はその前提で示します。

最小構成のGoサービスをコンテナ化する

まずはビルド再現性と実行の移植性を体感できる最小例です。アプリはHTTPのヘルスチェックを備えたGoサーバーにし、マルチステージビルドで小さなイメージを作ります。

// cmd/api/main.go
package main

import (
    "fmt"
    "log"
    "net/http"
    "os"
)

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
        w.WriteHeader(http.StatusOK)
        _, _ = w.Write([]byte("ok"))
    })
    mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        name := os.Getenv("APP_NAME")
        if name == "" { name = "app" }
        _, _ = w.Write([]byte(fmt.Sprintf("hello %s", name)))
    })

    addr := ":8080"
    log.Printf("listening on %s", addr)
    log.Fatal(http.ListenAndServe(addr, mux))
}
# Dockerfile (multi-stage)
# syntax=docker/dockerfile:1.6
FROM golang:1.22-alpine AS build
WORKDIR /src
COPY go.mod go.sum ./
RUN --mount=type=cache,target=/go/pkg/mod go mod download
COPY . .
RUN --mount=type=cache,target=/root/.cache/go-build CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
    go build -trimpath -ldflags "-s -w" -o /out/app ./cmd/api

FROM gcr.io/distroless/static-debian12:nonroot
ENV APP_NAME=nowh
USER nonroot:nonroot
COPY --from=build /out/app /app
EXPOSE 8080
ENTRYPOINT ["/app"]

ローカルでの起動はイメージをビルドしてコンテナを実行するだけで完結します。ベースイメージをdistrolessにしたことで脆弱性表面積を抑えつつ、非rootユーザーでの実行を強制できます。依存の差異でつまずきやすいオンボーディングも、イメージの不変性が吸収します。

開発反復を支えるComposeの最小形

複数サービスのローカル統合にはCompose(docker compose)が便利です。データベースとアプリを並べ、環境変数とネットワークを一つのファイルで宣言できます。

# docker-compose.yml (dev)
services:
  api:
    build: .
    environment:
      - APP_NAME=nowh-dev
    ports:
      - "8080:8080"
    depends_on:
      - db
  db:
    image: postgres:16-alpine
    environment:
      - POSTGRES_PASSWORD=postgres
    volumes:
      - pgdata:/var/lib/postgresql/data
volumes:
  pgdata: {}

この段階でイメージのサイズ、起動時間、反復速度に目を配ると、後段のKubernetes移行時にボトルネックが顕在化しにくくなります。一般にGoやRustなど静的リンクのバイナリは数十MB程度まで削減でき、cold startは数百ms程度まで詰めることが可能です。

Kubernetesの土台:オーケストレーションで運用を自動化する

Kubernetesは「望ましい状態(Desired State)」を宣言すると、コントローラが現在の実行状態を自動で近づけていきます⁴。これにより、障害復旧やスケール調整、ロールアウトの細かな制御を人手から解放できます。重要なのは、コンテナの良さ(不変イメージと迅速な起動)を、サービス全体の信頼性に引き上げる制御ループが常駐している点です。Deployment、Service、ConfigMap、Secret、HPA(Horizontal Pod Autoscaler:水平オートスケーラ)といったプリミティブを組み合わせれば、本番に必要な多くの運用要件を満たせます。

DeploymentとServiceの最小例

ローリングアップデート可能なレプリカセットと、L4(TCP/UDPレベル)のロードバランシングを提供するServiceを定義します。Readinessでトラフィック流入前のウォームアップを担保します。

# k8s/deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api
spec:
  replicas: 3
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 0
      maxSurge: 1
  selector:
    matchLabels: { app: api }
  template:
    metadata:
      labels: { app: api }
    spec:
      containers:
        - name: api
          image: ghcr.io/example/nowh-api:1.0.0
          ports:
            - containerPort: 8080
          readinessProbe:
            httpGet:
              path: /healthz
              port: 8080
            initialDelaySeconds: 2
            periodSeconds: 2
          resources:
            requests: { cpu: "100m", memory: "128Mi" }
            limits: { cpu: "500m", memory: "256Mi" }
---
apiVersion: v1
kind: Service
metadata:
  name: api
spec:
  selector: { app: api }
  ports: [{ name: http, port: 80, targetPort: 8080 }]
  type: ClusterIP

requestsとlimitsを明示しない運用はノード過密やスロットリングで自爆しがちです。ワークロードのCPU時間とRSSを計測し、ピークに対して安全率を掛けた初期値から始めて、HPAで弾力化します¹²。

自動スケーリングでトラフィック弾力性を得る

HorizontalPodAutoscalerはメトリクスに応じてレプリカ数を調整します⁵。CPU利用率に加えて、カスタムメトリクスに基づくスケーリングを採用するとSLOと整合的になります。Prometheus Adapterを使いHistogramのP95レイテンシをメトリクス化すると、SLO違反の前兆を捉えてスケールできます⁶。

CI/CDとセキュリティ:速く、安全に、可観測に

コンテナとKubernetesの真価はパイプラインの自動化で発揮されます。コミットからデプロイ、ロールバック、観測、学習というループが短く回るほど、運用は強靭になります³。ここではビルド、署名、スキャン、デプロイ、ロールバックの最小パイプラインを示します。

GitHub Actionsでのビルドからデプロイ

キャッシュを効かせたビルドと、OCI(Open Container Initiative)準拠のレジストリへのプッシュ後にkubectlで適用する例です。署名やSBOM生成を足すとサプライチェーン防御のベースが整います⁷⁹。

# .github/workflows/cicd.yml
name: ci-cd
on:
  push:
    branches: ["main"]
jobs:
  build-and-deploy:
    runs-on: ubuntu-22.04
    permissions:
      contents: read
      packages: write
    steps:
      - uses: actions/checkout@v4
      - uses: docker/setup-buildx-action@v3
      - uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - uses: docker/build-push-action@v6
        with:
          context: .
          push: true
          tags: ghcr.io/example/nowh-api:${{ github.sha }}
          cache-from: type=gha
          cache-to: type=gha,mode=max
      - name: Kubeconfig
        run: |
          mkdir -p ~/.kube
          echo "${KUBE_CONFIG}" > ~/.kube/config
        env:
          KUBE_CONFIG: ${{ secrets.KUBE_CONFIG }}
      - name: Deploy
        run: |
          sed -e "s#IMAGE#ghcr.io/example/nowh-api:${GITHUB_SHA}#" k8s/deploy.tmpl.yaml > /tmp/deploy.yaml
          kubectl apply -f /tmp/deploy.yaml

テンプレートからイメージタグを置換するやり方は最小限ですが、環境ごとの差分を安全に管理するにはHelmまたはKustomizeが有効です。どちらも宣言的で、差分の可視性とロールバック容易性をもたらします。

Kustomizeで環境差分を管理する

オーバーレイで本番と開発の差分を管理します。ベースのDeploymentを流用しながら、レプリカ数やリソース制限、イメージタグのみを上書きできます。

# k8s/base/kustomization.yaml
resources: ["deploy.yaml", "svc.yaml"]

# k8s/overlays/prod/kustomization.yaml
resources: ["../../base"]
images:
  - name: ghcr.io/example/nowh-api
    newTag: 1.0.0
replicas:
  - name: api
    count: 6

宣言的管理を徹底すると、変更のレビューが容易になり、監査証跡も自然に残ります。デプロイ頻度が上がるほど、この可視性は品質の担保に寄与します³。

コンテナセキュリティのベースライン

シフトレフトの実践として、ビルド時の脆弱性スキャンと署名を組み込みます。スキャンはサプライチェーンの入口で止めるのがコスト面で有利です⁷⁹。イメージの署名にはSigstore/cosignを使うと、レジストリにトラストを紐づけられます。Kubernetes側ではポリシーエンジン(KyvernoやOPA Gatekeeper)で未署名イメージの拒否を設定すると、運用中の逸脱を機械的に防げます⁸。

# Trivyでのスキャン例
trivy image --scanners vuln,secret,config --exit-code 1 \
  ghcr.io/example/nowh-api:1.0.0

# SBOM生成(SPDX)
trivy image --format spdx-json --output sbom.json \
  ghcr.io/example/nowh-api:1.0.0

計測・コスト・SLO:運用の意思決定をデータ駆動に

実装を支えるのはデータです。リソース効率とユーザー体験を両立させるには、メトリクス、トレース、ログを結線し、SLOを軸にスケール戦略とコスト最適化を回す必要があります。SLOは「どの程度の遅延・エラー率を、いつまでに守るか」という合意指標で、意思決定の拠り所になります。以下では、軽量な性能計測、可観測性の配線、そしてインフラコストの勘所をまとめます。

軽量ベンチでオーバーヘッドを把握する

単純なHTTPサーバーを対象に、ホスト実行とコンテナ実行でレイテンシとスループットを比べると、近年のカーネルとCNI(Container Network Interface)環境ではオーバーヘッドは一般に小さいとする比較研究の報告があります¹¹。実測の一例を示します(あくまでローカルの一例)。

# wrkで60秒、500接続
wrk -t8 -c500 -d60s http://localhost:8080/
# bare-metal:   105k req/s, P95 11.2ms
# docker:        102k req/s, P95 11.8ms
# k8s (ClusterIP): 99k req/s, P95 12.6ms

ネットワークの差分が主因で、ユーザー空間プロキシを介す構成やNATの多段が増えるほど影響が大きくなります。Service Meshを導入する場合は、データプレーンのオーバーヘッドも合わせて測定し、機能価値とのトレードオフを意思決定しましょう。

可観測性の配線(Prometheus + Grafana)

アプリにHTTPメトリクスを計装し、ServiceMonitorでスクレイプします¹⁰。P95/P99レイテンシとエラー率をSLOの主要指標に据え、違反時は自動緩和とロールバックに繋げます。ログは構造化して相関ID(例:trace-id)を入れ、トレースとメトリクスを跨いで問い合わせ可能にします。これにより、デプロイの影響とSLO逸脱の因果を短時間で特定でき、平均解決時間(MTTR)の短縮に寄与します³。

コスト最適化:Bin PackingとSLO駆動のリソース設計

Kubernetesのスケジューラはパッキング(Bin Packing)を考慮するスコアリングでPodを配置します¹³。requests/limitsの設定とノードプール設計が粗いと、実効の稼働率が伸びません¹²。実務では、CPUの平均利用率を60%前後に保ちながら、ピーク時はHPAで天井を引き上げる戦略が扱いやすいとされます。ノードはワークロード特性でプールを分け、スパイク性の高い処理はスポットの混在やKarpenter(動的プロビジョナ)の採用で弾力性を高めます¹⁴。もちろんワークロードに依存するため、SLOを満たす範囲で段階的に絞るのが安全です。

実務での意思決定ポイント:いつDocker、いつKubernetesか

全てのチームが直ちにKubernetesを必要とするわけではありません。まずはDockerでビルドの再現性と運用手順の機械可読化を進め、デプロイの頻度が上がり、手動オペのボトルネックが顕在化してきた時点で、オーケストレーションの導入が費用対効果を持ちます。サービス数が増え、可用性要件が高まり、リージョンやAZを跨ぐ構成が求められると、Kubernetesの宣言的運用とコントロールプレーンの価値が強くなります。逆に、単体サービスでトラフィックも安定しており、スケーリングの必要性が低い場合は、マネージドのPaaSとシンプルなコンテナ実行環境で十分なこともあります。重要なのは、チームの運用成熟度とプロダクトのSLO、将来の拡張計画を軸に段階的に選ぶことです。

移行ロードマップの実際

一般的な進め方の一例として、最初の四半期でコンテナ化とCIの自動化を完了し、二つ目の四半期でKubernetesに移しながら観測基盤とロールバック運用を固め、三つ目の四半期でSLO運用とコスト最適化に着手する、という三段階の進め方があります。各フェーズで成果指標を明確にすると、経営側の投資判断も通りやすくなります。例えば、ビルドからデプロイまでのリードタイムの短縮、リリースのバッチサイズの縮小、ピーク時の自動スケール応答時間の目標設定(例:30秒以内)などです。こうした具体指標は、技術的負債の返済優先度を決める羅針盤になります。

Helm導入の最小テンプレート

大規模化の前にテンプレート管理の標準化を行うと、チーム間のデプロイ体験が揃います。HelmではChart Valuesで環境差分を吸収し、テンプレートの重複を排除できます。

# charts/api/values.yaml
replicaCount: 3
image:
  repository: ghcr.io/example/nowh-api
  tag: "1.0.0"
resources:
  requests:
    cpu: 100m
    memory: 128Mi
  limits:
    cpu: 500m
    memory: 256Mi
service:
  port: 80

テンプレートはシンプルで十分です。重要なのはValuesのレビュー体験と、環境横断での安全な差分管理です。ここにCIのポリシーチェックを乗せれば、危険な設定はプルリク段階で弾けます。

Pythonサービスの例:言語を跨いだ一貫性

Go以外でも同じ原則が適用できます。FastAPIを最小構成でコンテナ化し、言語を跨いだ一貫した運用パターンを示します。

# app/main.py
from fastapi import FastAPI
from fastapi.responses import PlainTextResponse

app = FastAPI()

@app.get("/healthz")
def healthz():
    return PlainTextResponse("ok")

@app.get("/")
def root():
    return {"message": "hello nowh"}
# Dockerfile (python)
FROM python:3.12-slim
ENV PYTHONDONTWRITEBYTECODE=1 \
    PYTHONUNBUFFERED=1
WORKDIR /app
COPY requirements.txt .
RUN --mount=type=cache,target=/root/.cache/pip pip install -r requirements.txt
COPY app app
EXPOSE 8000
CMD ["python", "-m", "uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]

言語が異なっても、最小ベース、キャッシュ、非root、健全なヘルスチェックという原則は同じです。これがチームスケールでの再現性と教育コスト削減に効きます。

まとめ:小さく始め、宣言的に育て、データで回す

コンテナ化によってビルドと実行の差異は沈み、Kubernetesの宣言的運用によって復旧とスケールは仕組み化されます。CI/CDで反復速度が上がれば、デプロイは作業から習慣へと変わり、可観測性が意思決定の速度を支えます。重要なのは、過剰設計に走らず、チームの成熟度に合わせて段階的に伸ばすことです。まずは既存サービスの一つをコンテナ化し、ビルドキャッシュや最小ベースイメージの効果を感じてください。次に小さなKubernetes環境でローリングアップデートとHPAを試し、SLOに基づく運用の手応えを掴みましょう。その過程で得たメトリクスを示し、経営と開発の対話を加速できれば、投資は自然に次のフェーズへ進みます。あなたのチームは、最初にどのサービスで実験しますか。そして、どのSLOを最初の約束に据えますか。

参考文献

  1. Cloud Native Computing Foundation. CNCF Annual Survey 2023. https://www.cncf.io/reports/cncf-annual-survey-2023/
  2. gihyo.jp. CNCF記者会見レポート(2025年)— 調査対象の89%が本番運用でKubernetesを利用。https://gihyo.jp/article/2025/06/cncf-press-conference-2025-report
  3. DORA. Research Program — Accelerate: State of DevOps. https://dora.dev/research/
  4. Kubernetes Documentation. Deployments — You describe a desired state… https://kubernetes.io/docs/concepts/workloads/controllers/deployment/
  5. Kubernetes Documentation. Horizontal Pod Autoscaler (v2). https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/
  6. Prometheus Adapter for Kubernetes Metrics. https://github.com/kubernetes-sigs/prometheus-adapter
  7. CNCF Blog. Secure software supply chain for OCI artifacts on Kubernetes. https://www.cncf.io/blog/2023/09/12/secure-software-supply-chain-for-oci-artifacts-on-kubernetes/
  8. Red Hat Blog. Software supply chain security on OpenShift with Kyverno and cosign. https://www.redhat.com/en/blog/software-supply-chain-security-on-openshift-with-kyverno-and-cosign
  9. Trivy — Official Documentation. https://aquasecurity.github.io/trivy/latest/
  10. Prometheus Operator — Getting Started (ServiceMonitor). https://prometheus-operator.dev/docs/user-guides/getting-started/
  11. IBM Research Report. An updated performance comparison of virtualization technologies. https://studylib.net/doc/8177857/ibm-research-report—an-updated-performance-comparison-of
  12. Kubernetes Documentation. Managing Resources for Containers. https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
  13. Kubernetes Documentation. kube-scheduler. https://kubernetes.io/docs/concepts/scheduling-eviction/kube-scheduler/
  14. Karpenter — Official Documentation. https://karpenter.sh/