华子目录
什么是控制器
官方文档:https://v1-30.docs.kubernetes.io/zh-cn/docs/concepts/workloads/controllers/
控制器也是管理pod的一种手段
自主式pod:pod退出或意外关闭后不会被重新创建控制器管理的Pod:在控制器的生命周期里,始终要维持Pod 的副本数目
Pod控制器是管理pod的中间层,使用Pod控制器之后,只需要告诉Pod控制器,想要多少个什么样的
Pod就可以了,它会创建出满足条件的Pod并确保每一个Pod资源处于用户期望的目标状态。如果Pod资源在运行中出现故障,它会基于指定策略重新编排Pod
当建立控制器后,会把期望值写入etcd,k8s中的apiserver检索etcd中我们保存的期望状态,并对比pod的当前状态,如果出现差异,代码自驱动立即恢复
控制器常用类型
| 控制器名称 | 控制器用途 |
|---|---|
Replication Controller |
比较原始的pod控制器,已经被废弃,由ReplicaSet代替 |
ReplicaSet |
ReplicaSet 确保任何时间都有指定数量的 Pod 副本在运行 |
Deployment |
一个 Deployment 为 Pod 和 ReplicaSet 提供声明式的更新能力 |
DaemonSet |
DaemonSet 确保全指定节点上运行一个 Pod 的副本 |
StatefulSet |
StatefulSet 是用来管理有状态应用的工作负载 API 对象 |
Job |
执行批处理任务,仅执行一次任务,保证任务的一个或多个Pod成功结束 |
CronJob |
Cron Job 创建基于时间调度的 Jobs |
HPA全称Horizontal Pod Autoscaler |
根据资源利用率自动调整service中Pod数量,实现Pod水平自动缩放 |
replicaset控制器

replicaset功能
ReplicaSet是下一代的Replication Controller,官方推荐使用ReplicaSetReplicaSet和Replication Controller的唯一区别是选择器的支持,ReplicaSet支持新的基于集合的选择器需求ReplicaSet确保任何时间都有指定数量的Pod 副本在运行- 虽然
ReplicaSets可以独立使用,但今天它主要被Deployments用作协调 Pod 创建、删除和更新的机制
replicaset参数说明
| 参数名称 | 字段类型 | 参数说明 |
|---|---|---|
spec |
object |
详细定义对象,固定值就写spec |
spec.replicas |
int |
指定维护pod数量 |
spec.selector |
object |
Selector是对pod的标签查询,与pod数量匹配 |
spec.selector.matchLabels |
string |
指定Selector查询标签的名称和值,以key:value方式指定 |
spec.template |
object |
指定对pod的描述信息,比如lab标签,运行容器的信息等 |
spec.template.metadata |
object |
指定pod属性 |
spec.template.metadata.labels |
string |
指定pod标签 |
spec.template.spec |
object |
详细定义对象 |
spec.template.spec.containers |
list |
Spec对象的容器列表定义 |
spec.template.spec.containers.name |
string |
指定容器名称 |
spec.template.spec.containers.image |
string |
指定容器镜像 |
replicaset示例
bash
[root@k8s-master ~]# kubectl create deployment replicaset --image myapp:v1 --dry-run=client -o yaml > replicaset.yml
[root@k8s-master ~]# vim replicaset.yml
[root@k8s-master ~]# cat replicaset.yml
apiVersion: apps/v1
kind: ReplicaSet #ReplicaSet控制器
metadata:
labels:
app: myapp #标签为app=myapp
name: replicaset #指定ReplicaSet控制器名称,一定小写,如果出现大写报错
spec:
replicas: 2 #指定维护pod数量为2
selector: #指定检测匹配方式
matchLabels: #指定匹配方式为标签匹配
app: myapp #指定匹配的标签为app=myapp
template: #模板:当pod数量不足时,会根据下面的模板创建pod副本
metadata:
labels:
app: myapp
spec:
containers:
- image: myapp:v1
name: myapp
[root@k8s-master ~]# kubectl apply -f replicaset.yml
replicaset.apps/replicaset created
[root@k8s-master ~]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
replicaset-82kts 1/1 Running 0 12s 10.244.2.32 k8s-node2.org <none> <none>
replicaset-vn55z 1/1 Running 0 12s 10.244.1.23 k8s-node1.org <none> <none>
[root@k8s-master ~]# kubectl get pods --show-labels
NAME READY STATUS RESTARTS AGE LABELS
replicaset-82kts 1/1 Running 0 60s app=myapp
replicaset-vn55z 1/1 Running 0 60s app=myapp
replicaset是通过标签匹配pod的
bash
[root@k8s-master ~]# kubectl label pods replicaset-82kts app=huazi --overwrite
pod/replicaset-82kts labeled
[root@k8s-master ~]# kubectl get pods --show-labels
NAME READY STATUS RESTARTS AGE LABELS
replicaset-82kts 1/1 Running 0 3m43s app=huazi
replicaset-clsjq 1/1 Running 0 9s app=myapp #重新开启的pod
replicaset-vn55z 1/1 Running 0 3m43s app=myapp
恢复标签后,发现新创建的pod没了
bash
[root@k8s-master ~]# kubectl label pods replicaset-82kts app=myapp --overwrite
pod/replicaset-82kts labeled
[root@k8s-master ~]# kubectl get pods --show-labels
NAME READY STATUS RESTARTS AGE LABELS
replicaset-82kts 1/1 Running 0 7m2s app=myapp
replicaset-vn55z 1/1 Running 0 7m2s app=myapp
replicaset自动控制pod数量
deployment控制器
deployment控制器的功能

- 为了更好的解决服务编排的问题,
kuberenetes在v1.2版本开始,引入了Deployment控制器。 Deployment控制器并不直接管理pod,而是通过管理ReplicaSet来间接管理podDeployment管理ReplicaSet,ReplicaSet管理podDeployment为Pod和ReplicaSet提供了一个申明式的定义方法- 在
Deployment中ReplicaSet相当于一个版本
典型的应用场景
- 用来创建
Pod和ReplicaSet 滚动更新和回滚扩容和缩容暂停与恢复
Deployment控制器示例
bash
[root@k8s-master ~]# mkdir deployment
[root@k8s-master ~]# cd deployment/
[root@k8s-master deployment]# kubectl create deployment deployment --image myapp:v1 --dry-run=client -o yaml > deployment.yml
[root@k8s-master deployment]# ls
deployment.yml
[root@k8s-master deployment]# vim deployment.yml
[root@k8s-master deployment]# cat deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: myapp #便签app=myapp
name: deployment #指定Deployment控制器名称,一定小写,如果出现大写报错
spec:
replicas: 4 #控制器中pod数量为4个
selector:
matchLabels:
app: myapp
template: #模板:当pod数量不足时,会根据下面的模板创建pod副本
metadata:
labels:
app: myapp
spec:
containers:
- image: myapp:v1
name: myapp
[root@k8s-master deployment]# kubectl apply -f deployment.yml
deployment.apps/deployment created
[root@k8s-master deployment]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
deployment-5d886954d4-2j8jc 1/1 Running 0 13s 10.244.1.5 k8s-node1.org <none> <none>
deployment-5d886954d4-8dpr7 1/1 Running 0 13s 10.244.1.4 k8s-node1.org <none> <none>
deployment-5d886954d4-9zn5b 1/1 Running 0 13s 10.244.2.7 k8s-node2.org <none> <none>
deployment-5d886954d4-zcnxc 1/1 Running 0 13s 10.244.2.6 k8s-node2.org <none> <none>
[root@k8s-master deployment]# kubectl get pods --show-labels
NAME READY STATUS RESTARTS AGE LABELS
deployment-5d886954d4-2j8jc 1/1 Running 0 65s app=myapp,pod-template-hash=5d886954d4
deployment-5d886954d4-8dpr7 1/1 Running 0 65s app=myapp,pod-template-hash=5d886954d4
deployment-5d886954d4-9zn5b 1/1 Running 0 65s app=myapp,pod-template-hash=5d886954d4
deployment-5d886954d4-zcnxc 1/1 Running 0 65s app=myapp,pod-template-hash=5d886954d4
1.版本迭代
bash
[root@k8s-master deployment]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
deployment-5d886954d4-2j8jc 1/1 Running 0 2m1s 10.244.1.5 k8s-node1.org <none> <none>
deployment-5d886954d4-8dpr7 1/1 Running 0 2m1s 10.244.1.4 k8s-node1.org <none> <none>
deployment-5d886954d4-9zn5b 1/1 Running 0 2m1s 10.244.2.7 k8s-node2.org <none> <none>
deployment-5d886954d4-zcnxc 1/1 Running 0 2m1s 10.244.2.6 k8s-node2.org <none> <none>
#访问第一个pod中的ip,我们发现是v1版本
[root@k8s-master deployment]# curl 10.244.1.5
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
[root@k8s-master deployment]# kubectl describe deployments.apps deployment
Name: deployment
Namespace: default #命名空间
CreationTimestamp: Tue, 15 Oct 2024 10:53:21 -0400
Labels: app=myapp
Annotations: deployment.kubernetes.io/revision: 1
Selector: app=myapp
Replicas: 4 desired | 4 updated | 4 total | 4 available | 0 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 25% max unavailable, 25% max surge ##默认每次更新25%
Pod Template:
Labels: app=myapp
Containers:
myapp:
Image: myapp:v1
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: deployment-5d886954d4 (4/4 replicas created)
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ScalingReplicaSet 3m21s deployment-controller Scaled up replica set deployment-5d886954d4 to 4
[root@k8s-master deployment]# vim deployment.yml

bash
#我们发现,每次apply后,pod的ip都会变
[root@k8s-master deployment]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
deployment-7f4786db9c-5mxc2 1/1 Running 0 26s 10.244.1.7 k8s-node1.org <none> <none>
deployment-7f4786db9c-hbfmh 1/1 Running 0 27s 10.244.1.6 k8s-node1.org <none> <none>
deployment-7f4786db9c-pswjb 1/1 Running 0 27s 10.244.2.8 k8s-node2.org <none> <none>
deployment-7f4786db9c-xpqlf 1/1 Running 0 26s 10.244.2.9 k8s-node2.org <none> <none>
#测试:发现版本成为了V2
[root@k8s-master deployment]# curl 10.244.1.7
Hello MyApp | Version: v2 | <a href="hostname.html">Pod Name</a>
- 注意:
更新的过程是重新建立一个版本的RS,新版本的RS会把pod重建,然后把老版本的RS回收
2.版本回滚
bash
[root@k8s-master deployment]# vim deployment.yml

bash
[root@k8s-master deployment]# kubectl apply -f deployment.yml
deployment.apps/deployment configured
#我们发现,每次apply后,pod的ip都会变
[root@k8s-master deployment]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
deployment-5d886954d4-f4wsl 1/1 Running 0 6s 10.244.2.10 k8s-node2.org <none> <none>
deployment-5d886954d4-j8qkt 1/1 Running 0 6s 10.244.1.8 k8s-node1.org <none> <none>
deployment-5d886954d4-n5v9v 1/1 Running 0 5s 10.244.1.9 k8s-node1.org <none> <none>
deployment-5d886954d4-vj64d 1/1 Running 0 5s 10.244.2.11 k8s-node2.org <none> <none>
#测试
[root@k8s-master deployment]# curl 10.244.2.10
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
3.滚动更新策略
bash
[root@k8s-master deployment]# vim deployment.yml
[root@k8s-master deployment]# cat deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: myapp
name: deployment
spec:
minReadySeconds: 5 #最小就绪时间,指定pod每隔多久更新一次
replicas: 4
strategy: #指定更新策略
rollingUpdate:
maxSurge: 1 #比定义pod数量多几个
maxUnavailable: 0 #比定义pod个数少几个
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- image: myapp:v1
name: myapp
[root@k8s-master deployment]# kubectl apply -f deployment.yml
deployment.apps/deployment created
[root@k8s-master deployment]# kubectl get all
NAME READY STATUS RESTARTS AGE
pod/deployment-5d886954d4-67kjg 1/1 Running 0 43s
pod/deployment-5d886954d4-crrwc 1/1 Running 0 43s
pod/deployment-5d886954d4-ksfnh 1/1 Running 0 43s
pod/deployment-5d886954d4-nql72 1/1 Running 0 43s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/huazi ClusterIP 10.97.207.164 <none> 8080/TCP 6d22h
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 11d
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/deployment 4/4 4 4 43s
NAME DESIRED CURRENT READY AGE
replicaset.apps/deployment-5d886954d4 4 4 4 43s
4.暂停及恢复
- 在
实际生产环境中我们做的变更可能不止一处,当修改了一处后,如果执行变更就直接触发了 - 我们
期望是当我们把所有修改都搞定后,一次触发
bash
#先暂停
[root@k8s-master deployment]# kubectl rollout pause deployment deployment
deployment.apps/deployment paused
[root@k8s-master deployment]# vim deployment.yml

bash
[root@k8s-master deployment]# kubectl apply -f deployment.yml
deployment.apps/deployment configured
[root@k8s-master deployment]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
deployment-5d886954d4-4wz8z 1/1 Running 0 9m10s 10.244.2.14 k8s-node2.org <none> <none>
deployment-5d886954d4-67kjg 1/1 Running 0 18m 10.244.2.13 k8s-node2.org <none> <none>
deployment-5d886954d4-crrwc 1/1 Running 0 18m 10.244.1.10 k8s-node1.org <none> <none>
deployment-5d886954d4-fz7t4 1/1 Running 0 9m10s 10.244.1.12 k8s-node1.org <none> <none>
deployment-5d886954d4-ksfnh 1/1 Running 0 18m 10.244.1.11 k8s-node1.org <none> <none>
deployment-5d886954d4-nql72 1/1 Running 0 18m 10.244.2.12 k8s-node2.org <none> <none>
bash
#发现版本并没有被更新
[root@k8s-master deployment]# curl 10.244.2.14
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
bash
#也没有发现资源限制
[root@k8s-master deployment]# kubectl describe pods deployment-5d886954d4-4wz8z
Name: deployment-5d886954d4-4wz8z
Namespace: default
Priority: 0
Service Account: default
Node: k8s-node2.org/172.25.254.20
Start Time: Tue, 15 Oct 2024 11:35:29 -0400
Labels: app=myapp
pod-template-hash=5d886954d4
Annotations: <none>
Status: Running
IP: 10.244.2.14
IPs:
IP: 10.244.2.14
Controlled By: ReplicaSet/deployment-5d886954d4
Containers:
myapp:
Container ID: docker://9430d7c64e16a356882389763c74913dfe9081fb6c8534fdc760f39bb7d8fe8b
Image: myapp:v1
Image ID: docker-pullable://myapp@sha256:9eeca44ba2d410e54fccc54cbe9c021802aa8b9836a0bcf3d3229354e4c8870e
Port: <none>
Host Port: <none>
State: Running
Started: Tue, 15 Oct 2024 11:35:29 -0400
Ready: True
Restart Count: 0
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-6qzsv (ro)
Conditions:
Type Status
PodReadyToStartContainers True
Initialized True
Ready True
ContainersReady True
PodScheduled True
Volumes:
kube-api-access-6qzsv:
Type: Projected (a volume that contains injected data from multiple sources)
TokenExpirationSeconds: 3607
ConfigMapName: kube-root-ca.crt
ConfigMapOptional: <nil>
DownwardAPI: true
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 12m default-scheduler Successfully assigned default/deployment-5d886954d4-4wz8z to k8s-node2.org
Normal Pulled 12m kubelet Container image "myapp:v1" already present on machine
Normal Created 12m kubelet Created container myapp
Normal Started 12m kubelet Started container myapp
bash
[root@k8s-master deployment]# kubectl rollout history deployment deployment
deployment.apps/deployment
REVISION CHANGE-CAUSE
1 <none>
#解除暂停
[root@k8s-master deployment]# kubectl rollout resume deployment deployment
deployment.apps/deployment resumed
[root@k8s-master deployment]# kubectl rollout history deployment deployment
deployment.apps/deployment
REVISION CHANGE-CAUSE
1 <none>
2 <none>
bash
#发现pod中的ip也变了
[root@k8s-master deployment]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
deployment-79fcdf95d5-2xh8f 1/1 Running 0 43s 10.244.1.15 k8s-node1.org <none> <none>
deployment-79fcdf95d5-7s79k 1/1 Running 0 62s 10.244.2.15 k8s-node2.org <none> <none>
deployment-79fcdf95d5-b8459 1/1 Running 0 55s 10.244.1.14 k8s-node1.org <none> <none>
deployment-79fcdf95d5-c7dsd 1/1 Running 0 37s 10.244.2.17 k8s-node2.org <none> <none>
deployment-79fcdf95d5-cncld 1/1 Running 0 49s 10.244.2.16 k8s-node2.org <none> <none>
deployment-79fcdf95d5-jcndp 1/1 Running 0 67s 10.244.1.13 k8s-node1.org <none> <none>
#发现版本更新为V2
[root@k8s-master deployment]# curl 10.244.1.15
Hello MyApp | Version: v2 | <a href="hostname.html">Pod Name</a>
bash
#也发现了资源限制
[root@k8s-master deployment]# kubectl describe pods deployment-79fcdf95d5-2xh8f
Name: deployment-79fcdf95d5-2xh8f
Namespace: default
Priority: 0
Service Account: default
Node: k8s-node1.org/172.25.254.10
Start Time: Tue, 15 Oct 2024 11:50:08 -0400
Labels: app=myapp
pod-template-hash=79fcdf95d5
Annotations: <none>
Status: Running
IP: 10.244.1.15
IPs:
IP: 10.244.1.15
Controlled By: ReplicaSet/deployment-79fcdf95d5
Containers:
myapp:
Container ID: docker://54b5f21974e014328d77236a2b57b84afd66afff0a158768eb6928f513f9ba04
Image: myapp:v2
Image ID: docker-pullable://myapp@sha256:5f4afc8302ade316fc47c99ee1d41f8ba94dbe7e3e7747dd87215a15429b9102
Port: <none>
Host Port: <none>
State: Running
Started: Tue, 15 Oct 2024 11:50:08 -0400
Ready: True
Restart Count: 0
Limits:
cpu: 500m
memory: 200Mi
Requests:
cpu: 500m
memory: 200Mi
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-gb8n4 (ro)
Conditions:
Type Status
PodReadyToStartContainers True
Initialized True
Ready True
ContainersReady True
PodScheduled True
Volumes:
kube-api-access-gb8n4:
Type: Projected (a volume that contains injected data from multiple sources)
TokenExpirationSeconds: 3607
ConfigMapName: kube-root-ca.crt
ConfigMapOptional: <nil>
DownwardAPI: true
QoS Class: Guaranteed
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 2m24s default-scheduler Successfully assigned default/deployment-79fcdf95d5-2xh8f to k8s-node1.org
Normal Pulled 2m24s kubelet Container image "myapp:v2" already present on machine
Normal Created 2m24s kubelet Created container myapp
Normal Started 2m24s kubelet Started container myapp
回收
bash
[root@k8s-master deployment]# kubectl delete -f deployment.yml
deployment.apps "deployment" deleted
daemonset控制器
daemonset功能

DaemonSet确保全部(或者某些)节点上运行一个Pod。当有节点加入集群时, 也会为他们新增一个Pod ,当有节点从集群移除时,这些 Pod也被回收。删除 DaemonSet 将会删除它创建的所有Pod
daemonset的典型用法
- 在
每个节点上运行集群存储DaemonSet,例如glusterd、ceph。 - 在
每个节点上运行日志收集DaemonSet,例如fluentd、logstash。 - 在
每个节点上运行监控DaemonSet,例如Prometheus Node Exporter、zabbix agent等 - 一个
简单的用法是在所有的节点上都启动一个DaemonSet,将被作为每种类型的daemon使用 - 一个
稍微复杂的用法是单独对每种daemon类型使用多个DaemonSet,但具有不同的标志, 并且对不同硬件类型具有不同的内存、CPU要求
daemonset示例
- 由于
daemonset不能create,所以这里我们进行手写
bash
[root@k8s-master ~]# mkdir daemonset
[root@k8s-master ~]# cd daemonset/
bash
[root@k8s-master daemonset]# vim daemonset.yml
[root@k8s-master daemonset]# cat daemonset.yml
apiVersion: apps/v1
kind: DaemonSet
metadata:
labels:
app: myapp
name: daemonset
spec:
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
tolerations: #设置对污点的容忍
- effect: NoSchedule
operator: Exists
containers:
- image: myapp:v1
name: myapp
[root@k8s-master daemonset]# kubectl apply -f daemonset.yml
daemonset.apps/daemonset created
#发现master上也可以运行pod
[root@k8s-master daemonset]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
daemonset-9qznl 1/1 Running 0 13s 10.244.2.18 k8s-node2.org <none> <none>
daemonset-h7ggv 0/1 ContainerCreating 0 13s <none> k8s-master.org <none> <none>
daemonset-m78l6 1/1 Running 0 13s 10.244.1.16 k8s-node1.org <none> <none>
- 回收
bash
[root@k8s-master daemonset]# kubectl delete -f daemonset.yml
daemonset.apps "daemonset" deleted
job控制器
job控制器功能

job,主要用于负责批量处理(一次要处理指定数量任务)短暂的一次性(每个任务仅运行一次就结束)任务
job的特点
- 当
job创建的pod执行成功结束时,job将记录成功结束的pod数量 - 当
成功结束的pod达到指定的数量时,job将完成执行
job控制器示例
bash
[root@k8s-master ~]# mkdir job
[root@k8s-master ~]# cd job/
bash
cronjob控制器
cronjob控制器功能

CronJob创建基于时间调度的JobsCronJob控制器以Job控制器资源为其管控对象,并借助它管理pod资源对象CronJob可以以类似于Linux操作系统的周期性任务作业计划的方式控制其运行时间点及重复运行的方式。CronJob可以在特定的时间点(反复的)去运行job任务
cronjob控制器示例
bash