七、DaemonSet 和 Job:特殊的工作负载
1. DaemonSet:每个节点都需要的”守护神”
1.1 DaemonSet是什么
DaemonSet 是Kubernetes的一种工作负载控制器,它确保在集群的每个(或特定)节点上都运行一个Pod副本。
用生活化的比喻来理解:
想象一个小区的物业管理:
节点(Node) = 每栋楼DaemonSet Pod = 每栋楼的保安(每栋楼必须有且只有一个保安)
小区新建一栋楼 → 自动分配一个保安拆除一栋楼 → 这栋楼的保安也会撤离核心特点:
-
节点覆盖
- 每个节点运行一个Pod副本(且只有一个)
- 新节点加入集群,自动创建Pod
- 节点离开集群,自动删除Pod
-
不受调度器控制
- DaemonSet Pod绕过Scheduler直接调度
- 即使节点有污点(Taint),也能运行(如果配置了容忍)
-
自动修复
- Pod被删除后,会立即在同一节点上重建
1.2 为什么需要DaemonSet
在前面的章节中,我们学习了Deployment,它管理的Pod可能分散在不同节点上。但有些服务有特殊需求:
必须在每个节点上运行一个实例
想象这些场景:
场景1:日志收集
没有DaemonSet:┌─────────┐ ┌─────────┐ ┌─────────┐│ Node1 │ │ Node2 │ │ Node3 ││ │ │ │ │ ││ 3个Pod │ │ 5个Pod │ │ 2个Pod │└─────────┘ └─────────┘ └─────────┘ ? ? ? 怎么收集日志?要在哪里部署日志收集器?
有DaemonSet:┌─────────┐ ┌─────────┐ ┌─────────┐│ Node1 │ │ Node2 │ │ Node3 ││ Fluentd │ │ Fluentd │ │ Fluentd │ <- 每个节点都有日志收集器│ 3个Pod │ │ 5个Pod │ │ 2个Pod │ 自动收集本节点日志└─────────┘ └─────────┘ └─────────┘场景2:节点监控
需求:监控每个节点的CPU、内存、磁盘解决:在每个节点上运行监控Agent(如node-exporter)场景3:网络插件
需求:每个节点都需要网络组件(如Calico、Flannel)解决:用DaemonSet部署网络插件1.3 DaemonSet的典型应用场景
| 场景 | 示例 | 说明 |
|---|---|---|
| 日志收集 | Fluentd、Filebeat、Logstash | 收集节点上所有容器的日志 |
| 监控Agent | Prometheus Node Exporter、Datadog Agent | 监控节点资源和容器指标 |
| 网络插件 | Calico、Flannel、Weave | 为Pod提供网络功能 |
| 存储插件 | Ceph、GlusterFS客户端 | 提供分布式存储支持 |
| 安全扫描 | Falco、Sysdig | 监控节点安全事件 |
| 性能分析 | cAdvisor | 收集容器性能数据 |
DaemonSet vs Deployment对比:
Deployment:我需要3个副本,调度器帮我分配到合适的节点┌─────────┐ ┌─────────┐ ┌─────────┐│ Node1 │ │ Node2 │ │ Node3 ││ Pod1 │ │ Pod2 │ │ ││ │ │ Pod3 │ │ │└─────────┘ └─────────┘ └─────────┘
DaemonSet:每个节点必须运行一个副本┌─────────┐ ┌─────────┐ ┌─────────┐│ Node1 │ │ Node2 │ │ Node3 ││ Pod1 │ │ Pod2 │ │ Pod3 │ <- 每个节点都有└─────────┘ └─────────┘ └─────────┘2. DaemonSet的实现逻辑和工作原理
2.1 DaemonSet Controller的工作机制
DaemonSet由DaemonSet Controller管理,它的工作原理很像一个”节点巡查员”:
工作流程:
详细步骤解析:
-
持续监听
- DaemonSet Controller watch API Server的Node和Pod资源
- 一旦有变化(新增节点、节点标签改变、Pod删除),立即响应
-
节点匹配
- 检查节点是否满足nodeSelector、affinity等条件
- 检查节点是否有Taint,Pod是否有对应的Toleration
-
Pod创建
- 如果节点需要Pod但没有,创建一个
- 直接指定Pod运行的节点(不通过Scheduler)
- 设置Pod的ownerReference指向DaemonSet
-
Pod删除
- 如果节点不应该运行Pod但有Pod,删除它
- 节点被删除时,自动清理Pod
2.2 DaemonSet调度机制
与Deployment的重要区别:
Deployment Pod创建流程:用户 → API Server → Deployment Controller创建ReplicaSet→ ReplicaSet Controller创建Pod(状态:Pending,未分配节点)→ Scheduler选择节点并绑定→ Kubelet创建容器
DaemonSet Pod创建流程:用户 → API Server → DaemonSet Controller创建Pod→ 直接指定nodeName(绕过Scheduler)→ Kubelet创建容器为什么要绕过Scheduler?
-
明确的调度需求
- DaemonSet知道Pod必须运行在哪个节点
- 不需要Scheduler的复杂调度算法
-
忽略资源限制
- 某些系统组件(如网络插件)必须运行,即使节点资源不足
- Scheduler可能因资源不足拒绝调度,但DaemonSet强制创建
-
忽略污点
- DaemonSet可以配置Toleration容忍所有污点
- 在master节点上运行也没问题
2.3 DaemonSet的自我修复
场景1:Pod被意外删除
# 删除DaemonSet的一个Podkubectl delete pod <daemonset-pod> -n kube-system
# 立即(几秒内)重建kubectl get pods -n kube-system -w# 你会看到Pod立即进入Pending → Running场景2:节点宕机后恢复
节点宕机 → Pod状态变为Unknown→ 节点恢复 → DaemonSet Controller检测到→ 重建Pod场景3:新节点加入集群
# 添加新节点kubeadm join ...
# DaemonSet自动在新节点上创建Podkubectl get pods -o wide# 你会看到新节点上已经有DaemonSet的Pod3. DaemonSet配置详解
3.1 基本YAML结构
最简单的DaemonSet:
apiVersion: apps/v1kind: DaemonSetmetadata: name: example-daemonset namespace: defaultspec: selector: matchLabels: app: example template: metadata: labels: app: example spec: containers: - name: example-container image: busybox command: ['sh', '-c', 'echo Hello from $(hostname) && sleep 3600']核心字段说明:
| 字段 | 说明 | 必需 |
|---|---|---|
| apiVersion | apps/v1 | ✓ |
| kind | DaemonSet | ✓ |
| metadata.name | DaemonSet名称 | ✓ |
| spec.selector | 标签选择器,匹配Pod | ✓ |
| spec.template | Pod模板(与Pod的spec一样) | ✓ |
| spec.updateStrategy | 更新策略(RollingUpdate/OnDelete) | |
| spec.minReadySeconds | Pod就绪后多久才认为可用(秒) |
3.2 节点选择:控制在哪些节点上运行
方式1:nodeSelector(简单标签匹配)
apiVersion: apps/v1kind: DaemonSetmetadata: name: nginx-daemonsetspec: selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: nodeSelector: disktype: ssd # 只在有disktype=ssd标签的节点上运行 containers: - name: nginx image: nginx:1.20使用示例:
# 给节点打标签kubectl label nodes k8s-node1 disktype=ssdkubectl label nodes k8s-node2 disktype=ssd
# DaemonSet只会在node1和node2上创建Podkubectl get pods -o wide方式2:nodeAffinity(灵活的节点亲和性)
apiVersion: apps/v1kind: DaemonSetmetadata: name: monitoring-daemonsetspec: selector: matchLabels: app: monitoring template: metadata: labels: app: monitoring spec: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: kubernetes.io/hostname operator: In values: - k8s-node1 - k8s-node2 containers: - name: node-exporter image: prom/node-exporter:latest操作符说明:
| 操作符 | 说明 | 示例 |
|---|---|---|
| In | 标签值在列表中 | key: In values: [v1, v2] |
| NotIn | 标签值不在列表中 | key: NotIn values: [v1] |
| Exists | 标签存在(不管值是什么) | key: Exists |
| DoesNotExist | 标签不存在 | key: DoesNotExist |
| Gt | 标签值大于指定值(数值比较) | key: Gt value: "100" |
| Lt | 标签值小于指定值 | key: Lt value: "50" |
3.3 污点和容忍:在特殊节点上运行
什么是Taint(污点)和Toleration(容忍)?
污点(Taint)= 节点的"拒客牌"容忍(Toleration)= Pod的"通行证"
想象一个VIP餐厅:- 餐厅门口挂牌:"只接待VIP客户"(这是污点)- 你有VIP卡(这是容忍),就能进去污点的三种效果:
| 效果 | 说明 |
|---|---|
| NoSchedule | 不允许新Pod调度到该节点(已有Pod不受影响) |
| PreferNoSchedule | 尽量不调度新Pod到该节点(不是硬性要求) |
| NoExecute | 不允许Pod运行,已有Pod如果不容忍污点会被驱逐 |
DaemonSet容忍所有污点的示例:
apiVersion: apps/v1kind: DaemonSetmetadata: name: fluent-bit namespace: kube-systemspec: selector: matchLabels: app: fluent-bit template: metadata: labels: app: fluent-bit spec: tolerations: # 容忍master节点污点(允许在master上运行) - key: node-role.kubernetes.io/master operator: Exists effect: NoSchedule - key: node-role.kubernetes.io/control-plane operator: Exists effect: NoSchedule # 容忍节点未就绪 - key: node.kubernetes.io/not-ready operator: Exists effect: NoExecute # 容忍节点不可达 - key: node.kubernetes.io/unreachable operator: Exists effect: NoExecute containers: - name: fluent-bit image: fluent/fluent-bit:latest容忍的配置项:
| 字段 | 说明 |
|---|---|
| key | 污点的键(留空表示匹配所有键) |
| operator | 匹配操作符(Exists=存在即可,Equal=值必须相等) |
| value | 污点的值(operator为Equal时必须指定) |
| effect | 污点效果(NoSchedule/PreferNoSchedule/NoExecute,留空表示匹配所有效果) |
4. DaemonSet更新策略
4.1 更新策略类型
DaemonSet支持两种更新策略:
| 策略 | 说明 | 使用场景 |
|---|---|---|
| RollingUpdate | 滚动更新(默认) | 自动化更新,推荐 |
| OnDelete | 手动删除Pod后才更新 | 需要精细控制更新时机 |
4.2 滚动更新(RollingUpdate)
工作原理:
滚动更新流程:1. 更新DaemonSet的Pod模板2. DaemonSet Controller逐个节点更新Pod - 删除旧Pod - 等待旧Pod终止 - 创建新Pod - 等待新Pod就绪 - 继续下一个节点配置示例:
apiVersion: apps/v1kind: DaemonSetmetadata: name: fluentdspec: updateStrategy: type: RollingUpdate rollingUpdate: maxUnavailable: 1 # 同时更新的最大节点数(默认1) selector: matchLabels: app: fluentd template: metadata: labels: app: fluentd spec: containers: - name: fluentd image: fluent/fluentd:v1.14 # 镜像版本maxUnavailable参数:
maxUnavailable: 1 (保守更新)┌─────────┐ ┌─────────┐ ┌─────────┐│ Node1 │ │ Node2 │ │ Node3 ││ v1.14 ✓ │ │ v1.13 │ │ v1.13 │ <- 一个一个更新└─────────┘ └─────────┘ └─────────┘ ↓ 等待 等待┌─────────┐ ┌─────────┐ ┌─────────┐│ Node1 │ │ Node2 │ │ Node3 ││ v1.14 ✓ │ │ v1.14 ✓ │ │ v1.13 │└─────────┘ └─────────┘ └─────────┘
maxUnavailable: 2 (激进更新)┌─────────┐ ┌─────────┐ ┌─────────┐│ Node1 │ │ Node2 │ │ Node3 ││ v1.14 ✓ │ │ v1.14 ✓ │ │ v1.13 │ <- 两个同时更新└─────────┘ └─────────┘ └─────────┘执行更新:
# 方式1:修改镜像kubectl set image daemonset/fluentd fluentd=fluent/fluentd:v1.15 -n kube-system
# 方式2:修改YAML后applykubectl apply -f fluentd-daemonset.yaml
# 查看更新状态kubectl rollout status daemonset/fluentd -n kube-system
# 查看更新历史kubectl rollout history daemonset/fluentd -n kube-system监控更新过程:
# 实时监控Pod变化kubectl get pods -n kube-system -l app=fluentd -w
# 查看DaemonSet状态kubectl get daemonset fluentd -n kube-system# 输出:# NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE# fluentd 3 3 2 1 2## DESIRED: 期望的Pod数量(节点数)# CURRENT: 当前运行的Pod数量# READY: 就绪的Pod数量# UP-TO-DATE: 已更新到最新版本的Pod数量# AVAILABLE: 可用的Pod数量4.3 手动更新(OnDelete)
使用场景:
- 需要在特定时间窗口更新
- 需要逐个验证更新效果
- 关键服务不能自动更新
配置示例:
apiVersion: apps/v1kind: DaemonSetmetadata: name: critical-monitoringspec: updateStrategy: type: OnDelete # 手动更新模式 selector: matchLabels: app: monitoring template: metadata: labels: app: monitoring spec: containers: - name: monitor image: monitoring:v2.0更新流程:
# 1. 修改DaemonSet(只更新模板,不影响现有Pod)kubectl apply -f daemonset.yaml
# 2. 手动删除节点1的Pod(触发更新)kubectl delete pod critical-monitoring-xxxxx -n default
# 3. 新Pod自动创建,使用新模板
# 4. 验证新Pod正常后,继续删除下一个kubectl delete pod critical-monitoring-yyyyy -n default4.4 回滚DaemonSet
查看历史版本:
# 查看DaemonSet的历史版本kubectl rollout history daemonset/fluentd -n kube-system
# 输出:# REVISION CHANGE-CAUSE# 1 <none># 2 kubectl apply --filename=fluentd-v1.14.yaml# 3 kubectl set image daemonset/fluentd fluentd=fluent/fluentd:v1.15回滚到上一个版本:
# 回滚到上一版本kubectl rollout undo daemonset/fluentd -n kube-system
# 查看回滚状态kubectl rollout status daemonset/fluentd -n kube-system回滚到指定版本:
# 回滚到revision 2kubectl rollout undo daemonset/fluentd --to-revision=2 -n kube-system暂停和恢复更新:
# 暂停更新(已更新的不会回滚,未更新的停止更新)kubectl rollout pause daemonset/fluentd -n kube-system
# 恢复更新kubectl rollout resume daemonset/fluentd -n kube-system5. Job:一次性任务执行器
5.1 Job是什么
Job 是Kubernetes中用于运行一次性任务的工作负载控制器。
与Deployment/DaemonSet的区别:
Deployment: 我要这个服务一直运行着(长期运行服务) - Web服务器 - API服务 - 数据库
DaemonSet: 每个节点都要运行一个服务(节点级长期服务) - 日志收集 - 监控Agent
Job: 执行一个任务,完成就结束(一次性任务) - 数据库备份 - 批量数据处理 - 定时清理任务生活化比喻:
Deployment = 餐厅的厨师(一直工作)DaemonSet = 每层楼的保洁(每层都有,一直工作)Job = 搬家工人(搬完就走)5.2 Job的核心特点
-
任务完成即终止
- Pod运行完成(退出码0)后,Job标记为Complete
- Pod不会重启(除非失败重试)
-
失败重试
- 支持配置重试次数
- 失败的Pod会被删除并重建
-
并行执行
- 支持同时运行多个Pod
- 支持顺序执行或并行执行
-
保留历史
- 完成的Pod默认保留(可以查看日志)
- 可配置自动清理
5.3 Job的工作模式
模式1:单次执行(最常见)
apiVersion: batch/v1kind: Jobmetadata: name: backup-jobspec: template: spec: containers: - name: backup image: mysql:8.0 command: ['sh', '-c', 'mysqldump -h mysql -u root -pPassword123 mydb > /backup/mydb.sql'] restartPolicy: Never # 重要:Job必须设置为Never或OnFailure执行流程:
创建Job → 创建Pod → Pod运行 → 任务完成(退出码0) → Pod状态Completed → Job状态Complete模式2:固定完成次数(completions)
apiVersion: batch/v1kind: Jobmetadata: name: data-process-jobspec: completions: 5 # 需要成功完成5次 template: spec: containers: - name: processor image: data-processor:latest restartPolicy: Never执行流程:
创建5个Pod(可能顺序执行)→ 每个Pod完成一个任务 → 5个全部成功 → Job完成模式3:并行执行(parallelism)
apiVersion: batch/v1kind: Jobmetadata: name: parallel-jobspec: completions: 10 # 总共需要完成10个任务 parallelism: 3 # 同时运行3个Pod template: spec: containers: - name: worker image: worker:latest restartPolicy: Never执行流程:
第一批:创建3个Pod同时运行 → Pod1完成 → 创建Pod4 → Pod2完成 → 创建Pod5 → Pod3完成 → 创建Pod6...→ 10个全部完成 → Job完成对比图:
顺序执行(parallelism=1, completions=3):时间 →Pod1 [████████] Pod2 [████████] Pod3 [████████]
并行执行(parallelism=3, completions=3):时间 →Pod1 [████████]Pod2 [████████]Pod3 [████████]
混合模式(parallelism=2, completions=5):时间 →Pod1 [████████]Pod2 [████████] Pod3 [████████] Pod4 [████████] Pod5 [████████]5.4 Job配置详解
完整的Job YAML结构:
apiVersion: batch/v1kind: Jobmetadata: name: example-job namespace: defaultspec: # 并行控制 completions: 3 # 需要成功完成的Pod数量(默认1) parallelism: 2 # 同时运行的最大Pod数量(默认1)
# 失败控制 backoffLimit: 6 # 最大失败重试次数(默认6)
# 超时控制 activeDeadlineSeconds: 600 # Job运行的最大时间(秒),超时则终止
# 清理控制 ttlSecondsAfterFinished: 100 # Job完成后多久自动删除(秒)
# Pod模板 template: spec: restartPolicy: Never # 必须是Never或OnFailure containers: - name: worker image: busybox command: ['sh', '-c', 'echo Processing... && sleep 10 && echo Done!']关键字段详解:
| 字段 | 说明 | 默认值 |
|---|---|---|
| completions | 需要成功完成的Pod总数 | 1 |
| parallelism | 同时运行的最大Pod数 | 1 |
| backoffLimit | 失败重试次数限制 | 6 |
| activeDeadlineSeconds | Job运行最大时长(秒),超时终止所有Pod | |
| ttlSecondsAfterFinished | Job完成后自动清理时间(秒) | |
| restartPolicy | 必须是Never或OnFailure(不能是Always) | Never |
restartPolicy的区别:
# Never: Pod失败后不重启,Job Controller创建新PodrestartPolicy: Never# 行为:Pod1失败 → 创建Pod2 → Pod2失败 → 创建Pod3 ...
# OnFailure: Pod失败后在原地重启容器restartPolicy: OnFailure# 行为:Pod1失败 → 容器重启 → 失败 → 容器再重启 ...使用建议:
- Never:适合有状态任务或需要重新初始化环境的任务
- OnFailure:适合无状态任务,节省Pod创建开销
5.5 Job失败处理
场景1:backoffLimit限制重试次数
apiVersion: batch/v1kind: Jobmetadata: name: retry-jobspec: backoffLimit: 3 # 最多失败3次 template: spec: restartPolicy: Never containers: - name: worker image: busybox command: ['sh', '-c', 'exit 1'] # 故意失败执行结果:
创建Pod1 → 失败(退出码1)→ 创建Pod2 → 失败→ 创建Pod3 → 失败→ 创建Pod4 → 失败→ 达到backoffLimit限制,Job标记为Failed场景2:超时控制
apiVersion: batch/v1kind: Jobmetadata: name: timeout-jobspec: activeDeadlineSeconds: 30 # 30秒超时 template: spec: restartPolicy: Never containers: - name: worker image: busybox command: ['sh', '-c', 'sleep 60'] # 睡眠60秒执行结果:
创建Pod → 运行30秒 → 超时 → Job终止Pod → Job状态Failed查看Job状态:
# 查看Jobkubectl get jobs
# 输出示例:# NAME COMPLETIONS DURATION AGE# backup-job 1/1 15s 1m <- 成功# retry-job 0/1 2m 2m <- 失败
# 查看详细状态kubectl describe job retry-job
# 查看失败的Pod日志kubectl logs <pod-name>5.6 Job清理
问题:Job完成后Pod会一直存在
kubectl get jobs# NAME COMPLETIONS DURATION AGE# backup-job 1/1 15s 7d <- 7天前完成的Job还在
kubectl get pods# NAME READY STATUS RESTARTS AGE# backup-job-xxxxx 0/1 Completed 0 7d <- Pod也还在解决方案1:自动清理(推荐)
apiVersion: batch/v1kind: Jobmetadata: name: auto-cleanup-jobspec: ttlSecondsAfterFinished: 100 # 完成后100秒自动删除 template: spec: restartPolicy: Never containers: - name: worker image: busybox command: ['sh', '-c', 'echo Done && sleep 10']解决方案2:手动清理
# 删除Job(会级联删除Pod)kubectl delete job backup-job
# 批量删除已完成的Jobkubectl delete jobs --field-selector status.successful=1
# 批量删除失败的Jobkubectl delete jobs --field-selector status.failed=16. CronJob:定时任务调度器
6.1 CronJob是什么
CronJob 是Kubernetes中用于定时执行任务的控制器,就像Linux的crontab。
生活化比喻:
Job = 临时工(干完活就走)CronJob = 定时闹钟(每天早上7点叫你起床)
示例:- 每天凌晨2点备份数据库- 每小时清理临时文件- 每周一生成报表核心特点:
-
定时执行
- 按照Cron表达式定时创建Job
- Job执行完成后自动清理
-
历史管理
- 保留最近几次成功/失败的Job
- 自动清理旧Job
-
并发控制
- 控制同时运行的Job数量
- 处理上次Job未完成的情况
6.2 Cron表达式详解
格式:
分钟 小时 日期 月份 星期* * * * *字段说明:
| 字段 | 取值范围 | 特殊字符 |
|---|---|---|
| 分钟 | 0-59 | * , - / |
| 小时 | 0-23 | * , - / |
| 日期 | 1-31 | * , - / ? |
| 月份 | 1-12 | * , - / |
| 星期 | 0-7 (0和7都是周日) | * , - / ? |
特殊字符含义:
| 字符 | 含义 | 示例 |
|---|---|---|
| ***** | 任意值 | * * * * * = 每分钟 |
| , | 列举多个值 | 0 8,12,18 * * * = 8点、12点、18点 |
| - | 范围 | 0 9-17 * * * = 9点到17点每小时 |
| / | 步长/间隔 | */5 * * * * = 每5分钟 |
| ? | 不指定(日期和星期互斥时使用) | 0 0 1 * ? = 每月1号 |
常用示例:
| Cron表达式 | 说明 |
|---|---|
*/5 * * * * | 每5分钟 |
0 * * * * | 每小时整点 |
0 0 * * * | 每天凌晨0点 |
0 2 * * * | 每天凌晨2点 |
0 0 * * 0 | 每周日凌晨0点 |
0 0 1 * * | 每月1号凌晨0点 |
0 9-17 * * 1-5 | 周一到周五,9点到17点每小时 |
*/10 9-17 * * * | 9点到17点,每10分钟 |
0 0 1 1 * | 每年1月1号凌晨0点 |
在线测试工具: https://crontab.guru/
6.3 CronJob基本配置
最简单的CronJob:
apiVersion: batch/v1kind: CronJobmetadata: name: hello-cronjobspec: schedule: "*/1 * * * *" # 每分钟执行一次 jobTemplate: spec: template: spec: restartPolicy: Never containers: - name: hello image: busybox command: ['sh', '-c', 'date; echo Hello from CronJob']完整配置:
apiVersion: batch/v1kind: CronJobmetadata: name: backup-cronjob namespace: defaultspec: # Cron表达式 schedule: "0 2 * * *" # 每天凌晨2点
# 时区(Kubernetes 1.25+) timeZone: "Asia/Shanghai" # 使用中国时区
# 并发策略 concurrencyPolicy: Forbid # Allow/Forbid/Replace
# Job历史保留 successfulJobsHistoryLimit: 3 # 保留3个成功的Job failedJobsHistoryLimit: 1 # 保留1个失败的Job
# 启动延迟 startingDeadlineSeconds: 100 # 如果错过调度时间,100秒内还可以启动
# 暂停调度 suspend: false # true表示暂停,不创建新Job
# Job模板(与Job的spec相同) jobTemplate: spec: template: spec: restartPolicy: Never containers: - name: backup image: mysql:8.0 command: ['sh', '-c', 'mysqldump -h mysql -u root -p$MYSQL_PWD mydb > /backup/backup-$(date +%Y%m%d-%H%M%S).sql'] env: - name: MYSQL_PWD valueFrom: secretKeyRef: name: mysql-secret key: password关键字段详解:
| 字段 | 说明 | 默认值 |
|---|---|---|
| schedule | Cron表达式 | 必填 |
| timeZone | 时区(K8s 1.25+) | UTC |
| concurrencyPolicy | 并发策略 | Allow |
| successfulJobsHistoryLimit | 保留成功Job数量 | 3 |
| failedJobsHistoryLimit | 保留失败Job数量 | 1 |
| startingDeadlineSeconds | 错过调度后的启动宽限期(秒) | 无限制 |
| suspend | 是否暂停 | false |
6.4 并发策略详解
concurrencyPolicy决定如何处理Job重叠的情况:
策略1:Allow(允许并发,默认)
spec: schedule: "*/1 * * * *" concurrencyPolicy: Allow # 允许多个Job同时运行场景:
时间线:0:00 → Job1创建,运行中0:01 → Job2创建,运行中(Job1还没完成)0:02 → Job3创建,运行中(Job1、Job2都还没完成)
结果:3个Job同时运行适用场景: 任务之间互不影响,可以并行执行
策略2:Forbid(禁止并发)
spec: schedule: "*/1 * * * *" concurrencyPolicy: Forbid # 如果上一个Job还在运行,跳过本次调度场景:
时间线:0:00 → Job1创建,运行中0:01 → 检查:Job1还在运行 → 跳过,不创建Job20:02 → 检查:Job1还在运行 → 跳过,不创建Job30:03 → Job1完成0:04 → Job2创建,运行中
结果:同一时间只有1个Job运行适用场景: 任务不能并发(如数据库备份、资源清理)
策略3:Replace(替换)
spec: schedule: "*/1 * * * *" concurrencyPolicy: Replace # 终止旧Job,创建新Job场景:
时间线:0:00 → Job1创建,运行中0:01 → 检查:Job1还在运行 → 终止Job1 → 创建Job20:02 → 检查:Job2还在运行 → 终止Job2 → 创建Job3
结果:旧Job被新Job替换适用场景: 只关心最新的任务结果(如监控数据采集)
6.5 CronJob管理命令
创建和查看:
# 创建CronJobkubectl apply -f cronjob.yaml
# 查看CronJobkubectl get cronjobs# 输出:# NAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULE AGE# backup-cronjob 0 2 * * * False 0 15h 3d
# 查看详细信息kubectl describe cronjob backup-cronjob
# 查看CronJob创建的Jobkubectl get jobs# NAME COMPLETIONS DURATION AGE# backup-cronjob-28441920 1/1 15s 15h# backup-cronjob-28443360 1/1 16s 39m手动触发:
# 手动创建一个Job(不等调度时间)kubectl create job --from=cronjob/backup-cronjob manual-backup-job
# 查看手动创建的Jobkubectl get jobs manual-backup-job暂停和恢复:
# 暂停CronJob(不创建新Job,已有Job继续运行)kubectl patch cronjob backup-cronjob -p '{"spec":{"suspend":true}}'
# 恢复CronJobkubectl patch cronjob backup-cronjob -p '{"spec":{"suspend":false}}'删除:
# 删除CronJob(不删除已创建的Job)kubectl delete cronjob backup-cronjob
# 删除CronJob及其所有Jobkubectl delete cronjob backup-cronjob --cascade=foreground7. 实战1:部署Fluentd DaemonSet收集日志
7.1 实战目标
部署Fluentd日志收集器到集群的每个节点,收集所有容器的日志并输出到stdout(生产环境会输出到Elasticsearch等)。
架构图:
7.2 理解Fluentd工作原理
Fluentd是什么?
Fluentd是一个开源的日志收集器,专门设计用于统一日志收集和分发。
在K8s中的工作方式:
-
容器日志位置
容器日志 → Docker/containerd写入 → /var/log/containers/日志文件命名格式:<pod-name>_<namespace>_<container-name>-<container-id>.log示例:nginx-deployment-5d59d67564-abcde_default_nginx-a1b2c3d4.log -
Fluentd收集策略
DaemonSet部署到每个节点→ 挂载宿主机的/var/log/containers目录→ 读取所有日志文件→ 解析、过滤、格式化→ 发送到目标(Elasticsearch、S3、Kafka等)
7.3 准备ConfigMap配置
Fluentd需要配置文件告诉它如何处理日志。
创建fluentd-config.yaml:
apiVersion: v1kind: ConfigMapmetadata: name: fluentd-config namespace: kube-systemdata: fluent.conf: | # 输入源:读取容器日志 <source> @type tail # 使用tail插件(类似tail -f命令) path /var/log/containers/*.log # 监控所有容器日志 pos_file /var/log/fluentd-containers.log.pos # 记录读取位置(避免重复) tag kubernetes.* # 日志标签 read_from_head true # 从头开始读取 <parse> @type json # 解析JSON格式日志 time_key time time_format %Y-%m-%dT%H:%M:%S.%NZ keep_time_key true </parse> </source>
# 过滤器:添加Kubernetes元数据 <filter kubernetes.**> @type kubernetes_metadata # 添加Pod名称、命名空间等信息 </filter>
# 输出:打印到stdout(测试用) <match **> @type stdout # 输出到标准输出 </match>配置解释:
| 配置项 | 说明 |
|---|---|
| @type tail | 监控文件变化(类似tail -f) |
| path | 日志文件路径(支持通配符) |
| pos_file | 记录读取位置的文件(重启后从上次位置继续) |
| tag | 日志标签,用于路由 |
| @type json | 解析JSON格式的日志 |
| @type kubernetes_metadata | 自动添加Pod名称、命名空间、标签等Kubernetes信息 |
| @type stdout | 输出到标准输出(生产环境改为elasticsearch等) |
创建ConfigMap:
kubectl apply -f fluentd-config.yaml7.4 创建Fluentd DaemonSet
创建fluentd-daemonset.yaml:
apiVersion: apps/v1kind: DaemonSetmetadata: name: fluentd namespace: kube-system labels: app: fluentdspec: selector: matchLabels: app: fluentd template: metadata: labels: app: fluentd spec: # 服务账号(需要权限读取Pod元数据) serviceAccountName: fluentd
# 容忍master节点污点(在所有节点运行) tolerations: - key: node-role.kubernetes.io/master operator: Exists effect: NoSchedule - key: node-role.kubernetes.io/control-plane operator: Exists effect: NoSchedule
containers: - name: fluentd image: fluent/fluentd-kubernetes-daemonset:v1-debian-elasticsearch env: # 禁用Elasticsearch输出(我们只用stdout测试) - name: FLUENT_ELASTICSEARCH_HOST value: "localhost" resources: limits: memory: 200Mi requests: cpu: 100m memory: 200Mi volumeMounts: # 挂载配置文件 - name: config mountPath: /fluentd/etc/fluent.conf subPath: fluent.conf # 挂载容器日志目录 - name: varlog mountPath: /var/log readOnly: true # 挂载容器运行时目录 - name: varlibdockercontainers mountPath: /var/lib/docker/containers readOnly: true
volumes: - name: config configMap: name: fluentd-config - name: varlog hostPath: path: /var/log - name: varlibdockercontainers hostPath: path: /var/lib/docker/containers关键配置说明:
-
ServiceAccount
Fluentd需要调用K8s API获取Pod信息→ 需要创建ServiceAccount和对应的RBAC权限 -
tolerations
允许在master节点运行→ 收集master节点上的系统组件日志 -
volumeMounts
/var/log: 宿主机日志目录/var/lib/docker/containers: 容器日志文件的软链接源
7.5 创建RBAC权限
创建fluentd-rbac.yaml:
---apiVersion: v1kind: ServiceAccountmetadata: name: fluentd namespace: kube-system---apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRolemetadata: name: fluentdrules:- apiGroups: [""] resources: - pods - namespaces verbs: - get - list - watch---apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRoleBindingmetadata: name: fluentdroleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: fluentdsubjects:- kind: ServiceAccount name: fluentd namespace: kube-system权限说明:
get/list/watch pods: 获取Pod信息(名称、标签、命名空间)get/list/watch namespaces: 获取命名空间信息
7.6 部署和验证
1. 部署Fluentd
# 创建RBAC权限kubectl apply -f fluentd-rbac.yaml
# 创建ConfigMapkubectl apply -f fluentd-config.yaml
# 创建DaemonSetkubectl apply -f fluentd-daemonset.yaml2. 验证部署
# 查看DaemonSet状态kubectl get daemonset -n kube-system fluentd
# 输出:# NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE# fluentd 3 3 3 3 3 <none> 1m
# 查看Pod分布kubectl get pods -n kube-system -l app=fluentd -o wide
# 输出:# NAME READY STATUS RESTARTS AGE IP NODE# fluentd-abc12 1/1 Running 0 1m 10.244.0.5 k8s-master# fluentd-def34 1/1 Running 0 1m 10.244.1.5 k8s-node1# fluentd-ghi56 1/1 Running 0 1m 10.244.2.5 k8s-node23. 创建测试应用生成日志
# 创建一个简单的Pod生成日志kubectl run log-generator --image=busybox --restart=Never -- sh -c 'while true; do echo "Log message at $(date)"; sleep 5; done'4. 查看Fluentd收集的日志
# 查看Fluentd日志(会看到收集到的应用日志)kubectl logs -n kube-system -l app=fluentd --tail=50
# 输出示例:# 2024-01-15 10:30:15 +0000 kubernetes.var.log.containers.log-generator_default_log-generator-xxx.log:# {# "log":"Log message at Mon Jan 15 10:30:15 UTC 2024\n",# "stream":"stdout",# "time":"2024-01-15T10:30:15.123456789Z",# "kubernetes":{# "pod_name":"log-generator",# "namespace_name":"default",# "pod_id":"abc123",# "container_name":"log-generator",# "host":"k8s-node1"# }# }5. 测试更新DaemonSet
# 修改Fluentd镜像版本(触发滚动更新)kubectl set image daemonset/fluentd fluentd=fluent/fluentd-kubernetes-daemonset:v1.15-debian-elasticsearch-1 -n kube-system
# 查看更新状态kubectl rollout status daemonset/fluentd -n kube-system
# 监控更新过程kubectl get pods -n kube-system -l app=fluentd -w8. 实战2:使用Job执行一次性数据处理任务
8.1 实战目标
创建Job执行批量数据处理任务,演示并行执行、失败重试机制。
场景: 批量处理图片缩略图(模拟)
8.2 准备测试镜像
在harbor机器上创建处理镜像:
mkdir -p /root/k8s-demo/image-processorcd /root/k8s-demo/image-processor创建process.sh:
cat > process.sh << 'EOF'#!/bin/shTASK_ID=${TASK_ID:-"unknown"}echo "=== Task $TASK_ID Started at $(date) ==="SLEEP_TIME=$((5 + RANDOM % 10))echo "Processing for $SLEEP_TIME seconds..."sleep $SLEEP_TIMEecho "=== Task $TASK_ID Completed at $(date) ==="exit 0EOFchmod +x process.shDockerfile和构建:
FROM alpine:3.18COPY process.sh /usr/local/bin/CMD ["/usr/local/bin/process.sh"]docker build -t reg.westos.org/library/image-processor:v1 .docker push reg.westos.org/library/image-processor:v18.3 创建并行Job
parallel-job.yaml:
apiVersion: batch/v1kind: Jobmetadata: name: parallel-image-jobspec: completions: 10 parallelism: 3 backoffLimit: 20 ttlSecondsAfterFinished: 300 template: spec: restartPolicy: Never containers: - name: processor image: reg.westos.org/library/image-processor:v1 env: - name: TASK_ID valueFrom: fieldRef: fieldPath: metadata.name执行:
kubectl apply -f parallel-job.yamlwatch kubectl get jobs parallel-image-jobkubectl get pods -l job-name=parallel-image-job -w9. 实战3:使用CronJob定时备份etcd
9.1 准备备份镜像
mkdir -p /root/k8s-demo/etcd-backupcd /root/k8s-demo/etcd-backupbackup.sh:
cat > backup.sh << 'EOF'#!/bin/shset -eBACKUP_DIR="/backup"TIMESTAMP=$(date +%Y%m%d-%H%M%S)BACKUP_FILE="${BACKUP_DIR}/etcd-snapshot-${TIMESTAMP}.db"echo "=== Backing up etcd at $(date) ==="mkdir -p ${BACKUP_DIR}ETCDCTL_API=3 etcdctl snapshot save ${BACKUP_FILE} \ --endpoints=${ETCD_ENDPOINTS} \ --cacert=${ETCD_CACERT} \ --cert=${ETCD_CERT} \ --key=${ETCD_KEY}ETCDCTL_API=3 etcdctl snapshot status ${BACKUP_FILE} -w tablels -lh ${BACKUP_FILE}find ${BACKUP_DIR} -name "etcd-snapshot-*.db" -mtime +7 -deleteecho "=== Backup completed at $(date) ==="EOFchmod +x backup.shDockerfile:
FROM alpine:3.18RUN apk add --no-cache curl && \ ETCD_VER=v3.5.9 && \ curl -L https://github.com/etcd-io/etcd/releases/download/${ETCD_VER}/etcd-${ETCD_VER}-linux-amd64.tar.gz -o /tmp/etcd.tar.gz && \ tar xzvf /tmp/etcd.tar.gz -C /tmp && \ mv /tmp/etcd-${ETCD_VER}-linux-amd64/etcdctl /usr/local/bin/ && \ rm -rf /tmp/etcd*COPY backup.sh /usr/local/bin/ENTRYPOINT ["/usr/local/bin/backup.sh"]docker build -t reg.westos.org/library/etcd-backup:v1 .docker push reg.westos.org/library/etcd-backup:v19.2 在master创建备份目录
ssh root@192.168.100.20mkdir -p /data/etcd-backupchmod 777 /data/etcd-backup9.3 创建CronJob
etcd-backup-cronjob.yaml:
apiVersion: batch/v1kind: CronJobmetadata: name: etcd-backup namespace: kube-systemspec: schedule: "0 2 * * *" concurrencyPolicy: Forbid successfulJobsHistoryLimit: 3 failedJobsHistoryLimit: 1 jobTemplate: spec: template: spec: restartPolicy: OnFailure nodeSelector: node-role.kubernetes.io/control-plane: "" tolerations: - key: node-role.kubernetes.io/control-plane operator: Exists effect: NoSchedule containers: - name: etcd-backup image: reg.westos.org/library/etcd-backup:v1 env: - name: ETCD_ENDPOINTS value: "https://192.168.100.20:2379" - name: ETCD_CACERT value: "/etc/kubernetes/pki/etcd/ca.crt" - name: ETCD_CERT value: "/etc/kubernetes/pki/etcd/server.crt" - name: ETCD_KEY value: "/etc/kubernetes/pki/etcd/server.key" volumeMounts: - name: etcd-certs mountPath: /etc/kubernetes/pki/etcd readOnly: true - name: backup-dir mountPath: /backup volumes: - name: etcd-certs hostPath: path: /etc/kubernetes/pki/etcd - name: backup-dir hostPath: path: /data/etcd-backup9.4 部署和测试
kubectl apply -f etcd-backup-cronjob.yaml# 手动触发测试kubectl create job --from=cronjob/etcd-backup etcd-backup-manual -n kube-systemkubectl logs -n kube-system -l job-name=etcd-backup-manual -f# 验证备份文件ssh root@192.168.100.20 "ls -lh /data/etcd-backup/"10. 实战4:etcd备份恢复演练
10.1 准备工作
⚠️ 警告:仅在测试环境操作!
- 为所有节点创建虚拟机快照
- 快照名称:“Before-etcd-recovery-test”
10.2 创建初始备份
kubectl create job --from=cronjob/etcd-backup etcd-backup-before-test -n kube-systemkubectl wait --for=condition=complete job/etcd-backup-before-test -n kube-system --timeout=60s10.3 创建测试资源
kubectl create namespace recovery-testkubectl create deployment nginx --image=nginx:1.20 --replicas=3 -n recovery-testkubectl expose deployment nginx --port=80 -n recovery-testkubectl create configmap test-config --from-literal=key1=value1 -n recovery-testkubectl get all,cm -n recovery-test10.4 破坏etcd
ssh root@192.168.100.20mv /etc/kubernetes/manifests/etcd.yaml /tmp/mv /var/lib/etcd /var/lib/etcd.broken# 集群不可用10.5 恢复etcd
# 停止其他组件cd /etc/kubernetes/manifestsmv kube-apiserver.yaml kube-controller-manager.yaml kube-scheduler.yaml /tmp/
# 恢复数据BACKUP_FILE=$(ls -t /data/etcd-backup/*.db | head -1)ETCDCTL_API=3 etcdctl snapshot restore $BACKUP_FILE \ --data-dir=/var/lib/etcd \ --name=k8s-master \ --initial-cluster=k8s-master=https://192.168.100.20:2380 \ --initial-advertise-peer-urls=https://192.168.100.20:2380
chown -R root:root /var/lib/etcdchmod 700 /var/lib/etcd
# 恢复组件mv /tmp/*.yaml ./sleep 30exit10.6 验证恢复
kubectl get nodeskubectl get all,cm -n recovery-test# 应该看到之前创建的资源恢复成功!
总结
本章学习了三种特殊的工作负载:
DaemonSet:
- 每个节点运行一个Pod
- 用于日志收集、监控、网络插件
- 支持滚动更新和回滚
Job:
- 一次性任务执行
- 支持并行和失败重试
- 自动清理机制
CronJob:
- 定时任务调度
- Cron表达式控制执行时间
- 并发策略控制
实战收获:
- 部署Fluentd DaemonSet收集日志
- 使用Job并行处理任务
- CronJob定时备份etcd
- etcd备份恢复演练
生产建议:
- DaemonSet用于节点级服务
- Job用于批处理任务
- CronJob必须备份关键数据
- etcd备份是集群安全的最后防线
八、配置管理:ConfigMap和Secret
1. 为什么需要配置管理
在前面章节中,我们把配置信息直接写在YAML文件里。这样做有严重问题:
问题:
- 密码明文存储
- 无法复用配置
- 环境差异难以处理
- 版本管理混乱
Kubernetes解决方案:
- ConfigMap:存储非敏感配置(数据库地址、端口)
- Secret:存储敏感信息(密码、密钥)
2. ConfigMap:配置数据的”字典”
2.1 ConfigMap是什么
ConfigMap 存储非敏感配置数据,以键值对形式。
创建方式:
# 方式1:从字面量kubectl create configmap app-config \ --from-literal=DB_HOST=mysql.example.com \ --from-literal=DB_PORT=3306
# 方式2:从文件kubectl create configmap app-config --from-file=app.properties
# 方式3:从YAMLapiVersion: v1kind: ConfigMapmetadata: name: mysql-configdata: DB_HOST: "mysql-service" DB_PORT: "3306" my.cnf: | [mysqld] max_connections=2002.2 ConfigMap使用方式
方式1:环境变量注入
containers:- name: app image: nginx:1.20 env: - name: DATABASE_HOST valueFrom: configMapKeyRef: name: mysql-config key: DB_HOST方式2:文件挂载
containers:- name: app volumeMounts: - name: config-volume mountPath: /etc/configvolumes:- name: config-volume configMap: name: mysql-config2.3 ConfigMap热更新
⚠️ 重要特性:
- Volume方式:支持热更新(30-60秒)
- 环境变量:需要重启Pod
3. Secret:敏感数据的”保险箱”
3.1 Secret是什么
Secret 存储敏感信息,数据Base64编码(注意:不是加密!)
Secret类型:
| 类型 | 说明 | 用途 |
|---|---|---|
| Opaque | 通用类型 | 密码、密钥 |
| kubernetes.io/dockerconfigjson | Docker凭证 | 拉取私有镜像 |
| kubernetes.io/tls | TLS证书 | HTTPS |
3.2 Secret创建方式
# 创建通用Secretkubectl create secret generic mysql-secret \ --from-literal=username=root \ --from-literal=password='MyP@ssw0rd123'
# 创建Docker镜像仓库Secretkubectl create secret docker-registry harbor-secret \ --docker-server=reg.westos.org \ --docker-username=admin \ --docker-password=Harbor12345YAML方式(stringData自动编码):
apiVersion: v1kind: Secretmetadata: name: mysql-secrettype: OpaquestringData: username: root password: MyP@ssw0rd1233.3 Secret使用方式
环境变量注入:
containers:- name: mysql image: mysql:8.0 env: - name: MYSQL_ROOT_PASSWORD valueFrom: secretKeyRef: name: mysql-secret key: password文件挂载:
containers:- name: app volumeMounts: - name: secret-volume mountPath: /etc/secret readOnly: truevolumes:- name: secret-volume secret: secretName: mysql-secret⚠️ 安全注意:
- Base64不是加密
- 不要提交到Git
- 使用RBAC限制访问
- 避免在日志中打印
4. 实战:ConfigMap和Secret综合应用
4.1 实战目标
创建一个完整的Web应用,展示配置管理的最佳实践。
架构:
ConfigMap (app-config) → 环境变量Secret (db-secret) → 数据库密码Nginx Pod → 显示配置信息4.2 创建ConfigMap
app-configmap.yaml:
apiVersion: v1kind: ConfigMapmetadata: name: app-configdata: APP_NAME: "ConfigDemo" APP_ENV: "production" DB_HOST: "mysql-service" DB_PORT: "3306"
index.html: | <!DOCTYPE html> <html> <head> <title>Config Demo</title> <style> body { font-family: Arial; max-width: 800px; margin: 50px auto; padding: 20px; } .box { background: white; padding: 20px; border-radius: 8px; margin: 10px 0; } h1 { color: #333; } </style> </head> <body> <h1>ConfigMap和Secret演示</h1> <div class="box"> <h2>配置信息:</h2> <p>应用名称:<span id="app-name"></span></p> <p>运行环境:<span id="app-env"></span></p> <p>数据库地址:<span id="db-host"></span></p> </div> <div class="box"> <h2>敏感信息(从Secret读取):</h2> <p>数据库用户:<span id="db-user"></span></p> <p>数据库密码:<span id="db-pass"></span></p> </div> </body> </html>kubectl apply -f app-configmap.yaml4.3 创建Secret
db-secret.yaml:
apiVersion: v1kind: Secretmetadata: name: db-secrettype: OpaquestringData: DB_USER: "root" DB_PASSWORD: "MySecretPass123"kubectl apply -f db-secret.yaml4.4 创建应用
app-deployment.yaml:
apiVersion: apps/v1kind: Deploymentmetadata: name: config-demospec: replicas: 1 selector: matchLabels: app: config-demo template: metadata: labels: app: config-demo spec: containers: - name: nginx image: nginx:1.20 ports: - containerPort: 80
# ConfigMap环境变量 env: - name: APP_NAME valueFrom: configMapKeyRef: name: app-config key: APP_NAME - name: APP_ENV valueFrom: configMapKeyRef: name: app-config key: APP_ENV - name: DB_HOST valueFrom: configMapKeyRef: name: app-config key: DB_HOST
# Secret环境变量 - name: DB_USER valueFrom: secretKeyRef: name: db-secret key: DB_USER - name: DB_PASSWORD valueFrom: secretKeyRef: name: db-secret key: DB_PASSWORD
volumeMounts: - name: html mountPath: /usr/share/nginx/html/index.html subPath: index.html
# 启动脚本:生成页面 command: ["/bin/sh", "-c"] args: - | cat > /usr/share/nginx/html/index.html << EOF <!DOCTYPE html> <html> <head><title>Config Demo</title> <style> body { font-family: Arial; max-width: 800px; margin: 50px auto; padding: 20px; background: #f0f0f0; } .box { background: white; padding: 20px; border-radius: 8px; margin: 10px 0; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } h1 { color: #333; } .label { font-weight: bold; } .value { color: #0066cc; } </style> </head> <body> <h1>🎯 ConfigMap和Secret演示</h1> <div class="box"> <h2>ConfigMap配置:</h2> <p><span class="label">应用名称:</span><span class="value">$APP_NAME</span></p> <p><span class="label">运行环境:</span><span class="value">$APP_ENV</span></p> <p><span class="label">数据库地址:</span><span class="value">$DB_HOST</span></p> </div> <div class="box"> <h2>Secret敏感信息:</h2> <p><span class="label">数据库用户:</span><span class="value">$DB_USER</span></p> <p><span class="label">数据库密码:</span><span class="value">$DB_PASSWORD</span></p> </div> <div class="box"> <p>💡 提示:此页面展示了ConfigMap和Secret的使用</p> </div> </body> </html> EOF nginx -g 'daemon off;'
volumes: - name: html emptyDir: {}---apiVersion: v1kind: Servicemetadata: name: config-demospec: selector: app: config-demo ports: - port: 80 targetPort: 80 nodePort: 30088 type: NodePortkubectl apply -f app-deployment.yaml4.5 访问和测试
# 查看Podkubectl get pods -l app=config-demo
# 获取Service地址kubectl get svc config-demo
# 访问应用(浏览器打开)http://192.168.100.21:30088测试配置更新:
# 1. 更新ConfigMapkubectl patch configmap app-config -p '{"data":{"APP_ENV":"development"}}'
# 2. 重启Pod(环境变量需要重启)kubectl rollout restart deployment config-demo
# 3. 再次访问,看到环境变量已更新测试Secret更新:
# 更新Secretkubectl patch secret db-secret -p '{"stringData":{"DB_PASSWORD":"NewPassword456"}}'
# 重启Podkubectl rollout restart deployment config-demo
# 验证密码已更新总结
ConfigMap:
- 存储非敏感配置
- 支持文件热更新
- 多种创建和使用方式
Secret:
- 存储敏感信息
- Base64编码(不是加密)
- RBAC权限控制
最佳实践:
- 配置与代码分离
- 不要把Secret提交到Git
- 环境变量用于简单配置
- 文件挂载用于复杂配置
- 生产环境启用etcd加密