K8s 控制器全解:Deployment/DaemonSet/Job/CronJob+Service 服务发现实战宝典

Kubernetes Controllers

ReplicationController

学习参考:ReplicationController

ReplicationController ,简称 RC,核心能力与 ReplicaSet 基本一致。ReplicaSet是 RC 的迭代升级版本,原生支持更丰富的基于集合的标签选择算符

本文档不再对ReplicationController做详细展开。

Deployment

学习参考:Deployment

Deployment 介绍

Deployment,简称 deploy,为PodReplicaSet提供声明式应用更新能力。

ReplicaSet 仅负责保障集群中稳定运行指定数量的 Pod 实例;Deployment 则用于托管多组 ReplicaSet,并封装 Pod 滚动更新、版本回滚、扩缩容等上层运维能力。因此官方推荐直接使用 Deployment 管理 ReplicaSet,仅在需要自定义特殊更新流程时才直接操作 ReplicaSet,日常运维几乎无需单独维护 ReplicaSet 资源。

Deployment 用例

以下为 Deployment 的典型业务使用场景:

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 个阶段

  1. 节点上的 kubelet 组件默认每 10 秒向 kube-apiserver 上报一次节点心跳状态。

  2. Master 节点的controller-manager

    每5 秒轮询检查各节点心跳记录;若连续40 秒未收到某节点心跳,则标记该节点状态为NotReady

    对应控制参数:node-monitor-period=5snode-monitor-grace-period=40s。该组参数归属kube-controller-manager组件,该组件以静态 Pod 形式部署在 Master 节点,配置文件路径:/etc/kubernetes/manifests/kube-controller-manager.yaml

    修改方式:编辑该静态 Pod 配置文件,找到 command 配置段,新增或调整--pod-eviction-timeout参数,保存后静态 Pod 会自动重启生效。

  3. 节点持续保持 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 越多,更新过程中业务不可用实例数量会同步上升。

    理想场景下,完整滚动更新流程逻辑如下:

  1. 批量创建 3 个新版本 Pod,此时集群运行中 Pod 总数达到上限 13。

  2. 销毁 2 个旧版本 Pod,同步新建 2 个新版本 Pod;集群可用 Pod 维持 8 个。若此前新建的 3 个新 Pod 未完成就绪变为 Running,则集群内存在 5 个处于 ContainerCreating 状态的新 Pod,总副本数依旧保持上限 13。

  3. 待新建 Pod 全部就绪切换至 Running 状态(示例中 5 个新 Pod 同时就绪)。

  4. 集群运行 Pod 总数达到 13,此时可一次性销毁 5 个旧 Pod,同步创建 5 个新 Pod,保证集群可用副本数不低于 8。

  5. 上述新建、销毁循环持续执行,直至所有旧版本 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目录文件。

实现思路:

  1. 清理命令:rm -fr /var/data/*
  2. 固定调度至 worker31 节点运行:配置 nodeName: worker31
  3. 将宿主机/var/data目录挂载至 Pod 内:使用 hostPath 存储卷
  4. 周期性调度执行:采用 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 集群

实现思路:

  1. 选用内置 kubectl 工具的镜像 bitnami/kubectl。

  2. 将集群管理员 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

问题总结:

  1. 集群外部客户端必须预先获知 Pod 所在节点主机地址,才能正常访问 Pod。
  2. 若使用控制器批量调度 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 的方式。

下面介绍三种主流的集群内服务发现方案:

  1. 直接通过 Service 集群 IP 访问
  2. 通过 Pod 内置环境变量访问
  3. 通过 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

说明:

  1. 可通过环境变量 MYSQL_SERVICE_HOST 获取 mysql 服务地址。

  2. 环境变量仅在 Service 创建完成后,新建 Pod 才会自动注入。

  3. 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
相关推荐
GlueNa2SiO32 小时前
07-Docker Compose多容器编排
笔记·学习·docker·容器
SLD_Allen3 小时前
Kubernetes + Ray + Volcano:云原生AI训练调度体系
人工智能·云原生·kubernetes
GlueNa2SiO33 小时前
01-Docker入门与核心概念
笔记·docker·容器
A-刘晨阳3 小时前
Kubernetes 非共享存储详解:emptyDir 与 hostPath 从入门到实践
运维·云原生·容器·kubernetes·hostpath·emptydir
风曦Kisaki4 小时前
# Kubernetes(K8s)笔记Day15:K8s网络工作原理【Pod 网络模型,CNI 网络插件,Calico 工作模式详解,BGP协议,路由反射器】
运维·网络·nginx·云原生·容器·kubernetes
闲猫4 小时前
docker compose 命令
运维·docker·容器
明王明王4 小时前
从零搭建一个单节点 K8S 可观测实验室(七):从一次 Pod 创建过程,看懂 Kubernetes 核心原理
云原生·容器·kubernetes
云川之下1 天前
【k8s】NAD(NetworkAttachmentDefinition)与SR‑IOV‑Operator 关系
云原生·容器·kubernetes
ZzzZZzzzZZZzzzz…1 天前
K8S实战:NFS+StorageClass+PV/PVC+Deployment
运维·云原生·容器·kubernetes·storageclass·linux运维·持久卷