Pod
pod在之前说过,pod是kubernetes集群中是最小的调度单元,pod中可以运行多个容器,而node又可以包含多个pod,关系如下图:
在对pod的用法进行说明之前,有必要先对docker容器进行说明
在使用docker时,可以使用docker run命令创建一个容器,而在kubernetes集群中对长时间运行容器的要求是:
- 主程序需要一直在前台执行
如果我们创建的docker镜像的启动命令是后台执行程序,例如Linux脚本:
nohup ./start.sh &
那么kubelet在创建这个Pod执行完这个命令后,会认为这个Pod执行完毕,从而进行销毁pod,如果pod定义了ReplicationController,那么销毁了还会在创建,继续执行上述的命令然后又销毁,然后又重建,这样就会陷入恶性循环,这也是为什么执行的命令要运行在前台的原因,这一点一定要注意
Pod生命周期和重启策略
Pod的重启策略(RestartPolicy):
- Always:当容器失败时,由kubelet自动重启该容器
- OnFailure:当容器终止运行且退出代码不为0的时候,由kubelet自动重启
- Never:不论容器运行状态如何,kubelet永远都不会重启该容器
Pod的重启策略和控制器息息相关,每种控制器对Pod的重启策略如下:
- RC和DaemonSet:必须设置为Always,需要保证该容器持续运行
- Job:OnFailure和Never,确保容器执行完毕后不在重启
- kubelet:在Pod失效时自动重启它,不论将RestartPolicy设置成什么值,也不会对Pod进行健康检查
我们简单介绍完kubernetes的逻辑结构之后我们来看一下k8s控制器资源,k8s控制器资源分很多种,有replication controller,deployment,Horizontal pod autoscaler,statefulSet,daemonSet,job等...,接下来我们来详细分析一下下面资源的使用场景和区别
replication Controller控制器
replication controller简称RC,是kubernetes系统中的核心概念之一,简单来说,它其实定义了一个期望的场景,即声明某种pod的副本数量在任意时刻都复合某个预期值,所以RC的定义包含以下部分:
- pod期待的副本数量
- 用于筛选目标pod的Label Selector
- 当pod的副本数量小于期望值时,用于创建新的pod的pod模板(template)
下面是一个完整的RC定义的例子,即确保拥有app=mynginx标签的这个pod在整个kubernetes集群中始终只有一个副本,红色字体一定要一直,因为我们的控制器就是根据标签筛选出来的(因为pod的ip和pod名字都会变化):
[root@master ~]# vim nginx.yaml apiVersion: v1 kind: ReplicationController metadata: name: myweb namespace: default spec: replicas: 1 selector: app: mynginx template: metadata: labels: app: mynginx spec: containers: - name: mycontainer image: lizhaoqwe/nginx:v1 imagePullPolicy: IfNotPresent ports: - containerPort: 80
在我们定义了一个RC并将其提交到kubernetes集群中后,master上的controller Manager组件就得到了通知,定期巡检系统中当前存货的目标pod,并确保目标pod实力数量刚好等于此RC的期望值,如果多余期望值,则停掉一些,如果少于期望值会再创建一些。
以下面3个Node节点的集群为例,说明kubernetes是如何通过RC来实现pod副本数量自动控制的机制,我们创建2个pod运行redis-slave,系统可能会再两个节点上创建pod,如下图
假设Node2上的pod意外终止,则根据RC定义的replicas数量1,kubernetes将会自动创建并启动两个新的pod,以保证在整个集群中始终有两个redis-slave Pod运行,如下图所示,系统可能选择Node3或者Node1来创建新的pod
此外,我们还可以通过修改RC的副本数量,来实现Pod的动态扩容和缩容
[root@master ~]# kubectl scale --replicas=3 rc myweb replicationcontroller/myweb scaled
结果如下
这里需要注意的是,删除RC并不会影响通过该RC已创建好的pod,为了删除所有pod,可以设置replicas的值为0,然后更新该RC,另外,kubectl提供了stop和delete命令来一次性删除RC和RC控制的全部Pod
ReplicaSet
上一阶段讲过了ReplicationController控制器之后,相信大家对其已经了解了,ReplicaSet控制器其实就是ReplicationController的升级版,官网解释为下一代的“RC”,ReplicationController和ReplicaSet的唯一区别是ReplicaSet支持基于集合的Label selector,而RC只支持基于等式的Label Selector,这使得ReplicaSet的功能更强,下面等价于之前的RC例子的ReokucaSet的定义
[root@master ~]# vim replicaSet.yaml apiVersion: extensions/v1beta1 kind: ReplicaSet metadata: name: myweb namespace: default spec: replicas: 1 selector: matchLabels: app: mynginx matchExpressions: - {key: app, operator: In, values: [mynginx]} template: metadata: labels: app: mynginx spec: containers: - name: mycontainer image: lizhaoqwe/nginx:v1 imagePullPolicy: IfNotPresent ports: - containerPort: 80
kubectl命令行工具适用于RC的绝大部分命令同样适用于ReplicaSet,此外,我们当前很少单独适用ReplicaSet,它主要被Deployment这个更高层的资源对象所使用,从而形成一整套Pod创建,删除,更新的编排机制,我们在使用Deployment时无需关心它是如何维护和创建ReplicaSet的,这一切都是自动发生的
最后,总结一下RC(ReplicaSet)的一些特性和作用:
- 在绝大多数情况下,我们通过定义一个RC实现Pod的创建及副本数量的自动控制
- 在RC里包括完整的Pod定义模板
- RC通过Label Selector机制实现对Pod副本的自动控制
- 通过改变RC里的Pod副本数量,可以实现Pod的扩容和缩容
- 通过改变RC里Pod模板中的镜像版本,可以实现滚动升级
Deployment
Deployment是kubernetes在1.2版本中引入的新概念,用于更好的解决Pod的编排问题,为此,Deployment在内部使用了ReplicaSet来实现目的,我们可以把Deployment理解为ReplicaSet的一次升级,两者的相似度超过90%
Deployment的使用场景有以下几个:
- 创建一个Deployment对象来生成对应的ReplicaSet并完成Pod副本的创建
- 检查Deployment的状态来看部署动作是否完成(Pod副本数量是否达到了预期的值)
- 更新Deployment以创建新的Pod(比如镜像升级)
- 如果当前Deployment不稳定,可以回滚到一个早先的Deployment版本
- 暂停Deployment以便于一次性修改多个PodTemplateSpec的配置项,之后在恢复Deployment,进行新的发布
- 扩展Deployment以应对高负载
- 查看Deployment的状态,以此作为发布是否成功的纸币哦
- 清理不在需要的旧版本ReplicaSet
除了API生命与Kind类型有区别,Deployment的定义与Replica Set的定义很类似,我们这里还是以上面为例子
apiVersion: extensions/v1beta1 apiVersion: apps/v1
kind: ReplicaSet kind: Deployment
执行文件
[root@master ~]# kubectl apply -f deploy.yaml
deployment.apps/myweb created
查看结果
[root@master ~]# kubectl get deployments NAME READY UP-TO-DATE AVAILABLE AGE myweb 1/1 1 1 5m5s
解释一下上面的显示
NAME:你的deployment控制器的名字
READY:已经准备好的Pod个数/所期望的Pod个数
UP-TO-DATE:最新版本的Pod数量,用于在执行滚动升级时,有多少个Pod副本已经成功升级
AVAILABLE:当前集群中可用的Pod数量,也就是集群中存活的Pod数量
StatefulSet
在kubernetes系统中,Pod的管理对象RC,Deployment,DaemonSet和Job都面向无状态的服务,但现实中有很多服务时有状态的,比如一些集群服务,例如mysql集群,集群一般都会有这四个特点:
- 每个节点都是有固定的身份ID,集群中的成员可以相互发现并通信
- 集群的规模是比较固定的,集群规模不能随意变动
- 集群中的每个节点都是有状态的,通常会持久化数据到永久存储中
- 如果磁盘损坏,则集群里的某个节点无法正常运行,集群功能受损
如果你通过RC或Deployment控制Pod副本数量来实现上述有状态的集群,就会发现第一点是无法满足的,因为Pod名称和ip是随机产生的,并且各Pod中的共享存储中的数据不能都动,因此StatefulSet在这种情况下就派上用场了,那么StatefulSet具有以下特性:
- StatefulSet里的每个Pod都有稳定,唯一的网络标识,可以用来发现集群内的其它成员,假设,StatefulSet的名称为fengzi,那么第1个Pod叫fengzi-0,第2个叫fengzi-1,以此类推
- StatefulSet控制的Pod副本的启停顺序是受控的,操作第N个Pod时,前N-1个Pod已经是运行且准备状态
- StatefulSet里的Pod采用稳定的持久化存储卷,通过PV或PVC来实现,删除Pod时默认不会删除与StatefulSet相关的存储卷(为了保证数据的安全)
StatefulSet除了要与PV卷捆绑使用以存储Pod的状态数据,还要与Headless,Service配合使用,每个StatefulSet定义中都要生命它属于哪个Handless Service,Handless Service与普通Service的关键区别在于,它没有Cluster IP
一、创建pv
[root@master ~]# cat createpv.yaml apiVersion: v1 kind: PersistentVolume metadata: name: pv01 spec: nfs: path: /share_v1 server: 192.168.254.14 accessModes: - ReadWriteMany - ReadWriteOnce capacity: storage: 1Gi --- apiVersion: v1 kind: PersistentVolume metadata: name: pv02 spec: nfs: path: /share_v2 server: 192.168.254.14 accessModes: - ReadWriteMany - ReadWriteOnce capacity: storage: 1Gi --- apiVersion: v1 kind: PersistentVolume metadata: name: pv03 spec: nfs: path: /share_v3 server: 192.168.254.14 accessModes: - ReadWriteMany - ReadWriteOnce capacity: storage: 1Gi --- apiVersion: v1 kind: PersistentVolume metadata: name: pv04 spec: nfs: path: /share_v4 server: 192.168.254.14 accessModes: - ReadWriteMany - ReadWriteOnce capacity: storage: 2Gi --- apiVersion: v1 kind: PersistentVolume metadata: name: pv05 spec: nfs: path: /share_v5 server: 192.168.254.11 accessModes: - ReadWriteMany - ReadWriteOnce capacity: storage: 2Gi
二、创建pvc和statfulset控制器管理的pod
[root@master ~]# cat state.yaml apiVersion: v1 kind: Service metadata: name: myapp-svc namespace: default labels: app: myapp spec: ports: - name: http port: 80 clusterIP: None selector: app: myapp-pod --- apiVersion: apps/v1 kind: StatefulSet metadata: name: myapp spec: serviceName: myapp-svc replicas: 3 selector: matchLabels: app: myapp-pod template: metadata: labels: app: myapp-pod spec: containers: - name: myapp image: liwang7314/myapp:v1 imagePullPolicy: IfNotPresent ports: - name: http containerPort: 80 volumeMounts: - name: myappdata mountPath: /usr/share/nginx/html volumeClaimTemplates: - metadata: name: myappdata spec: accessModes: - ReadWriteOnce resources: requests: storage: 1Gi
三、创建完毕后观察
[root@master ~]# kubectl get pods NAME READY STATUS RESTARTS AGE myapp-0 1/1 Running 0 8s myapp-1 1/1 Running 0 5s myapp-2 1/1 Running 0 3s
DemonSet
在每一个node节点上只调度一个Pod,因此无需指定replicas的个数,比如:
- 在每个node上都运行一个日志采集程序,负责收集node节点本身和node节点之上的各个Pod所产生的日志
- 在每个node上都运行一个性能监控程序,采集该node的运行性能数据
[root@localhost ~]# cat daemon.yaml apiVersion: apps/v1 kind: DaemonSet metadata: name: fluentd-cloud-logging namespace: kube-system labels: k8s-app: fluentd-cloud-logging spec: selector: matchLabels: k8s-app: fluentd-cloud-logging template: metadata: namespace: kube-system labels: k8s-app: fluentd-cloud-logging spec: containers: - name: fluentd-cloud-logging image: kayrus/fluentd-elasticsearch:1.20 resources: limits: cpu: 100m memory: 200Mi env: - name: FLUENTD_ARGS value: -q volumeMounts: - name: varlog mountPath: /var/log readOnly: false - name: containers mountPath: /var/lib/docker/containers readOnly: false volumes: - name: containers hostPath: path: /usr - name: varlog hostPath: path: /usr/sbin
查看pod所在的节点
[root@localhost ~]# kubectl get pods -n kube-system -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES coredns-bccdc95cf-8sqzn 1/1 Running 2 2d7h 10.244.0.6 master <none> <none> coredns-bccdc95cf-vt8nz 1/1 Running 2 2d7h 10.244.0.7 master <none> <none> etcd-master 1/1 Running 1 2d7h 192.168.254.13 master <none> <none> fluentd-cloud-logging-6xx4l 1/1 Running 0 4h24m 10.244.2.7 node2 <none> <none> fluentd-cloud-logging-qrgg6 1/1 Rruning 0 4h24m 10.244.1.7 node1 <none> <none> kube-apiserver-master 1/1 Running 1 2d7h 192.168.254.13 master <none> <none> kube-controller-manager-master 1/1 Running 1 2d7h 192.168.254.13 master <none> <none> kube-flannel-ds-amd64-c97wh 1/1 Running 0 2d7h 192.168.254.12 node1 <none> <none> kube-flannel-ds-amd64-gl6wg 1/1 Running 1 2d7h 192.168.254.13 master <none> <none> kube-flannel-ds-amd64-npsqf 1/1 Running 0 2d7h 192.168.254.10 node2 <none> <none> kube-proxy-gwmx8 1/1 Running 1 2d7h 192.168.254.13 master <none> <none> kube-proxy-phqk2 1/1 Running 0 2d7h 192.168.254.12 node1 <none> <none> kube-proxy-qtt4b 1/1 Running 0 2d7h 192.168.254.10 node2 <none> <none> kube-scheduler-master 1/1 Running 2 2d7h 192.168.254.13 master <none> <none>