Kubernetes 基础体系 · 第 5/83 篇。示例基于 Kubernetes 当前稳定 API;弃用、版本偏差、云厂商差异和生产风险会明确说明。

kubectl 完整工作流:Context、查询、Patch、Debug、输出和安全

kubectl 是 Kubernetes API 的客户端。它本身通常不直接管理 Pod、Deployment 或 Node 的内部状态,而是:

  1. 从 kubeconfig 选择集群、身份和默认命名空间;
  2. 将命令转换为 Kubernetes API 请求;
  3. 由 API Server 完成认证、授权、准入、默认值处理和持久化;
  4. 根据服务器返回的对象或状态生成终端输出。

因此,kubectl 的核心不是“记住一堆命令”,而是理解一条完整的数据流:

flowchart LR
    U[kubectl 命令] --> K[加载 kubeconfig]
    K --> C[选择 context]
    C --> A[认证插件或证书]
    A --> S[API Server]
    S --> AU[认证与 RBAC 授权]
    AU --> AD[准入、默认值、校验]
    AD --> E[etcd 持久化]
    E --> S
    S --> R[对象或状态响应]
    R --> O[表格、YAML、JSON、JSONPath]

例如,kubectl get pods -n app 并不是从某个本地 Pod 缓存读取数据,而是向当前集群的 API Server 查询 Pod 资源。kubectl applypatchdelete 也都要经过 API Server 的认证、授权和准入链路。


1. 先理解 kubeconfig 和 Context

1.1 kubeconfig 中的三个基本对象

kubeconfig 是一个 YAML 配置文件,主要包含三类对象:

  • clusters:集群 API Server 地址和 CA 配置;
  • users:访问身份,例如客户端证书、Bearer Token 或 exec 认证插件;
  • contexts:把一个集群、一个用户和一个默认命名空间组合起来。

概念上,一个 Context 可以表示为:

Context=(cluster,user,namespace)\text{Context} = (\text{cluster}, \text{user}, \text{namespace})

例如:

apiVersion: v1
kind: Config
clusters:
- name: prod
  cluster:
    server: https://k8s-prod.example.com
    certificate-authority-data: <base64>
users:
- name: ci-readonly
  user:
    exec:
      command: cloud-login
contexts:
- name: prod-readonly
  context:
    cluster: prod
    user: ci-readonly
    namespace: default
current-context: prod-readonly

这里的 namespace 只是命名空间默认值,不是权限边界。权限仍然由 Kubernetes 的 RBAC 和其他授权机制决定。

1.2 kubeconfig 的加载顺序

kubectl 通常按以下方式确定 kubeconfig:

kubectl --kubeconfig ./prod.config get pods

如果没有显式指定 --kubeconfig,kubectl 会读取 KUBECONFIG 环境变量;没有该变量时,通常使用用户目录下的默认 kubeconfig:

echo "$KUBECONFIG"
ls -l "${HOME}/.kube/config"

多个 kubeconfig 文件可以通过路径列表合并。不同平台的路径分隔符不同,Linux 和 macOS 通常使用冒号:

export KUBECONFIG="$HOME/.kube/config:$HOME/.kube/prod.config"
kubectl config get-contexts

合并配置时,名称相同的条目可能发生覆盖或合并。生产环境不应只凭文件名判断最终生效内容,应使用 kubectl config view 检查结果。

1.3 查看和切换 Context

列出 Context:

kubectl config get-contexts

典型输出:

CURRENT   NAME             CLUSTER   AUTHINFO      NAMESPACE
*         dev-admin        dev       dev-admin     default
          prod-readonly    prod      ci-readonly   monitoring

切换当前 Context:

kubectl config use-context prod-readonly

查看当前 Context:

kubectl config current-context

在执行变更命令前,建议显式检查实际目标:

kubectl config current-context
kubectl config view --minify

--minify 只显示当前 Context 相关配置,适合确认当前集群和身份。不要在共享终端、工单或日志中随意使用:

kubectl config view --raw

因为 --raw 可能显示客户端证书、Token 或其他认证材料。

1.4 Context 的命名空间和 -n 的覆盖关系

下面两个命令的查询命名空间不同:

kubectl --context=dev-admin get pods
kubectl --context=dev-admin -n payments get pods

第一个使用 Context 中配置的默认命名空间;第二个使用命令行的 -n payments。命令行参数优先于 Context 的默认命名空间。

可以修改 Context 的默认命名空间:

kubectl config set-context dev-admin --namespace=payments

但这会修改本地 kubeconfig,之后省略 -n 的命令都会受影响。对于生产变更,显式写出:

kubectl --context=prod-admin -n payments get deployment

通常比依赖隐含的当前 Context 和默认命名空间更容易审查。

跨所有命名空间查询:

kubectl get pods --all-namespaces

或者:

kubectl get pods -A

需要注意,-A 只影响命名空间范围,不会绕过 RBAC。如果当前身份没有列出所有命名空间 Pod 的权限,仍会收到 Forbidden

1.5 Context 不是安全确认

Context 只决定请求发往哪里、使用哪个身份和默认命名空间。它不能阻止用户访问另一个 Context,也不能保证“只读”。

例如,一个名称叫 prod-readonly 的用户可能因为 kubeconfig 配置错误实际拥有写权限。因此,在高风险操作前使用:

kubectl auth can-i get pods \
  --context=prod-readonly \
  -n payments

kubectl auth can-i patch deployments \
  --context=prod-readonly \
  -n payments

输出 yesno 表示当前身份在指定范围内是否通过授权检查。它是授权检查,不是对象存在性检查;即使返回 yes,目标对象也可能不存在。


2. 查询资源:从类型、范围到状态

2.1 资源类型和资源实例

Kubernetes API 中有资源类型,例如:

  • pods
  • deployments.apps
  • services
  • nodes
  • customresourcedefinitions.apiextensions.k8s.io

查询资源:

kubectl get pods -n payments
kubectl get deployment api -n payments
kubectl get deployment.apps/api -n payments
kubectl get nodes

deploymentdeployment.apps 通常指向同一个资源,但显式写出 API 组有助于避免资源短名称冲突。

查看服务器支持的资源:

kubectl api-resources

查看某资源的 API 版本、是否命名空间范围、支持的短名称:

kubectl api-resources --api-group=apps

查询服务器版本:

kubectl version

客户端和服务器版本不一定相同。kubectl 与 API Server 通常支持一定范围的版本偏差,但具体兼容性受 Kubernetes 版本和命令影响。不要把“命令在本地能解析”理解成“目标集群一定支持该资源或字段”。

2.2 查询对象的三种视角

同一个对象可以用三种方式观察。

表格视图:适合快速浏览

kubectl get pods -n payments
kubectl get pods -n payments -o wide

输出中的 READYSTATUSRESTARTS 是面向人的摘要,不是完整状态模型。例如 STATUS=Running 不代表应用已经通过业务健康检查。

YAML 或 JSON:适合检查完整对象

kubectl get deployment api -n payments -o yaml
kubectl get pod api-7f6d9c8b7d-x2k4m -n payments -o json

YAML 和 JSON 通常包含:

  • metadata:名称、命名空间、标签、注解、UID、时间戳;
  • spec:期望状态;
  • status:控制器或 kubelet 观察到的状态。

specstatus 必须分开理解。用户或控制器通常修改 spec,控制器根据实际情况更新 status。修改 status 并不能直接让工作负载变健康。

describe:适合事件和控制器摘要

kubectl describe pod api-7f6d9c8b7d-x2k4m -n payments
kubectl describe deployment api -n payments

describe 不是 API 对象的规范序列化格式。它是 kubectl 根据对象和相关信息生成的诊断文本,适合人工阅读,但不适合作为稳定脚本接口。

2.3 metadataspecstatus 的实际关系

假设 Deployment 的 spec.replicas 为 3:

kubectl get deployment api -n payments \
  -o jsonpath='{.spec.replicas}{"\n"}{.status.replicas}{"\n"}{.status.availableReplicas}{"\n"}'

可能输出:

3
3
2

其含义是:

  • 期望副本数是 3;
  • Deployment 目前观察到 3 个副本;
  • 其中只有 2 个被认为可用。

此时不能只看 spec.replicas 判断服务已经完成发布。还应检查:

kubectl rollout status deployment/api -n payments
kubectl get pods -n payments -l app=api

Deployment 控制器会创建或调整 ReplicaSet,ReplicaSet 再创建 Pod;Pod 被调度后,kubelet 创建容器并执行启动、探针和重启逻辑。这是一个异步控制循环,不是一次 API 写入立即完成的事务。

2.4 标签选择器和字段选择器

标签选择器是 Kubernetes 中常见的集合查询方式:

kubectl get pods -n payments -l app=api
kubectl get pods -n payments -l 'app=api,component=backend'
kubectl get pods -n payments -l 'environment in (prod,staging)'

-l 选择的是 metadata.labels,不能直接用来匹配容器镜像、Pod IP 等字段。

字段选择器用于有限的字段过滤,例如:

kubectl get pods -A --field-selector=status.phase=Pending
kubectl get events -A --field-selector=type=Warning

字段选择器支持哪些字段取决于资源和 API Server。不能假设任意 JSON 字段都能用于 --field-selector

2.5 排序、监视和等待

按创建时间排序:

kubectl get pods -n payments --sort-by=.metadata.creationTimestamp

持续监视:

kubectl get pods -n payments --watch

只监听后续事件、不先输出已有对象:

kubectl get pods -n payments --watch-only

等待 Deployment 完成发布:

kubectl rollout status deployment/api \
  -n payments \
  --timeout=120s

--watch 是观察 API 资源变化;rollout status 则包含 Deployment 发布状态的语义。两者都不是永久保证:命令退出成功只能说明该次观察满足条件,不代表未来版本永远健康。

2.6 explain:直接查询字段语义

在不确定字段、嵌套结构或字段类型时,使用服务器提供的 OpenAPI schema:

kubectl explain deployment.spec.strategy
kubectl explain deployment.spec.strategy.rollingUpdate
kubectl explain pod.spec.containers

递归查看:

kubectl explain deployment --recursive

explain 反映目标 API Server 暴露的 schema。对于 CRD,schema 质量取决于 CRD 作者;对于不同 Kubernetes 版本,字段和描述也可能不同。因此应以目标集群的 kubectl explain 和官方版本文档为准。


3. 输出:人看、脚本读和结构化处理

3.1 输出格式的选择

常用输出格式包括:

kubectl get pods -n payments -o wide
kubectl get pod api-xxx -n payments -o yaml
kubectl get pod api-xxx -n payments -o json
kubectl get pods -n payments -o name

-o name 只输出资源名称,例如:

pod/api-xxx
pod/api-yyy

这适合管道处理:

kubectl get pods -n payments -o name |
  xargs -r -n1 kubectl delete -n payments

但删除操作风险很高。管道中应先单独打印并确认目标,再执行修改。

3.2 JSONPath

从对象中提取字段:

kubectl get pods -n payments \
  -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.phase}{"\n"}{end}'

这里:

  • .items[*] 遍历查询结果;
  • .metadata.name 读取名称;
  • {"\t"}{"\n"} 输出制表符和换行;
  • range ... end 构成循环。

提取容器镜像:

kubectl get pod api-xxx -n payments \
  -o jsonpath='{.spec.containers[*].image}{"\n"}'

JSONPath 适合简单提取,但复杂条件、转义和数组处理容易变得难读。可以先输出 JSON,再使用 jq

kubectl get pods -n payments -o json |
  jq -r '.items[] | [.metadata.name, .status.phase] | @tsv'

jq 是外部工具,不是 kubectl 内置依赖;脚本应明确声明运行环境。

3.3 自定义列和稳定性边界

kubectl get pods -n payments \
  -o custom-columns='NAME:.metadata.name,PHASE:.status.phase,NODE:.spec.nodeName'

自定义列适合人工或半自动检查,但字段为空时可能显示 <none>,字段名称也必须与目标资源 schema 匹配。

不要解析默认表格输出作为长期 API。默认列可能随着 Kubernetes 版本、资源类型或 kubectl 版本变化。稳定脚本应优先使用:

  • -o json
  • -o jsonpath
  • -o name
  • 明确的 API 客户端;
  • 对字段缺失和错误退出进行处理。

3.4 获取 Secret 的安全边界

kubectl get secret db-credentials -n payments -o yaml

Secret 的 data 通常是 Base64 编码,不是加密:

kubectl get secret db-credentials -n payments \
  -o jsonpath='{.data.password}' | base64 --decode

Base64 只提供编码,任何能读取 Secret 的身份都可以解码。因此不要把上述输出写入 CI 日志、Shell 历史、聊天记录或工单。Kubernetes 是否在 etcd 中加密 Secret,取决于集群的静态数据加密配置;客户端输出本身不能证明 etcd 已加密。


4. Patch:局部修改对象的三种语义

kubectl patch 用于向已有对象发送局部更新。它与“提交一份完整声明式配置”不同:Patch 的目标是对现有对象进行部分修改。

基本形式:

kubectl patch deployment api \
  -n payments \
  --type=merge \
  -p '{"spec":{"replicas":4}}'

输入是目标资源名称、命名空间、Patch 类型和 Patch 文档。API Server 会验证结果对象;若通过,才会持久化更新。

4.1 JSON Merge Patch

kubectl patch deployment api -n payments \
  --type=merge \
  -p '{"spec":{"replicas":4}}'

JSON Merge Patch 的主要规则是:

  • 对象字段递归合并;
  • 字段值为 null 时通常表示删除该字段;
  • 数组不能按元素合并,数组值会整体替换。

例如,当前对象为:

{
  "metadata": {
    "labels": {
      "app": "api",
      "team": "payments"
    }
  }
}

执行:

kubectl patch deployment api -n payments \
  --type=merge \
  -p '{"metadata":{"labels":{"team":"platform"}}}'

结果通常是:

{
  "metadata": {
    "labels": {
      "app": "api",
      "team": "platform"
    }
  }
}

但如果执行:

kubectl patch deployment api -n payments \
  --type=merge \
  -p '{"spec":{"template":{"spec":{"containers":[{"name":"api","image":"example/api:v2"}]}}}}'

则数组语义需要特别谨慎。Merge Patch 会把 containers 视为一个整体数组;不应把它当成“只更新名为 api 的元素,而保留其他容器”的通用机制。

4.2 JSON Patch

JSON Patch 是一组带路径的操作:

kubectl patch deployment api -n payments \
  --type=json \
  -p='[
    {"op":"replace","path":"/spec/replicas","value":4}
  ]'

常用操作包括:

  • add:添加字段、数组元素或替换已有路径;
  • remove:删除字段或数组元素;
  • replace:替换已有字段;
  • test:断言当前值;
  • movecopy:移动或复制值。

JSON Pointer 路径中的特殊字符需要转义:~ 写作 ~0/ 写作 ~1。例如标签键 example.com/owner 的 JSON Patch 路径是:

/metadata/labels/example.com~1owner

一个带前置条件的 Patch:

kubectl patch deployment api -n payments \
  --type=json \
  -p='[
    {"op":"test","path":"/spec/replicas","value":3},
    {"op":"replace","path":"/spec/replicas","value":4}
  ]'

执行过程是:

  1. API Server 读取当前对象;
  2. test 检查当前副本数是否仍为 3;
  3. 若不是 3,整个 Patch 失败;
  4. 若是 3,再把它改成 4。

这可以避免“我以为副本数是 3,实际另一个操作者已经改成 5,而我仍然覆盖成 4”的一类并发问题。它不是所有并发场景的完整解决方案,但比无条件覆盖更明确。

4.3 Strategic Merge Patch

对于部分内置 Kubernetes 资源,kubectl 支持战略合并 Patch:

kubectl patch deployment api -n payments \
  --type=strategic \
  -p='{"spec":{"template":{"spec":{"containers":[{"name":"api","image":"example/api:v2"}]}}}}'

战略合并依赖 Kubernetes 类型定义中的合并元数据,例如容器列表常按 name 作为合并键。因此它可能只更新名为 api 的容器,而不是替换整个容器数组。

但是,Strategic Merge Patch 不是适用于所有资源的通用协议。特别是 CRD 不具备内置 Go 类型的战略合并元数据,通常不能依赖这种行为。对 CRD,应根据其 schema 和 API 支持选择 JSON Merge Patch、JSON Patch 或 Server-Side Apply。

不要把三种 Patch 混为一谈:

类型 对象字段 数组 适用边界
JSON Merge Patch 递归合并 整体替换 简单局部对象修改
JSON Patch 按路径逐步操作 可按索引操作 需要精确操作或前置断言
Strategic Merge Patch 依赖 Kubernetes 类型元数据 可按合并键合并 主要用于部分内置资源

4.4 Patch、Apply 和 Update 的区别

假设 Deployment 当前有:

spec:
  replicas: 3
  template:
    metadata:
      labels:
        app: api

执行 Patch:

kubectl patch deployment api -n payments \
  --type=merge \
  -p '{"spec":{"replicas":4}}'

只表达“把副本数改为 4”,不会表达完整对象所有权。

执行客户端 Apply:

kubectl apply -f deployment.yaml

Apply 的语义是根据声明配置计算并协调对象字段;历史上客户端 Apply 使用 kubectl.kubernetes.io/last-applied-configuration 保存部分状态。Server-Side Apply 则由 API Server 记录字段管理者:

kubectl apply \
  --server-side \
  --field-manager=platform-deployer \
  -f deployment.yaml

查看字段管理信息:

kubectl get deployment api -n payments -o json \
  | jq '.metadata.managedFields'

如果不同管理者试图修改同一字段,Server-Side Apply 可能产生字段所有权冲突。可以使用:

kubectl apply \
  --server-side \
  --field-manager=platform-deployer \
  --force-conflicts \
  -f deployment.yaml

--force-conflicts 会接管冲突字段,可能覆盖其他管理者的意图,不应作为“修复失败就加参数”的常规做法。

Patch 通常适合:

  • 紧急调整副本数;
  • 添加临时注解;
  • 修改单个标签;
  • 进行带条件的精确更新。

Apply 更适合:

  • 配置文件是长期事实来源;
  • 需要声明字段所有权;
  • 需要 diff、审查和可重复发布。

修改后必须验证实际结果:

kubectl get deployment api -n payments -o yaml
kubectl rollout status deployment/api -n payments

Patch 成功只表示 API 更新被接受,不代表新 Pod 已经启动成功。

4.5 Patch 的并发和恢复风险

Patch 如果不带 resourceVersion 或类似前置条件,常见行为是“读取当前对象并应用修改”,这不等同于业务层面的锁。两个操作者同时修改同一字段时,后到的更新可能覆盖先到的更新。

一种防护方法是先读取并保存资源版本:

kubectl get deployment api -n payments \
  -o jsonpath='{.metadata.resourceVersion}{"\n"}'

然后把资源版本作为 Patch 条件或使用 JSON Patch 的 test。具体可用方式取决于资源和 API 操作;不能假设所有子资源或所有 Patch 类型都自动提供强一致的业务冲突检测。

如果 Patch 触发了错误发布,恢复来源应优先是经过审查的声明式配置:

kubectl apply -f deployment.yaml

如果使用 Deployment 的历史版本,也应先查看历史:

kubectl rollout history deployment/api -n payments
kubectl rollout undo deployment/api -n payments

回滚的是 Deployment 的发布历史,不是任意对象的通用撤销按钮。


5. Debug:从状态到故障路径

排障的基本原则是先确定故障发生在哪一层:

flowchart TD
    A[API 对象是否存在] -->|否| B[查询命名空间、Context、RBAC]
    A -->|是| C[Pod 是否被调度]
    C -->|Pending| D[调度器与事件]
    C -->|已调度| E[容器是否创建]
    E -->|ImagePull| F[镜像、凭据、仓库、网络]
    E -->|启动后退出| G[日志、退出码、OOM、信号]
    E -->|运行但不可用| H[Probe、Service、Endpoint、应用监听]

5.1 第一组命令:确认对象和事件

kubectl get deployment api -n payments
kubectl get pods -n payments -l app=api -o wide
kubectl describe pod <pod-name> -n payments
kubectl get events -n payments --sort-by=.lastTimestamp

如果命令报:

Error from server (NotFound)

优先检查名称、命名空间和 Context,而不是立即认为控制器坏了。

如果报:

Error from server (Forbidden)

这是认证身份已被识别但没有相应授权,使用:

kubectl auth can-i get pods -n payments
kubectl auth can-i list events -n payments

如果 Pod 为 Pending,重点看 describe 最后的 Events,常见路径包括:

  • 没有节点满足资源请求;
  • 节点有不容忍的 taint;
  • 节点选择器或亲和性无匹配;
  • PVC 尚未绑定;
  • 调度器或云厂商容量不足。

“Pending”只是 Pod phase,不是根因。根因通常在事件、调度约束或相关 PVC 状态中。

5.2 CrashLoopBackOff:先看当前日志和上一次日志

kubectl logs <pod-name> -n payments -c api
kubectl logs <pod-name> -n payments -c api --previous

CrashLoopBackOff 表示 kubelet 反复重启容器,并在重启之间采用退避等待。它不是应用退出原因本身。

--previous 很重要:如果容器已经重启,当前容器可能只显示新一轮启动日志,而真正的崩溃信息在上一实例中。

查看退出状态:

kubectl get pod <pod-name> -n payments -o json \
  | jq '.status.containerStatuses[] | {
      name,
      restartCount,
      state,
      lastState,
      ready
    }'

需要区分:

  • 应用主动退出;
  • 进程因未捕获异常退出;
  • 被信号终止;
  • 被 OOM Killer 终止;
  • 启动探针或存活探针失败导致 kubelet 重启。

5.3 ImagePullBackOff:验证镜像和拉取凭据

kubectl describe pod <pod-name> -n payments

常见事件:

Failed to pull image
ErrImagePull
ImagePullBackOff

排查顺序通常是:

  1. 镜像仓库地址、镜像名和 Tag 是否正确;
  2. 节点是否能访问仓库;
  3. 私有仓库凭据是否存在;
  4. ServiceAccount 是否引用了正确的 imagePullSecrets
  5. 镜像架构是否与节点匹配;
  6. 仓库是否发生限流或证书错误。

检查 ServiceAccount:

kubectl get serviceaccount default -n payments -o yaml
kubectl get secret -n payments

不要把 Secret YAML 原样贴到日志中。云厂商的镜像认证还可能由节点角色、工作负载身份或专用插件提供,不能只按通用 imagePullSecrets 推断。

5.4 OOMKilled:区分容器限制和节点压力

检查:

kubectl get pod <pod-name> -n payments -o json \
  | jq '.status.containerStatuses[] | {
      name,
      reason: .lastState.terminated.reason,
      exitCode: .lastState.terminated.exitCode,
      message: .lastState.terminated.message
    }'

如果 reasonOOMKilled,通常说明容器超过了内存限制,或者在节点内存压力下被杀死。还要检查资源配置:

kubectl get pod <pod-name> -n payments -o json \
  | jq '.spec.containers[] | {
      name,
      requests: .resources.requests,
      limits: .resources.limits
    }'

增加 limits.memory 只能改变允许的上限,不一定解决内存泄漏、缓存无界增长或请求突增。若只设置 limit 而不设置 request,调度器对资源预留的判断也可能与预期不同。

5.5 Probe 失败:启动、存活和就绪不是一回事

三类常见探针的作用不同:

  • startupProbe:应用启动阶段的保护;
  • livenessProbe:进程是否需要被重启;
  • readinessProbe:是否接收 Service 流量。

就绪失败通常不会重启容器,但会使 Pod 从可用端点中移除。存活失败可能触发容器重启。启动探针存在时,其他探针通常在启动探针成功前不执行。

查看探针配置:

kubectl get pod <pod-name> -n payments -o json \
  | jq '.spec.containers[] | {
      name,
      startupProbe,
      readinessProbe,
      livenessProbe
    }'

查看相关事件:

kubectl describe pod <pod-name> -n payments

不要只把探针失败归咎于“应用慢”。还应检查:

  • 探针访问的端口是否是容器实际监听端口;
  • HTTP 路径是否正确;
  • 应用是否只监听 127.0.0.1
  • 启动耗时是否超过探针窗口;
  • 探针命令是否依赖镜像中不存在的 Shell 或工具;
  • 服务依赖不可用时,readiness 是否应当失败而 liveness 不应失败。

5.6 execlogsport-forward

进入正在运行的容器:

kubectl exec -it <pod-name> -n payments -c api -- /bin/sh

exec 的前置条件是容器正在运行,且镜像中存在指定程序。很多精简镜像没有 /bin/bash,不能假定所有容器都能交互式登录。

执行单条命令:

kubectl exec <pod-name> -n payments -c api -- \
  printenv APP_ENV

查看多容器 Pod 的指定容器日志:

kubectl logs <pod-name> -n payments -c api

本地临时访问 Pod 或 Service:

kubectl port-forward -n payments service/api 8080:80

然后:

curl http://127.0.0.1:8080/healthz

port-forward 是客户端建立的临时转发,不等同于修改 Service、Ingress 或云负载均衡器。命令退出后转发消失;目标 Pod 发生变化时,连接也可能中断。

5.7 临时调试容器和节点调试

当业务镜像没有 Shell、curl 或诊断工具时,可以创建临时调试容器:

kubectl debug -it pod/<pod-name> \
  -n payments \
  --image=busybox:1.36 \
  --target=api \
  -- sh

这里的 --target=api 请求调试容器加入目标容器的进程命名空间。是否能看到目标进程,取决于容器运行时和节点配置;它不是所有环境都能保证完全一致。

临时容器会改变 Pod 对象并留下调试痕迹,且调试镜像本身也必须可信。生产环境应限制镜像来源、调试权限和临时容器生命周期。

节点调试:

kubectl debug node/<node-name> -it \
  --image=ubuntu:24.04

节点调试通常会创建一个特殊 Pod,并可能具有较高权限、主机文件系统访问或 host namespace 能力。它不是普通容器 Shell,必须按高风险操作处理。执行前先确认:

kubectl auth can-i create pods -n default
kubectl auth can-i update pods/ephemeralcontainers -n payments

实际所需权限取决于调试对象和集群配置。


6. 一个可重复的端到端工作流

下面以 payments 命名空间中的 api Deployment 为例。

第一步:确认目标

kubectl config current-context

kubectl config view --minify \
  -o jsonpath='context={.contexts[0].name}{"\n"}cluster={.contexts[0].context.cluster}{"\n"}user={.contexts[0].context.user}{"\n"}namespace={.contexts[0].context.namespace}{"\n"}'

如果命令行显式指定了 Context,检查时也应显式指定:

kubectl --context=prod-admin config view --minify

第二步:确认授权和资源存在

kubectl auth can-i get deployment/api -n payments
kubectl auth can-i patch deployment/api -n payments
kubectl get deployment api -n payments

资源查询成功不代表 Patch 一定有权限,所以读权限和写权限要分别检查。

第三步:读取结构化状态

kubectl get deployment api -n payments -o json |
  jq '{
    generation: .metadata.generation,
    observedGeneration: .status.observedGeneration,
    desiredReplicas: .spec.replicas,
    replicas: .status.replicas,
    availableReplicas: .status.availableReplicas,
    conditions: .status.conditions
  }'

metadata.generation 通常在期望配置变化时增加;status.observedGeneration 表示控制器已经观察到哪个期望版本。若二者不同,控制器可能尚未处理最新变更,或者控制器存在故障。这个字段是控制器协调状态的重要线索,但不能单独证明发布成功。

第四步:进行最小修改

例如仅将副本数从 3 改为 4,并用 JSON Patch 做条件保护:

kubectl patch deployment api -n payments \
  --type=json \
  -p='[
    {"op":"test","path":"/spec/replicas","value":3},
    {"op":"replace","path":"/spec/replicas","value":4}
  ]'

如果当前值不是 3,命令应失败,而不是静默覆盖其他人的修改。

第五步:观察控制器和 Pod

kubectl rollout status deployment/api \
  -n payments \
  --timeout=120s

kubectl get pods -n payments \
  -l app=api \
  -o wide

kubectl get events -n payments \
  --sort-by=.lastTimestamp

发布超时后不要只重复执行 Patch。先判断是:

  • 新 Pod 未调度;
  • 镜像无法拉取;
  • 容器启动后崩溃;
  • 探针失败;
  • 旧 Pod 未能终止;
  • 资源配额或节点容量不足。

第六步:必要时恢复

如果确认本次变更导致发布失败:

kubectl rollout history deployment/api -n payments
kubectl rollout undo deployment/api -n payments
kubectl rollout status deployment/api -n payments

恢复后仍应检查 Pod、事件和应用指标。回滚 API 对象版本不一定能修复外部依赖、数据库迁移或数据格式变化。


7. 错误处理和命令边界

7.1 常见错误的含义

The connection to the server ... was refused

通常表示 API Server 地址不可达、代理配置错误、网络路径故障或集群控制面不可用。

Unable to connect to the server: x509: certificate ...

通常涉及 CA、服务器证书、系统时间或 TLS 终端配置。不要用下面的方式掩盖问题:

kubectl --insecure-skip-tls-verify get pods

这会关闭服务器证书验证,容易遭受中间人攻击。它只适合受控的临时诊断,不应进入生产脚本或长期 kubeconfig。

Error from server (Unauthorized)

通常表示身份认证失败或认证凭据过期。

Error from server (Forbidden)

通常表示认证成功但 RBAC 等授权检查拒绝。

error: the server doesn't have a resource type ...

可能是资源名称错误、API 组错误、CRD 未安装或目标集群版本不支持该资源。

7.2 命令成功不代表业务成功

下面的命令返回成功:

kubectl apply -f deployment.yaml

只说明 API Server 接受了对象变更。后续仍可能因为调度、镜像、运行时、网络、探针或应用逻辑失败。

同样:

kubectl delete pod <pod-name> -n payments

成功表示删除请求被接受。控制器可能随后创建新 Pod,或者因为副本数、节点和调度约束导致新 Pod 仍然不可用。

脚本应检查退出码,并对异步结果显式等待:

set -euo pipefail

kubectl apply -f deployment.yaml
kubectl rollout status deployment/api -n payments --timeout=120s

set -e 不能替代业务验证,但可以避免前一步失败后继续执行危险命令。

7.3 不要把 describe 当作机器接口

以下做法脆弱:

kubectl describe pod "$POD" | grep -q "Ready"

因为 describe 的文本格式不是稳定 API,字段顺序、缩进和措辞都可能变化。脚本应读取结构化字段:

kubectl get pod "$POD" -n payments -o json |
  jq -e '
    any(.status.conditions[]?;
        .type == "Ready" and .status == "True")
  '

命令退出码由 jq -e 反映条件是否匹配,更适合自动化。


8. 安全:Context、身份和输出都可能泄露权限

8.1 kubeconfig 是敏感文件

检查权限:

ls -l "$HOME/.kube/config"
chmod 600 "$HOME/.kube/config"

但文件权限只是本机保护的一部分。还应注意:

  • users.user.token 可能是可直接使用的长期凭据;
  • client-key-data 可能是客户端私钥;
  • exec 插件会在本地执行程序并返回认证信息;
  • kubeconfig 中的 server 地址和 Context 名称也可能泄露环境信息;
  • CI 日志可能打印命令参数和环境变量。

不要从不可信来源直接合并 kubeconfig,也不要无审查地执行配置中的 exec 插件。云厂商 CLI、OIDC 插件和自定义认证程序可能访问本地凭据或启动外部进程。

8.2 RBAC 是服务器强制执行的权限边界

典型检查:

kubectl auth can-i get pods -n payments
kubectl auth can-i create pods -n payments
kubectl auth can-i patch deployments -n payments
kubectl auth can-i '*' '*' --all-namespaces

最后一个检查只适合明确的权限审计,不能在不受控的脚本中作为常规命令使用。

kubectl--as 参数是请求中的用户模拟字段,不是客户端本地切换身份。只有当前身份具有相应 impersonation 权限时才会成功:

kubectl auth can-i get pods \
  --as=alice@example.com \
  -n payments

不能通过 --as 绕过 RBAC。

8.3 高风险命令

以下命令可能造成明显影响:

kubectl delete pod ...
kubectl delete deployment ...
kubectl patch ...
kubectl apply ...
kubectl edit ...
kubectl debug node/...

尤其要注意:

kubectl delete pods --all -n payments
kubectl delete namespace payments
kubectl apply -f .

--all 和目录级 apply 的影响范围取决于命名空间、文件内容和当前 Context。执行前应先用只读命令打印目标:

kubectl get pods -n payments -l app=api -o name

不要把未经验证的用户输入直接拼接成 Shell 命令或资源名称。即使 kubectl 本身对参数进行解析,命令替换、通配符、xargs 和管道仍可能引入额外风险。

8.4 Secret、日志和调试容器

以下内容都可能包含敏感数据:

  • kubectl get secret -o yaml
  • 应用日志;
  • kubectl exec 中打印的环境变量;
  • 调试容器中的进程命令行;
  • Pod YAML 中的注解、配置和挂载信息;
  • kubectl config view --raw

调试时应避免:

kubectl exec "$POD" -n payments -- env
kubectl logs "$POD" -n payments > public-log.txt

除非确认环境变量和日志内容可以安全外传。生产排障记录应进行凭据、Token、Cookie、连接字符串和个人数据脱敏。

8.5 权限最小化与审计

只读排障身份通常只需要:

  • 对目标命名空间读取 Pod、Deployment、Service、事件;
  • 读取日志;
  • 必要时获取 Pod 状态;
  • 是否允许 execport-forward、临时容器,要单独评估。

getlistwatchlogsexeccreatepatchdelete 并不是同一种权限。允许读取 Pod 不等于允许读取 Secret,也不等于允许进入容器。

集群管理员还应结合 API Server 审计日志观察:

  • 谁在什么 Context 下执行了 Patch 或 Delete;
  • 是否频繁使用 --as
  • 是否创建了临时调试容器;
  • 谁读取了 Secret;
  • CI 使用的 ServiceAccount 是否拥有超出发布范围的权限。

9. 容易混淆的边界

Context 与命名空间

Context 的默认命名空间只影响省略 -n 时的请求范围;它不是隔离机制。真正的访问限制由 API Server 的授权策略决定。

STATUS=Running 与应用可用

Pod phase 为 Running 只说明 Pod 已被节点接纳且至少有容器处于运行相关状态。应用是否接流量应看 readiness、EndpointSlice、Service 和应用响应。

Patch 成功与发布成功

Patch 是 API 对象层面的成功;Deployment 发布、Pod 启动和业务健康属于后续控制循环。必须使用 rollout status、Pod 状态、事件和应用检查继续验证。

kubectl logs 与完整日志

容器日志可能因重启、日志轮转、容器运行时或采集系统而不完整。--previous 只能获取上一实例的日志,不是任意历史日志查询系统。

kubectl debug 与无风险诊断

调试容器和节点调试可能增加权限、访问主机命名空间或暴露敏感进程信息。它们解决的是工具缺失和现场观察问题,同时也扩大了攻击面。

版本一致与命令一致

kubectl 客户端版本、API Server 版本、CRD schema、云厂商认证插件和容器运行时可能不同。命令参数、输出字段、资源类型和调试行为都可能受这些差异影响。遇到版本问题时,应先检查:

kubectl version
kubectl api-resources
kubectl explain <resource> --recursive

再根据目标集群的 API 能力选择命令,而不是只依据本机 kubectl 的帮助文本。


一个可靠的 kubectl 工作流可以压缩为:

确认 Context 和命名空间
→ 确认身份与 RBAC
→ 读取结构化对象和事件
→ 用最小语义执行 Patch 或 Apply
→ 等待控制器完成异步协调
→ 根据 Pod、日志、探针和事件定位故障
→ 用声明式配置或发布历史恢复
→ 避免泄露 kubeconfig、Secret 和调试权限

掌握这条链路后,kubectl 不再只是命令集合,而是观察 Kubernetes 期望状态、实际状态、控制器行为和 API 安全边界的统一入口。


系列导航与关联阅读

官方资料

本文依据 Kubernetes、CNCF 与相关项目官方文档重新梳理;正文和生产清单由 WR BLOG 编写。