除錯執行中的 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}

舉個例子,要檢視正在執行的 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:/#

完成除錯後,不要忘記清理除錯 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,可以用來執行檢查檔案系統路徑或手動執行容器命令等任務。

完成除錯後,不要忘記清理除錯 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)許可權的屬性

假設你建立一個 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 標誌應用它。

建立一個名為 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
最後修改於 2024 年 12 月 27 日 太平洋標準時間上午 2:12:修復自定義配置檔案穩定版 (79da44942a)