Kubernetes での修正 PR の開発
- 状態: 実装済み(検証用のホストで PR の作成から GitOps による反映まで確認)
- 開始日: 2026年10月20日
- 最終更新: 2026年10月23日
目的
修正計画と修正 PRを Kubernetes でも使えるようにする。 直す場所は利用者に明示してもらい、言語パッケージの更新はクラスタの中の使い捨ての Pod で安全に動かす設計にする。
修正の対象と流れ
Git で管理するマニフェストに image: nginx:1.27.2 と書かれた Deployment が動いているとする。
修正版が 1.27.3 なら、そのタグを書き換える PR を作る。
変更前のマニフェストの抜粋:
PR で提案する変更後の抜粋:
通常のマニフェストに加え、Kustomize の images と Helm の values に書かれたイメージの指定も対象にする。
KestreLynx は修正をクラスタへ適用しない。 マージ後の反映は利用者の GitOps・CI が行い、自動同期が有効ならマージがそのまま反映になる。
方針
- 直す場所は利用者が対応付けで明示し、Argo CD や Flux の情報から推測しない
- 管理元の情報だけでは、変更すべきリポジトリやファイルを特定できないため
- Pod に書かれている版・動いている版・リポジトリのファイルの版の3つが一致したときだけ PR にする
- 稼働中の対象とは異なる版をもとに変更を作らないため
- Compose と同じく、digest で固定した参照は手動での対応にする
- タグの更新とは別の変更規則が必要になるため
決定事項
作用範囲の明示(affects)
Helm の1つのキーや Kustomize の1つの指定が、複数のワークロードに効くことがある。 KestreLynx は Helm や Kustomize を展開しないので、効く範囲を推測せず、設定に書いてもらう。
次は、Helm の image キーが、対応付けた web の app に加えて worker の app にも効く場合の設定例である。
affects には、対応付けたコンテナ以外の作用先を、種類・namespace・名前・コンテナ名で列挙する。
remediation:
mappings:
- workload: deployment/shop/web/app
repository: example/deployment
base_branch: main
helm_values:
path: releases/shop/values.yaml
key: image
affects:
- deployment/shop/worker/app
affects がなければ、利用者が「対応付けたコンテナだけに効く」と確認したものとして扱う。
念のため、同じ namespace などで同じ値を使う未記載のワークロードを見つけたら PR の作成を止めるが、記載漏れをすべて見つけられるわけではない。
言語パッケージの更新の実行方式
Docker ではパッケージマネージャーを作業用コンテナで動かしているが、対象のクラスタ内には Docker がない。 外部の Docker ホストを使う案は利用者の準備の負担が大きいため採らず、作業専用の namespace に使い捨ての Pod を作る方式にした。
Pod を作る権限は、利用者が言語パッケージの更新を有効にしたときだけ、その作業用 namespace に限って付ける。
作業用 Pod の安全策
言語パッケージの更新は Kubernetes 1.30 以降を対応対象とし、次の安全策を設ける。
- クラスタの標準機能である ValidatingAdmissionPolicy で作れる Pod の形を固定し、KestreLynx は作業前に違反する Pod の試し作成が拒否されることを確かめる
- 作った Pod の実際の中身が許可した形と一致することを確かめてから、更新元のファイルを渡す
- 結果は作業ごとの鍵で暗号化し、結果の本文は暗号文だけをログに残す
- GitHub のトークンや KestreLynx の設定は作業用 Pod に入れない
検証方法
環境
| 項目 | 内容 |
|---|---|
| OS | Ubuntu 26.04(検証用のホスト) |
| 検証用のホストの K3s | v1.31.5 |
| Flux | v2.4.0 |
| Argo CD | v2.13.2 |
| Helm | v3.16.2 |
| 検証用の GitHub リポジトリ | 修正対象のファイルを置き、PR の作成・マージ・反映を確認 |
| 手元の k3d | K3s v1.31.5・v1.34.12・v1.36.5(ログの上限は v1.31.5 のノードで変更) |
| 非公開の ghcr.io のイメージ | 検証用のホストから取得 |
Flux はマニフェストと Kustomize のアプリを、Argo CD は Helm のアプリを管理し、それぞれ別の対象を扱った。 これらとは別に、どちらも使わず直接適用したアプリも検証した。
確認した内容
| 観点 | 確かめ方 |
|---|---|
| 各形式の修正 PR | 検証用のリポジトリに対象のファイルを置き、KestreLynx が作った PR の差分を確認した |
| 繰り返し実行と記録の復元 | 再実行・PR のクローズ・記録の削除後に、PR の重複と対応付けを確認した |
| GitOps による反映 | PR をマージして変更の取得を促し、稼働中の対象と次の更新判定を確認した |
| 手動での対応になる場合 | イメージの指定や作用範囲を変え、自動更新が止まることを確認した |
| 作業用 Pod の安全策 | 権限・作成規則・通信制限を試し、再起動時の削除とログなどへの平文の記録も確認した |
| PR の置き換えと非公開のレジストリ | 新しい版の公開・再実行・認証情報の変更を行い、PR とコメントの状態、スキャンの成否とログを確認した |
| ノードのログの上限 | 約4.6MB のログを出す更新を上限 1MiB と 144MiB で実行し、ログの切り替わりと更新結果を確認した |
| 新しい版の K3s | 各版で権限・作成規則・更新・再解決を試し、成否と v1.36.5 での新しい Pod の通信制限を確認した |
検証結果
確認した範囲では、修正 PR の作成、既存の PR との対応付け、GitOps による反映、条件が整わない場合の停止は想定どおりに動いた。 検証で見つかった3点とその対応は、末尾の「検証で見つかった点」にまとめた。
修正 PR
言語パッケージの更新と依存元からの再解決は、作業用 Pod で実行した。
| 形式 | 変更内容 |
|---|---|
| マニフェスト | 通常のコンテナ・ネイティブサイドカー・CronJob のイメージ行を1つの PR で更新し、コメントと引用符を保った |
| Dockerfile | FROM のベースイメージを更新した |
| Kustomize | newTag だけを変更した |
| Helm values | タグの値だけを変更した |
| npm・pnpm | lockfile だけを変更し、minimist を 1.2.5 から 1.2.8 に更新した |
| pip | ハッシュ値を含む requirements.txt を変更し、urllib3 を 1.26.4 から 2.8.0 に更新した |
| uv | pyproject.toml と uv.lock を変更し、urllib3 を 1.26.4 から 2.8.0 に更新した |
| npm・pnpm の依存元からの再解決 | lockfile で依存元の mkdirp を 0.5.1 から 0.5.6 に上げ、minimist を 0.0.8 から 1.2.8 に更新した |
uv の依存元からの再解決は、候補に含まれる certifi の公開から72時間が経過していないため待機となり、検証中には PR を作成しなかった。
繰り返し実行と記録の復元
| 状況 | 結果 |
|---|---|
| 同じ更新を繰り返し実行 | PR は重複せず、既存の PR に対応付けられた |
| Dockerfile と pip の PR を閉じて再起動 | 同じ更新の PR は作り直されなかった |
| インスタンスの識別情報を残してローカルの記録を削除 | PR 本文の印から既存の PR と閉じた PR の状態を復元できた |
GitOps による反映
| 状況 | 結果 |
|---|---|
| GitOps 管理下のアプリの PR を作成 | PR 本文に Flux の Kustomization または Argo CD の Application を管理元として表示した |
| マージ後に Flux と Argo CD の変更の取得を促す | マージから約2分で Deployment と CronJob に更新が反映された |
| CronJob の更新が反映 | 新しく始まる Job は新しい版で動き、稼働中の Job は古い版のまま終了まで動いた |
| ロールアウト中や古い Job の稼働中に、新旧の版の混在やファイルとの不一致を検出 | 手動での対応となり、修正 PR の作成・更新を止めた |
| ロールアウトが完了 | 同じ更新を再び提案しなかった |
手動での対応になる場合
| 状況 | 結果 |
|---|---|
| digest で固定したイメージ参照 | 手動での対応となり、同じファイルの更新を止めた |
$imagepolicy の目印がある行 |
別のツールがタグを管理しているため、手動での対応となった |
| Kustomize の作用範囲に記載漏れがある | 同じ namespace で同じイメージを使うワークロードが affects にないため、更新を止めた |
Helm の affects に稼働していないワークロードがある |
稼働中の対象を確認できないため、更新を止めた |
作業用 Pod の安全策
| 状況 | 結果 |
|---|---|
| KestreLynx の権限を確認 | Pod の作成は作業用 namespace に限定され、対象への書き込みや Secret・ConfigMap の読み取りは拒否された |
| 規則違反の Pod の作成と、作成規則の適用が外れた状態を確認 | 検証した規則違反の Pod はすべて拒否され、規則の適用を確認できない場合も作業用 Pod を作らなかった |
| 管理者が実行中の作業用 Pod を変更 | ラベル・注釈・イメージ・既存の toleration の変更と、一時コンテナの追加は拒否された |
| 作業用 namespace の Pod 数の割り当てを使い切る | 作業用 Pod を作らず待機した |
| 作業中に KestreLynx が再起動 | 残った作業用 Pod を起動時に削除した |
| 言語パッケージ更新時のログ・終了メッセージ・Pod の情報・Event を確認 | 収集した情報に、入力や結果の平文は見つからなかった |
| 同じ NetworkPolicy を適用した検証用 Pod で、制限が効いた後に通信を確認 | 確認した接続先では、CoreDNS への DNS と外部の TCP 443 番だけに届いた |
通信制限の適用までに数秒かかった点は、末尾の「検証で見つかった点」に記載した。
PR の置き換えと非公開のレジストリ
| 状況 | 結果 |
|---|---|
| 非公開のレジストリに正しい認証情報で接続 | スキャンと新しい版の一覧の取得に成功した |
| 1.0.0 の稼働中に 1.0.1 への PR を作り、1.0.2 を公開 | 1.0.2 への PR を作り、古い PR に置き換え先を示すコメントを付けて閉じた |
| 置き換え後に再実行 | PR もコメントも増えなかった |
| 誤った認証情報で接続 | スキャンの失敗として通知され、PR は変わらなかった |
| 認証に成功した回と失敗した回のログを確認 | どちらの認証情報もログに見つからなかった |
ノードのログの上限
| 状況 | 結果 |
|---|---|
| 上限を 1MiB にしてログが切り替わる | 結果を受け付けず、失敗扱いにした |
| 失敗の理由を確認 | containerLogMaxSize を150,664,968バイトより大きくする必要があると示した |
| ノードの CPU を絞って4回実行 | ログの切り替わりは3回で起きたが、確実には起こせなかった |
| 同じ CPU の制限で上限を 144MiB に変更 | 6回ともログは切り替わらず、更新に成功した |
新しい版の K3s
| 状況 | 結果 |
|---|---|
| 権限と作成規則を確認 | 作業用 namespace に限った権限と、規則違反の Pod の拒否を確認した |
| 作業用 Pod を実行 | 言語パッケージの更新と依存元からの再解決に成功した |
| v1.36.5 で NetworkPolicy を適用してから新しい Pod を作成 | 最初の通信から制限が効き、数秒間の制限のない時間は観測されなかった |
検証で見つかった点
削除中の作業用 Pod による待機
| 項目 | 内容 |
|---|---|
| 起きたこと | 削除中の作業用 Pod を同時実行数の上限に数え、次の作業が待たされていた |
| 対応 | 削除中の Pod を数えないように修正した |
| 確認結果 | max_concurrent_pods が1でも、削除中の Pod による言語パッケージの項目の待機は起きなかった |
新しい Pod への通信制限の適用時間
| 項目 | 内容 |
|---|---|
| 起きたこと | 検証した K3s の既定の CNI では、新しい Pod に NetworkPolicy が効くまでの数秒間、送信が制限されなかった |
| 対応 | KestreLynx からは制御できない制限として、外向きの通信に記載した |
Pod の metadata.generation による作成の停止
| 項目 | 内容 |
|---|---|
| 起きたこと | Kubernetes 1.34 以降で API サーバーが付ける metadata.generation を許可されない変更と扱い、作業用 Pod を作れなかった |
| 対応 | metadata.generation だけを1以上の整数に限って許可項目に加えた |
| 確認結果 | K3s v1.31.5・v1.34.12・v1.36.5 で、規則違反の Pod は拒否され、言語パッケージの更新と依存元からの再解決に成功した |
未検証・次の作業
${VAR}を含む参照、newTagのない Kustomize の項目、空の Helm のタグは、単体テストでのみ確認した- K3s 以外の Kubernetes ディストリビューションや別の CNI での動作は未検証
- ログの切り替わりを確実に起こす再現方法は未確立
更新履歴
2026年10月23日
- Kubernetes での修正 PR の実装と実環境での検証結果
- GitOps による反映と作業用 Pod の安全策の確認
- 削除中の Pod による待機の修正と通信制限の適用時間の記録
- 未検証の範囲と次の作業の整理
- 非公開の ghcr.io のイメージによる PR の置き換えとログの上限の確認
- 新しい版の K3s での検証と
metadata.generationによる作業用 Pod の作成停止の修正
2026年10月20日
- 修正先の対応付けと版の照合
- 共有する設定の作用先の明示
- 作業用 Pod の実行方式と安全策
- 実装順と実環境の検証予定
この記事は、KestreLynxの開発記録です。KestreLynxについて