Kubernetes Controllers
ReplicationController
ReplicationController ,简称 RC,核心能力与 ReplicaSet 基本一致。ReplicaSet是 RC 的迭代升级版本,原生支持更丰富的基于集合的标签选择算符。
本文档不再对ReplicationController做详细展开。
Deployment
学习参考:Deployment
Deployment 介绍
Deployment,简称 deploy,为Pod与ReplicaSet提供声明式应用更新能力。
ReplicaSet 仅负责保障集群中稳定运行指定数量的 Pod 实例;Deployment 则用于托管多组 ReplicaSet,并封装 Pod 滚动更新、版本回滚、扩缩容等上层运维能力。因此官方推荐直接使用 Deployment 管理 ReplicaSet,仅在需要自定义特殊更新流程时才直接操作 ReplicaSet,日常运维几乎无需单独维护 ReplicaSet 资源。
Deployment 用例
以下为 Deployment 的典型业务使用场景:
- 创建 Deployment 以将 ReplicaSet 上线。可校验 ReplicaSet 的发布状态,确认实例是否正常就绪。
- 修改 Deployment 内部 PodTemplateSpec 字段,声明 Pod 的新状态。系统会自动生成全新 ReplicaSet,Deployment 按照可控的速率将业务流量从旧 ReplicaSet 平滑迁移至新 ReplicaSet;每一次模板变更都会生成一条独立的 Deployment 版本记录。
- 当 Deployment 当前运行状态异常、业务故障时,回滚到较早的 Deployment 版本。每一次版本回滚操作同样会新增一条版本修订记录。
- 调整 Deployment 副本数量实现扩容或缩容,承载更高或降低业务负载。
- 暂停 Deployment 的上线流程,批量完成 PodTemplateSpec 的多项配置修改后,再恢复发布流程一次性生成新版本。
- 读取 Deployment 运行状态,判断滚动发布流程是否出现阻塞、异常停滞。
- 自动清理历史版本中不再使用的 ReplicaSet,释放集群资源。
Deployment 管理
创建
命令行方式
bash
root@master30 ~ 09:32:14# kubectl create deployment web --image=hub.laoma.cloud/library/nginx:1.27 --replicas=2
deployment.apps/web created
# 查看deployment创建的资源
root@master30 ~ 09:53:57# kubectl get all
NAME READY STATUS RESTARTS AGE
pod/web-7c7f768db9-g5lsk 1/1 Running 0 41s
pod/web-7c7f768db9-lq5z4 1/1 Running 0 41s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/web 2/2 2 2 41s
NAME DESIRED CURRENT READY AGE
replicaset.apps/web-7c7f768db9 2 2 2 41s
3s
# 查看deployment详细信息
root@master30 ~ 09:53:59# kubectl describe deployments.apps web
Name: web
Namespace: configure
CreationTimestamp: Mon, 10 Aug 2026 09:53:18 +0800
Labels: app=web
Annotations: deployment.kubernetes.io/revision: 1
Selector: app=web
Replicas: 2 desired | 2 updated | 2 total | 2 available | 0 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 25% max unavailable, 25% max surge
Pod Template:
Labels: app=web
Containers:
nginx:
Image: hub.laoma.cloud/library/nginx:1.27
Port: <none>
Host Port: <none>
Environment: <none>
Mounts: <none>
Volumes: <none>
Node-Selectors: <none>
Tolerations: <none>
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
Progressing True NewReplicaSetAvailable
OldReplicaSets: <none>
NewReplicaSet: web-7c7f768db9 (2/2 replicas created)
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ScalingReplicaSet 53s deployment-controller Scaled up replica set web-7c7f768db9 to 2
# ReplicaSet与Deployment关系
root@master30 ~ 09:54:11# kubectl describe replicasets.apps web-7c7f768db9 | grep Controlled
Controlled By: Deployment/web
# 指明ReplicaSet是由Deployment/web创建的。
# pod与ReplicaSet关系
root@master30 ~ 09:55:38# kubectl describe pod web-7c7f768db9-g5lsk | grep Controlled
Controlled By: ReplicaSet/web-7c7f768db9
# 指明pod是由ReplicaSet/web-b78cbd74b创建的。
yaml 文件方式
bash
# 获取deployment的yaml文件
root@master30 ~ 09:56:18# kubectl create deployment web --image=hub.laoma.cloud/library/nginx:1.27 --replicas=2 --dry-run=client -o yaml > deployment-web.yaml
root@master30 ~ 09:57:37# cat deployment-web.yaml
bash
apiVersion: apps/v1
kind: Deployment
metadata:
creationTimestamp: null
labels:
app: web
name: web
spec:
replicas: 2
selector:
matchLabels:
app: web
strategy: {}
template:
metadata:
creationTimestamp: null
labels:
app: web
spec:
containers:
- image: hub.laoma.cloud/library/nginx:1.27
name: nginx
resources: {}
status: {}
格式说明:
① apiVersion,代表当前资源配置对应的 API 版本。
② kind,标识待创建的资源类型,本示例中为 Deployment。
③ metadata,存储资源元数据,其中 name 为必填字段。
④ spec,定义 Deployment 核心运行规格。
⑤ spec.replicas,指定业务 Pod 副本数量,默认值为 1。
⑥ spec.template,Pod 模板定义,是 Deployment 配置文件的核心模块。
⑦ spec.template.metadata,配置 Pod 自身元数据,至少需要定义一组标签,标签键与值可自定义。
⑧ spec.template.spec,描述 Pod 运行规格,内部定义容器各项参数,其中容器 name 与 image 为必填项。
bash
root@master30 ~ 09:57:55# kubectl apply -f deployment-web.yaml
deployment.apps/web configured
创建方式进行比较
-
命令行创建方式:
- 操作简单直观,执行速度快,上手门槛低。
- 适合临时测试、快速验证实验场景。
-
YAML 配置文件创建方式:
-
通过配置文件清晰描述应用最终期望状态,声明式管理更贴合云原生理念。
-
配置文件可作为部署模板,支持多环境重复复用。
-
可纳入代码仓库版本管理,实现部署流程代码化。
-
适用于正式生产环境、跨多集群、大规模批量部署场景。
-
需要掌握 YAML 语法与 K8s 资源字段,存在一定学习成本。
**kubectl apply ** 命令兼具资源创建与在线更新能力,日常运维使用十分便捷。
-
编辑
bash
# 方法1:命令行直接修改
root@master30 ~ 09:58:04# kubectl edit deployments.apps
deployment.apps/web edited
# 方法2:编辑本地配置文件,再通过apply命令更新集群资源
# 方法3:专用命令快速修改,例如调整Deployment副本数量
kubectl scale deployment web --replicas=4
删除
直接删除 Deployment 资源时,系统会同步删除其托管的所有子资源(ReplicaSet、Pod)。
bash
root@master30 ~ 09:58:23# kubectl delete deployments.apps web
deployment.apps "web" deleted
添加**--cascade=orphan**参数删除 Deployment,仅移除 Deployment 控制器,保留其管理的 ReplicaSet 与 Pod 资源。
bash
root@master30 ~ 09:58:40# kubectl apply -f deployment-web.yaml
deployment.apps/web created
root@master30 ~ 09:58:52# kubectl delete deployments.apps web --cascade=orphan
deployment.apps "web" deleted
# 确认残留资源
root@master30 ~ 09:59:18# kubectl get all -l app=web
NAME READY STATUS RESTARTS AGE
pod/web-7c7f768db9-56slk 1/1 Running 0 43s
pod/web-7c7f768db9-v6xlq 1/1 Running 0 43s
NAME DESIRED CURRENT READY AGE
replicaset.apps/web-7c7f768db9 2 2 2 43s
# 批量清理标签匹配的所有残留资源
root@master30 ~ 09:59:35# kubectl delete all -l app=web
pod "web-7c7f768db9-56slk" deleted
pod "web-7c7f768db9-v6xlq" deleted
replicaset.apps "web-7c7f768db9" deleted
root@master30 ~ 09:59:56# kubectl get all -l app=web
No resources found in configure namespace.
水平伸缩
bash
# 创建 deployment
root@master30 ~ 10:03:21# kubectl create deployment web --image=hub.laoma.cloud/library/nginx:1.27 --replicas=2
deployment.apps/web created
root@master30 ~ 10:04:29# kubectl scale deployment web --replicas=3
deployment.apps/web scaled
# 方式二
kubectl edit deployments.apps web
# 方式三:修改本地YAML文件后重新应用
root@master30 ~ 10:05:04# vim web.yaml
root@master30 ~ 10:05:41# kubectl get deployments.apps web -o yaml > web.yaml
root@master30 ~ 10:06:01# kubectl get pod
NAME READY STATUS RESTARTS AGE
web 1/1 Running 1 (62m ago) 2d18h
web-7c7f768db9-c86wq 1/1 Running 0 66s
web-7c7f768db9-rf5fz 1/1 Running 0 2m26s
web-7c7f768db9-sbqhb 1/1 Running 0 2m26s
健壮性测试
模拟关闭工作节点,验证 Pod 自动重建自愈能力。
bash
root@master30 ~ 10:40:08# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web-7c7f768db9-9ltgf 1/1 Running 0 14m 10.224.133.81 worker31.liu.cloud <none> <none>
web-7c7f768db9-c86wq 1/1 Running 0 35m 10.224.133.80 worker31.liu.cloud <none> <none>
web-7c7f768db9-sbqhb 1/1 Running 0 36m 10.224.133.79 worker31.liu.cloud <none> <none>
# 关闭 worker32
root@worker32 ~ 10:19:50# init 0
# 等待约5分钟,Kubernetes判定worker32节点不可用,将该节点上全部Pod标记为Unknown状态,并在其他健康节点重建Pod,维持预设副本总数为3
# 后续worker32节点恢复上线后,原Unknown状态Pod会被清除,已调度至其他节点正常运行的Pod不会自动回迁worker32。
K8s 判定节点宕机分为完整的3 个阶段:
-
节点上的 kubelet 组件默认每 10 秒向 kube-apiserver 上报一次节点心跳状态。
-
Master 节点的
controller-manager每5 秒轮询检查各节点心跳记录;若连续40 秒未收到某节点心跳,则标记该节点状态为
NotReady。对应控制参数:
node-monitor-period=5s、node-monitor-grace-period=40s。该组参数归属kube-controller-manager组件,该组件以静态 Pod 形式部署在 Master 节点,配置文件路径:/etc/kubernetes/manifests/kube-controller-manager.yaml修改方式:编辑该静态 Pod 配置文件,找到 command 配置段,新增或调整
--pod-eviction-timeout参数,保存后静态 Pod 会自动重启生效。 -
节点持续保持 NotReady 状态满 5 分钟后,集群开始驱逐该节点上所有 Pod,并调度至其他健康节点重建。
对应参数:
pod-eviction-timeout=300s。该参数同样隶属于
kube-controller-manager组件,修改步骤同上。
更新镜像
bash
# 将deployment内部容器镜像替换为 hub.laoma.cloud/library/nginx:1.28
# 先查看容器名称
root@master30 ~ 10:40:21# kubectl get deployments.apps -o wide
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
web 3/3 3 3 37m nginx hub.laoma.cloud/library/nginx:1.27 app=web
# 更新镜像为hub.laoma.cloud/library/nginx:1.28
root@master30 ~ 10:41:29# kubectl set image deployment/web nginx=hub.laoma.cloud/library/nginx:1.28 --record
# 方式二:直接编辑Deployment资源修改镜像版本
kubectl edit deployments.apps web
# 更新操作会生成全新ReplicaSet,用于创建新版本Pod
root@master30 ~ 10:42:32# kubectl get rs
NAME DESIRED CURRENT READY AGE
web-74c65fbcdd 3 3 3 18s
web-7c7f768db9 0 0 0 39m
# 校验镜像版本是否更新成功
root@master30 ~ 10:40:21# kubectl get deployments.apps -o wide
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
web 3/3 3 3 37m nginx hub.laoma.cloud/library/nginx:1.27 app=web
版本控制
通过 kubectl rollout 系列命令管理 Deployment 多版本发布记录。
bash
root@master30 ~ 10:43:40# kubectl rollout -h
Manage the rollout of one or many resources.
Valid resource types include:
* deployments
* daemonsets
* statefulsets
Examples:
# Rollback to the previous deployment
kubectl rollout undo deployment/abc
# Check the rollout status of a daemonset
kubectl rollout status daemonset/foo
# Restart a deployment
kubectl rollout restart deployment/abc
# Restart deployments with the 'app=nginx' label
kubectl rollout restart deployment --selector=app=nginx
Available Commands:
history View rollout history
pause Mark the provided resource as paused
restart Restart a resource
resume Resume a paused resource
status Show the status of the rollout
undo Undo a previous rollout
Usage:
kubectl rollout SUBCOMMAND [options]
Use "kubectl rollout <command> --help" for more information about a given command.
Use "kubectl options" for a list of global command-line options (applies to all commands).
实操示例:
bash
# 再次更新镜像至 hub.laoma.cloud/library/nginx:1.28
root@master30 ~ 10:43:49# kubectl set image deployment/web nginx=hub.laoma.cloud/library/nginx:1.28 --record
Flag --record has been deprecated, --record will be removed in the future
# 查看完整发布版本历史
root@master30 ~ 10:44:57# kubectl rollout history deployment web
deployment.apps/web
REVISION CHANGE-CAUSE
1 <none>
2 kubectl set image deployment/web nginx=hub.laoma.cloud/library/nginx:1.28 --record=true
# 回滚至第1个历史版本
root@master30 ~ 10:46:04# kubectl rollout undo deployment web --to-revision=1
deployment.apps/web rolled back
root@master30 ~ 10:46:33# kubectl rollout history deployment web
deployment.apps/web
REVISION CHANGE-CAUSE
2 kubectl set image deployment/web nginx=hub.laoma.cloud/library/nginx:1.28 --record=true
3 kubectl set image deployment/web nginx=hub.laoma.cloud/library/nginx:1.29 --record=true
4 <none>
滚动更新
Kubernetes 提供 maxSurge 与 maxUnavailable 两个核心参数,精细化控制滚动更新过程中 Pod 的新建、销毁速率。
-
maxSurge:控制滚动更新期间,集群总副本数超出预设 DESIRED 副本数量的最大上限。参数支持整数或百分比配置,百分比计算结果向上取整,默认值 25%。
举例:业务预设副本数 DESIRED 为 10,最大总副本数 = 向上取整 (10 + 10 × 25%) =13,对应查询中 CURRENT 字段最大值为 13。
-
maxUnavailable:控制滚动更新期间,集群内不可用业务副本占 DESIRED 总数的最大比例。支持整数或百分比配置,百分比计算结果向下取整,默认值 25%。
举例:业务预设副本数 DESIRED 为 10,集群最小可用副本数 = 10 - 向下取整 (10 × 25%)= 8,对应查询中 AVAILABLE 字段最低值为 8。
参数总结:
-
maxSurge 数值越大,滚动更新初期可同时创建的新版本 Pod 数量越多。
-
maxUnavailable
数值越大,滚动更新初期可同时销毁的旧版本 Pod 越多,更新过程中业务不可用实例数量会同步上升。
理想场景下,完整滚动更新流程逻辑如下:
-
批量创建 3 个新版本 Pod,此时集群运行中 Pod 总数达到上限 13。
-
销毁 2 个旧版本 Pod,同步新建 2 个新版本 Pod;集群可用 Pod 维持 8 个。若此前新建的 3 个新 Pod 未完成就绪变为 Running,则集群内存在 5 个处于 ContainerCreating 状态的新 Pod,总副本数依旧保持上限 13。
-
待新建 Pod 全部就绪切换至 Running 状态(示例中 5 个新 Pod 同时就绪)。
-
集群运行 Pod 总数达到 13,此时可一次性销毁 5 个旧 Pod,同步创建 5 个新 Pod,保证集群可用副本数不低于 8。
-
上述新建、销毁循环持续执行,直至所有旧版本 Pod 全部替换为新版本,滚动更新流程完成。
实操监控:
更新 Deployment 镜像,执行以下脚本持续监控 Pod 状态变化:
bash
root@master30 ~ 10:46:52# vim monitor_pod_numbers
bash
#!/bin/bash
while true
do
echo '===================' >> output.log
kubectl get pods --no-headers |awk '{print $3}' |sort | uniq -c |sed -r 's/^ +//'>> output.log
sleep 0.5
done
输出日志样例:
bash
===================
10 Running
===================
5 ContainerCreating
8 Running
2 Terminating
===================
5 ContainerCreating
8 Running
2 Terminating
===================
5 ContainerCreating
8 Running
===================
5 ContainerCreating
8 Running
===================
3 ContainerCreating
1 Pending
9 Running
4 Terminating
===================
5 ContainerCreating
8 Running
5 Terminating
===================
5 ContainerCreating
8 Running
5 Terminating
===================
5 ContainerCreating
8 Running
3 Terminating
===================
10 Running
4 Terminating
===================
10 Running
2 Terminating
===================
10 Running
===================
10 Running
===================
DaemonSet
学习参考:DaemonSet
DaemonSet 介绍
DaemonSet,简写 DS,核心作用是保证集群全部或匹配筛选条件的节点上各运行一个 Pod 副本。当集群新增节点时,控制器会自动在新节点创建对应 Pod;节点从集群移除时,节点上的 DS Pod 会同步回收删除。
DaemonSet 用例
DaemonSet 主流业务落地场景:
- 在集群每个节点部署存储守护进程,例如 glusterd、ceph 相关组件。
- 在集群每个节点部署日志采集组件,例如 fluentd、logstash。
- 在集群每个节点部署监控采集组件,例如 Prometheus Node Exporter、collectd。
DaemonSet 用法
简单使用场景:为每一类节点后台守护进程单独创建一套 DaemonSet,全节点统一部署。
复杂使用场景:同一类采集 / 守护程序部署多套独立 DaemonSet;每套配置差异化启动参数,针对不同硬件节点配置独立 CPU、内存资源配额。
DaemonSet 使用
DaemonSet 创建
bash
root@master30 ~ 11:12:03# vim daemonset.yaml
bash
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: busybox
spec:
selector:
matchLabels:
app: busybox
template:
metadata:
labels:
app: busybox
spec:
containers:
- name: busybox
image: hub.laoma.cloud/library/busybox
imagePullPolicy: IfNotPresent
command:
- sleep
- "36000"
bash
root@master30 ~ 11:28:03# kubectl apply -f daemonset.yaml
DaemonSet 查看
bash
root@master30 ~ 11:28:17# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
busybox-5c8xs 1/1 Running 0 12s 10.224.218.158 worker32.liu.cloud <none> <none>
busybox-r4f2n 1/1 Running 0 12s 10.224.133.97 worker31.liu.cloud <none> <none>
root@master30 ~ 11:28:29# kubectl get ds
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
busybox 2 2 2 2 2 <none> 25s
DaemonSet 调度
bash
# Master控制节点默认存在污点,禁止常规Pod调度
root@master30 ~ 11:28:42# kubectl describe node master30.liu.cloud |grep Taints
Taints: node-role.kubernetes.io/control-plane:NoSchedule
# 移除Master节点调度限制污点,允许Pod调度至Master
root@master30 ~ 11:29:09# kubectl taint node master30.liu.cloud node-role.kubernetes.io/control-plane-
node/master30.liu.cloud untainted
root@master30 ~ 11:29:53# kubectl get ds
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
busybox 3 3 3 3 3 <none> 106s
# 重新添加Master节点禁止调度污点
root@master30 ~ 11:30:03# kubectl taint node master30.liu.cloud node-role.kubernetes.io/control-plane:NoSchedule
node/master30.liu.cloud tainted
root@master30 ~ 11:30:29# kubectl get ds
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
busybox 2 2 2 2 2 <none> 2m19s
# 集群统计Pod数量变为2个,但已调度至Master节点的busybox Pod不会自动删除,也不再计入DS统计指标。
DaemonSet 健壮性
bash
# 强制删除任意一个DS管理的Pod
root@master30 ~ 11:31:35# kubectl delete pod busybox-5c8xs --force
Warning: Immediate deletion does not wait for confirmation that the running resource has been terminated. The resource may continue to run on the cluster indefinitely.
pod "busybox-5c8xs" force deleted
# 控制器会立刻新建Pod补齐节点实例
root@master30 ~ 11:31:43# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
busybox-h5cqh 1/1 Running 0 12s 10.224.218.159 worker32.liu.cloud <none> <none>
busybox-r4f2n 1/1 Running 0 3m38s 10.224.133.97 worker31.liu.cloud <none> <none>
busybox-wg6h6 1/1 Running 0 2m2s 10.224.43.138 master30.liu.cloud <none> <none>
DaemonSet 删除
直接删除 DaemonSet 资源时,系统会同步删除其创建的全部节点 Pod;添加**--cascade=orphan**参数删除仅移除 DS 控制器,保留节点上所有 Pod 实例。
bash
root@master30 ~ 11:31:55# kubectl delete daemonsets.apps busybox
daemonset.apps "busybox" deleted
K8s 集群中 DS
Kubernetes 集群内部系统组件普遍依托 DaemonSet 控制器部署,典型代表为 kube-proxy、calico-node。
bash
root@master30 ~ 11:33:14# kubectl get daemonsets.apps --namespace kube-system
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
calico-node 3 3 3 3 3 kubernetes.io/os=linux 4d20h
kube-proxy 3 3 3 3 3 kubernetes.io/os=linux 4d21h
calico-node 核心配置片段解析:
bash
kind: DaemonSet
apiVersion: apps/v1
metadata:
name: calico-node
namespace: kube-system
labels:
k8s-app: calico-node
spec:
selector:
matchLabels:
k8s-app: calico-node
updateStrategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
template:
metadata:
labels:
k8s-app: calico-node
annotations:
scheduler.alpha.kubernetes.io/critical-pod: ''
spec:
nodeSelector:
kubernetes.io/os: linux
hostNetwork: true
containers:
- name: calico-node
image: hub.laoma.cloud/calico/node:v3.28.0
完整原生配置文件内容更为复杂,本示例仅保留核心字段用于 DaemonSet 学习演示。
生产级示例
采集节点日志 - Fluent Bit
bash
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluent-bit-ds
namespace: kube-system
spec:
selector:
matchLabels:
app: fluent-bit
template:
metadata:
labels:
app: fluent-bit
spec:
containers:
- name: fluent-bit
image: cr.fluentbit.io/fluent/fluent-bit:latest
volumeMounts:
- name: varlog
mountPath: /var/log
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
readOnly: true
volumes:
- name: varlog
hostPath:
path: /var/log
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers
采集节点监控指标 - Node Exporter
bash
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: node-exporter-ds
namespace: monitoring
spec:
selector:
matchLabels:
app: node-exporter
template:
metadata:
labels:
app: node-exporter
spec:
containers:
- name: node-exporter
image: prom/node-exporter:latest
ports:
- containerPort: 9100
volumeMounts:
- name: proc
mountPath: /host/proc
readOnly: true
- name: sys
mountPath: /host/sys
readOnly: true
volumes:
- name: proc
hostPath:
path: /proc
- name: sys
hostPath:
path: /sys
Job
学习参考:Job
Job 介绍
- Job 控制器用于调度一次性批处理任务。
- 若 Pod 内业务执行失败异常退出,控制器会自动创建全新 Pod 重试任务,直至容器正常退出(退出码为 0),Job 流程才算执行完成。
- 若运行 Job Pod 的节点发生宕机故障,该 Pod 会被重新调度至其他健康节点执行。
- 直接删除 Job 资源会级联清除其创建的所有 Pod 实例。
- 暂停 Job 执行会立刻终止所有运行中的活跃 Pod,恢复后重新调度执行。
Job 用例
典型落地场景:
- 数据库定时清理、数据批量清洗。
- Kubernetes 集群资源备份、数据导出。
Job 使用
Job 基本管理
bash
root@master30 ~ 14:24:23# kubectl create job -h
Create a job with the specified name.
Examples:
# Create a job
kubectl create job my-job --image=busybox
# Create a job with command
kubectl create job my-job --image=busybox -- date
......
Usage:
kubectl create job NAME --image=image [--from=cronjob/name] -- [COMMAND]
[args...] [options]
示例 1:
bash
root@master30 ~ 14:26:29# kubectl create job myjob --image=hub.laoma.cloud/library/busybox -- echo hello k8s job!
job.batch/myjob created
root@master30 ~ 14:26:46# kubectl get all
NAME READY STATUS RESTARTS AGE
pod/myjob-pdrwv 0/1 Completed 0 11m
NAME COMPLETIONS DURATION AGE
job.batch/myjob 1/1 20s 11m
root@master30 ~ 14:28:08# kubectl logs myjob-pdrwv
hello k8s job!
# 删除 Job 资源会同步清理关联所有Pod
# 添加--cascade=orphan参数可保留已完成Pod
root@master30 ~ 14:28:39# kubectl delete jobs.batch myjob
job.batch "myjob" deleted
通过 YAML 配置文件创建 Job:
bash
root@master30 ~ 14:28:58# vim job.yaml
bash
apiVersion: batch/v1
kind: Job
metadata:
name: myjob
spec:
template:
metadata:
name: myjob
spec:
containers:
- name: hello
image: hub.laoma.cloud/library/busybox
imagePullPolicy: IfNotPresent
command: ["echo", "hello k8s job! "]
restartPolicy: Never
bash
root@master30 ~ 14:29:25# kubectl apply -f job.yaml
job.batch/myjob created
示例 2: 计算圆周率并输出 200 位小数。
bash
root@master30 ~ 14:29:34# vim job-pi.yaml
bash
apiVersion: batch/v1
kind: Job
metadata:
name: pi
spec:
template:
spec:
containers:
- name: pi
image: hub.laoma.cloud/library/perl
imagePullPolicy: IfNotPresent
command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(200)"]
restartPolicy: Never
backoffLimit: 4
bash
root@master30 ~ 14:30:03# kubectl apply -f job-pi.yaml
job.batch/pi created
# 等待任务执行完成
root@master30 ~ 14:30:14# kubectl get all
NAME READY STATUS RESTARTS AGE
pod/pi-9qtzw 0/1 Completed 0 79s
NAME COMPLETIONS DURATION AGE
job.batch/pi Running 0/1 6s 6s
root@master30 ~ 14:40:23# kubectl logs pi-9qtzw
3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446095505822317253594081284811174502841027019385211055596446229489549303820
restartPolicy
Job 内部 Pod 重启策略仅支持两种配置:
-
Never:任务执行失败退出后,不会重启当前异常 Pod;控制器持续新建独立 Pod 重试,因此集群会产生多个失败 Pod 实例。
-
OnFailure:任务执行失败退出后,复用当前 Pod 容器反复重启重试,集群始终仅保留单个 Pod,重启次数会持续累加。
验证实验:修改 job.yaml,将合法命令
echo替换为不存在的echoxxx模拟执行失败。
bash
root@master30 ~ 14:40:38# vim job.yaml
bash
apiVersion: batch/v1
kind: Job
metadata:
name: myjob
spec:
template:
metadata:
name: myjob
spec:
containers:
- name: hello
image: hub.laoma.cloud/library/busybox
imagePullPolicy: IfNotPresent
command: ["echoxxx", "hello k8s job! "]
restartPolicy: Never
重新应用配置观察效果:
bash
root@master30 ~ 14:41:03# kubectl apply -f job.yaml
root@master30:~# kubectl get all
NAME READY STATUS RESTARTS AGE
pod/myjob-rfvq4 0/1 StartError 0 8s
pod/myjob-gczbz 0/1 StartError 0 2s
pod/myjob-n4bpv 0/1 StartError 0 22s
NAME COMPLETIONS DURATION AGE
job.batch/myjob 0/1 33s 33s
# 可观察到集群生成多个状态异常的Pod
# 查看单个Pod详细事件日志
root@master30 ~ 14:42:25# kubectl describe pod myjob-rfvq4
......
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 31s default-scheduler Successfully assigned configure/myjob-rfvq4 to worker31.liu.cloud
Normal Pulled 31s kubelet Container image "hub.laoma.cloud/library/busybox" already present on machine
Normal Created 31s kubelet Created container hello
Warning Failed 31s kubelet Error: failed to create containerd task: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: exec: "echoxxx": executable file not found in $PATH: unknown
日志提示系统环境变量 PATH 中未找到指定可执行程序,与实验预期一致。
现象说明: 集群中出现大量失败 Pod 的原因?
原理解释: 首个 Pod 启动后执行命令异常退出;配置为restartPolicy: Never时,kubelet 不会重启当前 Pod,但 Job 默认要求完成 1 次成功执行,当前 COMPLETIONS 计数为 0 不满足完成条件,控制器会持续创建全新 Pod 重试。本示例中命令永久不存在,任务永远无法成功完成,集群会无限新建失败 Pod。如需终止该循环,需手动删除 Job 资源。
清理测试 Job 资源
bash
root@master30 ~ 14:56:13# kubectl delete jobs.batch myjob
job.batch "myjob" deleted
若将 restartPolicy 调整为 OnFailure 会产生什么变化?
bash
apiVersion: batch/v1
kind: Job
metadata:
name: myjob
spec:
template:
metadata:
name: myjob
spec:
containers:
- name: hello
image: hub.laoma.cloud/library/busybox
imagePullPolicy: IfNotPresent
command: ["echoxxxx", "hello k8s job! "]
restartPolicy: OnFailure
重新部署验证:
bash
root@master30 ~ 14:57:37# kubectl apply -f job.yaml
job.batch/myjob created
root@master30 ~ 14:57:54# kubectl get all
NAME READY STATUS RESTARTS AGE
pod/myjob-xbwxh 0/1 CrashLoopBackOff 1 68s
NAME COMPLETIONS DURATION AGE
job.batch/myjob 0/1 68s 68s
# Pod执行失败后仅在当前容器内反复重启。
# 集群Pod数量始终保持1个,RESTARTS字段数值持续上涨。
backoffLimit
用于限制任务执行失败后的最大重试次数。
bash
apiVersion: batch/v1
kind: Job
metadata:
name: myjob
spec:
backoffLimit: 2
template:
metadata:
name: myjob
spec:
containers:
- name: hello
image: hub.laoma.cloud/library/busybox
imagePullPolicy: IfNotPresent
command: ["echoxx", "hello k8s job! "]
restartPolicy: Never
测试效果:最多自动新建 2 次重试 Pod,达到上限后停止重试。
bash
root@master30 ~ 14:57:59# kubectl get all
NAME READY STATUS RESTARTS AGE
pod/myjob-87gs2 0/1 StartError 0 8s
pod/myjob-xp6fr 0/1 StartError 0 19s
pod/myjob-abcde 0/1 StartError 0 83s
NAME COMPLETIONS DURATION AGE
job.batch/myjob 0/1 83s 83s
completions
通过 completions 字段指定 Job 需要成功完成多少次 Pod 执行,才算整体任务结束。
bash
apiVersion: batch/v1
kind: Job
metadata:
name: myjob
spec:
completions: 2
template:
metadata:
name: myjob
spec:
containers:
- name: hello
image: hub.laoma.cloud/library/busybox
imagePullPolicy: IfNotPresent
command: ["echo", "hello k8s job! "]
restartPolicy: Never
上述配置含义:累计完成 2 个正常退出的 Pod 实例,Job 标记为完成状态。
bash
root@master30 ~ 14:58:53# kubectl get all
NAME READY STATUS RESTARTS AGE
pod/myjob-hkghv 0/1 Completed 0 23s
pod/myjob-mps9v 0/1 Completed 0 26s
NAME COMPLETIONS DURATION AGE
job.batch/myjob 2/2 12s 12s
未手动配置 completions 时,默认取值为 1。
parallelism
如需同时运行多个 Pod 并行处理任务、缩短整体执行耗时,可通过 parallelism 设置并发 Pod 数量。
bash
apiVersion: batch/v1
kind: Job
metadata:
name: myjob
spec:
completions: 6
parallelism: 2
template:
metadata:
name: myjob
spec:
containers:
- name: hello
image: hub.laoma.cloud/library/busybox
imagePullPolicy: IfNotPresent
command: ["echo", "hello k8s job! "]
restartPolicy: Never
bash
root@master30 ~ 15:00:13# kubectl get all
NAME READY STATUS RESTARTS AGE
pod/myjob-9mtqx 0/1 Completed 0 4s
pod/myjob-f4vrv 0/1 Completed 0 2s
pod/myjob-hd6mb 0/1 Completed 0 1s
pod/myjob-rvw4s 0/1 Completed 0 4s
pod/myjob-rxg7c 0/1 Completed 0 8s
pod/myjob-wr6xp 0/1 Completed 0 8s
NAME STATUS COMPLETIONS DURATION AGE
job.batch/myjob Running 5/6 8s 8s
运行逻辑:同一时刻最多并发 2 个 Pod 执行,直至累计完成 6 次成功任务。
未手动配置 parallelism 时,默认取值为 1。
上述示例仅用于演示并发特性,业务实用价值较低;真实业务中批量数据处理、分布式任务分发场景广泛使用该配置,多个 Pod 从统一任务池读取数据并行处理,副本数量越多整体处理速度越快。
activeDeadlineSeconds
Job 总运行时长达到该字段设定秒数后,系统会强制终止所有正在运行的 Pod ,Job 状态更新为type: Failed,失败原因为DeadlineExceeded。该时长约束作用于 Job 完整生命周期,与内部创建 Pod 数量无关。
bash
apiVersion: batch/v1
kind: Job
metadata:
name: pi-with-timeout
spec:
backoffLimit: 5
activeDeadlineSeconds: 10
template:
spec:
containers:
- name: pi
image: hub.laoma.cloud/library/perl
command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
restartPolicy: Never
优先级规则: .spec.activeDeadlineSeconds 超时限制优先级高于 .spec.backoffLimit 最大重试次数。若 Job 持续重试失败 Pod,但整体运行时长已触达 activeDeadlineSeconds 阈值,系统会直接停止新建 Pod,即便未达到 backoffLimit 设定的重试上限。
ttlSecondsAfterFinished
配置 .spec.ttlSecondsAfterFinished 字段,可启用 TTL 控制器自动清理已结束(成功 / 失败)的 Job 资源。
TTL 控制器清理 Job 时会级联删除所有关联依赖资源,包括下属 Pod 与 Job 自身。资源删除流程会遵循 Finalizers 生命周期保障机制。
配置示例:
bash
apiVersion: batch/v1
kind: Job
metadata:
name: pi-with-ttl
spec:
ttlSecondsAfterFinished: 100
template:
spec:
containers:
- name: pi
image: hub.laoma.cloud/library/perl
command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
restartPolicy: Never
本示例中 Job 执行完成 100 秒后,会被 TTL 控制器自动回收清理。
若字段值设为0,Job 执行结束后立即触发自动删除;若未配置该字段,TTL 控制器不会自动清理该 Job。
该 TTL 自动清理功能当前属于 Alpha 阶段特性,集群使用前需开启TTLAfterFinished特性门控,详细说明参考官方文档TTL 控制器。
CronJob
学习参考:CronJob
CronJob 介绍
Linux 系统内置 cron 工具用于周期性定时任务,Kubernetes 中 CronJob 实现同类能力,用于按固定周期调度 Job 执行,典型场景包含定时数据备份、周期性报表生成等。
CronJob 自动创建 Job 与下属 Pod 时,.metadata.name会作为 Pod 主机名前缀;CronJob 名称必须符合合法DNS 子域规范。为避免 Pod 主机名异常,建议严格遵循更严苛的DNS 标签命名规则。
CronJob 控制器会在原始名称后自动追加 11 位随机字符生成 Job 名称,受限于 Job 名称总长度上限 63 字符,CronJob 自身名称长度不能超过 52 字符。
CronJob 使用
bash
root@master30 ~ 15:07:44# kubectl create cronjob -h
Create a cronjob with the specified name.
crea
Aliases:
cronjob, cj
Examples:
# Create a cronjob
kubectl create cronjob my-job --image=busybox --schedule="*/1 * * * *"
# Create a cronjob with command
kubectl create cronjob my-job --image=busybox --schedule="*/1 * * * *" -- date
......
Usage:
kubectl create cronjob NAME --image=image --schedule='0/5 * * * ?' --
[COMMAND] [args...] [flags] [options]
实操示例:
bash
root@master30 ~ 15:09:09# kubectl create cronjob mycronjob --image=hub.laoma.cloud/library/busybox --schedule='*/2 * * * *' -- echo hello k8s job!
cronjob.batch/mycronjob created
等效 YAML 配置文件:
bash
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: mycronjob
spec:
schedule: "*/2 * * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: hello
image: hub.laoma.cloud/library/busybox
command: ["echo", "hello k8s job! "]
restartPolicy: Never
等待数个调度周期后,集群会生成多条历史 Job 记录,默认最多保留 3 条完成 Job。
bash
root@master30 ~ 15:25:56# kubectl get all
NAME READY STATUS RESTARTS AGE
pod/mycronjob-28301160-gvm2v 0/1 Completed 0 5m18s
pod/mycronjob-28301162-25jll 0/1 Completed 0 3m18s
pod/mycronjob-28301164-h7mf7 0/1 Completed 0 78s
NAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULE AGE
cronjob.batch/mycronjob */2 * * * * False 0 78s 36m
NAME COMPLETIONS DURATION AGE
job.batch/mycronjob-28301160 1/1 18s 5m18s
job.batch/mycronjob-28301162 1/1 19s 3m18s
job.batch/mycronjob-28301164 1/1 19s 78s
CronJob 参数
Cron 时间表
.spec.schedule 为必填字段,语法完全兼容标准 Linux Cron 表达式。
bash
# ┌───────────── 分钟 (0 - 59)
# │ ┌───────────── 小时 (0 - 23)
# │ │ ┌───────────── 每月日期 (1 - 31)
# │ │ │ ┌───────────── 月份 (1 - 12)
# │ │ │ │ ┌───────────── 每周日期 (0 - 6)(0代表周日;部分系统兼容7代表周日)
# │ │ │ │ │ 也可使用英文简写 sun,mon,tue,wed,thu,fri,sat
# │ │ │ │ │
# │ │ │ │ │
# * * * * *
表达式示例 0 0 13 * 5:每月 13 日零点、每周五零点执行任务。
任务模板
.spec.jobTemplate 用于定义 CronJob 生成 Job 的模板,为必填项。内部语法与独立 Job 资源完全一致,仅无需填写 apiVersion 与 kind 顶层字段。可在模板内自定义标签、注解等通用元数据,Job 完整配置规范参考编写 Job 规约。
任务延迟开始的最后期限
.spec.startingDeadlineSeconds 可选配置,用于设定错过调度时间后,任务允许延迟启动的最大秒数。
- 超过该截止时间仍未启动的任务会直接跳过,下一次定时调度不受影响。例如每日执行两次备份,允许最大延迟 8 小时,超出时限的备份无业务意义,直接放弃本次执行,等待下一轮定时。
- 错过截止时间的调度记录会标记为失败任务;未配置该字段时,任务无延迟时间限制。
- 逻辑判定:到达定时点后,控制器计算当前时间与预期调度时间的差值,若差值超过该阈值则跳过本次 Job 创建。示例配置 200,代表定时触发后最多延迟 200 秒启动,超时直接放弃。
并发性规则
.spec.concurrencyPolicy 可选配置,定义同一 CronJob 新旧任务执行重叠时的处理策略,仅支持以下三种取值:
-
Allow(默认策略):允许多个任务并发同时运行。 -
Forbid:禁止并发执行;定时触发时若上一轮任务仍未完成,直接跳过本次调度。 -
Replace:定时触发时若存在运行中的旧任务,终止旧任务并创建全新任务替换执行。注意:并发规则仅约束
同一个 CronJob
生成的任务;集群内不同 CronJob 创建的任务天然允许并发运行。
任务历史限制
.spec.successfulJobsHistoryLimit 与 .spec.failedJobsHistoryLimit 为可选配置,分别控制集群保留的成功、失败历史 Job 数量。系统默认保留 3 条成功 Job、1 条失败 Job;字段值设为0代表执行完成后不保留任何历史 Job。
如需更多自动清理 Job 的方案,参考官方文档自动清理完成的 Job。
综合案例
案例 1:定期清理主机目录内容
需求:每分钟执行一次脚本,清空 worker31 节点本地/var/data目录文件。
实现思路:
- 清理命令:rm -fr /var/data/*
- 固定调度至 worker31 节点运行:配置 nodeName: worker31
- 将宿主机
/var/data目录挂载至 Pod 内:使用 hostPath 存储卷 - 周期性调度执行:采用 CronJob 控制器
bash
apiVersion: batch/v1
kind: CronJob
metadata:
name: clean
spec:
jobTemplate:
metadata:
name: clean
spec:
template:
metadata:
spec:
nodeName: worker31.laoma.cloud
containers:
- command:
- sh
- -c
- sleep 3 && rm -fr /var/data/*
image: hub.laoma.cloud/library/busybox
name: clean
volumeMounts:
- mountPath: /var/data
name: data
volumes:
- name: data
hostPath:
path: /var/data
restartPolicy: OnFailure
schedule: '* * * * *'
案例 2:通过 cronjob 来操作 k8s 集群
实现思路:
-
选用内置 kubectl 工具的镜像 bitnami/kubectl。
-
将集群管理员 kubeconfig 凭据通过配置映射挂载至 Pod,供 kubectl 鉴权访问集群。
操作流程:
bash
# 创建ConfigMap存储kubeconfig文件
root@master30 ~ 15:30:04# kubectl create cm kubeconfig --from-file=config=/root/.kube/config
configmap/kubeconfig created
# 在CronJob模板中通过存储卷挂载该ConfigMap
bash
apiVersion: batch/v1
kind: CronJob
metadata:
name: clean
spec:
schedule: '* * * * *'
jobTemplate:
metadata:
name: clean
spec:
template:
spec:
restartPolicy: Never
containers:
- image: hub.laoma.cloud/kubernetes/kubectl:1.30.2
imagePullPolicy: IfNotPresent
name: kubectl
args:
- delete
- pods
- -l
- app=hello
- -n
- controllers
volumeMounts:
- name: kubeconfig
mountPath: "/.kube"
volumes:
- name: kubeconfig
configMap:
name: kubeconfig
hub.laoma.cloud/kubernetes/kubectl:v1.36.0 镜像基于 bitnami/kubectl 二次封装。
bitnami/kubectl 镜像官方使用文档参考:bitnami/kubectl。
环境清理
bash
root@master30 ~ 15:34:07# kubectl delete ns controllers
Kubernetes Service
学习参考:Service
环境准备
bash
root@master30 ~ 15:39:06# kubectl create ns services
namespace/services created
root@master30 ~ 16:19:50# kubectl config set-context --current --namespace services
Context "kubernetes-admin@kubernetes" modified.
先看两个例子
示例 1:
bash
root@master30 ~ 16:19:56# kubectl run web --image=hub.laoma.cloud/library/httpd --image-pull-policy=IfNotPresent -o yaml --dry-run=client > pod-web.yml
root@master30 ~ 16:20:39# kubectl apply -f pod-web.yml
pod/web created
root@master30 ~ 16:20:51# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web 1/1 Running 0 15s 10.224.218.145 worker32.liu.cloud <none> <none>
root@master30 ~ 16:21:06# curl http://10.224.218.145
# 删除pod
root@master30 ~ 16:21:30# kubectl delete pod web --force
Warning: Immediate deletion does not wait for confirmation that the running resource has been terminated. The resource may continue to run on the cluster indefinitely.
# 重新创建可对外访问的Pod,修改配置文件如下
root@master30 ~ 16:21:48# vim pod-web.yml
bash
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: web
name: web
spec:
containers:
- image: hub.laoma.cloud/library/httpd
imagePullPolicy: IfNotPresent
name: web
# 添加ports参数
ports:
- containerPort: 80
hostPort: 8080
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
bash
root@master30 ~ 16:22:23# kubectl apply -f pod-web.yml
pod/web created
root@master30 ~ 16:22:42# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web 1/1 Running 0 17s 10.224.218.147 worker32.liu.cloud <none> <none>
root@master30 ~ 16:22:59# curl http://worker32.liu.cloud:8080
<html><body><h1>It works!</h1></body></html>
# 结论:仅能通过Pod所在节点主机IP访问Pod,集群外部客户端必须提前知晓Pod运行在哪个节点。
# 若通过控制器批量创建同类Pod,同一节点上会出现端口冲突问题
# 清理 Pod 资源
root@master30 ~ 16:25:37# kubectl delete pod web --force
Warning: Immediate deletion does not wait for confirmation that the running resource has been terminated. The resource may continue to run on the cluster indefinitely.
pod "web" force deleted
示例 2:
bash
root@master30 ~ 16:26:45# kubectl create deployment web --image=hub.laoma.cloud/library/httpd --replicas=4 --dry-run=client -o yaml > deploy-web.yml
root@master30 ~ 16:26:55# vim deploy-web.yml
bash
apiVersion: apps/v1
kind: Deployment
metadata:
creationTimestamp: null
labels:
app: web
name: web
spec:
replicas: 4
selector:
matchLabels:
app: web
strategy: {}
template:
metadata:
creationTimestamp: null
labels:
app: web
spec:
containers:
- image: hub.laoma.cloud/library/httpd
name: httpd
#添加imagePullPolicy
imagePullPolicy: IfNotPresent
#添加ports
ports:
- containerPort: 80
hostPort: 8080
resources: {}
status: {}
bash
root@master30 ~ 16:27:18# kubectl apply -f deploy-web.yml
deployment.apps/web created
root@master30 ~ 16:27:32# kubectl get pod
NAME READY STATUS RESTARTS AGE
web-6f85d979d4-2v9fz 1/1 Running 0 15s
web-6f85d979d4-7fkcq 0/1 Pending 0 15s
web-6f85d979d4-p7zkm 1/1 Running 0 15s
web-6f85d979d4-rh5xn 0/1 Pending 0 15s
# 两个Pod处于挂起状态,根源是节点无空闲8080宿主机端口可用
root@master30 ~ 16:27:47# kubectl describe pod web-6f85d979d4-7fkcq
......
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling 49s default-scheduler 0/3 nodes are available: 1 node(s) had taint {node-role.kubernetes.io/master: }, that the pod didn't tolerate, 2 node(s) didn't have free ports for the requested pod ports.
Warning FailedScheduling 49s default-scheduler 0/3 nodes are available: 1 node(s) had taint {node-role.kubernetes.io/master: }, that the pod didn't tolerate, 2 node(s) didn't have free ports for the requested pod ports.
# 清理测试环境
root@master30 ~ 16:28:21# kubectl delete deployments.apps web
deployment.apps "web" deleted
问题总结:
- 集群外部客户端必须预先获知 Pod 所在节点主机地址,才能正常访问 Pod。
- 若使用控制器批量调度 Pod,无法在单节点上部署多个同类 Pod,会触发宿主机端口冲突。
Service 介绍
若通过 Deployment 托管应用,Deployment 会按需动态创建、销毁 Pod。任意时刻,我们都无法确定当前运行的 Pod 数量、健康状态;也无法便捷判断 Pod 是否可用。Kubernetes Pod 属于临时性资源,集群会持续调整 Pod 实例以匹配期望副本数,因此不能将单个 Pod 视作稳定可靠的访问目标。
每个 Pod 都会分配独立 IP(由集群网络插件保障)。针对同一个 Deployment 下的应用,当前在线的 Pod 实例集合会随时发生变化。
由此引出核心痛点:
若一组后端 Pod 为集群内前端 Pod 提供服务,前端应用该如何持续发现、追踪后端 Pod 的 IP 地址,实现流量负载分发?
解决方案:Service
- 在 Kubernetes 中,Service 可将一组 Pod 上运行的网络应用封装为标准化网络服务。Service 拥有固定不变的独立 IP 与访问端口,同时内置负载均衡能力。客户端仅需访问 Service 地址,Kubernetes 会自动维护 Service 与后端 Pod 的映射关系。即便后端 Pod 扩容、缩容、重建,也不会对前端客户端造成任何影响,Service 访问入口始终保持稳定。
- Kubernetes Service 的核心设计目标:无需改造现有业务代码,即可完成服务发现。无论是原生云应用,还是容器化迁移的传统老旧程序,都能借助 Service 实现一组 Pod 的统一网络暴露,供客户端稳定交互。
Service 基本管理
环境准备:创建 Deployment
bash
# 创建 Deployment
root@master30 ~ 16:33:51# kubectl create deployment web --image=hub.laoma.cloud/library/httpd:2.4.58 --replicas=3
deployment.apps/web created
# 查看Pod并展示标签信息
root@master30 ~ 16:34:19# kubectl get pods --show-labels
NAME READY STATUS RESTARTS AGE LABELS
web-d455dd6b-9c676 1/1 Running 0 6s app=web,pod-template-hash=d455dd6b
web-d455dd6b-c2mww 1/1 Running 0 6s app=web,pod-template-hash=d455dd6b
web-d455dd6b-jnzpr 1/1 Running 0 6s app=web,pod-template-hash=d455dd6b
创建 Service
bash
root@master30 ~ 16:34:25# kubectl create service clusterip web --tcp=8080:80
service/web created
root@master30 ~ 16:34:39# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
web ClusterIP 10.107.184.140 <none> 8080/TCP 9s
## 参数--tcp=8080:80含义:访问Service集群IP的8080端口,流量将转发至后端Pod的80端口
## Service 默认自带标签:app=<服务名称>
# 查看Service完整详情
root@master30 ~ 16:34:48# kubectl describe service web
Name: web
Namespace: services
Labels: app=web
Annotations: <none>
Selector: app=web
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.107.184.140
IPs: 10.107.184.140
Port: 8080-80 8080/TCP
TargetPort: 80/TCP
Endpoints: 10.224.133.113:80,10.224.133.114:80,10.224.218.159:80
Session Affinity: None
Events: <none>
root@master30 ~ 16:34:52# kubectl describe service web |grep -e Endpoints -e IP:
IP: 10.107.184.140
Endpoints: 10.224.133.117:80,10.224.218.165:80
# 访问验证:请求Service集群IP的8080端口
root@master30 ~ 16:35:17# curl 10.107.184.140:8080
<html><body><h1>It works!</h1></body></html>
验证 Service 核心能力
修改各 Pod 首页内容,验证负载均衡分发效果。
bash
# 获取所有Pod名称
root@master30 ~ 16:35:21# kubectl get pods -o name | awk -F/ '{print $2}'
web-d455dd6b-9c676
web-d455dd6b-c2mww
web-d455dd6b-jnzpr
# 批量修改每个Pod内的首页文件
root@master30 ~ 16:35:32# \
for pod in $(kubectl get pods -o name | awk -F/ '{print $2}')
do
kubectl exec -it $pod -- bash -c "echo $pod > htdocs/index.html"
done
# 批量访问并统计流量分发次数
root@master30 ~ 16:35:55# for i in {1..60};do curl -s 10.107.184.140:8080;done|sort |uniq -c
18 web-d455dd6b-9c676
16 web-d455dd6b-c2mww
26 web-d455dd6b-jnzpr
新建带有匹配标签的独立 Pod,Service 会自动将流量转发至该实例
bash
root@master30 ~ 16:36:32# kubectl run web --image=hub.laoma.cloud/library/httpd --labels=app=web
pod/web created
root@master30 ~ 16:36:53# for i in {1..60};do curl -s 10.107.184.140:8080;done|sort |uniq -c
18 <html><body><h1>It works!</h1></body></html>
14 web-d455dd6b-9c676
15 web-d455dd6b-c2mww
13 web-d455dd6b-jnzpr
滚动重启 Deployment 后,Service 仍可自动感知后端 Pod 变更,正常转发流量
bash
root@master30 ~ 16:36:56# kubectl rollout restart deployment web
root@master30 ~ 16:36:59# for i in {1..60};do curl -s 10.103.19.150:8080;done|sort |uniq -c
Deployment 与 Service 可使用两套独立标签匹配规则,仅需保证 Service 选择器能匹配 Pod 标签即可。
bash
# 清空现有资源,重新创建Deployment与Service
root@master30 ~ 16:37:09# kubectl delete deployments.apps web --force
Warning: Immediate deletion does not wait for confirmation that the running resource has been terminated. The resource may continue to run on the cluster indefinitely.
deployment.apps "web" force deleted
root@master30 ~ 16:37:27# kubectl delete service web
service "web" deleted
root@master30 ~ 16:37:45# vim deploy-web.yml
bash
apiVersion: apps/v1
kind: Deployment
metadata:
creationTimestamp: null
labels:
app: web
name: web
spec:
replicas: 2
selector:
matchLabels:
app1: web1
strategy: {}
template:
metadata:
creationTimestamp: null
labels:
app1: web1
app2: web2
spec:
containers:
- image: hub.laoma.cloud/library/httpd
name: httpd
imagePullPolicy: IfNotPresent
resources: {}
status: {}
bash
root@master30 ~ 16:38:08# kubectl apply -f deploy-web.yml
deployment.apps/web created
# 通过expose命令创建Service,自定义标签选择器
root@master30 ~ 16:38:18# kubectl expose deployment web --port=8080 --target-port=80 --selector=app2=web2
service/web exposed
# 参数说明:
# --port=8080:定义Service对外监听端口
# --target-port=80:定义后端Pod内应用监听端口
# --selector=app2=web2:定义Service筛选后端Pod的标签规则
root@master30 ~ 16:38:28# kubectl describe svc web |grep -e IP: -e Endpoints
IP: 10.105.141.215
Endpoints: 10.224.133.117:80,10.224.218.165:80
root@master30 ~ 16:38:37# curl 10.105.141.215:8080
<html><body><h1>It works!</h1></body></html>
无法通过 ping 命令连通 Service IP,但可以正常 ping 通 Pod IP;Service 仅针对指定业务端口配置 iptables 转发规则,不开放 ICMP 协议。
通过 YAML 文件创建 Service
bash
root@master30 ~ 16:38:55# kubectl delete svc web
service "web" deleted
# 导出Service资源标准YAML模板
root@master30 ~ 16:39:14# kubectl create service clusterip web --tcp=8080:80 -o yaml --dry-run=client > svc-web.yml
root@master30 ~ 16:39:21# cat svc-web.yml
bash
apiVersion: v1
kind: Service
metadata:
creationTimestamp: null
labels:
app: web
name: web
spec:
ports:
- name: 8080-80
port: 8080
protocol: TCP
targetPort: 80
selector:
app: web
type: ClusterIP
status:
loadBalancer: {}
Service 发现
服务发现指集群内部应用定位并访问目标 Service 的方式。
下面介绍三种主流的集群内服务发现方案:
- 直接通过 Service 集群 IP 访问
- 通过 Pod 内置环境变量访问
- 通过 DNS 域名解析访问
通过 IP 访问 Service
实验准备:mysql + wordpress 业务组合
部署 mysql 资源
bash
root@master30 ~ 16:48:01# kubectl run mysql --image=hub.laoma.cloud/library/mysql \
--image-pull-policy=IfNotPresent \
--env=MYSQL_ROOT_PASSWORD=redhat \
--env=MYSQL_USER=tom \
--env=MYSQL_PASSWORD=redhat \
--env=MYSQL_DATABASE=blog \
--dry-run=client -o yaml > pod-mysql.yaml
root@master30 ~ 16:51:08# kubectl apply -f pod-mysql.yaml
pod/mysql created
root@master30 ~ 16:51:30# kubectl expose pod mysql --port=3306 --target-port=3306
service/mysql exposed
root@master30 ~ 17:14:30# kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
mysql ClusterIP 10.96.50.110 <none> 3306/TCP 9s
root@master30 ~ 17:14:39# apt install -y mysql-client
root@master30 ~ 17:15:07# mysql -u tom -predhat -h 0.96.50.110 --ssl-mode=DISABLED -e 'show databases;'
mysql: [Warning] Using a password on the command line interface can be insecure.
+--------------------+
| Database |
+--------------------+
| information_schema |
| blog |
+--------------------+
部署 WordPress 资源
bash
root@master30 ~ 17:18:00# kubectl run wordpress \
--image=hub.laoma.cloud/library/wordpress \
--image-pull-policy=IfNotPresent \
--env=WORDPRESS_DB_USER=tom \
--env=WORDPRESS_DB_PASSWORD=redhat \
--env=WORDPRESS_DB_NAME=blog \
--env=WORDPRESS_DB_HOST=10.96.50.110
# 为方便外部测试,创建NodePort类型Service暴露WordPress
root@master30 ~ 17:18:37# kubectl expose pod wordpress --port=80 --target-port=80 --type NodePort
service/wordpress exposed
root@master30 ~ 17:18:59# kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
mysql ClusterIP 10.96.50.110 <none> 3306/TCP 4m39s
wordpress NodePort 10.110.158.41 <none> 80:31029/TCP 20s
# 外部访问地址
http://10.1.8.30:31029
通过环境变量访问 Service
查看 Pod 内自动注入的服务环境变量
bash
root@master30 ~ 17:21:01# kubectl run test --rm -it --image=hub.laoma.cloud/library/busybox --image-pull-policy=IfNotPresent sh
If you don't see a command prompt, try pressing enter.
/ # env|grep MYSQL
MYSQL_PORT_3306_TCP_ADDR=10.96.50.110
MYSQL_PORT_3306_TCP_PORT=3306
MYSQL_SERVICE_HOST=10.96.50.110
MYSQL_PORT_3306_TCP_PROTO=tcp
MYSQL_PORT=tcp://10.96.50.110:3306
MYSQL_SERVICE_PORT=3306
MYSQL_PORT_3306_TCP=tcp://10.96.50.110:3306
/ # exit
Session ended, resume using 'kubectl attach test -c test -i -t' command when the pod is running
pod "test" deleted
说明:
-
可通过环境变量
MYSQL_SERVICE_HOST获取 mysql 服务地址。 -
环境变量仅在 Service 创建完成后,新建 Pod 才会自动注入。
-
Service 具备命名空间隔离特性,Pod 仅能读取同一命名空间内 Service 的环境变量。
重新部署 WordPress,使用环境变量关联数据库
bash
# 删除原有WordPress Pod,重新创建
root@master30 ~ 17:21:06# kubectl delete pod wordpress --force
root@master30 ~ 17:21:10# kubectl run wordpress \
--image=hub.laoma.cloud/library/wordpress \
--image-pull-policy=IfNotPresent \
--env=WORDPRESS_DB_USER=tom \
--env=WORDPRESS_DB_PASSWORD=redhat \
--env=WORDPRESS_DB_NAME=blog \
--env=WORDPRESS_DB_HOST='$(MYSQL_SERVICE_HOST)'
# 验证Pod内数据库相关环境变量
root@master30 ~ 17:21:17# kubectl exec -it wordpress -- sh -c 'env|grep MYSQL'
MYSQL_PORT_3306_TCP_ADDR=10.96.50.110
MYSQL_PORT_3306_TCP_PORT=3306
MYSQL_SERVICE_HOST=10.96.50.110
MYSQL_PORT_3306_TCP_PROTO=tcp
MYSQL_PORT=tcp://10.96.50.110:3306
MYSQL_SERVICE_PORT=3306
MYSQL_PORT_3306_TCP=tcp://10.96.50.110:3306
# 外部访问地址
http://10.1.8.30:31029
通过 DNS 名称访问 Service
Kubernetes 提供更便捷的 DNS 域名服务发现能力,使用 kubeadm 搭建集群时会默认部署 coredns 组件。
bash
root@master30 ~ 17:22:44# kubectl get deployments.apps --namespace=kube-system
NAME READY UP-TO-DATE AVAILABLE AGE
calico-kube-controllers 1/1 1 1 5d2h
coredns 2/2 2 2 5d3h
coredns 是集群内置 DNS 服务,每当新建 Service,coredns 会自动添加对应 DNS 解析记录。集群内 Pod 可通过 <服务名>.<命名空间> 格式访问目标 Service。
bash
root@master30 ~ 17:23:01:~# kubectl run busybox --rm -it --image=hub.laoma.cloud/library/busybox /bin/sh
If you don't see a command prompt, try pressing enter.
/ # cat /etc/resolv.conf
nameserver 10.96.50.110
search service.svc.cluster.local svc.cluster.local cluster.local laoma.cloud
options ndots:5
/ # wget wordpress.service:80
Connecting to wordpress.service:80 (10.110.158.41:80)
Connecting to wordpress.service:80 (10.110.158.41:80)
saving to 'index.html'
index.html 100% |***************************************| 11607 0:00:00 ETA
'index.html' saved
当前 Pod 与 wordpress 服务处于同一命名空间,可省略命名空间后缀,直接使用服务名访问 Service。
bash
/ # rm index.html
/ # wget wordpress:80
Connecting to wordpress:80 (10.110.158.41:80)
Connecting to wordpress:80 (10.110.158.41:80)
saving to 'index.html'
index.html 100% |***************************************| 11571 0:00:00 ETA
'index.html' saved
10.96.0.10 即为集群 DNS 服务地址,可通过如下命令查看:
bash
root@master30 ~ 17:24:27# kubectl get service --namespace kube-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 5d3h
沿用上述 WordPress 业务示例,使用 DNS 域名配置数据库连接:
bash
# 删除原有WordPress Pod,重新创建
root@master30 ~ 17:24:30# kubectl delete pod wordpress --force
Warning: Immediate deletion does not wait for confirmation that the running resource has been terminated. The resource may continue to run on the cluster indefinitely.
pod "wordpress" force deleted
root@master30 ~ 17:25:09# kubectl run wordpress \
--image=hub.laoma.cloud/library/wordpress \
--image-pull-policy=IfNotPresent \
--env=WORDPRESS_DB_USER=tom \
--env=WORDPRESS_DB_PASSWORD=redhat \
--env=WORDPRESS_DB_NAME=blog \
--env=WORDPRESS_DB_HOST=mysql
pod/wordpress created
# 外部访问地址
http://10.1.8.30:31029
# 清理全部测试资源
root@master30 ~ 17:25:23# kubectl delete svc mysql wordpress
service "mysql" deleted
service "wordpress" deleted
root@master30 ~ 17:26:12# kubectl delete pod mysql wordpress
pod "mysql" deleted
pod "wordpress" deleted