除錯執行中的 Pod
本頁解釋瞭如何除錯在節點上執行(或崩潰)的 Pod。
準備工作
- 你的 Pod 應該已經排程並運行了。如果你的 Pod 尚未執行,請從 除錯 Pod 開始。
- 對於某些高階除錯步驟,你需要知道 Pod 在哪個節點上執行,並且需要 shell 訪問許可權才能在該節點上執行命令。你不需要該許可權即可執行使用
kubectl
的標準除錯步驟。
使用 kubectl describe pod
獲取 Pod 的詳細資訊
此示例中,我們將使用 Deployment 來建立兩個 Pod,類似於前面的示例。
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector:
matchLabels:
app: nginx
replicas: 2
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
resources:
limits:
memory: "128Mi"
cpu: "500m"
ports:
- containerPort: 80
透過執行以下命令建立 Deployment
kubectl apply -f https://k8s.io/examples/application/nginx-with-request.yaml
deployment.apps/nginx-deployment created
透過以下命令檢查 Pod 狀態
kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-deployment-67d4bdd6f5-cx2nz 1/1 Running 0 13s
nginx-deployment-67d4bdd6f5-w6kd7 1/1 Running 0 13s
我們可以使用 kubectl describe pod
檢索每個 Pod 的更多資訊。例如:
kubectl describe pod nginx-deployment-67d4bdd6f5-w6kd7
Name: nginx-deployment-67d4bdd6f5-w6kd7
Namespace: default
Priority: 0
Node: kube-worker-1/192.168.0.113
Start Time: Thu, 17 Feb 2022 16:51:01 -0500
Labels: app=nginx
pod-template-hash=67d4bdd6f5
Annotations: <none>
Status: Running
IP: 10.88.0.3
IPs:
IP: 10.88.0.3
IP: 2001:db8::1
Controlled By: ReplicaSet/nginx-deployment-67d4bdd6f5
Containers:
nginx:
Container ID: containerd://5403af59a2b46ee5a23fb0ae4b1e077f7ca5c5fb7af16e1ab21c00e0e616462a
Image: nginx
Image ID: docker.io/library/nginx@sha256:2834dc507516af02784808c5f48b7cbe38b8ed5d0f4837f16e78d00deb7e7767
Port: 80/TCP
Host Port: 0/TCP
State: Running
Started: Thu, 17 Feb 2022 16:51:05 -0500
Ready: True
Restart Count: 0
Limits:
cpu: 500m
memory: 128Mi
Requests:
cpu: 500m
memory: 128Mi
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-bgsgp (ro)
Conditions:
Type Status
Initialized True
Ready True
ContainersReady True
PodScheduled True
Volumes:
kube-api-access-bgsgp:
Type: Projected (a volume that contains injected data from multiple sources)
TokenExpirationSeconds: 3607
ConfigMapName: kube-root-ca.crt
ConfigMapOptional: <nil>
DownwardAPI: true
QoS Class: Guaranteed
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 34s default-scheduler Successfully assigned default/nginx-deployment-67d4bdd6f5-w6kd7 to kube-worker-1
Normal Pulling 31s kubelet Pulling image "nginx"
Normal Pulled 30s kubelet Successfully pulled image "nginx" in 1.146417389s
Normal Created 30s kubelet Created container nginx
Normal Started 30s kubelet Started container nginx
在這裡你可以看到關於容器和 Pod 的配置資訊(標籤、資源要求等),以及關於容器和 Pod 的狀態資訊(狀態、就緒性、重啟計數、事件等)。
容器狀態是 Waiting、Running 或 Terminated 之一。根據狀態,將提供額外的資訊——在這裡你可以看到,對於處於 Running 狀態的容器,系統會告訴你容器何時啟動。
Ready 告訴你容器是否通過了上次的就緒探針。(在此例中,容器未配置就緒探針;如果未配置就緒探針,則假定容器已就緒。)
重啟計數告訴你容器已重啟了多少次;此資訊可用於檢測重啟策略為“always”的容器中的崩潰迴圈。
目前與 Pod 相關聯的唯一 Condition 是二進位制的 Ready condition,它表示 Pod 能夠提供請求服務,並且應該新增到所有匹配服務的負載平衡池中。
最後,你將看到與 Pod 相關的最新事件日誌。“From”表示記錄事件的元件。“Reason”和“Message”告訴你發生了什麼。
示例:除錯 Pending Pod
使用事件可以檢測到一個常見場景,即你建立了一個無法適應任何節點的 Pod。例如,Pod 可能請求的資源多於任何節點上的空閒資源,或者它可能指定了一個與任何節點都不匹配的標籤選擇器。假設我們建立了前一個 Deployment,有 5 個副本(而不是 2 個),並且在每個(虛擬)機器都有 1 個 CPU 的四節點叢集上請求 600 毫核而不是 500 毫核。在這種情況下,其中一個 Pod 將無法排程。(請注意,由於每個節點上執行的叢集附加 Pod,例如 fluentd、skydns 等,如果請求 1000 毫核,那麼所有 Pod 都將無法排程。)
kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-deployment-1006230814-6winp 1/1 Running 0 7m
nginx-deployment-1006230814-fmgu3 1/1 Running 0 7m
nginx-deployment-1370807587-6ekbw 1/1 Running 0 1m
nginx-deployment-1370807587-fg172 0/1 Pending 0 1m
nginx-deployment-1370807587-fz9sd 0/1 Pending 0 1m
要找出 nginx-deployment-1370807587-fz9sd Pod 未執行的原因,我們可以在 pending Pod 上使用 kubectl describe pod
並檢視其事件。
kubectl describe pod nginx-deployment-1370807587-fz9sd
Name: nginx-deployment-1370807587-fz9sd
Namespace: default
Node: /
Labels: app=nginx,pod-template-hash=1370807587
Status: Pending
IP:
Controllers: ReplicaSet/nginx-deployment-1370807587
Containers:
nginx:
Image: nginx
Port: 80/TCP
QoS Tier:
memory: Guaranteed
cpu: Guaranteed
Limits:
cpu: 1
memory: 128Mi
Requests:
cpu: 1
memory: 128Mi
Environment Variables:
Volumes:
default-token-4bcbi:
Type: Secret (a volume populated by a Secret)
SecretName: default-token-4bcbi
Events:
FirstSeen LastSeen Count From SubobjectPath Type Reason Message
--------- -------- ----- ---- ------------- -------- ------ -------
1m 48s 7 {default-scheduler } Warning FailedScheduling pod (nginx-deployment-1370807587-fz9sd) failed to fit in any node
fit failure on node (kubernetes-node-6ta5): Node didn't have enough resource: CPU, requested: 1000, used: 1420, capacity: 2000
fit failure on node (kubernetes-node-wul5): Node didn't have enough resource: CPU, requested: 1000, used: 1100, capacity: 2000
在這裡,你可以看到排程器生成的事件,說明 Pod 因 FailedScheduling
(可能還有其他原因) 而未能排程。訊息告訴我們任何節點上都沒有足夠的資源來執行該 Pod。
要糾正此情況,你可以使用 kubectl scale
更新你的 Deployment,以指定四個或更少的副本。(或者你可以讓一個 Pod 處於 Pending 狀態,這是無害的。)
你在 kubectl describe pod
末尾看到的事件會持久儲存在 etcd 中,並提供關於叢集中發生情況的高階資訊。要列出所有事件,你可以使用
kubectl get events
但你必須記住事件是帶名稱空間的。這意味著如果你對某個名稱空間物件(例如,my-namespace
名稱空間中的 Pod 發生了什麼)的事件感興趣,你需要明確地向命令提供一個名稱空間。
kubectl get events --namespace=my-namespace
要檢視所有名稱空間中的事件,你可以使用 --all-namespaces
引數。
除了 kubectl describe pod
,獲取 Pod 額外資訊(除了 kubectl get pod
提供的資訊之外)的另一種方法是向 kubectl get pod
傳遞 -o yaml
輸出格式標誌。這將以 YAML 格式提供比 kubectl describe pod
更多的資訊——基本上是系統擁有的關於 Pod 的所有資訊。在這裡,你將看到諸如註解(沒有標籤限制的鍵值元資料,由 Kubernetes 系統元件內部使用)、重啟策略、埠和卷等內容。
kubectl get pod nginx-deployment-1006230814-6winp -o yaml
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: "2022-02-17T21:51:01Z"
generateName: nginx-deployment-67d4bdd6f5-
labels:
app: nginx
pod-template-hash: 67d4bdd6f5
name: nginx-deployment-67d4bdd6f5-w6kd7
namespace: default
ownerReferences:
- apiVersion: apps/v1
blockOwnerDeletion: true
controller: true
kind: ReplicaSet
name: nginx-deployment-67d4bdd6f5
uid: 7d41dfd4-84c0-4be4-88ab-cedbe626ad82
resourceVersion: "1364"
uid: a6501da1-0447-4262-98eb-c03d4002222e
spec:
containers:
- image: nginx
imagePullPolicy: Always
name: nginx
ports:
- containerPort: 80
protocol: TCP
resources:
limits:
cpu: 500m
memory: 128Mi
requests:
cpu: 500m
memory: 128Mi
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /var/run/secrets/kubernetes.io/serviceaccount
name: kube-api-access-bgsgp
readOnly: true
dnsPolicy: ClusterFirst
enableServiceLinks: true
nodeName: kube-worker-1
preemptionPolicy: PreemptLowerPriority
priority: 0
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
serviceAccount: default
serviceAccountName: default
terminationGracePeriodSeconds: 30
tolerations:
- effect: NoExecute
key: node.kubernetes.io/not-ready
operator: Exists
tolerationSeconds: 300
- effect: NoExecute
key: node.kubernetes.io/unreachable
operator: Exists
tolerationSeconds: 300
volumes:
- name: kube-api-access-bgsgp
projected:
defaultMode: 420
sources:
- serviceAccountToken:
expirationSeconds: 3607
path: token
- configMap:
items:
- key: ca.crt
path: ca.crt
name: kube-root-ca.crt
- downwardAPI:
items:
- fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
path: namespace
status:
conditions:
- lastProbeTime: null
lastTransitionTime: "2022-02-17T21:51:01Z"
status: "True"
type: Initialized
- lastProbeTime: null
lastTransitionTime: "2022-02-17T21:51:06Z"
status: "True"
type: Ready
- lastProbeTime: null
lastTransitionTime: "2022-02-17T21:51:06Z"
status: "True"
type: ContainersReady
- lastProbeTime: null
lastTransitionTime: "2022-02-17T21:51:01Z"
status: "True"
type: PodScheduled
containerStatuses:
- containerID: containerd://5403af59a2b46ee5a23fb0ae4b1e077f7ca5c5fb7af16e1ab21c00e0e616462a
image: docker.io/library/nginx:latest
imageID: docker.io/library/nginx@sha256:2834dc507516af02784808c5f48b7cbe38b8ed5d0f4837f16e78d00deb7e7767
lastState: {}
name: nginx
ready: true
restartCount: 0
started: true
state:
running:
startedAt: "2022-02-17T21:51:05Z"
hostIP: 192.168.0.113
phase: Running
podIP: 10.88.0.3
podIPs:
- ip: 10.88.0.3
- ip: 2001:db8::1
qosClass: Guaranteed
startTime: "2022-02-17T21:51:01Z"
檢查 Pod 日誌
首先,檢視受影響容器的日誌
kubectl logs ${POD_NAME} ${CONTAINER_NAME}
如果你的容器之前崩潰過,你可以使用以下命令訪問上一個容器的崩潰日誌
kubectl logs --previous ${POD_NAME} ${CONTAINER_NAME}
使用容器 exec 進行除錯
如果 容器映象 包含除錯工具(例如基於 Linux 和 Windows OS 基礎映象構建的映象),你可以使用 kubectl exec
在特定容器內執行命令。
kubectl exec ${POD_NAME} -c ${CONTAINER_NAME} -- ${CMD} ${ARG1} ${ARG2} ... ${ARGN}
注意
-c ${CONTAINER_NAME}
是可選的。對於只包含單個容器的 Pod,可以省略它。舉個例子,要檢視正在執行的 Cassandra Pod 的日誌,你可以執行
kubectl exec cassandra -- cat /var/log/cassandra/system.log
你可以使用 kubectl exec
的 -i
和 -t
引數執行一個連線到你終端的 shell,例如
kubectl exec -it cassandra -- sh
有關更多詳細資訊,請參閱 獲取執行中容器的 Shell。
使用臨時除錯容器進行除錯
Kubernetes v1.25 [穩定]
臨時容器 對於互動式故障排除很有用,尤其是在 kubectl exec
不足以解決問題(因為容器崩潰或容器映象不包含除錯工具,例如 distroless 映象)時。
臨時容器除錯示例
你可以使用 kubectl debug
命令將臨時容器新增到正在執行的 Pod 中。首先,為示例建立一個 Pod。
kubectl run ephemeral-demo --image=registry.k8s.io/pause:3.1 --restart=Never
本節中的示例使用 pause
容器映象,因為它不包含除錯工具,但此方法適用於所有容器映象。
如果你嘗試使用 kubectl exec
建立 shell,你將看到一個錯誤,因為此容器映象中沒有 shell。
kubectl exec -it ephemeral-demo -- sh
OCI runtime exec failed: exec failed: container_linux.go:346: starting container process caused "exec: \"sh\": executable file not found in $PATH": unknown
你可以改為使用 kubectl debug
新增一個除錯容器。如果你指定 -i
/--interactive
引數,kubectl
將自動附加到臨時容器的控制檯。
kubectl debug -it ephemeral-demo --image=busybox:1.28 --target=ephemeral-demo
Defaulting debug container name to debugger-8xzrl.
If you don't see a command prompt, try pressing enter.
/ #
此命令添加了一個新的 busybox 容器並連線到它。--target
引數指定另一個容器的程序名稱空間。此處有必要這樣做,因為 kubectl run
在其建立的 Pod 中不會啟用程序名稱空間共享。
你可以使用 kubectl describe
檢視新建立的臨時容器的狀態。
kubectl describe pod ephemeral-demo
...
Ephemeral Containers:
debugger-8xzrl:
Container ID: docker://b888f9adfd15bd5739fefaa39e1df4dd3c617b9902082b1cfdc29c4028ffb2eb
Image: busybox
Image ID: docker-pullable://busybox@sha256:1828edd60c5efd34b2bf5dd3282ec0cc04d47b2ff9caa0b6d4f07a21d1c08084
Port: <none>
Host Port: <none>
State: Running
Started: Wed, 12 Feb 2020 14:25:42 +0100
Ready: False
Restart Count: 0
Environment: <none>
Mounts: <none>
...
完成後使用 kubectl delete
刪除 Pod。
kubectl delete pod ephemeral-demo
使用 Pod 副本進行除錯
有時,Pod 配置選項會使在某些情況下難以進行故障排除。例如,如果你的容器映象不包含 shell 或者你的應用程式在啟動時崩潰,你就無法執行 kubectl exec
來排除容器故障。在這種情況下,你可以使用 kubectl debug
建立 Pod 的副本,並更改配置值以幫助除錯。
複製 Pod 並新增新容器
當你的應用程式正在執行但行為不符合預期,並且你想為 Pod 新增額外的故障排除實用程式時,新增新容器會很有用。
例如,你的應用程式的容器映象可能基於 busybox
構建,但你需要 busybox
中不包含的除錯工具。你可以使用 kubectl run
模擬此場景。
kubectl run myapp --image=busybox:1.28 --restart=Never -- sleep 1d
執行此命令以建立 myapp
的副本,名為 myapp-debug
,並新增一個新的 Ubuntu 容器用於除錯。
kubectl debug myapp -it --image=ubuntu --share-processes --copy-to=myapp-debug
Defaulting debug container name to debugger-w7xmf.
If you don't see a command prompt, try pressing enter.
root@myapp-debug:/#
注意
- 如果你不使用
--container
標誌選擇容器名稱,kubectl debug
將自動生成一個容器名稱。 -i
標誌使kubectl debug
預設附加到新容器。你可以透過指定--attach=false
來阻止這種情況。如果你的會話斷開連線,你可以使用kubectl attach
重新連線。--share-processes
允許此 Pod 中的容器檢視 Pod 中其他容器的程序。有關其工作原理的更多資訊,請參閱 在 Pod 中共享容器間的程序名稱空間。
完成除錯後,不要忘記清理除錯 Pod。
kubectl delete pod myapp myapp-debug
複製 Pod 並更改其命令
有時更改容器的命令會很有用,例如新增除錯標誌或因為應用程式正在崩潰。
為了模擬一個崩潰的應用程式,使用 kubectl run
建立一個立即退出的容器。
kubectl run --image=busybox:1.28 myapp -- false
你可以使用 kubectl describe pod myapp
看到這個容器正在崩潰。
Containers:
myapp:
Image: busybox
...
Args:
false
State: Waiting
Reason: CrashLoopBackOff
Last State: Terminated
Reason: Error
Exit Code: 1
你可以使用 kubectl debug
建立此 Pod 的副本,並將其命令更改為互動式 shell。
kubectl debug myapp -it --copy-to=myapp-debug --container=myapp -- sh
If you don't see a command prompt, try pressing enter.
/ #
現在你擁有一個互動式 shell,可以用來執行檢查檔案系統路徑或手動執行容器命令等任務。
注意
- 要更改特定容器的命令,你必須使用
--container
指定其名稱,否則kubectl debug
將會建立一個新容器來執行你指定的命令。 -i
標誌使kubectl debug
預設附加到容器。你可以透過指定--attach=false
來阻止這種情況。如果你的會話斷開連線,你可以使用kubectl attach
重新連線。
完成除錯後,不要忘記清理除錯 Pod。
kubectl delete pod myapp myapp-debug
複製 Pod 並更改容器映象
在某些情況下,你可能希望將出現問題的 Pod 從其常規生產容器映象更改為包含除錯版本或附加實用程式的映象。
例如,使用 kubectl run
建立一個 Pod。
kubectl run myapp --image=busybox:1.28 --restart=Never -- sleep 1d
現在使用 kubectl debug
製作一個副本並將其容器映象更改為 ubuntu
。
kubectl debug myapp --copy-to=myapp-debug --set-image=*=ubuntu
--set-image
的語法與 kubectl set image
使用相同的 container_name=image
語法。*=ubuntu
表示將所有容器的映象更改為 ubuntu
。
完成除錯後,不要忘記清理除錯 Pod。
kubectl delete pod myapp myapp-debug
透過節點上的 shell 進行除錯
如果這些方法都不奏效,你可以找到 Pod 正在執行的節點,並在該節點上建立一個 Pod。要使用 kubectl debug
在節點上建立互動式 shell,請執行:
kubectl debug node/mynode -it --image=ubuntu
Creating debugging pod node-debugger-mynode-pdx84 with container debugger on node mynode.
If you don't see a command prompt, try pressing enter.
root@ek8s:/#
在節點上建立除錯會話時,請記住:
kubectl debug
會根據節點名稱自動生成新 Pod 的名稱。- 節點的根檔案系統將掛載到
/host
。 - 容器在宿主 IPC、網路和 PID 名稱空間中執行,儘管 Pod 沒有特權,因此讀取某些程序資訊可能會失敗,並且
chroot /host
也可能失敗。 - 如果你需要特權 Pod,請手動建立或使用
--profile=sysadmin
標誌。
完成除錯後,不要忘記清理除錯 Pod。
kubectl delete pod node-debugger-mynode-pdx84
應用配置檔案除錯 Pod 或節點
當使用 kubectl debug
透過除錯 Pod 除錯節點、透過臨時容器除錯 Pod 或除錯複製的 Pod 時,你可以對它們應用配置檔案。透過應用配置檔案,可以設定特定的屬性,例如 securityContext,從而適應各種場景。配置檔案有兩種型別:靜態配置檔案和自定義配置檔案。
應用靜態配置檔案
靜態配置檔案是一組預定義屬性,你可以使用 --profile
標誌應用它們。可用的配置檔案如下:
配置檔案 | 描述 |
---|---|
legacy | 一組與 1.22 行為向後相容的屬性 |
general | 為每個除錯過程提供一組合理的通用屬性 |
baseline | 一組與 PodSecurityStandard 基線策略 相容的屬性 |
restricted | 一組與 PodSecurityStandard 受限策略 相容的屬性 |
netadmin | 一組包含網路管理員許可權的屬性 |
sysadmin | 一組包含系統管理員(root)許可權的屬性 |
注意
如果你不指定--profile
,則預設使用 legacy
配置檔案,但計劃在不久的將來棄用。因此建議使用 general
等其他配置檔案。假設你建立一個 Pod 並對其進行除錯。首先,建立一個名為 myapp
的 Pod 作為示例。
kubectl run myapp --image=busybox:1.28 --restart=Never -- sleep 1d
然後,使用臨時容器除錯 Pod。如果臨時容器需要特權,你可以使用 sysadmin
配置檔案。
kubectl debug -it myapp --image=busybox:1.28 --target=myapp --profile=sysadmin
Targeting container "myapp". If you don't see processes from this container it may be because the container runtime doesn't support this feature.
Defaulting debug container name to debugger-6kg4x.
If you don't see a command prompt, try pressing enter.
/ #
透過在容器內部執行以下命令來檢查臨時容器程序的能力。
/ # grep Cap /proc/$$/status
...
CapPrm: 000001ffffffffff
CapEff: 000001ffffffffff
...
這意味著透過應用 sysadmin
配置檔案,容器程序被授予了作為特權容器的全部能力。有關 能力的更多詳細資訊。
你還可以檢查臨時容器是否作為特權容器建立。
kubectl get pod myapp -o jsonpath='{.spec.ephemeralContainers[0].securityContext}'
{"privileged":true}
完成後清理 Pod。
kubectl delete pod myapp
應用自定義配置檔案
Kubernetes v1.32 [stable]
你可以將部分容器規約定義為 YAML 或 JSON 格式的自定義配置檔案,並使用 --custom
標誌應用它。
注意
自定義配置檔案僅支援修改容器規約,但容器規約中的name
、image
、command
、lifecycle
和 volumeDevices
欄位不允許修改。它不支援修改 Pod 規約。建立一個名為 myapp 的 Pod 作為示例。
kubectl run myapp --image=busybox:1.28 --restart=Never -- sleep 1d
建立 YAML 或 JSON 格式的自定義配置檔案。這裡,建立一個名為 custom-profile.yaml
的 YAML 格式檔案。
env:
- name: ENV_VAR_1
value: value_1
- name: ENV_VAR_2
value: value_2
securityContext:
capabilities:
add:
- NET_ADMIN
- SYS_TIME
執行此命令,使用帶有自定義配置檔案的臨時容器除錯 Pod。
kubectl debug -it myapp --image=busybox:1.28 --target=myapp --profile=general --custom=custom-profile.yaml
你可以檢查臨時容器是否已新增到目標 Pod 並應用了自定義配置檔案。
kubectl get pod myapp -o jsonpath='{.spec.ephemeralContainers[0].env}'
[{"name":"ENV_VAR_1","value":"value_1"},{"name":"ENV_VAR_2","value":"value_2"}]
kubectl get pod myapp -o jsonpath='{.spec.ephemeralContainers[0].securityContext}'
{"capabilities":{"add":["NET_ADMIN","SYS_TIME"]}}
完成後清理 Pod。
kubectl delete pod myapp