kubectl の TLS handshake timeout の原因を追ったら、原因は Kubernetes ではなく MTU だった話
はじめに
MicroK8s 環境で突然 `kubectl` が使えなくなりました。
実行すると:
kubectl get nodes以下のエラーになります。
couldn't get current server API group list:
Get "https://192.168.101.13:16443/api?timeout=32s":
net/http: TLS handshake timeout最初は、
MicroK8s の API Server が落ちた?
証明書がおかしい?
kubeconfig が壊れた?
kubectl のバージョン問題?
あたりを疑いました。
しかし、最終的な原因は Wi-Fi 経由の MTU 問題でした。
環境
今回の環境は以下です。
クライアント側
Ubuntu 24.04
ThinkCentre M75q Gen2
Wi-Fi 接続
kubectl v1.33.13
ネットワーク:
wlp2s0
IP: 192.168.11.225Kubernetes側
MicroK8s
API Server
https://192.168.101.13:16443最初の切り分け
TCP接続確認
まずポート自体が開いているか確認しました。
curl -Ik https://192.168.101.13:16443/api結果:
HTTP/2 401401 は認証エラーなので、逆に言えば:
TCP接続できている
HTTPSサーバーは応答している
ということです。
証明書確認
次に TLS 証明書を疑いました。
openssl で確認:
openssl s_client \
-connect 192.168.101.13:16443結果:
TLSv1.3
Cipher is TLS_CHACHA20_POLY1305_SHA256さらにクライアント証明書を指定すると:
Verification: OKになりました。
つまり:
サーバー証明書 OK
CA OK
クライアント証明書 OK
mTLS OK
です。
kubectl だけ失敗する謎
ここで疑問が出ました。
なぜ:
| コマンド | 結果 |
| ------- | -- |
| curl | 成功 |
| openssl | 成功 |
| kubectl | 失敗 |
なのか?
kubectl は Go の TLS 実装を使っています。
そのため、
「OpenSSLでは成功するが、Go TLSでは失敗する」
という状態でした。
tcpdump で決定的な証拠
原因特定のため tcpdump を取得しました。
sudo tcpdump \
-i wlp2s0 \
host 192.168.101.13 and port 16443 \
-nn -vvすると異常な通信が見えました。
例:
192.168.11.225 > 192.168.101.13
length 1500
seq 1:1449その後:
SACK {1449:1472}が返っています。
これはサーバー側が、
「1449〜1472 byte のデータが届いていない」
と言っている状態です。
さらに同じパケットを何度も再送しています。
つまり:
TCP接続は成功
TLS ClientHello送信
途中の大きなパケットが欠落
TLS handshake 完了できない
という流れでした。
なぜ curl は成功して kubectl は失敗したのか
原因は TLS ClientHello のサイズ差でした。
kubectl は Kubernetes API に接続するため:
HTTP/2
ALPN
TLS拡張
証明書情報
などを含みます。
その結果、最初の TLS パケットが大きくなります。
一方:
curl
openssl
は条件によって小さいパケットで済み、問題が表面化しませんでした。
解決方法
Wi-Fi インターフェースの MTU を下げました。
変更前:
MTU 1500変更:
sudo ip link set dev wlp2s0 mtu 1400その後:
kubectl get nodes結果:
NAME STATUS ROLES AGE VERSION
microk8s Ready <none> 15d v1.33.13無事復旧しました。
途中で試したが効果がなかったもの
kubectl バージョン変更
kubectl:
v1.32.9
↓
v1.33.13へ更新しました。
しかし原因ではありませんでした。
HTTP/2無効化
GODEBUG=http2client=0 kubectl get nodesも試しました。
改善なし。
TLS 1.3無効化
GODEBUG=tls13=0 kubectl get nodesも改善なし。
NICオフロード無効化
以下も試しました。
sudo ethtool -K wlp2s0 tso off gso off gro offしかし改善しませんでした。
これは NIC 内部の処理を変える設定であり、今回の問題だった経路 MTU には影響しませんでした。
最終的には元に戻しました。
sudo ethtool -K wlp2s0 tso on gso on gro on今回学んだこと
TLS handshake timeout でも TLS が原因とは限らない
エラーを見ると、
TLS handshake timeoutなので証明書や暗号方式を疑いました。
しかし実際には:
TLS開始
↓
大きなパケット送信
↓
MTU超過
↓
パケット欠落
↓
TLS完了できないというネットワーク問題でした。
Kubernetes系トラブルは下位層から見る
今回の調査順:
kubectlエラー確認
curl確認
openssl確認
証明書確認
tcpdump確認
MTU変更
という流れでした。
Kubernetes の問題に見えても、
TCP
MTU
Firewall
Routing
など下位レイヤーを見ることが重要だと再認識しました。
まとめ
MicroK8s の `kubectl TLS handshake timeout` は、
「API Serverが死んでいる」
とは限りません。
今回の原因は:
Wi-Fi経由のMTU問題により、kubectl の TLS ClientHello パケットが正常に通らなかったこと
でした。
解決した設定:
sudo ip link set dev wlp2s0 mtu 1400でした。
同じように、
curl は通る
openssl は通る
kubectl だけ TLS handshake timeout
という症状が出た場合は、MTUを疑う価値があります。