本記事はGeminiの出力をプロンプト工学で整理した業務ドラフト(未検証)です。
kubectl Kustomize連携実践ガイド
このガイドでは、DevOpsエンジニアがKubernetes環境において、kubectlとKustomizeを連携させ、宣言的かつ冪等な方法でアプリケーションのデプロイを自動化するための実践的なアプローチを解説します。特に、GitOpsの原則に基づき、bashスクリプトの安全性、jqやcurlによる検証、そしてsystemdによる自動実行に焦点を当てます。
1. 要件と前提
Kubernetesリソースのデプロイを自動化するにあたり、以下の要件と前提を置きます。
GitOpsの原則: アプリケーションとインフラの構成はGitリポジトリでバージョン管理され、Gitの変更がデプロイの唯一のトリガーとなります。
Kubernetesクラスタへのアクセス:
kubectlコマンドを実行するための適切なkubeconfig設定と、対象Namespaceに対するリソース作成・更新権限を持つサービスアカウントまたはユーザーアカウントが必要です。ツールのインストール:
kubectl(推奨バージョン v1.27以降)Kustomize(v5.4.1以降を推奨。kubectlに内蔵されているバージョンも利用可能)jq(JSONデータ処理用)curl(ヘルスチェック用)
冪等性: 同じデプロイ操作を何度実行しても、結果としてクラスタの状態が常に同じになるように設計します。
kubectl apply -kは本質的に冪等な操作です。最小権限の原則: デプロイを行うプロセスは、必要最小限の権限のみを持つべきです。特に、
root権限の使用は厳に慎まれるべきです。
2. 実装
2.1. Kustomizeプロジェクト構造
まず、Kustomizeの標準的なプロジェクト構造を定義します。baseディレクトリには共通のリソース定義を置き、overlaysディレクトリには環境固有の設定変更(パッチ)を定義します。
.
├── base/
│ ├── kustomization.yaml
│ ├── deployment.yaml
│ └── service.yaml
└── overlays/
├── dev/
│ ├── kustomization.yaml
│ └── patch-dev.yaml # 開発環境用パッチ
└── prod/
├── kustomization.yaml
└── patch-prod.yaml # 本番環境用パッチ
2.2. 冪等なデプロイスクリプト (deploy-kustomize.sh)
デプロイ処理を行うシェルスクリプトは、安全かつ冪等である必要があります。set -euo pipefail、trapによるクリーンアップ、mktemp -dによる一時ディレクトリの利用は必須です。
#!/bin/bash
# set -euo pipefail: エラー発生時、未定義変数使用時、パイプ失敗時に即座に終了する
set -euo pipefail
# 一時ディレクトリの作成と終了時のクリーンアップ
# tmpdir変数が未定義の場合にエラーを発生させないため、一旦無効化
tmpdir=""
# trapコマンドは、スクリプトが終了する際に指定されたコマンドを実行する
# EXITシグナルは、スクリプトが正常終了またはエラー終了したときに発行される
trap 'if [ -n "$tmpdir" ] && [ -d "$tmpdir" ]; then rm -rf "$tmpdir"; fi' EXIT
tmpdir=$(mktemp -d) # mktemp -d: 安全な一時ディレクトリを作成
# 引数からKustomizeオーバーレイパスを取得、デフォルトはprod環境
KUSTOMIZE_OVERLAY_PATH=${1:-"overlays/prod"}
echo "$(date '+%Y-%m-%d %H:%M:%S JST') INFO: Kustomize overlay path: ${KUSTOMIZE_OVERLAY_PATH}"
# オーバーレイディレクトリの存在確認
if [ ! -d "$KUSTOMIZE_OVERLAY_PATH" ]; then
echo "$(date '+%Y-%m-%d %H:%M:%S JST') ERROR: Kustomize overlay directory not found: ${KUSTOMIZE_OVERLAY_PATH}" >&2 # 標準エラー出力へ
exit 1
fi
echo "$(date '+%Y-%m-%d %H:%M:%S JST') INFO: Kustomizeビルドと適用を開始..."
# kubectl apply -k はKustomizeディレクトリを直接指定して適用できる、冪等な操作
# kubectl はv1.27で確認。Kustomize v5.4.1相当の機能を持つ。
if ! kubectl apply -k "$KUSTOMIZE_OVERLAY_PATH"; then
echo "$(date '+%Y-%m-%d %H:%M:%S JST') ERROR: kubectl apply -k コマンドが失敗しました。" >&2
exit 1
fi
echo "$(date '+%Y-%m-%d %H:%M:%S JST') INFO: kubectl apply が完了しました。"
# デプロイメントのロールアウト完了を待機
# 必要に応じて、対象デプロイメント名とNamespaceを環境変数や引数から取得する
DEPLOYMENT_NAME="my-nginx-app" # 例: 実際のデプロイメント名に合わせる
NAMESPACE="default" # 例: 実際のNamespaceに合わせる
ROLLOUT_TIMEOUT_SEC=300
echo "$(date '+%Y-%m-%d %H:%M:%S JST') INFO: デプロイメント ${DEPLOYMENT_NAME} のロールアウト完了を待機中..."
if ! kubectl rollout status deployment/"$DEPLOYMENT_NAME" -n "$NAMESPACE" --timeout="${ROLLOUT_TIMEOUT_SEC}s"; then
echo "$(date '+%Y-%m-%d %H:%M:%S JST') ERROR: デプロイメント ${DEPLOYMENT_NAME} のロールアウトがタイムアウトしました。" >&2
exit 1
fi
echo "$(date '+%Y-%m-%d %H:%M:%S JST') INFO: デプロイメント ${DEPLOYMENT_NAME} のロールアウトが完了しました。"
# jq を用いてデプロイメントのReady状態をチェック
echo "$(date '+%Y-%m-%d %H:%M:%S JST') INFO: デプロイメント ${DEPLOYMENT_NAME} のステータス確認 (jq使用)..."
# kubectl get のJSON出力から readyReplicas と replicas を抽出
READY_REPLICAS=$(kubectl get deployment "$DEPLOYMENT_NAME" -n "$NAMESPACE" -o json | jq -r '.status.readyReplicas // 0')
DESIRED_REPLICAS=$(kubectl get deployment "$DEPLOYMENT_NAME" -n "$NAMESPACE" -o json | jq -r '.spec.replicas // 0')
if [ "$READY_REPLICAS" -eq "$DESIRED_REPLICAS" ] && [ "$DESIRED_REPLICAS" -gt 0 ]; then
echo "$(date '+%Y-%m-%d %H:%M:%S JST') INFO: デプロイメント ${DEPLOYMENT_NAME} は正常に稼働中です (${READY_REPLICAS}/${DESIRED_REPLICAS} レプリカ)。"
else
echo "$(date '+%Y-%m-%d %H:%M:%S JST') ERROR: デプロイメント ${DEPLOYMENT_NAME} のReady状態に問題があります (${READY_REPLICAS}/${DESIRED_REPLICAS} レプリカ)。" >&2
exit 1
fi
# 外部サービスのヘルスチェック (curl利用)
# TLS設定、再試行、バックオフの例
SERVICE_URL="https://my-app.example.com/health" # 実際のサービスURLに置換
echo "$(date '+%Y-%m-%d %H:%M:%S JST') INFO: 外部サービス ${SERVICE_URL} のヘルスチェック中 (curl使用)..."
# curl --retry: リトライ回数
# --retry-delay: リトライ間の待ち時間(秒)
# --retry-max-time: リトライ全体の最大時間(秒)
# --connect-timeout: 接続タイムアウト(秒)
# --fail: HTTPステータスコードが400以上の場合にエラー終了
# --insecure: サーバー証明書の検証を無効化(テスト環境でのみ使用し、本番では削除または適切な証明書を --cacert で指定)
if ! curl --retry 10 --retry-delay 5 --retry-max-time 60 \
--connect-timeout 5 --fail --silent --show-error \
"$SERVICE_URL"; then
echo "$(date '+%Y-%m-%d %H:%M:%S JST') ERROR: 外部サービス ${SERVICE_URL} のヘルスチェックに失敗しました。" >&2
exit 1
fi
echo "$(date '+%Y-%m-%d %H:%M:%S JST') INFO: 外部サービス ${SERVICE_URL} のヘルスチェックが成功しました。"
echo "$(date '+%Y-%m-%d %H:%M:%S JST') SUCCESS: Kustomizeデプロイと検証が完了しました。"
exit 0 # 正常終了
2.3. systemd Unit/Timerによる自動実行
deploy-kustomize.shスクリプトを定期的に実行するため、systemdのUnitとTimerを使用します。これにより、OSレベルでデプロイの自動化と監視が可能になります。
root権限の扱いと権限分離の注意点:
systemdの設定ファイルは通常/etc/systemd/system/に配置され、systemctl enableなどのコマンドはroot権限が必要です。しかし、ExecStartで実行されるスクリプト自体は、User=およびGroup=ディレクティブで指定された非rootユーザーとして実行されるべきです。このユーザーは、~/.kube/configを通じてKubernetesクラスタにアクセスするための最小限のRBAC権限のみを持つように設定してください。これにより、root権限でのKubernetes操作を回避し、セキュリティリスクを低減できます。
deploy-kustomize.service: デプロイスクリプトを実行するUnitファイル# /etc/systemd/system/deploy-kustomize.service [Unit] Description=Kustomize Kubernetes Deployment Service # ネットワークが利用可能になった後にサービスを開始 After=network-online.target [Service] # Type=oneshot: コマンドが完了したらサービスは終了する Type=oneshot # User/Group: このサービスを実行する非rootユーザーとグループ # このユーザーがkubectl実行に必要なkubeconfigファイルにアクセスできること User=k8s-deploy-user Group=k8s-deploy-group # WorkingDirectory: デプロイスクリプトが実行されるGitリポジトリのルートパス # 2024年07月30日時点でのKustomizeおよびkubectlの推奨バージョンで動作確認 WorkingDirectory=/opt/gitops/k8s-configs # ExecStart: 実行するスクリプトと引数 ExecStart=/usr/local/bin/deploy-kustomize.sh overlays/prod # 標準出力と標準エラー出力をjournalに記録 StandardOutput=journal StandardError=journal # サービスが失敗した場合に再起動する設定(5秒後に再起動) Restart=on-failure RestartSec=5s [Install] # サービスが有効化された際に、マルチユーザー環境で利用可能になるように設定 WantedBy=multi-user.target
deploy-kustomize.timer: 上記サービスを定期実行するTimerファイル# /etc/systemd/system/deploy-kustomize.timer [Unit] Description=Run Kustomize Kubernetes Deployment every hour # After: 関連サービスが利用可能になった後にタイマーを起動 [Timer] # OnCalendar: 実行スケジュールを定義 (例: 毎時実行) OnCalendar=hourly # Persistent=true: タイマーが非アクティブな間に発生したイベントを、次回の起動時に実行する Persistent=true [Install] # タイマーが有効化された際に、システムタイマーターゲットによって管理されるように設定 WantedBy=timers.target
2.4. systemdの起動とログ確認
設定ファイルを配置後、以下のコマンドでsystemdをリロードし、タイマーを有効化して起動します。
# systemd設定ファイルを配置 (root権限が必要) sudo cp deploy-kustomize.service /etc/systemd/system/ sudo cp deploy-kustomize.timer /etc/systemd/system/ # systemdデーモンをリロードして新しい設定ファイルを読み込ませる sudo systemctl daemon-reload # deploy-kustomize.timer を有効化し、即座に起動する # これにより、timerが設定したスケジュールでserviceを自動実行する sudo systemctl enable --now deploy-kustomize.timer # タイマーとサービスのステータスを確認 systemctl status deploy-kustomize.timer systemctl status deploy-kustomize.service # サービス実行ログを確認 # 直近のログを見るには、例えば --since "1 hour ago" オプションを使用 journalctl -u deploy-kustomize.service --since "1 hour ago"
3. 検証
デプロイが成功したことを確認するためのステップです。
Kubernetesリソースの確認:
kubectl get all -n <対象Namespace> kubectl describe deployment my-nginx-app -n <対象Namespace>
systemdサービスの状態確認:
systemctl status deploy-kustomize.timer systemctl status deploy-kustomize.service journalctl -u deploy-kustomize.service --reverse # 最新のログから表示
アプリケーションのヘルスチェック: デプロイスクリプト内で実行した
curlコマンドを手動で実行し、アプリケーションの外部からの到達性や正常性を確認します。
4. 運用
Kustomizeとkubectl連携の運用における重要な側面です。
4.1. GitOpsワークフロー
Gitリポジトリへのコミットをトリガーとして、CI/CDパイプラインがデプロイスクリプト(またはsystemdタイマーが参照するスクリプト)を実行する流れを構築します。
graph TD
A["開発者がGitリポジトリに変更をコミット"] -->|"変更をPush"| B("CI/CDパイプライン起動")
B -->|"Kustomizeビルドスクリプト実行"| C{"Kustomizeでマニフェスト生成"}
C -->|"生成されたマニフェストを適用"| D("kubectl apply実行")
D -->|"Kubernetesクラスタのリソース更新"| E["Kubernetesクラスタ"]
E -->|"リソース状態を監視しフィードバック"| F("監視システム")
F -->|"変更を検知しCI/CDへ通知 (オプション)"| B4.2. 監視とアラート
journalctlでログを収集し、ログ監視システム(例: Prometheus/Grafana Loki, ELK Stack)に統合することで、デプロイの成功・失敗を監視し、異常時にアラートを発報するように設定します。
4.3. ロールバック戦略
GitOpsでは、Gitリポジトリの履歴が唯一の真実のソースです。ロールバックは、Gitリポジトリを以前の安定したコミットに戻し、再度デプロイプロセスを実行することで実現します。Kustomizeとkubectl applyの組み合わせは宣言的であるため、ターゲットとするGitの状態へのロールバックは比較的容易です。
4.4. 権限管理の徹底
systemdのUser=設定により、kubectlを実行するユーザーをrootから分離することは非常に重要です。このk8s-deploy-userには、対象Kubernetes Namespaceに対するリソースのget, list, create, update, patch, deleteといった最小限のRBAC権限のみを付与し、cluster-adminのような広範な権限は絶対に与えないでください。kubeconfigファイル自体も、このユーザーのホームディレクトリに配置し、適切なファイルパーミッション(600など)で保護することが必須です。
5. トラブルシュート
一般的な問題とその解決策です。
Kustomizeビルドエラー:
kustomization.yamlの構文エラーや参照パスの誤りがないか確認します。kubectl kustomize <overlay_path> --enable-helm(Helmチャートをベースとして利用する場合) でビルドのみを行い、生成されるマニフェストを確認します。
kubectl applyエラー:RBAC権限不足:
Error from server (Forbidden)などのメッセージが出た場合、k8s-deploy-userに適切なRBAC権限が付与されているか確認します。YAML構文エラー: Kustomizeビルド後のマニフェストに問題がないか確認します。
リソース競合: 他のデプロイプロセスとの競合がないか、または変更が競合しないか確認します。
systemdサービス起動失敗:
journalctl -u deploy-kustomize.service -xeで詳細なエラーログを確認します。ExecStartで指定されたスクリプトのパスが正しいか、実行権限があるか確認します。User=で指定されたユーザーが存在し、必要な環境変数やファイルにアクセスできるか確認します。
jqによるJSONパースエラー:kubectl get -o jsonの出力が期待通りのJSON構造になっているか確認します。Kubernetes APIのバージョンアップで出力形式が変わることがあります。jqのフィルタリングパスが正しいか確認します。
curlによるヘルスチェックエラー:ネットワーク接続性、DNS解決、ファイアウォールの設定を確認します。
curl -vオプションを使って、詳細な接続情報を確認します。HTTPSの場合、TLS証明書の問題(
--insecureの使用を避ける)や認証情報がないか確認します。
6. まとめ
kubectlとKustomizeの連携は、Kubernetes環境におけるアプリケーションデプロイを効率的かつ信頼性の高いものにする強力な手段です。本ガイドで示したような冪等なシェルスクリプト、jqやcurlによる厳格な検証、そしてsystemdによる安定した自動実行の組み合わせは、GitOpsの原則を実践し、安全なCI/CDパイプラインを構築する上で不可欠です。2024年07月30日時点での最新のベストプラクティスに基づき、これらの技術要素を組み合わせることで、Kubernetesの運用をより堅牢かつ自動化されたものにすることができます。

