<p>本記事は<strong>Geminiの出力をプロンプト工学で整理した業務ドラフト(未検証)</strong>です。</p>
<h1 class="wp-block-heading">kubectl: Deploymentのローリング再起動</h1>
<p>Kubernetes環境において、DeploymentのPodを再起動するシナリオは多岐にわたります。例えば、設定変更の適用、メモリリーク対策、または定期的なリフレッシュなどです。本記事では、<code>kubectl</code>コマンドを用いたDeploymentの安全なローリング再起動の手順、その自動化、そしてトラブルシューティングについて、DevOpsエンジニアの視点から解説します。</p>
<h2 class="wp-block-heading">1. 要件と前提</h2>
<h3 class="wp-block-heading">1.1. ローリング再起動とは</h3>
<p>ローリング再起動は、Deployment内のPodを一度にすべて停止させるのではなく、Podを順番に置き換えることで、サービスへの影響を最小限に抑えながら更新を行う手法です。KubernetesのDeploymentでは、デフォルトで<code>RollingUpdate</code>ストラテジーが採用されており、これによってゼロダウンタイムでの更新が可能になります。<code>kubectl rollout restart</code>コマンドは、DeploymentのPod Template Specに変更を加え、既存のPodを順次終了させ、新しいPodを起動させることでローリングアップデートをトリガーします。</p>
<h3 class="wp-block-heading">1.2. 前提条件</h3>
<ul class="wp-block-list">
<li><p>Kubernetesクラスターが稼働していること。</p></li>
<li><p><code>kubectl</code>コマンドラインツールがインストールされ、クラスターへの接続設定が完了していること。</p></li>
<li><p>再起動対象となるDeploymentがクラスター内に存在していること。</p></li>
<li><p>ロールアウトを正常に完了するための十分なクラスターリソース(CPU, メモリ)が存在すること。</p></li>
<li><p>Readiness ProbeとLiveness Probeが適切に設定されていること(これにより、新しいPodがトラフィックを受け入れる準備ができたことをKubernetesが判断できます)。</p></li>
</ul>
<h2 class="wp-block-heading">2. 実装</h2>
<h3 class="wp-block-heading">2.1. 基本的なローリング再起動の実施</h3>
<p>最もシンプルなローリング再起動は、以下の<code>kubectl</code>コマンドで実行できます。</p>
<div class="codehilite">
<pre data-enlighter-language="generic">kubectl rollout restart deployment/<deployment-name> -n <namespace>
</pre>
</div>
<p>例:</p>
<div class="codehilite">
<pre data-enlighter-language="generic">kubectl rollout restart deployment/my-app-deployment -n default
</pre>
</div>
<p>このコマンドは、DeploymentのPod Template Specに<code>kubernetes.io/change-cause</code>アノテーションまたはタイムスタンプを含むアノテーションを追加・更新することで、変更をトリガーし、ローリングアップデートを開始します。Kubernetesはこれを検知し、Deploymentコントローラーが新しいPodの起動と古いPodの終了を順次行います。</p>
<h3 class="wp-block-heading">2.2. 安全なスクリプトによる自動化</h3>
<p>ローリング再起動を自動化する場合、堅牢なシェルスクリプトを作成することが重要です。ここでは、<code>set -euo pipefail</code>や<code>trap</code>を用いた安全なbashスクリプトの例を示します。</p>
<div class="codehilite">
<pre data-enlighter-language="generic">#!/bin/bash
# file: restart-deployment.sh
# スクリプトの途中でエラーが発生した場合、即座に終了
# 未定義の変数が使用された場合、エラーとして終了
# パイプライン中のコマンドが一つでも失敗した場合、エラーとして終了
set -euo pipefail
# 現在のJST日付を取得
JST_TODAY=$(TZ='Asia/Tokyo' date +'%Y年%m月%d日 (%a)')
# 一時ディレクトリの作成と終了時のクリーンアップ
# mktemp -d はセキュリティを考慮し、ユニークな一時ディレクトリを作成
tmpdir=$(mktemp -d)
trap 'echo "[$JST_TODAY] 一時ディレクトリ ${tmpdir} をクリーンアップします。" && rm -rf "$tmpdir"' EXIT
# 設定変数
DEPLOYMENT_NAME="my-app-deployment"
NAMESPACE="default"
TIMEOUT_MINUTES="5m" # ロールアウト完了の最大待機時間
echo "[$JST_TODAY] Deployment '${DEPLOYMENT_NAME}' (namespace: '${NAMESPACE}') のローリング再起動を開始します。"
# Deploymentの存在チェック
if ! kubectl get deployment "${DEPLOYMENT_NAME}" -n "${NAMESPACE}" > /dev/null 2>&1; then
echo "[$JST_TODAY] エラー: Deployment '${DEPLOYMENT_NAME}' が存在しません。スクリプトを終了します。"
exit 1
fi
# ロールアウト再起動のトリガー
echo "[$JST_TODAY] kubectl rollout restart コマンドを実行します..."
if ! kubectl rollout restart deployment "${DEPLOYMENT_NAME}" -n "${NAMESPACE}"; then
echo "[$JST_TODAY] エラー: ロールアウト再起動のトリガーに失敗しました。スクリプトを終了します。"
exit 1
fi
# ロールアウトのステータス確認
echo "[$JST_TODAY] ロールアウトの完了を最大 ${TIMEOUT_MINUTES} 待機します..."
if ! kubectl rollout status deployment "${DEPLOYMENT_NAME}" -n "${NAMESPACE}" --timeout="${TIMEOUT_MINUTES}"; then
echo "[$JST_TODAY] エラー: ロールアウトが時間内に完了しませんでした。Deploymentの状態を確認してください。"
exit 1
fi
echo "[$JST_TODAY] Deployment '${DEPLOYMENT_NAME}' のローリング再起動が成功しました。"
# オプション: サービスヘルスチェック (curlとjqの活用)
# 必要に応じて、サービスのエンドポイントやヘルスチェックパスを調整してください。
# この例では、サービスタイプがLoadBalancerまたはNodePortで、外部IPが割り当てられていることを想定。
echo "[$JST_TODAY] サービスのエンドポイント情報を取得します..."
SERVICE_NAME="my-app-service" # ヘルスチェック対象のService名
SERVICE_JSON=$(kubectl get service "${SERVICE_NAME}" -n "${NAMESPACE}" -o json 2>/dev/null || true)
if [[ -n "${SERVICE_JSON}" ]]; then
# jqを用いて、LoadBalancerの外部IPまたはClusterIPを取得
# .status.loadBalancer.ingress[0].ip は LoadBalancer の外部IP
# .spec.clusterIP は ClusterIP
SERVICE_IP=$(echo "${SERVICE_JSON}" | jq -r '
.status.loadBalancer.ingress[0].ip // .spec.clusterIP // "null"
')
if [[ "${SERVICE_IP}" != "null" ]]; then
HEALTH_CHECK_URL="http://${SERVICE_IP}:8080/healthz" # アプリケーションのヘルスチェックURLに合わせる
echo "[$JST_TODAY] サービスヘルスチェックを開始します (${HEALTH_CHECK_URL})..."
# curl: 5回リトライ、5秒間隔、接続拒否もリトライ対象、サイレントモード、エラーコードで失敗、HTTPS証明書検証無効
if curl --retry 5 --retry-delay 5 --retry-connrefused -sS --fail -k "${HEALTH_CHECK_URL}"; then
echo "[$JST_TODAY] サービスヘルスチェックに成功しました。"
else
echo "[$JST_TODAY] 警告: サービスヘルスチェックに失敗しました。アプリケーションの状態を確認してください。"
fi
else
echo "[$JST_TODAY] 警告: サービス '${SERVICE_NAME}' のIPアドレスが見つかりませんでした。ヘルスチェックをスキップします。"
fi
else
echo "[$JST_TODAY] 警告: サービス '${SERVICE_NAME}' が見つからないか、JSON取得に失敗しました。ヘルスチェックをスキップします。"
fi
echo "[$JST_TODAY] スクリプトが正常に終了しました。"
</pre>
</div>
<h3 class="wp-block-heading">2.3. ロールアウトプロセスのMermaidフローチャート</h3>
<p>ローリング再起動のプロセスを図で示します。</p>
<div class="wp-block-merpress-mermaidjs diagram-source-mermaid"><pre class="mermaid">
graph TD
A["再起動トリガーを適用"] -->|kubectl rollout restart| B{"Deploymentの変更を検出"};
B -- Pod Template Spec変更 --> C["新しいReplicaSetを作成"];
C --> D{"Podの終了と新規作成を順次実行"};
D -- 新しいPod起動 --> E{"Readiness Probeが成功"};
E -- 成功 --> F["古いPodを削除し、新しいPodを追加"];
F --> G{"全てのPodが新しいバージョンに"};
G -- 更新完了 --> H["Rollout成功"];
E -- 失敗 --> I["Rollout失敗/Rollbackを検討"];
I --> J["トラブルシューティング"];
</pre></div>
<h2 class="wp-block-heading">3. 検証</h2>
<p>ローリング再起動後には、以下の方法で成功を確認します。</p>
<ol class="wp-block-list">
<li><p><strong>ロールアウト履歴の確認</strong>:</p>
<div class="codehilite">
<pre data-enlighter-language="generic">kubectl rollout history deployment/my-app-deployment -n default
</pre>
</div>
<p>最新のリビジョンが更新されていることを確認します。</p></li>
<li><p><strong>Podの確認</strong>:</p>
<div class="codehilite">
<pre data-enlighter-language="generic">kubectl get pods -n default -l app=my-app -o wide
</pre>
</div>
<p>すべてのPodが<code>Running</code>状態であり、新しいPodが起動していることを確認します。<code>AGE</code>が再起動からの時間を示します。</p></li>
<li><p><strong>イベントログの確認</strong>:</p>
<div class="codehilite">
<pre data-enlighter-language="generic">kubectl describe deployment my-app-deployment -n default
kubectl get events -n default
</pre>
</div>
<p>Deploymentのイベントログから、更新が正常に進行したか、エラーが発生していないかを確認します。</p></li>
<li><p><strong>アプリケーションログの確認</strong>:
アプリケーションのログを確認し、サービスが正常に動作しているか、エラーが発生していないかを検証します。</p>
<div class="codehilite">
<pre data-enlighter-language="generic">kubectl logs -f <pod-name> -n default
</pre>
</div></li>
</ol>
<h2 class="wp-block-heading">4. 運用</h2>
<h3 class="wp-block-heading">4.1. systemdによる自動化</h3>
<p>前述のシェルスクリプトを<code>systemd</code>のUnit/Timerと組み合わせて、定期的な再起動やイベントベースの再起動を自動化できます。</p>
<p><strong>4.1.1. systemd Service Unitの作成</strong>
<code>/etc/systemd/system/restart-my-app.service</code></p>
<div class="codehilite">
<pre data-enlighter-language="generic">[Unit]
Description=Restart my-app Kubernetes Deployment
Wants=network-online.target
After=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/restart-deployment.sh
User=kubernetes-admin # kubectl設定済みユーザー(サービスアカウント利用推奨)
Group=kubernetes-admin # kubectl設定済みグループ
# StandardOutput=journal
# StandardError=journal
# Restart=on-failure # 失敗時に自動再起動する場合
[Install]
WantedBy=multi-user.target
</pre>
</div>
<ul class="wp-block-list">
<li><p><code>ExecStart</code>: 上記で作成したシェルスクリプトのパスを指定します。</p></li>
<li><p><code>User</code>/<code>Group</code>: <code>kubectl</code>が動作するために必要なkubeconfigファイルへのアクセス権を持つユーザーを指定します。本番環境では、最小権限のサービスアカウントを作成し、その資格情報を使用するようシェルスクリプトを調整し、<code>User=...</code>は適切なものに設定することが強く推奨されます。<code>root</code>権限でサービスを実行する場合、セキュリティリスクが増大します。</p></li>
</ul>
<p><strong>4.1.2. systemd Timer Unitの作成</strong>
<code>/etc/systemd/system/restart-my-app.timer</code> (例: 毎日午前3時JSTに実行)</p>
<div class="codehilite">
<pre data-enlighter-language="generic">[Unit]
Description=Run my-app Deployment restart daily at 3 AM JST
[Timer]
OnCalendar=*-*-* 03:00:00 # 毎日午前3時 (システムのタイムゾーンに基づく)
Persistent=true # サーバ再起動後も、実行し損ねた場合は即座に実行
[Install]
WantedBy=timers.target
</pre>
</div>
<p><strong>4.1.3. systemdの設定と起動</strong></p>
<ol class="wp-block-list">
<li><p><strong>シェルスクリプトの配置と権限設定</strong>:</p>
<div class="codehilite">
<pre data-enlighter-language="generic">sudo install -m 755 restart-deployment.sh /usr/local/bin/
</pre>
</div></li>
<li><p><strong>systemd設定ファイルの再読み込み</strong>:</p>
<div class="codehilite">
<pre data-enlighter-language="generic">sudo systemctl daemon-reload
</pre>
</div></li>
<li><p><strong>タイマーの有効化と起動</strong>:</p>
<div class="codehilite">
<pre data-enlighter-language="generic">sudo systemctl enable restart-my-app.timer
sudo systemctl start restart-my-app.timer
</pre>
</div></li>
<li><p><strong>ステータスの確認</strong>:</p>
<div class="codehilite">
<pre data-enlighter-language="generic">sudo systemctl status restart-my-app.timer
sudo systemctl status restart-my-app.service
</pre>
</div></li>
<li><p><strong>ログの確認</strong>:</p>
<div class="codehilite">
<pre data-enlighter-language="generic">sudo journalctl -u restart-my-app.service -f
</pre>
</div>
<p>これにより、スクリプトの実行ログやエラーを確認できます。</p></li>
</ol>
<h3 class="wp-block-heading">4.2. root権限の扱いと権限分離</h3>
<p><code>systemd</code>サービスを管理する<code>systemctl</code>コマンドは、通常<code>root</code>権限が必要です。しかし、<code>ExecStart</code>で実行されるシェルスクリプト内で<code>kubectl</code>コマンドが呼び出される場合、その<code>kubectl</code>コマンドは<code>systemd</code> Unitファイルで指定された<code>User</code>で実行されます。</p>
<ul class="wp-block-list">
<li><p><strong>原則</strong>: <code>kubectl</code>コマンドは、可能な限り最小権限のサービスアカウントを使用すべきです。<code>kubectl</code>の実行ユーザーは、Deploymentの更新権限(<code>update</code>, <code>get</code>, <code>list</code>, <code>watch</code> on <code>deployments</code>, <code>replicasets</code>, <code>pods</code>)のみを持つロールにバインドされている必要があります。</p></li>
<li><p><strong>本番環境</strong>: <code>systemd</code> Unitの<code>User</code>設定で、特定の非<code>root</code>ユーザー(例: <code>kubernetes-operator</code>)を指定し、そのユーザーのホームディレクトリに<code>.kube/config</code>を配置し、適切な権限を設定します。これにより、万が一スクリプトが侵害されても、<code>root</code>権限によるシステム全体への影響を防ぐことができます。</p></li>
</ul>
<h2 class="wp-block-heading">5. トラブルシュート</h2>
<p>ローリング再起動中に問題が発生した場合の一般的なトラブルシューティング手順です。</p>
<ol class="wp-block-list">
<li><p><strong><code>kubectl rollout status</code>が失敗する場合</strong>:</p>
<ul>
<li><p><code>kubectl get events -n <namespace></code>: クラスターイベントを確認し、Podのスケジューリング失敗やイメージプルエラーなどがないか確認します。</p></li>
<li><p><code>kubectl describe pod <failed-pod-name> -n <namespace></code>: 失敗したPodの詳細を確認し、エラーメッセージやコンテナステータスを確認します。</p></li>
<li><p><code>kubectl logs <failed-pod-name> -n <namespace></code>: アプリケーションのログを確認し、アプリケーションレベルのエラーがないか調査します。</p></li>
<li><p><code>kubectl get rs -n <namespace></code>: 新しいReplicaSetが作成され、そのPodがPendingまたはCrashLoopBackOff状態になっていないか確認します。</p></li>
</ul></li>
<li><p><strong>Readiness Probeが失敗する場合</strong>:</p>
<ul>
<li><p>アプリケーションが起動に時間がかかっている可能性があります。<code>initialDelaySeconds</code>や<code>periodSeconds</code>、<code>timeoutSeconds</code>、<code>failureThreshold</code>などのReadiness Probe設定を見直します。</p></li>
<li><p>アプリケーションのヘルスチェックエンドポイントが正しく機能しているか確認します。</p></li>
</ul></li>
<li><p><strong>リソース不足</strong>:</p>
<ul>
<li><p><code>kubectl describe nodes</code>: ノードのリソース使用状況を確認し、CPUやメモリが不足していないか確認します。</p></li>
<li><p><code>kubectl top pods -n <namespace></code>: 各Podのリソース使用量を確認します。</p></li>
<li><p><code>resources.requests</code>および<code>resources.limits</code>をDeploymentのPod Template Specで適切に設定しているか確認します。</p></li>
</ul></li>
<li><p><strong>ロールバック</strong>:
問題が解決できない場合や、サービスが劣化している場合は、以前の安定したバージョンにロールバックすることを検討します。</p>
<div class="codehilite">
<pre data-enlighter-language="generic">kubectl rollout undo deployment/my-app-deployment -n default
# 特定のリビジョンに戻す場合 (historyで確認したリビジョン番号)
# kubectl rollout undo deployment/my-app-deployment -n default --to-revision=<revision-number>
</pre>
</div></li>
</ol>
<h2 class="wp-block-heading">6. まとめ</h2>
<p>Kubernetes Deploymentのローリング再起動は、サービスに中断を与えることなくアプリケーションを更新するための重要な運用プラクティスです。<code>kubectl rollout restart</code>コマンドを基本とし、<code>set -euo pipefail</code>や<code>trap</code>を用いた安全なシェルスクリプト、<code>jq</code>や<code>curl</code>による検証、そして<code>systemd</code> Unit/Timerによる自動化を組み合わせることで、堅牢な運用が実現できます。常にReadiness Probeの重要性を理解し、本番環境では最小権限の原則に従い、問題発生時には冷静にログやイベントを確認し、必要に応じてロールバックできるよう準備しておくことがDevOpsエンジニアには求められます。</p>
本記事はGeminiの出力をプロンプト工学で整理した業務ドラフト(未検証)です。
kubectl: Deploymentのローリング再起動
Kubernetes環境において、DeploymentのPodを再起動するシナリオは多岐にわたります。例えば、設定変更の適用、メモリリーク対策、または定期的なリフレッシュなどです。本記事では、kubectlコマンドを用いたDeploymentの安全なローリング再起動の手順、その自動化、そしてトラブルシューティングについて、DevOpsエンジニアの視点から解説します。
1. 要件と前提
1.1. ローリング再起動とは
ローリング再起動は、Deployment内のPodを一度にすべて停止させるのではなく、Podを順番に置き換えることで、サービスへの影響を最小限に抑えながら更新を行う手法です。KubernetesのDeploymentでは、デフォルトでRollingUpdateストラテジーが採用されており、これによってゼロダウンタイムでの更新が可能になります。kubectl rollout restartコマンドは、DeploymentのPod Template Specに変更を加え、既存のPodを順次終了させ、新しいPodを起動させることでローリングアップデートをトリガーします。
1.2. 前提条件
Kubernetesクラスターが稼働していること。
kubectlコマンドラインツールがインストールされ、クラスターへの接続設定が完了していること。
再起動対象となるDeploymentがクラスター内に存在していること。
ロールアウトを正常に完了するための十分なクラスターリソース(CPU, メモリ)が存在すること。
Readiness ProbeとLiveness Probeが適切に設定されていること(これにより、新しいPodがトラフィックを受け入れる準備ができたことをKubernetesが判断できます)。
2. 実装
2.1. 基本的なローリング再起動の実施
最もシンプルなローリング再起動は、以下のkubectlコマンドで実行できます。
kubectl rollout restart deployment/<deployment-name> -n <namespace>
例:
kubectl rollout restart deployment/my-app-deployment -n default
このコマンドは、DeploymentのPod Template Specにkubernetes.io/change-causeアノテーションまたはタイムスタンプを含むアノテーションを追加・更新することで、変更をトリガーし、ローリングアップデートを開始します。Kubernetesはこれを検知し、Deploymentコントローラーが新しいPodの起動と古いPodの終了を順次行います。
2.2. 安全なスクリプトによる自動化
ローリング再起動を自動化する場合、堅牢なシェルスクリプトを作成することが重要です。ここでは、set -euo pipefailやtrapを用いた安全なbashスクリプトの例を示します。
#!/bin/bash
# file: restart-deployment.sh
# スクリプトの途中でエラーが発生した場合、即座に終了
# 未定義の変数が使用された場合、エラーとして終了
# パイプライン中のコマンドが一つでも失敗した場合、エラーとして終了
set -euo pipefail
# 現在のJST日付を取得
JST_TODAY=$(TZ='Asia/Tokyo' date +'%Y年%m月%d日 (%a)')
# 一時ディレクトリの作成と終了時のクリーンアップ
# mktemp -d はセキュリティを考慮し、ユニークな一時ディレクトリを作成
tmpdir=$(mktemp -d)
trap 'echo "[$JST_TODAY] 一時ディレクトリ ${tmpdir} をクリーンアップします。" && rm -rf "$tmpdir"' EXIT
# 設定変数
DEPLOYMENT_NAME="my-app-deployment"
NAMESPACE="default"
TIMEOUT_MINUTES="5m" # ロールアウト完了の最大待機時間
echo "[$JST_TODAY] Deployment '${DEPLOYMENT_NAME}' (namespace: '${NAMESPACE}') のローリング再起動を開始します。"
# Deploymentの存在チェック
if ! kubectl get deployment "${DEPLOYMENT_NAME}" -n "${NAMESPACE}" > /dev/null 2>&1; then
echo "[$JST_TODAY] エラー: Deployment '${DEPLOYMENT_NAME}' が存在しません。スクリプトを終了します。"
exit 1
fi
# ロールアウト再起動のトリガー
echo "[$JST_TODAY] kubectl rollout restart コマンドを実行します..."
if ! kubectl rollout restart deployment "${DEPLOYMENT_NAME}" -n "${NAMESPACE}"; then
echo "[$JST_TODAY] エラー: ロールアウト再起動のトリガーに失敗しました。スクリプトを終了します。"
exit 1
fi
# ロールアウトのステータス確認
echo "[$JST_TODAY] ロールアウトの完了を最大 ${TIMEOUT_MINUTES} 待機します..."
if ! kubectl rollout status deployment "${DEPLOYMENT_NAME}" -n "${NAMESPACE}" --timeout="${TIMEOUT_MINUTES}"; then
echo "[$JST_TODAY] エラー: ロールアウトが時間内に完了しませんでした。Deploymentの状態を確認してください。"
exit 1
fi
echo "[$JST_TODAY] Deployment '${DEPLOYMENT_NAME}' のローリング再起動が成功しました。"
# オプション: サービスヘルスチェック (curlとjqの活用)
# 必要に応じて、サービスのエンドポイントやヘルスチェックパスを調整してください。
# この例では、サービスタイプがLoadBalancerまたはNodePortで、外部IPが割り当てられていることを想定。
echo "[$JST_TODAY] サービスのエンドポイント情報を取得します..."
SERVICE_NAME="my-app-service" # ヘルスチェック対象のService名
SERVICE_JSON=$(kubectl get service "${SERVICE_NAME}" -n "${NAMESPACE}" -o json 2>/dev/null || true)
if [[ -n "${SERVICE_JSON}" ]]; then
# jqを用いて、LoadBalancerの外部IPまたはClusterIPを取得
# .status.loadBalancer.ingress[0].ip は LoadBalancer の外部IP
# .spec.clusterIP は ClusterIP
SERVICE_IP=$(echo "${SERVICE_JSON}" | jq -r '
.status.loadBalancer.ingress[0].ip // .spec.clusterIP // "null"
')
if [[ "${SERVICE_IP}" != "null" ]]; then
HEALTH_CHECK_URL="http://${SERVICE_IP}:8080/healthz" # アプリケーションのヘルスチェックURLに合わせる
echo "[$JST_TODAY] サービスヘルスチェックを開始します (${HEALTH_CHECK_URL})..."
# curl: 5回リトライ、5秒間隔、接続拒否もリトライ対象、サイレントモード、エラーコードで失敗、HTTPS証明書検証無効
if curl --retry 5 --retry-delay 5 --retry-connrefused -sS --fail -k "${HEALTH_CHECK_URL}"; then
echo "[$JST_TODAY] サービスヘルスチェックに成功しました。"
else
echo "[$JST_TODAY] 警告: サービスヘルスチェックに失敗しました。アプリケーションの状態を確認してください。"
fi
else
echo "[$JST_TODAY] 警告: サービス '${SERVICE_NAME}' のIPアドレスが見つかりませんでした。ヘルスチェックをスキップします。"
fi
else
echo "[$JST_TODAY] 警告: サービス '${SERVICE_NAME}' が見つからないか、JSON取得に失敗しました。ヘルスチェックをスキップします。"
fi
echo "[$JST_TODAY] スクリプトが正常に終了しました。"
2.3. ロールアウトプロセスのMermaidフローチャート
ローリング再起動のプロセスを図で示します。
graph TD
A["再起動トリガーを適用"] -->|kubectl rollout restart| B{"Deploymentの変更を検出"};
B -- Pod Template Spec変更 --> C["新しいReplicaSetを作成"];
C --> D{"Podの終了と新規作成を順次実行"};
D -- 新しいPod起動 --> E{"Readiness Probeが成功"};
E -- 成功 --> F["古いPodを削除し、新しいPodを追加"];
F --> G{"全てのPodが新しいバージョンに"};
G -- 更新完了 --> H["Rollout成功"];
E -- 失敗 --> I["Rollout失敗/Rollbackを検討"];
I --> J["トラブルシューティング"];
3. 検証
ローリング再起動後には、以下の方法で成功を確認します。
ロールアウト履歴の確認:
kubectl rollout history deployment/my-app-deployment -n default
最新のリビジョンが更新されていることを確認します。
Podの確認:
kubectl get pods -n default -l app=my-app -o wide
すべてのPodがRunning状態であり、新しいPodが起動していることを確認します。AGEが再起動からの時間を示します。
イベントログの確認:
kubectl describe deployment my-app-deployment -n default
kubectl get events -n default
Deploymentのイベントログから、更新が正常に進行したか、エラーが発生していないかを確認します。
アプリケーションログの確認:
アプリケーションのログを確認し、サービスが正常に動作しているか、エラーが発生していないかを検証します。
kubectl logs -f <pod-name> -n default
4. 運用
4.1. systemdによる自動化
前述のシェルスクリプトをsystemdのUnit/Timerと組み合わせて、定期的な再起動やイベントベースの再起動を自動化できます。
4.1.1. systemd Service Unitの作成
/etc/systemd/system/restart-my-app.service
[Unit]
Description=Restart my-app Kubernetes Deployment
Wants=network-online.target
After=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/restart-deployment.sh
User=kubernetes-admin # kubectl設定済みユーザー(サービスアカウント利用推奨)
Group=kubernetes-admin # kubectl設定済みグループ
# StandardOutput=journal
# StandardError=journal
# Restart=on-failure # 失敗時に自動再起動する場合
[Install]
WantedBy=multi-user.target
ExecStart: 上記で作成したシェルスクリプトのパスを指定します。
User/Group: kubectlが動作するために必要なkubeconfigファイルへのアクセス権を持つユーザーを指定します。本番環境では、最小権限のサービスアカウントを作成し、その資格情報を使用するようシェルスクリプトを調整し、User=...は適切なものに設定することが強く推奨されます。root権限でサービスを実行する場合、セキュリティリスクが増大します。
4.1.2. systemd Timer Unitの作成
/etc/systemd/system/restart-my-app.timer (例: 毎日午前3時JSTに実行)
[Unit]
Description=Run my-app Deployment restart daily at 3 AM JST
[Timer]
OnCalendar=*-*-* 03:00:00 # 毎日午前3時 (システムのタイムゾーンに基づく)
Persistent=true # サーバ再起動後も、実行し損ねた場合は即座に実行
[Install]
WantedBy=timers.target
4.1.3. systemdの設定と起動
シェルスクリプトの配置と権限設定:
sudo install -m 755 restart-deployment.sh /usr/local/bin/
systemd設定ファイルの再読み込み:
sudo systemctl daemon-reload
タイマーの有効化と起動:
sudo systemctl enable restart-my-app.timer
sudo systemctl start restart-my-app.timer
ステータスの確認:
sudo systemctl status restart-my-app.timer
sudo systemctl status restart-my-app.service
ログの確認:
sudo journalctl -u restart-my-app.service -f
これにより、スクリプトの実行ログやエラーを確認できます。
4.2. root権限の扱いと権限分離
systemdサービスを管理するsystemctlコマンドは、通常root権限が必要です。しかし、ExecStartで実行されるシェルスクリプト内でkubectlコマンドが呼び出される場合、そのkubectlコマンドはsystemd Unitファイルで指定されたUserで実行されます。
原則: kubectlコマンドは、可能な限り最小権限のサービスアカウントを使用すべきです。kubectlの実行ユーザーは、Deploymentの更新権限(update, get, list, watch on deployments, replicasets, pods)のみを持つロールにバインドされている必要があります。
本番環境: systemd UnitのUser設定で、特定の非rootユーザー(例: kubernetes-operator)を指定し、そのユーザーのホームディレクトリに.kube/configを配置し、適切な権限を設定します。これにより、万が一スクリプトが侵害されても、root権限によるシステム全体への影響を防ぐことができます。
5. トラブルシュート
ローリング再起動中に問題が発生した場合の一般的なトラブルシューティング手順です。
kubectl rollout statusが失敗する場合:
kubectl get events -n <namespace>: クラスターイベントを確認し、Podのスケジューリング失敗やイメージプルエラーなどがないか確認します。
kubectl describe pod <failed-pod-name> -n <namespace>: 失敗したPodの詳細を確認し、エラーメッセージやコンテナステータスを確認します。
kubectl logs <failed-pod-name> -n <namespace>: アプリケーションのログを確認し、アプリケーションレベルのエラーがないか調査します。
kubectl get rs -n <namespace>: 新しいReplicaSetが作成され、そのPodがPendingまたはCrashLoopBackOff状態になっていないか確認します。
Readiness Probeが失敗する場合:
リソース不足:
kubectl describe nodes: ノードのリソース使用状況を確認し、CPUやメモリが不足していないか確認します。
kubectl top pods -n <namespace>: 各Podのリソース使用量を確認します。
resources.requestsおよびresources.limitsをDeploymentのPod Template Specで適切に設定しているか確認します。
ロールバック:
問題が解決できない場合や、サービスが劣化している場合は、以前の安定したバージョンにロールバックすることを検討します。
kubectl rollout undo deployment/my-app-deployment -n default
# 特定のリビジョンに戻す場合 (historyで確認したリビジョン番号)
# kubectl rollout undo deployment/my-app-deployment -n default --to-revision=<revision-number>
6. まとめ
Kubernetes Deploymentのローリング再起動は、サービスに中断を与えることなくアプリケーションを更新するための重要な運用プラクティスです。kubectl rollout restartコマンドを基本とし、set -euo pipefailやtrapを用いた安全なシェルスクリプト、jqやcurlによる検証、そしてsystemd Unit/Timerによる自動化を組み合わせることで、堅牢な運用が実現できます。常にReadiness Probeの重要性を理解し、本番環境では最小権限の原則に従い、問題発生時には冷静にログやイベントを確認し、必要に応じてロールバックできるよう準備しておくことがDevOpsエンジニアには求められます。
コメント