kuberenetes控制器(管理Pod)

kuberenetes控制器(管理Pod)

K8S中去管理pod的任务是由控制器(controllers)去做的,控制器又称为工作负载(workload),有了控制器才能更好的对服务进行编排,有五个比较重要的控制器,如下。

  • Deployment
  • StatefulSet
  • DaemonSet
  • Job
  • CronJob

下面会分别介绍,先看看Pod与控制器的关系。

Pod 与控制器的关系

首先控制器是在集群上管理和运行容器的对象,也就是管理Pod的,第二控制与pod关联是通过标签(label-selector)进行关联,通过控制器去管理Pod会实现更多流啤的功能,像是什么弹性伸缩升级滚动更新之类的,都是在控制器层面实现的。

Deployment部署无状态应用

使用Deployment,这个控制器创建了nginx,这种算是无状态的应用,像是nginx根本不用去考虑分配到那个节点或容器IP是多少,所以Deployment适用于部署无状态的应用,他能管理Pod和ReplicaSet

[root@k8s01 yml]# kubectl get replicasets.apps 
NAME              DESIRED   CURRENT   READY   AGE
nginx-dd6b5d745   3         3         3       3d5h
[root@k8s01 yml]# 

ReplicaSet这对于用于来说,这是一个隐藏的控制器,用作管理Pod副本数的,我预期的状态是创建三个副本,可以看一下上文的配置文件,如果有一个pod挂掉了他就会再启动一个pod,会确保有三个在运行,deployment是管理replicasets&pod的,replicasets也做版本的管理,之前我们使用kubectl rollout回滚到上一个版本就是依靠的replicasets

所以总结一下就是deployment能管理PodReplicaSet、具有上线部署、副本设定、滚动更新、回滚等功能,下面编辑一下nginx-deployment的配置文件

[root@k8s01 yml]# kubectl edit deployments.apps nginx 
spec:
  progressDeadlineSeconds: 600
  replicas: 3					#设置副本数
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      run: nginx				#标签选择器,pod与控制器关联
  strategy:
    rollingUpdate:
      maxSurge: 25%
      maxUnavailable: 25%
    type: RollingUpdate			# 滚动更新策略,默认滚动更新

查看回滚版本,也是replicasets来做的。

[root@k8s01 yml]# kubectl rollout history deployment nginx 
deployment.apps/nginx 
REVISION  CHANGE-CAUSE
1         <none>

[root@k8s01 yml]# 

支持声明式更新,例如想更新某一个字段,他就会更新某一个字段,例如更新image字段,deployments比较适合Web服务,之前部署用的就是deployments

有状态和无状态服务区别

刚刚上面提到了deployment部署的是无状态服务,还有一种服务是有状态服务,这俩服务有啥区别,下面分别看一下。

无状态服务概述

deployment认为所有的Pod都是一样的,也就是这些。

[root@k8s01 yml]# kubectl get pods 
NAME                    READY   STATUS    RESTARTS   AGE
nginx-dd6b5d745-6z8lx   1/1     Running   5          3d5h
nginx-dd6b5d745-cv4t8   1/1     Running   4          3d5h
nginx-dd6b5d745-tgh5c   1/1     Running   5          3d5h
[root@k8s01 yml]# 

这三个副本都是一模一样的,都是使用同一个镜像去启动的,里面的数据也是完全一样的,属于是对等关系,而且他们的启动顺序也是没有任何规律,先启动哪个都无所谓,对我应用部署没有任何影响,也不用考虑pod在哪个node运行,更不用也不用考虑Pod:IP是什么,所以你可以随意扩容和缩容。

有状态服务概述

举一个最简单的例子,例如我现在需要使用三个zookeeper做一个集群,集群最低要求要有三个zookeeper实例,每个容器都设置的一个名为ZOO_MY_ID 的全局变量,而且这个值各个容器之间都不一样,有三个容器,我设置的分别是{1..3},各个节点地址都是用的zoo{1..3},这算是唯一的网络标识符,容器挂了重启这个标识符是不变的,但是我那个zookeeper没有挂载数据卷,其实还要有持久存储。

像是这种服务你就不能随意的扩容和缩减了,需要有序进行了,包括扩展删除终止滚动更新服务,像是这种就算有状态服务了,每个实例之间属于不对等关系,各个容器之间有些参数不一致,或是挂载的数据卷源/位置不一致,你不可以随意的扩容或缩减这些服务,否则就会有问题,在K8S中,要实现这种服务就要使用StatefulSet了。

headless service

想知道StatefulSet具体是怎么实现的,要从一个服务谈起,也就是headless service,称为无头服务,回忆一下之前的Service,它是一组pod的访问策略,提供负载均衡和服务发现,访问这个Service他会帮你把请求转发到某个Pod,也就是这个东西,这是之前创建的。

[root@k8s01 yml]# kubectl get services 
NAME            TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)        AGE
kubernetes      ClusterIP   10.0.0.1     <none>        443/TCP        10d
nginx-service   NodePort    10.0.0.180   <none>        80:30010/TCP   3d21h
[root@k8s01 yml]# 

类型NodePort,无头服务也是使用了Service,不过在定义的时候不需要CLUSTER-IP了,直接会绑定具体Pod的IP,上面就是一个常规的Service,他关联了三个标签为nginxPod,且提供了一个统一的入口,IP10.0.0.180,你访问这个IP:port他会帮你把请求转发到后端Pod,也就是这里。

[root@k8s01 yml]# kubectl get endpoints nginx-service 
NAME            ENDPOINTS                                        AGE
nginx-service   10.244.0.38:80,10.244.1.100:80,10.244.1.101:80   3d22h
[root@k8s01 yml]# 

再来看headless service,和常规的稍微有点区别,他会将CLUSTER-IP置位none

[root@k8s01 yml]# cat headless-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: nginx

和正常创建Service的区别就是clusteripnone,默认clusterIP是启用的,现在制定为none了就是不为这个Service分配一个clusterip了,创建一下看效果。

[root@k8s01 yml]# kubectl create -f headless-service.yaml 
service/nginx created
[root@k8s01 yml]# kubectl get services nginx
NAME    TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
nginx   ClusterIP   None         <none>        80/TCP    6s
[root@k8s01 yml]# 

可以看到CLUSTER-IPNone了,他没有具体的IP,这种情况下主要用的就是DNS访问了,他保证了唯一的网络标识符,使用IP无法保证你这个pod挂掉重启后永远都是使用这个IP,所以这就是利用了DNS保证了网络唯一标识符的,我们之前部署过了coredns,下面开始部署有状态服务。

部署有状态应用

SatefulSet是部署有状态应用,解决Pod独立生命周期,保持Pod顺序和唯一性。稳定,唯一的网络标识符,持久存储。有序,优雅的部署和扩展,删除和终止。有序,滚动升级

[root@k8s01 yml]# kubectl delete -f headless-service.yaml 
service "nginx" deleted
[root@k8s01 yml]# kubectl delete -f deployment.yaml 
deployment.apps "nginx-deployment" deleted
[root@k8s01 yml]# kubectl delete -f services.yaml 
service "nginx-service" deleted
[root@k8s01 yml]# cat nginx-headless.yaml 
apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: nginx

---

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: nginx
spec:
  selector:
    matchLabels:
      app: nginx
  serviceName: "nginx"
  replicas: 3
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        ports:
        - containerPort: 80
          name: nginx

我把ServiceStatefulSet写到了一起,StatefulSetdeployment有差别的地方就是StatefulSet有个serviceName,他是用来关联上面无头service的,上面无头Service的名称就是nginx,直接用这个文件进行部署,部署完之后看一下所创建的StatefulSetService

[root@k8s01 yml]# kubectl create -f nginx-headless.yaml 
service/nginx created
statefulset.apps/nginx created
[root@k8s01 yml]# kubectl get services nginx
NAME    TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
nginx   ClusterIP   None         <none>        80/TCP    14s
[root@k8s01 yml]# kubectl get pods
NAME      READY   STATUS    RESTARTS   AGE
nginx-0   1/1     Running   0          53s
nginx-1   1/1     Running   0          36s
nginx-2   1/1     Running   0          20s
[root@k8s01 yml]# 

创建好了,现在了解一下唯一的网络标识是怎么来的,就是上图NAME那一行的名称,这就是一个固定的DNS名称了,而且会有有序编号,现在随便启动一个镜像去测试一下。

[root@k8s01 yml]# kubectl run -it --image=busybox:1.28.4 --rm --restart=Never sh
If you don't see a command prompt, try pressing enter.
/ # 
/ # for i in 0 1 2;do nslookup nginx-$i.nginx;done
Server:    10.0.0.2
Address 1: 10.0.0.2 kube-dns.kube-system.svc.cluster.local

Name:      nginx-0.nginx
Address 1: 10.244.1.102 nginx-0.nginx.default.svc.cluster.local
Server:    10.0.0.2
Address 1: 10.0.0.2 kube-dns.kube-system.svc.cluster.local

Name:      nginx-1.nginx
Address 1: 10.244.0.39 nginx-1.nginx.default.svc.cluster.local
Server:    10.0.0.2
Address 1: 10.0.0.2 kube-dns.kube-system.svc.cluster.local

Name:      nginx-2.nginx
Address 1: 10.244.1.103 nginx-2.nginx.default.svc.cluster.local
/ # pod "sh" deleted
[root@k8s01 yml]# kubectl get pods -o wide
NAME      READY   STATUS    RESTARTS   AGE     IP             NODE    NOMINATED NODE   READINESS GATES
nginx-0   1/1     Running   0          9m19s   10.244.1.102   k8s03   <none>           <none>
nginx-1   1/1     Running   0          9m2s    10.244.0.39    k8s02   <none>           <none>
nginx-2   1/1     Running   0          8m46s   10.244.1.103   k8s03   <none>           <none>
[root@k8s01 yml]# 

没什么问题, pod名称后需要加上Service的名称才能解析到,这就是一个Pod有一个唯一的域名,这是个固定的,并且解析到自身的IP,所以,总结一下。

StatefulSetdeployment的主要区别就是StatefulSet是有身份的,这个身份有三要素,分别是有自己的域名、主机名、存储(PVC),每一个Pod都有一个唯一的主机名,看一下是什么就知道了。

[root@k8s01 yml]# for i in {0,1,2};do kubectl exec nginx-$i hostname;done
nginx-0
nginx-1
nginx-2
[root@k8s01 yml]# 

DaemonSet

这个比较好理解,他会在你每一个Node上运行一个Pod,新加入的Node也会自动运行一个Pod,比较适合运行采集器之类的东西

[root@k8s01 yml]# cat daemonset.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: filebeat
spec:
  selector:
    matchLabels:
      app: filebeat
  template:
    metadata:
      labels:
        app: filebeat
    spec:
      containers:
      - name: filebeat
        image: nginx:latest
        volumeMounts: 
        - mountPath: /log
          name: varlog
      volumes:
      - name: varlog
        hostPath: 
          path: /var/log
          type: Directory
[root@k8s01 yml]# 

使用了nginx的镜像,你就把他当成filebeat,这东西是干嘛的应该都了解过,上面把宿主机的/var/log目录挂在到了容器的/log,上面提到了这个类型会在所有的Node节点都启动一个的pod,所以你可以理解为该Pod可以收集宿主机日志,通过filebeat读取挂载目录的日志推到logstash||elasticsearch

[root@k8s01 yml]# kubectl create -f daemonset.yaml
daemonset.apps/filebeat created
[root@k8s01 yml]# kubectl get pods -o wide
NAME             READY   STATUS    RESTARTS   AGE   IP             NODE    NOMINATED NODE   READINESS GATES
filebeat-5sfvj   1/1     Running   0          26s   10.244.0.46    k8s02   <none>           <none>
filebeat-q5f7c   1/1     Running   0          26s   10.244.2.60    k8s05   <none>           <none>
filebeat-zcl56   1/1     Running   0          26s   10.244.1.104   k8s03   <none>           <none>

可以看到每个节点都有一个在运行,下面去Pod里看看宿主机的东西是否被挂载到Pod,挂载到了就没问题

[root@k8s01 yml]# kubectl exec -it filebeat-5sfvj /bin/bash
root@filebeat-5sfvj:/# ls -l /log/
total 5468
drwxr-xr-x. 2 root root      204 May  1 05:24 anaconda
drwx------. 2 root root       23 May  1 05:24 audit
-rw-------. 1 root root        0 Jun  5 03:19 boot.log
-rw-------  1 root root    78724 Jun  2 02:50 boot.log-20200602
-rw-------  1 root root     9076 Jun  3 08:06 boot.log-20200603
-rw-------  1 root root    37533 Jun  5 03:19 boot.log-20200605
-rw-------. 1 root voice       0 May  1 05:22 btmp
drwxr-xr-x  2 root root     4096 Jun  5 08:44 containers
-rw-------. 1 root root   294625 Jun  5 08:46 cron
-rw-r--r--  1 root root   122195 Jun  5 02:58 dmesg
-rw-r--r--  1 root root   122185 Jun  5 02:42 dmesg.old
-rw-r--r--. 1 root root     1842 May  1 05:27 firewalld
-rw-r--r--. 1 root root      193 May  1 05:21 grubby_prune_debug
-rw-r--r--. 1 root root   292000 Jun  5 06:13 lastlog
-rw-------. 1 root root   153548 Jun  5 08:46 maillog
-rw-------. 1 root root  4517207 Jun  5 08:46 messages
drwxr-xr-x  7 root root     4096 Jun  5 08:43 pods
drwxr-xr-x. 2 root root        6 May  1 05:24 rhsm
drwxr-xr-x. 2 root root       91 Jun  5 02:42 sa
-rw-------. 1 root root    51606 Jun  5 08:24 secure
-rw-------. 1 root root        0 May  1 05:22 spooler
-rw-------. 1 root root        0 May  1 05:21 tallylog
drwxr-xr-x. 2 root root       23 May  1 05:24 tuned
-rw-r--r--. 1 root root    21390 Jun  5 02:58 vmware-vgauthsvc.log.0
-rw-r--r--. 1 root root    27822 Jun  5 06:05 vmware-vmsvc.log
-rw-rw-r--. 1 root voice   41472 Jun  5 08:24 wtmp
-rw-------. 1 root root     4262 Jun  1 09:35 yum.log
root@filebeat-5sfvj:/# 

没问题,使用场景就是这样,将来使用filebeat就可以直接采集这个目录下的日志文件了

Job与CronJob

Job分为普通任务(Job)和定时任务(CronJob),普通任务是一次性执行的,定时任务就是有计划的去执行,下面分别去了解一下。

Job

也就是普通任务,下面是一个例子,在官网扒过来的。

[root@k8s01 yml]# cat job.yaml
apiVersion: batch/v1
kind: Job
metadata: 
   name: pi
spec: 
  template: 
    spec: 
      containers: 
      - name: pi
        image: perl
        command: ["perl","-Mbignum=bpi","-wle","print bpi(2000)"]
      restartPolicy: Never
  backoffLimit: 4

使用perl镜像启动一个容器,运行一条命令去计算,这个不用深入了解,重启策略为Never,表示容器退出码为0的情况下从不重启,backoffLimit设置重试的次数,如果这个任务执行失败了怎么办,现在定义了4,如果失败了他会帮你重试4次,为毛要有这个?上面提到了Never表示容器退出码为0的情况下从不重启,如果退出状态码非0还是会重启,如果四次退出码都是0,也就是到达了backoffLimit的限制,他就不会再重启了,默认是6,下面创建一下。

[root@k8s01 yml]# kubectl create -f job.yml 
job.batch/pi created
[root@k8s01 yml]# kubectl get jobs.batch 
NAME   COMPLETIONS   DURATION   AGE
pi     1/1           3m17s      3m22s
[root@k8s01 yml]# kubectl get pod pi-k2zvd 
NAME       READY   STATUS      RESTARTS   AGE
pi-k2zvd   0/1     Completed   0          3m25s
[root@k8s01 yml]# 

可以看到pod的状态为Completed了,说明他执行完命令后正常关闭了,关闭后他不会被删除,自行删除吧,所以这个job比较适合临时跑一个任务,跑完之后就退出了,不再执行了。

CronJob

定时任务,像crontab一样,基于时间去调度,用来做备份还是蛮不错的,下面一个例子,也是在官方扒过来的,顺便改了改。

[root@k8s01 yml]# cat cronjob.yaml
apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: opesn
spec:
  schedule: "*/1 * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: opesn
            image: centos:latest
            command: ["bash","-c","date; echo opesn"]
          restartPolicy: OnFailure
[root@k8s01 yml]# 

schedule为时间表达式,和crontab的表达式写法是一样的,所以这个就是一分钟一次,重启策略为OnFailure,如果容器异常退出就会重启.

[root@k8s01 yml]# kubectl create -f cronjob.yaml 
cronjob.batch/opesn created
[root@k8s01 yml]# kubectl get cronjobs.batch opesn 
NAME    SCHEDULE      SUSPEND   ACTIVE   LAST SCHEDULE   AGE
opesn   */1 * * * *   False     1        17s             59s


[root@k8s01 yml]# kubectl get pod
NAME                     READY   STATUS              RESTARTS   AGE
opesn-1591347900-492v4   0/1     ContainerCreating   0          17s
[root@k8s01 yml]# kubectl logs opesn-1591347900-492v4 
Fri Jun  5 09:05:22 UTC 2020
opesn
[root@k8s01 yml]# 


[root@k8s01 yml]# kubectl get pod
NAME                     READY   STATUS      RESTARTS   AGE
opesn-1591347900-492v4   0/1     Completed   0          107s
opesn-1591347960-4fn49   0/1     Completed   0          47s
[root@k8s01 yml]# kubectl logs opesn-1591347960-4fn49 
Fri Jun  5 09:06:21 UTC 2020
opesn
[root@k8s01 yml]# 


[root@k8s01 yml]# kubectl get pod
NAME                     READY   STATUS      RESTARTS   AGE
opesn-1591347900-492v4   0/1     Completed   0          2m36s
opesn-1591347960-4fn49   0/1     Completed   0          96s
opesn-1591348020-4fs92   0/1     Completed   0          36s
[root@k8s01 yml]# kubectl logs opesn-1591348020-4fs92 
Fri Jun  5 09:07:21 UTC 2020
opesn
[root@k8s01 yml]# 

就是这种效果,创建成功后每分钟执行一次,历史默认保留三次

posted @ 2020-06-18 10:25  helloord  阅读(430)  评论(0编辑  收藏  举报