Kubernetes 調(diào)試運行中的Pod

2022-05-27 14:12 更新

在開始之前

  • 你的 Pod 應(yīng)該已經(jīng)被調(diào)度并正在運行中, 如果你的 Pod 還沒有運行,請參閱調(diào)試 Pod。
  • 對于一些高級調(diào)試步驟,你應(yīng)該知道 Pod 具體運行在哪個節(jié)點上,并具有在該節(jié)點上運行命令的 shell 訪問權(quán)限。 你不需要任何訪問權(quán)限就可以使用 ?kubectl ?去運行一些標(biāo)準(zhǔn)調(diào)試步驟。

使用 kubectl describe pod 命令獲取 Pod 詳情

與之前的例子類似,我們使用一個 Deployment 來創(chuàng)建兩個 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

使用如下命令創(chuàng)建 Deployment:

kubectl apply -f https://k8s.io/examples/application/nginx-with-request.yaml
deployment.apps/nginx-deployment created

使用如下命令查看 Pod 狀態(tài):

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

在這里,你可以看到有關(guān)容器和 Pod 的配置信息(標(biāo)簽、資源需求等), 以及有關(guān)容器和 Pod 的狀態(tài)信息(狀態(tài)、就緒、重啟計數(shù)、事件等) 。

容器狀態(tài)是 Waiting、Running 和 Terminated 之一。 根據(jù)狀態(tài)的不同,還有對應(yīng)的額外的信息 —— 在這里你可以看到, 對于處于運行狀態(tài)的容器,系統(tǒng)會告訴你容器的啟動時間。

Ready 指示是否通過了最后一個就緒態(tài)探測。 (在本例中,容器沒有配置就緒態(tài)探測;如果沒有配置就緒態(tài)探測,則假定容器已經(jīng)就緒。)

Restart Count 告訴你容器已重啟的次數(shù); 這些信息對于定位配置了 “Always” 重啟策略的容器持續(xù)崩潰問題非常有用。

目前,唯一與 Pod 有關(guān)的狀態(tài)是 Ready 狀況,該狀況表明 Pod 能夠為請求提供服務(wù), 并且應(yīng)該添加到相應(yīng)服務(wù)的負(fù)載均衡池中。

最后,你還可以看到與 Pod 相關(guān)的近期事件。 系統(tǒng)通過指示第一次和最后一次看到事件以及看到該事件的次數(shù)來壓縮多個相同的事件。 “From” 標(biāo)明記錄事件的組件, “SubobjectPath” 告訴你引用了哪個對象(例如 Pod 中的容器), “Reason” 和 “Message” 告訴你發(fā)生了什么。

例子: 調(diào)試 Pending 狀態(tài)的 Pod

可以使用事件來調(diào)試的一個常見的場景是,你創(chuàng)建 Pod 無法被調(diào)度到任何節(jié)點。 比如,Pod 請求的資源比較多,沒有任何一個節(jié)點能夠滿足,或者它指定了一個標(biāo)簽,沒有節(jié)點可匹配。 假定我們創(chuàng)建之前的 Deployment 時指定副本數(shù)是 5(不再是 2),并且請求 600 毫核(不再是 500), 對于一個 4 個節(jié)點的集群,若每個節(jié)點只有 1 個 CPU,這時至少有一個 Pod 不能被調(diào)度。 (需要注意的是,其他集群插件 Pod,比如 fluentd、skydns 等等會在每個節(jié)點上運行, 如果我們需求 1000 毫核,將不會有 Pod 會被調(diào)度。)

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

為了查找 Pod nginx-deployment-1370807587-fz9sd 沒有運行的原因,我們可以使用 ?kubectl describe pod? 命令描述 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

這里你可以看到由調(diào)度器記錄的事件,它表明了 Pod 不能被調(diào)度的原因是 ?FailedScheduling?(也可能是其他值)。 其 message 部分表明沒有任何節(jié)點擁有足夠多的資源。

要糾正這種情況,可以使用 ?kubectl scale? 更新 Deployment,以指定 4 個或更少的副本。 (或者你可以讓 Pod 繼續(xù)保持這個狀態(tài),這是無害的。)

你在 ?kubectl describe pod? 結(jié)尾處看到的事件都保存在 etcd 中, 并提供關(guān)于集群中正在發(fā)生的事情的高級信息。 如果需要列出所有事件,可使用命令:

kubectl get events

但是,需要注意的是,事件是區(qū)分名字空間的。 如果你對某些名字空間域的對象(比如 ?my-namespace? 名字下的 Pod)的事件感興趣, 你需要顯式地在命令行中指定名字空間:

kubectl get events --namespace=my-namespace

查看所有 namespace 的事件,可使用 ?--all-namespaces? 參數(shù)。

除了 ?kubectl describe pod? 以外,另一種獲取 Pod 額外信息(除了 ?kubectl get pod?)的方法 是給 ?kubectl get pod? 增加 ?-o yaml? 輸出格式參數(shù)。 該命令將以 YAML 格式為你提供比 ?kubectl describe pod? 更多的信息 —— 實際上是系統(tǒng)擁有的關(guān)于 Pod 的所有信息。 在這里,你將看到注解(沒有標(biāo)簽限制的鍵值元數(shù)據(jù),由 Kubernetes 系統(tǒng)組件在內(nèi)部使用)、 重啟策略、端口和卷等。

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 進(jìn)行調(diào)試

如果 容器鏡像 包含調(diào)試程序, 比如從 Linux 和 Windows 操作系統(tǒng)基礎(chǔ)鏡像構(gòu)建的鏡像,你可以使用 ?kubectl exec? 命令 在特定的容器中運行一些命令:

kubectl exec ${POD_NAME} -c ${CONTAINER_NAME} -- ${CMD} ${ARG1} ${ARG2} ... ${ARGN}

Note: ?-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

使用臨時調(diào)試容器來進(jìn)行調(diào)試

FEATURE STATE: Kubernetes v1.23 [beta]

當(dāng)由于容器崩潰或容器鏡像不包含調(diào)試程序(例如無發(fā)行版鏡像等) 而導(dǎo)致 ?kubectl exec? 無法運行時,臨時容器對于排除交互式故障很有用。

使用臨時容器來調(diào)試的例子

你可以使用 ?kubectl debug? 命令來給正在運行中的 Pod 增加一個臨時容器。 首先,像示例一樣創(chuàng)建一個 pod:

kubectl run ephemeral-demo --image=k8s.gcr.io/pause:3.1 --restart=Never

Note: 本節(jié)示例中使用 ?pause ?容器鏡像,因為它不包含調(diào)試程序,但是這個方法適用于所有容器鏡像。

如果你嘗試使用 ?kubectl exec? 來創(chuàng)建一個 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? 添加調(diào)試容器。 如果你指定 ?-i? 或者 ?--interactive? 參數(shù),?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? 參數(shù)指定另一個容器的進(jìn)程命名空間。 這是必需的,因為 ?kubectl run? 不能在它創(chuàng)建的pod中啟用 共享進(jìn)程命名空間。

Note: 容器運行時必須支持 ?--target? 參數(shù)。 如果不支持,則臨時容器可能不會啟動,或者可能使用隔離的進(jìn)程命名空間啟動, 以便 ?ps ?不顯示其他容器內(nèi)的進(jìn)程。

你可以使用 ?kubectl describe? 查看新創(chuàng)建的臨時容器的狀態(tài):

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? 來移除已經(jīng)結(jié)束掉的 Pod:

kubectl delete pod ephemeral-demo

通過 Pod 副本調(diào)試

有些時候 Pod 的配置參數(shù)使得在某些情況下很難執(zhí)行故障排查。 例如,在容器鏡像中不包含 shell 或者你的應(yīng)用程序在啟動時崩潰的情況下, 就不能通過運行 ?kubectl exec? 來排查容器故障。 在這些情況下,你可以使用 ?kubectl debug? 來創(chuàng)建 Pod 的副本,通過更改配置幫助調(diào)試。

在添加新的容器時創(chuàng)建 Pod 副本

當(dāng)應(yīng)用程序正在運行但其表現(xiàn)不符合預(yù)期時,你會希望在 Pod 中添加額外的調(diào)試工具, 這時添加新容器是很有用的。

例如,應(yīng)用的容器鏡像是建立在 ?busybox ?的基礎(chǔ)上, 但是你需要 ?busybox ?中并不包含的調(diào)試工具。 你可以使用 ?kubectl run? 模擬這個場景:

kubectl run myapp --image=busybox:1.28 --restart=Never -- sleep 1d

通過運行以下命令,建立 ?myapp ?的一個名為 ?myapp-debug? 的副本, 新增了一個用于調(diào)試的 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:/#
Note:
  • 如果你沒有使用 ?--container? 指定新的容器名,?kubectl debug? 會自動生成的。
  • 默認(rèn)情況下,?-i? 標(biāo)志使 ?kubectl debug? 附加到新容器上。 你可以通過指定 --attach=false 來防止這種情況。 如果你的會話斷開連接,你可以使用 ?kubectl attach? 重新連接。
  • ?--share-processes? 允許在此 Pod 中的其他容器中查看該容器的進(jìn)程。

不要忘了清理調(diào)試 Pod:

kubectl delete pod myapp myapp-debug

在改變 Pod 命令時創(chuàng)建 Pod 副本 

有時更改容器的命令很有用,例如添加調(diào)試標(biāo)志或因為應(yīng)用崩潰。

為了模擬應(yīng)用崩潰的場景,使用 ?kubectl run? 命令創(chuàng)建一個立即退出的容器:

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? 命令創(chuàng)建該 Pod 的一個副本, 在該副本中命令改變?yōu)榻换ナ?nbsp;shell:

kubectl debug myapp -it --copy-to=myapp-debug --container=myapp -- sh
If you don't see a command prompt, try pressing enter.
/ #

現(xiàn)在你有了一個可以執(zhí)行類似檢查文件系統(tǒng)路徑或者手動運行容器命令的交互式 shell。

Note:
  • 要更改指定容器的命令,你必須用 ?--container? 命令指定容器的名字, 否則 ?kubectl debug? 將建立一個新的容器運行你指定的命令。
  • 默認(rèn)情況下,標(biāo)志 ?-i? 使 ?kubectl debug? 附加到容器。 你可通過指定 ?--attach=false? 來防止這種情況。 如果你的斷開連接,可以使用 ?kubectl attach? 重新連接。

不要忘了清理調(diào)試 Pod:

kubectl delete pod myapp myapp-debug

在更改容器鏡像時創(chuàng)建 Pod 副本

在某些情況下,你可能想從正常生產(chǎn)容器鏡像中 把行為異常的 Pod 改變?yōu)榘{(diào)試版本或者附加應(yīng)用的鏡像。

下面的例子,用 ?kubectl run?創(chuàng)建一個 Pod:

kubectl run myapp --image=busybox:1.28 --restart=Never -- sleep 1d

現(xiàn)在可以使用 ?kubectl debug? 創(chuàng)建一個副本 并改變?nèi)萜麋R像為 ?ubuntu?:

kubectl debug myapp --copy-to=myapp-debug --set-image=*=ubuntu

?--set-image? 與 ?container_name=image? 使用相同的 ?kubectl set image? 語法。 ?*=ubuntu? 表示把所有容器的鏡像改為 ?ubuntu?。

kubectl delete pod myapp myapp-debug

在節(jié)點上通過 shell 來進(jìn)行調(diào)試

如果這些方法都不起作用,你可以找到運行 Pod 的節(jié)點,然后在節(jié)點上部署一個運行在宿主名字空間的特權(quán) Pod。

你可以通過?kubectl debug? 在節(jié)點上創(chuàng)建一個交互式 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:/#

當(dāng)在節(jié)點上創(chuàng)建調(diào)試會話,注意以下要點:

  • ?kubectl debug? 基于節(jié)點的名字自動生成新的 Pod 的名字。
  • 新的調(diào)試容器運行在宿主命名空間里(IPC, 網(wǎng)絡(luò) 還有PID命名空間)。
  • 節(jié)點的根文件系統(tǒng)會被掛載在 ?/host?。

當(dāng)你完成節(jié)點調(diào)試時,不要忘記清理調(diào)試 Pod:

kubectl delete pod node-debugger-mynode-pdx84


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號