Skip to content
Published on

CI/CDベストプラクティス2025:チームのためのパイプライン設計、自動化、セキュリティまで

Authors

はじめに

2025年(ねん)、CI/CDはもはや選択(せんたく)ではなく必須(ひっす)です。GoogleのDORA(DevOps Research and Assessment)レポートによると、Eliteレベルのチームは1日(にち)に複数回(ふくすうかい)デプロイしながらも、変更(へんこう)失敗率(しっぱいりつ)を5%未満(みまん)に維持(いじ)しています。一方(いっぽう)、Lowレベルのチームは月(つき)1回(かい)のデプロイで失敗率(しっぱいりつ)が46%に達(たっ)しています。

この格差(かくさ)の核心(かくしん)は**パイプライン設計(せっけい)**にあります。単(たん)にCI/CDツールを導入(どうにゅう)するだけでなく、テスト自動化(じどうか)、セキュリティ統合(とうごう)、段階的(だんかいてき)デプロイ、そして可観測性(かかんそくせい)まで含(ふく)む総合的(そうごうてき)な戦略(せんりゃく)が必要(ひつよう)です。

この記事(きじ)では、CI/CDパイプラインを設計(せっけい)・運用(うんよう)するために必要(ひつよう)な全(すべ)てをカバーします。プラットフォーム比較(ひかく)からパイプライン設計原則(げんそく)、テスト戦略(せんりゃく)、Dockerビルド最適化(さいてきか)、GitOps、セキュリティ、デプロイ戦略(せんりゃく)、ロールバック、そしてモニタリングまで実践(じっせん)中心(ちゅうしん)にまとめました。


1. 2025年(ねん)のCI/CD現況(げんきょう)

1.1 DORAメトリクスで見(み)るチームパフォーマンス

DORAメトリクスはソフトウェアデリバリーの成果(せいか)を測定(そくてい)する4つの核心(かくしん)指標(しひょう)です。

指標(しひょう)EliteHighMediumLow
デプロイ頻度(ひんど)1日(にち)複数回(ふくすうかい)週1回〜月1回月1回〜6ヶ月(げつ)1回6ヶ月(げつ)以上(いじょう)
リードタイム(コミット→デプロイ)1時間(じかん)未満(みまん)1日〜1週間(しゅうかん)1週間〜1ヶ月1ヶ月〜6ヶ月
変更(へんこう)失敗率(しっぱいりつ)0〜5%5〜10%10〜15%46〜60%
復旧時間(ふっきゅうじかん)(MTTR)1時間未満1日未満1日〜1週間6ヶ月以上

1.2 シフトレフト戦略(せんりゃく)

シフトレフト(Shift Left)は、テストとセキュリティを開発(かいはつ)の初期(しょき)段階(だんかい)に前倒(まえだお)しする戦略(せんりゃく)です。

従来のアプローチ:
CodeBuildTestSecurityDeployMonitor
                              ↑ ここで問題発見

シフトレフト:
Code + Test + SecurityBuildDeployMonitor
ここで問題発見 (コスト10倍削減)

核心原則(かくしんげんそく):

  • コミット前(まえ)検証(けんしょう): pre-commit hookでリント、フォーマット、シークレットスキャン
  • PR段階(だんかい)テスト: ユニットテスト + 統合(とうごう)テスト + SAST自動実行(じっこう)
  • ビルド時(じ)セキュリティ: コンテナイメージスキャン、依存関係(いぞんかんけい)脆弱性(ぜいじゃくせい)検査(けんさ)
  • デプロイ前(まえ)検証(けんしょう): スモークテスト、カナリア分析(ぶんせき)

1.3 2025年(ねん)の主要(しゅよう)トレンド

  • プラットフォームエンジニアリング: 開発者(かいはつしゃ)セルフサービスプラットフォームでCI/CD標準化(ひょうじゅんか)
  • AI駆動(くどう)CI/CD: テスト失敗(しっぱい)予測(よそく)、自動(じどう)ロールバック判断(はんだん)、フレイキーテスト検出(けんしゅつ)
  • eBPF基盤(きばん)の可観測性(かかんそくせい): パイプラインパフォーマンスモニタリングの新(あたら)しいパラダイム
  • サプライチェーンセキュリティ: SBOM、SLSA、Sigstore基盤(きばん)のソフトウェアサプライチェーンセキュリティ

2. CI/CDプラットフォーム比較(ひかく)

2.1 主要(しゅよう)プラットフォーム比較表(ひかくひょう)

機能(きのう)GitHub ActionsJenkinsGitLab CICircleCI
ホスティングSaaS/セルフホストセルフホストSaaS/セルフホストSaaS
設定方式(ほうしき)YAMLGroovy/YAMLYAMLYAML
エコシステムMarketplace 15,000+Plugin 1,800+ビルトイン統合(とうごう)Orbs 3,000+
コンテナサポートネイティブプラグインネイティブネイティブ
セルフランナー対応(たいおう)デフォルト対応対応
価格(かかく)2,000分(ふん)無料(むりょう)/月無料(OSS)400分無料/月6,000クレジット無料/月
学習(がくしゅう)コスト低(ひく)い高(たか)い中程度(ちゅうていど)低い
キャッシュ10GB/リポプラグインネイティブネイティブ

2.2 GitHub Actions

# .github/workflows/ci.yml
name: CI Pipeline

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

concurrency:
  group: ci-${{ '{{' }} github.ref {{ '}}' }}
  cancel-in-progress: true

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'
      - run: npm ci
      - run: npm run lint

  test:
    runs-on: ubuntu-latest
    needs: lint
    strategy:
      matrix:
        shard: [1, 2, 3, 4]
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'
      - run: npm ci
      - run: npm test -- --shard=${{ '{{' }} matrix.shard {{ '}}' }}/4

  build:
    runs-on: ubuntu-latest
    needs: test
    steps:
      - uses: actions/checkout@v4
      - uses: docker/setup-buildx-action@v3
      - uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: myapp:latest
          cache-from: type=gha
          cache-to: type=gha,mode=max

2.3 Jenkinsパイプライン

// Jenkinsfile (Declarative Pipeline)
pipeline {
    agent any

    environment {
        DOCKER_REGISTRY = 'registry.example.com'
        IMAGE_NAME = 'myapp'
    }

    stages {
        stage('Checkout') {
            steps {
                checkout scm
            }
        }

        stage('Lint & Test') {
            parallel {
                stage('Lint') {
                    steps {
                        sh 'npm run lint'
                    }
                }
                stage('Unit Test') {
                    steps {
                        sh 'npm test -- --coverage'
                    }
                    post {
                        always {
                            junit 'reports/junit.xml'
                        }
                    }
                }
            }
        }

        stage('Build & Push') {
            steps {
                script {
                    def image = docker.build("${DOCKER_REGISTRY}/${IMAGE_NAME}:${BUILD_NUMBER}")
                    docker.withRegistry("https://${DOCKER_REGISTRY}", 'registry-credentials') {
                        image.push()
                        image.push('latest')
                    }
                }
            }
        }
    }

    post {
        failure {
            slackSend(
                channel: '#ci-alerts',
                color: 'danger',
                message: "Build FAILED: ${env.JOB_NAME} #${env.BUILD_NUMBER}"
            )
        }
    }
}

2.4 GitLab CI

# .gitlab-ci.yml
stages:
  - lint
  - test
  - build
  - deploy

variables:
  DOCKER_HOST: tcp://docker:2376

lint:
  stage: lint
  image: node:20-alpine
  cache:
    key: npm-cache
    paths:
      - node_modules/
  script:
    - npm ci
    - npm run lint

test:
  stage: test
  image: node:20-alpine
  parallel: 4
  script:
    - npm ci
    - npm test -- --shard=$CI_NODE_INDEX/$CI_NODE_TOTAL
  coverage: '/Statements\s*:\s*(\d+\.?\d*)%/'
  artifacts:
    reports:
      junit: reports/junit.xml

build:
  stage: build
  image: docker:24
  services:
    - docker:24-dind
  script:
    - docker build -t myapp:latest .
    - docker push myapp:latest
  only:
    - main

3. パイプライン設計(せっけい)原則(げんそく)

3.1 高速(こうそく)フィードバックループ

開発者(かいはつしゃ)がPRを出(だ)してから結果(けっか)を待(ま)つ時間(じかん)は、生産性(せいさんせい)に直接(ちょくせつ)影響(えいきょう)します。

目標時間:
├── リント + フォーマットチェック: 30秒以内
├── ユニットテスト: 2分以内
├── 統合テスト: 5分以内
├── ビルド: 3分以内
└── 全パイプライン: 10分以内

現実 (最適化前): 30分以上
現実 (最適化後): 810

3.2 並列処理(へいれつしょり)

# 並列パイプライン例
jobs:
  # フェーズ1: リント/セキュリティは独立して並列実行
  lint:
    runs-on: ubuntu-latest
    # ...
  security-scan:
    runs-on: ubuntu-latest
    # ...

  # フェーズ2: テストはシャードで並列分割
  test:
    needs: [lint]
    strategy:
      matrix:
        shard: [1, 2, 3, 4]
    # ...

  # フェーズ3: テスト通過後にビルド
  build:
    needs: [test, security-scan]
    # ...

3.3 キャッシュ戦略(せんりゃく)

# npmキャッシュ(GitHub Actions)
- uses: actions/cache@v4
  with:
    path: ~/.npm
    key: npm-${{ '{{' }} hashFiles('**/package-lock.json') {{ '}}' }}
    restore-keys: |
      npm-

# Dockerレイヤーキャッシュ
- uses: docker/build-push-action@v5
  with:
    cache-from: type=gha
    cache-to: type=gha,mode=max

# Gradleキャッシュ
- uses: actions/cache@v4
  with:
    path: |
      ~/.gradle/caches
      ~/.gradle/wrapper
    key: gradle-${{ '{{' }} hashFiles('**/*.gradle*') {{ '}}' }}

3.4 冪等性(べきとうせい)(Idempotency)

パイプラインは同(おな)じ入力(にゅうりょく)に対(たい)して常(つね)に同(おな)じ結果(けっか)を出(だ)す必要(ひつよう)があります。

# 悪い例: タイムスタンプベースのタグ(再実行時に異なる結果)
# IMAGE_TAG: my-app:build-20250323-142000

# 良い例: コミットSHAベースのタグ(常に同一)
# IMAGE_TAG: my-app:abc1234

# 良い例: セマンティックバージョン(決定的)
# IMAGE_TAG: my-app:v1.2.3

4. CIでのテスト戦略(せんりゃく)

4.1 テストピラミッド

          /   E2E    \          遅いが高い信頼性
         /  (510%)  \
        / Integration  \       中速度、中信頼性
       /   (1525%)    \
      /   Unit Tests     \    高速で大量
     /     (6580%)      \
    /______________________\

4.2 テスト分割(ぶんかつ)(Test Splitting)

# Jestテストシャーディング
test:
  strategy:
    matrix:
      shard: [1, 2, 3, 4]
  steps:
    - run: npx jest --shard=${{ '{{' }} matrix.shard {{ '}}' }}/4

# Cypress並列実行
e2e:
  strategy:
    matrix:
      container: [1, 2, 3]
  steps:
    - uses: cypress-io/github-action@v6
      with:
        record: true
        parallel: true
        group: 'e2e-tests'

4.3 フレイキーテスト管理(かんり)

フレイキー(Flaky)テストは、同(おな)じコードで時(とき)に成功(せいこう)し時(とき)に失敗(しっぱい)するテストです。

// フレイキーテスト検出・隔離戦略
// jest.config.js
module.exports = {
  // 失敗時に自動リトライ
  retryTimes: 2,

  // フレイキーテストレポーター
  reporters: [
    'default',
    ['jest-flaky-reporter', {
      outputFile: 'flaky-tests.json',
      threshold: 3  // 3回以上フレイキーなら報告
    }]
  ]
};
# CIでフレイキーテストを隔離
test-stable:
  runs-on: ubuntu-latest
  steps:
    - run: npx jest --testPathIgnorePatterns="flaky"

test-flaky:
  runs-on: ubuntu-latest
  continue-on-error: true  # 失敗してもパイプライン継続
  steps:
    - run: npx jest --testPathPattern="flaky" --retries=3

4.4 テストカバレッジゲート

# カバレッジ閾値設定
test:
  steps:
    - run: npx jest --coverage
    - name: Check coverage threshold
      run: |
        COVERAGE=$(cat coverage/coverage-summary.json | jq '.total.statements.pct')
        if (( $(echo "$COVERAGE < 80" | bc -l) )); then
          echo "Coverage $COVERAGE% is below 80% threshold"
          exit 1
        fi

5. Dockerビルド最適化(さいてきか)

5.1 マルチステージビルド

# Stage 1: 依存関係インストール
FROM node:20-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --production

# Stage 2: ビルド
FROM node:20-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build

# Stage 3: プロダクションイメージ
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production

# セキュリティ: non-rootユーザー
RUN addgroup --system --gid 1001 nodejs && \
    adduser --system --uid 1001 nextjs

COPY --from=builder --chown=nextjs:nodejs /app/.next ./.next
COPY --from=deps --chown=nextjs:nodejs /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./

USER nextjs
EXPOSE 3000
CMD ["npm", "start"]

5.2 レイヤーキャッシュ最適化(さいてきか)

# 悪い例: ソース変更時にnpm ci再実行
COPY . .
RUN npm ci
RUN npm run build

# 良い例: 依存関係ファイルのみ先にコピー
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build

5.3 BuildKitとBuildx

# GitHub ActionsでBuildKit使用
- uses: docker/setup-buildx-action@v3

- uses: docker/build-push-action@v5
  with:
    context: .
    push: true
    tags: myapp:latest
    cache-from: type=gha
    cache-to: type=gha,mode=max
    platforms: linux/amd64,linux/arm64

5.4 Kaniko(Dockerデーモン不要(ふよう)のビルド)

# KubernetesでKanikoによるイメージビルド
apiVersion: v1
kind: Pod
metadata:
  name: kaniko-build
spec:
  containers:
    - name: kaniko
      image: gcr.io/kaniko-project/executor:latest
      args:
        - "--dockerfile=Dockerfile"
        - "--context=git://github.com/myorg/myapp"
        - "--destination=registry.example.com/myapp:latest"
        - "--cache=true"
        - "--cache-repo=registry.example.com/myapp/cache"

5.5 イメージサイズ最適化(さいてきか)

イメージサイズ比較:
├── node:201.1GB
├── node:20-slim     → 220MB
├── node:20-alpine   → 140MB
├── distroless/nodejs → 120MB
└── マルチステージ最適化 → 80〜100MB

6. GitOpsとArgoCD

6.1 GitOps原則(げんそく)

GitOpsはGitリポジトリを単一(たんいつ)真実(しんじつ)の情報源(じょうほうげん)(Single Source of Truth)として使用(しよう)する運用(うんよう)モデルです。

GitOpsワークフロー:
1. 開発者がGitに変更をPush
2. CIがイメージをビルド・テスト
3. CIがデプロイマニフェストのイメージタグを更新
4. ArgoCDがGitとクラスター状態を比較
5. 差異があれば自動同期(または手動承認)
6. クラスターがGit状態と一致

┌────────┐    Push     ┌────────┐   Detect   ┌────────┐
Dev   │ ──────────>Git<────────>ArgoCD└────────┘             └────────┘            └───┬────┘
Sync
                                            ┌────▼────┐
K8sCluster                                            └─────────┘

6.2 ArgoCD App of Appsパターン

# apps/root-app.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: root-app
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/myorg/gitops-config
    targetRevision: main
    path: apps
  destination:
    server: https://kubernetes.default.svc
    namespace: argocd
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
# apps/api-service.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: api-service
spec:
  project: default
  source:
    repoURL: https://github.com/myorg/gitops-config
    path: services/api
    targetRevision: main
  destination:
    server: https://kubernetes.default.svc
    namespace: production
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
      - CreateNamespace=true

6.3 Argo Rollouts(段階的(だんかいてき)デリバリー)

apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: api-service
spec:
  replicas: 10
  strategy:
    canary:
      canaryService: api-canary
      stableService: api-stable
      trafficRouting:
        istio:
          virtualService:
            name: api-vsvc
      steps:
        - setWeight: 10
        - pause:
            duration: 5m
        - analysis:
            templates:
              - templateName: success-rate
        - setWeight: 30
        - pause:
            duration: 5m
        - analysis:
            templates:
              - templateName: success-rate
        - setWeight: 60
        - pause:
            duration: 5m
        - setWeight: 100
# AnalysisTemplate
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
  name: success-rate
spec:
  metrics:
    - name: success-rate
      interval: 60s
      successCondition: result[0] >= 0.95
      provider:
        prometheus:
          address: http://prometheus:9090
          query: |
            sum(rate(http_requests_total{status=~"2.*",app="api-service",version="canary"}[5m]))
            /
            sum(rate(http_requests_total{app="api-service",version="canary"}[5m]))

7. CI/CDセキュリティ

7.1 セキュリティスキャン統合(とうごう)

CI/CDセキュリティレイヤー:
┌─────────────────────────────────────────────┐
Layer 1: Pre-commit                         │
- Secret scanning (gitleaks, detect-secrets)- Lint (security rules)├─────────────────────────────────────────────┤
Layer 2: PR / Build- SAST (Semgrep, CodeQL, SonarQube)- SCA (Dependabot, Snyk, Trivy)- License compliance                       │
├─────────────────────────────────────────────┤
Layer 3: Container Build- Image scanning (Trivy, Grype)- Base image policy (distroless, alpine)- SBOM generation (Syft)├─────────────────────────────────────────────┤
Layer 4: Deploy- Policy enforcement (OPA/Kyverno)- Signing (cosign, Sigstore)- Runtime security (Falco)└─────────────────────────────────────────────┘

7.2 シークレット管理(かんり)

# GitHub Actionsでシークレット使用
deploy:
  steps:
    - name: Deploy
      env:
        AWS_ACCESS_KEY_ID: ${{ '{{' }} secrets.AWS_ACCESS_KEY_ID {{ '}}' }}
        AWS_SECRET_ACCESS_KEY: ${{ '{{' }} secrets.AWS_SECRET_ACCESS_KEY {{ '}}' }}
      run: |
        aws ecs update-service --cluster prod --service api

# OIDC認証(シークレットレス方式 - 推奨)
permissions:
  id-token: write
  contents: read

steps:
  - uses: aws-actions/configure-aws-credentials@v4
    with:
      role-to-assume: arn:aws:iam::123456789012:role/github-actions
      aws-region: ap-northeast-2

7.3 SBOMとサプライチェーンセキュリティ

# SyftでSBOM生成
- name: Generate SBOM
  uses: anchore/sbom-action@v0
  with:
    image: myapp:latest
    format: spdx-json
    output-file: sbom.spdx.json

# cosignでイメージ署名
- name: Sign image
  run: |
    cosign sign --key env://COSIGN_PRIVATE_KEY myapp:latest

# cosignで署名検証
- name: Verify signature
  run: |
    cosign verify --key cosign.pub myapp:latest

7.4 シークレットスキャン自動化(じどうか)

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/gitleaks/gitleaks
    rev: v8.18.0
    hooks:
      - id: gitleaks

# CIでgitleaks実行
security:
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v4
      with:
        fetch-depth: 0
    - uses: gitleaks/gitleaks-action@v2
      env:
        GITHUB_TOKEN: ${{ '{{' }} secrets.GITHUB_TOKEN {{ '}}' }}

8. デプロイ戦略(せんりゃく)比較(ひかく)

8.1 戦略比較表(ひかくひょう)

戦略(せんりゃく)ダウンタイムリスクリソースコストロールバック速度(そくど)複雑度(ふくざつど)
Recreateあり高(たか)い1x遅(おそ)い低(ひく)い
Rolling Updateなし中(ちゅう)1x〜1.25x低い
Blue-Greenなし低い2x即時(そくじ)
Canaryなし非常(ひじょう)に低い1.1x即時高い
A/B Testingなし非常に低い1.1x即時非常に高い

8.2 Blue-Greenデプロイ

# Kubernetes Blue-Greenデプロイ
apiVersion: v1
kind: Service
metadata:
  name: api-service
spec:
  selector:
    app: api
    version: green  # blueからgreenに切替
  ports:
    - port: 80
      targetPort: 8080
Blue-Green切替プロセス:
1. Blue(v1)運用中 → Green(v2)デプロイ
2. Greenヘルスチェック・スモークテスト
3. Service selectorをGreenに切替
4. 問題時はBlueに即時ロールバック
5. 安定化後にBlueリソースを整理

[Users][LB][Blue v1] Active
                  [Green v2] ← 準備中

[Users][LB][Blue v1] ← 待機
                  [Green v2] Active

8.3 カナリアデプロイ

# Istio VirtualServiceでカナリアデプロイ
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: api-service
spec:
  hosts:
    - api-service
  http:
    - route:
        - destination:
            host: api-service
            subset: stable
          weight: 90
        - destination:
            host: api-service
            subset: canary
          weight: 10

8.4 Feature Flags

// LaunchDarklyまたは自社Feature Flagシステム
import { featureFlags } from './feature-flags';

async function handleRequest(req: Request) {
  const userId = req.user.id;

  if (await featureFlags.isEnabled('new-checkout-flow', userId)) {
    return newCheckoutFlow(req);
  }

  return legacyCheckoutFlow(req);
}
Feature Flagベースのデプロイ:
1. 新機能をフラグで囲んでコードをデプロイ
2. 内部ユーザーのみ有効化
3. 段階的に割合を拡大(1%5%25%100%4. 問題時はフラグを無効にするだけで即時非活性化
5. デプロイとリリースを分離

9. ロールバック戦略(せんりゃく)

9.1 自動(じどう)ロールバック

# Argo Rollouts自動ロールバック
spec:
  strategy:
    canary:
      steps:
        - setWeight: 10
        - analysis:
            templates:
              - templateName: error-rate-check
      # 分析失敗時に自動ロールバック
      abortScaleDownDelaySeconds: 30
# Kubernetes Deployment自動ロールバック
apiVersion: apps/v1
kind: Deployment
spec:
  progressDeadlineSeconds: 300  # 5分以内に完了しなければ失敗
  minReadySeconds: 30
  strategy:
    rollingUpdate:
      maxSurge: 25%
      maxUnavailable: 0

9.2 サーキットブレイカーパターン

// デプロイサーキットブレイカー
class DeploymentCircuitBreaker {
  private errorThreshold = 0.05; // 5%エラー率
  private windowSize = 300;       // 5分ウィンドウ

  async shouldRollback(metrics: DeploymentMetrics): Promise<boolean> {
    const errorRate = metrics.errors / metrics.totalRequests;
    const p99Latency = metrics.p99LatencyMs;

    return (
      errorRate > this.errorThreshold ||
      p99Latency > 3000 // 3秒超過
    );
  }

  async executeRollback(deployment: string) {
    console.log(`Rolling back ${deployment}`);
    await exec(`kubectl rollout undo deployment/${deployment}`);

    await notify({
      channel: '#deployments',
      message: `Auto-rollback triggered for ${deployment}`,
      severity: 'critical'
    });
  }
}

9.3 データベースマイグレーションロールバック

安全なDBマイグレーション戦略:
1. Expand-Contractパターン
   Phase 1 (Expand): 新カラム追加、両方に書き込み
   Phase 2 (Migrate): 既存データの移行
   Phase 3 (Contract): 旧カラム削除

2. ロールバック可能なマイグレーションのみ適用
   - カラム追加(ロールバック可能)
   - インデックス追加(ロールバック可能)
   - カラム削除(ロールバック不可 → Expand-Contract使用)
   - 型変更(ロールバック不可 → 新カラム追加後に切替)
-- 安全なマイグレーション例
-- Step 1: 新カラム追加(ロールバック可能)
ALTER TABLE users ADD COLUMN email_verified BOOLEAN DEFAULT FALSE;

-- Step 2: データマイグレーション(バックグラウンド)
UPDATE users SET email_verified = TRUE
WHERE verified_at IS NOT NULL;

-- Step 3: アプリコードで新カラム使用に切替
-- Step 4: 旧カラム削除(別マイグレーション)
-- ALTER TABLE users DROP COLUMN verified_at;

10. パイプラインヘルスモニタリング

10.1 コア指標(しひょう)

パイプラインヘルスダッシュボード:
┌─────────────────────────────────────────┐
Build Time Trend│  ██████████████ 8m (avg)Target: < 10m                          │
├─────────────────────────────────────────┤
Success Rate│  ████████████████████ 94%Target: > 95%├─────────────────────────────────────────┤
Flaky Test Rate│  ██ 3%Target: < 2%├─────────────────────────────────────────┤
Mean Time to Recovery (MTTR)│  ████ 25min                             │
Target: < 30min                        │
└─────────────────────────────────────────┘

10.2 ビルド時間(じかん)追跡(ついせき)

# ビルドメトリクスをDatadogに報告
- name: Report build metrics
  if: always()
  run: |
    END_TIME=$(date +%s)
    DURATION=$((END_TIME - START_TIME))
    curl -X POST "https://api.datadoghq.com/api/v1/series" \
      -H "DD-API-KEY: $DD_API_KEY" \
      -d "{
        \"series\": [{
          \"metric\": \"ci.build.duration\",
          \"points\": [[$END_TIME, $DURATION]],
          \"tags\": [
            \"repo:myapp\",
            \"branch:$GITHUB_REF_NAME\",
            \"status:$JOB_STATUS\"
          ]
        }]
      }"

10.3 失敗(しっぱい)分析(ぶんせき)自動化(じどうか)

# ビルド失敗自動分類スクリプト
import re
from enum import Enum

class FailureCategory(Enum):
    FLAKY_TEST = "flaky_test"
    DEPENDENCY = "dependency"
    COMPILATION = "compilation"
    INFRASTRUCTURE = "infrastructure"
    TIMEOUT = "timeout"
    UNKNOWN = "unknown"

def categorize_failure(log: str) -> FailureCategory:
    patterns = {
        FailureCategory.FLAKY_TEST: [
            r"retry.*failed",
            r"intermittent",
            r"flaky"
        ],
        FailureCategory.DEPENDENCY: [
            r"npm ERR!.*404",
            r"Could not resolve dependencies",
            r"ECONNRESET"
        ],
        FailureCategory.COMPILATION: [
            r"error TS\d+",
            r"SyntaxError",
            r"TypeError"
        ],
        FailureCategory.INFRASTRUCTURE: [
            r"runner.*offline",
            r"disk space",
            r"out of memory"
        ],
        FailureCategory.TIMEOUT: [
            r"timed out",
            r"deadline exceeded"
        ]
    }

    for category, regexes in patterns.items():
        for pattern in regexes:
            if re.search(pattern, log, re.IGNORECASE):
                return category

    return FailureCategory.UNKNOWN

11. 面接(めんせつ)質問集(しつもんしゅう)

基本概念(きほんがいねん)

Q1. CIとCDの違(ちが)いを説明(せつめい)してください。

**CI(Continuous Integration)**は、開発者(かいはつしゃ)がコード変更(へんこう)を頻繁(ひんぱん)にメインブランチに統合(とうごう)するプラクティスです。各統合(かくとうごう)は自動化(じどうか)されたビルドとテストで検証(けんしょう)されます。

CDには2つの意味(いみ)があります:

  • Continuous Delivery: コードが常(つね)にデプロイ可能(かのう)な状態(じょうたい)を維持(いじ)。プロダクションデプロイは手動(しゅどう)承認(しょうにん)。
  • Continuous Deployment: すべての変更が自動的(じどうてき)にプロダクションにデプロイ。手動介入(かいにゅう)なし。

核心的(かくしんてき)な違い: CIは「統合」に、CDは「配信(はいしん)/デプロイ」に焦点(しょうてん)。CI無(な)しにCDは不可能(ふかのう)ですが、CIだけでCDをしないことは可能です。

Q2. DORAメトリクス4つを説明(せつめい)してください。
  1. デプロイ頻度(ひんど)(Deployment Frequency): プロダクションにどれくらい頻繁(ひんぱん)にデプロイするか
  2. リードタイム(Lead Time for Changes): コミットからプロダクションデプロイまでの時間(じかん)
  3. 変更失敗率(へんこうしっぱいりつ)(Change Failure Rate): デプロイ中(ちゅう)に失敗(しっぱい)またはロールバックが必要(ひつよう)な割合(わりあい)
  4. 復旧時間(ふっきゅうじかん)(MTTR): 障害発生(しょうがいはっせい)から復旧(ふっきゅう)までの時間

Eliteチーム: 1日複数回(ふくすうかい)デプロイ、1時間未満(みまん)のリードタイム、5%未満の失敗率、1時間未満の復旧

Q3. GitOpsのコア原則(げんそく)を説明(せつめい)してください。
  1. 宣言的(せんげんてき)(Declarative): システム状態(じょうたい)を宣言的に定義(ていぎ)
  2. バージョン管理(かんり)(Versioned): Gitを単一(たんいつ)真実(しんじつ)の情報源(じょうほうげん)として使用
  3. 自動適用(じどうてきよう)(Automated): 承認(しょうにん)された変更(へんこう)が自動的にシステムに適用(てきよう)
  4. 自己修復(じこしゅうふく)(Self-Healing): 実際(じっさい)の状態が宣言された状態と異(こと)なれば自動復旧(ふっきゅう)

メリット: 監査(かんさ)証跡(しょうせき)、容易(ようい)なロールバック、PRベースの変更管理、再現可能(さいげんかのう)な環境(かんきょう)

Q4. Blue-GreenデプロイとカナリアデプロイのUI(ちが)いを説明してください。

Blue-Green: 2つの同一(どういつ)環境(かんきょう)(Blue/Green)を運用(うんよう)。新(あたら)しいバージョンをGreenにデプロイ後(ご)、トラフィックを一度(いちど)に切替(きりか)え。ロールバックはBlueに即時(そくじ)切替。

  • メリット: 即時ロールバック、シンプルな実装(じっそう)
  • デメリット: リソース2倍必要(ひつよう)、データベース同期(どうき)が複雑(ふくざつ)

Canary: 新バージョンを少数(しょうすう)(1〜10%)に先(さき)にデプロイ。メトリクス分析(ぶんせき)後(ご)、段階的(だんかいてき)に拡大(かくだい)。

  • メリット: リスク最小化(さいしょうか)、実際のトラフィックで検証
  • デメリット: 実装が複雑、モニタリング必須(ひっす)
Q5. シフトレフト(Shift Left)とは何(なん)ですか?

テストとセキュリティを開発(かいはつ)ライフサイクルの左側(ひだりがわ)(初期段階(しょきだんかい))に移動(いどう)させる戦略(せんりゃく)です。

適用例(てきようれい):

  • pre-commit hookでコードリント、フォーマット、シークレットスキャン
  • PR段階(だんかい)でSAST、SCA、ユニットテスト実行(じっこう)
  • ビルド時(じ)にコンテナイメージスキャン
  • IDEプラグインで開発中(かいはつちゅう)にリアルタイムフィードバック

効果(こうか): 欠陥(けっかん)を早(はや)く発見(はっけん)するほど修正(しゅうせい)コストが指数的(しすうてき)に減少(げんしょう)(プロダクション比(ひ)10〜100倍(ばい)の節約(せつやく))

深掘(ふかぼ)り質問(しつもん)

Q6. フレイキーテスト(Flaky Test)をどう管理(かんり)しますか?
  1. 検出(けんしゅつ): 同(おな)じコードで繰(く)り返(かえ)し実行(じっこう)時(じ)に結果(けっか)が変(か)わるテストを特定(とくてい)
  2. 隔離(かくり): フレイキーテストを別(べつ)のtest suiteに分離(ぶんり)、continue-on-error適用(てきよう)
  3. リトライ: jestのretryTimes、pytest-rerunfailuresなどで自動(じどう)リトライ
  4. 追跡(ついせき): フレイキーテストダッシュボードで頻度(ひんど)・パターン分析(ぶんせき)
  5. 根本原因(こんぽんげんいん)解決(かいけつ): タイミング問題(もんだい)、共有状態(きょうゆうじょうたい)、外部依存性(がいぶいぞんせい)など原因(げんいん)除去(じょきょ)
  6. ポリシー: 一定期間内(いっていきかんない)に修正(しゅうせい)されなければ無効化(むこうか)または削除(さくじょ)
Q7. Dockerイメージビルドをどう最適化(さいてきか)しますか?
  1. マルチステージビルド: ビルドツールを最終(さいしゅう)イメージから除外(じょがい)
  2. レイヤーキャッシュ最適化(さいてきか): 頻繁(ひんぱん)に変更(へんこう)されるファイルを後(あと)にCOPY
  3. 軽量(けいりょう)ベースイメージ: alpine、distroless使用
  4. .dockerignore: 不要(ふよう)なファイルを除外
  5. BuildKit使用(しよう): 並列(へいれつ)ビルド、キャッシュマウント
  6. 依存関係(いぞんかんけい)分離(ぶんり): package.jsonを先(さき)にコピーしてnpm ciキャッシュ
  7. Kaniko: Dockerデーモン無(な)しでビルド(CI/CDセキュリティ向上(こうじょう))
Q8. シークレット管理(かんり)のベストプラクティスを説明(せつめい)してください。
  1. 絶対(ぜったい)にGitにコミットしない: gitleaks、detect-secretsでpre-commit検査(けんさ)
  2. OIDC認証(にんしょう): 長期(ちょうき)シークレットの代(か)わりに一時(いちじ)トークンを使用
  3. シークレットマネージャー活用(かつよう): AWS Secrets Manager、HashiCorp Vault、Doppler
  4. 最小権限(さいしょうけんげん)原則(げんそく): 必要(ひつよう)最小限(さいしょうげん)の権限(けんげん)のみ付与(ふよ)
  5. シークレットローテーション: 定期的(ていきてき)にシークレット更新(こうしん)を自動化(じどうか)
  6. 監査(かんさ)ログ: シークレットアクセス記録(きろく)を追跡(ついせき)
  7. 環境(かんきょう)分離(ぶんり): dev/staging/prodシークレットを分離
Q9. データベースマイグレーションを安全(あんぜん)にロールバックする方法(ほうほう)は?

Expand-Contractパターンを使用(しよう):

Phase 1 (Expand):

  • 新(あたら)しいカラム/テーブルを追加(ついか)
  • アプリが旧(きゅう)スキーマと新(しん)スキーマの両方(りょうほう)に互換(ごかん)するようコード修正(しゅうせい)
  • 新スキーマへのデータ書(か)き込(こ)みを開始(かいし)

Phase 2 (Migrate):

  • 既存(きそん)データを新スキーマに移行(いこう)(バックグラウンド)
  • アプリを新スキーマのみ使用(しよう)するように切替(きりか)え

Phase 3 (Contract):

  • 旧カラム/テーブルを削除(さくじょ)(別(べつ)デプロイ)
  • この段階(だんかい)のみロールバック不可(ふか)

核心(かくしん): 各(かく)段階(だんかい)が独立(どくりつ)してロールバック可能(かのう)であること。

Q10. CI/CDパイプラインのセキュリティをどう強化(きょうか)しますか?
  1. サプライチェーンセキュリティ: SBOM生成(せいせい)、イメージ署名(しょめい)(cosign)、SLSA準拠(じゅんきょ)
  2. シークレット管理(かんり): OIDC、Vault、環境変数(かんきょうへんすう)の最小化(さいしょうか)
  3. SAST/DAST/SCA: Semgrep、Trivy、Dependabot統合(とうごう)
  4. コンテナセキュリティ: 非(ひ)rootユーザー、distrolessベース、イメージスキャン
  5. ポリシー遵守(じゅんしゅ): OPA/Kyvernoでデプロイポリシーを強制(きょうせい)
  6. アクセス制御(せいぎょ): 最小権限(さいしょうけんげん)、ブランチ保護(ほご)ルール
  7. 監査(かんさ): すべてのデプロイ記録(きろく)を追跡(ついせき)、変更履歴(へんこうりれき)を維持(いじ)
Q11. GitHub ActionsとJenkinsの長所(ちょうしょ)・短所(たんしょ)を比較(ひかく)してください。

GitHub Actions:

  • 長所: GitHubネイティブ統合(とうごう)、SaaSでメンテナンス不要(ふよう)、Marketplaceエコシステム、簡単(かんたん)なYAML設定(せってい)
  • 短所: GitHub依存性(いぞんせい)、カスタマイズの限界(げんかい)、複雑(ふくざつ)なワークフロー管理(かんり)が困難(こんなん)

Jenkins:

  • 長所: 完全(かんぜん)な自由度(じゆうど)、1800+プラグイン、セルフホスト制御(せいぎょ)、Groovyスクリプティング
  • 短所: 高(たか)いメンテナンスコスト、複雑な設定、セキュリティパッチ管理、スケーリングの困難さ

選択基準(せんたくきじゅん): 小規模(しょうきぼ)/GitHub中心(ちゅうしん)プロジェクトはActions、複雑なエンタープライズ/マルチSCMはJenkins

Q12. Argo RolloutsのProgressive Deliveryを説明(せつめい)してください。

Progressive Deliveryは、新(あたら)しいバージョンを段階的(だんかいてき)にデプロイしながら、自動化(じどうか)された分析(ぶんせき)で安全性(あんぜんせい)を検証(けんしょう)する方式(ほうしき)です。

Argo Rolloutsワークフロー:

  1. カナリアに10%トラフィックを割(わ)り当(あ)て
  2. AnalysisTemplateで成功率(せいこうりつ)、レイテンシーを検証(5分(ふん))
  3. 通過(つうか)時(じ)に30%に拡大(かくだい)、再(ふたた)び分析
  4. 60%、100%へ段階的(だんかいてき)に拡大
  5. 分析失敗時(しっぱいじ)に自動(じどう)ロールバック

主要(しゅよう)コンポーネント:

  • Rollout: デプロイ戦略(せんりゃく)を定義(ていぎ)
  • AnalysisTemplate: 検証条件(けんしょうじょうけん)を定義(Prometheus、Datadogなど)
  • TrafficRouting: Istio、Nginx、ALBなどと連携(れんけい)
Q13. パイプラインパフォーマンスをどう最適化(さいてきか)しますか?
  1. 並列処理(へいれつしょり): 独立(どくりつ)した作業(さぎょう)を同時(どうじ)実行(じっこう)
  2. テスト分割(ぶんかつ): シャーディングでテストを複数(ふくすう)のランナーに分配(ぶんぱい)
  3. キャッシュ: 依存関係(いぞんかんけい)、Dockerレイヤー、ビルド結果(けっか)のキャッシュ
  4. 選択的(せんたくてき)実行(じっこう): 変更(へんこう)されたファイルに応(おう)じて必要(ひつよう)な作業(さぎょう)のみ実行
  5. 増分(ぞうぶん)ビルド: 全体(ぜんたい)ビルドの代(か)わりに変更部分(ぶぶん)のみビルド
  6. リソース最適化: ランナーサイズ、同時実行数(どうじじっこうすう)の調整(ちょうせい)
  7. フィードバックループ短縮(たんしゅく): 速(はや)いチェックを先(さき)に、遅(おそ)いチェックは後(あと)に
Q14. Feature Flagベースのデプロイの長所(ちょうしょ)・短所(たんしょ)は?

長所:

  • デプロイとリリースの分離(ぶんり): コードをデプロイしつつ、機能(きのう)は後(あと)から有効化(ゆうこうか)
  • 高速(こうそく)ロールバック: コードロールバック無(な)しでフラグを無効(むこう)にするだけ
  • 段階的(だんかいてき)リリース: ユーザー比率(ひりつ)を段階的(だんかいてき)に拡大(かくだい)
  • A/Bテスト: 機能(きのう)ごとの実験(じっけん)が可能(かのう)

短所:

  • 技術的負債(ぎじゅつてきふさい): 古(ふる)いフラグの整理(せいり)が必要(ひつよう)
  • 複雑性(ふくざつせい): フラグの組(く)み合(あ)わせでテストケースが増加(ぞうか)
  • コード可読性(かどくせい): 条件分岐(じょうけんぶんき)の増加(ぞうか)でコードが複雑化(ふくざつか)
  • 一貫性(いっかんせい): ユーザーごとに異(こと)なる体験(たいけん)でバグ再現(さいげん)が困難(こんなん)
Q15. モノレポでのCI/CD戦略(せんりゃく)を説明(せつめい)してください。
  1. 影響範囲(えいきょうはんい)分析(ぶんせき): 変更(へんこう)されたファイルから影響(えいきょう)を受(う)けるパッケージのみビルド/テスト
  2. ツール活用(かつよう): Turborepo、Nx、Bazelなどで依存関係(いぞんかんけい)グラフベースのビルド
  3. キャッシュ: リモートキャッシュ(Turborepo Remote Cache)でビルド結果(けっか)を共有(きょうゆう)
  4. 選択的(せんたくてき)デプロイ: 変更されたサービスのみデプロイ
  5. 並列処理(へいれつしょり): 独立(どくりつ)したパッケージを同時(どうじ)にビルド/テスト
# Turborepo例
turbo run build --filter=...[HEAD~1]
# HEAD以降に変更されたパッケージとその依存パッケージのみビルド

12. クイズ

Q1. DORAメトリクスでEliteチームのデプロイ頻度(ひんど)は?

正解(せいかい): 1日(にち)に複数回(ふくすうかい)(On-demand, multiple deploys per day)

Eliteチームは1日に複数回デプロイしながらも、変更失敗率(へんこうしっぱいりつ)5%未満(みまん)、復旧時間(ふっきゅうじかん)1時間未満を維持(いじ)しています。

Q2. Expand-Contractパターンでロールバック不可能(ふかのう)な段階(だんかい)は?

正解: Contract段階(旧(きゅう)カラム/テーブルの削除(さくじょ))

Expand(追加(ついか))とMigrate(マイグレーション)はロールバック可能(かのう)ですが、Contract(削除)はデータが消(き)えるためロールバックが不可能です。そのため、Contractは十分(じゅうぶん)な安定化(あんていか)期間(きかん)の後(あと)に別途(べっと)実施(じっし)します。

Q3. GitOpsにおける「単一(たんいつ)真実(しんじつ)の情報源(じょうほうげん)」とは?

正解: Gitリポジトリ

GitOpsにおいて、Gitリポジトリはシステムの望(のぞ)ましい状態(じょうたい)(Desired State)を定義(ていぎ)する唯一(ゆいいつ)のソースです。クラスターの実際(じっさい)の状態は常(つね)にGitで宣言(せんげん)された状態と一致(いっち)する必要(ひつよう)があり、ArgoCDのようなツールがこれを自動的(じどうてき)に検出(けんしゅつ)・同期(どうき)します。

Q4. カナリアデプロイで自動(じどう)ロールバックをトリガーする基準(きじゅん)は?

正解: AnalysisTemplateに定義(ていぎ)されたメトリクス基準(成功率(せいこうりつ)、レイテンシーなど)

Argo RolloutsのAnalysisTemplateでPrometheus、Datadogなどのメトリクスをクエリし、成功率が基準(きじゅん)(例: 95%)以下(いか)、またはP99レイテンシーが基準を超過(ちょうか)した場合(ばあい)、自動的(じどうてき)にロールバックをトリガーします。

Q5. SBOM(Software Bill of Materials)の目的(もくてき)は?

正解: ソフトウェアに含(ふく)まれるすべての構成要素(こうせいようそ)(ライブラリ、依存関係(いぞんかんけい))のリストを提供(ていきょう)し、サプライチェーンセキュリティを強化(きょうか)すること

SBOMはソフトウェアの「材料(ざいりょう)リスト」であり、脆弱性(ぜいじゃくせい)発見時(はっけんじ)に影響範囲(えいきょうはんい)を迅速(じんそく)に把握(はあく)し、ライセンスコンプライアンスを確認(かくにん)し、サプライチェーン攻撃(こうげき)(例: Log4Shell)への対応(たいおう)を支援(しえん)します。Syft、Trivyなどのツールで自動生成(じどうせいせい)できます。


13. 参考(さんこう)資料(しりょう)

公式(こうしき)ドキュメント

DORAとDevOps

セキュリティ

デプロイ戦略(せんりゃく)

ツールとエコシステム