Kubernetes 有状态应用基本概念&Nginx部署,


1、无状态与有状态

Deployment控制器设计原则:管理的所有Pod一模一样,提供同一个服务,也不考虑在哪台Node运行,可随意扩容和缩容。这种应用称为“无状态”,例如Web服务

但是,在实际的场景中,并不能满足所有应用,尤其是分布式应用,会部署多个实例,这些实例之间往往有依赖关系,例如主从关系、主备关系,这种应用称为“有状态”,例如MySQL主从、Etcd集群、redis-cluster 等等

2、StatefulSet 控制器概述

StatefulSet控制器用于部署有状态应用,满足一些有状态应用的需求:

  • Pod有序的部署、扩容、删除和停止
  •  Pod分配一个稳定的且唯一的网络标识
  •  Pod分配一个独享的存储

3、StatefulSet 控制器:网络标识

稳定的网络标识:

使用无头服务 Headless Service(相比普通Service只是将spec.clusterIP定义为None,也就是没有clusterIP,使用endport 来通信)来维护Pod网络身份,会为每个Pod分配一个数字编号并且按照编号顺序部署。还需要在StatefulSet添加serviceName: “nginx”字段指定StatefulSet控制器要使用这个Headless Service。

稳定主要体现在主机名和Pod A记录:

  • 主机名:<statefulset名称>-<编号>
  • Pod DNS A记录:<statefulset名称-编号>.<service-name> .<namespace>.svc.cluster.local (POD 之间通过DNS A 记录通信)

例如: web-0.web.default.svc.cluster.local

  1. 备注: 
  2.  
  3. A记录: 将域名指向一个IPv4地址(例如:100.100.100.100),需要增加A记录 
  4. CNAME记录: 如果将域名指向一个域名,实现与被指向域名相同的访问效果,需要增加CNAME记录。这个域名一般是主机服务商提供的一个域名 
  5. MX记录: 建立电子邮箱服务,将指向邮件服务器地址,需要设置MX记录。建立邮箱时,一般会根据邮箱服务商提供的MX记录填写此记录 
  6. NS记录: 域名解析服务器记录,如果要将子域名指定某个域名服务器来解析,需要设置NS记录 
  7. TXT记录: 可任意填写,可为空。一般做一些验证记录时会使用此项,如:做SPF(反垃圾邮件)记录 
  8. AAAA记录: 将主机名(或域名)指向一个IPv6地址(例如:ff03:0:0:0:0:0:0:c1),需要添加AAAA记录 

案例:

通过创建一个 nginx 应用的statefluset 控制器

创建 Headless Service ,定义 clusterIP: None (表示K8S 不会在给这个service 去颁发一个clusterIP 了;相比 deployment 控制器 的每个POD 都是相同的,而 statefuset 控制器的每个POD 都是有状态的

需要单独去访问 )

  1. [root@master-1 statefulset]# vim service.yaml  
  2. apiVersion: v1 
  3. kind: Service 
  4. metadata: 
  5.   name: web 
  6. spec: 
  7.   clusterIP: None 
  8.   ports: 
  9.   - protocol: TCP 
  10.     port: 80 
  11.   selector: 
  12.     app: nginx 
  13.  
  14. [root@master-1 statefulset]# kubectl apply -f service.yaml  
  15.  
  16. [root@master-1 statefulset]# kubectl get service 
  17. NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE 
  18. web          ClusterIP   None         <none>        80/TCP    8m18s 

#创建statefulset,指定serviceName

  1. [root@master-1 statefulset]# cat statefulset.yaml  
  2. apiVersion: apps/v1 
  3. kind: StatefulSet 
  4. metadata: 
  5.   name: web 
  6. spec: 
  7.   serviceName: "web" 
  8.   replicas: 3  
  9.   selector: 
  10.     matchLabels: 
  11.       app: nginx  
  12.   template: 
  13.     metadata: 
  14.       labels: 
  15.         app: nginx 
  16.     spec: 
  17.       containers: 
  18.       - name: nginx 
  19.         image: nginx  
  20.         ports: 
  21.         - containerPort: 80 
  22.           name: web 
  23.            
  24.  
  25. [root@master-1 statefulset]# kubectl apply -f statefulset.yaml 
  26. statefulset.apps/web created 
  27.  
  28.  
  29. #发现pod名称 带有序号 
  30. [root@master-1 statefulset]# kubectl get pods 
  31. NAME    READY   STATUS    RESTARTS   AGE 
  32. web-0   1/1     Running   0          16s 
  33. web-1   1/1     Running   0          13s 
  34. web-2   1/1     Running   0          6s 
  35.  
  36.  
  37.  
  38.  
  39. #查了POD的主机名 
  40. 主机名 默认与 POD 名一致,即使POD 飘逸到其他node 上 或者删除后重建  主机名 都是和POD  名一致。有个稳定的主机名 
  41.  
  42.  
  43. [root@master-1 statefulset]# kubectl exec -it web-0 -- hostname 
  44. web-0 
  45. [root@master-1 statefulset]# kubectl exec -it web-1 -- hostname 
  46. web-1 
  47. [root@master-1 statefulset]# kubectl exec -it web-2 -- hostname 
  48. web-2 
  49.  
  50.  
  51.  
  52. #临时启动一个busybox pod ,测试dns 解析(注意这里的busybox版本为1.28.4 最新版的busybox nslookup 会有问题) 
  53. [root@master-1 statefulset]# kubectl run -it dns-test --rm --image=busybox:1.28.4 -- sh 
  54. / # nslookup web 
  55. Server:    10.0.0.2 
  56. Address 1: 10.0.0.2 kube-dns.kube-system.svc.cluster.local 
  57.  
  58. Name:      web 
  59. Address 1: 10.244.2.114 web-2.web.default.svc.cluster.local 
  60. Address 2: 10.244.2.113 web-0.web.default.svc.cluster.local 
  61. Address 3: 10.244.1.65 web-1.web.default.svc.cluster.local 
  62.  
  63.  
  64. 可以看到解析出3条记录出来,解析出对应的三个Pod IP记录,其他Pod可使用这个名称访问 
  65.  
  66.  
  67. 模拟测试删除这些 pod ,升级镜像版本,发现pod ip虽然发生变化,但是 主机名,Pod DNS A记录 不会发生变化 
  68. / # nslookup web 
  69. Server:    10.0.0.2 
  70. Address 1: 10.0.0.2 kube-dns.kube-system.svc.cluster.local 
  71.  
  72. Name:      web 
  73. Address 1: 10.244.2.116 web-2.web.default.svc.cluster.local 
  74. Address 2: 10.244.2.115 web-0.web.default.svc.cluster.local 
  75. Address 3: 10.244.1.66  web-1.web.default.svc.cluster.local 

这个就验证了 statefulset 的 Pod是 有序的部署、扩容、删除和停止 且 给每一个POD 分配一个稳定的且唯一的网络标识

4、StatefulSet 控制器:独享存储

独享存储:StatefulSet的存储卷使用VolumeClaimTemplate创建,称为卷申请模板,当StatefulSet使用VolumeClaimTemplate创建一个PersistentVolume时,

同样也会为每个Pod分配并创建一个编号的PVC,每个PVC绑定对应的PV,从而保证每个Pod都拥有独立的存储。

在创建StatefulSet 控制器 独享存储前,需要先定义好存储卷,使用pv 作为持久化存储卷,后端存储为NFS

这里采用动态PV 的方式(NFS server 搭建的过程省略)

一、部署NFS服务器

  1. #服务器安装nfs服务,提供nfs存储功能 
  2. 1、安装nfs-utils 
  3. yum install nfs-utils (centos) 
  4. 或者  apt-get install nfs-kernel-server (ubuntu) 
  5.  
  6. 2、启动服务 
  7. systemctl enable nfs-server 
  8. systemctl start nfs-server 
  9.  
  10. 3、创建共享目录完成共享配置 
  11. mkdir /home/nfs   #创建共享目录 
  12.  
  13. 4、编辑共享配置 
  14. vim /etc/exports                                            
  15. #语法格式:    共享文件路径     客户机地址(权限)     #这里的客户机地址可以是IP,网段,域名,也可以是任意* 
  16. /home/nfs  *(rw,async,no_root_squash) 
  1. 服务自检命令  
  2. exportfs -arv 
  3.  
  4.  
  5. 5、重启服务 
  6. systemctl restart nfs-server 
  7.  
  8.  
  9. 6、本机查看nfs 共享目录 
  10. #showmount -e 服务器IP地址 (如果提示命令不存在,则需要yum install showmount) 
  11.  
  12. showmount -e 127.0.0.1 
  13. /home/nfs/nginx * 
  14.  
  15.  
  16.  
  17. 7、客户端模拟挂载[所有k8s的节点都需要安装客户端] 
  18. [root@master-1 ~]# yum install nfs-utils (centos) 
  19. 或者  apt-get install nfs-common (ubuntu) 
  20. [root@master-1 ~]# mkdir /test 
  21. [root@master-1 ~]# mount -t nfs 172.16.201.209:/home/nfs /test 
  22.  
  23. #取消挂载 
  24. [root@master-1 ~]# umount /test 

二、配置PV 动态供给(NFS StorageClass),创建pvc

#部署NFS实现自动创建PV插件: 一共设计到4个yaml 文件 ,官方的文档有详细的说明

https://github.com/kubernetes-incubator/external-storage

Kubernetes 有状态应用基本概念&Nginx部署
Kubernetes 有状态应用基本概念&Nginx部署
  1. root@k8s-master1:~ # mkdir  /root/pvc 
  2. root@k8s-master1:~ # cd   /root/pvc 

创建rbac.yaml 文件

  1. root@k8s-master1:pvc # cat rbac.yaml  
  2. kind: ServiceAccount 
  3. apiVersion: v1 
  4. metadata: 
  5.   name: nfs-client-provisioner 
  6. --- 
  7. kind: ClusterRole 
  8. apiVersion: rbac.authorization.k8s.io/v1 
  9. metadata: 
  10.   name: nfs-client-provisioner-runner 
  11. rules: 
  12.   - apiGroups: [""] 
  13.     resources: ["persistentvolumes"] 
  14.     verbs: ["get", "list", "watch", "create", "delete"] 
  15.   - apiGroups: [""] 
  16.     resources: ["persistentvolumeclaims"] 
  17.     verbs: ["get", "list", "watch", "update"] 
  18.   - apiGroups: ["storage.k8s.io"] 
  19.     resources: ["storageclasses"] 
  20.     verbs: ["get", "list", "watch"] 
  21.   - apiGroups: [""] 
  22.     resources: ["events"] 
  23.     verbs: ["create", "update", "patch"] 
  24. --- 
  25. kind: ClusterRoleBinding 
  26. apiVersion: rbac.authorization.k8s.io/v1 
  27. metadata: 
  28.   name: run-nfs-client-provisioner 
  29. subjects: 
  30.   - kind: ServiceAccount 
  31.     name: nfs-client-provisioner 
  32.     namespace: default 
  33. roleRef: 
  34.   kind: ClusterRole 
  35.   name: nfs-client-provisioner-runner 
  36.   apiGroup: rbac.authorization.k8s.io 
  37. --- 
  38. kind: Role 
  39. apiVersion: rbac.authorization.k8s.io/v1 
  40. metadata: 
  41.   name: leader-locking-nfs-client-provisioner 
  42. rules: 
  43.   - apiGroups: [""] 
  44.     resources: ["endpoints"] 
  45.     verbs: ["get", "list", "watch", "create", "update", "patch"] 
  46. --- 
  47. kind: RoleBinding 
  48. apiVersion: rbac.authorization.k8s.io/v1 
  49. metadata: 
  50.   name: leader-locking-nfs-client-provisioner 
  51. subjects: 
  52.   - kind: ServiceAccount 
  53.     name: nfs-client-provisioner 
  54.     # replace with namespace where provisioner is deployed 
  55.     namespace: default 
  56. roleRef: 
  57.   kind: Role 
  58.   name: leader-locking-nfs-client-provisioner 
  59.   apiGroup: rbac.authorization.k8s.io 

创建deployment.yaml 文件

#官方默认的镜像地址,国内可能无法下载,可以使用 image:

fxkjnj/nfs-client-provisioner:latest

#定义NFS 服务器的地址,共享目录名称

  1. root@k8s-master1:pvc # cat deployment.yaml  
  2. apiVersion: v1 
  3. kind: ServiceAccount 
  4. metadata: 
  5.   name: nfs-client-provisioner 
  6. --- 
  7. kind: Deployment 
  8. apiVersion: apps/v1  
  9. metadata: 
  10.   name: nfs-client-provisioner 
  11. spec: 
  12.   replicas: 1 
  13.   strategy: 
  14.     type: Recreate 
  15.   selector: 
  16.     matchLabels: 
  17.       app: nfs-client-provisioner 
  18.   template: 
  19.     metadata: 
  20.       labels: 
  21.         app: nfs-client-provisioner 
  22.     spec: 
  23.       serviceAccountName: nfs-client-provisioner 
  24.       containers: 
  25.         - name: nfs-client-provisioner 
  26.           image: fxkjnj/nfs-client-provisioner:latest 
  27.           volumeMounts: 
  28.             - name: nfs-client-root 
  29.               mountPath: /persistentvolumes 
  30.           env: 
  31.             - name: PROVISIONER_NAME 
  32.               value: fuseim.pri/ifs 
  33.             - name: NFS_SERVER 
  34.               value: 172.16.201.209  
  35.             - name: NFS_PATH 
  36.               value: /home/nfs 
  37.       volumes: 
  38.         - name: nfs-client-root 
  39.           nfs: 
  40.             server: 172.16.201.209 
  41.             path: /home/nfs 

创建class.yaml

# archiveOnDelete: "true" 表示当PVC 删除后,后端数据不直接删除,而是归档

  1. root@k8s-master1:pvc # cat class.yaml  
  2. apiVersion: storage.k8s.io/v1 
  3. kind: StorageClass 
  4. metadata: 
  5.   name: managed-nfs-storage 
  6. provisioner: fuseim.pri/ifs # or choose another name, must match deployment's env PROVISIONER_NAME' 
  7. parameters: 
  8.   archiveOnDelete: "true" 

查看存储类

  1. root@k8s-master1:~/kubernetes/redis# kubectl get sc 
  2. NAME                  PROVISIONER      RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE 
  3. managed-nfs-storage   fuseim.pri/ifs   Delete          Immediate           false                  6s 

基于上面的内容创建 statefulset.yaml 文件

  1. root@k8s-master1:~ # mkdir /root/statefulset 
  2. root@k8s-master1:~ # cd  /root/statefulset 
  3. root@k8s-master1:statefulset # vim  statefulset.yaml 
  4. apiVersion: apps/v1 
  5. kind: StatefulSet 
  6. metadata: 
  7.   name: web 
  8. spec: 
  9.   serviceName: "web" 
  10.   replicas: 3  
  11.   selector: 
  12.     matchLabels: 
  13.       app: nginx  
  14.   template: 
  15.     metadata: 
  16.       labels: 
  17.         app: nginx 
  18.     spec: 
  19.       containers: 
  20.       - name: nginx 
  21.         image: nginx:1.16  
  22.         ports: 
  23.         - containerPort: 80 
  24.           name: web 
  25.         volumeMounts: 
  26.         - name: nginx-pvc       #指定PVC名称 
  27.           mountPath: /usr/share/nginx/html 
  28.   volumeClaimTemplates:     #相当于pvc模板 
  29.   - metadata: 
  30.       name: nginx-pvc           #创建的PVC名称 
  31.     spec: 
  32.       storageClassName: "managed-nfs-storage"   #指定动态PV名称 
  33.       accessModes: 
  34.       - ReadWriteOnce           #访问模式,读写在单台机器 
  35.       resources: 
  36.         requests: 
  37.           storage: 1Gi 
  38.  
  39.  
  40. root@k8s-master1:statefulset #  kubectl apply -f statefulset.yaml 
  41. statefulset.apps/web created 
  42.  
  43. root@k8s-master1:~/kubernetes/statefulset# kubectl get pv 
  44. NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                     STORAGECLASS          REASON   AGE 
  45. pvc-8eacbe25-3875-4f78-91ca-ba83b6967a8a   100Gi      RWX            Delete           Bound    redis/nfs-redis           managed-nfs-storage            6d 
  46. pvc-935033b7-9ac8-4346-8543-1f95492dcde9   1Gi        RWO            Delete           Bound    default/nginx-pvc-web-1   managed-nfs-storage            39s 
  47. pvc-bd3a8c59-b66d-457b-a6f2-90f3b7f9ebf0   1Gi        RWO            Delete           Bound    default/nginx-pvc-web-2   managed-nfs-storage            19s 
  48. pvc-be5cf42a-aeaa-4667-901c-77e1d2350f49   1Gi        RWO            Delete           Bound    default/nginx-pvc-web-0   managed-nfs-storage            61s 
  49.  
  50.  
  51.  
  52. root@k8s-master1:~/kubernetes/statefulset# kubectl get pvc 
  53. NAME              STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS          AGE 
  54. nginx-pvc-web-0   Bound    pvc-be5cf42a-aeaa-4667-901c-77e1d2350f49   1Gi        RWO            managed-nfs-storage   82s 
  55. nginx-pvc-web-1   Bound    pvc-935033b7-9ac8-4346-8543-1f95492dcde9   1Gi        RWO            managed-nfs-storage   61s 
  56. nginx-pvc-web-2   Bound    pvc-bd3a8c59-b66d-457b-a6f2-90f3b7f9ebf0   1Gi        RWO            managed-nfs-storage   40s 
  57.  
  58. oot@k8s-master1:~/kubernetes/statefulset# kubectl get pods --show-labels  
  59. NAME                                      READY   STATUS    RESTARTS   AGE     LABELS 
  60. web-0                                     1/1     Running   0          4m50s   app=nginx,controller-revision-hash=web-b56c497b,statefulset.kubernetes.io/pod-name=web-0 
  61. web-1                                     1/1     Running   0          4m29s   app=nginx,controller-revision-hash=web-b56c497b,statefulset.kubernetes.io/pod-name=web-1 
  62. web-2                                     1/1     Running   0          4m8s    app=nginx,controller-revision-hash=web-b56c497b,statefulset.kubernetes.io/pod-name=web-2 
  63.  
  64. #分别进入到3个pod 中,写入一个数据,验证各自的独享存储 
  65. root@k8s-master1:~/kubernetes/statefulset# kubectl  get pods -o wide --selector app=nginx 
  66. NAME                                READY   STATUS    RESTARTS   AGE     IP               NODE        NOMINATED NODE   READINESS GATES 
  67. web-0                               1/1     Running   0          7m6s    10.244.169.179   k8s-node2   <none>           <none> 
  68. web-1                               1/1     Running   0          6m45s   10.244.107.228   k8s-node3   <none>           <none> 
  69. web-2                               1/1     Running   0          6m24s   10.244.169.180   k8s-node2   <none>           <none> 
  70.  
  71.  
  72. [root@master-1 ~]# kubectl exec -it web-0 -- bash -c "echo 'congratulations  web-0 for k8s' > /usr/share/nginx/html/index.html" 
  73. [root@master-1 ~]# kubectl exec -it web-1 -- bash -c "echo 'congratulations  web-1 for k8s' > /usr/share/nginx/html/index.html" 
  74. [root@master-1 ~]# kubectl exec -it web-2 -- bash -c "echo 'congratulations  web-2 for k8s' > /usr/share/nginx/html/index.html" 
  75.  
  76.  
  77. #直接访问pod IP 测试内容: 
  78. root@k8s-master1:~/kubernetes/statefulset# curl 10.244.169.179 
  79. congratulations  web-0 for k8s 
  80.  
  81. root@k8s-master1:~/kubernetes/statefulset# curl  10.244.107.228 
  82. congratulations  web-1 for k8s 
  83.  
  84. root@k8s-master1:~/kubernetes/statefulset#  curl 10.244.169.180 
  85. congratulations  web-2 for k8s 

删除statefulset

删除statefulset 有两张方法,级联删除 和 非级联删除

  • 使用非级联删除 statefulset 时,statefulset 的POD 不会被删除
  • 使用级联删除时,statefulset 和 pod 都会被删除
  1. (1)、非级联删除 
  2.  
  3. 使用kubectl  delete statefulset XXXX 删除 statefulset ,只需要提供 --cascade=false 参数,就会采用非联机删除,此时删除statefulset 不会删除pod 
  4.  
  5. kubectl delete statefulset web --cascade=false 
  6.  
  7.  
  8. (2)、级联删除 
  9. 省略 --cascade=false 参数 即可 
  10.  
  11. kubectl delete statefulset web  

相关内容