RAKUS Developers Blog | ラクス エンジニアブログ

株式会社ラクスのITエンジニアによる技術ブログです。

【SREの運用改善事例】CIとAIで改善するk8sエコシステムのバージョンアップ運用

1. はじめに

こんにちは!SRE課のモリモトです。

本記事では、CIとAIでKubernetesエコシステムのバージョンアップ運用を改善した事例をご紹介します。

なぜ改善が必要だったか

SREチームでは、Kubernetesエコシステムの多数のOSSを社内共通Helm Chartとして管理し、各プロダクトチームへ提供しています。

バージョンの更新検知にはRenovateを利用していますが、バージョンアップPRを継続的に処理する運用が確立されておらず、PRが滞留している状態でした。 その結果、差分がますます大きくなり動作確認や影響調査のコストが増大し、脆弱性対応等の緊急性の高いもの以外は対応できておりませんでした。

この状況を解消するため、バージョンアップ作業の効率化とチーム全員で継続的に対応できる仕組みづくりを行う必要がありました。

どんな改善をした?

まずCIで機械的に判定できるリスクを先に落とし、次にAIでリリースノート確認や影響調査を行い、人間は動作確認と最終判断を行うのみとすることでバージョンアップ対応を効率化しました。

2. バージョンアップ運用フロー

今回構築した運用フローは以下の6ステップです。

Step 自動化レベル 内容
1 自動 Renovate PRが作成される
2 自動 CIによる機械的チェック
3 半自動 PRに対してreview-renovate-prラベル付与することで、AIレビューを起動
4 手動 人間がAIチェックレポートを確認し、マージ可否を判断
5 手動 developブランチにマージし、検証用クラスタで動作確認
6 手動 mainブランチにマージ

Renovateによる自動検知から始まり、CIとAIによるチェックを経て、最終的に人間が判断・検証を行う「自動化と安全性のバランス」を重視したフローになっています。

ここから、Step2とStep3の中身について紹介していきます。

2-1. CIによる機械的チェック(GitHub Actions)

RenovateによってHelm ChartのバージョンアップPRが作成された際に、3種類の機械的なチェックを実行することで明らかにマージ不可なものを振り落としていきます。

①helm templateコマンドによるレンダリングチェック

バージョンアップによってHelmテンプレートの構文エラーや必須パラメータの欠落が発生し、マニフェストが正常に出力できなくなる事態を未然に防ぐために、レンダリングチェックを行っています。

実装詳細

リポジトリに存在するすべてのChartを毎回チェックするとCIの実行時間が増大してしまうため、まずは git diff を用いて変更があったChartのディレクトリのみを抽出し、動的にテストマトリクスを生成します。

# 変更されたHelmチャートを検出してマトリックスを作成
set-matrix:
  run: |
    # 変更があったファイルのパスを抽出(例: charts/app-name/values.yaml)
    targets=$(git diff --diff-filter=AMR --name-only \
      "${{ github.event.pull_request.base.sha }}" \
      "${{ github.event.pull_request.head.sha }}" \
      -- "${{ inputs.chart_dir }}")
    
    if [ -z "${targets}" ]; then
      targets=$(jq -nc '[]')
    else
      # ディレクトリ階層を整理して重複を排除し、JSON配列に変換
      # 例: 'charts/app-a/values.yaml' -> 'charts/app-a' のように抽出
      # ※注意:リポジトリのディレクトリ構成に合わせて cut の階層 (-f 1,2) は適宜変更してください
      targets=$(printf '%s\n' "${targets}" | cut -d '/' -f 1,2 | sort -u | jq -R | jq -sc)
    fi
    echo "value=${targets}" >> "$GITHUB_OUTPUT"

その上で、抽出された各Chartに対して以下のチェックを行います。

  • 依存関係の解決: helm dependency updateを実行し、サブチャートを含めて正しく依存関係が解決できるか確認。
  • 環境ごとの網羅的チェック: values/ ディレクトリ配下に配置された各環境用(開発・本番など)のvaluesファイルをループ処理し、ベースの values.yaml と組み合わせて helm templateが成功するかを確認。
# Helm dependency update
if [[ -f "$CHART_PATH/Chart.yaml" ]]; then
  echo "Running helm dependency update..."
  helm dependency update "$CHART_PATH" 2>&1
fi

# values/ ディレクトリ内の全valuesファイルでテンプレート実行
VALUES_DIR="$CHART_PATH/values"

if [[ -d "$VALUES_DIR" ]]; then
  for values_file in "$VALUES_DIR"/*.yaml; do
    # ベースのvalues.yamlと環境特有のvalues.yamlをマージしてテスト
    helm template "$CHART_NAME" "$CHART_PATH" \
      -f "$CHART_PATH/values.yaml" \
      -f "$values_file" > /dev/null 2>&1
    
    if [[ $? -eq 0 ]]; then
      echo "✅ Successfully rendered with $(basename "$values_file")"
    else
      echo "::error::Failed to render $CHART_PATH with values: $(basename "$values_file")"
      exit 1
    fi
  done
fi

これにより、「一部の環境のvaluesファイルとの組み合わせでのみテンプレートのレンダリングに失敗する」といった状況を早期に検知・除外できます。

②PlutoによるKubernetes API互換性チェック

非推奨または削除されたAPIを利用しているマニフェストが原因で障害が起きることは避けたいので、Pluto を用いてk8s APIの互換性チェックを組み込んでいます。

実装詳細

pluto detect-filesを実行し、Helm Chart内に非推奨APIや削除予定のAPIが含まれていないかをスキャンします。(チェック対象のk8sバージョンは現在のk8sクラスタのバージョンに設定)

pluto detect-files -d "${CHART_PATH}" \
   -o wide \
   --target-versions "k8s=${{ inputs.target_k8s_version }}"

③HelmChart展開後のマニフェスト差分把握

Helm Chartのバージョンアップの際に、「Chartのバージョンが上がったことで、最終的に出力されるKubernetesマニフェストにどのような変化が起きるのか?影響のある値の変更が入っているか?」を把握するのが非常に面倒でした。

そこで、マニフェストの差分(Diff)を自動生成してPRにコメントを投稿する仕組みを組み込みました。

※以下の例は現在弊社で使用しているバージョンではありません。

PRコメントの例

実装詳細

まずは、git worktreeを活用してCI内で一時ディレクトリを作成し、PRの base(変更前)と head(変更後)のコードツリーを同時に展開します。 そして、それぞれのツリーで対象のvaluesファイルを適用してhelm templateを実行し、出力された展開後のマニフェスト同士をdiffコマンドで比較します。

# 一時ディレクトリを作成(例: /tmp/tmp.xxxxx)
WORK_DIR=$(mktemp -d)

# 1. PRの変更前(base)と変更後(head)のコードを別々のディレクトリに展開
git worktree add --detach "$WORK_DIR/base" "${{ github.event.pull_request.base.sha }}"
git worktree add --detach "$WORK_DIR/head" "${{ github.event.pull_request.head.sha }}"

# 2. 変更前(base)のソースからマニフェストをレンダリングして保存
helm template "${CHART_NAME}" "$WORK_DIR/base/${CHART_PATH}" \
  -f "$WORK_DIR/base/${CHART_PATH}/values.yaml" \
  -f "$WORK_DIR/base/${CHART_PATH}/values/main.yaml" \
  --output-dir "$WORK_DIR/rendered-base"

# 3. 変更後(head)のソースからマニフェストをレンダリングして保存
helm template "${CHART_NAME}" "$WORK_DIR/head/${CHART_PATH}" \
  -f "$WORK_DIR/head/${CHART_PATH}/values.yaml" \
  -f "$WORK_DIR/head/${CHART_PATH}/values/main.yaml" \
  --output-dir "$WORK_DIR/rendered-head"

# 4. レンダリング結果のディレクトリ同士を比較し、差分ファイルを出力
diff -r -U 3 -N "$WORK_DIR/rendered-base/" "$WORK_DIR/rendered-head/" > /tmp/helm-diff.txt || true

その後、出力された差分をgh cliを使ってPRに投稿します。

差分が200行を超える大規模な変更だった場合はPRコメントの文字数制限にひっかからないように、表示するのは先頭200行のみでそれ以降は「以下省略。詳細はGitHub Actionsのログをご確認ください」というメッセージと共に該当ActionログへのURLを案内する工夫を入れています。

※もっと綺麗に書ける気がします。汚くてすみません。。

DIFF_OUTPUT=$(cat "$DIFF_FILE" || echo "")
LINE_COUNT=$(echo "$DIFF_OUTPUT" | wc -l)

# 差分が長すぎる(200行超)場合は丸める処理
if [ "$LINE_COUNT" -gt 200 ]; then
  DIFF_OUTPUT=$(echo "$DIFF_OUTPUT" | head -200)
  JOB_ID=$(gh api "/repos/${GH_REPOSITORY}/actions/runs/${GH_ACTIONS_RUN_ID}/jobs" \
    --jq ".jobs[] | select(.name == \"diff-helm-charts ($CHART_PATH)\") | .id")
  ACTIONS_URL="${GH_SERVER_URL}/${GH_REPOSITORY}/actions/runs/${GH_ACTIONS_RUN_ID}/job/${JOB_ID}"
  DIFF_OUTPUT="${DIFF_OUTPUT}
  ...
  以下省略。詳細はGitHub Actionsのログをご確認ください。
  ${ACTIONS_URL}"
fi

# PRに投稿するコメントボディを作成
if [ -z "$DIFF_OUTPUT" ]; then
  BODY="### ✅ Diff: \`${CHART_PATH}\`
  差分は検出されませんでした。
\`${VALUES_FILES_TEXT}\` を適用し、HelmChart展開後のマニフェスト差分をバージョンアップ前後で確認"
else
  # 改行とバッククォートの間の空白を完全に削除
  BODY="### ⚠️ Diff: \`${CHART_PATH}\`
  差分が検出されました。
\`${VALUES_FILES_TEXT}\` を適用し、HelmChart展開後のマニフェスト差分をバージョンアップ前後で確認
  以下は差分内容です。

  \`\`\`diff
  ${DIFF_OUTPUT}
  \`\`\`"
fi

# PRへコメント投稿
gh pr comment "${GH_PR_NUMBER}" \
  --repo "${GH_REPOSITORY}" \
  --body "$BODY"

展開後の差分を可視化することにより、レビュアー(人間)はもちろん、この後に解説するAIもマニフェストの差分を把握することができるので、影響調査の精度を向上させることができます。

2-2. AIによる影響調査

リリースノートをすべて確認して影響調査をするのが非常に手間がかかる作業だったので、AIを活用して改善しました。

仕組みとしては、RenovatePRにreview-renovate-prラベルを付与することでGitHub Actionsが起動し、GitHub ActionsからDevinのPlaybookを起動することでバージョンアップ調査・レビュー用セッションを起動する仕組みを構築しました。

AIレビューコメント例

バージョンアップによる変更点が一覧化され、それが既存のコードに影響があるのかをAIがチェックしてくれています。

※以下の例は現在弊社で使用しているバージョンではありません。

AIレビューコメント①

AIレビューコメント②

なぜラベル起動にしたか

すべてのRenovate PRに対してAIレビューを常時起動させることも可能ですが、AIのコストが膨れ上がってしまうことを懸念し、あえて「人間のラベル付与」をトリガーとして必要なときのみに起動するようにしました。

プロンプト

AIに以下の内容をチェックさせるためのプロンプトを渡しています。

  • 対象OSSの公式リリースノートやChangelogを確認し、破壊的変更や非推奨になったパラメータがないか
  • リポジトリ内の既存コード(k8sマニフェスト、アプリケーションコード等)に対する影響がないか

プロンプトについては、以下ブログに記載されているものを大いに参考にさせていただいております🙇‍♂️🙇‍♂️ ありがとうございます🙇‍♂️🙇‍♂️ tech.uzabase.com

**重要**: メジャーおよびマイナー更新では、**各バージョンごとに個別に評価**すること という指示を与えているのでリリースノートの読み飛ばしを防ぐことができ、高い精度で調査してくれているなと感じています。

## 概要
Renovateによって自動生成されたプルリクエスト(PR)をレビューし、ライブラリ(helm chartやイメージバージョン)のアップデートに伴う変更点の調査、エビデンスに基づく差分分析、コードへの影響評価、および推奨するアクションの提示を実施します。

## ユーザーに必要なもの
- レビュー対象のRenovateのプルリクエストのURL

## 手順
1. 指定されたRenovateのプルリクエストを開き、以下を正確に特定してください。
   - アップデート対象ライブラリ名
   - 旧バージョン → 新バージョン
   - メジャー / マイナー / パッチの種別

2. アップデート対象となった各ライブラリの公式ドキュメント、リリースノート、変更履歴(changelog)などを参照し、バージョン間の主要な変更点(新機能、非推奨機能、破壊的変更、バグ修正など)を特定してください。単に「旧→新」を比較するのではなく、以下を必ず実施してください。

   2-1. 更新タイプの分類
      - メジャー(1.x → 2.x):高リスク、破壊的変更の可能性大
      - マイナー(1.1 → 1.2):中リスク、新機能
      - パッチ(1.1.1 → 1.1.2):低リスク、バグ修正
   
   2-2. メジャー・マイナーバージョン更新時の評価方法
      - **重要**: メジャーおよびマイナー更新では、**各バージョンごとに個別に評価**すること
      - 例: v1.5.0 → v1.7.0 の場合
         - v1.5.0 → v1.6.0 の変更を評価
         - v1.6.0 → v1.7.0 の変更を評価
         - それぞれのバージョンの変更内容を明記
      - パッチバージョン更新は集約して評価可能

   2-3. 複数バージョンジャンプの場合(例:3.18.2→3.19.0)
      - すべての中間バージョンを分析
      - バージョン間の変更を集約
      - 累積的な影響を強調

3. 特定したライブラリの変更点を、元のRenovateのプルリクエストにGitHubのコメント機能を使用して、「ライブラリ変更点の概要」として記載してください。その際に以下のルールを必ず守ってください。
   - コメントタイトル: **ライブラリ変更点の概要**
   - 破壊的変更やCRD変更、対応必須と思われるものについては特に明記
   - 各変更点について、必ずエビデンス(一次情報)を明示

4. ライブラリの変更点を踏まえ、リポジトリ内の既存コード(k8sマニフェスト、アプリケーションコード等)への影響範囲を調査してください。具体的には、以下のような点を確認してください。
    - 調査観点
        - 廃止されたAPIや変更されたAPIの使用箇所
        - 廃止されたパラメータや変更されたパラメータの使用箇所
        - 仕様変更に伴い、修正が必要となるロジック
        - アップデートによる潜在的なバグやパフォーマンスへの影響
        - 推奨機能を使用していない箇所、非推奨機能の使用箇所

5. 調査したコードへの影響範囲と、推奨される修正対応について、元のRenovateのプルリクエストにGitHubのコメント機能を使用して、「コードへの影響と推奨アクション」として記載してください。その際に以下のルールを必ず守ってください。
   - コメントタイトル: **コードへの影響と推奨アクション**
   - コメントの最初に、以下のような**評価サマリ**を記述すること。
      - リスクレベル(低・中・高)
      - マージ判定(マージ可能、条件付きでマージ可能、このままではマージ不可)
   - 各調査結果について、必ずエビデンス(一次情報)を明示すること。「影響がない」と判断する場合も、その根拠を明示すること。

## 仕様
1. Renovate PR には以下がコメントとして記載されていること。
   - 「ライブラリ変更点の概要」
   - 「コードへの影響と推奨アクション」
2. いずれのPRも、レビュー承認なしにマージされていないこと。

## アドバイスとポイント
1.  ライブラリの変更点を調査する際は、公式ドキュメントやリリースノートを最優先の情報源としてください。フォーラムやIssue Trackerの情報は補助的に使用してください。
2.  コードへの影響調査は、直接的な利用箇所だけでなく、間接的な影響や全体的な動作への影響も考慮に入れてください。
3.  GitHubへのコメントやプルリクエストの説明文は、他の開発者が変更の意図と内容を迅速に理解できるよう、明確かつ簡潔に記述してください。
4.  「差分が小さい」「影響は軽微」という表現は、必ず理由とURLを伴わせてください。
5.  大規模な変更や判断に迷う場合は、理由を明記した上で「判断保留」と明示してください。

## 禁止事項
1. レビュアーの承認なしでのマージ禁止
2. エビデンスなしの断定禁止
3. 元のRenovate PR ブランチへの直接コミット禁止

なぜDevinを選定したのか

今回Devinを選択した理由は「とにかく早く形にして検証を回したかったから」です。

社内でDevinをCIから利用した事例があることを把握していたため、すぐ試せそうだったからという理由で選択しただけなので強いこだわりはありません。 GitHub Actions上からサブスク枠のClaudeやCodexを使う方法があるのであればそちらに変えたいなあと考えています。

3. 導入後の効果

個人的な体感で、バージョンアップ対応1回あたりで1時間程度の短縮ができたと思っています。

特に、今までだと脆弱性対応以外のバージョンアップが滞っていたこともありバージョン差異が大きく、膨大な量のリリースノートを確認して影響調査を行う必要があったため非常に大変でしたが、そこをAIが代替してくれるのでかなりの時間が短縮できました。

また、詳細は説明していないですが、動作確認用のk8sクラスタを整備してdevelopブランチにマージするだけで即座に反映される環境を用意したことで、動作確認にかかる時間も短縮することができました。 また、現在進行中ですが動作確認手順の整備やAI活用も進んでいけばさらに短縮できると考えています。

4. 今後の展望

今後、時間があるときに以下のような箇所を改善・対応していきたいです。

  • リリースノートや影響の調査に利用しているAIの選定(コスト面、性能面)
  • AIを利用した動作確認の自動化
  • この仕組みを他チームへ横展開

5. まとめ

日々増え続けるk8sエコシステムの運用改善に少しでもお役に立てたら嬉しいです。

最後までお読みいただきありがとうございました!

参考文献

zenn.dev

zenn.dev

tech.uzabase.com

zenn.dev

zenn.dev

Copyright © RAKUS Co., Ltd. All rights reserved.