Kubernetes クラスタのトラブルシュートで、ssh でノードに直接アクセスしてデバッグしたい場面があります。しかし、OS アカウントやネットワーク管理の都合で、ssh の利用が難しい場合があります。そんなとき、 kubectl debug node を利用すると、ssh に相当する操作をkubectl で簡単に行えます。
デモ
kubectl debug node でノードに Pod を作成し、ホスト上のコマンド(systemctl, htop) を実行したデモです。
kubectl debug node の使い方
kubectl debug pod (Ephemeral Containers) と違い、kubectl debug node は特権を持った Pod を作成しているだけなので、権限さえあれば Kubernetes クラスタ側の設定は不要です。
使い方は非常にシンプルで、ノード名と利用するコンテナイメージを指定するだけです。kubectl exec のように -it を指定すると、インタラクティブに操作できます。なお実行には、クラスタの管理者権限 (RBAC の cluster-admin 相当) が必要になります。
以下は busybox イメージを指定した例です。後述のように chroot でホストのコマンド(シェル)を利用する場合は、busybox など軽量なイメージがおすすめです。
$ kubectl debug node/<NODE_NAME> -it --image=busybox
上記を実行後、kubectl exec のように Pod のシェルをそのまま操作できるようになります。/host 以下にノードのルートディレクトリがマウントされているため、ノード上のファイルシステム全てにアクセスできます。
root@<NODE_NAME>:/# ls /host
# ノードのルートディレクトリが表示される
bin initrd.img media run tmp
boot initrd.img.old mnt sbin usr
dev lib opt snap var
etc lib64 proc srv vmlinuz
home lost+found root sys vmlinuz.old
デバッグ用に作成される Pod は、以下の権限を持っています。これは、多くの環境でノードの root に相当する権限を持った Pod であるため、扱いは十分に注意しましょう。
- HostPath: ホストのルートディレクトリを、Pod の
/hostにマウントしている - HostPID: ホストのプロセス空間を利用。
psなどで直接プロセスが見れる - HostIPC: ホストの IPC 名前空間を使用
- HostNetwork: ホストのネットワークを利用
ただし、privileged は指定されていないため、特別な Linux Capabilities が必要なコマンドは動かない場合があります。(後述)
終了後、この Pod は手動で削除する必要があります。Kubernetes 1.21 時点では、終了時に Pod を削除する --rm のようなオプションは存在していません。
chroot でホストのコマンドを使う
デバッグ用 Pod のシェルからホストのコマンドをそのまま実行すると、共有ライブラリなどパスの問題でエラーが出て実行できない場合があります。
# /host/bin/systemctl status
/host/bin/systemctl: error while loading shared libraries: libsystemd-shared-237.so: cannot open shared object file: No such file or directory
Pod のシェル内で、/host に chroot し、/ をホストのルートディレクトリにすると正常に動くようになります。
# chroot /host
コマンドが正しく動くようになりました
。chroot すると ssh したときと同様、ホストの環境がそのまま利用できるので非常に便利です。
# systemctl status
● demo-tkusumi-worker-default-f59fa5d5-d7qbs
State: running
Jobs: 0 queued
Failed: 0 units
...
root のまま操作することに不安がある場合、su で一般ユーザに切り替えておくと安心です。
# su - ubuntu
ホストのファイルをコピーする
kubectl cp または kubectl exec を利用すると、scp のようにノード上のファイルを簡単にコピーできます。
kubectl cp の方が利用はシンプルですが、セキュリティ上の理由で symlink がサポートされません 1。そのため、symlink の追従が必要で、かつ信頼できるノードである場合は、kubectl exec を利用します。
デバッグ Pod の作成
cp, exec どちらも、まずは kubectl debug node でデバッグ用 Pod を立ち上げます。このとき Pod が終了しないように sleep infinity などを指定しておきます。
$ kubectl debug node/<NODE_NAME> --image busybox -- sleep infinity
デバッグの Pod 名が表示されます。以下の場合、node-debugger-demo-tkusumi-worker-default-f59fa5d5-xjkc9-9xj69 が Pod 名です。
Creating debugging pod node-debugger-demo-tkusumi-worker-default-f59fa5d5-xjkc9-9xj69 with container debugger on node demo-tkusumi-worker-default-f59fa5d5-xjkc9.
コピーが終わったら、この Pod を忘れずに削除しましょう。
kubectl cp の例
ホストの /etc/kubernetes 以下を、操作元の /tmp にコピーする例です。パスに /host が入る点に注意してください。
$ kubectl cp <DEBUG_POD>:/host/etc/kubernetes/ /tmp/
操作元の /tmp/ 以下にファイルがコピーされました
。
$ ls /tmp/host/etc/kubernetes
drain-kubeconfig.yaml kube-proxy.yaml manifests
extra kubelet-kubeconfig.yaml probe-kubeconfig.yaml
kube-proxy-kubeconfig.yaml kubelet.yaml ssl
kubectl exec の例
cp 同様に、ホストの /etc/kubernetes 以下を、操作元の /tmp にコピーする例です。パスに /host が入る点に注意してください。
$ kubectl exec <DEBUG_POD> tar cf - /host/etc/kubernetes | tar xf - -C /tmp/
操作元の /tmp/ 以下にファイルがコピーされました
。
$ ls /tmp/host/etc/kubernetes
drain-kubeconfig.yaml kube-proxy.yaml manifests
extra kubelet-kubeconfig.yaml probe-kubeconfig.yaml
kube-proxy-kubeconfig.yaml kubelet.yaml ssl
特別な Capabilities が必要な場合
Kubernetes 1.21 時点では、kubectl debug node で、Linux Capabilities や privileged を指定できません。そのため、特別な Capabilities が必要なコマンドは実行できないことがあります。privileged は Add --privileged flag to kubectl debug node #101897 の PR がマージされれば、指定できるようになります。
ホストの Docker を使った workaround
現状どうしても、Linux Capabilities や privileged を使いたい場合は、ホストの Docker を使って権限昇格する方法があります。
まず kubectl debug node で Pod のシェルを立ち上げます。
$ kubectl debug node/<NODE_NAME> -it --image=busybox
Pod のシェル内で、ホストのルートディレクトリに chroot します。
# chroot /host
例えば、strace は CAP_SYS_PTRACE の Capabilitiy が必要なため、利用できません。
# strace -e trace=file -p 1
strace: Could not attach to process. If your uid matches the uid of the target process, check the setting of /proc/sys/kernel/yama/ptrace_scope, or try again as the root user. For more details, see /etc/sysctl.d/10-ptrace.conf: Operation not permitted
strace: attach: ptrace(PTRACE_SEIZE, 1): Operation not permitted
ホストの Docker で --cap-add SYS_PTRACE を指定し、コンテナを立ち上げます。引数で再度、chroot している点に留意してください。
# docker run -it --cap-add SYS_PTRACE --pid=host --net=host --ipc=host -v /:/host busybox chroot /host
strace が実行できるようになりました
。
# strace -e trace=file -p 1
strace: Process 1 attached
[pid 23001] execve("/usr/bin/strace", ["strace", "-e", "trace=file", "-p", "1"], 0x7ffde9127f70 /* 10 vars */) = 0
[pid 23001] access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
[pid 23001] access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
[pid 23001] openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
補足: HostPath による権限昇格について
もし、HostPath による権限昇格ができることに不安を覚えた場合は、Pod Security Policy (将来的にはその後継) による Pod のセキュリティポリシーの見直しが必要かもしれません。多くの環境で、HostPath でホストの任意のディレクトリをマウントできる Pod の作成権限は、ノードの root 相当の権限を持っていることを意味しています。
既知の問題と今後の機能追加
Kubernetes v1.21 時点での、kubectl debug node の既知の問題と、今後の入る可能性のある機能です。
- NoExecute の taint が付与されたノードで
kubectl debug nodeが動作しない -
--overridesフラグの追加- JSON 形式のパッチでデバッグ用 Pod をカスタマイズできる
- Support kubectl debug overload from overrides json #97887
-
--privilegedフラグの追加
まとめ
kubectl debug node は、ノードのデバッグに非常に便利です。特に chroot を使うと、ホストのコマンドがそのまま利用できるので、ssh と同じ感覚で操作が可能です。また、kubectl の cp や exec と組み合わせると、scp のようなファイルのコピーも可能です。
参考
- https://kubernetes.io/docs/tasks/debug-application-cluster/debug-running-pod/#node-shell-session
- kubectl debug #1441
- KEP-1441: kubectl debug