二十、Kubernetes基础-69-kubernetes-declarative-yaml-deep-dive
Kubernetes 集群声明式文件 YAML 深度解析:从语法基础到资源对象描述全攻略
摘要:本文从 YAML 语法规范出发,深入剖析 Kubernetes 声明式资源管理的核心机制,涵盖数据类型映射、多文档流、锚点引用、apiVersion 版本协商、GVK/GVR 模型、资源对象四层结构(TypeMeta / ObjectMeta / Spec / Status)以及声明式与命令式的本质差异,并结合生产级示例揭示
kubectl apply的三路合并(three-way merge)策略与 Server-Side Apply 的演进。适合希望系统性掌握 Kubernetes 资源编排原理的中高级工程师。
一、YAML 语法深度解析
1.1 YAML 本质与设计哲学
YAML(YAML Ain’t Markup Language)是一种以数据为中心的序列化格式,其设计目标是人类可读性最大化。与 JSON 和 XML 不同,YAML 使用缩进而非括号或标签来表达层级结构,这一设计直接影响了 Kubernetes 声明式文件的书写范式。
YAML 1.2 规范(2009 年发布)明确了 YAML 是 JSON 的超集——任何合法的 JSON 文档都是合法的 YAML 文档。Kubernetes 的 API Server 同时接受 JSON 和 YAML 格式,但在人工编写场景下 YAML 凭借其简洁性成为事实标准。
1.2 基础数据类型与映射规则
YAML 定义了三种基本数据结构,它们与 Kubernetes API 对象的 Go 结构体之间存在精确的映射关系:
1.2.1 映射(Mapping)—— 对应 Go struct / map
# 块风格映射(Block Mapping)
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deploy
namespace: production
labels:
app: nginx
tier: frontend
version: v1.21.6
# 流风格映射(Flow Mapping)—— 等效写法
metadata: {name: nginx-deploy, namespace: production}
技术细节:YAML 中的 key 遵循以下规则:
- 同一层级 key 不可重复,重复时后者覆盖前者(这是常见的配置错误来源)
- key 的类型默认为字符串,无需引号
- 包含特殊字符(
:,#,{,},[,])的 key 必须用引号包裹
1.2.2 序列(Sequence)—— 对应 Go slice / array
# 块风格序列(Block Sequence)
spec:
containers:
- name: nginx
image: nginx:1.21.6
ports:
- containerPort: 80
protocol: TCP
- containerPort: 443
protocol: TCP
- name: sidecar-log
image: fluent-bit:2.1
# 流风格序列(Flow Sequence)
ports: [{containerPort: 80, protocol: TCP}, {containerPort: 443, protocol: TCP}]
关键注意:序列中每个元素以 - 开头,- 后的缩进空格是语法的一部分。混合使用 Tab 和 Space 是 YAML 解析失败的首要原因,Kubernetes 严格要求使用空格缩进。
1.2.3 标量(Scalar)—— 对应 Go 基本类型
# 字符串(默认不需要引号)
name: nginx-deploy
# 需要引号的场景
annotation_value: "true" # 防止被解析为布尔值
port_string: "8080" # 防止被解析为整数
special: "hello: world" # 包含冒号
# 多行字符串 —— 在 ConfigMap 中极为常见
data:
nginx.conf: | # 字面量块(Literal Block):保留换行符
server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
}
}
init-script.sh: |+ # |+ 保留末尾换行
#!/bin/bash
echo "initializing..."
one-liner: > # 折叠块(Folded Block):换行替换为空格
this is a very long string
that spans multiple lines
but will be folded into one
1.3 YAML 类型自动推断陷阱
YAML 的隐式类型推断是 Kubernetes 配置中最常见的坑之一:
# 类型推断规则表
string_value: hello # → string
integer_value: 42 # → int
float_value: 3.14 # → float
boolean_true: true # → bool (true)
boolean_yes: yes # → bool (true) ⚠️ 注意!
boolean_on: on # → bool (true) ⚠️ 注意!
null_value: ~ # → null
null_value2: null # → null
octal_value: 0o777 # → int (YAML 1.2)
octal_legacy: 0777 # → string (YAML 1.2) / int (YAML 1.1) ⚠️
# 科学计数法
scientific: 1.0e+3 # → float 1000.0
# 日期(YAML 1.1 支持,1.2 中为字符串)
date_value: 2024-01-15 # → date / string(取决于解析器版本)
# 安全写法:明确使用引号
env_value: "yes" # → string "yes"
port: "8080" # → string "8080"
version: "1.0" # → string "1.0"(避免被截断为 1)
生产建议:在 env 中的 value 字段、ConfigMap 的 data 字段、labels 和 annotations 的值中,一律使用双引号显式标注字符串类型,避免隐式类型转换导致的运行时错误。
1.4 锚点与别名(Anchors & Aliases)
锚点机制允许在 YAML 中实现节点复用,在大型 Kubernetes 配置中可显著减少重复:
# 定义锚点
.default-resources: &default-resources
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
.default-probes: &default-probes
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /readyz
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
spec:
containers:
- name: app
image: myapp:v1
resources: *default-resources # 引用锚点
<<: *default-probes # 合并键(Merge Key):展开映射
- name: worker
image: myworker:v1
resources: *default-resources # 复用同一资源配置
注意:kubectl 在发送到 API Server 之前会完全展开锚点引用,API Server 侧不感知锚点语法。因此锚点仅是客户端层面的语法糖。
1.5 多文档流(Multi-Document Stream)
单个 YAML 文件中可包含多个文档,使用 --- 分隔:
# all-in-one.yaml
---
apiVersion: v1
kind: Namespace
metadata:
name: myapp
---
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: myapp
data:
DATABASE_URL: "postgres://db:5432/myapp"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
namespace: myapp
spec:
replicas: 3
# ... 省略
---
apiVersion: v1
kind: Service
metadata:
name: myapp-svc
namespace: myapp
spec:
selector:
app: myapp
ports:
- port: 80
targetPort: 8080
执行 kubectl apply -f all-in-one.yaml 时,kubectl 按顺序逐个提交资源。在生产环境中,推荐将资源按依赖顺序排列:Namespace → RBAC → ConfigMap/Secret → Deployment → Service → Ingress。
二、Kubernetes 资源对象描述方法
2.1 声明式 vs 命令式:本质差异
Kubernetes 提供三种资源管理方式,理解它们的本质差异是正确使用 YAML 的前提:
| 维度 | 命令式命令 | 命令式对象配置 | 声明式对象配置 |
|---|---|---|---|
| 典型命令 | kubectl run / kubectl expose |
kubectl create -f |
kubectl apply -f |
| 操作语义 | 告诉系统"做什么" | 告诉系统"创建这个" | 告诉系统"确保状态是这样" |
| 幂等性 | ❌ 非幂等 | ❌ 重复执行报错 | ✅ 天然幂等 |
| 变更追踪 | 无 | 无 | last-applied-configuration 注解 |
| 适用场景 | 调试、一次性任务 | 初始化部署 | 生产环境标准 |
声明式的核心思想:用户只需描述期望状态(Desired State),Kubernetes 的控制器(Controller)负责驱动**当前状态(Current State)**向期望状态收敛。这就是声明式 YAML 存在的根本意义。
2.2 API 版本与 GVK/GVR 模型
每个 Kubernetes 资源对象由 GVK(Group-Version-Kind) 唯一标识,对应 API 路径上的 GVR(Group-Version-Resource):
GVK: apps/v1/Deployment
│ │ └── Kind(资源类型)
│ └── Version(API 版本)
└── Group(API 组)
GVR: apps/v1/deployments
│ │ └── Resource(复数形式,用于 REST 路径)
│ └── Version
└── Group
对应 API 路径: /apis/apps/v1/namespaces/{ns}/deployments/{name}
API 版本成熟度
| 阶段 | 格式 | 含义 | 稳定性保证 |
|---|---|---|---|
| Alpha | v1alpha1 |
实验性功能,默认关闭 | 无,可能随时移除 |
| Beta | v1beta1 |
功能基本稳定,默认开启 | 较高,但字段可能变更 |
| Stable | v1 |
正式发布 | 完全向后兼容 |
查看集群支持的 API 版本:
# 查看所有 API 资源及其版本
kubectl api-resources -o wide
# 查看特定资源支持的 API 版本
kubectl api-versions | grep apps
# 查看资源的详细字段说明(极为实用)
kubectl explain deployment.spec.strategy --api-version=apps/v1
kubectl explain pod.spec.containers --recursive
2.3 资源对象四层结构模型
每个 Kubernetes 资源对象的 YAML 描述遵循统一的四层结构,这是由 API Server 的反序列化机制决定的:
┌─────────────────────────────────────────┐
│ TypeMeta(类型元信息) │ ← apiVersion + kind
├─────────────────────────────────────────┤
│ ObjectMeta(对象元信息) │ ← metadata 块
├─────────────────────────────────────────┤
│ Spec(期望状态) │ ← 用户定义的期望状态
├─────────────────────────────────────────┤
│ Status(实际状态) │ ← 控制器填充,用户不应编写
└─────────────────────────────────────────┘
2.3.1 TypeMeta —— 资源的身份证
apiVersion: apps/v1 # API 组/版本,决定了序列化/反序列化使用的 schema
kind: Deployment # 资源类型,必须与 apiVersion 匹配
apiVersion 的解析规则:
- 核心组(Core Group):直接写
v1,对应路径/api/v1/。包括 Pod、Service、ConfigMap、Secret、Namespace 等 - 命名组(Named Group):写
group/version,对应路径/apis/{group}/{version}/。如apps/v1、batch/v1、networking.k8s.io/v1
2.3.2 ObjectMeta —— 资源的 DNA
metadata:
# === 核心标识字段 ===
name: nginx-deploy # 资源名称(同 namespace 内唯一)
namespace: production # 命名空间(集群级资源无此字段)
# === 系统生成字段(不应手动填写)===
uid: a1b2c3d4-e5f6-7890-abcd-ef1234567890 # 全局唯一 ID
resourceVersion: "12345678" # etcd 中的 modifiedIndex,乐观并发控制
generation: 3 # spec 变更计数器
creationTimestamp: "2024-01-15T08:30:00Z"
# === 标签(Labels)—— 用于筛选和分组 ===
labels:
app.kubernetes.io/name: nginx # 推荐标签规范(k8s.io 推荐)
app.kubernetes.io/instance: nginx-prod
app.kubernetes.io/version: "1.21.6"
app.kubernetes.io/component: webserver
app.kubernetes.io/part-of: web-platform
app.kubernetes.io/managed-by: helm
# === 注解(Annotations)—— 用于存储非筛选元数据 ===
annotations:
kubernetes.io/change-cause: "update nginx to 1.21.6"
prometheus.io/scrape: "true"
prometheus.io/port: "9113"
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"apps/v1","kind":"Deployment",...}
# === 终结器(Finalizers)—— 资源删除保护 ===
finalizers:
- kubernetes.io/pvc-protection
# === 属主引用(OwnerReferences)—— 资源间父子关系 ===
ownerReferences:
- apiVersion: apps/v1
kind: ReplicaSet
name: nginx-deploy-7c8b9d6f4
uid: xxx-yyy-zzz
controller: true
blockOwnerDeletion: true
Labels vs Annotations 的本质区别:
| 维度 | Labels | Annotations |
|---|---|---|
| 索引 | 存入 etcd 索引,支持 selector 查询 |
不建索引,不支持选择器 |
| 长度限制 | key ≤ 63 字符,value ≤ 63 字符 | value 可达 256KB |
| 用途 | 资源筛选、Service 路由、调度约束 | 元数据存储、工具配置、审计信息 |
| 性能 | 高频查询优化 | 不适合高频查询 |
2.3.3 Spec —— 声明式的灵魂
Spec 是用户声明期望状态的核心区域,不同资源类型的 Spec 结构差异巨大。以 Deployment 为例进行深度拆解:
spec:
# === 副本控制 ===
replicas: 3 # 期望副本数
revisionHistoryLimit: 10 # 保留的历史 ReplicaSet 数量
progressDeadlineSeconds: 600 # 部署进度超时时间
# === 选择器(不可变字段,创建后不可修改)===
selector:
matchLabels:
app: nginx
matchExpressions: # 集合选择器(更灵活)
- key: tier
operator: In
values: [frontend, web]
- key: environment
operator: NotIn
values: [test]
# === 更新策略 ===
strategy:
type: RollingUpdate # RollingUpdate | Recreate
rollingUpdate:
maxSurge: 25% # 滚动更新时最大超出副本比例
maxUnavailable: 25% # 滚动更新时最大不可用副本比例
# === Pod 模板(核心中的核心)===
template:
metadata:
labels:
app: nginx # 必须匹配 selector
annotations:
checksum/config: "sha256:abc123" # 配置变更触发滚动更新技巧
spec:
# --- 调度策略 ---
nodeSelector:
kubernetes.io/os: linux
disktype: ssd
affinity:
podAntiAffinity: # Pod 反亲和:分散部署
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app: nginx
topologyKey: kubernetes.io/hostname
tolerations: # 容忍污点
- key: "node-role.kubernetes.io/master"
operator: "Exists"
effect: "NoSchedule"
# --- 安全上下文 ---
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 2000
seccompProfile:
type: RuntimeDefault
# --- 服务账号 ---
serviceAccountName: nginx-sa
automountServiceAccountToken: false
# --- 初始化容器 ---
initContainers:
- name: init-config
image: busybox:1.36
command: ['sh', '-c', 'cp /config-template/* /config/']
volumeMounts:
- name: config-template
mountPath: /config-template
- name: config
mountPath: /config
# --- 主容器 ---
containers:
- name: nginx
image: nginx:1.21.6
imagePullPolicy: IfNotPresent # Always | IfNotPresent | Never
# 端口声明(信息性,不做实际限制)
ports:
- name: http
containerPort: 80
protocol: TCP
# 资源配额(QoS 类别的决定因素)
resources:
requests: # 调度依据,保证分配
cpu: 100m # 100 millicores = 0.1 CPU
memory: 128Mi
ephemeral-storage: 1Gi
limits: # 硬上限,超出触发 OOMKill/throttle
cpu: 500m
memory: 512Mi
ephemeral-storage: 2Gi
# 环境变量注入
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name # Downward API
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
- name: CPU_LIMIT
valueFrom:
resourceFieldRef:
containerName: nginx
resource: limits.cpu
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: password
envFrom:
- configMapRef:
name: app-config
prefix: APP_ # 添加前缀避免冲突
# 健康检查三件套
startupProbe: # 启动探针(1.20+ 推荐)
httpGet:
path: /healthz
port: 8080
failureThreshold: 30
periodSeconds: 10
livenessProbe: # 存活探针
httpGet:
path: /healthz
port: 8080
httpHeaders:
- name: X-Health-Check
value: liveness
initialDelaySeconds: 0 # startupProbe 存在时可设为 0
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 3
readinessProbe: # 就绪探针
httpGet:
path: /readyz
port: 8080
periodSeconds: 5
successThreshold: 1
failureThreshold: 3
# 生命周期钩子
lifecycle:
postStart:
exec:
command: ["/bin/sh", "-c", "echo 'started' > /tmp/started"]
preStop: # 优雅终止关键
exec:
command: ["/bin/sh", "-c", "nginx -s quit && sleep 10"]
# 卷挂载
volumeMounts:
- name: config
mountPath: /etc/nginx/conf.d
readOnly: true
- name: data
mountPath: /usr/share/nginx/html
- name: tmp
mountPath: /tmp
# --- 卷声明 ---
volumes:
- name: config
configMap:
name: nginx-config
items:
- key: default.conf
path: default.conf
mode: 0644
- name: data
persistentVolumeClaim:
claimName: nginx-data-pvc
- name: tmp
emptyDir:
sizeLimit: 100Mi
- name: config-template
configMap:
name: nginx-template
# --- 优雅终止 ---
terminationGracePeriodSeconds: 60 # 默认 30s,根据业务调整
# --- DNS 策略 ---
dnsPolicy: ClusterFirst
dnsConfig:
options:
- name: ndots
value: "2" # 减少不必要的 DNS 查询
2.3.4 Status —— 控制平面的反馈
Status 由控制器自动填充,反映资源的实际运行状态:
# kubectl get deployment nginx-deploy -o yaml 查看
status:
observedGeneration: 3 # 控制器已处理的最新 generation
replicas: 3 # 当前总副本数
readyReplicas: 3 # 就绪副本数
updatedReplicas: 3 # 已更新副本数
availableReplicas: 3 # 可用副本数
conditions:
- type: Available
status: "True"
lastTransitionTime: "2024-01-15T08:35:00Z"
lastUpdateTime: "2024-01-15T08:35:00Z"
reason: MinimumReplicasAvailable
message: "Deployment has minimum availability."
- type: Progressing
status: "True"
lastTransitionTime: "2024-01-15T08:30:00Z"
lastUpdateTime: "2024-01-15T08:35:00Z"
reason: NewReplicaSetAvailable
message: 'ReplicaSet "nginx-deploy-7c8b9d6f4" has successfully progressed.'
重要:用户编写 YAML 时绝不应该包含 status 段。声明式配置只关注期望状态(Spec),实际状态由系统维护。
三、kubectl apply 的三路合并原理
3.1 三路合并(Three-Way Strategic Merge Patch)
kubectl apply 是声明式管理的核心命令,其背后的合并策略远比表面看起来复杂:
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ Last Applied │ │ Live Object │ │ Local File │
│ Configuration │ │ (etcd 中的当前值) │ │ (用户本次提交) │
│ (annotation 中) │ │ │ │ │
└────────┬─────────┘ └────────┬─────────┘ └────────┬─────────┘
│ │ │
└───────────────────────┼───────────────────────┘
│
┌────────▼─────────┐
│ Three-Way Merge │
│ 计算 Patch │
└────────┬─────────┘
│
┌────────▼─────────┐
│ 发送 Strategic │
│ Merge Patch 到 │
│ API Server │
└──────────────────┘
合并决策矩阵:
| Last Applied | Live Object | Local File | 决策 |
|---|---|---|---|
| A | A | A | 无变更,跳过 |
| A | A | B | 用户修改,更新为 B |
| A | B | A | 其他人/控制器修改,保留 B |
| A | B | B | 双方都改为相同值,保留 B |
| A | B | C | 冲突,以 Local File 为准更新为 C |
| 无 | 无 | A | 新增字段 A |
| A | A | 无 | 用户删除,移除字段 |
| A | B | 无 | 用户删除,移除字段(即使 Live 有变更) |
3.2 Server-Side Apply(SSA)
Kubernetes 1.22 GA 的 Server-Side Apply 解决了客户端 apply 的多项局限:
# 启用 Server-Side Apply
kubectl apply -f deployment.yaml --server-side --field-manager=my-controller
# 查看字段属主
kubectl get deployment nginx-deploy -o yaml --show-managed-fields
SSA 引入了**字段所有权(Field Ownership)**概念:
managedFields:
- manager: kubectl-client-side-apply
operation: Apply
apiVersion: apps/v1
time: "2024-01-15T08:30:00Z"
fieldsType: FieldsV1
fieldsV1:
f:spec:
f:replicas: {} # kubectl 拥有 replicas 字段
f:template:
f:spec:
f:containers:
k:{"name":"nginx"}:
f:image: {} # kubectl 拥有 image 字段
- manager: hpa-controller
operation: Update
apiVersion: apps/v1
time: "2024-01-15T09:00:00Z"
fieldsType: FieldsV1
fieldsV1:
f:spec:
f:replicas: {} # HPA 也声明了对 replicas 的所有权
当多个 manager 声明同一字段的所有权时,需要通过 --force-conflicts 解决冲突。这在 HPA + Deployment 的 replicas 字段冲突场景中最为常见。
四、核心资源对象 YAML 速查
4.1 工作负载类
# === Pod(最小调度单元,生产中很少直接创建)===
apiVersion: v1
kind: Pod
metadata:
name: debug-pod
spec:
containers:
- name: debug
image: nicolaka/netshoot:latest
command: ["sleep", "infinity"]
# === StatefulSet(有状态应用)===
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
serviceName: mysql-headless # 必须关联 Headless Service
replicas: 3
podManagementPolicy: OrderedReady # OrderedReady | Parallel
updateStrategy:
type: RollingUpdate
rollingUpdate:
partition: 0 # 分区更新:只更新序号 >= partition 的 Pod
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:8.0
volumeClaimTemplates: # 自动创建 PVC
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: fast-ssd
resources:
requests:
storage: 50Gi
# === DaemonSet(每节点一个 Pod)===
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: node-exporter
namespace: monitoring
spec:
selector:
matchLabels:
app: node-exporter
updateStrategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1 # DaemonSet 无 maxSurge
template:
metadata:
labels:
app: node-exporter
spec:
hostNetwork: true # 使用宿主机网络
hostPID: true
containers:
- name: node-exporter
image: prom/node-exporter:v1.7.0
ports:
- containerPort: 9100
hostPort: 9100
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: 200m
memory: 128Mi
# === Job / CronJob(批处理)===
apiVersion: batch/v1
kind: CronJob
metadata:
name: db-backup
spec:
schedule: "0 2 * * *" # 每天凌晨 2 点
concurrencyPolicy: Forbid # Allow | Forbid | Replace
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 5
startingDeadlineSeconds: 200
jobTemplate:
spec:
backoffLimit: 3 # 失败重试次数
activeDeadlineSeconds: 3600 # 单次 Job 超时
ttlSecondsAfterFinished: 86400 # 完成后自动清理
template:
spec:
restartPolicy: OnFailure # Job 中只能是 OnFailure 或 Never
containers:
- name: backup
image: postgres:15
command: ["pg_dump", "-h", "postgres-svc", "-U", "admin", "mydb"]
4.2 服务发现与网络
# === Service(四种类型)===
apiVersion: v1
kind: Service
metadata:
name: myapp-svc
spec:
type: ClusterIP # ClusterIP | NodePort | LoadBalancer | ExternalName
selector:
app: myapp
ports:
- name: http
port: 80 # Service 端口
targetPort: 8080 # Pod 端口(可使用命名端口)
protocol: TCP
sessionAffinity: ClientIP # None | ClientIP
sessionAffinityConfig:
clientIP:
timeoutSeconds: 10800
---
# === Ingress ===
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp-ingress
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
nginx.ingress.kubernetes.io/rate-limit: "100"
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
ingressClassName: nginx
tls:
- hosts:
- myapp.example.com
secretName: myapp-tls
rules:
- host: myapp.example.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: myapp-api-svc
port:
number: 80
- path: /
pathType: Prefix
backend:
service:
name: myapp-frontend-svc
port:
number: 80
4.3 配置与存储
# === ConfigMap ===
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data: # 文本数据
DATABASE_HOST: "postgres-svc"
LOG_LEVEL: "info"
config.yaml: |
server:
port: 8080
timeout: 30s
database:
maxConnections: 100
binaryData: # 二进制数据(Base64 编码)
logo.png: iVBORw0KGgoAAAANSUhEUgAA...
immutable: true # 不可变 ConfigMap(1.21+ GA,提升性能)
---
# === Secret ===
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
type: Opaque # Opaque | kubernetes.io/tls | kubernetes.io/dockerconfigjson
data: # Base64 编码
username: YWRtaW4= # echo -n 'admin' | base64
password: cGFzc3dvcmQxMjM=
stringData: # 明文(提交时自动 Base64 编码)
connection-string: "postgres://admin:password123@db:5432/myapp"
---
# === PersistentVolumeClaim ===
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: data-pvc
spec:
accessModes:
- ReadWriteOnce # RWO | ROX | RWX | RWOP(1.22+)
storageClassName: fast-ssd
resources:
requests:
storage: 100Gi
volumeMode: Filesystem # Filesystem | Block
4.4 RBAC 权限控制
# === ServiceAccount ===
apiVersion: v1
kind: ServiceAccount
metadata:
name: app-sa
namespace: production
annotations:
# AWS IRSA
eks.amazonaws.com/role-arn: "arn:aws:iam::123456789:role/app-role"
---
# === Role(命名空间级别)===
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: pod-reader
namespace: production
rules:
- apiGroups: [""] # 核心组
resources: ["pods", "pods/log"]
verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list"]
resourceNames: ["myapp"] # 限定特定资源名
---
# === RoleBinding ===
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: read-pods
namespace: production
subjects:
- kind: ServiceAccount
name: app-sa
namespace: production
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io
五、生产级 YAML 编写规范与最佳实践
5.1 资源配额 QoS 等级
Pod 的 QoS 等级由 resources 配置自动决定,直接影响 OOM 时的驱逐优先级:
| QoS 等级 | 条件 | OOM 驱逐优先级 |
|---|---|---|
| Guaranteed | 所有容器均设置 requests = limits(CPU 和 Memory) | 最低(最后被驱逐) |
| Burstable | 至少一个容器设置了 requests 或 limits,但不满足 Guaranteed 条件 | 中等 |
| BestEffort | 没有任何容器设置 requests 和 limits | 最高(最先被驱逐) |
生产铁律:所有生产工作负载必须至少达到 Burstable 级别,核心服务必须达到 Guaranteed 级别。
5.2 安全加固清单
spec:
# Pod 级别安全
securityContext:
runAsNonRoot: true # 禁止 root 运行
runAsUser: 65534 # nobody
runAsGroup: 65534
fsGroup: 65534
seccompProfile:
type: RuntimeDefault # 启用 seccomp
# 容器级别安全
containers:
- name: app
securityContext:
allowPrivilegeEscalation: false # 禁止提权
readOnlyRootFilesystem: true # 只读根文件系统
capabilities:
drop: ["ALL"] # 丢弃所有 Linux capabilities
add: ["NET_BIND_SERVICE"] # 按需添加
privileged: false # 禁止特权模式
5.3 镜像策略
containers:
- name: app
# ✅ 正确:使用完整的 SHA256 摘要
image: myregistry.com/myapp@sha256:a1b2c3d4e5f6...
# ✅ 可接受:使用明确的语义化版本标签
image: myregistry.com/myapp:v1.2.3
# ❌ 避免:使用 latest 标签
image: myregistry.com/myapp:latest
# 拉取策略
imagePullPolicy: IfNotPresent # 配合具体版本标签使用
5.4 标签规范
遵循 Kubernetes 推荐标签 规范:
metadata:
labels:
# 推荐标签
app.kubernetes.io/name: myapp
app.kubernetes.io/instance: myapp-production
app.kubernetes.io/version: "1.2.3"
app.kubernetes.io/component: api-server
app.kubernetes.io/part-of: ecommerce-platform
app.kubernetes.io/managed-by: helm
# 自定义业务标签
team: platform-engineering
cost-center: "CC-12345"
environment: production
六、YAML 调试与验证工具链
6.1 客户端验证
# 干运行(不实际提交,仅验证语法和 schema)
kubectl apply -f deployment.yaml --dry-run=client -o yaml
# 服务端干运行(经过 admission webhook 验证)
kubectl apply -f deployment.yaml --dry-run=server -o yaml
# 使用 kubectl diff 预览变更
kubectl diff -f deployment.yaml
6.2 静态分析工具
# kubeval —— 校验 YAML 是否符合 Kubernetes schema
kubeval deployment.yaml --kubernetes-version 1.28.0
# kubeconform —— kubeval 的高性能替代
kubeconform -kubernetes-version 1.28.0 -strict deployment.yaml
# kube-linter —— 安全和最佳实践检查
kube-linter lint deployment.yaml
# pluto —— 检测已废弃的 API 版本
pluto detect-files -d ./k8s/
6.3 explain 命令深度使用
# 递归查看所有字段
kubectl explain pod.spec --recursive | head -100
# 查看特定字段的详细说明
kubectl explain deployment.spec.strategy.rollingUpdate
# 指定 API 版本
kubectl explain ingress.spec --api-version=networking.k8s.io/v1
七、总结
Kubernetes 声明式 YAML 的本质是用结构化数据描述基础设施的期望状态。掌握 YAML 语法只是第一步,真正的技术深度在于理解:
- GVK/GVR 模型决定了资源的 API 路径和序列化方式
- **四层结构(TypeMeta / ObjectMeta / Spec / Status)**是所有资源对象的统一骨架
- 三路合并策略和 Server-Side Apply 是声明式管理的核心引擎
- QoS 等级、安全上下文、资源配额是生产环境 YAML 编写的安全底线
- 声明式配置应纳入 GitOps 工作流,实现版本控制、审计追踪和自动化交付
声明式不仅是一种配置格式,更是一种基础设施管理哲学:描述"是什么",而非"做什么"。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)