当前位置: 代码网 > 服务器>服务器>云虚拟主机 > Kubernetes有状态应用管理StatefulSet使用详解

Kubernetes有状态应用管理StatefulSet使用详解

2024年05月19日 云虚拟主机 我要评论
什么是有状态应用我们在《kubernetes工作负载管理》中主要介绍了无状态应用的管理,当时也有提到有状态应用,但是由于那时候还没有解释数据如何持久化就没有做深度的介绍,而在这章,我们会着重介绍如何进

什么是有状态应用

我们在《kubernetes工作负载管理》中主要介绍了无状态应用的管理,当时也有提到有状态应用,但是由于那时候还没有解释数据如何持久化就没有做深度的介绍,而在这章,我们会着重介绍如何进行有状态应用的管理。

实例之间的不等关系以及实例对外数据有依赖关系的应用,就被称为"有状态应用"。

所谓实例之间的不等关系即对分布式应用来说,各实例,各应用之间往往有比较大的依赖关系,比如某个应用必须先于其他应用启动,否则其他应用将不能启动等。

对外数据有依赖关系的应用,最显著的就是数据库应用,对于数据库应用,我们是需要持久化保存其数据的,如果是无状态应用,在数据库重启数据和应用就失去了联系,这显然是违背我们的初衷,不能投入生产的。

所以,为了解决kubernetes中有状态应用的有效支持,kubernetes使用statefulset来编排管理有状态应用。 statefulset类似于replicaset,不同之处在于它可以控制pod的启动顺序,它为每个pod设置唯一的标识。其具有一下功能:

  • 稳定的,唯一的网络标识符
  • 稳定的,持久化存储
  • 有序的,优雅部署和缩放
  • 有序的,自动滚动更新

statefulset的设计很容易理解,它把现实世界抽象为以下两种情况:

(1)、拓扑状态。这就意味着应用之间是不对等关系,应用要按某种顺序启动,即使应用重启,也必须按其规定的顺序重启,并且重启后其网络标识必须和原来的一样,这样才能保证原访问者能通过同样的方法访问新的pod;

(2)、存储状态 。这就意味着应用绑定了存储数据,不论什么时候,不论什么情况,对应用来说,只要存储里的数据没有变化,读取到的数据应该是同一份;

所以statefulset的核心功能就是以某种方式记录pod的状态,然后在pod被重新创建时,通过某种方法恢复其状态。

如何使用statefulset

在《kubernetes应用访问管理》中,我们介绍了service,它是为一组pod提供外部访问的一种方式。通常,我们使用 service访问pod有一下两种方式:

  • (1)、通过cluster ip,这个clustre ip就相当于vip,我们访问这个ip,就会将请求转发到后端pod上;
  • (2)、通过dns方式,通过这种方式首先得确保kubernetes集群中有dns服务。这个时候我们只要访问"my-service.my-namespace.svc,cluster.local",就可以访问到名为my-service的service所代理的后端pod;

而对于第二种方式,有下面两种处理方法:

  • (1)、normal service,即解析域名,得到的是cluster ip,然后再按照方式一访问;
  • (2)、headless service,即解析域名,得到的是后端某个pod的ip地址,这样就可以直接访问;

而在使用statefulset的时候,主要用到headless service,还记得headless service怎么定义的吗?

我们只需要把clusterip设置为none即可,如下:

apiversion: v1
kind: service
metadata:
  name: nginx-headless-service
  labels:
    name: nginx-headless-service
spec:
  clusterip: none
  selector:
    name: nginx
  ports:
  - port: 8000
    targetport: 80

了解了headless service,还需要了解pv、pvc是怎么使用的,如果忘记了,可以移步《kubernetes数据持久化管理》回顾,这里就不再赘述了。

下面,我们开始使用statefulset。

首先,我们创建两个个pv,因为准备为有状态应用创建两个副本,如下:

apiversion: v1
kind: persistentvolume
metadata:
  name: nginx-pv01
  labels:
    storage: pv
spec:
  accessmodes:
  - readwriteonce
  capacity:
    storage: 1gi
  persistentvolumereclaimpolicy: recycle
  nfs:
    path: /data/k8s
    server: 192.168.205.128
---
apiversion: v1
kind: persistentvolume
metadata:
  name: nginx-pv02
  labels:
    storage: pv
spec:
  accessmodes:
  - readwriteonce
  capacity:
    storage: 1gi
  persistentvolumereclaimpolicy: recycle
  nfs:
    path: /data/k8s
    server: 192.168.205.128

statefulset需要yaml文件

apiversion: v1
kind: service
metadata:
  name: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterip: none
  selector:
    app: nginx
    role: stateful
---
apiversion: apps/v1
kind: statefulset
metadata:
  name: web
spec:
  servicename: "nginx"
  replicas: 2
  selector:
    matchlabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
        role: stateful
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerport: 80
          name: web
        volumemounts:
        - name: www
          mountpath: /usr/share/nginx/html
  volumeclaimtemplates:
  - metadata:
      name: www
    spec:
      accessmodes: [ "readwriteonce" ]
      resources:
        requests:
          storage: 1gi

注意上面的 yaml 文件中和volumemounts进行关联的是一个新的属性:volumeclaimtemplates,该属性会自动声明一个 pvc 对象和 pv 进行管理,而servicename: "nginx"表示在执行控制循环的时候,用nginx这个headless service来保存pod的可解析身份。

创建完成后,可以看到会起两个pod:

$ kubectl get pod | grep web
web-0                                  1/1     running   0             2m45s
web-1                                  1/1     running   0             2m41s

从这两个pod的命令可以看到,它们的名字不像deployment那样随机生成的字符串,而是0,1这样的序号。这是因为statefulset要保证每个pod顺序,确保每次重启或者更新,每个pod依然保持以前的数据,不会错乱。所以statefulset会以[statefulset-name]-[index]规则进行命名,其中index从0开始。而且每个pod的创建是有顺序的,如上只有web-0进入running状态后,web-1才创建。

当两个pod都进入running状态后,就可以查看其各自的网络身份了,我们通过kubectl exec来查看,如下:

$ kubectl exec web-0 -- sh -c 'hostname'
web-0
$ kubectl exec web-1 -- sh -c 'hostname'
web-1

可以看到这两个pod的hostname和pod的名字是一致的,都被分配为对应的编号,接下来我们用dns的方式来访问headless service。

我们先启动一个调试pod,如下:

apiversion: v1
kind: pod
metadata:
  name: dnsutils
  namespace: default
spec:
  containers:
  - name: dnsutils
    image: lansible/dnstools
    command:
      - sleep
      - "3600"
    imagepullpolicy: ifnotpresent
  restartpolicy: always

dnsutils容器解析

$ kubectl exec -it dnsutils -- /bin/sh
/ # nslookup web-0.nginx
server:         10.96.0.10
address:        10.96.0.10#53
name:   web-0.nginx.default.svc.cluster.local
address: 172.16.51.247
/ # nslookup web-1.nginx
server:         10.96.0.10
address:        10.96.0.10#53
name:   web-1.nginx.default.svc.cluster.local
address: 172.16.51.251
/ #

从nslookup的结果分析,在访问web-0.nginx的时候解析的是web-0这个pod的ip,另一个亦然。这表示,如果我们在应用中配置web-0.nginx,则只会调用web-0这个pod,在配置有状态应用,比如zookeeper的时候,我们需要在配置文件里指定zkserver,这时候就可以指定类似:zk-0.zookeeper,zk-1.zookeeper。

如果我们现在更新statefuleset,起更新顺序是怎么样的呢?

首先,我们新开一个终端,输入以下命令用以观察:

$ kubectl get pods -w -l role=stateful
name    ready   status    restarts   age
web-0   1/1     running   0          67m
web-1   1/1     running   0          67m

然后使用以下命令更新应用的镜像,如下:

 $ kubectl set image statefulset/web nginx=nginx:1.8

然后观察web应用的更新顺序,如下:

$ kubectl get pods -w -l role=stateful
name    ready   status    restarts   age
web-0   1/1     running   0          67m
web-1   1/1     running   0          67m
web-1   1/1     terminating   0          68m
web-1   1/1     terminating   0          68m
web-1   0/1     terminating   0          68m
web-1   0/1     terminating   0          68m
web-1   0/1     terminating   0          68m
web-1   0/1     pending       0          0s
web-1   0/1     pending       0          0s
web-1   0/1     containercreating   0          0s
web-1   0/1     containercreating   0          1s
web-1   1/1     running             0          10s
web-0   1/1     terminating         0          69m
web-0   1/1     terminating         0          69m
web-0   0/1     terminating         0          69m
web-0   0/1     terminating         0          69m
web-0   0/1     terminating         0          69m
web-0   0/1     pending             0          0s
web-0   0/1     pending             0          0s
web-0   0/1     containercreating   0          0s
web-0   0/1     containercreating   0          1s
web-0   1/1     running             0          9s

从整个顺序可以看到,起更新是从后往前进行更新的,也就是先更新web-1的pod,再更新web-0的pod。通过这种严格的对应规则,statefulset就保证了pod的网络标识的稳定性,通过这个方法,就可以把pod的拓扑状态按照pod的名字+编号的方式固定起来。此外,kubernetes还为每一个pod提供了一个固定并且唯一的访问入口,即这个pod的dns记录。

由此,我们对statefulset梳理如下:

  • (1)、statefulset直接管理的是pod。这是因为statefulset里的pod实例不像replicaset中的pod实例完全一样,它们是有细微的区别,比如每个pod的名字、hostname等是不同的,而且statefulset区分这些实例的方式就是为pod加上编号;
  • (2)、kubernetes通过headless service为这个编号的pod在dns服务器中生成带同样编号的记录。只要statefulset能保证这个pod的编号不变,那么service中类似于web-0.nginx.default.svc.cluster.local这样的dns记录就不会变,而这条记录所解析的pod ip地址会随着pod的重新创建自动更新;
  • (3)、statefulset还可以为每个pod分配并创建一个和pod同样编号的pvc。这样kubernetes就可以通过persitent volume机制为这个pvc绑定对应的pv,从而保证每一个pod都拥有独立的volume。这种情况下即使pod被删除,它所对应的pvc和pv依然会保留下来,所以当这个pod被重新创建出来过后,kubernetes会为它找到同样编号的pvc,挂载这个pvc对应的volume,从而获取到以前volume以前的数据;

总结

statefulset这个控制器的主要作用之一,就是使用pod模板创建pod的时候,对它们进行编号,并且按照编号顺序完成作业,当statefulset的控制循环发现pod的实际状态和期望状态不一致的时候,也会按着顺序对pod进行操作。

当然 statefulset 还拥有其他特性,在实际的项目中,我们还是很少回去直接通过 statefulset 来部署我们的有状态服务的,除非你自己能够完全能够 hold 住,对于一些特定的服务,我们可能会使用更加高级的 operator 来部署,比如 etcd-operator、prometheus-operator 等等,这些应用都能够很好的来管理有状态的服务,而不是单纯的使用一个 statefulset 来部署一个 pod就行,因为对于有状态的应用最重要的还是数据恢复、故障转移等等。

以上就是kubernetes有状态应用管理statefulset使用详解的详细内容,更多关于kubernetes statefulset应用管理的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com