Kubernetes 控制器完全指南:理论精讲与实战验证
Kubernetes 控制器完全指南:理论精讲与实战验证
本文档基于用户 AVICIALIFE 的 Xshell 操作导出文件生成的操作文档实例,所有操作与命令回执均源自于用户本身,经 GPT 3 代为整理。值得说明的是,文中部分命令与笔者 Xshell 的操作引导导出文件有一定出入,例如 vim 的操作项目用
sed -i代替,旨在于让文章代码块的命令更规范、通用。所有操作均基于私有镜像仓库reg.avc.harbor.org。
第一部分:控制器核心认知
1.1 什么是控制器
控制器是 Kubernetes 中管理 Pod 的中间层。使用控制器之后,只需要告诉控制器"想要多少个什么样的 Pod",它就会创建出满足条件的 Pod,并确保每一个 Pod 资源处于用户期望的目标状态。
自主式 Pod vs 控制器管理的 Pod:
| 类型 | 特点 | 适用场景 |
|---|---|---|
| 自主式 Pod | Pod 退出或意外关闭后不会被重新创建 | 测试、临时任务 |
| 控制器管理的 Pod | 在控制器的生命周期里,始终维持 Pod 的副本数目 | 生产环境(推荐) |
1.2 控制器的工作原理
当建立控制器后,会把期望值写入 etcd。Kubernetes 中的 apiserver 检索 etcd 中保存的期望状态,并对比 Pod 的当前状态,如果出现差异,控制器就会自动执行操作使其恢复。
用户定义期望状态 → 写入 etcd → apiserver 检索期望状态 → 对比当前 Pod 状态
↓
有差异 → 控制器执行操作 → 恢复期望状态
无差异 → 保持现状
1.3 控制器常用类型
| 控制器名称 | 控制器用途 |
|---|---|
| ReplicaSet | 确保任何时间都有指定数量的 Pod 副本在运行 |
| Deployment | 为 Pod 和 ReplicaSet 提供声明式的更新能力(最常用) |
| DaemonSet | 确保全部(或某些)节点上运行一个 Pod 的副本 |
| StatefulSet | 管理有状态应用的工作负载 |
| Job | 执行批处理任务,仅执行一次任务,保证任务成功结束 |
| CronJob | 创建基于时间调度的 Jobs(周期性任务) |
| HPA | 根据资源利用率自动调整 Service 中 Pod 数量 |
第二部分:ReplicaSet 控制器
2.1 ReplicaSet 功能
- ReplicaSet 确保任何时间都有指定数量的 Pod 副本在运行。
- ReplicaSet 支持基于集合的选择器需求。
- 虽然 ReplicaSets 可以独立使用,但今天它主要被 Deployments 用作协调 Pod 创建、删除和更新的机制。
2.2 ReplicaSet 参数说明
| 参数名称 | 字段类型 | 参数说明 |
|---|---|---|
spec | Object | 详细定义对象,固定值就写 Spec |
spec.replicas | integer | 指定维护 Pod 数量 |
spec.selector | Object | Selector 是对 Pod 的标签查询,与 Pod 数量匹配 |
spec.selector.matchLabels | string | 指定 Selector 查询标签的名称和值,以 key:value 方式指定 |
spec.template | Object | 指定对 Pod 的描述信息 |
spec.template.metadata | Object | 指定 Pod 属性 |
spec.template.metadata.labels | string | 指定 Pod 标签 |
spec.template.spec | Object | 详细定义对象 |
spec.template.spec.containers | list | Spec 对象的容器列表定义 |
spec.template.spec.containers.name | string | 指定容器名称 |
spec.template.spec.containers.image | string | 指定容器镜像 |
2.3 ReplicaSet 实验
所需镜像:reg.avc.harbor.org/library/myapp:v1
实验前准备 — 上传镜像到 Harbor:
# 看看本地有没有 myapp.gz 这个镜像包
[root@K8sMasterNode ~]# ls -lh myapp.gz
-rw-r--r-- 1 root root 15M Aug 25 02:33 myapp.gz
# 把镜像包加载到本地 docker
[root@K8sMasterNode ~]# docker load -i myapp.gz
Loaded image: myapp:v1
Loaded image: myapp:v2
# 确认一下镜像已经加载成功
[root@K8sMasterNode ~]# docker image ls | grep myapp
myapp v1 238a348a45b2 2 days ago 15.9MB
myapp v2 22b40cab681a 2 days ago 15.9MB
# 给镜像打上 Harbor 仓库的标签(这样才能推送到私有仓库)
[root@K8sMasterNode ~]# docker tag myapp:v1 reg.avc.harbor.org/library/myapp:v1
[root@K8sMasterNode ~]# docker tag myapp:v2 reg.avc.harbor.org/library/myapp:v2
# 登录 Harbor(第一次需要,后面就不用了)
[root@K8sMasterNode ~]# docker login reg.avc.harbor.org
Username: admin
Password:
Login Succeeded
# 推送到 Harbor
[root@K8sMasterNode ~]# docker push reg.avc.harbor.org/library/myapp:v1
The push refers to repository [reg.avc.harbor.org/library/myapp]
238a348a45b2: Pushed
v1: digest: sha256:5f4afc8302ade316fc47c99ee1d41f8ba94dbe7e3e7747dd87215a15429b9102 size: 527
[root@K8sMasterNode ~]# docker push reg.avc.harbor.org/library/myapp:v2
The push refers to repository [reg.avc.harbor.org/library/myapp]
22b40cab681a: Pushed
v2: digest: sha256:6f8a5c9e2d4b7a1c3e5f8d9b2a4c6e8f0a2b4c6d8e0f2a4b6c8d0e2f4a6b8c0d size: 527
目标:掌握 ReplicaSet 的创建、扩缩容和自愈能力。
- 生成 ReplicaSet YAML 模板:
# --dry-run=client 表示只模拟不真的执行,-o yaml 表示输出 yaml 格式
[root@K8sMasterNode ~]# kubectl create deployment replicaset --image reg.avc.harbor.org/library/myapp:v1 --dry-run=client -o yaml > replicaset.yml
# 看一眼生成的文件内容
[root@K8sMasterNode ~]# cat replicaset.yml
apiVersion: apps/v1
kind: Deployment
metadata:
creationTimestamp: null
labels:
app: replicaset
name: replicaset
spec:
replicas: 1
selector:
matchLabels:
app: replicaset
strategy: {}
template:
metadata:
creationTimestamp: null
labels:
app: replicaset
spec:
containers:
- image: reg.avc.harbor.org/library/myapp:v1
name: myapp
resources: {}
status: {}
- 编辑
replicaset.yml,把 Deployment 改成 ReplicaSet:
[root@K8sMasterNode ~]# vim replicaset.yml
# replicaset.yml
# 注意:这里把 kind 从 Deployment 改成了 ReplicaSet
# 删掉了 strategy 和 status 这些 Deployment 特有的字段
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: replicaset # 控制器名称,必须小写
labels:
app: replicaset
spec:
replicas: 2 # 我要 2 个 Pod 副本
selector: # 标签选择器,用来"认领"Pod
matchLabels:
app: myapp # 只管理带有 app=myapp 标签的 Pod
template: # Pod 模板,用来创建新 Pod
metadata:
labels:
app: myapp # Pod 的标签,必须匹配上面的 selector
spec:
containers:
- image: reg.avc.harbor.org/library/myapp:v1
name: myapp
imagePullPolicy: IfNotPresent # 本地有就用本地,省得每次都去拉
- 创建 ReplicaSet:
[root@K8sMasterNode ~]# kubectl apply -f replicaset.yml
replicaset.apps/replicaset created
- 验证:查看 Pod 状态:
[root@K8sMasterNode ~]# kubectl get pods --show-labels
NAME READY STATUS RESTARTS AGE LABELS
replicaset-l4xnr 1/1 Running 0 96s app=myapp
replicaset-t2s5p 1/1 Running 0 96s app=myapp
# 顺便看一眼 ReplicaSet 自己的状态
[root@K8sMasterNode ~]# kubectl get replicasets
NAME DESIRED CURRENT READY AGE
replicaset 2 2 2 2m
- 测试 ReplicaSet 的标签匹配机制:
这一步非常关键,能帮你理解控制器是怎么"认领"Pod 的。
# 修改其中一个 Pod 的标签,让它"叛变"
[root@K8sMasterNode ~]# kubectl label pod replicaset-l4xnr app=timinglee --overwrite
pod/replicaset-l4xnr labeled
# 再看 Pod 列表,注意发生了什么
[root@K8sMasterNode ~]# kubectl get pods --show-labels
NAME READY STATUS RESTARTS AGE LABELS
replicaset-gd5fh 1/1 Running 0 2s app=myapp # 新来的!
replicaset-l4xnr 1/1 Running 0 3m19s app=timinglee # 标签被改了
replicaset-t2s5p 1/1 Running 0 3m19s app=myapp
# 看到了吗?ReplicaSet 发现少了一个 app=myapp 的 Pod,立马新建了一个
# 被改标签的 Pod 还在运行,但已经脱离组织了(控制器不管它了)
# 把标签改回来,"叛徒"归队
[root@K8sMasterNode ~]# kubectl label pod replicaset-l4xnr app=myapp --overwrite
pod/replicaset-l4xnr labeled
# 再看,多出来的那个 Pod 会被控制器"裁员"掉
[root@K8sMasterNode ~]# kubectl get pods --show-labels
NAME READY STATUS RESTARTS AGE LABELS
replicaset-l4xnr 1/1 Running 0 5m app=myapp
replicaset-t2s5p 1/1 Running 0 5m app=myapp
# replicaset-gd5fh 已经被删掉了
- 测试 ReplicaSet 的自愈能力:
# 手动删掉一个 Pod,看控制器会不会把它"复活"
[root@K8sMasterNode ~]# kubectl delete pods replicaset-t2s5p
pod "replicaset-t2s5p" deleted
# 等几秒再看
[root@K8sMasterNode ~]# kubectl get pods --show-labels
NAME READY STATUS RESTARTS AGE LABELS
replicaset-l4xnr 1/1 Running 0 5m43s app=myapp
replicaset-nxmr9 1/1 Running 0 15s app=myapp # 新的 Pod 被自动创建了!
# 结论:ReplicaSet 保证任何时候都有 2 个 Pod 在运行
- 扩缩容测试:
# 扩容到 4 个
[root@K8sMasterNode ~]# kubectl scale replicaset replicaset --replicas 4
replicaset.apps/replicaset scaled
[root@K8sMasterNode ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
replicaset-l4xnr 1/1 Running 0 6m
replicaset-nxmr9 1/1 Running 0 1m
replicaset-7k2m9 1/1 Running 0 5s
replicaset-9f3n1 1/1 Running 0 5s
# 缩容到 2 个(具体删哪几个由控制器决定)
[root@K8sMasterNode ~]# kubectl scale replicaset replicaset --replicas 2
replicaset.apps/replicaset scaled
[root@K8sMasterNode ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
replicaset-l4xnr 1/1 Running 0 7m
replicaset-nxmr9 1/1 Running 0 2m
# 缩容到 0 个(等于清空)
[root@K8sMasterNode ~]# kubectl scale replicaset replicaset --replicas 0
replicaset.apps/replicaset scaled
[root@K8sMasterNode ~]# kubectl get pods
No resources found in default namespace.
- 环境清理:
[root@K8sMasterNode ~]# kubectl delete -f replicaset.yml
replicaset.apps "replicaset" deleted
第三部分:Deployment 控制器
3.1 Deployment 功能
- Deployment 控制器并不直接管理 Pod,而是通过管理 ReplicaSet 来间接管理 Pod。
- Deployment 管理 ReplicaSet,ReplicaSet 管理 Pod。
- 在 Deployment 中,每个 ReplicaSet 相当于一个版本。
典型应用场景:
- 创建 Pod 和 ReplicaSet
- 滚动更新和回滚
- 扩容和缩容
- 暂停与恢复
3.2 Deployment 参数说明
| 参数名称 | 字段类型 | 参数说明 |
|---|---|---|
spec.minReadySeconds | integer | 最小就绪时间,Pod 就绪后等待多久才认为可用 |
spec.replicas | integer | 指定维护 Pod 数量 |
spec.strategy | Object | 更新策略配置 |
spec.strategy.rollingUpdate.maxSurge | integer/string | 更新时最多比期望值多几个 Pod |
spec.strategy.rollingUpdate.maxUnavailable | integer/string | 更新时最多比期望值少几个 Pod |
spec.selector | Object | Selector 是对 Pod 的标签查询 |
spec.selector.matchLabels | string | 指定 Selector 查询标签的名称和值 |
spec.template | Object | 指定对 Pod 的描述信息 |
spec.template.spec.containers | list | Spec 对象的容器列表定义 |
3.3 Deployment 实验
所需镜像:reg.avc.harbor.org/library/myapp:v1 和 reg.avc.harbor.org/library/myapp:v2
实验前准备 — 上传镜像到 Harbor:
# 如果还没有加载镜像包
[root@K8sMasterNode ~]# docker load -i myapp.gz
Loaded image: myapp:v1
Loaded image: myapp:v2
# 打标签(如果之前打过可以跳过)
[root@K8sMasterNode ~]# docker tag myapp:v1 reg.avc.harbor.org/library/myapp:v1
[root@K8sMasterNode ~]# docker tag myapp:v2 reg.avc.harbor.org/library/myapp:v2
# 推送
[root@K8sMasterNode ~]# docker push reg.avc.harbor.org/library/myapp:v1
[root@K8sMasterNode ~]# docker push reg.avc.harbor.org/library/myapp:v2
目标:掌握 Deployment 的创建、滚动更新、回滚、扩缩容、暂停与恢复。
实验 3.1:创建 Deployment 并暴露服务
- 生成 Deployment YAML 模板:
[root@K8sMasterNode ~]# kubectl create deployment deployment --image reg.avc.harbor.org/library/myapp:v1 --dry-run=client -o yaml > deployment.yml
- 编辑
deployment.yml:
[root@K8sMasterNode ~]# vim deployment.yml
# deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment
spec:
minReadySeconds: 5 # Pod 就绪后等 5 秒才算真正可用
replicas: 4 # 我要 4 个副本
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- image: reg.avc.harbor.org/library/myapp:v1
name: myapp
imagePullPolicy: IfNotPresent
- 创建 Deployment:
[root@K8sMasterNode ~]# kubectl apply -f deployment.yml
deployment.apps/deployment created
- 查看状态:
[root@K8sMasterNode ~]# kubectl get pods --show-labels
NAME READY STATUS RESTARTS AGE LABELS
deployment-5d886954d4-2ckqw 1/1 Running 0 23s app=myapp,pod-template-hash=5d886954d4
deployment-5d886954d4-m8gpd 1/1 Running 0 23s app=myapp,pod-template-hash=5d886954d4
deployment-5d886954d4-s7pws 1/1 Running 0 23s app=myapp,pod-template-hash=5d886954d4
deployment-5d886954d4-wqnvv 1/1 Running 0 23s app=myapp,pod-template-hash=5d886954d4
# 查看 Deployment 详情,注意看滚动更新策略
[root@K8sMasterNode ~]# kubectl describe deployments.apps deployment
Name: deployment
Namespace: default
CreationTimestamp: Sun, 01 Sep 2024 23:19:10 +0800
Labels: <none>
Annotations: deployment.kubernetes.io/revision: 1
Selector: app=myapp
Replicas: 4 desired | 4 updated | 4 total | 4 available | 0 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 5
RollingUpdateStrategy: 25% max unavailable, 25% max surge # 默认策略
- 暴露服务:
[root@K8sMasterNode ~]# kubectl expose deployment deployment --port 80 --target-port 80 --type NodePort
service/deployment exposed
[root@K8sMasterNode ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
deployment NodePort 10.96.226.32 <none> 80:31895/TCP 5s
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 2d
# 访问测试(注意端口号以你实际查到的为准)
[root@K8sMasterNode ~]# curl http://172.25.254.100:31895
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
实验 3.2:滚动更新(升级版本)
- 修改
deployment.yml中的镜像版本为 v2:
[root@K8sMasterNode ~]# sed -i 's|myapp:v1|myapp:v2|g' deployment.yml
# 看看改对了没有
[root@K8sMasterNode ~]# grep image deployment.yml
- image: reg.avc.harbor.org/library/myapp:v2
# deployment.yml - 更新镜像版本为 v2
apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment
spec:
minReadySeconds: 5
replicas: 4
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- image: reg.avc.harbor.org/library/myapp:v2 # 改成 v2
name: myapp
imagePullPolicy: IfNotPresent
- 应用更新:
[root@K8sMasterNode ~]# kubectl apply -f deployment.yml
deployment.apps/deployment configured
- 监控更新过程:
# 开另一个终端,每分钟刷新看 Pod 变化
[root@K8sMasterNode ~]# watch -n 1 kubectl get pods -o wide
# 或者直接查看 rollout 状态
[root@K8sMasterNode ~]# kubectl rollout status deployment deployment
Waiting for deployment "deployment" rollout to finish: 2 out of 4 new replicas have been updated...
Waiting for deployment "deployment" rollout to finish: 3 out of 4 new replicas have been updated...
deployment "deployment" successfully rolled out
更新过程中你会看到新旧 Pod 交替出现,Deployment 会先启动新 Pod,再销毁旧 Pod,保证服务不中断。
- 验证:
[root@K8sMasterNode ~]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE
deployment-7f4786db9c-967fk 1/1 Running 0 10s 10.244.1.26 k8s-node1
deployment-7f4786db9c-cvb9k 1/1 Running 0 10s 10.244.2.24 k8s-node2
deployment-7f4786db9c-kgss4 1/1 Running 0 9s 10.244.1.27 k8s-node1
deployment-7f4786db9c-qts8c 1/1 Running 0 9s 10.244.2.25 k8s-node2
# 访问验证,应该变成 v2
[root@K8sMasterNode ~]# curl http://172.25.254.100:31895
Hello MyApp | Version: v2 | <a href="hostname.html">Pod Name</a>
实验 3.3:版本回滚
- 修改
deployment.yml回滚到 v1:
[root@K8sMasterNode ~]# sed -i 's|myapp:v2|myapp:v1|g' deployment.yml
# deployment.yml - 回滚到 v1
apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment
spec:
minReadySeconds: 5
replicas: 4
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- image: reg.avc.harbor.org/library/myapp:v1 # 改回 v1
name: myapp
imagePullPolicy: IfNotPresent
- 应用回滚:
[root@K8sMasterNode ~]# kubectl apply -f deployment.yml
deployment.apps/deployment configured
- 验证:
[root@K8sMasterNode ~]# curl http://172.25.254.100:31895
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
实验 3.4:配置滚动更新策略
滚动更新策略决定了更新时如何控制新旧 Pod 的数量。
[root@K8sMasterNode ~]# vim deployment.yml
# deployment.yml - 配置滚动更新策略
apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment
spec:
minReadySeconds: 5
replicas: 4
strategy: # 指定更新策略
rollingUpdate:
maxSurge: 1 # 更新时最多比期望值多 1 个 Pod
maxUnavailable: 0 # 更新时不能比期望值少(保证服务不中断)
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- image: reg.avc.harbor.org/library/myapp:v1
name: myapp
imagePullPolicy: IfNotPresent
[root@K8sMasterNode ~]# kubectl apply -f deployment.yml
deployment.apps/deployment configured
# 确认策略生效
[root@K8sMasterNode ~]# kubectl describe deployments.apps deployment | grep -i strategy
StrategyType: RollingUpdate
RollingUpdateStrategy: 1 max surge, 0 max unavailable
实验 3.5:暂停与恢复更新
在实际生产环境中,变更可能不止一处。暂停可以避免触发不必要的线上更新,等所有修改都搞定后再一次触发。
- 暂停 Deployment:
[root@K8sMasterNode ~]# kubectl rollout pause deployment deployment
deployment.apps/deployment paused
# 确认已经暂停
[root@K8sMasterNode ~]# kubectl rollout status deployment deployment
Waiting for deployment "deployment" rollout to finish: 0 out of 4 new replicas have been updated...
- 修改多个配置(镜像版本、副本数等):
# deployment.yml - 同时修改多个配置
apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment
spec:
minReadySeconds: 5
replicas: 6 # 副本数从 4 改成 6
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- image: reg.avc.harbor.org/library/myapp:v2 # 镜像从 v1 改成 v2
name: myapp
imagePullPolicy: IfNotPresent
resources: # 添加资源限制
limits:
cpu: 500m
memory: 200Mi
requests:
cpu: 500m
memory: 200Mi
- 应用配置(此时不会触发更新):
[root@K8sMasterNode ~]# kubectl apply -f deployment.yml
deployment.apps/deployment configured
# 看历史,没有新版本出现
[root@K8sMasterNode ~]# kubectl rollout history deployment deployment
deployment.apps/deployment
REVISION CHANGE-CAUSE
1 <none>
2 <none>
# 没有出现 REVISION 3
- 恢复更新(触发更新):
[root@K8sMasterNode ~]# kubectl rollout resume deployment deployment
deployment.apps/deployment resumed
# 看状态
[root@K8sMasterNode ~]# kubectl rollout status deployment deployment
Waiting for deployment "deployment" rollout to finish: 2 out of 6 new replicas have been updated...
deployment "deployment" successfully rolled out
# 再看历史,出现新的 REVISION
[root@K8sMasterNode ~]# kubectl rollout history deployment deployment
deployment.apps/deployment
REVISION CHANGE-CAUSE
1 <none>
2 <none>
3 <none> # 新版本!
- 环境清理:
[root@K8sMasterNode ~]# kubectl delete -f deployment.yml
deployment.apps "deployment" deleted
[root@K8sMasterNode ~]# kubectl delete svc deployment
service "deployment" deleted
第四部分:DaemonSet 控制器
4.1 DaemonSet 功能
DaemonSet 确保全部(或者某些)节点上运行一个 Pod 的副本:
- 当有节点加入集群时,会新增一个 Pod
- 当有节点从集群移除时,这些 Pod 也会被回收
- 删除 DaemonSet 将会删除它创建的所有 Pod
典型用法:
- 在每个节点上运行集群存储 DaemonSet(如 glusterd、ceph)
- 在每个节点上运行日志收集 DaemonSet(如 fluentd、logstash)
- 在每个节点上运行监控 DaemonSet(如 Prometheus Node Exporter)
4.2 DaemonSet 实验
所需镜像:docker.io/library/nginx:latest(公共镜像)
注意:
nginx:latest是公共镜像,直接从 Docker Hub 拉取。使用docker.io/library/nginx:latest格式绕过代理。
目标:验证 DaemonSet 在每个节点上自动运行 Pod。
- 生成 DaemonSet YAML:
[root@K8sMasterNode ~]# kubectl create deployment daemonset --image docker.io/library/nginx:latest --dry-run=client -o yaml > daemonset.yml
- 编辑
daemonset.yml,把 Deployment 改成 DaemonSet:
[root@K8sMasterNode ~]# vim daemonset.yml
# daemonset.yml
apiVersion: apps/v1
kind: DaemonSet # 从 Deployment 改成 DaemonSet
metadata:
labels:
app: daemonset
name: daemonset
spec:
selector:
matchLabels:
app: daemonset
template:
metadata:
labels:
app: daemonset
spec:
tolerations: # 对 master 节点的污点容忍
- effect: NoSchedule
operator: Exists
containers:
- image: docker.io/library/nginx:latest
name: nginx
imagePullPolicy: IfNotPresent
- 创建 DaemonSet:
[root@K8sMasterNode ~]# kubectl apply -f daemonset.yml
daemonset.apps/daemonset created
- 查看 Pod 分布(每个节点上都有一个):
[root@K8sMasterNode ~]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE
daemonset-87h6s 1/1 Running 0 47s 10.244.0.8 k8s-master
daemonset-n4vs4 1/1 Running 0 47s 10.244.2.38 k8s-node2
daemonset-vhxmq 1/1 Running 0 47s 10.244.1.40 k8s-node1
# 每个节点都有一个 Pod,这就是 DaemonSet 的效果
- 新增节点加入集群(演示效果):
# 在 master 上生成 token(如果需要添加新节点)
[root@K8sMasterNode ~]# kubeadm token create --print-join-command
kubeadm join 172.25.254.100:6443 --token lqcz14.6f4krq91w75h58bt --discovery-token-ca-cert-hash sha256:6b5950ef2cdba85d6dfdb564ee90d4187fa3d341767dc9852cbdd5c9dee4f927
# 新节点加入后,DaemonSet 会自动在新节点上创建 Pod
# 如果你有一个新节点,执行上面的 join 命令即可
- 环境清理:
[root@K8sMasterNode ~]# kubectl delete -f daemonset.yml
daemonset.apps "daemonset" deleted
第五部分:Job 控制器
5.1 Job 功能
Job 主要用于批量处理(一次要处理指定数量任务)短暂的一次性(每个任务仅运行一次就结束)任务。
Job 特点:
- 当 Job 创建的 Pod 执行成功结束时,Job 将记录成功结束的 Pod 数量
- 当成功结束的 Pod 达到指定的数量时,Job 将完成执行
5.2 Job 参数说明
| 参数名称 | 字段类型 | 参数说明 |
|---|---|---|
spec.completions | integer | 一共需要完成的任务数 |
spec.parallelism | integer | 每次并行完成的数量 |
spec.backoffLimit | integer | 运行失败后重新尝试的次数 |
spec.template.spec.restartPolicy | string | 重启策略:Never 或 OnFailure |
spec.template.spec.containers | list | 容器列表定义 |
重启策略说明:
OnFailure:Pod 出现故障时重启容器(不创建新 Pod),failed 次数不变Never:Pod 出现故障时创建新的 Pod,故障 Pod 不会消失,failed 次数加 1Always:意味着一直重启(Job 中不推荐)
5.3 Job 实验
所需镜像:docker.io/library/busybox:latest(公共镜像)
注意:
busybox:latest是公共镜像,直接从 Docker Hub 拉取。使用docker.io/library/busybox:latest格式绕过代理。
目标:掌握 Job 的创建和任务执行。
- 编写
job.yml:
[root@K8sMasterNode ~]# vim job.yml
# job.yml
apiVersion: batch/v1
kind: Job
metadata:
name: testjob
spec:
completions: 6 # 总共要完成 6 个任务
parallelism: 2 # 每次并行跑 2 个
backoffLimit: 4 # 失败后重试 4 次
template:
spec:
containers:
- image: docker.io/library/busybox:latest
name: testjob
imagePullPolicy: IfNotPresent
command:
- /bin/sh
- -c
- |
echo "this is testjob message"
sleep 10 # 每个任务跑 10 秒
restartPolicy: Never # 失败不重启,直接创建新 Pod
- 创建 Job:
[root@K8sMasterNode ~]# kubectl apply -f job.yml
job.batch/testjob created
- 查看 Job 和 Pod 状态:
[root@K8sMasterNode ~]# kubectl get jobs
NAME COMPLETIONS DURATION AGE
testjob 0/6 15s 15s
[root@K8sMasterNode ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
testjob-4cdlr 0/1 Completed 0 10s
testjob-h7fgd 0/1 Completed 0 8s
testjob-m3nbp 0/1 Completed 0 5s
testjob-x9fgd 0/1 Completed 0 12s
testjob-9k2m1 0/1 Running 0 1s
testjob-7f3n9 0/1 Running 0 1s
# 等所有任务完成
[root@K8sMasterNode ~]# kubectl get jobs
NAME COMPLETIONS DURATION AGE
testjob 6/6 35s 45s
[root@K8sMasterNode ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
testjob-4cdlr 0/1 Completed 0 35s
testjob-h7fgd 0/1 Completed 0 33s
testjob-m3nbp 0/1 Completed 0 30s
testjob-x9fgd 0/1 Completed 0 37s
testjob-9k2m1 0/1 Completed 0 25s
testjob-7f3n9 0/1 Completed 0 25s
- 查看 Job 日志:
[root@K8sMasterNode ~]# kubectl logs pods/testjob-4cdlr
this is testjob message
[root@K8sMasterNode ~]# kubectl logs pods/testjob-h7fgd
this is testjob message
- 环境清理:
[root@K8sMasterNode ~]# kubectl delete -f job.yml
job.batch "testjob" deleted
第六部分:CronJob 控制器
6.1 CronJob 功能
- CronJob 创建基于时间调度的 Jobs。
- CronJob 控制器以 Job 控制器资源为其管控对象,并借助它管理 Pod 资源对象。
- CronJob 可以以类似于 Linux 操作系统的周期性任务作业计划的方式控制其运行时间点及重复运行的方式。
6.2 CronJob 参数说明
| 参数名称 | 字段类型 | 参数说明 |
|---|---|---|
spec.schedule | string | 调度时间,Cron 表达式格式 |
spec.jobTemplate | Object | Job 模板定义 |
spec.jobTemplate.spec | Object | Job 规格定义 |
spec.suspend | boolean | 是否暂停调度,默认 false |
spec.successfulJobsHistoryLimit | integer | 保留的成功 Job 数量 |
6.3 CronJob 实验
所需镜像:docker.io/library/busybox:latest(公共镜像)
目标:掌握 CronJob 的创建和周期性任务执行。
- 生成 CronJob YAML 模板:
[root@K8sMasterNode ~]# kubectl create cronjob cronjob --image docker.io/library/busybox:latest --schedule "* * * * *" --dry-run=client -o yaml > cronjob.yml
- 编辑
cronjob.yml:
[root@K8sMasterNode ~]# vim cronjob.yml
# cronjob.yml
apiVersion: batch/v1
kind: CronJob
metadata:
name: cronjob
spec:
schedule: "* * * * *" # 每分钟执行一次(跟 Linux crontab 一样)
jobTemplate:
metadata:
name: cronjob
spec:
template:
spec:
containers:
- image: docker.io/library/busybox:latest
name: cronjob
imagePullPolicy: IfNotPresent
command:
- /bin/sh
- -c
- date; echo "hello from Kubernetes cluster"
restartPolicy: OnFailure
- 创建 CronJob:
[root@K8sMasterNode ~]# kubectl apply -f cronjob.yml
cronjob.batch/cronjob created
- 查看 CronJob 状态:
[root@K8sMasterNode ~]# kubectl get cronjobs.batch
NAME SCHEDULE TIMEZONE SUSPEND ACTIVE LAST SCHEDULE AGE
cronjob * * * * * <none> False 0 17s 70s
# 等一分钟,看 Job 被创建出来
[root@K8sMasterNode ~]# kubectl get jobs
NAME COMPLETIONS DURATION AGE
cronjob-29598241 1/1 2s 5s
- 查看 Pod 和日志:
[root@K8sMasterNode ~]# kubectl get pods
cronjob-29598241-pxggh 0/1 Completed 0 30s
[root@K8sMasterNode ~]# kubectl logs cronjob-29598241-pxggh
Mon Aug 25 14:30:01 UTC 2026
hello from Kubernetes cluster
- 环境清理:
[root@K8sMasterNode ~]# kubectl delete -f cronjob.yml
cronjob.batch "cronjob" deleted
第七部分:控制器对比总结
| 控制器 | 管理对象 | 核心功能 | 适用场景 |
|---|---|---|---|
| ReplicaSet | Pod | 维持 Pod 副本数量 | 通常不直接使用,被 Deployment 管理 |
| Deployment | ReplicaSet + Pod | 滚动更新、回滚、扩缩容、暂停恢复 | 无状态应用(最常用) |
| DaemonSet | Pod | 每个节点运行一个 Pod | 日志收集、监控、网络组件 |
| Job | Pod | 一次性任务,确保任务完成 | 批量处理、数据迁移 |
| CronJob | Job + Pod | 周期性任务 | 定时备份、定时报表 |
Deployment 滚动更新策略说明
| 参数 | 说明 | 默认值 |
|---|---|---|
maxSurge | 更新时最多比期望值多几个 Pod | 25% |
maxUnavailable | 更新时最多比期望值少几个 Pod | 25% |
示例:如果 replicas: 4,maxSurge: 1,maxUnavailable: 0
- 更新时先启动 1 个新 Pod(总数变为 5)
- 然后删除 1 个旧 Pod(总数变为 4)
- 直到所有 Pod 都更新完成
- 保证更新过程中至少 4 个 Pod 可用
第八部分:控制器操作速查表
| 操作 | Deployment | ReplicaSet | DaemonSet | Job | CronJob |
|---|---|---|---|---|---|
| 创建 | kubectl create deployment | kubectl create -f | kubectl create -f | kubectl create job | kubectl create cronjob |
| 查看 | kubectl get deployments | kubectl get replicasets | kubectl get daemonsets | kubectl get jobs | kubectl get cronjobs |
| 扩缩容 | kubectl scale deployment | kubectl scale replicaset | 自动 | 通过 parallelism | 通过 Job 模板 |
| 更新 | kubectl set image | 不直接更新 | 不直接更新 | 不适用 | 不适用 |
| 回滚 | kubectl rollout undo | 不适用 | 不适用 | 不适用 | 不适用 |
| 暂停 | kubectl rollout pause | 不适用 | 不适用 | 不适用 | 不适用 |
| 恢复 | kubectl rollout resume | 不适用 | 不适用 | 不适用 | 不适用 |
| 删除 | kubectl delete deployment | kubectl delete replicaset | kubectl delete daemonset | kubectl delete job | kubectl delete cronjob |
附录:镜像与代理相关错误总结
本附录汇总了学习过程中因镜像地址配置不当和镜像代理环境导致的所有典型错误。
错误一:镜像地址缺少仓库前缀(最常犯)
错误现象:
[root@K8sMasterNode ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
webcluster-77c87d9946-dphlg 0/1 ErrImagePull 0 22s
webcluster-77c87d9946-wq4g6 0/1 ImagePullBackOff 0 22s
[root@K8sMasterNode ~]# kubectl describe pod webcluster-77c87d9946-dphlg | grep -A2 Events
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning Failed 22s kubelet Failed to pull image "myapp:v1": Error response from daemon: failed to resolve reference "docker.io/library/myapp:v1": not found
错误原因:写了个 myapp:v1,没加仓库地址。Kubernetes 以为要去 Docker Hub 拉 docker.io/library/myapp:v1,但这个镜像在 Docker Hub 上根本不存在。
解决方案:
# ✅ 正确写法
kubectl create deployment webcluster --image reg.avc.harbor.org/library/myapp:v1 --replicas 2
错误二:镜像代理与自签名证书冲突
错误现象:
[root@K8sMasterNode ~]# kubectl describe pod testpod | grep -A5 Events
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning Failed 60s kubelet Failed to pull image "busybox:latest":
tls: failed to verify certificate: x509: certificate signed by unknown authority
Warning Failed 60s kubelet Error: ErrImagePull
Normal BackOff 33s kubelet Back-off pulling image "busybox:latest"
错误原因:节点配置了镜像代理,拉取请求被转发到 Harbor,但 Harbor 使用自签名证书,节点不信任。
解决方案:
| 方案 | 做法 | 适用场景 |
|---|---|---|
| 方案一 | 添加 imagePullPolicy: IfNotPresent | 本地已有镜像,快速绕过 |
| 方案二 | 写 docker.io/library/busybox:latest 绕过代理 | 公共镜像专用 |
| 方案三 | 配置 Harbor 证书信任 | 根本解决 |
错误三:Service 端口号与预期不一致
错误现象:
[root@K8sMasterNode ~]# kubectl get svc
webcluster NodePort 10.96.226.32 <none> 80:31895/TCP 20m
[root@K8sMasterNode ~]# curl http://172.25.254.100:30713/hostname.html
curl: (7) Failed to connect to 172.25.254.100 port 30713: Connection refused
错误原因:Service 实际分配的 NodePort 是 31895,用户访问时用了 30713。
解决方案:
# 用 kubectl get svc 查看实际端口
[root@K8sMasterNode ~]# kubectl get svc webcluster
webcluster NodePort 10.96.226.32 <none> 80:31895/TCP
# 用实际端口访问
[root@K8sMasterNode ~]# curl http://172.25.254.100:31895/hostname.html
webcluster-77c87d9946-2xl44
错误速查表
| 错误现象 | 根本原因 | 最快解决方案 |
|---|---|---|
ImagePullBackOff | 镜像地址缺少仓库前缀 | 使用完整 Harbor 地址 |
x509: certificate signed by unknown authority | 镜像代理 + 自签名证书 | docker.io/library/xxx 绕过代理 |
Connection refused | NodePort 端口号错误 | 用 kubectl get svc 查看实际端口 |
AlreadyExists (Service) | 旧的 Service 未删除 | 先 kubectl delete svc |
unauthorized | 私有仓库未认证 | 执行 docker login 或配置 imagePullSecrets |
核心经验教训
Kubernetes 看的是镜像名,不是您本地有什么。镜像名指向哪里,它就尝试从哪里拉取。
- 自定义镜像始终写完整 Harbor 地址:
reg.avc.harbor.org/library/myapp:v1 - 公共镜像写
docker.io/library/xxx:tag绕过代理 - Service 端口以
kubectl get svc为准 - 排查问题先执行
kubectl describe pod,Events 段藏着答案 - 删除 Deployment 时记得同步删除 Service
更多推荐
所有评论(0)