网站首页 > 知识剖析 正文
在之前的文章中, 介绍了搭建好的#minikube#环境,如果你现在还没有一个可用的minikube环境, 那么可以去该篇文章中直接下载;
在之前的文章中, 先后介绍了如何从源代码开始构建一个Node.js应用和Spring Boot 应用, 并且部署到Kubernetes 中(这个Kubernetes 环境主要是之前建好的#minikube#) , 现在我们开始进一步深入的学习Kubernetes, 用一个个可以实际运行的例子的形式, 深入理解#Kubernetes#的概念以及原理.
在#动手动脑学Kubernetes#系列教程中, 我们展示了Kubernetes的基本用法
在第一篇里, 学习了#Pod#的基本知识;
在第二篇里, 学习了标签(#Label#)的语法, 使用Label来选择和过滤Kubernetes 资源;
在第三篇里, 介绍了#Deployment#的使用, 介绍了Deployment与Replica Set、Pod的关系, 并展示了如何进行应用的版本回滚;
在第四篇里, 介绍了#Service#的使用,使用Replication Controller创建Pod, 并创建Service, 展示了从Service 调用应用的方法; 随后又展示了扩展 Pod的数量为2, 比较了Service和之前的不同, 基本展示了Cluster IP 类型的Service的基本用法.
在第五篇里, 介绍了#Namespace#的使用, 除了创建,列出系统的Namespace之外, 还说明Namespace 如何对资源进行隔离, 建立Development, Staging, Production等环境的方法.
在第六篇里, 介绍了#Service Discovery#的使用, 讲解了如何检查Kube-dns, 如何检查和使用Service的FQDN等知识, 对Kubernetes的DNS 系统有整体的理解.
在第七篇里, 介绍了#Port Forwards#, #端口转发#的用法, 在本地程序开发的时候, 使用端口转发可以简化本地测试的工作, 同时介绍了其他几种本地端口转发的用法.
在第八篇里, 介绍了#Probe#(#探针#)的知识, 介绍了livenessProbe 和readinessProbe的用法,同时介绍了Pod 容器中的几个状态变化, 以及2个容器生命周期回调接口.
在第九篇里, 介绍了#环境变量#的用法, 使用环境变量可以把Pod 定义的信息传递给运行其中的镜像.
在第十篇里, 我们介绍了#Volume#, 卷的用法, 主要展示了emptyDir卷的使用, emptyDir卷的生命周期是和Pod 生命周期同步的.
在第十一篇里, 我们介绍了#Persistent Volume#, 也就是持久卷的用法,展示了当数据存入到PV 之后,数据超乎Pod 生命周期之外的情况.
在第十二篇里, 介绍了Secret, 也就是机密信息的用法, 机密是绑定在命名空间里的, 在使用时候和Volume的用法一样, 可以被Pod 访问, 本篇展示了Opaque类型的Secret的用法.
在第十三篇里, 介绍了日志(logging)的使用, 介绍了Kubernetes中基本日志记录的查看和常用的命令行参数,在理论部分展示了其他几种logging的使用.
在第十四篇里, 介绍了Job, 也就是作业的使用, 介绍了Kubernetes中最基本的、非并行性的一次性运行的作业, 同时介绍了作业的基本概念和基本用法.
今天, 我们来学习StatefulSet的用法, 继续来学习Kubernetes 吧!
如果有无状态应用程序,则要使用Deployment。 但是,对于有状态的应用程序,就需要使用StatefulSet。 与Deployment不同,StatefulSet提供有关其管理的Pod的身份(即可预测名称)和启动顺序的某些保证。
与部署相比,还有另外两点不同:对于网络通信,您需要创建无头服务,而对于持久性,StatefulSet管理每个Pod的持久卷。
从镜像开始
首先, 来创建一个有状态的应用,然后体验StatefulSet .
让我们首先创建有状态的应用程序,即StatefulSet以及持久卷PV和Headless服务.
来回顾一下无头服务:
无头服务(Headless Services)
有时不需要或不想要负载均衡,以及单独的 Service IP。 遇到这种情况,可以通过指定 Cluster IP(spec.clusterIP)的值为 "None" 来创建 Headless Service。
你可以使用无头 Service 与其他服务发现机制进行接口,而不必与 Kubernetes 的实现捆绑在一起。
对这无头 Service 并不会分配 Cluster IP,kube-proxy 不会处理它们, 而且平台也不会为它们进行负载均衡和路由。 DNS 如何实现自动配置,依赖于 Service 是否定义了选择算符。
带选择算符的服务
对定义了选择算符的无头服务,Endpoint 控制器在 API 中创建了 Endpoints 记录, 并且修改 DNS 配置返回 A 记录(地址),通过这个地址直接到达 Service 的后端 Pod 上。
无选择算符的服务
对没有定义选择算符的无头服务,Endpoint 控制器不会创建 Endpoints 记录。 然而 DNS 系统会查找和配置,无论是:
对于 ExternalName 类型的服务,查找其 CNAME 记录
对所有其他类型的服务,查找与 Service 名称相同的任何 Endpoints 的记录
可见无头服务, 就是没有Cluster IP的Service, 来看看具体的定义吧.
GitHub地址:
https://raw.githubusercontent.com/hintcnuie/mehdb/main/app.yaml
文件内容:
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mehdb
spec:
selector:
matchLabels:
app: mehdb
serviceName: "mehdb"
replicas: 2
template:
metadata:
labels:
app: mehdb
spec:
containers:
- name: shard
image: quay.io/mhausenblas/mehdb:0.6
ports:
- containerPort: 9876
env:
- name: MEHDB_DATADIR
value: "/mehdbdata"
livenessProbe:
initialDelaySeconds: 2
periodSeconds: 10
httpGet:
path: /status
port: 9876
readinessProbe:
initialDelaySeconds: 15
periodSeconds: 30
httpGet:
path: /status?level=full
port: 9876
volumeMounts:
- name: data
mountPath: /mehdbdata
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 1Gi
---
apiVersion: v1
kind: Service
metadata:
name: mehdb
labels:
app: mehdb
spec:
ports:
- port: 9876
clusterIP: None
selector:
app: mehdb
在上面的文件定义里面, 定义了一个mehdb 的Service, 一个名为data的PVC, 最后是名为mehdb的StatefulSet, 在这里类型为StatefulSet.
运行StatefulSet
先来创建一下StatefulSet:
$ kubectl apply -f https://raw.githubusercontent.com/hintcnuie/mehdb/main/app.yaml
statefulset.apps/mehdb created
service/mehdb created
然后检查一下相应的创建过程:
$ kubectl get sts,po,pvc,svc
NAME READY AGE
statefulset.apps/mehdb 2/2 4m23s
NAME READY STATUS RESTARTS AGE
pod/mehdb-0 1/1 Running 0 4m23s
pod/mehdb-1 1/1 Running 0 3m
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
persistentvolumeclaim/data-mehdb-0 Bound pvc-ae4c72bc-5821-485b-ba74-b86f3b21b8de 1Gi RWO standard 4m23s
persistentvolumeclaim/data-mehdb-1 Bound pvc-a4f548fa-5685-441e-883d-442d4e8f60f3 1Gi RWO standard 3m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 52d
service/mehdb ClusterIP None <none> 9876/TCP 4m23s
注意: 在这里我们又学到了一招,可以同时查看过个资源对象的命令的写法, 就是kubectl get sts,po,pvc,svc, 注意这几个资源名称之间不能有空间.另外, StatefulSet的缩写是sts, 是不是简单了很多!
现在,我们可以检查有状态的应用程序是否正常运行。 为此,我们使用无头服务mehdb:9876的/status端点,并且由于尚未将任何数据放入数据存储区,因此我们希望报告0个键:
$ kubectl run -it --rm jumpod --restart=Never --image=quay.io/openshiftlabs/jump:0.2 -- curl -s mehdb:9876/status?level=full
0
pod "jumpod" deleted
注意上面的用法, 这里面通过kubectl run命令并指定镜像, 直接创建了一个jumpod, 而且使用--rm的参数, 让这个pod 在执行完之后就自动销毁.
我们来查看一下这个StatefulSet下面的Pod:
$ kubectl get sts
NAME READY AGE
mehdb 2/2 13m
[vagrant@control-plane ~]$ kubectl describe sts mehdb
Name: mehdb
Namespace: default
CreationTimestamp: Sat, 20 Mar 2021 14:08:09 +0000
Selector: app=mehdb
Labels: <none>
Annotations: <none>
Replicas: 2 desired | 2 total
Update Strategy: RollingUpdate
Partition: 0
Pods Status: 2 Running / 0 Waiting / 0 Succeeded / 0 Failed
Pod Template:
Labels: app=mehdb
Containers:
shard:
Image: quay.io/mhausenblas/mehdb:0.6
Port: 9876/TCP
Host Port: 0/TCP
Liveness: http-get http://:9876/status delay=2s timeout=1s period=10s #success=1 #failure=3
Readiness: http-get http://:9876/status%3Flevel=full delay=15s timeout=1s period=30s #success=1 #failure=3
Environment:
MEHDB_DATADIR: /mehdbdata
Mounts:
/mehdbdata from data (rw)
Volumes: <none>
Volume Claims:
Name: data
StorageClass:
Labels: <none>
Annotations: <none>
Capacity: 1Gi
Access Modes: [ReadWriteOnce]
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal SuccessfulCreate 13m statefulset-controller create Claim data-mehdb-0 Pod mehdb-0 in StatefulSet mehdb success
Normal SuccessfulCreate 13m statefulset-controller create Pod mehdb-0 in StatefulSet mehdb successful
Normal SuccessfulCreate 12m statefulset-controller create Claim data-mehdb-1 Pod mehdb-1 in StatefulSet mehdb success
Normal SuccessfulCreate 12m statefulset-controller create Pod mehdb-1 in StatefulSet mehdb successful
通过Replicas参数我们可以看到, StatefulSet 下面有两个就绪状态的Pod, 先来看看详情:
$ kubectl describe pod -l app=mehdb
Name: mehdb-0
Namespace: default
Priority: 0
Node: localhost.localdomain/10.0.2.15
Start Time: Sat, 20 Mar 2021 14:08:11 +0000
Labels: app=mehdb
controller-revision-hash=mehdb-9b596f6c9
statefulset.kubernetes.io/pod-name=mehdb-0
Annotations: <none>
Status: Running
IP: 172.17.0.5
IPs:
IP: 172.17.0.5
Controlled By: StatefulSet/mehdb
Containers:
shard:
Container ID: docker://917ac165ecfe545faa99bef0ded7e63daff51bcfdaf1a0a1a391a10b5fca6b99
Image: quay.io/mhausenblas/mehdb:0.6
Image ID: docker-pullable://quay.io/mhausenblas/mehdb@sha256:1f8fd2b656213fe4b15e925afe061dee6083900c7e1e150f762a7e23f8ca90fc
Port: 9876/TCP
Host Port: 0/TCP
State: Running
Started: Sat, 20 Mar 2021 14:09:06 +0000
Ready: True
Restart Count: 0
Liveness: http-get http://:9876/status delay=2s timeout=1s period=10s #success=1 #failure=3
Readiness: http-get http://:9876/status%3Flevel=full delay=15s timeout=1s period=30s #success=1 #failure=3
Environment:
MEHDB_DATADIR: /mehdbdata
Mounts:
/mehdbdata from data (rw)
/var/run/secrets/kubernetes.io/serviceaccount from default-token-q77th (ro)
Conditions:
Type Status
Initialized True
Ready True
ContainersReady True
PodScheduled True
Volumes:
data:
Type: PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace)
ClaimName: data-mehdb-0
ReadOnly: false
default-token-q77th:
Type: Secret (a volume populated by a Secret)
SecretName: default-token-q77th
Optional: false
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling 16m (x2 over 16m) default-scheduler 0/1 nodes are available: 1 pod has unbound immediate PersistentVolumeClaims.
Normal Scheduled 16m default-scheduler Successfully assigned default/mehdb-0 to localhost.localdomain
Normal Pulling 16m kubelet Pulling image "quay.io/mhausenblas/mehdb:0.6"
Normal Pulled 15m kubelet Successfully pulled image "quay.io/mhausenblas/mehdb:0.6" in 52.790537274s
Normal Created 15m kubelet Created container shard
Normal Started 15m kubelet Started container shard
命令行不明显的话,我们来看看Dashboard, 可以看到在metadata 部分有个uid字段:
到目前为止, 我们看到的StatefulSet 的表现, 和之前的Deployment并没有展现出什么不同, 那么两者之间究竟有什么区别呢?
StatefulSet 和Deployment的区别
和 Deployment 类似, StatefulSet 管理基于相同容器规约的一组 Pod。但和 Deployment 不同的是, StatefulSet 为它们的每个 Pod 维护了一个有粘性的 ID。这些 Pod 是基于相同的规约来创建的, 但是不能相互替换:无论怎么调度,每个 Pod 都有一个永久不变的 ID。
具体的说, 他们之间的差异在于:
特性 | Deployment | StatefulSet |
适合场景 | 无状态的应用 | 有状态的应用 |
特点 | 1.pod之间没有顺序 2.所有pod共享存储 3.pod名字包含随机数字 4.service都有ClusterIP,可以负载均衡 | 1.部署、扩展、更新、删除都要有顺序 2.每个pod都有自己存储,所以都用volumeClaimTemplates,为每个pod都生成一个自己的存储,保存自己的状态 3.pod名字始终是固定的 4.service没有ClusterIP,是headlessservice,所以无法负载均衡,返回的都是pod名,所以pod名字都必须 |
是否暴露到外网 | 可以 | 一般不 |
请求面向的对象 | serviceName | 指定pod的域名 |
灵活性 | 只能通过service/serviceIp访问到k8s自动转发的pod | 可以访问任意一个自定义的pod |
易用性 | 只需要关心Service的信息即可 | 需要知道要访问的pod启动的名称、headlessService名称 |
PV/PVC绑定关系的稳定性(多replicas) | (pod挂掉后重启)无法保证初始的绑定关系 | 可以保证 |
pod名称稳定性 | 不稳定,因为是通过template创建,每次为了避免重复都会后缀一个随机数 | 稳定,每次都一样 |
启动顺序(多replicas) | 随机启动,如果pod宕掉重启,会自动分配一个node重新启动 | pod按 app-0、app-1...app-(n-1),如果pod宕掉重启,还会在之前的node上重新启动 |
停止顺序(多replicas) | 随机停止 | 倒序停止 |
Pod 名称的区别
通过上面的比较, 我们先来看Pod的名称, 我们之前看到的两个Pod 的名称分别是:
- mehdb-1
- mehdb-0
这个确实是和我们之前用Deployment 创建的Pod 名称不一样, 回想我们在Deployment 介绍那篇文章中创建的Pod, 创建的Pod 如下所示:
我们能看到, 使用Deployment 创建的Pod, 其网络名称的后缀是随机的, 这就是其中的区别.
我们再来尝试删掉一个Pod, 看看重新生成的Pod 会不会还是同一个:
$ kubectl delete pod/mehdb-1
pod "mehdb-1" deleted
[vagrant@control-plane ~]$ kubectl get pods
NAME READY STATUS RESTARTS AGE
mehdb-0 1/1 Running 0 55m
mehdb-1 0/1 Running 0 4s
[vagrant@control-plane ~]$ kubectl describe sts mehdb
Name: mehdb
Namespace: default
CreationTimestamp: Sat, 20 Mar 2021 14:08:09 +0000
Selector: app=mehdb
Labels: <none>
Annotations: <none>
Replicas: 2 desired | 2 total
Update Strategy: RollingUpdate
Partition: 0
Pods Status: 2 Running / 0 Waiting / 0 Succeeded / 0 Failed
Pod Template:
Labels: app=mehdb
Containers:
shard:
Image: quay.io/mhausenblas/mehdb:0.6
Port: 9876/TCP
Host Port: 0/TCP
Liveness: http-get http://:9876/status delay=2s timeout=1s period=10s #success=1 #failure=3
Readiness: http-get http://:9876/status%3Flevel=full delay=15s timeout=1s period=30s #success=1 #failure=3
Environment:
MEHDB_DATADIR: /mehdbdata
Mounts:
/mehdbdata from data (rw)
Volumes: <none>
Volume Claims:
Name: data
StorageClass:
Labels: <none>
Annotations: <none>
Capacity: 1Gi
Access Modes: [ReadWriteOnce]
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal SuccessfulCreate 56m statefulset-controller create Claim data-mehdb-0 Pod mehdb-0 in StatefulSet mehdb success
Normal SuccessfulCreate 56m statefulset-controller create Pod mehdb-0 in StatefulSet mehdb successful
Normal SuccessfulCreate 54m statefulset-controller create Claim data-mehdb-1 Pod mehdb-1 in StatefulSet mehdb success
Normal SuccessfulCreate 30s (x2 over 54m) statefulset-controller create Pod mehdb-1 in StatefulSet mehdb successful
从上面的运行记录中我们可以看到, 当使用kubectl get pos查询Pod 状态时, 上面的命令行输出中显示的是, Pod mehdb不是ready 的状态:
NAME READY STATUS RESTARTS AGE
mehdb-1 0/1 Running 0 4s
而且Pod mehdb在Age 字段上也和mehdb-0 有明显的不同, 这个在我们再次检查Pod 状态的时候就能看出来.
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
mehdb-0 1/1 Running 0 63m
mehdb-1 1/1 Running 0 7m34s
所以说, 上面的操作, 证明在StatefulSet下面的Pod, 确实会使用同一个网络ID , 而且这个网络ID是有顺序的.
扩容/缩容 StatefulSet
刚才我们使用kubectl deleet 来删除一个StatefulSet中的Pod, 这个在Kuberenete文档中其实是很有风险而且不安全的操作, 下面我们使用kubectl scale 来增加Pod 数量, 看看Pod 命名的变化:
$ kubectl scale sts mehdb --replicas=5
statefulset.apps/mehdb scaled
$ kubectl get pods -w -l app=mehdb
NAME READY STATUS RESTARTS AGE
mehdb-0 1/1 Running 0 83m
mehdb-1 1/1 Running 0 27m
mehdb-2 0/1 Running 0 39s
mehdb-2 1/1 Running 0 44s
mehdb-3 0/1 Pending 0 0s
mehdb-3 0/1 Pending 0 0s
mehdb-3 0/1 Pending 0 2s
mehdb-3 0/1 ContainerCreating 0 2s
mehdb-3 0/1 Running 0 4s
mehdb-3 1/1 Running 0 31s
mehdb-4 0/1 Pending 0 0s
mehdb-4 0/1 Pending 0 0s
mehdb-4 0/1 Pending 0 3s
mehdb-4 0/1 ContainerCreating 0 3s
mehdb-4 0/1 Running 0 4s
mehdb-4 1/1 Running 0 34s
在运行完kubectl scale 命令之后, 我们紧接着运行了kubetctl get pod -w,注意后面的参数-w表示的是--watch的意思, 就是会持续地监控我们指定的Pod, 可以看出, Kuberentes 慢慢地把StatefulSet 下面的Pod 扩容到5个Pod, 而且命名是非常有顺序的.
删除StatefulSet及其资源
最后来删除StatefulSet.
$ kubectl delete sts/mehdb
statefulset.apps "mehdb" deleted
$ kubectl get sts
No resources found in default namespace.
$ kubectl get pods
No resources found in default namespace.
$ kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
data-mehdb-0 Bound pvc-ae4c72bc-5821-485b-ba74-b86f3b21b8de 1Gi RWO standard 12h
data-mehdb-1 Bound pvc-a4f548fa-5685-441e-883d-442d4e8f60f3 1Gi RWO standard 12h
data-mehdb-2 Bound pvc-74df0444-0c0b-4509-ab07-dc13c8384079 1Gi RWO standard 11h
data-mehdb-3 Bound pvc-3a23d929-5e5e-4d28-bc30-c862a6b4dec7 1Gi RWO standard 11h
data-mehdb-4 Bound pvc-76111f79-93f4-4021-87d9-120f46906a40 1Gi RWO standard 11h
[vagrant@control-plane ~]$ kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pvc-3a23d929-5e5e-4d28-bc30-c862a6b4dec7 1Gi RWO Delete Bound default/data-mehdb-3 standard 11h
pvc-74df0444-0c0b-4509-ab07-dc13c8384079 1Gi RWO Delete Bound default/data-mehdb-2 standard 11h
pvc-76111f79-93f4-4021-87d9-120f46906a40 1Gi RWO Delete Bound default/data-mehdb-4 standard 11h
pvc-a4f548fa-5685-441e-883d-442d4e8f60f3 1Gi RWO Delete Bound default/data-mehdb-1 standard 12h
pvc-ae4c72bc-5821-485b-ba74-b86f3b21b8de 1Gi RWO Delete Bound default/data-mehdb-0 standard 12h
可以看到, 当删除StatefulSet mehdb的时候, StatefulSet和相应的Pod 都会被立刻删除; 但是相应的PV和PVC则没有被删除, 需要单独删除.
这其实为了数据安全考虑,手工逐一删除就好了.
好了, 对于StatefulSet的操作部分, 先介绍到这里吧, 下面学习下理论知识.
什么是StatefulSets
StatefulSet 是用来管理有状态应用的工作负载 API 对象。
StatefulSet 用来管理某 Pod 集合的部署和扩缩, 并为这些 Pod 提供持久存储和持久标识符。
和 Deployment 类似, StatefulSet 管理基于相同容器规约的一组 Pod。但和 Deployment 不同的是, StatefulSet 为它们的每个 Pod 维护了一个有粘性的 ID。这些 Pod 是基于相同的规约来创建的, 但是不能相互替换:无论怎么调度,每个 Pod 都有一个永久不变的 ID。
如果希望使用存储卷为工作负载提供持久存储,可以使用 StatefulSet 作为解决方案的一部分。 尽管 StatefulSet 中的单个 Pod 仍可能出现故障, 但持久的 Pod 标识符使得将现有卷与替换已失败 Pod 的新 Pod 相匹配变得更加容易
StatefulSets 对于需要满足以下一个或多个需求的应用程序很有价值:
- 稳定的、唯一的网络标识符。
- 稳定的、持久的存储。
- 有序的、优雅的部署和缩放。
- 有序的、自动地滚动更新。
在上面描述中,“稳定的”意味着 Pod 调度或重调度的整个过程是有持久性的。 如果应用程序不需要任何稳定的标识符或有序的部署、删除或伸缩,则应该使用 由一组无状态的副本控制器提供的工作负载来部署应用程序,比如 Deployment 或者 ReplicaSet 可能更适用于你的无状态应用部署需要。
使用StatefulSet的限制
- 给定 Pod 的存储必须由 PersistentVolume 驱动 基于所请求的 storage class 来提供,或者由管理员预先提供。
- 删除或者收缩 StatefulSet 并不会删除它关联的存储卷。 这样做是为了保证数据安全,它通常比自动清除 StatefulSet 所有相关的资源更有价值。
- StatefulSet 当前需要无头服务 来负责 Pod 的网络标识。你需要负责创建此服务。
- 当删除 StatefulSets 时,StatefulSet 不提供任何终止 Pod 的保证。 为了实现 StatefulSet 中的 Pod 可以有序地且体面地终止,可以在删除之前将 StatefulSet 缩放为 0。
- 在默认 Pod 管理策略(OrderedReady) 时使用 滚动更新,可能进入需要人工干预 才能修复的损坏状态。
StatefulSet 中的Pod 选择算符
你必须设置 StatefulSet 的 .spec.selector 字段,使之匹配其在 .spec.template.metadata.labels 中设置的标签。在 Kubernetes 1.8 版本之前, 被忽略 .spec.selector 字段会获得默认设置值。 在 1.8 和以后的版本中,未指定匹配的 Pod 选择器将在创建 StatefulSet 期间导致验证错误。
Pod 标识
StatefulSet Pod 具有唯一的标识,该标识包括顺序标识、稳定的网络标识和稳定的存储。 该标识和 Pod 是绑定的,不管它被调度在哪个节点上。
有序索引
对于具有 N 个副本的 StatefulSet,StatefulSet 中的每个 Pod 将被分配一个整数序号, 从 0 到 N-1,该序号在 StatefulSet 上是唯一的。
稳定的网络 ID
StatefulSet 中的每个 Pod 根据 StatefulSet 的名称和 Pod 的序号派生出它的主机名。 组合主机名的格式为$(StatefulSet 名称)-$(序号)。 上例将会创建三个名称分别为 mehdb-0、mehdb-1 的 Pod。
StatefulSet 可以使用 无头服务 控制它的 Pod 的网络域。管理域的这个服务的格式为:
$(服务名称).$(命名空间).svc.cluster.local
其中 cluster.local 是集群域。 一旦每个 Pod 创建成功,就会得到一个匹配的 DNS 子域,格式为:
$(pod 名称).$(所属服务的 DNS 域名)
其中所属服务由 StatefulSet 的 serviceName 域来设定。
稳定的存储
Kubernetes 为每个 VolumeClaimTemplate 创建一个 PersistentVolume。 在上面的 nginx 示例中,每个 Pod 将会得到基于 StorageClass my-storage-class 提供的 1 Gib 的 PersistentVolume。如果没有声明 StorageClass,就会使用默认的 StorageClass。 当一个 Pod 被调度(重新调度)到节点上时,它的 volumeMounts 会挂载与其 PersistentVolumeClaims 相关联的 PersistentVolume。 请注意,当 Pod 或者 StatefulSet 被删除时,与 PersistentVolumeClaims 相关联的 PersistentVolume 并不会被删除。要删除它必须通过手动方式来完成。
Pod 名称标签
当 StatefulSet 控制器(Controller) 创建 Pod 时, 它会添加一个标签 statefulset.kubernetes.io/pod-name,该标签值设置为 Pod 名称。 这个标签允许你给 StatefulSet 中的特定 Pod 绑定一个 Service。
StatefulSet 部署和扩缩保证
- 对于包含 N 个 副本的 StatefulSet,当部署 Pod 时,它们是依次创建的,顺序为 0..N-1。
- 当删除 Pod 时,它们是逆序终止的,顺序为 N-1..0。
- 在将缩放操作应用到 Pod 之前,它前面的所有 Pod 必须是 Running 和 Ready 状态。
- 在 Pod 终止之前,所有的继任者必须完全关闭。
StatefulSet 不应将 pod.Spec.TerminationGracePeriodSeconds 设置为 0。 这种做法是不安全的,要强烈阻止。更多的解释请参考 强制删除 StatefulSet Pod。
强制删除 StatefulSet 类型的 Pods
在 StatefulSet 的正常操作中,永远不需要强制删除 StatefulSet 管理的 Pod。 StatefulSet 控制器负责创建、 扩缩和删除 StatefulSet 管理的 Pods。它尝试确保指定数量的从序数 0 到 N-1 的 Pod 处于活跃状态并准备就绪。StatefulSet 确保在任何时候,集群中最多只有一个具有给定标识的 Pod。 这就是所谓的由 StatefulSet 提供的*最多一个(At Most One)*的语义。
应谨慎进行手动强制删除操作,因为它可能会违反 StatefulSet 固有的至多一个的语义。 StatefulSets 可用于运行分布式和集群级的应用,这些应用需要稳定的网络标识和可靠的存储。 这些应用通常配置为具有固定标识固定数量的成员集合。 具有相同身份的多个成员可能是灾难性的,并且可能导致数据丢失 (例如:票选系统中的恶裂场景)。
如何安全地强制删除StatefulSet中的Pod
强制删除不会等待来自 kubelet 对 Pod 已终止的确认消息。 无论强制删除是否成功杀死了 Pod,它都会立即从 API 服务器中释放该名字。 这将让 StatefulSet 控制器创建一个具有相同标识的替身 Pod;因而可能导致正在运行 Pod 的重复, 并且如果所述 Pod 仍然可以与 StatefulSet 的成员通信,则将违反 StatefulSet 所要保证的 最多一个的语义。
当你强制删除 StatefulSet 类型的 Pod 时,你要确保有问题的 Pod 不会再和 StatefulSet 管理的其他 Pod 通信并且可以安全地释放其名字以便创建替代 Pod。
如果要使用 kubectl 1.5 以上版本强制删除 Pod,请执行下面命令:
kubectl delete pods <pod> --grace-period=0 --force
如果你使用 kubectl 的 1.4 以下版本,则应省略 --force 选项:
kubectl delete pods <pod> --grace-period=0
如果在这些命令后 Pod 仍处于 Unknown 状态,请使用以下命令从集群中删除 Pod:
kubectl patch pod <pod> -p '{"metadata":{"finalizers":null}}'
请始终谨慎地执行强制删除 StatefulSet 类型的 pods,并且能够完全了解所涉及的风险。
好了, 关于StatefulSet, 我们先简单地介绍到这里吧, 后续会更加深入的例子, 通过创建一个有状态应用的方式,来更好地展示这部分知识.
回顾
本篇文章中简单介绍了StatefulSet 的用法, 展示了StatefulSet 的创建, 扩容, 以及和Deployment的不同之处. 在最后的理论部分, 指出了在删除StatefulSet 里面的Pod 时候的注意事项.
不积跬步无以至千里,继续学习Kubernetes 吧!
猜你喜欢
- 2025-04-29 typescript - webpack打包警告'mode' option has not been set
- 2025-04-29 通过番计时器实例学习 React 生命周期函数 componentDidMount
- 2025-04-29 Vue3 入门指南: 深入理解 Setup 函数
- 2025-04-29 几句代码写个飞快的下载软件,轻快版 aria2 支持 UPnP,独立 EXE 程序
- 2025-04-29 我终于搞懂了async/await、promise和setTimeout的执行顺序
- 2025-04-29 setTimeout、Promise、Async/await的区别
- 2025-04-29 [西门子PLC] 博途编程中Set置位和Reset复位指令使用技巧
- 2025-04-29 setTimeout 出大 bug?为什么需要 setBigTimeout?
- 2025-04-29 JSP request.setAttribute()详解及实例
- 2025-04-29 JavaScript 中让 setInterval 立即执行的小技巧
- 04-29php开发者composer使用看这一篇就够了
- 04-29引用和变量声明在不同语言中的实作
- 04-29PHP 没你想的那么差
- 04-29Ubuntu linux 上的 Nginx 和 Php 安装
- 04-29CentOS下通过yum搭建lnmp(单版本PHP)
- 04-29为什么 PHP8 是个高性能版本
- 04-29PHP8函数包含文件-PHP8知识详解
- 04-29使用无参数函数进行命令执行
- 最近发表
- 标签列表
-
- xml (46)
- css animation (57)
- array_slice (60)
- htmlspecialchars (54)
- position: absolute (54)
- datediff函数 (47)
- array_pop (49)
- jsmap (52)
- toggleclass (43)
- console.time (63)
- .sql (41)
- ahref (40)
- js json.parse (59)
- html复选框 (60)
- css 透明 (44)
- css 颜色 (47)
- php replace (41)
- css nth-child (48)
- min-height (40)
- xml schema (44)
- css 最后一个元素 (46)
- location.origin (44)
- table border (49)
- html tr (40)
- video controls (49)