07.基于LNMP架构部署blog应用和DaemonSet、Job

2026-04-24

复习和预习

昨天课堂内容

  1. secret
  2. rs
  3. deployment

课前复习

configure

  1. K8s中Secret的核心作用是什么?与ConfigMap的核心区别是什么?

  2. K8s中的Secret主要分为哪几种类型?

  3. 创建Secret的方式有哪些?

  4. Pod中引用Secret的方式有哪些?

  5. 如何查看Secret的明文数据?

  6. 删除被Pod引用的ConfigMap/Secret后,Pod会发生什么变化?

  7. 在实际生产中,使用ConfigMap和Secret有哪些最佳实践?(至少列出4点)

controller

  1. K8s中ReplicaSet(RS)的核心作用是什么?

  2. ReplicaSet的核心配置字段有哪些?分别说明作用。

  3. ReplicaSet是否支持Pod的滚动更新?为什么?

  4. 如何调整ReplicaSet的Pod副本数?

  5. K8s中Deployment的核心作用是什么?与ReplicaSet的关系是什么?

  6. Deployment的滚动更新核心参数有哪些?

  7. 如何查看Deployment的历史版本?如何将Deployment回滚到上一个版本?

  8. 如何查看Deployment的滚动更新状态?如何暂停/恢复Deployment的滚动更新?

  9. 使用kubectl命令创建Deployment后,如何确认其管理的Pod、RS是否正常创建?

  10. Deployment管理的Pod标签被手动修改后,Deployment会如何处理?

综合实验

基于LNMP架构部署blog应用。

具体要求如下:

  1. 使用mysql:5.7、php-7.2-fpm-wordpress、nginx镜像和wordpress-4.9.4-zh_CN.zip文件部署blog应用。
  2. 使用pv-pvs方式为mysql和wordpress提供存储。
  3. pv通过nfs提供。
  4. blog 应用通过https访问。
  5. 密码和站点证书用secret存储。
  6. 站点 vhost 配置使用configmap提供。
  7. 以上三个应用都使用deployment方式部署。

提示:php-fpm-wordpress 镜像找老师获取。

今天课堂内容

  1. DaemonSet
  2. Job
  3. CronJob

综合实验

实验要求

基于LNMP架构部署blog应用。

具体要求如下:

  1. 使用mysql:5.7、php-7.2-fpm-wordpress、nginx镜像和wordpress-4.9.4-zh_CN.zip文件部署blog应用。
  2. 使用pv-pvs方式为mysql和wordpress提供存储。
  3. pv通过nfs提供。
  4. blog 应用通过https访问。
  5. 密码和站点证书用secret存储。
  6. 站点 vhost 配置使用configmap提供。

提示:php-fpm-wordpress 镜像找老师获取。

实验步骤

部署 NFS 服务

NFS 服务器操作

bash 复制代码
# 安装 NFS 服务
[root@master30 ~]# apt install -y nfs-kernel-server

# 创建共享目录
[root@master30 ~]# mkdir -p -m 777 /nfsshares/{mysql,blog}

# 配置 NFS 共享(地址已改为10.1.8.30,允许所有节点访问)
[root@master30 ~]# echo "/nfsshares/mysql *(rw,sync,no_root_squash,no_all_squash)
/nfsshares/blog *(rw,sync,no_root_squash,no_all_squash)" >> /etc/exports

# 启动服务
[root@master30 ~]# systemctl restart nfs-server

NFS 客户端操作

bash 复制代码
## 安装 NFS 客户端
[root@worker31-32 ~]# apt install -y nfs-common
 
## 验证共享
[root@worker31-32 ~]# showmount -e master30
Export list for master30:
/nfsshares/blog  *
/nfsshares/mysql *

准备 pv 和 pvc

bash 复制代码
[root@master30 ~]# vim pv-pvc.yaml
bash 复制代码
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-mysql
spec:
  capacity:
    storage: 5Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteOnce
  nfs:
    path: /nfsshares/mysql
    server: 10.1.8.30
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-blog
spec:
  capacity:
    storage: 5Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteOnce
  nfs:
    path: /nfsshares/blog
    server: 10.1.8.30
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-mysql
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-blog
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi
bash 复制代码
[root@master30 ~]# kubectl get pvc
NAME       STATUS   VOLUME   CAPACITY  ACCESS MODES  STORAGECLASS VOLUMEATTRIBUTESCLASS   AGE
pvc-blog   Bound    pv-blog    5Gi        RWO                     <unset>                  9s
pvc-mysql  Bound    pv-mysql   5Gi        RWO                     <unset>                  9s

部署 mysql

bash 复制代码
[root@master30 ~]# kubectl create secret generic mysql --from-literal username=tom --from-literal password_for_user=redhat --from-literal password_for_root=redhat --from-literal db_name=wordpress
[root@master30 certs]# kubectl get secrets mysql
NAME    TYPE     DATA   AGE
mysql   Opaque   4      17s

[root@master30 ~]# vim pod-mysql.yaml
yaml 复制代码
---
apiVersion: v1
kind: Pod
metadata:
  name: mysql
  labels:
    name: mysql
spec:
  containers:
  - image: docker.io/library/mysql:5.7
    imagePullPolicy: IfNotPresent
    name: mysql
    ports:
    - containerPort: 3306
      name: mysql
    env:
    - name: MYSQL_ROOT_PASSWORD
      valueFrom:
        secretKeyRef:
          name: mysql
          key: password_for_root
    - name: MYSQL_USER
      valueFrom:
        secretKeyRef:
          name: mysql
          key: username
    - name: MYSQL_PASSWORD
      valueFrom:
        secretKeyRef:
          name: mysql
          key: password_for_user
    - name: MYSQL_DATABASE
      valueFrom:
        secretKeyRef:
          name: mysql
          key: db_name
    volumeMounts:
    - name: mysql-data
      mountPath: /var/lib/mysql
  volumes:
    - name: mysql-data
      persistentVolumeClaim:
        claimName: pvc-mysql
bash 复制代码
[root@master30 ~]# kubectl apply -f pod-mysql.yaml 
[root@master30 ~]# kubectl get pods
NAME    READY   STATUS    RESTARTS   AGE
mysql   1/1     Running   0          3s

[root@master30 ~]# apt install -y mysql-client
[root@master30 ~]# mysql -utom -predhat -h 10.224.19.1 -e 'show databases;'
mysql: [Warning] Using a password on the command line interface can be insecure.
+--------------------+
| Database           |
+--------------------+
| information_schema |
| wordpress          |
+--------------------+

部署 php-fpm

bash 复制代码
# worker节点导入镜像
[root@worker31 ~]# nerdctl load -i php-7.2-fpm-wordpress.tar 
[root@worker32 ~]# nerdctl load -i php-7.2-fpm-wordpress.tar
# 镜像名称是 docker.io/library/php:7.2-fpm-wordpress

[root@master30 ~]# vim pod-php.yaml
yaml 复制代码
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: php-fpm
  name: php-fpm
spec:
  containers:
  - image: docker.io/library/php:7.2-fpm-wordpress
    imagePullPolicy: IfNotPresent
    name: php-fpm
    volumeMounts:
    # PHP 网站根目录
    - name: blog-data
      mountPath: /usr/share/nginx/html  
  volumes:
  # PHP 网站根目录
  - name: blog-data
    persistentVolumeClaim:
      claimName: pvc-blog
bash 复制代码
[root@master30 ~]# kubectl apply -f pod-php.yaml
[root@master30 ~]# kubectl get pods php-fpm -o jsonpath={.status.podIP}
10.224.19.44

部署 nginx

创建私钥和证书

bash 复制代码
#--1--生成私钥 
[root@master30 ~]# openssl genrsa -out blog.key 2048  

#--2--生成请求文件csr
[root@master30 ~]# openssl req -new -key blog.key -out blog.csr -subj "/C=CN/ST=JS/L=NJ/O=LM/OU=DEVOPS/CN=blog.laoma.cloud/emailAddress=webadmin@laoma.cloud" 
# CN的值必须是网站域名

#--3--使用自己的私钥对请求文件签名,以生成证书 
[root@master30 ~]# openssl x509 -req -days 3650 -in blog.csr -signkey blog.key -out blog.crt

创建 tls 类型secret

bash 复制代码
[root@master30 ~]# kubectl create secret tls tls-blog --cert=./blog.crt --key=./blog.key
[root@master30 ~]# kubectl get secrets tls-blog 
NAME       TYPE                DATA   AGE
tls-blog   kubernetes.io/tls   2      25s

准备虚拟主机配置文件

bash 复制代码
[root@master30 ~]# vim vhost-blog.conf 
bash 复制代码
server {
    listen 80;
    listen 443 ssl;
    server_name blog.laoma.cloud;  # 替换为实际域名

    # TLS 配置
    ssl_certificate /etc/nginx/tls/tls.crt;
    ssl_certificate_key /etc/nginx/tls/tls.key;

    # 网站根目录
    root /usr/share/nginx/html;
    index index.php index.html;

    # PHP 转发配置(PHP IP)
    location ~ \.php$ {
        fastcgi_pass 10.224.19.44:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }

    # 静态资源缓存
    location ~* \.(jpg|jpeg|png|gif|css|js)$ {
        expires 30d;
        add_header Cache-Control "public, max-age=2592000";
    }
}
bash 复制代码
[root@master30 ~]# kubectl create configmap vhost-blog --from-file=vhost-blog.conf=./vhost-blog.conf

准备 pod-nginx

bash 复制代码
[root@master30 ~]# vim pod-nginx.yaml
yaml 复制代码
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: nginx
  name: nginx
spec:
  containers:
  - image: docker.io/library/nginx:latest
    imagePullPolicy: IfNotPresent
    name: nginx
    volumeMounts:
    - name: vhost-blog
      mountPath: /etc/nginx/conf.d/vhost-blog.conf
      subPath: vhost-blog.conf
    - name: tls-blog
      mountPath: /etc/nginx/tls
      readOnly: true
    - name: blog-data
      mountPath: /usr/share/nginx/html
  volumes:
  - name: vhost-blog
    configMap:
      name: vhost-blog
  - name: tls-blog
    secret:
      secretName: tls-blog
  - name: blog-data
    persistentVolumeClaim:
      claimName: pvc-blog
bash 复制代码
[root@master30 ~]# kubectl apply -f pod-nginx.yaml
[root@master30 ~]# kubectl get pod nginx 
NAME    READY   STATUS    RESTARTS   AGE
nginx   1/1     Running   0          11s
[root@master30 ~]# kubectl get pod nginx -o jsonpath={.status.podIP}
10.224.113.173

准备测试页面

bash 复制代码
[root@master30 ~]# echo Hello World From Nginx > /nfsshares/blog/index.html
[root@master30 ~]# echo "<?php phpinfo(); ?>" > /nfsshares/blog/phpinfo.php
[root@master30 ~]# cat <<'EOF' > /nfsshares/blog/test-mysql.php
<?php
// 数据库配置信息 ------ 请改成你自己的
$host = '10.224.19.45';   // 数据库地址,一般都是 localhost
$user = 'tom';        // 数据库用户名
$pass = 'redhat';            // 数据库密码
$dbname = 'wordpress';      // 要连接的数据库名(必须先创建好)

// 创建连接
$conn = mysqli_connect($host, $user, $pass, $dbname);

// 检测连接是否成功
if (!$conn) {
    die("连接失败:" . mysqli_connect_error());
}

// 设置字符集,防止乱码
mysqli_set_charset($conn, 'utf8mb4');

echo "✅ MySQL 数据库连接成功!\n";

// 关闭连接(可选)
mysqli_close($conn);
?>
EOF

测试 Nginx

bash 复制代码
# 配置解析
[root@master30 ~]# echo '10.224.113.173 blog.laoma.cloud' >> /etc/hosts

# http 站点
[root@master30 ~]# curl http://blog.laoma.cloud/index.html
Hello World From Nginx
[root@master30 ~]# curl -s http://blog.laoma.cloud/phpinfo.php |grep -o 'PHP Version 7'
PHP Version 7
[root@master30 ~]# curl http://blog.laoma.cloud/test-mysql.php
✅ MySQL 数据库连接成功!

# https 站点
[root@master30 ~]# curl -k https://blog.laoma.cloud/index.html
Hello World From Nginx
[root@master30 ~]# curl -sk https://blog.laoma.cloud/phpinfo.php |grep -o 'PHP Version 7'
PHP Version 7
[root@master30 ~]# curl -k https://blog.laoma.cloud/test-mysql.php
✅ MySQL 数据库连接成功!

部署 wordpress 代码

yaml 复制代码
## 进入 NFS 服务器(IP:10.1.8.30),下载 Wordpress 源码
[root@master30 ~]# unzip wordpress-4.9.4-zh_CN.zip 
[root@master30 ~]# cp -a wordpress/* /nfsshares/blog/

## 修改配置文件(连接 MySQL)
[root@master30 ~]# cp /nfsshares/blog/wp-config{-sample,}.php 

## 配置数据库信息
[root@master30 ~]# vim /nfsshares/blog/wp-config.php
/** WordPress数据库的名称 */
define('DB_NAME', 'wordpress');

/** MySQL数据库用户名 */
define('DB_USER', 'tom');

/** MySQL数据库密码 */
define('DB_PASSWORD', 'redhat');

/** MySQL主机 */
define('DB_HOST', '10.224.19.45');

访问网站

bash 复制代码
[root@master30 ~]# kubectl port-forward pod/nginx --address 10.1.8.30 80:80 443:443
Forwarding from 10.1.8.30:80 -> 80
Forwarding from 10.1.8.30:443 -> 443

访问 https://blog.laoma.cloud/

Kubernetes Controllers

学习参考:控制器

环境准备

bash 复制代码
[root@master30 ~]# kubectl create ns controllers
[root@master30 ~]# kubectl config set-context --current --namespace controllers

DaemonSet

学习参考:DaemonSet

DaemonSet 介绍

DaemonSet,简写DS,确保全部(或者某些)节点上运行一个 Pod 的副本。 当有新节点加入集群时, 也会在新节点上新增一个 Pod 。 当有节点从集群移除时,移除节点上的 Pod 也会被回收。

DaemonSet 用例

DaemonSet 的一些典型用例:

  • 在每个节点上运行集群守护进程,例如存储守护进程 glusterd 和 ceph。
  • 在每个节点上运行日志收集守护进程, 例如 flunentd或logstash。
  • 在每个节点上运行监控守护进程,例如 Prometheus Node Exporter或 collectd。

DaemonSet 用法

简单的用法:为每种类型的守护进程在所有的节点上都启动一个 DaemonSet。

复杂的用法:为同一种守护进程部署多个 DaemonSet;每个具有不同的标志, 并且对不同硬件类型具有不同的内存、CPU 要求。

DaemonSet 使用

DaemonSet 创建
bash 复制代码
[root@master30 ~]# vim daemonset.yaml
yaml 复制代码
apiVersion: apps/v1
kind: DaemonSet
metadata:
 name: busybox
spec:
 selector:
   matchLabels:
    app: busybox
 template:
  metadata:
   labels:
    app: busybox
  spec:
    containers:
    - name: busybox
      image: docker.io/library/busybox
      imagePullPolicy: IfNotPresent
      command:
      - sleep
      - "36000"
bash 复制代码
[root@master30 ~]# kubectl apply -f daemonset.yaml
DaemonSet 查看
bash 复制代码
[root@master30 ~]# kubectl get pod -o wide 
NAME            READY   STATUS    RESTARTS   AGE    IP              NODE                  NOMINATED NODE   READINESS GATES
busybox-5hfv6   1/1     Running   0          102s   10.224.41.132   worker31.laoma.cloud   <none>           <none>
busybox-jdnsv   1/1     Running   0          102s   10.224.193.66   worker32.laoma.cloud   <none>           <none>

[root@master30 ~]# kubectl get ds
NAME    DESIRED CURRENT READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
busybox 2       2       2       2            2           <none>          118s
DaemonSet 调度
bash 复制代码
# master节点是默认是不可调度节点
[root@master30 ~]# kubectl describe node master.laoma.cloud |grep Taints
Taints:             node-role.kubernetes.io/control-plane:NoSchedule

# 设置master节点可调度
[root@master30 ~]# kubectl taint node master.laoma.cloud node-role.kubernetes.io/control-plane-
[root@master30 ~]# kubectl get ds
NAME      DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
busybox   3         3         3       3            3           <none>          7m43s

# 设置master节点不可调度
[root@master30 ~]# kubectl taint node master.laoma.cloud node-role.kubernetes.io/control-plane:NoSchedule
[root@master30 ~]# kubectl get ds
NAME      DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
busybox   2         2         2       2            2           <none>          8m37s
# pod数量为2个,但是master节点上的busybox pod不会自动删除,也不会计数到这里。
DaemonSet 健壮性
bash 复制代码
# 删除其中一个pod
[root@master30 ~]# kubectl delete pod busybox-5hfv6

# 自动创建新pod
[root@master30 ~]# kubectl get pod -o wide 
NAME            READY   STATUS    RESTARTS   AGE    IP              NODE                  NOMINATED NODE   READINESS GATES
busybox-jdnsv   1/1     Running   0          7m6s   10.224.193.66   worker32.laoma.cloud   <none>           <none>
busybox-wlqqv   1/1     Running   0          8s     10.224.41.133   worker31.laoma.cloud   <none>           <none>
DaemonSet 删除

删除 DaemonSet 时,默认会删除它创建的所有 Pod,使用**--cascade=orphan**选项,将保留DaemonSet 创建 Pod。

bash 复制代码
[root@master master]# kubectl delete daemonsets.apps busybox 
daemonset.apps "busybox" deleted

K8s 集群中 DS

  1. Kubernetes 使用 DaemonSet 控制器运行系统组件,例如kube-proxy、calico-node。
bash 复制代码
[root@master30 ~]# 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   4h12m
kube-proxy    3         3         3       3            3           kubernetes.io/os=linux   4h56m
  1. 分析calico-node 的yaml文件:
yaml 复制代码
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: docker.io/calico/node:v3.28.0

注意: 完整配置文件内容要更复杂一些, 为了方便学习DaemonSet, 这里只保留了最重要的内容。

生产级示例

采集节点日志-Fluent
yaml 复制代码
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
yaml 复制代码
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继续运行任务,直到任务运行完成,也就是pod中任务退出代码为0, Job结束。
  • 在job运行过程中,如果托管pod的节点发生故障,Job pod将被自动重新安排到另一个节点。
  • 删除 Job 的操作会清除所创建的全部 Pod。
  • 挂起 Job 的操作会删除 Job 的所有活跃 Pod,直到 Job 被再次恢复执行。

如果你想按某种排期表(Schedule)运行 Job(单个任务或多个并行任务),请参阅 CronJob

Job 用例

简单的使用场景:

  • 执行数据库清理
  • 备份 Kubernetes 集群

Job 实践

Job 基本管理
bash 复制代码
[root@master30 ~]# 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 ~]# kubectl create job myjob --image=docker.io/library/busybox -- echo hello k8s job!
[root@master30 ~]# 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 ~]# kubectl logs myjob-pdrwv 
hello k8s job!

# 删除 Job 的操作会清除所创建的全部 Pod
# 使用--cascade=orphan选项,可以保留job创建的pod
[root@master30 ~]# kubectl delete jobs.batch myjob 

通过 yaml 文件创建job。

bash 复制代码
[root@master30 ~]# vim job.yaml
yaml 复制代码
apiVersion: batch/v1
kind: Job
metadata:
  name: myjob
spec:
  template:
    metadata:
      name: myjob
    spec:
      containers:
      - name: hello
        image: docker.io/library/busybox
        imagePullPolicy: IfNotPresent
        command: ["echo", "hello k8s job! "]
      restartPolicy: Never
bash 复制代码
[root@master30 ~]# kubectl apply -f job.yaml

**示例2:**计算pi,保留小数点200位。

bash 复制代码
[root@master30 ~]# vim job-pi.yaml
yaml 复制代码
apiVersion: batch/v1
kind: Job
metadata:
  name: pi
spec:
  template:
    spec:
      containers:
      - name: pi
        image: docker.io/library/perl
        imagePullPolicy: IfNotPresent
        command: ["perl",  "-Mbignum=bpi", "-wle", "print bpi(200)"]
      restartPolicy: Never
  backoffLimit: 4
bash 复制代码
[root@master30 ~]# kubectl apply -f job-pi.yaml

# 等几分钟
[root@master30 ~]# kubectl get all
NAME           READY   STATUS      RESTARTS   AGE
pod/pi-2hn9n   0/1     Completed   0          3m13s

NAME           COMPLETIONS   DURATION   AGE
job.batch/pi   1/1           76s        3m13s

[root@master30 ~]# kubectl logs pi-qzxv4 
3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446095505822317253594081284811174502841027019385211055596446229489549303820
restartPolicy

restart 策略只能是:

  • Nerver :只要任务没有完成,就会创建新的 pod,直到 job 完成,所以有可能会产生多个pod。
  • OnFailure :只要任务没有完成,就会重启 pod,直到job完成。

我们做个试验, 修改job.yaml, 故意引入一个错误 :echo 修改为 echoxxx

bash 复制代码
[root@master30 ~]# vim job.yaml
yaml 复制代码
apiVersion: batch/v1
kind: Job
metadata:
  name: myjob
spec:
  template:
    metadata:
      name: myjob
    spec:
      containers:
      - name: hello
        image: docker.io/library/busybox
        imagePullPolicy: IfNotPresent
        command: ["echoxxx", "hello k8s job! "]
      restartPolicy: Never

再次应用,验证。

bash 复制代码
[root@master30 ~]# kubectl apply -f job.yaml
[root@master30 ~]# kubectl get all
NAME              READY   STATUS       RESTARTS   AGE
pod/myjob-6hp5t   0/1     StartError   0          33s
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 ~]# kubectl describe pod myjob-6hp5t
......
Events:
  Type     Reason     Age   From               Message
  ----     ------     ----  ----               -------
  Normal   Scheduled  50s   default-scheduler  Successfully assigned laoma/myjob-6hp5t to worker32.laoma.cloud
  Normal   Pulled     50s   kubelet            Container image "busybox" already present on machine
  Normal   Created    50s   kubelet            Created container hello
  Warning  Failed     50s   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

日志显示没有可执行程序, 符合我们的预期。

问题现象: 为什么会看到这么多失败的Pod?

解释: 当第一个Pod启动时, 容器失败退出, 根据 restartPolicy: Never, 此失败容器不会被重启, 但 Job 默认预期完成的Pod数量是1, 目前COMPLETIONS为0, 不满足, 所以 Job 会创建新的Pod, 直到 COMPLETIONS 为1。 对于我们这个例子, SUCCESSFUL 永远也到不了1, 所以Job 会一直创建新的Pod。 为了终止这个行为, 我们删除Job 。

清理 job

bash 复制代码
[root@master30 ~]# kubectl delete jobs.batch myjob

如果将 restartPolicy 设置为 OnFailure 会怎么样?

yaml 复制代码
apiVersion: batch/v1
kind: Job
metadata:
  name: myjob
spec:
  template:
    metadata:
      name: myjob
    spec:
      containers:
      - name: hello
        image: docker.io/library/busybox
        imagePullPolicy: IfNotPresent
        command: ["echoxxxx", "hello k8s job! "]
      restartPolicy: OnFailure

再次应用,验证。

bash 复制代码
[root@master30 ~]# kubectl apply -f job.yaml
[root@master30 ~]# 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

如果Job执行失败,我们可以指定job执行失败最大次数。

yaml 复制代码
apiVersion: batch/v1
kind: Job
metadata:
  name: myjob
spec:
  backoffLimit: 2
  template:
    metadata:
      name: myjob
    spec:
      containers:
      - name: hello
        image: docker.io/library/busybox
        imagePullPolicy: IfNotPresent
        command: ["echoxx", "hello k8s job! "]
      restartPolicy: Never

测试结果:最多重建2次。

bash 复制代码
[root@master30 ~]# kubectl get all
NAME              READY   STATUS               RESTARTS   AGE
pod/myjob-bdgxq   0/1     StartError   0          30s
pod/myjob-ffth2   0/1     StartError   0          83s
pod/myjob-abcde   0/1     StartError   0          83s

NAME              COMPLETIONS   DURATION   AGE
job.batch/myjob   0/1           83s        83s
completions

我们还可以通过 completions 指定 Pod 执行完成多少次,才算Job执行完成。

yaml 复制代码
apiVersion: batch/v1
kind: Job
metadata:
  name: myjob
spec:
  completions: 2
  template:
    metadata:
      name: myjob
    spec:
      containers:
      - name: hello
        image: docker.io/library/busybox
        imagePullPolicy: IfNotPresent
        command: ["echo", "hello k8s job! "]
      restartPolicy: Never

上面配置的含义是: 每次运行两个Pod, 直到总共有6个Pod成功完成。

bash 复制代码
[root@master30 ~]# kubectl get all
NAME              READY   STATUS      RESTARTS   AGE
pod/myjob-72zgz   0/1     Completed   0          4s
pod/myjob-8brdq   0/1     Completed   0          8s

NAME              COMPLETIONS   DURATION   AGE
job.batch/myjob   2/2           12s        12s

如果不指定completions, 默认值均为1。

parallelism

有时我们希望Job同时运行多个Pod, 提高Job的执行效率,通过parallelism设置并行Pod数量 。

yaml 复制代码
apiVersion: batch/v1
kind: Job
metadata:
  name: myjob
spec:
  completions: 6
  parallelism: 2
  template:
    metadata:
      name: myjob
    spec:
      containers:
      - name: hello
        image: docker.io/library/busybox
        imagePullPolicy: IfNotPresent
        command: ["echo", "hello k8s job! "]
      restartPolicy: Never
bash 复制代码
[root@master30 ~]# kubectl get all
NAME              READY   STATUS      RESTARTS   AGE
pod/myjob-72zgz   0/1     Completed   0          4s
pod/myjob-8brdq   0/1     Completed   0          8s
pod/myjob-8l5cx   0/1     Completed   0          8s
pod/myjob-9gkt8   0/1     Completed   0          12s
pod/myjob-wcwwh   0/1     Completed   0          12s
pod/myjob-xs6pd   0/1     Completed   0          4s

NAME              COMPLETIONS   DURATION   AGE
job.batch/myjob   6/6           12s        12s

效果: 每次运行2个Pod, 直到总共有6个Pod成功完成。

如果不指定parallelism, 默认值均为1。

上面的例子只是为了演示Job的并行特性, 实际用途不大。 不过现实中确实存在很多需要并行处理的场景。 比如批处理程序, 每个副本(Pod) 都会从任务池中读取任务并执行, 副本越多, 执行时间就越短, 效率就越高。这种类似的场景都可以用Job来实现。

activeDeadlineSeconds

一旦 Job 运行时间达到该值,其所有运行中的 Pod 都会被终止 ,并且 Job 的状态更新为 type: Failedreason: DeadlineExceeded。该值适用于 Job 的整个生命期,无论 Job 创建了多少个 Pod。

yaml 复制代码
apiVersion: batch/v1
kind: Job
metadata:
  name: pi-with-timeout
spec:
  backoffLimit: 5
  activeDeadlineSeconds: 10
  template:
    spec:
      containers:
      - name: pi
        image: docker.io/library/perl
        command: ["perl",  "-Mbignum=bpi", "-wle", "print bpi(2000)"]
      restartPolicy: Never

Job 的 .spec.activeDeadlineSeconds 优先级高于其 .spec.backoffLimit 设置。 因此,如果一个 Job 正在重试一个或多个失效的 Pod,该 Job 一旦到达 activeDeadlineSeconds 所设的时限即不再部署额外的 Pod,即使其重试次数还未达到 backoffLimit 所设的限制。

ttlSecondsAfterFinished

通过设置 Job 的 .spec.ttlSecondsAfterFinished 字段,可以让该控制器清理掉已结束的资源

TTL 控制器清理 Job 时,会级联式地删除 Job 对象。 换言之,它会删除所有依赖的对象,包括 Pod 及 Job 本身。 注意,当 Job 被删除时,系统会考虑其生命周期保障,例如其 Finalizers。

例如:

yaml 复制代码
apiVersion: batch/v1
kind: Job
metadata:
  name: pi-with-ttl
spec:
  ttlSecondsAfterFinished: 100
  template:
    spec:
      containers:
      - name: pi
        image: docker.io/library/perl
        command: ["perl",  "-Mbignum=bpi", "-wle", "print bpi(2000)"]
      restartPolicy: Never

Job pi-with-ttl 在结束 100 秒之后,可以成为被自动删除的对象。

如果该字段设置为 0,Job 在结束之后立即成为可被自动删除的对象。 如果该字段没有设置,Job 不会在结束之后被 TTL 控制器自动清除。

注意这种 TTL 机制仍然是一种 Alpha 状态的功能特性,需要配合 TTLAfterFinished 特性门控使用。有关详细信息,可参考 TTL 控制器的文档。

相关推荐
毛骗导演11 小时前
Cladue Code 源码解析-键盘事件与 Vim 模式:parse-keypress 解析状态机
前端·架构
不甘先生11 小时前
Go 包引用架构指南:从 internal 隔离到破解循环依赖的实战手册
架构·golang
胡利光11 小时前
Context Engineering 实战 02|System Prompt 是架构决策,不是写说明书
java·架构·prompt
薛定猫AI11 小时前
【深度解析】Memo 2.5 Pro:面向长程 Agent 工作流的 MoE 大模型架构与实战接入
架构
SamDeepThinking12 小时前
秒杀系统的幂等,只做一层Redis判重远远不够
java·后端·架构
Ribou12 小时前
Kubernetes v1.35.2 基于 Cilium Gateway API 的服务访问架构
架构·kubernetes·gateway
米高梅狮子12 小时前
09.kube-proxy、Ingress和Network Policy
云原生·容器·架构·kubernetes·自动化
剑飞的编程思维12 小时前
传统制造业数字化转型|架构评估核心全维度清单
架构
小程故事多_8012 小时前
DeepSeek-V4技术报告全解读 从架构到Infra的全栈重构之路
人工智能·重构·架构·智能体