深入浅出 Kubernetes:StatefulSet 概念理解与实践

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: 深入浅出 Kubernetes:StatefulSet 概念理解与实践一 背景知识及相关概念StatefulSet 的设计其实非常容易理解。它把真实世界里的应用状态,抽象为了两种情况:拓扑状态。

深入浅出 Kubernetes:StatefulSet 概念理解与实践

一 背景知识及相关概念

StatefulSet 的设计其实非常容易理解。它把真实世界里的应用状态,抽象为了两种情况:

拓扑状态。这种情况意味着,应用的多个实例之间不是完全对等的关系。这些应用实例,必须按照某些顺序启动,比如应用的主节点 A 要先于从节点 B 启动。而如果你把 A 和 B 两个 Pod 删除掉,它们再次被创建出来时也必须严格按照这个顺序才行。并且,新创建出来的 Pod,必须和原来 Pod 的网络标识一样,这样原先的访问者才能使用同样的方法,访问到这个新 Pod。

存储状态。这种情况意味着,应用的多个实例分别绑定了不同的存储数据。对于这些应用实例来说,Pod A 第一次读取到的数据,和隔了十分钟之后再次读取到的数据,应该是同一份,哪怕在此期间 Pod A 被重新创建过。这种情况最典型的例子,就是一个数据库应用的多个存储实例。

StatefulSet 的核心功能,就是通过某种方式记录这些状态,然后在 Pod 被重新创建时,能够为新 Pod 恢复这些状态。

这个 Service 又是如何被访问的呢?

第一种方式,是以 Service 的 VIP(Virtual IP,即:虚拟 IP)方式。比如:当我访问 172.20.25.3 这个 Service 的 IP 地址时,172.20.25.3 其实就是一个 VIP,它会把请求转发到该 Service 所代理的某一个 Pod 上。

第二种方式,就是以 Service 的 DNS 方式。比如:这时候,只要我访问“my-svc.my-namespace.svc.cluster.local”这条 DNS 记录,就可以访问到名叫 my-svc 的 Service 所代理的某一个 Pod。

二 StatefulSet 的两种结构

2.1 拓扑结构

让我们来看一下以下例子:

apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  selector:
    app: nginx
  ports:
    - port: 80
      name: web
  clusterIP: None

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web-server-gysl
  labels:
    app: nginx
spec:
  serviceName: "nginx"
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      restartPolicy: Always
      containers:
        - name: web-server
          image: nginx:1.16.0
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 80
              name: web-port

这些 Pod 的创建,也是严格按照编号顺序进行的。比如,在 web-server-gysl-0 进入到 Running 状态、并且细分状态(Conditions)成为 Ready 之前,web-server-gysl-1 会一直处于 Pending 状态。

使用以下命令测试:

kubectl run -i --tty  --image toolkit:v1.0.0821 dns-test --restart=Never --rm /bin/bash
[root@dns-test /]# nslookup web-server-gysl-0.nginx
Server:         10.0.0.2
Address:        10.0.0.2#53

Name:   web-server-gysl-0.nginx.default.svc.cluster.local
Address: 172.20.25.3

[root@dns-test /]# nslookup web-server-gysl-1.nginx
Server:         10.0.0.2
Address:        10.0.0.2#53

Name:   web-server-gysl-1.nginx.default.svc.cluster.local
Address: 172.20.72.7

[root@dns-test /]# nslookup nginx
Server:         10.0.0.2
Address:        10.0.0.2#53

Name:   nginx.default.svc.cluster.local
Address: 172.20.72.7
Name:   nginx.default.svc.cluster.local
Address: 172.20.25.3

由于最近版本的 busybox 有坑,我自己制作了一个 DNS 测试工具,Dockerfile 如下:

FROM centos:7.6.1810
RUN  yum -y install bind-utils
CMD  ["/bin/bash","-c","while true;do sleep 60000;done"]

回到 Master 节点看一下:

$ kubectl get pod -o wide
NAME                READY   STATUS    RESTARTS   AGE   IP            NODE          NOMINATED NODE   READINESS GATES
web-server-gysl-0   1/1     Running   0          43m   172.20.25.3   172.31.2.12   <none>           <none>
web-server-gysl-1   1/1     Running   0          42m   172.20.72.7   172.31.2.11   <none>           <none>
$ kubectl get svc -o wide
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE   SELECTOR
nginx        ClusterIP   None         <none>        80/TCP    43m   app=nginx

当我们在集群内部分别 ping 域名 web-server-gysl-0.nginx.default.svc.cluster.local 和 web-server-gysl-1.nginx.default.svc.cluster.local 时,正常返回了对应的 Pod IP, 在 ping 域名 nginx.default.svc.cluster.local 时,则随机返回2个 Pod IP 中的一个。完全印证了上文所述内容。

在上述操作过程中,我随机删除了这些 Pod 中的某一个或几个,稍后再次来查看的时候,新创建的 Pod 依然按照之前的编号进行了编排。

此外,我将 StatefulSet 的一个 Pod 所在的集群内节点下线,再次查看 Pod 的情况,系统在其他节点上以原 Pod 的名称迅速创建了新的 Pod。编号都是从 0 开始累加,与 StatefulSet 的每个 Pod 实例一一对应,绝不重复。

2.2 存储结构

由于测试环境资源有限,原计划使用 rook-ceph 来进行实验的,无奈使用 NFS 来进行实验。 Ceph 创建 PV 的相关 yaml 如下:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-gysl
  labels:
    type: local
spec:
  capacity:
    storage: 2Gi
  accessModes:
    - ReadWriteOnce
  rbd:
    monitors:
      - '172.31.2.11:6789'
      - '172.31.2.12:6789'
    pool: data
    image: data
    fsType: xfs
    readOnly: true
    user: admin
    keyring: /etc/ceph/keyrin

NFS 实验相关 yaml:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-nfs-gysl-0
  labels:
    environment: test
spec:
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Recycle
  storageClassName: nfs
  nfs:
    path: /data-0
    server: 172.31.2.10
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-nfs-gysl-1
  labels:
    environment: test
spec:
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Recycle
  storageClassName: nfs
  nfs:
    path: /data-1
    server: 172.31.2.10
---
apiVersion: storage.k8s.io/v1beta1
kind: StorageClass
metadata:
  name: nfs
provisioner: fuseim.pri/ifs
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: statefulset-pvc-gysl
spec:
  replicas: 2
  serviceName: "gysl-web"
  selector:
    matchLabels:
      app: pod-gysl
  template:
    metadata:
      name: web-pod
      labels:
        app: pod-gysl
    spec:
      containers:
        - name: nginx
          image: nginx
          imagePullPolicy: IfNotPresent
          ports:
            - name: web-port
              containerPort: 80
          volumeMounts:
            - name: www-vct
              mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
    - metadata:
        name: www-vct
        annotations:
          volume.beta.kubernetes.io/storage-class: "nfs"
      spec:
        accessModes:
          - ReadWriteOnce
        resources:
          requests:
            storage: 1Gi
        storageClassName: nfs
---
apiVersion: v1
kind: Service
metadata:
  name: gysl-web
spec:
  type: NodePort
  selector:
    app: pod-gysl
  ports:
    - name: web-svc
      protocol: TCP
      nodePort: 31688
      port: 8080
      targetPort: 80

通过以下命令向相关 Pod 写入验证内容:

for node in 0 1;do kubectl exec statefulset-pvc-gysl-$node -- sh -c "echo \<h1\>Node: ${node}\</h1\>>/usr/share/nginx/html/index.html";done

观察实验结果:

$ kubectl get pod -o wide
NAME                     READY   STATUS    RESTARTS   AGE   IP            NODE          NOMINATED NODE   READINESS GATES
statefulset-pvc-gysl-0   1/1     Running   0          51m   172.20.85.2   172.31.2.11   <none>           <none>
statefulset-pvc-gysl-1   1/1     Running   0          32m   172.20.65.4   172.31.2.12   <none>           <none>
$ kubectl get pvc
NAME                             STATUS   VOLUME          CAPACITY   ACCESS MODES   STORAGECLASS   AGE
www-vct-statefulset-pvc-gysl-0   Bound    pv-nfs-gysl-0   1Gi        RWO            nfs            51m
www-vct-statefulset-pvc-gysl-1   Bound    pv-nfs-gysl-1   1Gi        RWO            nfs            49m
$ kubectl get pv
NAME            CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                                    STORAGECLASS   REASON   AGE
pv-nfs-gysl-0   1Gi        RWO            Recycle          Bound    default/www-vct-statefulset-pvc-gysl-0   nfs                     51m
pv-nfs-gysl-1   1Gi        RWO            Recycle          Bound    default/www-vct-statefulset-pvc-gysl-1   nfs                     51m
$ cat /data-0/index.html
<h1>Node: 0</h1>
$ cat /data-1/index.html
<h1>Node: 1</h1>
$ curl 172.31.2.11:31688
<h1>Node: 0</h1>
$ curl 172.31.2.11:31688
<h1>Node: 1</h1>
$ curl 172.31.2.12:31688
<h1>Node: 1</h1>
$ curl 172.31.2.12:31688
<h1>Node: 0</h1>

kubectl run -i --tty  --image toolkit:v1.0.0821 test --restart=Never --rm /bin/bash
[root@test /]# curl statefulset-pvc-gysl-0.gysl-web
<h1>Node: 0</h1>
[root@test /]# curl statefulset-pvc-gysl-0.gysl-web
<h1>Node: 0</h1>
[root@test /]# curl statefulset-pvc-gysl-1.gysl-web
<h1>Node: 1</h1>
[root@test /]# curl statefulset-pvc-gysl-1.gysl-web
<h1>Node: 1</h1>
[root@test /]# curl gysl-web:8080
<h1>Node: 1</h1>
[root@test /]# curl gysl-web:8080
<h1>Node: 1</h1>
[root@test /]# curl gysl-web:8080
<h1>Node: 0</h1>
[root@test /]# curl gysl-web:8080
<h1>Node: 0</h1>

从实验结果中我们可以看出 Pod 与 PV、PVC 的对应关系,结合上文中的 yaml 我们不难发现:

  1. Pod 与对应的 PV 存储是一一对应的,在创 Pod 的同时, StatefulSet根据对应的规创建了相应的 PVC,PVC 选择符合条件的 PV 进绑定。当 Pod 被删除之后,数据依然保存在 PV 中,当被删除的 Pod 再次被创建时, 该 Pod 依然会立即与原来的 Pod 进行绑定,保持原有的对应关系。
  2. 在集群内部,可以通过 pod 名加对应的服务名访问指定的 Pod 及其绑定的 PV。 如果通过服务名来访问 StatefulSet ,那么服务名的功能类似于 VIP 的功能。
相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
相关文章
|
25天前
|
Kubernetes Cloud Native Docker
云原生时代的容器化实践:Docker和Kubernetes入门
【10月更文挑战第37天】在数字化转型的浪潮中,云原生技术成为企业提升敏捷性和效率的关键。本篇文章将引导读者了解如何利用Docker进行容器化打包及部署,以及Kubernetes集群管理的基础操作,帮助初学者快速入门云原生的世界。通过实际案例分析,我们将深入探讨这些技术在现代IT架构中的应用与影响。
82 2
|
2月前
|
Kubernetes 持续交付 微服务
深入浅出:理解 Kubernetes 核心概念
Kubernetes 是一个由 Google 开源的容器编排平台,用于自动化部署、扩展和管理容器化应用程序。它已成为微服务架构下的行业标准。本文深入浅出地介绍了 Kubernetes 的核心概念和组件,包括 Master 和 Node 组件、Pod、Service、Deployment 等,并提供了基本操作示例和实战应用,帮助你更好地管理和利用容器环境。
|
25天前
|
Kubernetes 监控 负载均衡
深入云原生:Kubernetes 集群部署与管理实践
【10月更文挑战第37天】在数字化转型的浪潮中,云原生技术以其弹性、可扩展性成为企业IT架构的首选。本文将引导你了解如何部署和管理一个Kubernetes集群,包括环境准备、安装步骤和日常维护技巧。我们将通过实际代码示例,探索云原生世界的秘密,并分享如何高效运用这一技术以适应快速变化的业务需求。
60 1
|
1月前
|
Kubernetes 负载均衡 Cloud Native
云原生应用:Kubernetes在容器编排中的实践与挑战
【10月更文挑战第27天】Kubernetes(简称K8s)是云原生应用的核心容器编排平台,提供自动化、扩展和管理容器化应用的能力。本文介绍Kubernetes的基本概念、安装配置、核心组件(如Pod和Deployment)、服务发现与负载均衡、网络配置及安全性挑战,帮助读者理解和实践Kubernetes在容器编排中的应用。
76 4
|
1月前
|
Kubernetes 监控 Cloud Native
云原生应用:Kubernetes在容器编排中的实践与挑战
【10月更文挑战第26天】随着云计算技术的发展,容器化成为现代应用部署的核心趋势。Kubernetes(K8s)作为容器编排领域的佼佼者,以其强大的可扩展性和自动化能力,为开发者提供了高效管理和部署容器化应用的平台。本文将详细介绍Kubernetes的基本概念、核心组件、实践过程及面临的挑战,帮助读者更好地理解和应用这一技术。
63 3
|
2月前
|
Kubernetes 监控 开发者
专家级实践:利用Cloud Toolkit进行微服务治理与容器化部署
【10月更文挑战第19天】在当今的软件开发领域,微服务架构因其高可伸缩性、易于维护和快速迭代的特点而备受青睐。然而,随着微服务数量的增加,管理和服务治理变得越来越复杂。作为阿里巴巴云推出的一款免费且开源的开发者工具,Cloud Toolkit 提供了一系列实用的功能,帮助开发者在微服务治理和容器化部署方面更加高效。本文将从个人的角度出发,探讨如何利用 Cloud Toolkit 来应对这些挑战。
40 2
|
2月前
|
Kubernetes 持续交付 Docker
探索DevOps实践:利用Docker与Kubernetes实现微服务架构的自动化部署
【10月更文挑战第18天】探索DevOps实践:利用Docker与Kubernetes实现微服务架构的自动化部署
91 2
|
27天前
|
存储 Kubernetes 调度
【赵渝强老师】K8s中Deployment控制器与StatefulSet控制器的区别
K8s中的Deployment控制器用于管理无状态应用程序,关注Pod数量、更新方式等;而StatefulSets控制器则管理有状态应用程序,提供持久存储和唯一标识符,适用于需要稳定网络标识符和持久化存储的场景。两者的主要区别在于是否维护状态和顺序。
|
27天前
|
存储 Kubernetes 调度
【赵渝强老师】K8s的有状态控制器StatefulSet
在Kubernetes中,StatefulSets用于部署有状态应用程序,提供持久存储和唯一标识符。与Deployment不同,StatefulSets确保Pod的标识符在重新调度后保持不变,适用于需要稳定网络标识符和持久存储的场景。本文介绍了StatefulSets的创建、扩容与缩容、更新与回滚等操作,并提供了具体示例和视频讲解。
|
27天前
|
Kubernetes 负载均衡 调度
Kubernetes集群管理与编排实践
Kubernetes集群管理与编排实践