<p>本記事は<strong>Geminiの出力をプロンプト工学で整理した業務ドラフト(未検証)</strong>です。</p>
<h1 class="wp-block-heading">kubectl debug –image カスタムイメージを活用したKubernetesトラブルシューティング</h1>
<p>Kubernetes環境で稼働するアプリケーションのトラブルシューティングは、多くの場合、コンテナ内部にアクセスして状態を確認する必要があります。<code>kubectl exec</code>は手軽な方法ですが、デバッグに必要なツールがコンテナイメージに含まれていない場合、解決は困難になります。</p>
<p>そこで役立つのが、<code>kubectl debug</code>コマンドの<code>--image</code>オプションです。この機能は、トラブルシューティング専用のカスタムイメージを一時的に稼働中のPodにアタッチし、高度なデバッグツールを利用可能にします。本記事では、<code>kubectl debug --image</code>の活用方法、安全なスクリプト実装、および運用上の考慮事項について解説します。</p>
<h2 class="wp-block-heading">要件と前提</h2>
<h3 class="wp-block-heading"><code>kubectl debug</code>とエフェメラルコンテナの概要</h3>
<p><code>kubectl debug</code>は、Podのトラブルシューティングを目的としたコマンドです。特に<code>--image</code>オプションを利用すると、対象Podと同じネットワークおよびプロセス名前空間を共有する「エフェメラルコンテナ(一時コンテナ)」を一時的に追加できます。このエフェメラルコンテナには、<code>strace</code>、<code>tcpdump</code>、<code>nsenter</code>、<code>jq</code>、<code>curl</code>といった、通常のアプリケーションイメージには含まれないデバッグ専用ツールを組み込んだカスタムイメージを使用できます。</p>
<h3 class="wp-block-heading">Kubernetesバージョン要件</h3>
<p>エフェメラルコンテナ機能は、Kubernetes 1.23でベータ版、<strong>Kubernetes 1.25</strong>で安定版(GA)となりました。そのため、この機能を利用するには、Kubernetesクラスターがバージョン1.25以降であることが強く推奨されます[1], [2]。</p>
<h3 class="wp-block-heading">必要な権限(RBAC)</h3>
<p><code>kubectl debug</code>でエフェメラルコンテナを作成するには、対象の<code>ServiceAccount</code>が以下のRBAC権限を持っている必要があります。</p>
<div class="codehilite">
<pre data-enlighter-language="generic">rules:
- apiGroups: [""]
resources: ["pods/ephemeralcontainers"]
verbs: ["create"]
</pre>
</div>
<p><code>root</code>権限の扱いに関しては、エフェメラルコンテナ内で実行されるプロセスは、デバッグ対象のコンテナと同様に、コンテナのセキュリティコンテキスト(<code>runAsUser</code>, <code>runAsGroup</code>など)に従います。そのため、カスタムデバッグイメージを構築する際には、可能な限り<code>root</code>以外のユーザーで実行するように設計し、不要な特権昇格を避けるべきです。</p>
<h3 class="wp-block-heading">トラブルシューティングのフロー</h3>
<p><code>kubectl debug --image</code>を用いたトラブルシューティングの一般的なフローは以下の通りです。</p>
<div class="wp-block-merpress-mermaidjs diagram-source-mermaid"><pre class="mermaid">
graph TD
A["問題発生を検知"] --> B{"対象Podの特定"};
B --> C["kubectl debug --imageコマンド実行"];
C --> D["カスタムデバッグイメージの起動"];
D --> E{"デバッグツールの実行"};
E --> F{"ログ/ネットワーク/プロセス解析"};
F -- 問題解決 --> G["Podの正常化"];
F -- 問題未解決 --> E;
G --> H["デバッグコンテナの自動終了"];
</pre></div>
<h2 class="wp-block-heading">実装</h2>
<h3 class="wp-block-heading">デバッグ用カスタムイメージの作成</h3>
<p>まず、デバッグに必要なツール(例: <code>strace</code>, <code>tcpdump</code>, <code>curl</code>, <code>jq</code>) を含むカスタムイメージを作成します。以下は、Debianベースの最小限のデバッグイメージの<code>Dockerfile</code>例です。</p>
<div class="codehilite">
<pre data-enlighter-language="generic"># Dockerfile.debug
FROM debian:stable-slim
LABEL maintainer="Your Name <your.email@example.com>"
# apt-get update & install necessary tools
RUN set -eux; \
apt-get update; \
apt-get install -y --no-install-recommends \
curl \
jq \
iproute2 \
net-tools \
procps \
strace \
tcpdump \
dnsutils \
vim-tiny \
; \
rm -rf /var/lib/apt/lists/*;
# For security, run as non-root user if possible.
# In many debugging scenarios, root is needed for tools like tcpdump/strace,
# but if not, create a user:
# RUN useradd -m debugger && chown -R debugger:debugger /home/debugger
# USER debugger
# WORKDIR /home/debugger
# Default command (optional, kubectl debug can override)
# CMD ["bash"]
</pre>
</div>
<p>このDockerfileをビルドし、コンテナレジストリにプッシュします。</p>
<div class="codehilite">
<pre data-enlighter-language="generic">#!/usr/bin/env bash
set -euo pipefail
# 変数定義
IMAGE_NAME="your-registry.example.com/debug-tools:latest"
DOCKERFILE_PATH="./Dockerfile.debug"
echo "### デバッグ用カスタムイメージのビルドとプッシュを開始します"
docker build -t "${IMAGE_NAME}" -f "${DOCKERFILE_PATH}" .
echo "イメージ ${IMAGE_NAME} をビルドしました。"
# DockerHubやGCR, ACRなどへのログインが必要な場合があります
# docker login your-registry.example.com
echo "イメージ ${IMAGE_NAME} をプッシュします..."
docker push "${IMAGE_NAME}"
echo "イメージ ${IMAGE_NAME} を正常にプッシュしました。"
echo "### カスタムイメージのビルドとプッシュが完了しました。"
</pre>
</div>
<h3 class="wp-block-heading"><code>kubectl debug</code>を用いたデバッグ手順</h3>
<p>以下の<code>bash</code>スクリプトは、サンプルのNginx Podをデプロイし、そのPodに対して<code>kubectl debug --image</code>コマンドでカスタムデバッグコンテナをアタッチする一連の処理を示します。</p>
<div class="codehilite">
<pre data-enlighter-language="generic">#!/usr/bin/env bash
set -euo pipefail
# 一時ディレクトリの作成とクリーンアップ
_tmpdir=$(mktemp -d)
trap 'rm -rf "${_tmpdir}"' EXIT
echo "### kubectl debug --image 実行スクリプト"
# デバッグ用カスタムイメージの名前
CUSTOM_DEBUG_IMAGE="your-registry.example.com/debug-tools:latest" # 上記でプッシュしたイメージに合わせる
# アプリケーションのデプロイ
APP_NAMESPACE="debug-test"
APP_NAME="nginx-app"
DEPLOYMENT_NAME="${APP_NAME}-deployment"
POD_NAME="" # 後で取得
echo "1. 名前空間 ${APP_NAMESPACE} を作成します。"
kubectl create namespace "${APP_NAMESPACE}" || true
echo "2. サンプルNginxアプリケーションをデプロイします。"
cat <<EOF | kubectl apply -n "${APP_NAMESPACE}" -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: ${DEPLOYMENT_NAME}
labels:
app: ${APP_NAME}
spec:
replicas: 1
selector:
matchLabels:
app: ${APP_NAME}
template:
metadata:
labels:
app: ${APP_NAME}
spec:
containers:
- name: nginx
image: nginx:1.23.3-alpine
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: ${APP_NAME}-service
labels:
app: ${APP_NAME}
spec:
selector:
app: ${APP_NAME}
ports:
- protocol: TCP
port: 80
targetPort: 80
EOF
echo "3. Podが起動するまで待機します..."
kubectl wait --for=condition=ready pod -l app=${APP_NAME} -n "${APP_NAMESPACE}" --timeout=120s
# Pod名の取得
POD_NAME=$(kubectl get pod -l app=${APP_NAME} -n "${APP_NAMESPACE}" -o jsonpath='{.items[0].metadata.name}')
echo "ターゲットPod: ${POD_NAME}"
if [[ -z "${POD_NAME}" ]]; then
echo "エラー: ターゲットPodが見つかりませんでした。" >&2
exit 1
fi
# kubectl debug コマンドの実行
echo "4. Pod '${POD_NAME}' にデバッグ用エフェメラルコンテナをアタッチします。"
echo "カスタムイメージ: ${CUSTOM_DEBUG_IMAGE}"
echo "ターゲットコンテナ: nginx"
echo "デバッグセッションを開始します。終了するには 'exit' と入力してください。"
# `--target`で、どのコンテナのnamespaceを共有するかを指定する
# `--container`で、作成するエフェメラルコンテナの名前を指定する
kubectl debug "${POD_NAME}" \
-n "${APP_NAMESPACE}" \
--image="${CUSTOM_DEBUG_IMAGE}" \
--target=nginx \
--container=debug-session \
-- /bin/bash
echo "5. デバッグセッションが終了しました。"
echo "### デバッグ後、リソースをクリーンアップします。"
echo "名前空間 ${APP_NAMESPACE} を削除します。"
kubectl delete namespace "${APP_NAMESPACE}"
echo "スクリプトが完了しました。"
</pre>
</div>
<p>上記のスクリプトを実行すると、Nginx Podの内部にデバッグ用カスタムイメージがアタッチされ、<code>bash</code>シェルが起動します。このシェルから、<code>curl</code>や<code>jq</code>といったツールを使用してPod内部のネットワークや設定を検証できます。</p>
<h3 class="wp-block-heading"><code>curl</code>を用いたネットワーク診断と再試行</h3>
<p>デバッグコンテナ内での<code>curl</code>の使用例です。外部サービスへの接続性や、内部APIの応答を確認する際に役立ちます。TLS証明書の問題を無視したり、ネットワークの一時的な問題に対応するための再試行ロジックを含めます。</p>
<div class="codehilite">
<pre data-enlighter-language="generic">#!/usr/bin/env bash
set -euo pipefail
# 変数定義
TARGET_URL="http://localhost:80" # Nginxコンテナの内部URL
MAX_RETRIES=5
RETRY_DELAY=2 # seconds
TIMEOUT=5 # seconds
echo "### curl を用いたサービスエンドポイントの確認と再試行"
# 外部サービスへの接続確認 (例: Google DNS over HTTPS)
# TARGET_URL="https://dns.google/resolve?name=kubernetes.io&type=A"
# curl コマンド例 (デバッグコンテナ内で実行されることを想定)
# -k: Insecure, TLS証明書の検証をスキップ (開発/デバッグ用途のみ)
# --retry: 指定回数リトライ
# --retry-all-errors: 全てのエラーでリトライ
# --retry-delay: リトライ間の待機時間
# --retry-max-time: リトライの合計時間制限
# --connect-timeout: 接続タイムアウト
# -s: サイレントモード
# -w '%{http_code}\n': HTTPステータスコードのみ出力
# -o /dev/null: 出力を捨てる
# -v: 詳細出力 (デバッグ時のみ)
echo "URL: ${TARGET_URL} への接続を試行します..."
HTTP_STATUS=$(curl \
-k \
--retry ${MAX_RETRIES} \
--retry-all-errors \
--retry-delay ${RETRY_DELAY} \
--retry-max-time $((MAX_RETRIES * RETRY_DELAY + TIMEOUT)) \
--connect-timeout ${TIMEOUT} \
-s \
-w '%{http_code}' \
-o /dev/null \
"${TARGET_URL}")
if [[ "${HTTP_STATUS}" == "200" ]]; then
echo "成功: HTTPステータスコード ${HTTP_STATUS}"
else
echo "失敗: HTTPステータスコード ${HTTP_STATUS}" >&2
exit 1
fi
echo "### curl 処理が完了しました。"
</pre>
</div>
<h3 class="wp-block-heading"><code>jq</code>を用いたJSONデータの処理</h3>
<p><code>kubectl</code>コマンドの出力や、APIからのJSONレスポンスを解析する際に<code>jq</code>は非常に強力です。</p>
<div class="codehilite">
<pre data-enlighter-language="generic">#!/usr/bin/env bash
set -euo pipefail
echo "### jq を用いたJSONデータの処理"
# 例: Podの情報を取得し、jqでフィルタリング
# (デバッグコンテナ内で実行されることを想定)
# この例では、シェルスクリプトからPod情報を取得していますが、
# 実際のデバッグシナリオでは、別のcurlコマンドの出力などをjqで処理します。
echo "現在実行中のデバッグ対象Podの情報を取得します..."
POD_INFO_JSON=$(kubectl get pod "${POD_NAME}" -n "${APP_NAMESPACE}" -o json) # POD_NAMEは前のセクションから引き継ぐ
echo "Pod名: $(echo "${POD_INFO_JSON}" | jq -r '.metadata.name')"
echo "コンテナイメージ:"
echo "${POD_INFO_JSON}" | jq -r '.spec.containers[] | .name + ": " + .image'
echo "Podのステータス:"
echo "${POD_INFO_JSON}" | jq -r '.status.phase'
echo "### jq 処理が完了しました。"
</pre>
</div>
<h2 class="wp-block-heading">検証</h2>
<p>デバッグコンテナが起動したら、実際にツールが機能するか検証します。</p>
<ol class="wp-block-list">
<li><p><strong>ネットワーク接続の検証</strong>: <code>curl <サービス名>:<ポート></code> や <code>ping <IPアドレス></code> を実行し、Pod内部からのネットワーク接続性を確認します。</p>
<div class="codehilite">
<pre data-enlighter-language="generic"># デバッグコンテナのシェル内で実行
curl localhost:80
ping kubernetes.io
</pre>
</div></li>
<li><p><strong>プロセス状態の検証</strong>: <code>ps aux</code> や <code>top</code> で実行中のプロセスを確認します。<code>strace</code>で特定のプロセスのシステムコールを追跡することも可能です。</p>
<div class="codehilite">
<pre data-enlighter-language="generic"># デバッグコンテナのシェル内で実行
ps aux
strace -p $(pgrep nginx) # NginxプロセスのPIDを指定
</pre>
</div></li>
<li><p><strong>ファイルシステム/設定の検証</strong>: <code>ls -l /etc/nginx</code> や <code>cat /etc/nginx/nginx.conf</code> などで設定ファイルの内容を確認します。</p></li>
</ol>
<h2 class="wp-block-heading">運用</h2>
<h3 class="wp-block-heading">カスタムデバッグイメージのセキュリティと管理</h3>
<ul class="wp-block-list">
<li><p><strong>最小限のイメージ</strong>: デバッグイメージは必要最小限のツールのみを含め、サイズを小さく保ちます。これにより、イメージプル時間を短縮し、攻撃サーフェスを減らします。</p></li>
<li><p><strong>定期的な更新</strong>: ベースイメージやツールは定期的に更新し、脆弱性に対応します。CI/CDパイプラインに組み込み、自動でビルド・プッシュするように設定することを推奨します。</p></li>
<li><p><strong>信頼できるレジストリ</strong>: イメージは信頼できるプライベートコンテナレジストリに格納し、アクセスを制限します。</p></li>
</ul>
<h3 class="wp-block-heading">RBACの管理と権限分離</h3>
<p><code>pods/ephemeralcontainers/create</code>権限は強力なため、この権限を付与する対象は最小限に絞るべきです。特定のチームやロールのみに限定し、必要に応じて一時的な権限昇格メカニズムを検討します。</p>
<h3 class="wp-block-heading"><code>systemd</code>を用いた定期的なデバッグ/ヘルスチェック</h3>
<p><code>kubectl debug</code>は対話的なデバッグに優れていますが、特定の状況下で定期的にPodの状態をスナップショットしたり、自動で診断を実行したりする用途には向いていません。このような場合、<code>systemd</code>の<code>Unit</code>と<code>Timer</code>を組み合わせて、クラスター外の管理サーバーから<code>kubectl</code>コマンドを実行することが考えられます。</p>
<p>以下は、特定のPodのログを定期的に収集する<code>systemd</code>の例です。</p>
<p><strong>1. <code>debug-log-collector.service</code></strong></p>
<div class="codehilite">
<pre data-enlighter-language="generic"># /etc/systemd/system/debug-log-collector.service
[Unit]
Description=Kubernetes Pod Log Collector for Debugging
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
Environment="KUBECONFIG=/root/.kube/config" # kubeconfigのパスを適切に設定
ExecStart=/usr/local/bin/collect-pod-logs.sh
StandardOutput=journal
StandardError=journal
User=root # kubeconfigへのアクセス権を持つユーザー
Group=root
</pre>
</div>
<p><strong>2. <code>debug-log-collector.timer</code></strong></p>
<div class="codehilite">
<pre data-enlighter-language="generic"># /etc/systemd/system/debug-log-collector.timer
[Unit]
Description=Run Kubernetes Pod Log Collector every 30 minutes
[Timer]
# OnCalendar=*-*-* *:0/30:00 # 30分ごとに実行
OnBootSec=5min # 起動後5分で初回実行
OnUnitActiveSec=30min # ユニットがアクティブになった後30分ごとに実行
Persistent=true # タイマーが停止しても次回起動時に前回のスケジュールを引き継ぐ
[Install]
WantedBy=timers.target
</pre>
</div>
<p><strong>3. <code>/usr/local/bin/collect-pod-logs.sh</code> (実行スクリプト)</strong></p>
<div class="codehilite">
<pre data-enlighter-language="generic">#!/usr/bin/env bash
set -euo pipefail
# 一時ディレクトリの作成とクリーンアップ
_tmpdir=$(mktemp -d)
trap 'rm -rf "${_tmpdir}"' EXIT
LOG_DIR="/var/log/kubernetes-debug"
NAMESPACE="target-namespace" # ターゲットの名前空間
POD_LABEL="app=problematic-app" # ターゲットのPodラベル
mkdir -p "${LOG_DIR}"
echo "$(date '+%Y-%m-%d %H:%M:%S JST') - Collecting logs for pods in namespace ${NAMESPACE} with label ${POD_LABEL}" | tee -a "${LOG_DIR}/collection.log"
POD_NAMES=$(kubectl get pods -n "${NAMESPACE}" -l "${POD_LABEL}" -o jsonpath='{.items[*].metadata.name}')
if [[ -z "${POD_NAMES}" ]]; then
echo "No pods found with label ${POD_LABEL} in namespace ${NAMESPACE}." | tee -a "${LOG_DIR}/collection.log"
exit 0
fi
for POD in ${POD_NAMES}; do
echo " - Collecting logs for pod: ${POD}" | tee -a "${LOG_DIR}/collection.log"
LOG_FILE="${LOG_DIR}/${POD}_$(date '+%Y%m%d%H%M%S').log"
kubectl logs "${POD}" -n "${NAMESPACE}" --tail=100 > "${LOG_FILE}" 2>&1
echo " Logs saved to ${LOG_FILE}" | tee -a "${LOG_DIR}/collection.log"
done
echo "Log collection completed." | tee -a "${LOG_DIR}/collection.log"
</pre>
</div>
<p><strong>Systemdの設定と起動</strong></p>
<div class="codehilite">
<pre data-enlighter-language="generic"># スクリプトに実行権限を付与
chmod +x /usr/local/bin/collect-pod-logs.sh
# systemdユニットとタイマーをリロード
systemctl daemon-reload
# タイマーを有効化し、すぐに開始
systemctl enable --now debug-log-collector.timer
# ステータスの確認
systemctl status debug-log-collector.timer
systemctl status debug-log-collector.service
# ログの確認
journalctl -u debug-log-collector.service
</pre>
</div>
<p>この<code>systemd</code>設定により、管理サーバーは指定された間隔でターゲットPodのログを自動的に収集し、<code>/var/log/kubernetes-debug</code>に保存します。これは、断続的な問題の診断や、特定の状態変化の追跡に有効です。</p>
<h2 class="wp-block-heading">トラブルシューティング</h2>
<ul class="wp-block-list">
<li><p><strong><code>Error from server (Forbidden): pods "..." is forbidden: User "..." cannot create ephemeralcontainers</code></strong>: RBAC権限が不足しています。<code>pods/ephemeralcontainers/create</code>権限を付与してください。</p></li>
<li><p><strong><code>Error: unknown flag: --image</code></strong>: Kubernetesクラスターまたは<code>kubectl</code>クライアントのバージョンが古く、エフェメラルコンテナ機能がサポートされていない可能性があります。Kubernetes 1.25以降にアップグレードしてください。</p></li>
<li><p><strong><code>Error: container "debug-session" not found</code> (or similar image pull errors)</strong>: カスタムイメージの名前が間違っている、イメージレジストリにアクセスできない、またはイメージが存在しない可能性があります。<code>docker pull your-registry.example.com/debug-tools:latest</code>などで手動でプルを試行し、問題がイメージ側にあるか確認してください。</p></li>
<li><p><strong><code>The connection to the server localhost:8080 was refused - did you specify the right host or port?</code></strong>: <code>kubeconfig</code>の設定が正しくないか、<code>KUBECONFIG</code>環境変数が指すファイルにクラスター情報がない可能性があります。</p></li>
</ul>
<h2 class="wp-block-heading">まとめ</h2>
<p><code>kubectl debug --image</code>は、Kubernetes環境における複雑なトラブルシューティングを劇的に効率化する強力なツールです。カスタムデバッグイメージを活用することで、アプリケーションのコンテナイメージを変更することなく、必要なデバッグツールを動的にPodに注入できます。</p>
<p>本記事で紹介した安全な<code>bash</code>スクリプトの書き方、<code>jq</code>や<code>curl</code>といったツールの活用、そして<code>systemd</code>による運用自動化のヒントは、DevOpsエンジニアがKubernetesクラスターの安定稼働を維持するための強力な基盤となるでしょう。セキュリティと権限管理に注意を払いながら、これらのテクニックを積極的に活用してください。</p>
<hr/>
<p><strong>参考文献:</strong></p>
<p>[1] Kubernetes Authors / CNCF. “Debugging with Ephemeral Containers”. Kubernetes Documentation. (最終アクセス日: 2024-07-30). URL: https://kubernetes.io/docs/tasks/debug/debug-application/debug-running-pod/#debugging-with-ephemeral-containers</p>
<p>[2] Kubernetes Authors / CNCF. “Kubernetes 1.25: Ephemeral Containers Graduate to Stable”. Kubernetes Blog. 2022-08-23 JST. URL: https://kubernetes.io/blog/2022/08/23/kubernetes-v1-25-release-announcement/#ephemeral-containers</p>
本記事はGeminiの出力をプロンプト工学で整理した業務ドラフト(未検証)です。
kubectl debug –image カスタムイメージを活用したKubernetesトラブルシューティング
Kubernetes環境で稼働するアプリケーションのトラブルシューティングは、多くの場合、コンテナ内部にアクセスして状態を確認する必要があります。kubectl execは手軽な方法ですが、デバッグに必要なツールがコンテナイメージに含まれていない場合、解決は困難になります。
そこで役立つのが、kubectl debugコマンドの--imageオプションです。この機能は、トラブルシューティング専用のカスタムイメージを一時的に稼働中のPodにアタッチし、高度なデバッグツールを利用可能にします。本記事では、kubectl debug --imageの活用方法、安全なスクリプト実装、および運用上の考慮事項について解説します。
要件と前提
kubectl debugとエフェメラルコンテナの概要
kubectl debugは、Podのトラブルシューティングを目的としたコマンドです。特に--imageオプションを利用すると、対象Podと同じネットワークおよびプロセス名前空間を共有する「エフェメラルコンテナ(一時コンテナ)」を一時的に追加できます。このエフェメラルコンテナには、strace、tcpdump、nsenter、jq、curlといった、通常のアプリケーションイメージには含まれないデバッグ専用ツールを組み込んだカスタムイメージを使用できます。
Kubernetesバージョン要件
エフェメラルコンテナ機能は、Kubernetes 1.23でベータ版、Kubernetes 1.25で安定版(GA)となりました。そのため、この機能を利用するには、Kubernetesクラスターがバージョン1.25以降であることが強く推奨されます[1], [2]。
必要な権限(RBAC)
kubectl debugでエフェメラルコンテナを作成するには、対象のServiceAccountが以下のRBAC権限を持っている必要があります。
rules:
- apiGroups: [""]
resources: ["pods/ephemeralcontainers"]
verbs: ["create"]
root権限の扱いに関しては、エフェメラルコンテナ内で実行されるプロセスは、デバッグ対象のコンテナと同様に、コンテナのセキュリティコンテキスト(runAsUser, runAsGroupなど)に従います。そのため、カスタムデバッグイメージを構築する際には、可能な限りroot以外のユーザーで実行するように設計し、不要な特権昇格を避けるべきです。
トラブルシューティングのフロー
kubectl debug --imageを用いたトラブルシューティングの一般的なフローは以下の通りです。
graph TD
A["問題発生を検知"] --> B{"対象Podの特定"};
B --> C["kubectl debug --imageコマンド実行"];
C --> D["カスタムデバッグイメージの起動"];
D --> E{"デバッグツールの実行"};
E --> F{"ログ/ネットワーク/プロセス解析"};
F -- 問題解決 --> G["Podの正常化"];
F -- 問題未解決 --> E;
G --> H["デバッグコンテナの自動終了"];
実装
デバッグ用カスタムイメージの作成
まず、デバッグに必要なツール(例: strace, tcpdump, curl, jq) を含むカスタムイメージを作成します。以下は、Debianベースの最小限のデバッグイメージのDockerfile例です。
# Dockerfile.debug
FROM debian:stable-slim
LABEL maintainer="Your Name <your.email@example.com>"
# apt-get update & install necessary tools
RUN set -eux; \
apt-get update; \
apt-get install -y --no-install-recommends \
curl \
jq \
iproute2 \
net-tools \
procps \
strace \
tcpdump \
dnsutils \
vim-tiny \
; \
rm -rf /var/lib/apt/lists/*;
# For security, run as non-root user if possible.
# In many debugging scenarios, root is needed for tools like tcpdump/strace,
# but if not, create a user:
# RUN useradd -m debugger && chown -R debugger:debugger /home/debugger
# USER debugger
# WORKDIR /home/debugger
# Default command (optional, kubectl debug can override)
# CMD ["bash"]
このDockerfileをビルドし、コンテナレジストリにプッシュします。
#!/usr/bin/env bash
set -euo pipefail
# 変数定義
IMAGE_NAME="your-registry.example.com/debug-tools:latest"
DOCKERFILE_PATH="./Dockerfile.debug"
echo "### デバッグ用カスタムイメージのビルドとプッシュを開始します"
docker build -t "${IMAGE_NAME}" -f "${DOCKERFILE_PATH}" .
echo "イメージ ${IMAGE_NAME} をビルドしました。"
# DockerHubやGCR, ACRなどへのログインが必要な場合があります
# docker login your-registry.example.com
echo "イメージ ${IMAGE_NAME} をプッシュします..."
docker push "${IMAGE_NAME}"
echo "イメージ ${IMAGE_NAME} を正常にプッシュしました。"
echo "### カスタムイメージのビルドとプッシュが完了しました。"
kubectl debugを用いたデバッグ手順
以下のbashスクリプトは、サンプルのNginx Podをデプロイし、そのPodに対してkubectl debug --imageコマンドでカスタムデバッグコンテナをアタッチする一連の処理を示します。
#!/usr/bin/env bash
set -euo pipefail
# 一時ディレクトリの作成とクリーンアップ
_tmpdir=$(mktemp -d)
trap 'rm -rf "${_tmpdir}"' EXIT
echo "### kubectl debug --image 実行スクリプト"
# デバッグ用カスタムイメージの名前
CUSTOM_DEBUG_IMAGE="your-registry.example.com/debug-tools:latest" # 上記でプッシュしたイメージに合わせる
# アプリケーションのデプロイ
APP_NAMESPACE="debug-test"
APP_NAME="nginx-app"
DEPLOYMENT_NAME="${APP_NAME}-deployment"
POD_NAME="" # 後で取得
echo "1. 名前空間 ${APP_NAMESPACE} を作成します。"
kubectl create namespace "${APP_NAMESPACE}" || true
echo "2. サンプルNginxアプリケーションをデプロイします。"
cat <<EOF | kubectl apply -n "${APP_NAMESPACE}" -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: ${DEPLOYMENT_NAME}
labels:
app: ${APP_NAME}
spec:
replicas: 1
selector:
matchLabels:
app: ${APP_NAME}
template:
metadata:
labels:
app: ${APP_NAME}
spec:
containers:
- name: nginx
image: nginx:1.23.3-alpine
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: ${APP_NAME}-service
labels:
app: ${APP_NAME}
spec:
selector:
app: ${APP_NAME}
ports:
- protocol: TCP
port: 80
targetPort: 80
EOF
echo "3. Podが起動するまで待機します..."
kubectl wait --for=condition=ready pod -l app=${APP_NAME} -n "${APP_NAMESPACE}" --timeout=120s
# Pod名の取得
POD_NAME=$(kubectl get pod -l app=${APP_NAME} -n "${APP_NAMESPACE}" -o jsonpath='{.items[0].metadata.name}')
echo "ターゲットPod: ${POD_NAME}"
if [[ -z "${POD_NAME}" ]]; then
echo "エラー: ターゲットPodが見つかりませんでした。" >&2
exit 1
fi
# kubectl debug コマンドの実行
echo "4. Pod '${POD_NAME}' にデバッグ用エフェメラルコンテナをアタッチします。"
echo "カスタムイメージ: ${CUSTOM_DEBUG_IMAGE}"
echo "ターゲットコンテナ: nginx"
echo "デバッグセッションを開始します。終了するには 'exit' と入力してください。"
# `--target`で、どのコンテナのnamespaceを共有するかを指定する
# `--container`で、作成するエフェメラルコンテナの名前を指定する
kubectl debug "${POD_NAME}" \
-n "${APP_NAMESPACE}" \
--image="${CUSTOM_DEBUG_IMAGE}" \
--target=nginx \
--container=debug-session \
-- /bin/bash
echo "5. デバッグセッションが終了しました。"
echo "### デバッグ後、リソースをクリーンアップします。"
echo "名前空間 ${APP_NAMESPACE} を削除します。"
kubectl delete namespace "${APP_NAMESPACE}"
echo "スクリプトが完了しました。"
上記のスクリプトを実行すると、Nginx Podの内部にデバッグ用カスタムイメージがアタッチされ、bashシェルが起動します。このシェルから、curlやjqといったツールを使用してPod内部のネットワークや設定を検証できます。
curlを用いたネットワーク診断と再試行
デバッグコンテナ内でのcurlの使用例です。外部サービスへの接続性や、内部APIの応答を確認する際に役立ちます。TLS証明書の問題を無視したり、ネットワークの一時的な問題に対応するための再試行ロジックを含めます。
#!/usr/bin/env bash
set -euo pipefail
# 変数定義
TARGET_URL="http://localhost:80" # Nginxコンテナの内部URL
MAX_RETRIES=5
RETRY_DELAY=2 # seconds
TIMEOUT=5 # seconds
echo "### curl を用いたサービスエンドポイントの確認と再試行"
# 外部サービスへの接続確認 (例: Google DNS over HTTPS)
# TARGET_URL="https://dns.google/resolve?name=kubernetes.io&type=A"
# curl コマンド例 (デバッグコンテナ内で実行されることを想定)
# -k: Insecure, TLS証明書の検証をスキップ (開発/デバッグ用途のみ)
# --retry: 指定回数リトライ
# --retry-all-errors: 全てのエラーでリトライ
# --retry-delay: リトライ間の待機時間
# --retry-max-time: リトライの合計時間制限
# --connect-timeout: 接続タイムアウト
# -s: サイレントモード
# -w '%{http_code}\n': HTTPステータスコードのみ出力
# -o /dev/null: 出力を捨てる
# -v: 詳細出力 (デバッグ時のみ)
echo "URL: ${TARGET_URL} への接続を試行します..."
HTTP_STATUS=$(curl \
-k \
--retry ${MAX_RETRIES} \
--retry-all-errors \
--retry-delay ${RETRY_DELAY} \
--retry-max-time $((MAX_RETRIES * RETRY_DELAY + TIMEOUT)) \
--connect-timeout ${TIMEOUT} \
-s \
-w '%{http_code}' \
-o /dev/null \
"${TARGET_URL}")
if [[ "${HTTP_STATUS}" == "200" ]]; then
echo "成功: HTTPステータスコード ${HTTP_STATUS}"
else
echo "失敗: HTTPステータスコード ${HTTP_STATUS}" >&2
exit 1
fi
echo "### curl 処理が完了しました。"
jqを用いたJSONデータの処理
kubectlコマンドの出力や、APIからのJSONレスポンスを解析する際にjqは非常に強力です。
#!/usr/bin/env bash
set -euo pipefail
echo "### jq を用いたJSONデータの処理"
# 例: Podの情報を取得し、jqでフィルタリング
# (デバッグコンテナ内で実行されることを想定)
# この例では、シェルスクリプトからPod情報を取得していますが、
# 実際のデバッグシナリオでは、別のcurlコマンドの出力などをjqで処理します。
echo "現在実行中のデバッグ対象Podの情報を取得します..."
POD_INFO_JSON=$(kubectl get pod "${POD_NAME}" -n "${APP_NAMESPACE}" -o json) # POD_NAMEは前のセクションから引き継ぐ
echo "Pod名: $(echo "${POD_INFO_JSON}" | jq -r '.metadata.name')"
echo "コンテナイメージ:"
echo "${POD_INFO_JSON}" | jq -r '.spec.containers[] | .name + ": " + .image'
echo "Podのステータス:"
echo "${POD_INFO_JSON}" | jq -r '.status.phase'
echo "### jq 処理が完了しました。"
検証
デバッグコンテナが起動したら、実際にツールが機能するか検証します。
ネットワーク接続の検証: curl <サービス名>:<ポート> や ping <IPアドレス> を実行し、Pod内部からのネットワーク接続性を確認します。
# デバッグコンテナのシェル内で実行
curl localhost:80
ping kubernetes.io
プロセス状態の検証: ps aux や top で実行中のプロセスを確認します。straceで特定のプロセスのシステムコールを追跡することも可能です。
# デバッグコンテナのシェル内で実行
ps aux
strace -p $(pgrep nginx) # NginxプロセスのPIDを指定
ファイルシステム/設定の検証: ls -l /etc/nginx や cat /etc/nginx/nginx.conf などで設定ファイルの内容を確認します。
運用
カスタムデバッグイメージのセキュリティと管理
最小限のイメージ: デバッグイメージは必要最小限のツールのみを含め、サイズを小さく保ちます。これにより、イメージプル時間を短縮し、攻撃サーフェスを減らします。
定期的な更新: ベースイメージやツールは定期的に更新し、脆弱性に対応します。CI/CDパイプラインに組み込み、自動でビルド・プッシュするように設定することを推奨します。
信頼できるレジストリ: イメージは信頼できるプライベートコンテナレジストリに格納し、アクセスを制限します。
RBACの管理と権限分離
pods/ephemeralcontainers/create権限は強力なため、この権限を付与する対象は最小限に絞るべきです。特定のチームやロールのみに限定し、必要に応じて一時的な権限昇格メカニズムを検討します。
systemdを用いた定期的なデバッグ/ヘルスチェック
kubectl debugは対話的なデバッグに優れていますが、特定の状況下で定期的にPodの状態をスナップショットしたり、自動で診断を実行したりする用途には向いていません。このような場合、systemdのUnitとTimerを組み合わせて、クラスター外の管理サーバーからkubectlコマンドを実行することが考えられます。
以下は、特定のPodのログを定期的に収集するsystemdの例です。
1. debug-log-collector.service
# /etc/systemd/system/debug-log-collector.service
[Unit]
Description=Kubernetes Pod Log Collector for Debugging
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
Environment="KUBECONFIG=/root/.kube/config" # kubeconfigのパスを適切に設定
ExecStart=/usr/local/bin/collect-pod-logs.sh
StandardOutput=journal
StandardError=journal
User=root # kubeconfigへのアクセス権を持つユーザー
Group=root
2. debug-log-collector.timer
# /etc/systemd/system/debug-log-collector.timer
[Unit]
Description=Run Kubernetes Pod Log Collector every 30 minutes
[Timer]
# OnCalendar=*-*-* *:0/30:00 # 30分ごとに実行
OnBootSec=5min # 起動後5分で初回実行
OnUnitActiveSec=30min # ユニットがアクティブになった後30分ごとに実行
Persistent=true # タイマーが停止しても次回起動時に前回のスケジュールを引き継ぐ
[Install]
WantedBy=timers.target
3. /usr/local/bin/collect-pod-logs.sh (実行スクリプト)
#!/usr/bin/env bash
set -euo pipefail
# 一時ディレクトリの作成とクリーンアップ
_tmpdir=$(mktemp -d)
trap 'rm -rf "${_tmpdir}"' EXIT
LOG_DIR="/var/log/kubernetes-debug"
NAMESPACE="target-namespace" # ターゲットの名前空間
POD_LABEL="app=problematic-app" # ターゲットのPodラベル
mkdir -p "${LOG_DIR}"
echo "$(date '+%Y-%m-%d %H:%M:%S JST') - Collecting logs for pods in namespace ${NAMESPACE} with label ${POD_LABEL}" | tee -a "${LOG_DIR}/collection.log"
POD_NAMES=$(kubectl get pods -n "${NAMESPACE}" -l "${POD_LABEL}" -o jsonpath='{.items[*].metadata.name}')
if [[ -z "${POD_NAMES}" ]]; then
echo "No pods found with label ${POD_LABEL} in namespace ${NAMESPACE}." | tee -a "${LOG_DIR}/collection.log"
exit 0
fi
for POD in ${POD_NAMES}; do
echo " - Collecting logs for pod: ${POD}" | tee -a "${LOG_DIR}/collection.log"
LOG_FILE="${LOG_DIR}/${POD}_$(date '+%Y%m%d%H%M%S').log"
kubectl logs "${POD}" -n "${NAMESPACE}" --tail=100 > "${LOG_FILE}" 2>&1
echo " Logs saved to ${LOG_FILE}" | tee -a "${LOG_DIR}/collection.log"
done
echo "Log collection completed." | tee -a "${LOG_DIR}/collection.log"
Systemdの設定と起動
# スクリプトに実行権限を付与
chmod +x /usr/local/bin/collect-pod-logs.sh
# systemdユニットとタイマーをリロード
systemctl daemon-reload
# タイマーを有効化し、すぐに開始
systemctl enable --now debug-log-collector.timer
# ステータスの確認
systemctl status debug-log-collector.timer
systemctl status debug-log-collector.service
# ログの確認
journalctl -u debug-log-collector.service
このsystemd設定により、管理サーバーは指定された間隔でターゲットPodのログを自動的に収集し、/var/log/kubernetes-debugに保存します。これは、断続的な問題の診断や、特定の状態変化の追跡に有効です。
トラブルシューティング
Error from server (Forbidden): pods "..." is forbidden: User "..." cannot create ephemeralcontainers: RBAC権限が不足しています。pods/ephemeralcontainers/create権限を付与してください。
Error: unknown flag: --image: Kubernetesクラスターまたはkubectlクライアントのバージョンが古く、エフェメラルコンテナ機能がサポートされていない可能性があります。Kubernetes 1.25以降にアップグレードしてください。
Error: container "debug-session" not found (or similar image pull errors): カスタムイメージの名前が間違っている、イメージレジストリにアクセスできない、またはイメージが存在しない可能性があります。docker pull your-registry.example.com/debug-tools:latestなどで手動でプルを試行し、問題がイメージ側にあるか確認してください。
The connection to the server localhost:8080 was refused - did you specify the right host or port?: kubeconfigの設定が正しくないか、KUBECONFIG環境変数が指すファイルにクラスター情報がない可能性があります。
まとめ
kubectl debug --imageは、Kubernetes環境における複雑なトラブルシューティングを劇的に効率化する強力なツールです。カスタムデバッグイメージを活用することで、アプリケーションのコンテナイメージを変更することなく、必要なデバッグツールを動的にPodに注入できます。
本記事で紹介した安全なbashスクリプトの書き方、jqやcurlといったツールの活用、そしてsystemdによる運用自動化のヒントは、DevOpsエンジニアがKubernetesクラスターの安定稼働を維持するための強力な基盤となるでしょう。セキュリティと権限管理に注意を払いながら、これらのテクニックを積極的に活用してください。
参考文献:
[1] Kubernetes Authors / CNCF. “Debugging with Ephemeral Containers”. Kubernetes Documentation. (最終アクセス日: 2024-07-30). URL: https://kubernetes.io/docs/tasks/debug/debug-application/debug-running-pod/#debugging-with-ephemeral-containers
[2] Kubernetes Authors / CNCF. “Kubernetes 1.25: Ephemeral Containers Graduate to Stable”. Kubernetes Blog. 2022-08-23 JST. URL: https://kubernetes.io/blog/2022/08/23/kubernetes-v1-25-release-announcement/#ephemeral-containers
コメント