Kubernetes 基础体系 · 第 5/83 篇。示例基于 Kubernetes 当前稳定 API;弃用、版本偏差、云厂商差异和生产风险会明确说明。
kubectl 完整工作流:Context、查询、Patch、Debug、输出和安全
kubectl 是 Kubernetes API 的客户端。它本身通常不直接管理 Pod、Deployment 或 Node 的内部状态,而是:
- 从 kubeconfig 选择集群、身份和默认命名空间;
- 将命令转换为 Kubernetes API 请求;
- 由 API Server 完成认证、授权、准入、默认值处理和持久化;
- 根据服务器返回的对象或状态生成终端输出。
因此,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 apply、patch、delete 也都要经过 API Server 的认证、授权和准入链路。
1. 先理解 kubeconfig 和 Context
1.1 kubeconfig 中的三个基本对象
kubeconfig 是一个 YAML 配置文件,主要包含三类对象:
clusters:集群 API Server 地址和 CA 配置;users:访问身份,例如客户端证书、Bearer Token 或exec认证插件;contexts:把一个集群、一个用户和一个默认命名空间组合起来。
概念上,一个 Context 可以表示为:
例如:
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
输出 yes 或 no 表示当前身份在指定范围内是否通过授权检查。它是授权检查,不是对象存在性检查;即使返回 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
deployment 和 deployment.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
输出中的 READY、STATUS、RESTARTS 是面向人的摘要,不是完整状态模型。例如 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 观察到的状态。
spec 和 status 必须分开理解。用户或控制器通常修改 spec,控制器根据实际情况更新 status。修改 status 并不能直接让工作负载变健康。
describe:适合事件和控制器摘要
kubectl describe pod api-7f6d9c8b7d-x2k4m -n payments
kubectl describe deployment api -n payments
describe 不是 API 对象的规范序列化格式。它是 kubectl 根据对象和相关信息生成的诊断文本,适合人工阅读,但不适合作为稳定脚本接口。
2.3 metadata、spec、status 的实际关系
假设 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:断言当前值;move、copy:移动或复制值。
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}
]'
执行过程是:
- API Server 读取当前对象;
test检查当前副本数是否仍为 3;- 若不是 3,整个 Patch 失败;
- 若是 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
排查顺序通常是:
- 镜像仓库地址、镜像名和 Tag 是否正确;
- 节点是否能访问仓库;
- 私有仓库凭据是否存在;
- ServiceAccount 是否引用了正确的
imagePullSecrets; - 镜像架构是否与节点匹配;
- 仓库是否发生限流或证书错误。
检查 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
}'
如果 reason 为 OOMKilled,通常说明容器超过了内存限制,或者在节点内存压力下被杀死。还要检查资源配置:
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 exec、logs 和 port-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 状态;
- 是否允许
exec、port-forward、临时容器,要单独评估。
get、list、watch、logs、exec、create、patch 和 delete 并不是同一种权限。允许读取 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 完整学习路线:从 Pod 与控制面到安全、运维和 Operator
- 上一篇:Kubernetes 声明式配置:YAML、默认值、字段所有权、Apply 和 Diff
- 下一篇:Kubernetes Label、Selector、Annotation 与 OwnerReference:对象关系设计
- 延伸:Kubernetes 工作负载排障:Pending、CrashLoop、ImagePull、OOM 和 Probe
官方资料
本文依据 Kubernetes、CNCF 与相关项目官方文档重新梳理;正文和生产清单由 WR BLOG 编写。

评论
0 条讨论