pod控制器
pod类型
自主式pod:没有自愈能力;删除后就没有了
控制器pod:有自愈能力;删除后还可以自动重建(根据replicaset设置pod的副本和期望值来定义)
pod控制器和作用
Pod控制器,又称之为工作负载(workload),是用于实现管理pod的中间层,确保pod资源符合预期的状态,pod的资源出现故障时,会尝试进行重启,当根据重启策略无效,则会重新新建pod的资源。
无状态与有状态的区别
有状态:数据之间需要同步,必须要加存储;每次都有上下级关系;每次访问都有不同的关系
无状态:数据之间不互相同步;每次访问都一样的关系
pod控制器类型
ReplicaSet、Deployment、DaemonSet、StatefulSet、 Job、Cronjob
pod与控制器之间的关系
控制器controllers:在集群上通过标签(label-selector)去管理pod对象
pod:通过控制器去实现应用的运维(升级等)
ReplicaSet
管理副本控制器,支持滚动式缩容和扩容
ReplicaSet三个组件
- 用户期望的pod副本数量
- 标签选择器,判断哪个pod归自己管理
- 当现存的pod数量不足,会根据pod资源模板进行新建
帮助用户管理无状态的pod资源,精确反应用户定义的目标数量,但是RelicaSet不是直接使用的控制器,而是使用Deployment
Deployment
管理Pod和ReplicaSet,工作在replicaset上,用于管理无状态应用;支持滚动更新和回滚,提供声明式配置,还可以线上部署。
ReplicaSet与Deployment这两个资源对象逐步替换之前RC的作用
示例
[root@master01 ~]# vim nginx-deploment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.15.4
ports:
- containerPort: 80
[root@master01 ~]# kubectl create -f nginx-deploment.yaml
[root@master01 ~]# kubectl get pods,deploy,rs
#查看控制器配置
[root@master01 ~]# kubectl edit deployment/nginx-deployment
StatefulSet
管理有状态应用;k8s独有的
- 稳定的持久化存储,即Pod重新调度后还是能访问到相同的持久化数据,基于PVC来实现
- 稳定的网络标志,即Pod重新调度后其PodName和HostName不变,基于Headless Service(即没有Cluster IP的Service)来实现
- 有序部署,有序扩展,即Pod是有顺序的,在部署或者扩展的时候要依据定义的顺序依次进行(即从0到N-1,在下一个Pod运行之前所有之前的Pod必须都是Running和Ready状态),基于init containers来实现
- 有序收缩,有序删除(即从N-1到0)
- 官网: kubernetes.io/docs/concep…
statefulsetset组成部分
- Headless Service(无头服务):用于为Pod资源标识符生成可解析的DNS记录。
- volumeClaimTemplates(存储卷申请模板):基于静态或动态PV供给方式为Pod资源提供专有的固定存储。
- StatefulSet:用于管控Pod资源。
为什么要用headless
在deployment中,每一个pod是没有名称,是随机字符串,是无序的。而statefulset中是要求有序的,每一个pod的名称必须是固定的。当节点挂了,重建之后的标识符是不变的,每一个节点的节点名称是不能改变的。pod名称是作为pod识别的唯一标识符,必须保证其标识符的稳定并且唯一。 为了实现标识符的稳定,这时候就需要一个headless service 解析直达到pod,还需要给pod配置一个唯一的名称。
为什么要有volumeClainTemplate
大部分有状态副本集都会用到持久存储,比如分布式系统来说,由于数据是不一样的,每个节点都需要自己专用的存储节点。而在 deployment中pod模板中创建的存储卷是一个共享的存储卷,多个pod使用同一个存储卷,而statefulset定义中的每一个pod都不能使用同一个存储卷,由此基于pod模板创建pod是不适应的,这就需要引入volumeClainTemplate,当在使用statefulset创建pod时,会自动生成一个PVC,从而请求绑定一个PV,从而有自己专用的存储卷
应用场景
- 动态性强:Pod会飘到别的node节点
- 更新发布频繁:互联网思维小步快跑,先实现再优化,老板永远是先上线再慢慢优化,先把idea变成产品挣到钱然后再慢慢一点一点优化
- 支持自动伸缩:一来大促,肯定是要扩容多个副本
清单定义StatefulSet
如上所述,一个完整的 StatefulSet 控制器由一个 Headless Service、一个 StatefulSet 和一个 volumeClaimTemplate 组成。如下资源清单中的定义
root@master01 ~]# vim stateful-demo.yaml
apiVersion: v1
kind: Service
metadata:
name: myapp-svc
labels:
app: myapp-svc
spec:
ports:
- port: 80
name: web
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: ikubernetes/myapp:v1
ports:
- containerPort: 80
name: web
volumeMounts:
- name: myappdata
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: myappdata
annotations: #动态PV创建时,使用annotations在PVC里声明一个StorageClass对象的标识进行关联
volume.beta.kubernetes.io/storage-class: nfs-client-storageclass
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 2Gi
清单定义StatefulSet
如上所述,一个完整的 StatefulSet 控制器由一个 Headless Service、一个 StatefulSet 和一个 volumeClaimTemplate 组成
[root@master01 ~]# vim stateful-demo.yaml
apiVersion: v1
kind: Service
metadata:
name: myapp-svc
labels:
app: myapp-svc
spec:
ports:
- port: 80
name: web
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: ikubernetes/myapp:v1
ports:
- containerPort: 80
name: web
volumeMounts:
- name: myappdata
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: myappdata
annotations: #动态PV创建时,使用annotations在PVC里声明一个StorageClass对象的标识进行关联
volume.beta.kubernetes.io/storage-class: nfs-client-storageclass
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 2Gi
[root@master01 ~]# kubectl apply -f stateful-demo.yaml
创建PV
stor01节点
[root@localhost ~]# mkdir -p /data/volumes/v{1,2,3,4,5}
[root@localhost ~]# vim /etc/exports
/data/volumes/v1 192.168.42.0/24(rw,no_root_squash)
/data/volumes/v2 192.168.42.0/24(rw,no_root_squash)
/data/volumes/v3 192.168.42.0/24(rw,no_root_squash)
/data/volumes/v4 192.168.42.0/24(rw,no_root_squash)
/data/volumes/v5 192.168.42.0/24(rw,no_root_squash)
[root@localhost ~]# systemctl restart rpcbind
[root@localhost ~]# systemctl restart nfs
[root@localhost ~]# exportfs -arv
[root@localhost ~]# showmount -e
定义PV
[root@master01 ~]# vim pv-demo.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv001
labels:
name: pv001
spec:
nfs:
path: /data/volumes/v1
server: stor01
accessModes: ["ReadWriteMany","ReadWriteOnce"]
capacity:
storage: 1Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv002
labels:
name: pv002
spec:
nfs:
path: /data/volumes/v2
server: stor01
accessModes: ["ReadWriteOnce"]
capacity:
storage: 2Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv003
labels:
name: pv003
spec:
nfs:
path: /data/volumes/v3
server: stor01
accessModes: ["ReadWriteMany","ReadWriteOnce"]
capacity:
storage: 2Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv004
labels:
name: pv004
spec:
nfs:
path: /data/volumes/v4
server: stor01
accessModes: ["ReadWriteMany","ReadWriteOnce"]
capacity:
storage: 2Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv005
labels:
name: pv005
spec:
nfs:
path: /data/volumes/v5
server: stor01
accessModes: ["ReadWriteMany","ReadWriteOnce"]
capacity:
storage: 2Gi
[root@master01 ~]# kubectl apply -f pv-demo.yaml
[root@master01 ~]# kubectl get pv
创建statefulset
kubectl apply -f stateful-demo.yaml
kubectl get svc #查看创建的无头服务myapp-svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 50d
myapp-svc ClusterIP None <none> 80/TCP 38s
kubectl get sts #查看statefulset
NAME DESIRED CURRENT AGE
myapp 3 3 55s
kubectl get pvc #查看pvc绑定
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
myappdata-myapp-0 Bound pv002 2Gi RWO 1m
myappdata-myapp-1 Bound pv003 2Gi RWO,RWX 1m
myappdata-myapp-2 Bound pv004 2Gi RWO,RWX 1m
kubectl get pv #查看pv绑定
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv001 1Gi RWO,RWX Retain Available 6m
pv002 2Gi RWO Retain Bound default/myappdata-myapp-0 6m
pv003 2Gi RWO,RWX Retain Bound default/myappdata-myapp-1 6m
pv004 2Gi RWO,RWX Retain Bound default/myappdata-myapp-2 6m
pv005 2Gi RWO,RWX Retain Available 6m
kubectl get pods #查看Pod信息
NAME READY STATUS RESTARTS AGE
myapp-0 1/1 Running 0 2m
myapp-1 1/1 Running 0 2m
myapp-2 1/1 Running 0 2m
kubectl delete -f stateful-demo.yaml
//当删除的时候是从myapp-2开始进行删除的,关闭是逆向关闭
kubectl get pods -w
//此时PVC依旧存在的,再重新创建pod时,依旧会重新去绑定原来的pvc
kubectl apply -f stateful-demo.yaml
kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
myappdata-myapp-0 Bound pv002 2Gi RWO 5m
myappdata-myapp-1 Bound pv003 2Gi RWO,RWX 5m
myappdata-myapp-2 Bound pv004 2Gi RWO,RWX
滚动更新
vim stateful-demo.yaml #修改image版本为v2
.....
image: ikubernetes/myapp:v2
....
kubectl apply -f stateful-demo.yaml
kubectl get pods -w #查看滚动更新的过程
NAME READY STATUS RESTARTS AGE
myapp-0 1/1 Running 0 29s
myapp-1 1/1 Running 0 27s
myapp-2 0/1 Terminating 0 26s
myapp-2 0/1 Terminating 0 30s
myapp-2 0/1 Terminating 0 30s
myapp-2 0/1 Pending 0 0s
myapp-2 0/1 Pending 0 0s
myapp-2 0/1 ContainerCreating 0 0s
myapp-2 1/1 Running 0 31s
myapp-1 1/1 Terminating 0 62s
myapp-1 0/1 Terminating 0 63s
myapp-1 0/1 Terminating 0 66s
myapp-1 0/1 Terminating 0 67s
myapp-1 0/1 Pending 0 0s
myapp-1 0/1 Pending 0 0s
myapp-1 0/1 ContainerCreating 0 0s
myapp-1 1/1 Running 0 30s
myapp-0 1/1 Terminating 0 99s
myapp-0 0/1 Terminating 0 100s
myapp-0 0/1 Terminating 0 101s
myapp-0 0/1 Terminating 0 101s
myapp-0 0/1 Pending 0 0s
myapp-0 0/1 Pending 0 0s
myapp-0 0/1 ContainerCreating 0 0s
myapp-0 1/1 Running 0 1s
//在创建的每一个Pod中,每一个pod自己的名称都是可以被解析的
kubectl exec -it myapp-0 /bin/sh
Name: myapp-0.myapp-svc.default.svc.cluster.local
Address 1: 10.244.2.27 myapp-0.myapp-svc.default.svc.cluster.local
/ # nslookup myapp-1.myapp-svc.default.svc.cluster.local
nslookup: can't resolve '(null)': Name does not resolve
Name: myapp-1.myapp-svc.default.svc.cluster.local
Address 1: 10.244.1.14 myapp-1.myapp-svc.default.svc.cluster.local
/ # nslookup myapp-2.myapp-svc.default.svc.cluster.local
nslookup: can't resolve '(null)': Name does not resolve
Name: myapp-2.myapp-svc.default.svc.cluster.local
Address 1: 10.244.2.26 myapp-2.myapp-svc.default.svc.cluster.local
//从上面的解析,我们可以看到在容器当中可以通过对Pod的名称进行解析到ip。其解析的域名格式如下:
(pod_name).(service_name).(namespace_name).svc.cluster.local
总结
无状态:
- deployment 认为所有的pod都是一样的
- 不用考虑顺序的要求
- 不用考虑在哪个node节点上运行
- 可以随意扩容和缩容
有状态
- 实例之间有差别,每个实例都有自己的独特性,元数据不同,例如etcd,zookeeper
- 实例之间不对等的关系,以及依靠外部存储的应用。
常规service和无头服务区别
- service:一组Pod访问策略,提供cluster-IP群集之间通讯,还提供负载均衡和服务发现。
- Headless service:无头服务,不需要cluster-IP,而是直接以DNS记录的方式解析出被代理Pod的IP地址。
[root@master01 ~]# vim pod6.yaml
apiVersion: v1
kind: Pod
metadata:
name: dns-test
spec:
containers:
- name: busybox
image: busybox:1.28.4
args:
- /bin/sh
- -c
- sleep 36000
restartPolicy: Never
[root@master01 ~]# vim sts.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
ports:
- port: 80
name: web
clusterIP: None
selector:
app: nginx
---
apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
name: nginx-statefulset
namespace: default
spec:
serviceName: nginx
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
[root@master01 ~]# kubectl apply -f pod6.yaml
[root@master01 ~]# kubectl apply -f sts.yaml
[root@master01 ~]# kubectl get pods,svc
[root@master01 ~]# kubectl exec -it dns-test sh
/ # nslookup nginx-statefulset-0.nginx.default.svc.cluster.local
/ # nslookup nginx-statefulset-1.nginx.default.svc.cluster.local
/ # nslookup nginx-statefulset-2.nginx.default.svc.cluster.local
[root@master01 ~]# kubectl exec -it nginx-statefulset-0 bash
/# curl nginx-statefulset-0.nginx
/# curl nginx-statefulset-1.nginx
/# curl nginx-statefulset-2.nginx
扩展伸缩
kubectl scale sts myapp --replicas=4 #扩容副本增加到4个
kubectl get pods -w #动态查看扩容
kubectl get pv #查看pv绑定
kubectl patch sts myapp -p '{"spec":{"replicas":2}}' #打补丁方式缩容
kubectl get pods -w #动态查看缩容
DaemonSet
每个节点都运行特定的pod副本,通常用于实现系统后台任务;无状态
特点:服务是无状态的,服务必须是守护进程
会在每个节点上都放一个监听的pod,去收集数据(创建一个pod,但是每个节点上都有一个pod)
示例
[root@master01 ~]# vim ds.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: nginx-daemonSet
labels:
app: nginx
spec:
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.15.4
ports:
- containerPort: 80
[root@master01 ~]# kubectl apply -f ds.yaml
[root@master01 ~]# kubectl get pods
Job
只要完成就退出,不重启
Job分为普通任务(Job)和定时任务(CronJob) 常用于运行那些仅需要执行一次的任务
示例
[root@master01 ~]# vim 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
[root@master01 ~]# docker pull perl
[root@master01 ~]# kubectl apply -f job.yaml
[root@master01 ~]# kubectl get pods
[root@master01 ~]# kubectl logs pi-flf2s
cronjob
周期性任务,像Linux的Crontab一样。 周期性任务
[root@node02 ~]# vim cronjob.yaml
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: hello
spec:
schedule: "*/1 * * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: hello
image: busybox
imagePullPolicy: IfNotPresent
args:
- /bin/sh
- -c
- date; echo Hello from the Kubernetes cluster
restartPolicy: OnFailure
[root@node02 ~]# kubectl create -f cronjob.yaml
[root@node02 ~]# kubectl get cronjob