第 14 章:K8s 常用操作命令:从日常巡检到应急排障

0 阅读7分钟

上一章是实战练习。本章把专栏前 13 章用到的命令整理成一份可收藏的工具箱,按场景分类,复制即用。

适合场景:上班时突然线上告警,你需要在 30 秒内定位到是哪个 Pod、哪个节点、哪个容器出了问题。

阅读约定

本章所有命令上方都有一行 # 对象:xxx 的注释,明确这条命令操作的是哪类资源。看到 # 对象:Pod 就知道 <pod-name> 要填 Pod 名字,看到 # 对象:Node 就知道要填节点名。

示例资源命名约定

示例名资源类型
k8s-m1 / k8s-w1Node(节点)
web-xxx / nginx-xxxPod(带 RS 后缀的实例)
web / api-deployDeployment
web-svc / api-svcService
db-sts / redis-stsStatefulSet
data-pvcPersistentVolumeClaim
app-cfg / db-passConfigMap / Secret
web-hpaHorizontalPodAutoscaler
web-ingIngress

14.1 命令太长?可选的效率工具(非必须)

本章所有命令都基于标准 kubectl,在 Linux 服务器上直接可用。

但日常敲 kubectl get pods -n kube-system 这种长命令会很累。如果你愿意,可以配置 alias 或安装 kubectx 系列工具提升效率:

# 给 ~/.bashrc 加几个常用 alias(可选)
alias k='kubectl'
alias kgp='kubectl get pods'
alias kl='kubectl logs'

# 自动补全(推荐)
echo 'source <(kubectl completion bash)' >> ~/.bashrc
source ~/.bashrc

# kubectx / kubens:一条命令切换集群和命名空间(可选)
sudo git clone https://github.com/ahmetb/kubectx /opt/kubectx
sudo ln -s /opt/kubectx/kubectx /usr/local/bin/kubectx
sudo ln -s /opt/kubectx/kubens /usr/local/bin/kubens

配置好后,下面这些命令等价:

# 完整写法(本章统一用这种)
$ kubectl get pods -n kube-system

# 配了 alias + kubens 之后
$ kubens kube-system
$ k get pods

本章后续所有示例都使用完整的 kubectl 命令,方便直接复制到任何 Linux 服务器上执行。

14.2 集群与节点巡检

早会前 30 秒,快速判断集群是否健康。

# 对象:Node —— 列出集群所有节点,看 STATUS 列是否有 NotReady
$ kubectl get nodes -o wide
NAME        STATUS   ROLES           AGE   VERSION   INTERNAL-IP    OS-IMAGE
k8s-m1      Ready    control-plane   90d   v1.29.4   10.0.0.11      Ubuntu 22.04
k8s-w1      Ready    <none>          90d   v1.29.4   10.0.0.12      Ubuntu 22.04
k8s-w2      Ready    <none>          90d   v1.29.4   10.0.0.13      Ubuntu 22.04

# 对象:Node —— 看每个节点 CPU / 内存实时用量(依赖 Metrics Server)
$ kubectl top nodes

# 对象:Node —— 看节点 k8s-w1 的 Conditions(DiskPressure / MemoryPressure / PIDPressure)
$ kubectl describe node k8s-w1 | grep -A5 Conditions

# 对象:控制面 —— 检查 API Server / etcd / Scheduler / ControllerManager 是否就绪
$ kubectl get --raw='/readyz?verbose'

# 对象:Pod(系统组件)—— 列出 kube-system 命名空间下的所有系统 Pod
$ kubectl -n kube-system get pods

常见坑

  • 节点 NotReady → 先看 kubectl describe node k8s-w1 最底部的 Events
  • 节点磁盘满 → DiskPressure=True,需要清理镜像:crictl rmi --prune
  • 证书到期 → kubectl get csr,或 kubeadm certs check-expiration

14.3 Pod:日常操作 90% 都在这里

14.3.1 查

# 对象:Pod —— 列出当前命名空间下所有 Pod,-o wide 多看 IP 和所在节点
$ kubectl get pods -o wide

# 对象:Pod —— 全集群范围(所有命名空间)找名字带 nginx 的 Pod
$ kubectl get pods -A | grep nginx

# 对象:Pod —— 按 label 过滤(找 app=web 的所有 Pod)
$ kubectl get pods -l app=web

# 对象:Pod —— 多 label 组合(app 是 web 或 api,且 tier=frontend)
$ kubectl get pods -l 'app in (web,api),tier=frontend'

# 对象:Pod —— 只看跑在节点 k8s-w1 上的 Pod(按字段过滤)
$ kubectl get pods --field-selector spec.nodeName=k8s-w1

# 对象:Pod —— 全集群找非 Running 状态的 Pod(排查异常的入口)
$ kubectl get pods -A --field-selector=status.phase!=Running

# 对象:Pod —— 全集群找非 Running / 非 Completed 的 Pod(更常用的过滤)
$ kubectl get pods -A | grep -vE 'Running|Completed'

# 对象:Pod —— 按容器重启次数排序(找出不稳定的 Pod)
$ kubectl get pods -A --sort-by='.status.containerStatuses[0].restartCount'

# 对象:Pod —— 持续观察当前命名空间的 Pod 变化(Ctrl+C 退出)
$ kubectl get pods -w

# 对象:Pod —— 每 2 秒刷新一次(watch 是 Linux 命令)
$ watch -n 2 kubectl get pods

14.3.2 看日志

# 对象:Pod —— 看 Pod web-xxx 当前容器的日志
$ kubectl logs web-xxx

# 对象:Pod —— 持续跟踪 Pod web-xxx 的日志(类似 tail -f)
$ kubectl logs -f web-xxx

# 对象:Pod 内的某个 Container —— 多容器 Pod 必须用 -c 指定容器名
$ kubectl logs web-xxx -c sidecar

# 对象:Pod —— 看上一次崩溃前的日志(排查 CrashLoopBackOff 神器)
$ kubectl logs web-xxx --previous

# 对象:Pod —— 最近 1 小时 + 最后 200 行
$ kubectl logs web-xxx --since=1h --tail=200

# 对象:Pod —— 日志带时间戳
$ kubectl logs web-xxx --timestamps

# 对象:一组 Pod —— 按 label 一次拉多个 Pod 的日志
$ kubectl logs -l app=web --all-containers --tail=100

# 对象:一组 Pod —— 用 stern 实时跟踪名字带 web 前缀的所有 Pod
# Linux 安装 stern:
$ wget https://github.com/stern/stern/releases/latest/download/stern_linux_amd64.tar.gz
$ tar -xzf stern_linux_amd64.tar.gz && sudo mv stern /usr/local/bin/
$ stern web -n production

14.3.3 进容器 & 临时调试

# 对象:Pod 内的容器 —— 进入 Pod web-xxx 的默认容器
$ kubectl exec -it web-xxx -- /bin/sh

# 对象:Pod 内的容器 —— 进入 Pod web-xxx 里名为 sidecar 的容器
$ kubectl exec -it web-xxx -c sidecar -- /bin/bash

# 对象:Pod 内的容器 —— 不进容器,直接执行一条命令拿结果
$ kubectl exec web-xxx -- cat /etc/nginx/nginx.conf

# 对象:Pod —— 容器里没有 shell 也没有 curl?注入一个临时 debug 容器
# --target 指定要共享哪个容器的进程命名空间
$ kubectl debug -it web-xxx --image=busybox:1.31 --target=app

# 对象:Pod ↔ 本地 —— 双向复制文件
$ kubectl cp web-xxx:/var/log/nginx/access.log ./access.log
$ kubectl cp ./local.conf web-xxx:/etc/nginx/conf.d/local.conf

14.3.4 看事件(排查调度/拉镜像失败的真相)

# 对象:Event —— 看当前命名空间最近事件,按时间排序
$ kubectl get events --sort-by=.lastTimestamp

# 对象:Event —— 只看 Warning 类型的事件
$ kubectl get events --field-selector type=Warning

# 对象:Pod —— 看 Pod web-xxx 的完整描述,最后 30 行就是事件
$ kubectl describe pod web-xxx | tail -30

90% 的 Pod 起不来,看 events 就够了:镜像拉不下来 / 资源不够 / 探针失败 / 卷挂载超时,全在这里。

14.4 Deployment:发布与回滚

# 对象:Deployment —— 列出当前命名空间所有 Deployment 及副本状态
$ kubectl get deploy

# 对象:Deployment —— 看 Deployment api-deploy 的详细信息
$ kubectl get deploy api-deploy -o wide

# 对象:ReplicaSet —— 看 label 是 app=api 的所有 RS(理解 Deployment → RS → Pod 链路)
$ kubectl get rs -l app=api

# 对象:Deployment —— 把 api-deploy 副本数改成 5
$ kubectl scale deploy api-deploy --replicas=5

# 对象:Deployment —— 把 api-deploy 里 api 容器的镜像更新到 v1.2.3(触发滚动更新)
$ kubectl set image deploy/api-deploy api=myrepo/api:v1.2.3

# 对象:Deployment —— 观察 api-deploy 这次发布的进度(直到完成或失败)
$ kubectl rollout status deploy/api-deploy

# 对象:Deployment —— 看 api-deploy 的历史发布版本
$ kubectl rollout history deploy/api-deploy

# 对象:Deployment —— 看 api-deploy 第 3 个版本改了什么
$ kubectl rollout history deploy/api-deploy --revision=3

# 对象:Deployment —— 回滚到上一个版本
$ kubectl rollout undo deploy/api-deploy

# 对象:Deployment —— 回滚到指定版本
$ kubectl rollout undo deploy/api-deploy --to-revision=2

# 对象:Deployment —— 暂停发布(金丝雀手动分批用)
$ kubectl rollout pause deploy/api-deploy

# 对象:Deployment —— 恢复发布
$ kubectl rollout resume deploy/api-deploy

# 对象:Deployment —— 强制重建所有 Pod(不换镜像,只想重启)
$ kubectl rollout restart deploy/api-deploy

# 对象:Deployment —— 直接编辑 etcd 里的 spec(生产慎用)
$ kubectl edit deploy api-deploy

生产建议

  • 永远用 kubectl set image 或改 YAML + apply不要kubectl edit 改线上
  • rollout restart 是最常用的"重启大法",比删 Pod 优雅

14.5 Service 与网络排障

服务不通时,按下面顺序排查:

# 对象:Service —— 看 Service web-svc 是否存在
$ kubectl get svc web-svc

# 对象:Service —— 看 Service web-svc 的 selector 配置
$ kubectl describe svc web-svc | grep Selector

# 对象:Endpoints —— 看 Service web-svc 后端有哪些 Pod IP(最常见根因:<none>)
$ kubectl get endpoints web-svc
NAME       ENDPOINTS                                      AGE
web-svc    10.244.1.15:80,10.244.2.18:80,10.244.3.20:80   5d

# 对象:Pod —— 列出 label app=web 的所有 Pod 及其完整 label 集合
# 用来核对 Service 的 selector 是否真的匹配到 Pod
$ kubectl get pods --show-labels -l app=web

# 对象:临时 Pod —— 起一个临时 busybox 容器,进集群里测网络
$ kubectl run tmp --rm -it --image=busybox:1.31 -- sh
/ # wget -qO- http://web-svc                                # 测 HTTP 通不通
/ # nslookup web-svc.default.svc.cluster.local             # 测 DNS 解析

# 对象:Pod(CoreDNS)—— 看 CoreDNS 系统 Pod 状态
$ kubectl -n kube-system get pods -l k8s-app=kube-dns

# 对象:Pod(CoreDNS)—— 看 CoreDNS 日志(DNS 解析失败时排查)
$ kubectl -n kube-system logs -l k8s-app=kube-dns --tail=50

# 对象:NetworkPolicy —— 列出所有命名空间的网络策略
$ kubectl get networkpolicy -A

# 对象:NetworkPolicy —— 看具体某条网络策略的规则
$ kubectl describe networkpolicy <policy-name>

完整 DNS 命名规则(背下来):

# Service 的 DNS
<service-name>.<namespace>.svc.cluster.local

# StatefulSet Pod 的 DNS(按 Pod 名稳定访问)
<pod-name>.<headless-service-name>.<namespace>.svc.cluster.local

14.6 ConfigMap 与 Secret

# 对象:ConfigMap —— 三种创建方式
$ kubectl create configmap app-cfg --from-literal=LOG_LEVEL=debug
$ kubectl create configmap app-cfg --from-file=nginx.conf
$ kubectl create configmap app-cfg --from-env-file=.env

# 对象:Secret —— 创建通用类型 Secret
$ kubectl create secret generic db-pass --from-literal=password=123456

# 对象:Secret —— 创建 TLS 类型 Secret(给 Ingress 用)
$ kubectl create secret tls my-tls --cert=tls.crt --key=tls.key

# 对象:ConfigMap —— 查看 ConfigMap app-cfg 的完整内容
$ kubectl get cm app-cfg -o yaml

# 对象:Secret —— 查看 Secret db-pass 的内容(base64 编码后的)
$ kubectl get secret db-pass -o yaml

# 对象:Secret —— 取出 Secret db-pass 里的 password 字段并 base64 解码
# 注意:base64 是编码不是加密,任何能读 Secret 的人都能解出来
$ kubectl get secret db-pass -o jsonpath='{.data.password}' | base64 -d

# 对象:Deployment —— 改了 ConfigMap 之后 Pod 不会自动重启,需要手动重启 Deployment
$ kubectl rollout restart deploy/api-deploy

14.7 存储:PV / PVC

# 对象:PVC —— 列出当前命名空间所有 PVC,看 STATUS 是否 Bound
$ kubectl get pvc
NAME       STATUS   VOLUME     CAPACITY   STORAGECLASS
data-pvc   Bound    pv-abc     10Gi       alicloud-ssd

# 对象:PVC —— PVC 卡在 Pending 时,看 Events 找原因
$ kubectl describe pvc data-pvc

# 对象:PV —— 列出集群里所有 PV
$ kubectl get pv

# 对象:PV —— 看具体某个 PV 的详情(回收策略、绑定关系)
$ kubectl describe pv pv-abc

# 对象:StorageClass —— 列出集群里所有存储类
$ kubectl get sc

14.8 HPA 与资源

# 对象:HPA —— 列出当前命名空间所有 HPA,看目标 / 当前 / 期望副本
$ kubectl get hpa
NAME      REFERENCE              TARGETS         MINPODS   MAXPODS   REPLICAS
web-hpa   Deployment/api-deploy  35%/50%         2         10        4

# 对象:Pod —— 看所有 Pod 的 CPU / 内存实时用量
$ kubectl top pods

# 对象:Pod 内的 Container —— 看每个容器的资源用量(多容器 Pod 用)
$ kubectl top pods --containers

# 对象:Pod —— 按内存排序,找内存大户
$ kubectl top pods --sort-by=memory

# 对象:HPA —— 编辑 HPA web-hpa(调阈值、副本上下限)
$ kubectl edit hpa web-hpa

14.9 端口转发与本地调试

# 对象:Service —— 把 Service web-svc 的 80 端口转发到本机 8080
$ kubectl port-forward svc/web-svc 8080:80

# 对象:Pod —— 把 Pod web-xxx 的 80 端口转发到本机 8080(绕过 Service 直接连 Pod)
$ kubectl port-forward web-xxx 8080:80

# 对象:Service —— 后台转发,一次起多个(数据库 / 缓存本地调试用)
$ kubectl port-forward svc/web-svc 8080:80 &
$ kubectl port-forward svc/db-svc  5432:5432 &

# 之后访问本机 127.0.0.1:8080 即访问到集群里的 web-svc:80

数据库本地调试神器:把 RDS / Redis 通过 port-forward 暴露到本机,本地代码直连。

14.10 标签与注解

# 对象:Pod —— 给 Pod web-xxx 打上 env=prod 标签
$ kubectl label pod web-xxx env=prod

# 对象:Pod —— 删除 Pod web-xxx 的 env 标签(末尾的 - 表示删除)
$ kubectl label pod web-xxx env-

# 对象:Pod —— 给 Pod web-xxx 加注解(annotation 不参与 selector 匹配)
$ kubectl annotate pod web-xxx description="测试 Pod"

# 对象:Node —— 给节点 k8s-w1 打标签(用于 nodeSelector / 亲和性调度)
$ kubectl label node k8s-w1 disk=ssd

# 对象:Node —— 给节点 k8s-w1 打污点(防止普通 Pod 调度上来)
$ kubectl taint node k8s-w1 dedicated=agent:NoSchedule

# 对象:Node —— 删除节点 k8s-w1 上名为 dedicated 的污点
$ kubectl taint node k8s-w1 dedicated:NoSchedule-

14.11 节点维护:Cordon / Drain

升级内核、换硬盘、清磁盘前的标准动作:

# 对象:Node —— 标记 k8s-w1 不可调度(不再分配新 Pod,但不驱逐已有的)
$ kubectl cordon k8s-w1

# 对象:Node —— 驱逐 k8s-w1 上所有 Pod(自动迁移到其他节点)
$ kubectl drain k8s-w1 \
    --ignore-daemonsets \
    --delete-emptydir-data \
    --grace-period=60 \
    --timeout=5m

# 对象:Node —— 维护完成后恢复调度
$ kubectl uncordon k8s-w1

注意

  • --ignore-daemonsets 是必须的,DaemonSet 不受 drain 影响
  • --delete-emptydir-data 会删除 emptyDir 数据,确认无状态再用
  • 数据层(StatefulSet)慎用 drain,先确认副本数足够

14.12 批量与一次性操作

# 对象:Pod —— 删除所有 Failed 状态的 Pod
$ kubectl delete pods --field-selector=status.phase=Failed

# 对象:Pod —— 删除所有 Succeeded 状态的 Pod(清理 Job 残留)
$ kubectl delete pods --field-selector=status.phase=Succeeded

# 对象:Pod —— 按 label 批量删除
$ kubectl delete pods -l app=web

# 对象:Pod —— 强制删除卡死的 Pod(节点失联场景,谨慎使用)
$ kubectl delete pod web-xxx --force --grace-period=0

# 对象:所有工作负载 —— 删除 my-ns 命名空间下所有 Pod / Deployment / Service
$ kubectl delete all --all -n my-ns

# 对象:Namespace —— 删除整个命名空间(连带所有资源)
$ kubectl delete ns my-ns

危险操作提示kubectl delete all --all 不会删 PV/PVC/ConfigMap/Secret/Ingress/CRD,别误以为"清空了"。

14.13 输出格式化:让命令变脚本

# 对象:Pod —— 输出 JSON / YAML 完整定义
$ kubectl get pod web-xxx -o json
$ kubectl get pod web-xxx -o yaml

# 对象:Pod —— 只取 Pod 的 IP
$ kubectl get pod web-xxx -o jsonpath='{.status.podIP}'

# 对象:Pod —— 取所有容器的镜像列表
$ kubectl get pod web-xxx -o jsonpath='{.spec.containers[*].image}'

# 对象:Pod —— 循环输出每个 Pod 的名字和 IP(每行一个)
$ kubectl get pods -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.podIP}{"\n"}{end}'

# 对象:Pod —— 自定义输出列
$ kubectl get pods -o custom-columns=NAME:.metadata.name,IP:.status.podIP,NODE:.spec.nodeName

# 对象:Pod —— 配合 jq 做复杂过滤(找出非 Running 的 Pod 名)
$ kubectl get pods -o json | jq '.items[] | select(.status.phase!="Running") | .metadata.name'

# 对象:Pod —— wide 模式,多看 IP 和节点
$ kubectl get pods -o wide

14.14 应急排障 SOP(线上告警 → 定位)

按下面顺序执行,90% 的问题在 3 分钟内定位

# Step 1. 对象:Pod —— 全集群找非 Running 的 Pod(定位是哪个 Pod 异常)
$ kubectl get pods -A | grep -vE 'Running|Completed'

# Step 2. 对象:Pod —— 看异常 Pod 的事件(镜像拉不下来?资源不够?)
$ kubectl describe pod web-xxx -n production | tail -20

# Step 3. 对象:Pod —— 看上一次崩溃前的日志(如果是 CrashLoopBackOff)
$ kubectl logs web-xxx -n production --previous --tail=100

# Step 4. 对象:Pod —— 看现在的实时日志
$ kubectl logs web-xxx -n production --tail=100 -f

# Step 5. 对象:Endpoints —— 看 Service web-svc 后端是不是 <none>
$ kubectl get endpoints web-svc -n production

# Step 5b. 对象:临时 Pod —— 在集群里实测 Service 通不通
$ kubectl run tmp --rm -it --image=busybox:1.31 -n production \
    -- wget -qO- http://web-svc:80/health

# Step 6. 对象:Node —— 看节点 Conditions 是否健康(磁盘 / 内存 / PID 压力)
$ kubectl describe node k8s-w1 | grep -A5 Conditions

# Step 6b. 对象:Node —— 看节点资源用量
$ kubectl top nodes

# Step 7. 对象:Pod 内的容器 —— 实在搞不定,进容器看
$ kubectl exec -it web-xxx -n production -- sh

14.15 本章小结

K8s 命令虽多,但 80% 的日常运维只用到这些:

场景对象命令
看资源Pod / Svc / Deploykubectl get pods/svc/deploy -o wide
看日志Podkubectl logs <pod> -f / --previous
看事件Pod / Eventkubectl describe pod <pod> / kubectl get events --sort-by=.lastTimestamp
进容器Pod 内容器kubectl exec -it <pod> -- sh
改副本Deploymentkubectl scale deploy <deploy> --replicas=N
改镜像Deploymentkubectl set image deploy/<deploy> <container>=<image>
重启Deploymentkubectl rollout restart deploy/<deploy>
回滚Deploymentkubectl rollout undo deploy/<deploy>
转发Service / Podkubectl port-forward svc/<svc> 8080:80
维护节点Nodekubectl drain <node> --ignore-daemonsets

剩下的 20%,记住两件事:

  1. kubectl api-resources 查所有可用资源名
  2. kubectl <command> --help 永远比搜索引擎快