コンテキスト
デプロイ作業において、kubectl コマンドが応答を停止(ハング)することほど、作業を停滞させるものはありません。最近、トラフィックが急増した本番環境のクラスターを管理していた際に、この問題に直面しました。期待していたリソース一覧の代わりに表示されたのは、Unable to connect to the server: context deadline exceeded というメッセージでした。これは要するにタイムアウトです。kubectl が API サーバーからのレスポンスを待機したものの、制限時間に達してしまったことを意味します。
これは通常、通信経路のどこかに障害があることを示しています。コントロールプレーンの負荷飽和であれ、単純なファイアウォールの設定ミスであれ、解決には体系的なアプローチが必要です。ボトルネックを特定し、解決するために私が使用しているワークフローを以下に紹介します。
デバッグプロセス
1. 詳細ログの出力レベルを上げる
まずは内部で何が起きているかを確認します。標準出力では実際のハンドシェイクが隠されてしまうため、-v フラグを使用してリクエストの詳細を表示します。
kubectl get pods -v=9
レベル 9 では、リクエストに相当する curl コマンドが出力されます。IP アドレスに注目してください。もし VPN が切断されているのに kubectl がプライベートな 10.x.x.x アドレスに接続しようとしているなら、エラーの原因は一目瞭然です。また、古い kubeconfig が削除済みのロードバランサーの DNS 名を指している場合にも、この現象が発生することがあります。
2. ネットワーク経路のテスト
IP アドレスが正しい場合は、ローカルマシンからポート 6443 に実際に到達できるかを確認します。手軽なチェックには nc (netcat) が便利です。
nc -zv <api-server-ip> 6443
ここで接続に失敗した場合は、ネットワークレイヤーに問題があります。これは多くの場合、AWS セキュリティグループに現在の IP が登録されていないか、企業内ファイアウォールが非標準ポートのアウトバウンドトラフィックをブロックしていることが原因です。直ちに 'Connection Refused' が返される場合は、サーバーは稼働していますが接続を拒否しています。ハングする場合は、パケットが完全にドロップされています。
3. コントロールプレーンのヘルスチェック
マスターノードに SSH アクセスできる場合は、kube-apiserver コンテナが実際に正常かどうかを確認します。kubeadm クラスターでは、スタティック Pod を直接調査できます。
sudo crictl ps | grep kube-apiserver
sudo crictl logs <container-id> --tail 50
"etcdserver: request timed out" や OOMKilled イベントなどの特定のパターンを探します。API サーバーがクラッシュループに陥っている場合、クライアントのリクエストに応答するためのリソースが不足します。
解決策
解決策 1: プロキシと環境変数の確認
ローカルの環境変数が原因であることはよくあります。企業の環境では、kubectl が誤って内部クラスターのトラフィックを Web プロキシ経由でルーティングしてしまうことがあります。プロキシは内部クラスターネットワークに到達できないため、多くの場合タイムアウトが発生します。
# テストのために一時的にプロキシを解除
unset http_proxy
unset https_proxy
# あるいは API サーバーの IP が除外されていることを確認
export no_proxy=$no_proxy,10.96.0.1,<cluster-api-ip>
解決策 2: Etcd のレイテンシの解消
API サーバーの速度は etcd に依存します。etcd が書き込みのコミットに 100ms 以上かかる場合、API サーバーはクライアントへの応答をタイムアウトさせる可能性があります。ログに "apply entries took too long" という警告が出ていないか確認してください。
私は以前、etcd のデータディレクトリを NVMe SSD に移動することで、持続的なタイムアウト問題を解決したことがあります。AWS を使用している場合は、EBS ボリュームが gp3 で、少なくとも 3,000 IOPS がプロビジョニングされていることを確認してください。高いディスク待機時間は、Kubernetes のレスポンスを損なう隠れた原因となります。
解決策 3: ロードバランサーのアイドルタイムアウトの調整
クラスターが AWS ELB や Nginx Ingress の背後にある場合、ロードバランサーが接続を早期に切断している可能性があります。これは kubectl logs -f や kubectl exec のような長時間実行されるコマンドでよく発生します。
ロードバランサーのアイドルタイムアウトを 300 秒以上に設定してください。Nginx ベースのセットアップの場合は、Ingress の設定に以下のアノテーションを追加します:
nginx.ingress.kubernetes.io/proxy-connect-timeout: "600"
nginx.ingress.kubernetes.io/proxy-read-timeout: "600"
解決策 4: リソース不足への対処
CPU 使用率が 95% に達しているマスターノードは、最終的にリクエストをドロップします。マスターノードで top や htop を使用して、リソースの競合を確認してください。ある事例では、暴走したロギングエージェントが 4GB の RAM を消費し、kube-apiserver がディスクにスワップしてしまっていました。エージェントのリソース制限を設定したところ、context deadline exceeded エラーは即座に解消されました。
検証
修正を適用した後、軽量なコマンドを実行して接続を確認します:
kubectl version
それが成功したら、接続の安定性を確認するためにより重い操作をテストします:
kubectl get pods -A
リストが 1 〜 2 秒以内に表示されれば、API サーバーは再び正常な状態に戻っています。
学んだ教訓
- レイヤーを切り分ける:
-v=9を使用して、タイムアウトが自分の PC からリクエストが送信される前に起きているのか、それともサーバーの応答を待っている間に起きているのかを判断します。 - Etcd メトリクスを監視する:
etcd_disk_wal_fsync_duration_secondsを注視してください。この値が急上昇すると、API サーバーは必然的に遅延します。 - VPN に注意する: リモートワークでは、不安定なトンネル接続が原因になることがよくあります。API サーバーへの継続的な ping でパケットロスが発生する場合、ローカルネットワークがボトルネックである可能性が高いです。

