【K8S专栏】Kubernetes有状态应用管理

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: 【K8S专栏】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就行,因为对于有状态的应用最重要的还是数据恢复、故障转移等等。

相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
相关文章
|
7天前
|
Kubernetes 应用服务中间件 nginx
二进制安装Kubernetes(k8s)v1.32.0
本指南提供了一个详细的步骤,用于在Linux系统上通过二进制文件安装Kubernetes(k8s)v1.32.0,支持IPv4+IPv6双栈。具体步骤包括环境准备、系统配置、组件安装和配置等。
93 10
|
12天前
|
存储 Kubernetes 关系型数据库
阿里云ACK备份中心,K8s集群业务应用数据的一站式灾备方案
本文源自2024云栖大会苏雅诗的演讲,探讨了K8s集群业务为何需要灾备及其重要性。文中强调了集群与业务高可用配置对稳定性的重要性,并指出人为误操作等风险,建议实施周期性和特定情况下的灾备措施。针对容器化业务,提出了灾备的新特性与需求,包括工作负载为核心、云资源信息的备份,以及有状态应用的数据保护。介绍了ACK推出的备份中心解决方案,支持命名空间、标签、资源类型等维度的备份,并具备存储卷数据保护功能,能够满足GitOps流程企业的特定需求。此外,还详细描述了备份中心的使用流程、控制台展示、灾备难点及解决方案等内容,展示了备份中心如何有效应对K8s集群资源和存储卷数据的灾备挑战。
|
26天前
|
Kubernetes Cloud Native API
深入理解Kubernetes——容器编排的王者之道
深入理解Kubernetes——容器编排的王者之道
40 1
|
1月前
|
Kubernetes Cloud Native 持续交付
深入理解Kubernetes:容器编排的基石
深入理解Kubernetes:容器编排的基石
|
1月前
|
Kubernetes 负载均衡 Cloud Native
云原生应用:Kubernetes在容器编排中的实践与挑战
【10月更文挑战第27天】Kubernetes(简称K8s)是云原生应用的核心容器编排平台,提供自动化、扩展和管理容器化应用的能力。本文介绍Kubernetes的基本概念、安装配置、核心组件(如Pod和Deployment)、服务发现与负载均衡、网络配置及安全性挑战,帮助读者理解和实践Kubernetes在容器编排中的应用。
87 4
|
1月前
|
Kubernetes 监控 Cloud Native
云原生应用:Kubernetes在容器编排中的实践与挑战
【10月更文挑战第26天】随着云计算技术的发展,容器化成为现代应用部署的核心趋势。Kubernetes(K8s)作为容器编排领域的佼佼者,以其强大的可扩展性和自动化能力,为开发者提供了高效管理和部署容器化应用的平台。本文将详细介绍Kubernetes的基本概念、核心组件、实践过程及面临的挑战,帮助读者更好地理解和应用这一技术。
74 3
|
2月前
|
Kubernetes 持续交付 开发工具
ACK One GitOps:ApplicationSet UI简化多集群GitOps应用管理
ACK One GitOps新发布了多集群应用控制台,支持管理Argo CD ApplicationSet,提升大规模应用和集群的多集群GitOps应用分发管理体验。
|
2月前
|
Kubernetes API 调度
中间层 k8s(Kubernetes) 到底是什么,架构是怎么样的?
中间层 k8s(Kubernetes) 到底是什么,架构是怎么样的?
65 3
|
2月前
|
运维 Kubernetes Cloud Native
云原生时代的容器编排:Kubernetes入门与实践
【10月更文挑战第4天】在云计算的浪潮中,云原生技术以其敏捷、可扩展和高效的特点引领着软件开发的新趋势。作为云原生生态中的关键组件,Kubernetes(通常被称为K8s)已成为容器编排的事实标准。本文将深入浅出地介绍Kubernetes的基本概念,并通过实际案例引导读者理解如何利用Kubernetes进行高效的容器管理和服务部署。无论你是初学者还是有一定经验的开发者,本文都将为你打开云原生世界的大门,并助你一臂之力在云原生时代乘风破浪。
|
1月前
|
Kubernetes 监控 Cloud Native
Kubernetes集群的高可用性与伸缩性实践
Kubernetes集群的高可用性与伸缩性实践
71 1