Skip to content
Published on

DevOps/SRE 完全攻略:CI/CDからKubernetes、MLOpsまで

Authors

はじめに

現代のソフトウェア開発において、DevOpsと**SRE(Site Reliability Engineering)**はもはや選択肢ではなく、必須事項です。Netflixは1日に数千回デプロイし、Googleは数十億ユーザーに99.99%の可用性を保証しています。その背後には、徹底的に自動化されたパイプラインとデータ駆動の運用哲学があります。

このガイドでは、DevOps/SREの核心概念からKubernetesの実践運用、AI/MLワークフロー自動化まで、実際のコードとともに完全解説します。


1. DevOps基礎:CI/CDパイプライン

CI/CDとは何か

**CI(Continuous Integration)**は、開発者が頻繁にコードを統合し、自動的にビルド・テストする実践です。**CD(Continuous Delivery/Deployment)**は、検証済みコードを自動的にプロダクションにデプロイします。

区分目的自動化範囲
CIコード統合の検証ビルド、テスト、Lint
CD (Delivery)リリース準備ステージングまで自動
CD (Deployment)自動デプロイプロダクションまで自動

GitHub ActionsでCI/CDを構築

# .github/workflows/ci-cd.yml
name: CI/CD Pipeline

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  test:
    name: Test & Lint
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.11'
          cache: 'pip'

      - name: Install dependencies
        run: |
          pip install -r requirements.txt
          pip install pytest pytest-cov flake8

      - name: Lint with flake8
        run: flake8 src/ --max-line-length=88

      - name: Run tests
        run: pytest tests/ --cov=src --cov-report=xml

      - name: Upload coverage
        uses: codecov/codecov-action@v4
        with:
          file: coverage.xml

  build:
    name: Build & Push Docker Image
    runs-on: ubuntu-latest
    needs: test
    if: github.ref == 'refs/heads/main'
    permissions:
      contents: read
      packages: write

    steps:
      - uses: actions/checkout@v4

      - name: Log in to Container Registry
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Extract metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
          tags: |
            type=sha,prefix=sha-
            type=ref,event=branch
            type=semver,pattern={{version}}

      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

  deploy:
    name: Deploy to Kubernetes
    runs-on: ubuntu-latest
    needs: build
    environment: production

    steps:
      - uses: actions/checkout@v4

      - name: Configure kubectl
        uses: azure/k8s-set-context@v3
        with:
          kubeconfig: ${{ secrets.KUBECONFIG }}

      - name: Deploy with Helm
        run: |
          helm upgrade --install my-app ./helm/my-app \
            --namespace production \
            --set image.tag=${{ github.sha }} \
            --wait --timeout=5m

デプロイ戦略の比較

Blue-Greenデプロイ:同一のプロダクション環境を2つ(Blue/Green)維持します。新バージョンをGreenにデプロイ後、トラフィックを一度に切り替えます。ロールバックは即座ですが、リソースが2倍必要です。

Canaryデプロイ:トラフィックの一部(例:5%)のみを新バージョンへルーティングし、段階的に拡大します。実際のユーザーで検証しながらリスクを最小化します。

# Argo Rollouts Canaryデプロイ設定
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: my-app
spec:
  replicas: 10
  strategy:
    canary:
      steps:
        - setWeight: 10
        - pause: { duration: 5m }
        - setWeight: 30
        - pause: { duration: 10m }
        - setWeight: 60
        - pause: { duration: 10m }
        - setWeight: 100
      canaryService: my-app-canary
      stableService: my-app-stable

2. GitOpsとInfrastructure as Code

GitOpsの原則

GitOpsはGitを**唯一の信頼できる情報源(Single Source of Truth)**として使用する運用モデルです。

  • 宣言的(Declarative):システム状態をコードで宣言
  • バージョン管理:すべての変更がGit履歴で追跡
  • 自動化:Git変更 → 自動同期
  • 監査可能:PR ベースの変更で誰が何をなぜ変更したかを記録

主要ツールとしてArgoCDFluxがあります。

TerraformでIaCを実装

# main.tf - AWS EKSクラスターのプロビジョニング
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
  backend "s3" {
    bucket = "my-terraform-state"
    key    = "prod/eks/terraform.tfstate"
    region = "ap-northeast-1"
  }
}

module "eks" {
  source  = "terraform-aws-modules/eks/aws"
  version = "~> 20.0"

  cluster_name    = "prod-cluster"
  cluster_version = "1.29"

  vpc_id     = module.vpc.vpc_id
  subnet_ids = module.vpc.private_subnets

  eks_managed_node_groups = {
    general = {
      instance_types = ["m5.xlarge"]
      min_size       = 2
      max_size       = 10
      desired_size   = 3
    }
    gpu = {
      instance_types = ["g4dn.xlarge"]
      min_size       = 0
      max_size       = 5
      desired_size   = 1
      taints = [{
        key    = "nvidia.com/gpu"
        value  = "true"
        effect = "NO_SCHEDULE"
      }]
    }
  }
}

3. Kubernetes完全攻略

コアリソースの理解

リソース役割
Pod実行単位、1つ以上のコンテナの集合
DeploymentPodレプリカ管理、ローリングアップデート
ServicePodグループへのネットワークエンドポイント
HPACPU/メモリ基準の水平自動スケーリング
ConfigMap環境変数/設定ファイルの分離
Secret機密情報(パスワード、トークン)の管理
Ingress外部HTTPトラフィックのルーティング

実践的なDeployment + HPAマニフェスト

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: ml-inference-api
  namespace: production
  labels:
    app: ml-inference-api
    version: v1
spec:
  replicas: 3
  selector:
    matchLabels:
      app: ml-inference-api
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  template:
    metadata:
      labels:
        app: ml-inference-api
        version: v1
      annotations:
        prometheus.io/scrape: 'true'
        prometheus.io/port: '8080'
        prometheus.io/path: '/metrics'
    spec:
      containers:
        - name: api
          image: ghcr.io/myorg/ml-inference-api:sha-abc123
          ports:
            - containerPort: 8080
          env:
            - name: MODEL_NAME
              valueFrom:
                configMapKeyRef:
                  name: ml-config
                  key: model_name
            - name: DB_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: db-secret
                  key: password
          resources:
            requests:
              cpu: '500m'
              memory: '512Mi'
            limits:
              cpu: '2000m'
              memory: '2Gi'
          readinessProbe:
            httpGet:
              path: /health
              port: 8080
            initialDelaySeconds: 10
            periodSeconds: 5
          livenessProbe:
            httpGet:
              path: /health
              port: 8080
            initialDelaySeconds: 30
            periodSeconds: 10
---
# hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: ml-inference-api-hpa
  namespace: production
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: ml-inference-api
  minReplicas: 3
  maxReplicas: 20
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70
    - type: Resource
      resource:
        name: memory
        target:
          type: Utilization
          averageUtilization: 80
    - type: Pods
      pods:
        metric:
          name: http_requests_per_second
        target:
          type: AverageValue
          averageValue: '100'
  behavior:
    scaleUp:
      stabilizationWindowSeconds: 60
      policies:
        - type: Pods
          value: 4
          periodSeconds: 60
    scaleDown:
      stabilizationWindowSeconds: 300

HelmチャートによるPackage管理

HelmはKubernetesのパッケージマネージャです。複雑なアプリケーションのデプロイをテンプレートで管理します。

# helm/my-app/values.yaml
replicaCount: 3

image:
  repository: ghcr.io/myorg/my-app
  pullPolicy: IfNotPresent
  tag: 'latest'

service:
  type: ClusterIP
  port: 80
  targetPort: 8080

ingress:
  enabled: true
  className: nginx
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
  hosts:
    - host: api.example.com
      paths:
        - path: /
          pathType: Prefix
  tls:
    - secretName: api-tls
      hosts:
        - api.example.com

resources:
  requests:
    cpu: 500m
    memory: 512Mi
  limits:
    cpu: 2000m
    memory: 2Gi

autoscaling:
  enabled: true
  minReplicas: 3
  maxReplicas: 20
  targetCPUUtilizationPercentage: 70

postgresql:
  enabled: true
  auth:
    database: myapp
    existingSecret: db-credentials

redis:
  enabled: true
  architecture: replication

4. モニタリングと可観測性

可観測性の3つの柱

ツール用途
メトリクス(Metrics)Prometheus, Grafana数値データ、ダッシュボード
ログ(Logs)Loki, Elasticsearchイベント記録、デバッグ
トレース(Traces)Jaeger, Tempo分散リクエスト追跡

Prometheusアラートルール

# prometheus-rules.yaml
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: ml-service-alerts
  namespace: monitoring
spec:
  groups:
    - name: ml-service.rules
      interval: 30s
      rules:
        - alert: HighErrorRate
          expr: |
            sum(rate(http_requests_total{status=~"5.."}[5m]))
            /
            sum(rate(http_requests_total[5m])) > 0.05
          for: 2m
          labels:
            severity: critical
          annotations:
            summary: '高いエラーレートを検出'
            description: '過去5分間のエラーレートは{{ $value | humanizePercentage }}です'

        - alert: SlowResponseTime
          expr: |
            histogram_quantile(0.99,
              sum(rate(http_request_duration_seconds_bucket[5m])) by (le, service)
            ) > 1.0
          for: 5m
          labels:
            severity: warning
          annotations:
            summary: 'p99レイテンシが遅い'
            description: '{{ $labels.service }}のp99レイテンシは{{ $value }}秒です'

        - alert: PodCrashLooping
          expr: |
            increase(kube_pod_container_status_restarts_total[15m]) > 3
          for: 0m
          labels:
            severity: critical
          annotations:
            summary: 'Podがクラッシュループ中'
            description: 'Pod {{ $labels.namespace }}/{{ $labels.pod }}がクラッシュループしています'

        - alert: HighMemoryUsage
          expr: |
            container_memory_usage_bytes
            /
            container_spec_memory_limit_bytes > 0.85
          for: 5m
          labels:
            severity: warning
          annotations:
            summary: 'メモリ使用率が高い'

OpenTelemetryによる計装

# instrumentation.py
from opentelemetry import trace, metrics
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
from opentelemetry.instrumentation.requests import RequestsInstrumentor

def setup_telemetry(service_name: str, otlp_endpoint: str):
    """OpenTelemetryの設定"""
    # トレーサーの設定
    tracer_provider = TracerProvider()
    otlp_exporter = OTLPSpanExporter(endpoint=otlp_endpoint)
    tracer_provider.add_span_processor(BatchSpanProcessor(otlp_exporter))
    trace.set_tracer_provider(tracer_provider)

    # メトリクスの設定
    meter_provider = MeterProvider()
    metrics.set_meter_provider(meter_provider)

    return trace.get_tracer(service_name)

# FastAPIアプリへの適用
from fastapi import FastAPI

app = FastAPI()
tracer = setup_telemetry("ml-inference-api", "http://otel-collector:4317")

# 自動計装
FastAPIInstrumentor.instrument_app(app)
RequestsInstrumentor().instrument()

@app.post("/predict")
async def predict(payload: dict):
    with tracer.start_as_current_span("model-inference") as span:
        span.set_attribute("model.name", "bert-base")
        span.set_attribute("input.length", len(str(payload)))

        result = run_inference(payload)

        span.set_attribute("prediction.confidence", result["confidence"])
        return result

5. SREの原則

SLI / SLO / SLAの階層構造

  • SLI(Service Level Indicator):実際に測定されるサービスパフォーマンス指標(例:リクエスト成功率、レイテンシ)
  • SLO(Service Level Objective):SLIに対する目標値(例:99.9%の可用性)
  • SLA(Service Level Agreement):顧客と合意した契約レベル(SLOより緩く設定)

Error Budgetの計算

SLOが99.9%の場合、1か月(30日)で許容されるダウンタイムは以下の通りです。

エラーバジェット = 100% - SLO = 0.1%
月次許容ダウンタイム = 30日 x 24時間 x 60分 x 0.1%43.2

エラーバジェットが消費されると、新機能のデプロイを停止し、安定性作業に集中します。

SLOレベル月次許容ダウンタイム
99%7時間18分
99.9%43分48秒
99.99%4分22秒
99.999%26秒

Toil削減戦略

Toilは手動的・反復的・自動化可能な運用作業です。Google SREは、Toilを全業務時間の50%未満に抑えることを推奨しています。

Toil削減の方法:

  1. 繰り返し作業のスクリプト化・自動化
  2. ランブック(Runbook)を自動化コードへ変換
  3. アラート品質の改善によるノイズ削減
  4. セルフヒーリング(Self-Healing)システムの構築

6. AI/MLワークフローの自動化

MLflowによる実験管理

# train.py
import mlflow
import mlflow.sklearn
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score, f1_score

mlflow.set_tracking_uri("http://mlflow-server:5000")
mlflow.set_experiment("fraud-detection-v2")

with mlflow.start_run(run_name="rf-baseline"):
    # ハイパーパラメータの記録
    params = {"n_estimators": 100, "max_depth": 10, "random_state": 42}
    mlflow.log_params(params)

    # モデルの学習
    model = RandomForestClassifier(**params)
    model.fit(X_train, y_train)

    # メトリクスの記録
    y_pred = model.predict(X_test)
    mlflow.log_metric("accuracy", accuracy_score(y_test, y_pred))
    mlflow.log_metric("f1_score", f1_score(y_test, y_pred))

    # モデルの保存
    mlflow.sklearn.log_model(model, "model",
        registered_model_name="fraud-detector")

Argo WorkflowsによるMLパイプライン

# ml-pipeline.yaml
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
  name: ml-training-pipeline
spec:
  entrypoint: ml-pipeline
  templates:
    - name: ml-pipeline
      dag:
        tasks:
          - name: data-prep
            template: prepare-data
          - name: train
            template: train-model
            dependencies: [data-prep]
          - name: evaluate
            template: evaluate-model
            dependencies: [train]
          - name: deploy
            template: deploy-model
            dependencies: [evaluate]

    - name: prepare-data
      container:
        image: ghcr.io/myorg/data-prep:latest
        command: [python, prepare_data.py]
        resources:
          requests:
            memory: 4Gi
            cpu: '2'

    - name: train-model
      container:
        image: ghcr.io/myorg/ml-trainer:latest
        command: [python, train.py]
        resources:
          requests:
            memory: 16Gi
            cpu: '8'
            nvidia.com/gpu: '1'

    - name: evaluate-model
      container:
        image: ghcr.io/myorg/ml-evaluator:latest
        command: [python, evaluate.py]

    - name: deploy-model
      container:
        image: ghcr.io/myorg/model-deployer:latest
        command: [python, deploy.py]

Python MLサービスのDockerfile

# Dockerfile
FROM python:3.11-slim AS builder

WORKDIR /app

# 依存関係レイヤーの分離(キャッシュ活用)
COPY requirements.txt .
RUN pip install --no-cache-dir --user -r requirements.txt

FROM python:3.11-slim AS runtime

# セキュリティ:root権限なしで実行
RUN groupadd -r appuser && useradd -r -g appuser appuser

WORKDIR /app

# ビルダーからパッケージをコピー
COPY --from=builder /root/.local /home/appuser/.local
COPY --chown=appuser:appuser src/ ./src/
COPY --chown=appuser:appuser models/ ./models/

USER appuser

ENV PATH=/home/appuser/.local/bin:$PATH
ENV PYTHONUNBUFFERED=1
ENV PYTHONDONTWRITEBYTECODE=1

EXPOSE 8080

HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
  CMD python -c "import requests; requests.get('http://localhost:8080/health').raise_for_status()"

CMD ["uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "8080", "--workers", "4"]

7. セキュリティ:RBACとSecretsの管理

Kubernetes RBAC

# rbac.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: ml-service-account
  namespace: production
---
# 最小権限のRole
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: ml-service-role
  namespace: production
rules:
  - apiGroups: ['']
    resources: ['pods', 'services']
    verbs: ['get', 'list', 'watch']
  - apiGroups: ['']
    resources: ['secrets']
    resourceNames: ['ml-model-secrets']
    verbs: ['get']
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: ml-service-rolebinding
  namespace: production
subjects:
  - kind: ServiceAccount
    name: ml-service-account
    namespace: production
roleRef:
  kind: Role
  apiGroup: rbac.authorization.k8s.io
  name: ml-service-role

HashiCorp VaultによるSecret管理

# vault_client.py
import hvac
import os

def get_secret(secret_path: str) -> dict:
    """Vaultからシークレットを安全に取得します。"""
    client = hvac.Client(
        url=os.environ["VAULT_ADDR"],
        token=os.environ["VAULT_TOKEN"]
    )

    if not client.is_authenticated():
        raise RuntimeError("Vault認証失敗")

    secret = client.secrets.kv.v2.read_secret_version(
        path=secret_path,
        mount_point="secret"
    )
    return secret["data"]["data"]

# KubernetesではVault Agent Injectorを使用して
# Podアノテーションによる自動シークレット注入が可能

NetworkPolicyによるトラフィック制御

# network-policy.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: ml-service-netpol
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: ml-inference-api
  policyTypes:
    - Ingress
    - Egress
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              name: ingress-nginx
      ports:
        - protocol: TCP
          port: 8080
  egress:
    - to:
        - namespaceSelector:
            matchLabels:
              name: database
      ports:
        - protocol: TCP
          port: 5432
    - to:
        - namespaceSelector:
            matchLabels:
              name: monitoring
      ports:
        - protocol: TCP
          port: 4317 # OTLP gRPC

8. インシデント管理

インシデント対応プロセス

  1. 検出(Detection):Prometheusアラートまたはユーザー報告
  2. トリアージ(Triage):深刻度の判断(P1/P2/P3)
  3. コミュニケーション(Communication):ステータスページ更新、ステークホルダーへの通知
  4. 緩和(Mitigation):トラフィック切り替え、ロールバック、スケールアウト
  5. 解決(Resolution):根本原因の修正
  6. 事後レビュー(Post-Mortem):ブレームレスPost-Mortemの作成

効果的なPost-Mortemの書き方

良いPost-Mortemは個人の責任ではなく、システムの改善に集中します。

  • 詳細なタイムラインの記録
  • 根本原因(Root Cause)と誘発要因(Trigger)の区別
  • 5 Whys分析の実施
  • 具体的なアクションアイテム(担当者と期限付き)

まとめ

DevOps/SREは単なるツールセットではなく、文化と哲学です。自動化で人為的ミスを減らし、データで意思決定を行い、継続的な改善でシステムの信頼性を高めます。

核心原則をまとめると:

  • 自動化優先:すべての繰り返し作業はコードで
  • 測定可能性:SLI/SLOで目標を明確に
  • 高速な失敗:Canaryデプロイでリスクを最小化
  • ブレームレス文化:システムを改善し、人を非難しない

クイズ

Q1. CI/CDパイプラインにおけるBlue-GreenデプロイとCanaryデプロイの違いは何ですか?

答え: Blue-Greenは2つの同一プロダクション環境を維持し、トラフィックを一度に切り替える方式で、Canaryはトラフィックの一部のみを新バージョンに段階的に切り替える方式です。

解説: Blue-Greenデプロイはロールバックが即座(トラフィック切り替えのみ)でダウンタイムがありませんが、リソースが2倍必要です。Canaryデプロイは実際のユーザートラフィックで新バージョンを検証しながらリスクを最小化できますが、監視が複雑になります。Argo Rolloutsのようなツールが両方式をサポートします。

Q2. Kubernetes HPAがスケーリングする基準メトリクスは何ですか?

答え: デフォルトではCPU使用率とメモリ使用率です。Custom Metrics APIを通じて、RPS(秒あたりリクエスト数)やキュー深度などのカスタムメトリクスも使用できます。

解説: HPA(HorizontalPodAutoscaler)のv2 APIでは、resourcepodsobjectexternalの4種類のメトリクスタイプをサポートします。CPU 70%の目標を設定すると、既存Podの平均CPUがそれを超えたときにPod数を増やします。Prometheus Adapterをインストールすると、PrometheusメトリクスをHPAに連携できます。

Q3. SREにおけるError Budgetの役割は何ですか?

答え: Error Budgetは、SLOで定義された許容失敗量であり、安定性と機能開発速度のバランスを取るメカニズムです。

解説: SLOが99.9%の場合、Error Budgetは0.1%です。このバジェットが十分であれば、新機能のデプロイや実験的な変更が可能です。バジェットが枯渇すると、新しいデプロイを凍結し、安定性の改善に集中します。これにより、開発チームと運営チームがデータに基づいてデプロイ速度を協議できます。

Q4. Prometheusのpull方式によるメトリクス収集の利点は何ですか?

答え: pull方式はPrometheusがスクレイピング対象を中央で管理するため設定がシンプルで、対象サービスがダウンした際に即座に検出でき、セキュリティ面でファイアウォールのインバウンドルールが不要です。

解説: push方式(StatsD、InfluxDBなど)では、各サービスがメトリクスサーバーのアドレスを知る必要があり、ネットワーク問題時にデータが失われる可能性があります。pull方式はPrometheusが設定ファイルやサービスディスカバリで対象を管理するため、サービスの追加・削除が柔軟です。非常に短命なバッチジョブにはPushgatewayを活用します。

Q5. GitOpsと従来のCI/CDの違いは何ですか?

答え: GitOpsはGitを唯一の信頼できる情報源として使用し、宣言的な状態を継続的に同期させるのに対し、従来のCI/CDはパイプラインが直接命令的にデプロイを実行します。

解説: 従来のCI/CD(Jenkins、GitHub Actions)はパイプラインから直接kubectl applyhelm upgradeを実行します。GitOps(ArgoCD、Flux)はGitの宣言的な状態とクラスターの実際の状態を継続的に比較し、自動同期します。GitOpsはドリフト検出、自動ロールバック、完全な監査証跡が可能で、CI/CDシステムにクラスターアクセス権限を付与する必要がなくセキュリティが強化されます。