经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 数据库/运维 » Linux/Shell » 查看文章
Kubernetes 实战——有状态应用(StatefulSet)
来源:cnblogs  作者:LB477  时间:2021/6/21 10:00:26  对本文有异议

一、简介

  • 有状态实例:新实例和旧实例需要有相同的名称、网络标识和状态
  • 无状态实例:可随时被替换

1. ReplicaSet 和有状态 Pod

ReplicaSet 通过 Pod 模板创建多个 Pod 副本,这些副本除了名字和 IP 地址不同,没有其他差异。若 Pod 模板指定了 PVC,则其创建的所有 Pod 共享相同的 PVC 和 PV

集群应用可能要求实例具有唯一的网络标识。可针对每个实例创建一个独立的 Service 来提供稳定的网络地址(因为服务 IP 固定)。但 Pod 无法获取该 IP,不能在别的 Pod 里通过 IP 自行注册

2. 了解 StatefulSet

  • 每一个实例不可替代,都拥有稳定的名字(从零开始的顺序索引)和状态(独立的数据卷)
  • 有状态的 Pod 有时需要通过其主机名来定位。因为彼此状态不同,通常希望操作的是指定的那个
    • 一个 StatefulSet 常要求创建一个用来记录每个 Pod 网络标记的 headless Service。通过该 Service,每个 Pod 将拥有独立的 DNS 记录,这样集群中的 Pod 或客户端可以通过主机名来定位
    • 如一个 default 命名空间,名为 foo 的服务,它的一个 Pod 名为 a-0,就可以通过a-0.foo.default.svc.cluster.local来定位该 Pod
    • 也可以通过 DNS 服务查找域名foo.default.svc.cluster.local对应的所有 SRV 记录,获取一个 StatefulSet 所有 Pod 的信息
  • 当 StatefulSet 管理的 Pod 消失后,会重启一个标识完全一致的 Pod 替换(不一定在同一个节点)
  • 扩容用下一个索引值,缩容先删除最高索引值,扩/缩容都是逐步进行的K8s 保证两个拥有相同标记和绑定相同 PVC 的有状态 Pod 不会同时运行
    • 若有不健康实例,则不允许做缩容操作(避免一次删除两个)
    • 缩容只删除 Pod,保留创建的持久卷声明(PVC 被删除后,与之绑定的 PV 也会被回收或删除),需要手动删除。再扩容会重新挂载上

3. 专属存储

  • 有状态的 Pod 存储必须是持久的,且与 Pod 解耦。即 StatefulSet 的 Pod 需要关联到不同的持久卷声明,且与独立的持久卷对应
  • 持久卷可以预先创建,也可以由持久卷的动态供应机制实时创建

卷声明模板

StatefulSet 可以有一个或多个卷声明模板,会在创建 Pod 前创建持久卷声明,并绑定到 Pod 实例上

二、使用 StatefulSet

1. 创建

① 容器准备

docker.io/luksa/kubia-pet

  • POST 请求将 body 中的数据存储到 /var/data/kubia.txt
  • GET 请求返回主机名和存储的数据

② 手动创建存储卷

  1. apiVersion: v1
  2. kind: List
  3. items:
  4. - apiVersion: v1
  5. kind: PersistentVolume
  6. metadata:
  7. name: pv-a # 持久卷名称 pv-a、pv-b、pv-c
  8. spec:
  9. capacity:
  10. storage: 1Mi # 持久卷大小
  11. accessModes:
  12. - ReadWriteOnce
  13. persistentVolumeReclaimPolicy: Recycle # 卷被声明释放后,空间被回收再利用
  14. nfs: # 卷使用 nfs 持久磁盘。见 https://www.cnblogs.com/lb477/p/14713883.html
  15. server: 192.168.11.210
  16. path: "/nfs/pv-a"
  17. ...

③ 创建控制 Service

  1. apiVersion: v1
  2. kind: Service
  3. metadata:
  4. name: kubia
  5. spec:
  6. clusterIP: None # StatefulSet 的控制 Service 必须是 headless 模式
  7. selector:
  8. app: kubia
  9. ports:
  10. - name: http
  11. port: 80

④ 创建 StatefulSet

  1. apiVersion: apps/v1
  2. kind: StatefulSet
  3. metadata:
  4. name: kubia
  5. spec:
  6. selector:
  7. matchLabels:
  8. app: kubia
  9. serviceName: kubia
  10. replicas: 2
  11. template:
  12. metadata:
  13. labels:
  14. app: kubia
  15. spec:
  16. containers:
  17. - name: kubia
  18. image: luksa/kubia-pet
  19. ports:
  20. - name: http
  21. containerPort: 8080
  22. volumeMounts:
  23. - name: data
  24. mountPath: /var/data # Pod 中的容器会把 pvc 数据卷嵌入指定目录
  25. volumeClaimTemplates: # 创建持久卷声明的模板,会为每个 Pod 创建并关联一个 pvc
  26. - metadata:
  27. name: data
  28. spec:
  29. resources:
  30. requests:
  31. storage: 1Mi
  32. accessModes:
  33. - ReadWriteOnce

⑤ 查看创建结果

  1. $ kubectl get pod -w
  2. NAME READY STATUS RESTARTS AGE
  3. kubia-0 0/1 ContainerCreating 0 35s
  4. kubia-0 1/1 Running 0 53s
  5. kubia-1 0/1 Pending 0 0s
  6. kubia-1 0/1 ContainerCreating 0 3s
  7. kubia-1 1/1 Running 0 20s
  8. $ kubectl get pv
  9. NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
  10. pv-a 1Mi RWO Recycle Bound default/data-kubia-0 18m
  11. pv-b 1Mi RWO Recycle Bound default/data-kubia-1 18m
  12. pv-c 1Mi RWO Recycle Available 18m
  13. $ kubectl get pvc
  14. NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
  15. data-kubia-0 Bound pv-a 1Mi RWO 2m3s
  16. data-kubia-1 Bound pv-b 1Mi RWO 70s

2. 测试

  • 直连 Pod 来访问:借助另一个 Pod,在其内部运行 curl 命令或使用端口转发
  • 通过 API 服务器与 Pod 通信:API 服务器可通过代理直接连接到指定 Pod:可通过访问<apiServerHost>:<port>/api/v1/namespaces/default/pods/kubia-0/proxy/<path>请求 Pod,但 API 服务器有安全保障,需要在每次请求中添加授权令牌。因此可使用 kubectl 代理和 API 服务器代理与 Pod 通信:
  1. $ kubectl proxy
  2. Starting to serve on 127.0.0.1:8001
  3. $ curl localhost:8001/api/v1/namespaces/default/pods/kubia-0/proxy/
  4. You've hit kubia-0
  5. Data stored on this pod: No data posted yet

测试

  1. # 1. 应用的状态独立
  2. $ curl -X POST -d "Hello kubia-0" localhost:8001/api/v1/namespaces/default/pods/kubia-0/proxy/
  3. Data stored on pod kubia-0
  4. $ curl localhost:8001/api/v1/namespaces/default/pods/kubia-0/proxy/
  5. You've hit kubia-0
  6. Data stored on this pod: Hello kubia-0
  7. $ curl localhost:8001/api/v1/namespaces/default/pods/kubia-1/proxy/
  8. You've hit kubia-1
  9. Data stored on this pod: No data posted yet
  10. # 2. 重新启动一个完全相同的 Pod(新的 Pod 可能被调度到其他节点)
  11. $ kubectl delete pod kubia-0
  12. pod "kubia-0" deleted
  13. $ kubectl get pod
  14. NAME READY STATUS RESTARTS AGE
  15. kubia-0 0/1 ContainerCreating 0 1s
  16. kubia-1 1/1 Running 0 106m
  17. $ curl localhost:8001/api/v1/namespaces/default/pods/kubia-0/proxy/
  18. You've hit kubia-0
  19. Data stored on this pod: Hello kubia-0

暴露 StatefulSet 的 Pod

  1. # 一个常规的 ClusterIP Service,只能在集群内部访问
  2. apiVersion: v1
  3. kind: Service
  4. metadata:
  5. name: kubia-public
  6. spec:
  7. selector:
  8. app: kubia
  9. ports:
  10. - port: 80
  11. targetPort: 8080
  1. $ curl localhost:8001/api/v1/namespaces/default/services/kubia-public/proxy/
  2. You've hit kubia-1 / 0

3. 发现伙伴节点

SRV 记录指向提供指定服务的服务器的主机名和端口号

获取 StatefulSet 里的所有 Pod 信息

  1. # 运行一个名为 srvlookup 的一次性 Pod,关联控制台并在终止后立即删除
  2. $ kubectl run -it srvlookup --image=tutum/dnsutils --rm --restart=Never -- dig SRV kubia.default.svc.cluster.local
  3. ;; ANSWER SECTION:
  4. kubia.default.svc.cluster.local. 30 IN SRV 0 50 80 kubia-0.kubia.default.svc.cluster.local.
  5. kubia.default.svc.cluster.local. 30 IN SRV 0 50 80 kubia-1.kubia.default.svc.cluster.local.
  6. ;; ADDITIONAL SECTION:
  7. kubia-0.kubia.default.svc.cluster.local. 30 IN A 10.244.0.15
  8. kubia-1.kubia.default.svc.cluster.local. 30 IN A 10.244.0.16
  9. ...
  10. # 返回的 SRV 记录顺序随机

让节点返回所有集群节点的数据

4. 处理节点失效

可通过关闭节点的 eth0 网络接口模拟节点的网络断开

  • 当一个节点失效,运行在该节点上的 Kubelet 服务就无法与 K8s API 服务器通信,即无法汇报节点及其 Pod 的状态
  • StatefulSet 在明确知道一个 Pod 不再运行之前,不会创建一个替换的 Pod
  • 一段时间后,该节点状态变为 NotReady,Pod 状态变为 Unknown
    • 若节点恢复,汇报状态后 Pod 会被重新标记为 Running
    • 若 Pod 的 Unknown 状态持续几分钟(可配置)后,主节点就会将 Pod 从节点驱逐(删除 Pod 资源)
      • 若此时 describe Pod,可看到其状态为 Terminating,即已经被标记为删除。但由于节点不能通信,该 Pod 仍会一直运行
    • 可强制删除:kubectl delete pod kubia-0 --force --grace-period 0(除非确定节点不再运行,否则不要强制删除有状态的 Pod)

原文链接:http://www.cnblogs.com/lb477/p/14898850.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号