资源对象与 API 设计
API Group
Kubernetes 里所有资源都通过 API Server 的 REST API 操作,URL 路径本身就体现了资源的组织方式。为了避免所有资源挤在一起,K8s 按功能领域把 API 分成若干组(Group):
- 核心组(core group):历史最早的一批资源,组名为空,路径是
/api/v1(单数api)。Pod、Service、Node、Namespace、ConfigMap、Secret、PV/PVC 都在这一组。 - 命名组(named group):后来新增的资源都归属某个具体的组,路径是
/apis/组名/版本(复数apis)。例如 Deployment 在 apps 组,路径是/apis/apps/v1。
几条完整的资源路径长这样:
# 命名组、命名空间级资源
/apis/apps/v1/namespaces/default/deployments/web
# 核心组、命名空间级资源
/api/v1/namespaces/default/pods/web-6d9f8c5b7-x2k9m
# 集群级资源(路径里没有 namespaces 这一段)
/apis/storage.k8s.io/v1/storageclasses/standard
/api/v1/nodes/node-1
命名空间级资源的路径里带 namespaces/空间名 一段;集群级资源(Node、StorageClass、ClusterRole 等)不带。
常见组与资源对照:
| 组 | 路径 | 常见资源 |
|---|---|---|
| 核心组(组名为空) | /api/v1 | Pod、Service、Node、Namespace、ConfigMap、Secret、PersistentVolume、PersistentVolumeClaim |
| apps | /apis/apps/v1 | Deployment、StatefulSet、DaemonSet、ReplicaSet |
| batch | /apis/batch/v1 | Job、CronJob |
| networking.k8s.io | /apis/networking.k8s.io/v1 | Ingress、NetworkPolicy |
| rbac.authorization.k8s.io | /apis/rbac.authorization.k8s.io/v1 | Role、ClusterRole、RoleBinding、ClusterRoleBinding |
| storage.k8s.io | /apis/storage.k8s.io/v1 | StorageClass、CSIDriver |
| apiextensions.k8s.io | /apis/apiextensions.k8s.io/v1 | CustomResourceDefinition(CRD) |
| policy | /apis/policy/v1 | PodDisruptionBudget |
分组的意义在于:每个子系统可以独立演进版本、独立开关,CRD 扩展本质上也是往 API Server 注册一个新的组。
想直接看原始 API 也不需要 Dashboard:
# 看核心组下有哪些资源
kubectl get --raw /api/v1 | head -c 500
# 看 apps 组 v1 版本下的资源列表
kubectl get --raw /apis/apps/v1 | python3 -m json.tool | head -40
YAML 里的 apiVersion 就是「组/版本」的拼接:核心组没有组名,只写 v1;命名组要写完整,如 apps/v1、batch/v1、networking.k8s.io/v1。
Version
同一个组里可以并存多个版本,版本号代表这套 API 的成熟度:
| 级别 | 典型命名 | 默认开启 | 兼容性承诺 | 适用场景 |
|---|---|---|---|---|
| Alpha | v1alpha1 | 否(需要显式开 feature gate) | 不保证,可能有 bug,随时整个删掉 | 尝鲜、测试环境 |
| Beta | v1beta1 | 通常是 | 功能基本稳定,但字段细节仍可能变 | 非关键业务可谨慎使用 |
| Stable(GA) | v1、v2 | 是 | 长期维护、向后兼容 | 生产环境 |
几点实践经验:
- 一个对象在 etcd 里只存一份(按内部的 storage version 存储),不同版本之间由 API Server 做转换。你用 v1 读写的对象,控制器内部可能用的是另一个版本,互相不影响。
- beta 资源毕业到 stable 后,老的 beta 版本会被标记废弃(deprecated),若干个大版本后彻底移除。最出名的例子是 Ingress:
extensions/v1beta1→networking.k8s.io/v1beta1→networking.k8s.io/v1(1.19 GA,旧版本从 1.22 起全部移除)。
查看集群当前支持的所有「组/版本」组合:
kubectl api-versions
admissionregistration.k8s.io/v1
apiextensions.k8s.io/v1
apps/v1
authentication.k8s.io/v1
authorization.k8s.io/v1
autoscaling/v1
autoscaling/v2
batch/v1
certificates.k8s.io/v1
coordination.k8s.io/v1
networking.k8s.io/v1
policy/v1
rbac.authorization.k8s.io/v1
scheduling.k8s.io/v1
storage.k8s.io/v1
v1
(输出有删减)
注意最后那行孤零零的 v1,它就是核心组——没有组名,所以只显示版本号。
beta 版 API 会随集群升级被移除,还在用它的资源升级后会创建失败。升级前对照发行说明检查 kubectl api-resources 的输出,或者用 pluto 这类工具扫描现有清单里是否还引用已废弃的 apiVersion。
Kind
Kind 是资源的类型名。YAML 里 apiVersion + kind 两个字段共同确定「我要创建的是什么东西」。
常用 Kind 与 apiVersion 对照表:
| Kind | apiVersion | 说明 |
|---|---|---|
| Pod | v1 | 最小调度单位,一组共享网络的容器 |
| Service | v1 | 服务发现与负载均衡入口 |
| ConfigMap / Secret | v1 | 配置与敏感信息 |
| Namespace | v1 | 命名空间(集群级) |
| Node | v1 | 集群节点(集群级) |
| PersistentVolume | v1 | 集群级存储资源 |
| PersistentVolumeClaim | v1 | 存储申请(命名空间级) |
| ServiceAccount | v1 | Pod 访问 API 的身份 |
| Deployment | apps/v1 | 无状态应用 |
| StatefulSet | apps/v1 | 有状态应用 |
| DaemonSet | apps/v1 | 每个节点跑一份 |
| ReplicaSet | apps/v1 | 副本集,一般由 Deployment 管理,不直接用 |
| Job | batch/v1 | 一次性任务 |
| CronJob | batch/v1 | 定时任务 |
| Ingress | networking.k8s.io/v1 | 七层流量入口 |
| NetworkPolicy | networking.k8s.io/v1 | Pod 间网络访问控制 |
| StorageClass | storage.k8s.io/v1 | 存储类,动态供给 PV(集群级) |
| Role / ClusterRole | rbac.authorization.k8s.io/v1 | RBAC 权限定义 |
| HorizontalPodAutoscaler | autoscaling/v2 | 自动扩缩容 |
| CustomResourceDefinition | apiextensions.k8s.io/v1 | 自定义资源定义(集群级) |
这张表不用背,日常用命令现查更靠谱:
kubectl api-resources
NAME SHORTNAMES APIVERSION NAMESPACED KIND
configmaps cm v1 true ConfigMap
endpoints ep v1 true Endpoints
namespaces ns v1 false Namespace
nodes no v1 false Node
persistentvolumeclaims pvc v1 true PersistentVolumeClaim
persistentvolumes pv v1 false PersistentVolume
pods po v1 true Pod
secrets v1 true Secret
serviceaccounts sa v1 true ServiceAccount
services svc v1 true Service
daemonsets ds apps/v1 true DaemonSet
deployments deploy apps/v1 true Deployment
replicasets rs apps/v1 true ReplicaSet
statefulsets sts apps/v1 true StatefulSet
cronjobs cj batch/v1 true CronJob
jobs batch/v1 true Job
ingresses ing networking.k8s.io/v1 true Ingress
networkpolicies netpol networking.k8s.io/v1 true NetworkPolicy
clusterroles rbac.authorization.k8s.io/v1 false ClusterRole
clusterrolebindings rbac.authorization.k8s.io/v1 false ClusterRoleBinding
(输出有删减)
输出里每一列都有用:
- SHORTNAMES 是简写,
kubectl get po等价于kubectl get pods。 - NAMESPACED 表示是否命名空间级资源(见下文 Namespace 一节)。
- 想精确过滤可以加参数:
kubectl api-resources --namespaced=false只看集群级资源,kubectl api-resources --api-group=apps只看 apps 组。
再进一步,想知道某个 Kind 的字段怎么写,用 explain 查,比翻官方文档快:
kubectl explain deployment.spec.template.spec.containers
api-resources、api-versions、explain 这三个命令分别回答「集群里有哪些资源」「支持哪些版本」「字段怎么写」三个问题,够用了。
Metadata
每个资源对象都有一个 metadata 段,记录对象的身份信息和公共字段。常用字段如下:
| 字段 | 谁填 | 说明 |
|---|---|---|
| name | 用户 | 对象名,同一命名空间内同类型唯一。小写字母、数字、-、.,最长 253 字符 |
| namespace | 用户 | 所属命名空间,不写则用 kubectl 当前 context 的默认空间 |
| labels | 用户 | 键值对标签,用于组织和选择(见 Labels 一节) |
| annotations | 用户/系统 | 键值对注解,存非标识性的附加信息(见 Annotation 一节) |
| uid | 系统 | 集群内全局唯一 ID,由 API Server 在创建时分配 |
| resourceVersion | 系统 | 对象内部版本号,每修改一次就递增,用于乐观并发控制 |
| generation | 系统 | spec 的代数,spec 每变一次加 1,控制器靠它判断「这版期望状态处理过没有」 |
| creationTimestamp | 系统 | 创建时间(RFC 3339 格式) |
| deletionTimestamp | 系统 | 对象带 finalizer 时,删除请求会先写这个字段,表示「正在删除中」 |
| ownerReferences | 控制器 | 记录属主对象(见 OwnerReference 一节) |
| finalizers | 控制器 | 删除前必须完成的清理动作列表(见 Finalizer 一节) |
一份真实的 metadata 大概长这样:
metadata:
name: web
namespace: default
labels:
app.kubernetes.io/name: web
annotations:
kubernetes.io/change-cause: "image updated to nginx:1.27"
# ---- 以下字段由系统维护,不需要也不应该手写 ----
uid: 3f8c1d2e-7a4b-4c5d-9e6f-1a2b3c4d5e6f
resourceVersion: "482135"
generation: 2
creationTimestamp: "2024-05-01T08:00:00Z"
想「改名」只能删掉重建。resourceVersion 则参与并发控制:两个客户端同时改一个对象,后提交的会因为版本号对不上而冲突,必须重新读取再改。
Labels
Label 是挂在对象上的键值对标签,是 K8s 组织和选择资源的基础机制:Service 靠 label 找到要代理的 Pod,Deployment 靠 label 管理 Pod,日常运维查询也靠 label 过滤。
格式要求:
- key 可以带一段 DNS 前缀,形如
app.kubernetes.io/name;不带前缀的app、env也合法。 - value 最长 63 字符,字母或数字开头结尾,中间可以有
-、_、.。
官方约定了一套推荐标签(前缀 app.kubernetes.io),建议直接采用:
| 标签 | 含义 | 示例 |
|---|---|---|
| app.kubernetes.io/name | 应用名 | mysql |
| app.kubernetes.io/instance | 实例名,区分同一应用的多份部署 | mysql-prod |
| app.kubernetes.io/version | 应用版本 | "8.0.36" |
| app.kubernetes.io/component | 在架构中的角色 | database |
| app.kubernetes.io/part-of | 所属的大系统 | mall |
| app.kubernetes.io/managed-by | 管理工具 | helm |
YAML 示例:
metadata:
name: web
labels:
app.kubernetes.io/name: web
app.kubernetes.io/instance: web-prod
app.kubernetes.io/version: "1.2.3"
env: prod
tier: frontend
命令行增删改:
# 打标签
kubectl label pod web-6d9f8c5b7-x2k9m env=prod
# 改标签(key 已存在时要加 --overwrite)
kubectl label pod web-6d9f8c5b7-x2k9m env=staging --overwrite
# 删标签(key 后面跟个减号)
kubectl label pod web-6d9f8c5b7-x2k9m env-
# 查询时显示标签列
kubectl get pods --show-labels
Label 创建后可以随时改,改 label 不会重建对象——这也是它好用的地方。但要小心:改了 label 可能让 Pod 脱离(或被错误纳入)某个 Service、Deployment 的管理范围。
Selector
Selector(标签选择器)是「按 label 筛选对象」的表达式,有两种语法。
等值匹配(Equality-based)
最简单的一种:判断 key 等不等于某个值。支持 =、==、!= 三种操作符,多个条件之间是与关系。YAML 里对应 matchLabels:
selector:
matchLabels:
app: web
tier: frontend
集合匹配(Set-based)
更灵活:判断 key 的值是否落在某个集合里。YAML 里对应 matchExpressions,支持四种运算符:
| 运算符 | 含义 | 是否需要 values |
|---|---|---|
| In | 值在集合中 | 是 |
| NotIn | 值不在集合中 | 是 |
| Exists | 存在这个 key(不管值是什么) | 否 |
| DoesNotExist | 不存在这个 key | 否 |
selector:
matchLabels:
app: web
matchExpressions:
- key: tier
operator: In
values: ["frontend", "backend"]
- key: track
operator: NotIn
values: ["canary"]
- key: debug
operator: DoesNotExist
matchLabels 和 matchExpressions 可以混用,所有条件之间都是与关系。
kubectl 的 -l 查询
日常排障最常用的是 kubectl get 的 -l 参数,两种语法都支持:
# 等值匹配
kubectl get pods -l app=web
kubectl get pods -l 'app=web,tier=frontend'
kubectl get pods -l 'app!=web'
# 集合匹配
kubectl get pods -l 'env in (prod,staging)'
kubectl get pods -l 'env notin (dev)'
kubectl get pods -l 'debug' # Exists:有这个 key 就行
kubectl get pods -l '!debug' # DoesNotExist:没有这个 key
哪里会用到 selector
- Service 的
spec.selector:只支持等值匹配,选出要代理的 Pod。 - Deployment / StatefulSet / DaemonSet / Job 的
spec.selector:管理 Pod 的依据,必须和 Pod 模板里的 labels 对得上,支持两种语法。 - NetworkPolicy 的
podSelector、PVC 的spec.selector、HPA 等也都靠 selector 找目标。
spec.selector 是不可变字段,创建后再改,apply 会直接报错。规划标签时先想清楚,别把每次发版都变的值(比如版本号)放进 selector 命中的 label 里。
Annotation
Annotation 和 Label 一样是键值对,但定位完全不同:它不参与选择,只用来记录附加信息。
| 对比项 | Label | Annotation |
|---|---|---|
| 用途 | 标识对象、被 selector 选择 | 存说明性、辅助性信息 |
| 能否用于查询筛选 | 能(-l、selector) | 不能 |
| value 限制 | 最长 63 字符,字符集受限 | 宽松,可以存一大段文本(如 JSON),但一个对象所有 annotation 总和不能超过 256 KB |
| 典型使用者 | 人和控制器 | 工具、控制器、第三方系统 |
常见的系统注解(看对象详情时经常会遇到):
| 注解 | 含义 |
|---|---|
| kubectl.kubernetes.io/last-applied-configuration | kubectl apply 记录的上一次完整配置,三方合并靠它 |
| kubernetes.io/change-cause | 本次变更的说明,会显示在 rollout history 里 |
| deployment.kubernetes.io/revision | Deployment 的修订号,回滚靠它定位 |
| pv.kubernetes.io/bound-by-controller | PVC 由绑定控制器完成绑定的标记 |
| service.beta.kubernetes.io/ 开头的一系列 | 云厂商负载均衡器的各种配置(类型、内网、证书等) |
| nginx.ingress.kubernetes.io/rewrite-target | ingress-nginx 的 URL 重写规则 |
命令行操作和 label 几乎一样:
# 写注解
kubectl annotate deployment web kubernetes.io/change-cause="image updated to nginx:1.27"
# 查看
kubectl describe pod web-6d9f8c5b7-x2k9m | grep -A5 Annotations
# 删除(key 后面跟个减号)
kubectl annotate deployment web kubernetes.io/change-cause-
实际使用中的分工习惯:会被筛选查询的信息放 label(app、env、tier 这类),说明性、配置性、给工具读的信息放 annotation。
Namespace
Namespace 把一个物理集群划分成多个逻辑空间:同一类资源的名字在不同命名空间里可以重复;RBAC 权限、ResourceQuota 配额、NetworkPolicy 网络策略也都以命名空间为配置边界。
注意它提供的是弱隔离:默认情况下不同命名空间的 Pod 网络是互通的。要做到真正的多租户隔离,得配合 RBAC、NetworkPolicy、ResourceQuota 一起用。
初始的四个命名空间
新集群建好后自带四个:
| 命名空间 | 用途 |
|---|---|
| default | 不指定 namespace 时的默认落点 |
| kube-system | 控制平面组件(apiserver、scheduler、CoreDNS 等)所在空间,别把业务放进去 |
| kube-public | 所有人(包括未认证用户)可读,主要放集群信息 ConfigMap,一般用不到 |
| kube-node-lease | 存放每个节点的 Lease 对象,kubelet 靠它上报心跳,决定节点是否 Ready |
哪些资源是命名空间级,哪些是集群级
| 命名空间级(属于某个 ns) | 集群级(不属于任何 ns) |
|---|---|
| Pod、Service、Endpoints | Node |
| Deployment、StatefulSet、DaemonSet、ReplicaSet | Namespace 自身 |
| Job、CronJob | PersistentVolume、StorageClass |
| ConfigMap、Secret、ServiceAccount | ClusterRole、ClusterRoleBinding |
| PersistentVolumeClaim | CustomResourceDefinition |
| Ingress、NetworkPolicy | APIService、Webhook 配置 |
| Role、RoleBinding、ResourceQuota、LimitRange、HPA | 以及所有声明为 Cluster 作用域的 CRD |
记不清就用命令查(就是 api-resources 输出里的 NAMESPACED 列):
# 只看命名空间级资源
kubectl api-resources --namespaced=true
# 只看集群级资源
kubectl api-resources --namespaced=false
常用操作:
kubectl get ns # 列出所有命名空间
kubectl create ns web # 创建
kubectl get pods -n kube-system # 指定命名空间查询
kubectl get pods -A # 跨所有命名空间查询
kubectl config set-context --current --namespace=web # 把当前 context 的默认空间切到 web
删除命名空间会连带删除其中的所有资源——Pod、Service、ConfigMap,以及 PVC 绑定的数据,且没有回收站。生产环境执行前务必确认空间名没敲错。
OwnerReference
K8s 里很多对象不是孤立的:Deployment 创建 ReplicaSet,ReplicaSet 创建 Pod。这种父子关系靠子对象 metadata 里的 ownerReferences 字段记录:
# 一个由 ReplicaSet 创建的 Pod
metadata:
name: web-6d9f8c5b7-x2k9m
ownerReferences:
- apiVersion: apps/v1
kind: ReplicaSet
name: web-6d9f8c5b7
uid: 3f8c1d2e-7a4b-4c5d-9e6f-1a2b3c4d5e6f
controller: true
blockOwnerDeletion: true
字段含义:
apiVersion/kind/name/uid:四元组精确指向属主对象,uid 用来防止「同名不同对象」的混淆。controller: true:表示这个属主是当前的管理控制器,一个对象最多只能有一个 controller 属主。blockOwnerDeletion: true:前台级联删除时,属主要等这个子对象删完才能消失(见 Garbage Collection 一节)。
这个字段一般不需要手写——控制器创建子对象时会自动填上。你只要知道它存在,因为接下来的级联删除、垃圾回收全靠它工作。
查看某个 Pod 的属主:
kubectl get pod web-6d9f8c5b7-x2k9m -o jsonpath='{.metadata.ownerReferences}'
几条约束要知道:
- 命名空间级对象(如 Pod)的属主必须在同一个命名空间内,或者是集群级对象;集群级对象的属主只能是集群级对象。
- 跨命名空间设置属主是无效的,垃圾回收器会把它当作「属主不存在」处理,可能误删子对象——不要这么干。
Finalizer
Finalizer 是 metadata 里的一个字符串列表,作用只有一个:只要这个列表非空,对象就删不掉。
有了 finalizer,删除流程变成两段:
- 你发出删除请求,API Server 不真删,只给对象打上
deletionTimestamp,对象进入「删除中」状态。 - 对应的控制器看到这个状态后开始清理(比如释放云盘、通知负载均衡器),清理完把自己负责的那个 finalizer 从列表里摘掉。等列表空了,对象才真正从 etcd 里消失。
常见的 finalizer:
| Finalizer | 出现在哪 | 保护什么 |
|---|---|---|
| kubernetes.io/pv-protection | PersistentVolume | PV 还被 PVC 绑定时禁止删除,防止数据被误回收 |
| kubernetes.io/pvc-protection | PersistentVolumeClaim | PVC 正被 Pod 使用时禁止删除 |
| kubernetes | Namespace | 命名空间控制器要先把空间内所有资源删干净,才放行空间本身 |
| foregroundDeletion | 各种对象 | 前台级联删除时使用(见下一节) |
举个例子,直接删一个还在绑定状态的 PV:
kubectl delete pv data-pv
persistentvolume "data-pv" deleted
命令虽然返回了 deleted,但 kubectl get pv 会看到它卡在 Terminating 状态——直到它和 PVC 解绑、控制器摘掉 pv-protection 之后,才真正消失。
命名空间卡在 Terminating
这是新手最常踩的坑:kubectl delete ns xxx 之后,空间永远卡在 Terminating。原因几乎都是某个 finalizer 对应的控制器已经不在了(比如装过某个 Operator 又卸载了、有残留的 APIService),没人来摘 finalizer,删除流程就一直等下去。
排查和手动清理:
# 看卡在哪个 finalizer 上
kubectl get ns xxx -o jsonpath='{.metadata.finalizers}'
# 确认原因后,手动清空 finalizer 强制释放(merge patch)
kubectl patch ns xxx -p '{"metadata":{"finalizers":null}}' --type=merge
# 普通资源同理,例如卡住的 PV
kubectl patch pv data-pv -p '{"metadata":{"finalizers":null}}' --type=merge
清空 finalizer 等于跳过了控制器本该做的清理:云盘可能没释放、负载均衡器可能还挂着、外部资源可能泄漏。先搞清楚为什么卡住(通常是控制器异常或被提前卸载),能恢复控制器就别手动摘。
Garbage Collection
垃圾回收器(Garbage Collector)是 controller-manager 里的一个组件,工作方式很直接:盯着 ownerReferences,属主被删了,就把它的子对象也删掉。K8s 的级联删除就是它实现的。
删属主时有三种传播策略(propagationPolicy):
| 策略 | 行为 | 适用场景 |
|---|---|---|
| background(默认) | 属主立即删除,子对象由 GC 在后台异步清理 | 日常删除,速度最快 |
| foreground | 属主先进入「删除中」状态(挂上 foregroundDeletion finalizer),等所有子对象删完,属主才消失 | 想确认删干净了再继续,比如删完马上重建 |
| orphan | 只删属主,子对象留下变成「孤儿」,ownerReferences 被摘掉 | 想保留 Pod 现场排查,或把资源移交给别的控制器接管 |
用 --cascade 参数指定:
# 默认就是 background,可以不写
kubectl delete deployment web
# 前台删除:命令会阻塞,直到 RS、Pod 全部删完才返回
kubectl delete deployment web --cascade=foreground
# 孤儿删除:Deployment 没了,但 Pod 还在跑
kubectl delete deployment web --cascade=orphan
现在理解上一节的内容了:foreground 策略正是靠 foregroundDeletion finalizer 实现的——finalizer 卡住属主,GC 先删子对象,删完摘 finalizer,属主才真正消失。三个机制是一套组合拳。
线上 Pod 出问题想保留现场(日志、进程状态)慢慢看,又想先删掉 Deployment 阻止它重建——--cascade=orphan 就是干这个的。排查完记得手动删 Pod,孤儿对象没有控制器管理,会一直占着资源。
kubectl
kubectl 是操作集群的唯一入口,本质上是个 API Server 的 HTTP 客户端。这节只讲日常最高频的几块。
kubeconfig 与 context
kubectl 靠 kubeconfig 文件找到集群并完成认证,默认位置是 ~/.kube/config。文件里有三类信息:
clusters:集群地址和 CA 证书users:认证凭据(客户端证书或 token)contexts:把「哪个集群 + 哪个用户 + 默认哪个命名空间」绑成一组,起个名字
多集群管理的日常就是切 context:
kubectl config get-contexts # 列出所有 context,带 * 的是当前
kubectl config current-context # 看当前用的是哪个
kubectl config use-context prod # 切换
kubectl config set-context --current --namespace=web # 改当前 context 的默认命名空间
也可以用环境变量 KUBECONFIG 或 --kubeconfig 参数临时指定别的配置文件,多个文件用冒号拼接会合并生效。
声明式 apply vs 命令式 create
两种操作风格:
# 命令式:直接下指令,快,但操作过程不可复现
kubectl create deployment web --image=nginx:1.27
kubectl scale deployment web --replicas=3
# 声明式:写 YAML 描述期望状态,apply 让集群向它对齐
kubectl apply -f web.yaml
apply 的特点是可重复执行:文件没变就什么都不做,变了就增量更新,靠 kubectl.kubernetes.io/last-applied-configuration 注解做三方合并。生产环境一律用 apply,YAML 纳入 Git 管理;命令式只在临时调试时用。
get 的常用输出
kubectl get pods # 简要列表
kubectl get pods -o wide # 多几列:Pod IP、所在节点
kubectl get pod web-xxx -o yaml # 完整对象,排障时常看
kubectl get pod web-xxx -o json # JSON 格式,方便交给 jq 处理
# jsonpath 精确取字段
kubectl get pods -o jsonpath='{.items[*].metadata.name}'
kubectl get pod web-xxx -o jsonpath='{.spec.containers[0].image}'
# 自定义列
kubectl get pods -o custom-columns=NAME:.metadata.name,NODE:.spec.nodeName
# 持续观察变化
kubectl get pods -w
describe:排障第一站
get 只能看概要,对象出问题时先看 describe——它会给出详情、状态和关联信息,尤其末尾的 Events:为什么调度失败、为什么镜像拉不下来,基本都写在这里。
kubectl describe pod web-xxx
kubectl describe node node-1
--dry-run=client 生成 YAML 骨架
手写 YAML 最容易忘字段,让 kubectl 先生成骨架再改,是日常使用频率最高的技巧之一:
# 生成 Deployment 骨架(不真正创建)
kubectl create deployment web --image=nginx:1.27 --dry-run=client -o yaml > web.yaml
# Service、ConfigMap、Job 同理
kubectl create service clusterip web --tcp=80:80 --dry-run=client -o yaml
kubectl create configmap app-config --from-literal=LOG_LEVEL=info --dry-run=client -o yaml
kubectl create job test --image=busybox:1.36 --dry-run=client -o yaml -- echo hello
两种 dry-run 的区别:--dry-run=client 只在本地渲染,不联系集群;--dry-run=server 会把请求发给 API Server 走一遍校验和准入控制,但不落库,适合提交前验证。
--dry-run=client -o yaml 生成骨架、--dry-run=server 提交前校验,能减少一大半「YAML 写错被 API Server 拒掉」的来回折腾。
YAML 最佳实践
新手写清单容易乱,按下面的清单过一遍,能避开大部分问题。
结构:固定四段式
一份资源清单固定四段,按这个顺序写:
apiVersion: apps/v1 # 1. 用哪个 API 版本
kind: Deployment # 2. 什么类型
metadata: # 3. 元数据:名字、命名空间、标签
name: web
spec: # 4. 期望状态
...
版本与标签
- 镜像不用 latest:写
image: nginx:1.27.5这种明确版本。latest 会让不同节点拉到不同镜像,回滚也说不清回到哪。 - apiVersion 用 stable:生产环境优先 v1、apps/v1 这类 GA 版本,别用 alpha/beta。
- labels 配齐全:至少带上
app.kubernetes.io/name、instance、version,后面监控、灰度、查询都靠它们。 - namespace 显式写出:别依赖 context 的默认值,换台机器 apply 就落到别的空间去了。
resources 必须配
不配 requests/limits 的容器是最低 QoS 等级,节点资源紧张时最先被杀;反过来也可能把节点资源吃光拖垮邻居。哪怕先估个值也要写:
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi
格式细节
- 缩进统一 2 个空格,禁用 Tab(YAML 语法不允许 Tab)。
- 数字型字符串要加引号:
version: "1.0"、端口类环境变量value: "8080",否则会被解析成数字。 - 小心 YAML 的布尔坑:
yes/no/on/off不加引号会被当成布尔值,值是这类词时统一加引号。 - 多个资源可以放一个文件用
---分隔;但建议一个文件一个资源,配合目录管理更清晰。
提交前校验
# 语法 + 服务端校验(推荐,会走一遍准入控制)
kubectl apply -f web.yaml --dry-run=server
# 离线按官方 schema 校验(适合 CI,kubeconform 是 kubeval 的继任者)
kubeconform -strict web.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
namespace: production
labels:
app.kubernetes.io/name: web
app.kubernetes.io/instance: web-prod
app.kubernetes.io/version: "1.2.3"
spec:
replicas: 3
selector:
matchLabels:
app.kubernetes.io/name: web
template:
metadata:
labels:
app.kubernetes.io/name: web
spec:
containers:
- name: web
image: nginx:1.27.5
ports:
- containerPort: 80
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi