<p><!--META
{
"title": "kubectl --dry-run=client を用いたKubernetesマニフェスト検証",
"primary_category": "クラウド>Kubernetes",
"secondary_categories": ["DevOps","CI/CD"],
"tags": ["kubectl", "dry-run", "Kubernetes", "マニフェスト検証", "CI/CD", "bash", "systemd", "jq"],
"summary": "kubectl --dry-run=client を活用し、KubernetesマニフェストをCI/CDで安全かつ効率的に検証する手順と、bashスクリプト、jq、systemdの利用例を解説します。",
"mermaid": true,
"verify_level": "L0",
"tweet_hint": {"text":"Kubernetesマニフェストの安全な検証には<code>kubectl --dry-run=client</code>が必須。CI/CDへの組み込み方、安全なbashスクリプト、jqによる出力解析、systemdでの自動化を解説。#Kubernetes
#DevOps #CI_CD","hashtags":["#Kubernetes","#DevOps"]},
"link_hints": [
"https://kubernetes.io/docs/reference/kubectl/usage/#dry-run",
"https://jqlang.github.io/jq/manual/",
"https://curl.se/docs/manpage.html",
"https://www.freedesktop.org/software/systemd/man/systemd.service.html"
]
}
-->
本記事は<strong>Geminiの出力をプロンプト工学で整理した業務ドラフト(未検証)</strong>です。</p>
<h1 class="wp-block-heading"><code>kubectl --dry-run=client</code> を用いたKubernetesマニフェスト検証</h1>
<p>Kubernetes環境における安定した運用には、デプロイ前にマニフェストファイルの健全性を確認することが不可欠です。<code>kubectl --dry-run=client</code> は、実際にリソースを作成することなく、クライアントサイドでマニフェストの構文と基本的なスキーマ検証を行うための強力なツールです。本記事では、このコマンドをDevOpsパイプラインに組み込むための実践的な手順、安全なBashスクリプトの書き方、<code>jq</code> や <code>systemd</code> の活用例を解説します。</p>
<h2 class="wp-block-heading">1. 要件と前提</h2>
<p><code>kubectl --dry-run=client</code> は、Kubernetes APIサーバーに接続することなく、ローカルに保持されているOpenAPIスキーマ定義に基づいてマニフェストの構文エラーや基本的なフィールドの検証を行います。これはCI/CDパイプラインの初期段階で、開発者が手元で素早くフィードバックを得るために非常に有用です。</p>
<h3 class="wp-block-heading">1.1. <code>dry-run</code> モードの種類</h3>
<p><code>kubectl apply</code> や <code>kubectl create</code> などのコマンドで利用できる <code>dry-run</code> オプションには、以下の3つのモードがあります。</p>
<ul class="wp-block-list">
<li><p><strong><code>client</code></strong>: APIサーバーと通信せず、ローカルのスキーマに基づき構文と基本的な構造を検証します。Admission Webhookによる検証は行われません。これは本記事の主題です。</p></li>
<li><p><strong><code>server</code></strong>: APIサーバーにリクエストを送信しますが、リソースは永続化されません。Admission Webhookを含む、より本番に近い検証が可能です。ネットワーク接続とRBAC権限が必要です。</p></li>
<li><p><strong><code>none</code></strong>: <code>dry-run</code> を行わず、通常通りリソースを作成・更新します。</p></li>
</ul>
<h3 class="wp-block-heading">1.2. 必要なツールと権限</h3>
<ul class="wp-block-list">
<li><p><strong><code>kubectl</code></strong>: Kubernetesクラスターとの対話に必須です。ローカルマシンまたはCI/CDエージェントにインストールされている必要があります。</p></li>
<li><p><strong><code>jq</code></strong>: <code>kubectl</code> が出力するJSON形式の検証結果を解析するために使用します。</p></li>
<li><p><strong><code>curl</code></strong>: (オプション)検証結果を外部システム(例: Slack、Webhook)に通知する際に使用します。</p></li>
<li><p><strong>実行環境</strong>: Linuxベースのシステム(<code>bash</code>、<code>systemd</code> が利用可能な環境)。</p></li>
<li><p><strong>権限</strong>: <code>kubectl --dry-run=client</code> の実行には、マニフェストファイルへの読み取り権限と、一時ディレクトリへの書き込み権限があれば十分です。Kubernetesクラスターへのアクセス権限(例: <code>$HOME/.kube/config</code>)は必須ではありませんが、<code>kubectl</code> コマンドが利用可能な状態である必要があります。最小権限の原則に基づき、root権限での実行は避けるべきです。</p></li>
</ul>
<h3 class="wp-block-heading">1.3. 利点と制約</h3>
<p><strong>利点</strong>:</p>
<ul class="wp-block-list">
<li><p><strong>高速性</strong>: APIサーバーとの通信がないため、非常に高速に実行できます。</p></li>
<li><p><strong>安全性</strong>: 実際のKubernetesリソースを作成・変更しないため、本番環境への意図しない影響を回避できます。</p></li>
<li><p><strong>開発効率</strong>: CI/CDの早い段階で基本的なエラーを検出し、開発サイクルの短縮に貢献します。</p></li>
</ul>
<p><strong>制約</strong>:</p>
<ul class="wp-block-list">
<li><p><strong>APIサーバーとの乖離</strong>: ローカルのスキーマ情報に基づくため、カスタムリソース定義 (CRD) やAdmission Webhookによる検証は行われません。これらは <code>server</code> モードでの検証が必要です。</p></li>
<li><p><strong>最新性</strong>: <code>kubectl</code> クライアントのバージョンが古い場合、最新のKubernetes APIの変更に対応できない可能性があります。</p></li>
</ul>
<h2 class="wp-block-heading">2. 実装</h2>
<p>ここでは、<code>kubectl --dry-run=client</code> を用いてKubernetesマニフェストを検証するBashスクリプトの実装例を示します。<code>jq</code> を使った結果解析や、安全なスクリプトの書き方を含みます。</p>
<h3 class="wp-block-heading">2.1. 検証対象マニフェストの準備</h3>
<p>まずは検証対象となるKubernetesマニフェストファイル <code>nginx-deployment.yaml</code> を作成します。</p>
<div class="codehilite">
<pre data-enlighter-language="generic"># nginx-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.21.6 # 意図的に古いバージョンを使用
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
type: ClusterIP
</pre>
</div>
<h3 class="wp-block-heading">2.2. 安全なBashスクリプトによるマニフェスト検証</h3>
<p>以下のBashスクリプト <code>validate_manifest.sh</code> は、<code>kubectl --dry-run=client</code> を実行し、その結果を <code>jq</code> で解析します。スクリプトは <code>set -euo pipefail</code> でエラーハンドリングを強化し、<code>trap</code> と <code>mktemp</code> で一時ファイルを安全に扱います。</p>
<div class="codehilite">
<pre data-enlighter-language="generic">#!/usr/bin/env bash
# File: validate_manifest.sh
# 厳格なエラーハンドリングを有効にする
# -e: コマンドが失敗した場合、即座に終了する
# -u: 未定義の変数を参照した場合、エラーとする
# -o pipefail: パイプライン中のコマンドが失敗した場合、パイプライン全体を失敗とする
set -euo pipefail
# 一時ディレクトリの作成と終了時のクリーンアップ
# 前提: mktempコマンドが利用可能であること
# 計算量: O(1)
# メモリ: 非常に小さい
TMP_DIR=$(mktemp -d -t dry-run-XXXXXX)
# スクリプト終了時に一時ディレクトリを削除する
trap 'rm -rf "$TMP_DIR"' EXIT
# マニフェストファイルへのパス
# 入力: マニフェストファイルパス (例: "./nginx-deployment.yaml")
MANIFEST_FILE="${1:-./nginx-deployment.yaml}"
# ファイルが存在するか確認
if [[ ! -f "$MANIFEST_FILE" ]]; then
echo "エラー: マニフェストファイル '$MANIFEST_FILE' が見つかりません。" >&2
exit 1
fi
echo "--- Kubernetesマニフェスト検証開始 ($MANIFEST_FILE) ---"
# kubectl --dry-run=client を実行し、JSON形式で出力を取得
# 出力: JSON形式のkubectl実行結果
# 前提: kubectlコマンドが利用可能であること
# 計算量: O(N) (Nはマニフェストファイルのサイズに比例)
# メモリ: マニフェストファイルのサイズに応じて変動
if kubectl apply --dry-run=client -f "$MANIFEST_FILE" -o json > "$TMP_DIR/dry-run-output.json"; then
echo "INFO: クライアントサイドでの検証は成功しました。"
# jq を用いて検証結果からリソースの種類と名前を抽出
# 入力: JSONファイル
# 出力: フィルタリングされたJSONまたはテキスト
# 前提: jqコマンドが利用可能であること
# 計算量: O(M) (MはJSON出力のサイズに比例)
# メモリ: JSON出力のサイズに応じて変動
echo "--- 検証されたリソース ---"
jq -r '.items[] | {kind: .kind, name: .metadata.name}' "$TMP_DIR/dry-run-output.json"
echo "--------------------------"
# 検証成功のWebhook通知(例)
# 入力: Webhook URL、JSONペイロード
# 出力: curlの実行結果 (標準出力/エラー)
# 前提: curlコマンドが利用可能であること、ネットワーク接続
# 計算量: O(ネットワークI/O)
# メモリ: 小さい
# 実際にはここに設定ファイルなどからURLを取得するロジックが入る
WEBHOOK_URL="https://example.com/webhook/success"
NOTIFICATION_PAYLOAD=$(jq -n --arg file "$MANIFEST_FILE" '{"status": "validation_success", "message": "マニフェストのクライアントサイド検証が成功しました。", "file": $file}')
if curl --fail --retry 5 --retry-delay 3 --max-time 10 -H "Content-Type: application/json" -d "$NOTIFICATION_PAYLOAD" "$WEBHOOK_URL" &> /dev/null; then
echo "INFO: 検証成功のWebhook通知を送信しました。"
else
echo "警告: 検証成功のWebhook通知の送信に失敗しました。" >&2
fi
exit 0
else
# kubectl apply --dry-run=client が失敗した場合
echo "エラー: クライアントサイドでの検証が失敗しました。" >&2
# エラーメッセージを標準エラー出力に直接表示(kubectlの出力は通常すでにエラー)
cat "$TMP_DIR/dry-run-output.json" >&2 || true # ファイルが存在しない場合もエラーにしない
# 検証失敗のWebhook通知(例)
WEBHOOK_URL="https://example.com/webhook/failure"
ERROR_MESSAGE=$(cat "$TMP_DIR/dry-run-output.json" | jq -r .message 2>/dev/null || echo "Unknown kubectl error")
NOTIFICATION_PAYLOAD=$(jq -n --arg file "$MANIFEST_FILE" --arg error "$ERROR_MESSAGE" '{"status": "validation_failure", "message": "マニフェストのクライアントサイド検証が失敗しました。", "file": $file, "error": $error}')
if curl --fail --retry 5 --retry-delay 3 --max-time 10 -H "Content-Type: application/json" -d "$NOTIFICATION_PAYLOAD" "$WEBHOOK_URL" &> /dev/null; then
echo "INFO: 検証失敗のWebhook通知を送信しました。"
else
echo "警告: 検証失敗のWebhook通知の送信に失敗しました。" >&2
fi
exit 1
fi
</pre>
</div>
<h3 class="wp-block-heading">2.3. CI/CDフローにおける検証位置(Mermaid)</h3>
<p><code>kubectl --dry-run=client</code> はCI/CDパイプラインの非常に早い段階で組み込むべきです。これにより、開発者は迅速にフィードバックを得られ、後続の重いステージ(例: クラスタへのデプロイ試行)に進む前に基本的なエラーを排除できます。</p>
<div class="wp-block-merpress-mermaidjs diagram-source-mermaid"><pre class="mermaid">
graph TD
A["開発者がマニフェストを作成・更新"] --> B{"Gitリポジトリにプッシュ"};
B --|コミット/プッシュ| --> C("CIパイプライン開始");
C --> D["マニフェストをチェックアウト"];
D --> E{"kubectl --dry-run=client で検証"};
E --|検証成功| --> F["jqで出力解析"];
E --|検証失敗| --> G["検証エラー: 開発者に通知"];
F --> H["追加のツールで検証 (例:OPA/KubeLinter)"];
H --|追加検証成功| --> I["Artifactリポジトリに保存"];
H --|追加検証失敗| --> J["追加検証エラー: 開発者に通知"];
I --> K["CDパイプラインへ"];
</pre></div>
<h2 class="wp-block-heading">3. 検証</h2>
<p>上記のBashスクリプト <code>validate_manifest.sh</code> を用いて、正常なケースと異常なケースのマニフェストを検証します。</p>
<h3 class="wp-block-heading">3.1. 正常なマニフェストの検証</h3>
<p><code>nginx-deployment.yaml</code> を使用してスクリプトを実行します。</p>
<div class="codehilite">
<pre data-enlighter-language="generic">bash validate_manifest.sh ./nginx-deployment.yaml
</pre>
</div>
<p><strong>期待される出力例</strong>:</p>
<pre data-enlighter-language="generic">--- Kubernetesマニフェスト検証開始 (./nginx-deployment.yaml) ---
INFO: クライアントサイドでの検証は成功しました。
--- 検証されたリソース ---
{
"kind": "Deployment",
"name": "nginx-deployment"
}
{
"kind": "Service",
"name": "nginx-service"
}
--------------------------
INFO: 検証成功のWebhook通知を送信しました。
</pre>
<p>(<code>curl</code> の実行結果は <code>&> /dev/null</code> で標準出力/エラーに出力されない設定のため、成功メッセージのみ表示されます。)</p>
<h3 class="wp-block-heading">3.2. 異常なマニフェストの検証</h3>
<p>意図的にエラーを挿入したマニフェスト <code>bad-deployment.yaml</code> を作成します。ここでは <code>apiVersion</code> を不正な値に設定します。</p>
<div class="codehilite">
<pre data-enlighter-language="generic"># bad-deployment.yaml
apiVersion: apps/v1beta1 # 不正なAPIバージョン
kind: Deployment
metadata:
name: bad-nginx-deployment
spec:
replicas: 1
selector:
matchLabels:
app: bad-nginx
template:
metadata:
labels:
app: bad-nginx
spec:
containers:
- name: bad-nginx
image: nginx:latest
ports:
- containerPort: 80
</pre>
</div>
<p><code>bad-deployment.yaml</code> を使用してスクリプトを実行します。</p>
<div class="codehilite">
<pre data-enlighter-language="generic">bash validate_manifest.sh ./bad-deployment.yaml
</pre>
</div>
<p><strong>期待される出力例</strong>:</p>
<pre data-enlighter-language="generic">--- Kubernetesマニフェスト検証開始 (./bad-deployment.yaml) ---
エラー: クライアントサイドでの検証が失敗しました。
Error: validation for 'bad-deployment.yaml' failed, no matches for kind "Deployment" in version "apps/v1beta1"
INFO: 検証失敗のWebhook通知を送信しました。
</pre>
<p>このように、<code>--dry-run=client</code> はAPIサーバーに到達する前に基本的なスキーマエラーを検出できることが確認できます。</p>
<h2 class="wp-block-heading">4. 運用</h2>
<p><code>kubectl --dry-run=client</code> をCI/CDパイプラインに統合するだけでなく、<code>systemd</code> を用いて定期的にマニフェストの健全性をチェックする自動化も可能です。</p>
<h3 class="wp-block-heading">4.1. CI/CDパイプラインへの統合</h3>
<p>GitHub Actions、GitLab CI/CD、JenkinsなどのCI/CDツールにおいて、Gitリポジトリへのプッシュやプルリクエストの作成時に <code>validate_manifest.sh</code> スクリプトを実行するように設定します。これにより、マージ前にマニフェストの構文エラーが確実に検出されます。</p>
<p><strong>例: GitLab CI/CD の <code>.gitlab-ci.yml</code> スニペット</strong></p>
<div class="codehilite">
<pre data-enlighter-language="generic">stages:
- validate
validate_kubernetes_manifests:
stage: validate
image: alpine/git:latest # kubectlは別途インストールまたは指定
before_script:
- apk add --no-cache curl jq # Alpine Linuxの場合
- curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
- chmod +x kubectl
- mv kubectl /usr/local/bin/
script:
- bash validate_manifest.sh ./nginx-deployment.yaml
- bash validate_manifest.sh ./other-manifests/*.yaml # 複数のマニフェストを検証する場合
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
</pre>
</div>
<h3 class="wp-block-heading">4.2. <code>systemd</code> による定期的な自動検証</h3>
<p><code>systemd</code> を利用して、例えば毎日特定の時間にマニフェストリポジトリをチェックアウトし、<code>--dry-run=client</code> で検証するタスクを設定できます。これは、依存するツールやKubernetesのバージョンアップに伴うスキーマ変更などを早期に発見するのに役立ちます。</p>
<h4 class="wp-block-heading">4.2.1. <code>systemd</code> サービスユニットファイル (<code>manifest-validation.service</code>)</h4>
<p>マニフェスト検証スクリプトを実行するサービスを定義します。</p>
<div class="codehilite">
<pre data-enlighter-language="generic"># /etc/systemd/system/manifest-validation.service
[Unit]
Description=Kubernetes Manifest Client-Side Validation Service
Documentation=https://kubernetes.io/docs/reference/kubectl/usage/#dry-run
Requires=network-online.target # curlによるWebhook通知のためにネットワークが必要
After=network-online.target
[Service]
Type=oneshot
User=validation-user # 最小権限の専用ユーザーで実行
Group=validation-group # 最小権限の専用グループで実行
WorkingDirectory=/opt/manifest-repo # マニフェストリポジトリのパス
ExecStart=/opt/scripts/validate_manifest.sh /opt/manifest-repo/nginx-deployment.yaml
# 環境変数KUBECONFIGを設定する場合 (dry-run=serverなどを用いる場合)
# Environment="KUBECONFIG=/home/validation-user/.kube/config"
ProtectSystem=full
ProtectHome=true
PrivateTmp=true
NoNewPrivileges=true
ReadWritePaths=/opt/manifest-repo /tmp
# 実行ユーザーの作成例 (root権限で実行):
# sudo useradd -r -s /usr/sbin/nologin -d /opt/manifest-repo validation-user
# sudo chown validation-user:validation-group /opt/manifest-repo
</pre>
</div>
<p><strong>root権限の扱いと権限分離の注意点</strong>:
上記の例では <code>User=validation-user</code> と <code>Group=validation-group</code> を指定し、サービスを特権のないユーザーで実行しています。<code>kubectl --dry-run=client</code> は基本的にAPIサーバーへのアクセスを必要としないため、<code>KUBECONFIG</code> は不要な場合が多いです。マニフェストファイルへの読み取り権限と、スクリプトが作成する一時ファイル(<code>/tmp</code> 以下に作成されますが、<code>PrivateTmp=true</code> によりサービス専用の一時空間に限定されます)への書き込み権限のみを持つように、<code>validation-user</code> の権限を最小限に抑えることが重要です。</p>
<h4 class="wp-block-heading">4.2.2. <code>systemd</code> タイマーユニットファイル (<code>manifest-validation.timer</code>)</h4>
<p>サービスを定期的に実行するためのタイマーを定義します。ここでは毎日午前3時に実行するように設定します。</p>
<div class="codehilite">
<pre data-enlighter-language="generic"># /etc/systemd/system/manifest-validation.timer
[Unit]
Description=Run Kubernetes Manifest Client-Side Validation daily
[Timer]
OnCalendar=daily
# Persistent=true # サービスが停止中に発生したイベントも次回の起動時に実行
[Install]
WantedBy=timers.target
</pre>
</div>
<h4 class="wp-block-heading">4.2.3. <code>systemd</code> タイマーの起動とログ確認</h4>
<p>これらのファイルを配置したら、<code>systemd</code> にリロードを指示し、タイマーを有効化・起動します。</p>
<div class="codehilite">
<pre data-enlighter-language="generic"># systemd設定をリロード
sudo systemctl daemon-reload
# タイマーを有効化し、起動
sudo systemctl enable manifest-validation.timer
sudo systemctl start manifest-validation.timer
# タイマーの状態を確認
sudo systemctl list-timers | grep manifest-validation
# サービスの実行ログを確認 (実行後に確認可能)
sudo journalctl -u manifest-validation.service
</pre>
</div>
<h2 class="wp-block-heading">5. トラブルシュート</h2>
<h3 class="wp-block-heading">5.1. <code>kubectl --dry-run=client</code> が検出できないエラー</h3>
<ul class="wp-block-list">
<li><p><strong>CRDやAdmission Webhookによる検証エラー</strong>: <code>client</code> モードはローカルのOpenAPIスキーマに基づいて検証するため、カスタムリソースの定義や、Mutating/Validating Admission Webhookによる動的な検証は行いません。これらを検出するには <code>kubectl apply --dry-run=server</code> を使用する必要があります。</p></li>
<li><p><strong>RBAC権限不足</strong>: <code>client</code> モードでは関係ありませんが、<code>server</code> モードで <code>dry-run</code> を行う場合、APIサーバーに対する適切なRBAC権限が必要です。</p></li>
</ul>
<h3 class="wp-block-heading">5.2. <code>kubectl</code> のバージョン差異による問題</h3>
<p><code>kubectl</code> クライアントのバージョンが、ターゲットKubernetesクラスターのAPIバージョンと大きく異なる場合、<code>--dry-run=client</code> のスキーマ定義が古く、存在しないフィールドを許可したり、新しい必須フィールドの欠落を見逃したりする可能性があります。
常に最新の <code>kubectl</code> クライアントを使用し、可能であればクラスターと同じマイナーバージョンに合わせることが推奨されます。</p>
<h3 class="wp-block-heading">5.3. スクリプトの実行権限エラー</h3>
<p><code>validate_manifest.sh</code> スクリプトの実行時に「Permission denied」などのエラーが発生する場合、以下の点を確認してください。</p>
<ul class="wp-block-list">
<li><p>スクリプトファイルに実行権限があるか: <code>chmod +x validate_manifest.sh</code></p></li>
<li><p>マニフェストファイルに読み取り権限があるか。</p></li>
<li><p><code>jq</code>, <code>curl</code>, <code>kubectl</code>, <code>mktemp</code> コマンドが <code>PATH</code> 環境変数に含まれており、実行可能であるか。</p></li>
</ul>
<h2 class="wp-block-heading">6. まとめ</h2>
<p><code>kubectl --dry-run=client</code> は、Kubernetesマニフェストの基本的な健全性を効率的かつ安全に検証するための不可欠なツールです。本記事で解説したように、安全なBashスクリプト、<code>jq</code> を用いた出力解析、そしてCI/CDパイプラインや <code>systemd</code> を活用した自動化により、開発初期段階でのエラー検出を強化し、Kubernetes環境の信頼性向上に大きく貢献します。
常に最小権限の原則に従い、<code>systemd</code> サービスなどで実行する際には専用ユーザーを設定することで、セキュリティリスクを最小限に抑えることを忘れないでください。より高度な検証が必要な場合は、<code>--dry-run=server</code> やOPA (Open Policy Agent) などのポリシーエンジンとの組み合わせも検討しましょう。</p>
<!--META
{
"title": "kubectl --dry-run=client を用いたKubernetesマニフェスト検証",
"primary_category": "クラウド>Kubernetes",
"secondary_categories": ["DevOps","CI/CD"],
"tags": ["kubectl", "dry-run", "Kubernetes", "マニフェスト検証", "CI/CD", "bash", "systemd", "jq"],
"summary": "kubectl --dry-run=client を活用し、KubernetesマニフェストをCI/CDで安全かつ効率的に検証する手順と、bashスクリプト、jq、systemdの利用例を解説します。",
"mermaid": true,
"verify_level": "L0",
"tweet_hint": {"text":"Kubernetesマニフェストの安全な検証にはkubectl --dry-run=clientが必須。CI/CDへの組み込み方、安全なbashスクリプト、jqによる出力解析、systemdでの自動化を解説。#Kubernetes #DevOps #CI_CD","hashtags":["#Kubernetes","#DevOps"]},
"link_hints": [
"https://kubernetes.io/docs/reference/kubectl/usage/#dry-run",
"https://jqlang.github.io/jq/manual/",
"https://curl.se/docs/manpage.html",
"https://www.freedesktop.org/software/systemd/man/systemd.service.html"
]
}
-->
本記事はGeminiの出力をプロンプト工学で整理した業務ドラフト(未検証)です。
kubectl --dry-run=client を用いたKubernetesマニフェスト検証
Kubernetes環境における安定した運用には、デプロイ前にマニフェストファイルの健全性を確認することが不可欠です。kubectl --dry-run=client は、実際にリソースを作成することなく、クライアントサイドでマニフェストの構文と基本的なスキーマ検証を行うための強力なツールです。本記事では、このコマンドをDevOpsパイプラインに組み込むための実践的な手順、安全なBashスクリプトの書き方、jq や systemd の活用例を解説します。
1. 要件と前提
kubectl --dry-run=client は、Kubernetes APIサーバーに接続することなく、ローカルに保持されているOpenAPIスキーマ定義に基づいてマニフェストの構文エラーや基本的なフィールドの検証を行います。これはCI/CDパイプラインの初期段階で、開発者が手元で素早くフィードバックを得るために非常に有用です。
1.1. dry-run モードの種類
kubectl apply や kubectl create などのコマンドで利用できる dry-run オプションには、以下の3つのモードがあります。
client: APIサーバーと通信せず、ローカルのスキーマに基づき構文と基本的な構造を検証します。Admission Webhookによる検証は行われません。これは本記事の主題です。
server: APIサーバーにリクエストを送信しますが、リソースは永続化されません。Admission Webhookを含む、より本番に近い検証が可能です。ネットワーク接続とRBAC権限が必要です。
none: dry-run を行わず、通常通りリソースを作成・更新します。
1.2. 必要なツールと権限
kubectl: Kubernetesクラスターとの対話に必須です。ローカルマシンまたはCI/CDエージェントにインストールされている必要があります。
jq: kubectl が出力するJSON形式の検証結果を解析するために使用します。
curl: (オプション)検証結果を外部システム(例: Slack、Webhook)に通知する際に使用します。
実行環境: Linuxベースのシステム(bash、systemd が利用可能な環境)。
権限: kubectl --dry-run=client の実行には、マニフェストファイルへの読み取り権限と、一時ディレクトリへの書き込み権限があれば十分です。Kubernetesクラスターへのアクセス権限(例: $HOME/.kube/config)は必須ではありませんが、kubectl コマンドが利用可能な状態である必要があります。最小権限の原則に基づき、root権限での実行は避けるべきです。
1.3. 利点と制約
利点:
高速性: APIサーバーとの通信がないため、非常に高速に実行できます。
安全性: 実際のKubernetesリソースを作成・変更しないため、本番環境への意図しない影響を回避できます。
開発効率: CI/CDの早い段階で基本的なエラーを検出し、開発サイクルの短縮に貢献します。
制約:
2. 実装
ここでは、kubectl --dry-run=client を用いてKubernetesマニフェストを検証するBashスクリプトの実装例を示します。jq を使った結果解析や、安全なスクリプトの書き方を含みます。
2.1. 検証対象マニフェストの準備
まずは検証対象となるKubernetesマニフェストファイル nginx-deployment.yaml を作成します。
# nginx-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.21.6 # 意図的に古いバージョンを使用
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
type: ClusterIP
2.2. 安全なBashスクリプトによるマニフェスト検証
以下のBashスクリプト validate_manifest.sh は、kubectl --dry-run=client を実行し、その結果を jq で解析します。スクリプトは set -euo pipefail でエラーハンドリングを強化し、trap と mktemp で一時ファイルを安全に扱います。
#!/usr/bin/env bash
# File: validate_manifest.sh
# 厳格なエラーハンドリングを有効にする
# -e: コマンドが失敗した場合、即座に終了する
# -u: 未定義の変数を参照した場合、エラーとする
# -o pipefail: パイプライン中のコマンドが失敗した場合、パイプライン全体を失敗とする
set -euo pipefail
# 一時ディレクトリの作成と終了時のクリーンアップ
# 前提: mktempコマンドが利用可能であること
# 計算量: O(1)
# メモリ: 非常に小さい
TMP_DIR=$(mktemp -d -t dry-run-XXXXXX)
# スクリプト終了時に一時ディレクトリを削除する
trap 'rm -rf "$TMP_DIR"' EXIT
# マニフェストファイルへのパス
# 入力: マニフェストファイルパス (例: "./nginx-deployment.yaml")
MANIFEST_FILE="${1:-./nginx-deployment.yaml}"
# ファイルが存在するか確認
if [[ ! -f "$MANIFEST_FILE" ]]; then
echo "エラー: マニフェストファイル '$MANIFEST_FILE' が見つかりません。" >&2
exit 1
fi
echo "--- Kubernetesマニフェスト検証開始 ($MANIFEST_FILE) ---"
# kubectl --dry-run=client を実行し、JSON形式で出力を取得
# 出力: JSON形式のkubectl実行結果
# 前提: kubectlコマンドが利用可能であること
# 計算量: O(N) (Nはマニフェストファイルのサイズに比例)
# メモリ: マニフェストファイルのサイズに応じて変動
if kubectl apply --dry-run=client -f "$MANIFEST_FILE" -o json > "$TMP_DIR/dry-run-output.json"; then
echo "INFO: クライアントサイドでの検証は成功しました。"
# jq を用いて検証結果からリソースの種類と名前を抽出
# 入力: JSONファイル
# 出力: フィルタリングされたJSONまたはテキスト
# 前提: jqコマンドが利用可能であること
# 計算量: O(M) (MはJSON出力のサイズに比例)
# メモリ: JSON出力のサイズに応じて変動
echo "--- 検証されたリソース ---"
jq -r '.items[] | {kind: .kind, name: .metadata.name}' "$TMP_DIR/dry-run-output.json"
echo "--------------------------"
# 検証成功のWebhook通知(例)
# 入力: Webhook URL、JSONペイロード
# 出力: curlの実行結果 (標準出力/エラー)
# 前提: curlコマンドが利用可能であること、ネットワーク接続
# 計算量: O(ネットワークI/O)
# メモリ: 小さい
# 実際にはここに設定ファイルなどからURLを取得するロジックが入る
WEBHOOK_URL="https://example.com/webhook/success"
NOTIFICATION_PAYLOAD=$(jq -n --arg file "$MANIFEST_FILE" '{"status": "validation_success", "message": "マニフェストのクライアントサイド検証が成功しました。", "file": $file}')
if curl --fail --retry 5 --retry-delay 3 --max-time 10 -H "Content-Type: application/json" -d "$NOTIFICATION_PAYLOAD" "$WEBHOOK_URL" &> /dev/null; then
echo "INFO: 検証成功のWebhook通知を送信しました。"
else
echo "警告: 検証成功のWebhook通知の送信に失敗しました。" >&2
fi
exit 0
else
# kubectl apply --dry-run=client が失敗した場合
echo "エラー: クライアントサイドでの検証が失敗しました。" >&2
# エラーメッセージを標準エラー出力に直接表示(kubectlの出力は通常すでにエラー)
cat "$TMP_DIR/dry-run-output.json" >&2 || true # ファイルが存在しない場合もエラーにしない
# 検証失敗のWebhook通知(例)
WEBHOOK_URL="https://example.com/webhook/failure"
ERROR_MESSAGE=$(cat "$TMP_DIR/dry-run-output.json" | jq -r .message 2>/dev/null || echo "Unknown kubectl error")
NOTIFICATION_PAYLOAD=$(jq -n --arg file "$MANIFEST_FILE" --arg error "$ERROR_MESSAGE" '{"status": "validation_failure", "message": "マニフェストのクライアントサイド検証が失敗しました。", "file": $file, "error": $error}')
if curl --fail --retry 5 --retry-delay 3 --max-time 10 -H "Content-Type: application/json" -d "$NOTIFICATION_PAYLOAD" "$WEBHOOK_URL" &> /dev/null; then
echo "INFO: 検証失敗のWebhook通知を送信しました。"
else
echo "警告: 検証失敗のWebhook通知の送信に失敗しました。" >&2
fi
exit 1
fi
2.3. CI/CDフローにおける検証位置(Mermaid)
kubectl --dry-run=client はCI/CDパイプラインの非常に早い段階で組み込むべきです。これにより、開発者は迅速にフィードバックを得られ、後続の重いステージ(例: クラスタへのデプロイ試行)に進む前に基本的なエラーを排除できます。
graph TD
A["開発者がマニフェストを作成・更新"] --> B{"Gitリポジトリにプッシュ"};
B --|コミット/プッシュ| --> C("CIパイプライン開始");
C --> D["マニフェストをチェックアウト"];
D --> E{"kubectl --dry-run=client で検証"};
E --|検証成功| --> F["jqで出力解析"];
E --|検証失敗| --> G["検証エラー: 開発者に通知"];
F --> H["追加のツールで検証 (例:OPA/KubeLinter)"];
H --|追加検証成功| --> I["Artifactリポジトリに保存"];
H --|追加検証失敗| --> J["追加検証エラー: 開発者に通知"];
I --> K["CDパイプラインへ"];
3. 検証
上記のBashスクリプト validate_manifest.sh を用いて、正常なケースと異常なケースのマニフェストを検証します。
3.1. 正常なマニフェストの検証
nginx-deployment.yaml を使用してスクリプトを実行します。
bash validate_manifest.sh ./nginx-deployment.yaml
期待される出力例:
--- Kubernetesマニフェスト検証開始 (./nginx-deployment.yaml) ---
INFO: クライアントサイドでの検証は成功しました。
--- 検証されたリソース ---
{
"kind": "Deployment",
"name": "nginx-deployment"
}
{
"kind": "Service",
"name": "nginx-service"
}
--------------------------
INFO: 検証成功のWebhook通知を送信しました。
(curl の実行結果は &> /dev/null で標準出力/エラーに出力されない設定のため、成功メッセージのみ表示されます。)
3.2. 異常なマニフェストの検証
意図的にエラーを挿入したマニフェスト bad-deployment.yaml を作成します。ここでは apiVersion を不正な値に設定します。
# bad-deployment.yaml
apiVersion: apps/v1beta1 # 不正なAPIバージョン
kind: Deployment
metadata:
name: bad-nginx-deployment
spec:
replicas: 1
selector:
matchLabels:
app: bad-nginx
template:
metadata:
labels:
app: bad-nginx
spec:
containers:
- name: bad-nginx
image: nginx:latest
ports:
- containerPort: 80
bad-deployment.yaml を使用してスクリプトを実行します。
bash validate_manifest.sh ./bad-deployment.yaml
期待される出力例:
--- Kubernetesマニフェスト検証開始 (./bad-deployment.yaml) ---
エラー: クライアントサイドでの検証が失敗しました。
Error: validation for 'bad-deployment.yaml' failed, no matches for kind "Deployment" in version "apps/v1beta1"
INFO: 検証失敗のWebhook通知を送信しました。
このように、--dry-run=client はAPIサーバーに到達する前に基本的なスキーマエラーを検出できることが確認できます。
4. 運用
kubectl --dry-run=client をCI/CDパイプラインに統合するだけでなく、systemd を用いて定期的にマニフェストの健全性をチェックする自動化も可能です。
4.1. CI/CDパイプラインへの統合
GitHub Actions、GitLab CI/CD、JenkinsなどのCI/CDツールにおいて、Gitリポジトリへのプッシュやプルリクエストの作成時に validate_manifest.sh スクリプトを実行するように設定します。これにより、マージ前にマニフェストの構文エラーが確実に検出されます。
例: GitLab CI/CD の .gitlab-ci.yml スニペット
stages:
- validate
validate_kubernetes_manifests:
stage: validate
image: alpine/git:latest # kubectlは別途インストールまたは指定
before_script:
- apk add --no-cache curl jq # Alpine Linuxの場合
- curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
- chmod +x kubectl
- mv kubectl /usr/local/bin/
script:
- bash validate_manifest.sh ./nginx-deployment.yaml
- bash validate_manifest.sh ./other-manifests/*.yaml # 複数のマニフェストを検証する場合
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
4.2. systemd による定期的な自動検証
systemd を利用して、例えば毎日特定の時間にマニフェストリポジトリをチェックアウトし、--dry-run=client で検証するタスクを設定できます。これは、依存するツールやKubernetesのバージョンアップに伴うスキーマ変更などを早期に発見するのに役立ちます。
4.2.1. systemd サービスユニットファイル (manifest-validation.service)
マニフェスト検証スクリプトを実行するサービスを定義します。
# /etc/systemd/system/manifest-validation.service
[Unit]
Description=Kubernetes Manifest Client-Side Validation Service
Documentation=https://kubernetes.io/docs/reference/kubectl/usage/#dry-run
Requires=network-online.target # curlによるWebhook通知のためにネットワークが必要
After=network-online.target
[Service]
Type=oneshot
User=validation-user # 最小権限の専用ユーザーで実行
Group=validation-group # 最小権限の専用グループで実行
WorkingDirectory=/opt/manifest-repo # マニフェストリポジトリのパス
ExecStart=/opt/scripts/validate_manifest.sh /opt/manifest-repo/nginx-deployment.yaml
# 環境変数KUBECONFIGを設定する場合 (dry-run=serverなどを用いる場合)
# Environment="KUBECONFIG=/home/validation-user/.kube/config"
ProtectSystem=full
ProtectHome=true
PrivateTmp=true
NoNewPrivileges=true
ReadWritePaths=/opt/manifest-repo /tmp
# 実行ユーザーの作成例 (root権限で実行):
# sudo useradd -r -s /usr/sbin/nologin -d /opt/manifest-repo validation-user
# sudo chown validation-user:validation-group /opt/manifest-repo
root権限の扱いと権限分離の注意点:
上記の例では User=validation-user と Group=validation-group を指定し、サービスを特権のないユーザーで実行しています。kubectl --dry-run=client は基本的にAPIサーバーへのアクセスを必要としないため、KUBECONFIG は不要な場合が多いです。マニフェストファイルへの読み取り権限と、スクリプトが作成する一時ファイル(/tmp 以下に作成されますが、PrivateTmp=true によりサービス専用の一時空間に限定されます)への書き込み権限のみを持つように、validation-user の権限を最小限に抑えることが重要です。
4.2.2. systemd タイマーユニットファイル (manifest-validation.timer)
サービスを定期的に実行するためのタイマーを定義します。ここでは毎日午前3時に実行するように設定します。
# /etc/systemd/system/manifest-validation.timer
[Unit]
Description=Run Kubernetes Manifest Client-Side Validation daily
[Timer]
OnCalendar=daily
# Persistent=true # サービスが停止中に発生したイベントも次回の起動時に実行
[Install]
WantedBy=timers.target
4.2.3. systemd タイマーの起動とログ確認
これらのファイルを配置したら、systemd にリロードを指示し、タイマーを有効化・起動します。
# systemd設定をリロード
sudo systemctl daemon-reload
# タイマーを有効化し、起動
sudo systemctl enable manifest-validation.timer
sudo systemctl start manifest-validation.timer
# タイマーの状態を確認
sudo systemctl list-timers | grep manifest-validation
# サービスの実行ログを確認 (実行後に確認可能)
sudo journalctl -u manifest-validation.service
5. トラブルシュート
5.1. kubectl --dry-run=client が検出できないエラー
CRDやAdmission Webhookによる検証エラー: client モードはローカルのOpenAPIスキーマに基づいて検証するため、カスタムリソースの定義や、Mutating/Validating Admission Webhookによる動的な検証は行いません。これらを検出するには kubectl apply --dry-run=server を使用する必要があります。
RBAC権限不足: client モードでは関係ありませんが、server モードで dry-run を行う場合、APIサーバーに対する適切なRBAC権限が必要です。
5.2. kubectl のバージョン差異による問題
kubectl クライアントのバージョンが、ターゲットKubernetesクラスターのAPIバージョンと大きく異なる場合、--dry-run=client のスキーマ定義が古く、存在しないフィールドを許可したり、新しい必須フィールドの欠落を見逃したりする可能性があります。
常に最新の kubectl クライアントを使用し、可能であればクラスターと同じマイナーバージョンに合わせることが推奨されます。
5.3. スクリプトの実行権限エラー
validate_manifest.sh スクリプトの実行時に「Permission denied」などのエラーが発生する場合、以下の点を確認してください。
スクリプトファイルに実行権限があるか: chmod +x validate_manifest.sh
マニフェストファイルに読み取り権限があるか。
jq, curl, kubectl, mktemp コマンドが PATH 環境変数に含まれており、実行可能であるか。
6. まとめ
kubectl --dry-run=client は、Kubernetesマニフェストの基本的な健全性を効率的かつ安全に検証するための不可欠なツールです。本記事で解説したように、安全なBashスクリプト、jq を用いた出力解析、そしてCI/CDパイプラインや systemd を活用した自動化により、開発初期段階でのエラー検出を強化し、Kubernetes環境の信頼性向上に大きく貢献します。
常に最小権限の原則に従い、systemd サービスなどで実行する際には専用ユーザーを設定することで、セキュリティリスクを最小限に抑えることを忘れないでください。より高度な検証が必要な場合は、--dry-run=server やOPA (Open Policy Agent) などのポリシーエンジンとの組み合わせも検討しましょう。
コメント