关于 Kubernetes 系列其他文章的传送门
《 部署 Kubernetes 也能如此简单吗? 》
《 初识 k8s,新时代的宠儿 》
《 深度学习 Pod 》
《 学会这 5 种 Pod 控制器,搞定发布、更新、回滚,从此告别人肉运维!! 》 您当前所在位置
《 k8s 中的网络入口 – Service 资源管理 》
《 k8s 中的 7 层调度 – Ingress 》
《 k8s 中的存储系统 – Volumes 》
《 k8s 中强大的配置中心 – ConfigMap 》
《 k8s 中的部署神器 – Statefulset 控制器 》
《 k8s 中的安全守护神 – 你不知道不代表它不在 》
《 k8s 中的图形接口 – Dashboard 》
《 k8s 中的网络插件 – flannel 和 Calico 》
《 k8s 中的调度器 – 亲和性、污点和容忍性 》
《 k8s 中对资源的监控 – 资源指标、Prometheus、弹性伸缩器 HPA 》
《 k8s 中的云原生应用管理利器 – Helm 》
《 理解 k8s 高可用,让你的集群稳如泰山 》
一、关于 k8s 控制器
Controllers 的工作任务就是通过内部的和解循环,来确保一个对象存在且吻合于用户所期望的状态。
k8s 内键了 4、50 种 Controller,为了统一管理,都被打包在 Kubernetes-controller-manager 中,这是 k8s 控制平面中核心的守护进程。
Kubernetes-controller-manager 在启动 k8s 时可以使用 --controllers= 参数来控制要加载哪些控制器,可用参数有:
*:启用所有控制器,除了如下两个;
bootstrapsigner 和 tokencleaner:默认不加载的控制器;
-NAME:可使用 - 加上名字指定不加载哪个控制器;
而如果使用 kubeadm 部署的 k8s,controller-manager 会被运行为一个名为 kube-controller-manager-master.monster.com 的 Pod。
可以在 controller-manager 的配置文件 ↓ 中看到:
/etc/kubernetes/manifests/kebe-controller-manager.yaml
默认使用 --controllers=*,bootstrapsigner,tokencleaner 参数把所有的控制器都加载。
此配置文件修改后,不需要人工介入,k8s 会在很短的时间内把旧的 Pod 干掉,按照新的配置文件重建一个 Pod。
二、Pod 控制器的类型
控制器按照应用程序可划分为多种类型:
守护进程型:
无状态:
非系统级:Deployment 、ReplicaSet ;如 Nginx、Apache;
系统级:DaemonSet ;需要在每个 node 节点上都运行一份,但只能运行一份的程序,如 ZABBIX;
有状态:StatefulSet ;
非守护进程:
Job :专门控制作业的控制器;比如备份一次;
CronJob :如果作业需要周期性运行则使用此控制器;比如每周备份一次;
1. ReplicaSet 控制器
ReplicaSet 是专门运行无状态类型应用的 Pod 控制器。用于确保在任何时间,必须有精确数量的 Pod 副本处于运行状态。而且所有 Pod 被无区别对待,没有任何个性,任何一个 Pod 出现故障,都可以被其他新的 Pod 取代。
控制器通过标签选择器来过滤出 Pod,并核对数量和预期的数量是否一致。如果不一致,则根据创建 Pod 时使用的模板来创建。
控制器创建 Pod 时,会根据 控制器模板 创建,并附加一个随机的名字,但容器内部所使用的镜像和配置等应该都是相近的。
使用 kubectl get rs 可以查看当前运行的属于 ReplicaSet 类型的 Pod。
查看 ReplicaSet 的 API 参数:
1 ~]# kubectl explain rs.spec
replicas:期望的 Pod 副本数;
selector:标签选择器;
matchLabels:可以直接指明标签来做匹配;可以指定多个,当指定多个时为“与”关系;
matchExpressions:使用表达式;
key:指定要匹配的 Key;
operator:指定匹配时的操作符;
values:指定匹配值;
template:Pod 模板;
metadata:定义元数据,一般不需要,否则会导致重名;
spec:定义 Pod 的 spec;
示例:创建 ReplicaSet 类型 Pod 控制器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 ~]# vim rs-example.yaml apiVersion: apps/v1 kind: ReplicaSet metadata: name: myapp-rs namespace: default spec: replicas: 2 selector: matchLabels: app: myapp-pod template: metadata: labels: app: myapp-pod spec: containers: - name: myapp image: ikubernetes/myapp:v1 ports: - name: http containerPort: 80
启用 Pod 控制器:
1 ~]# kubectl apply -f rs-example.yaml
查看刚才创建的 rs 类型的 Pod 控制器:
1 2 3 ~]# kubectl get rs NAME DESIRED CURRENT READY AGE myapp-rs 2 2 2 23s
也可以查看控制器的详细信息:
1 ~]# kubectl describe rs myapp-rs
注意:控制器只根据标签进行 Pod 的编排,一定要精心规划,确保标签不要重复。另外在定义标签选择器时建议使用更苛刻一些的匹配条件,来帮助精准匹配。
更改 Pod 控制器下的运行的 Pod 副本数:
1 kubectl scale --replicas=3 rs myapp-rs -n default
2. Deployment 控制器
发布
如果想对程序进行版本升级,使用 ReplicaSet 控制器不能自由的控制升级方式,而且还需要手动去进行。
而 k8s 提供的 Deployment 控制器,则可以自由定义升级方式。它通过控制 ReplicaSets,让 ReplicaSets 去控制 Pods 的方式,来自动完成事先定义好的发布规则,比如灰度发布、蓝绿发布甚至是金丝雀发布等。
Deployment 控制器以配置文件内 rs 模板内容的 hash 值来判断是否属于同一版本;
回滚
Deployment 保留着多个过去的 rs,如果发布后出现 BUG 需要回滚,还可以回滚。当前的 k8s 版本默认保留着所有的历史 rs。
创建 Deployment 类型的 Pod 控制器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 ~]# vim deploy-nginx.yaml apiVersion: apps/v1 kind: Deployment metadata: name: deploy-nginx namespace: default spec: replicas: 3 minReadySeconds: 10 strategy: rollingUpdate: maxSurge: 1 maxUnavailable: 1 type: RollingUpdate selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.10-alpine ports: - containerPort: 80 name: http readinessProbe: periodSeconds: 1 httpGet: path: / port: http
启用 Pod 控制器:
1 ~]# kubectl apply -f deploy-nginx.yaml
查看刚刚创建的 Pod 控制器:
1 2 3 ~]# kubectl get deploy NAME READY UP-TO-DATE AVAILABLE AGE deploy-nginx 3/3 3 3 78s
这个 deploy 类型的控制器会自动创建 rs 类型的控制器,名字的最后一段的是 Pod 模板的 hash 值:
1 2 3 ~]# kubectl get rs NAME DESIRED CURRENT READY AGE deploy-nginx-5745bb45d7 3 3 3 2m30s
这个 rs 类型的控制器会去创建 Pod,由这个 rs 管理:
1 2 3 4 5 ~]# kubectl get pods NAME READY STATUS RESTARTS AGE deploy-nginx-5745bb45d7-dszvv 1/1 Running 0 6m35s deploy-nginx-5745bb45d7-jshmw 1/1 Running 0 6m35s deploy-nginx-5745bb45d7-pk7zm 1/1 Running 0 6m35s
发布、更新
定义更新策略的参数:
strategy:定义更新策略:
rollingUpdate:定义滚动升级时,副本数量的上下限,可以使用百分比定义;
maxSurge:滚动更新的时候,可以超过用户预期的副本数量,此值用来定义可以超过几个;
maxUnavailable:定义更新过程中,可以比预期的副本数量少几个;
type:定义更新类型:
rollingUpdate(默认):滚动升级;
recreate:重新创建。
现在编辑刚才的配置文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 ~]# vim deploy-nginx.yaml apiVersion: apps/v1 kind: Deployment metadata: name: deploy-nginx namespace: default spec: replicas: 3 minReadySeconds: 10 strategy: rollingUpdate: maxSurge: 1 maxUnavailable: 1 type: RollingUpdate selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.11.3-alpine ports: - containerPort: 80 name: http readinessProbe: periodSeconds: 1 httpGet: path: / port: http
如果只修改模板外的参数,比如副本数增加,k8s 只会把缺少的 Pod 补上,而不会动已有的 Pod。而像这样修改模板内部的参数,模板的 hash 值就会变化,那么 k8s 才会更新所有的 Pod;
启用 Pod 控制器,完成版本的发布:
1 ~]# kubectl apply -f deploy-nginx.yaml --record
注意:上面创建时使用了 --record 参数,这样可以把这次创建时的命令记录下来,使用如下操作时,就能看见创建控制器时使用的命令了 :
1 2 3 4 5 ~]# kubectl rollout history deployment/deploy-nginx deployment.apps/deploy-nginx REVISION CHANGE-CAUSE 4 <none> 5 kubectl apply --filename=deploy-nginx.yaml --record=true
使用以下命令可以看到控制器创建 Pod 的过程:加一个新的,减一个旧的以此循环:
查看 Pod,看名字中的 hash 值已经和之前的不一样了,现在运行的 3 个 Pod 已经是新的 Pod 了:
1 2 3 4 5 ~]# kubectl get pods NAME READY STATUS RESTARTS AGE deploy-nginx-57bffc5ddc-mb69v 1/1 Running 0 2m47s deploy-nginx-57bffc5ddc-nrjfg 1/1 Running 0 2m47s deploy-nginx-57bffc5ddc-ttn8r 1/1 Running 0 2m11s
查看 rs 可以看到,旧版本的 rs 也保留着,只是不再控制任何 Pod 了。在 Deployment 内保留着所有过去的 rs,这样所有的发布版本就被保留了下来,需要回滚的时候只需要把旧的 rs 激活即可:
1 2 3 4 ~]# kubectl get rs NAME DESIRED CURRENT READY AGE CONTAINERS IMAGES SELECTOR deploy-nginx-5745bb45d7 0 0 0 12m nginx nginx:1.10-alpine app=nginx,pod-template-hash=5745bb45d7 deploy-nginx-57bffc5ddc 3 3 3 8m34s nginx nginx:1.11.3-alpine app=nginx,pod-template-hash=57bffc5ddc
另一种更新方法
直接更改指定控制器中指定容器的镜像版本:
1 2 # ↓ 控制器类型 # ↓ 控制器名 # ↓ 容器的镜像名 kubectl set image deployment deploy-nginx nginx=nginx:1.11.3-alpine
回滚之前的更新操作
查看滚动更新历史:
1 2 3 4 5 ~]# kubectl rollout history deployment/deploy-nginx deployment.apps/deploy-nginx REVISION CHANGE-CAUSE 3 <none> 4 <none>
回滚到指定的一条:
--to-revision=0:可用此参数指定要回滚到哪个 ID,默认为 0 代表回滚至前一个;
1 2 ~]# kubectl rollout undo deployment/deploy-nginx deployment.apps/deploy-nginx rolled back
回滚过程中可以使用如下命令查看实时状态:
1 ~]# kubectl rollout status deployment/deploy-nginx
金丝雀发布
在发布过程中暂停的参数:
刚开始发布就暂停:
1 2 3 ~]# kubectl set image deployment deploy-nginx nginx=nginx:1.11.3-alpine && kubectl rollout pause deployment/deploy-nginx deployment.apps/deploy-nginx image updated deployment.apps/deploy-nginx paused
可以看到有两个新的,两个旧的:
1 2 3 4 5 6 ~]# kubectl get pods NAME READY STATUS RESTARTS AGE deploy-nginx-57bffc5ddc-848xs 1/1 Running 0 50s deploy-nginx-57bffc5ddc-b5kb2 1/1 Running 0 50s deploy-nginx-7d6b7b855-ckz6l 1/1 Running 0 2m16s deploy-nginx-7d6b7b855-t4fmh 1/1 Running 0 2m16s
接下来如果出现问题,直接回滚就可以;如果没问题,就可以继续发布了:
1 kubectl rollout resume deployment/deploy-nginx
3. DaemonSet 控制器
用来在每个计算节点中运行且只运行一个 Pod,运行的数量和节点数一致,无法人为控制。
支持滚动发布,但有所不同的是滚动发布时只能减不能加,因为每个节点只能运行 1 个 Pod。
下面是部署一个 filebeat 的示例配置文件,此镜像需要向容器内传递几个变量:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 ~]# vim filebeat-ds.yaml apiVersion: apps/v1 kind: DaemonSet metadata: name: filebeat-ds labels: app: filebeat spec: selector: matchLabels: app: filebeat template: metadata: labels: app: filebeat name: filebeat spec: containers: - name: filebeat image: ikubernetes/filebeat:5.6.5-alpine env: - name: REDIS_HOST value: db.ikubernetes.io:6379 - name: LOG_LEVEL value: info
指定新镜像以更新程序版本:
1 kubectl set image ds/filebeat-ds filebeat=ikubernetes/filebeat:5.6.6-alpine
查看刚才创建的 ds 类型的控制器:
1 2 3 ~]# kubectl get ds NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE filebeat-ds 3 3 3 3 3 <none> 10m
节点选择器
使用节点选择器,实现只在指定的节点中运行 ds 类型的 Pod:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 ~]# vim filebeat-ds.yaml apiVersion: apps/v1 kind: DaemonSet metadata: name: filebeat-ds labels: app: filebeat spec: selector: matchLabels: app: filebeat template: metadata: labels: app: filebeat name: filebeat spec: containers: - name: filebeat image: ikubernetes/filebeat:5.6.5-alpine env: - name: REDIS_HOST value: db.ikubernetes.io:6379 - name: LOG_LEVEL value: info nodeSelector: logcollection: "on"
使用 kubectl apply -f filebeat-ds.yaml 创建后,可以看到因为当前所有节点都没有匹配的标签,所以没有 Pod 被创建:
1 2 3 4 5 6 ~]# kubectl get deploy No resources found in default namespace. ~]# kubectl get ds NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE filebeat-ds 0 0 0 0 0 logcollection=on 25m
现在只为其中一个节点添加这个自定义标签 logcollection:
1 ~]# kubectl label node node1.monster.com logcollection="on"
再次查看运行的 Pod,可以看到因为 node1 节点的标签,被节点选择器选中,所以在 node1 节点中创建了 Pod:
1 2 3 4 5 6 7 ~]# kubectl get ds NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE filebeat-ds 1 1 1 1 1 logcollection=on 35m ~]# kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES filebeat-ds-79bbt 1/1 Running 0 17s 10.244.1.26 node1.monster.com <none> <none>
4. Job 控制器
可以使用以下方式定义重启策略,如果 Pod 中的所有容器意外停止了,是否要重启:
Always:默认策略,当容器退出时总是重启容器;
OnFailure:当容器退出时,从不重启;
Never:容器正常退出不会再重新启动,退出码非 0 时才重启容器。
单路作业示例配置 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ~]# vim job-example.yaml apiVersion: batch/v1 kind: Job metadata: name: job-example spec: template: metadata: labels: app: myjob spec: containers: - name: myjob image: alpine command: ["/bin/sh" , "-c" , " sleep 10" ] restartPolicy: Never
10 秒钟后,此 Job 就运行完成了,可以看到状态为 Completed:
1 2 3 4 5 6 7 ~]# kubectl get job NAME COMPLETIONS DURATION AGE job-example 1/1 21s 24s ~]# kubectl get pods NAME READY STATUS RESTARTS AGE job-example-fjqjv 0/1 Completed 0 75s
多路作业示例配置 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ~]# vim job-multi.yaml apiVersion: batch/v1 kind: Job metadata: name: job-multi spec: completions: 5 parallelism: 2 template: metadata: labels: app: myjob spec: containers: - name: myjob image: alpine command: ["/bin/sh" , "-c" , "sleep 30" ] restartPolicy: Never
5. CronJob 控制器
CronJob 直接控制 Job,再由 Job 控制 Pod。
周期性计划任务和 Linux 中的 crontab 使用方法是一样的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 -> apiVersion: batch/v1beta1 kind: CronJob metadata: name: cronjob-example labels: app: mycronjob spec: schedule: "*/2 * * * *" jobTemplate: metadata: labels: app: mycronjob-jobs spec: parallelism: 2 template: spec: containers: - name: myjob image: alpine command: - /bin/sh - -c - date; echo Hello from the Kubernetes cluster; sleep 10 restartPolicy: OnFailure