hualinux 进阶 1.15:StatefulSet有状态应用(一)

目录

一、StatefulSets介绍

二、StatefulSet 状态分类

2.1 拓扑状态

2.2 存储状态

三、建立一个简单的拓扑状态的StatefulSet

3.1 编写statefulSet的YAML文件

3.2 pod对应的域名

3.3 访问测试

3.3.1 安装centos pod

3.3.2 测试

四、稳定的存储

4.1 pv和pvc的关系

4.1.1 pv生命周期

4.1.2 pv回收策略

4.1.3 pv的声明类型

4.2 storageClassName 

4.3 pv pvc sc之间的关系


Deployment 是一种无状态的应用,群集中pod都是一样的,所以适合当负载均衡,但是实际中还有不少是有状态的应用,群集中的pod并不一样的,所有讲到今天的主角StatefulSets

一、StatefulSets介绍

Deployment 认为,一个应用的所有 Pod,是完全一样的。所以,它们互相之间没有顺序,也无所谓运行在哪台宿主机上。需要的时候,Deployment 就可以通过 Pod 模板创建新的 Pod;不需要的时候,Deployment 就可以“杀掉”任意一个 Pod。

在实际的场景中,并不是所有的应用都可以满足这样的要求。尤其是分布式应用,它的多个实例之间,往往有依赖关系,比如:主从关系、主备关系。

还有就是数据存储类应用,它的多个实例,往往都会在本地磁盘上保存一份数据。而这些实例一旦被杀掉,即便重建出来,实例与数据之间的对应关系也已经丢失,从而导致应用失败。

所以,这种实例之间有不对等关系,以及实例对外部数据有依赖关系的应用,就被称为“有状态应用”(Stateful Application)。

Kubernetes 有状态与无状态介绍
无状态:deployment
  - 认为所有pod都是一样的,不具备与其他实例有不同的关系。
  - 没有顺序的要求。
  - 不用考虑再哪个Node运行。
  - 随意扩容缩容。

有状态:SatefulSet
  - 集群节点之间的关系。
  - 数据不完全一致。
  - 实例之间不对等的关系。
  - 依靠外部存储的应用。
  - 通过dns维持身份

StatefulSets更多的相关的知识,可以看它的官方中文,我这里不多介绍

二、StatefulSet 状态分类

2.1 拓扑状态

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

2.2 存储状态

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

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

 

三、建立一个简单的拓扑状态的StatefulSet

说了那么多,我先建立一个简单的StatefulSet,绑定节点hostpath的,我在《hualinux 进阶 1.7:kubeadm1.18搭建k8s群集 》的基础上建立的

3.1 编写statefulSet的YAML文件

#节点上操作
#建立相关目录,我这里只有一个节点是vm821
mkdir -p /disk1/www/hualinux.com
echo 'vm821 index.html'>/disk1/www/hualinux.com/index.html


#在master上操作
mkdir -pv /disk1/myk8s
cd /disk1/myk8s/

cat>nginx-statefulset.yaml<<EOF
apiVersion: v1
kind: Service
metadata:
  name: nginx-ser-none
  labels:
    web: nginx18
spec:
  selector:
   #查找匹配的标签的pod
    web: nginx18
  ports:
    - protocol: TCP
      #services对外端口
      port: 80
  #statefulSet一定要使用handless services
  clusterIP: None    
#多个Yaml文件可以用 --- 分隔
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  serviceName: "nginx-ser-none"
  replicas: 2
  selector:
    matchLabels:
      web: nginx18
  template:
    metadata:
      labels:
        web: nginx18
    spec:
      containers:
      - name: nginx
        image: nginx:1.18
        ports:
        - containerPort: 80
          name: nginx
        volumeMounts:
        - mountPath: /usr/share/nginx/html
          name: hualinux
      volumes:
      - name: hualinux
        hostPath:
          # directory location on host
          path: /disk1/www/hualinux.com
EOF
kubectl apply -f nginx-statefulset.yaml 

查看状态得

#和Deployment不同的时pod是带有序列的
[root@vm82 myk8s]# kubectl get po -o wide
NAME    READY   STATUS    RESTARTS   AGE     IP          NODE    NOMINATED NODE   READINESS GATES
web-0   1/1     Running   0          4m31s   10.44.0.1   vm821   <none>           <none>
web-1   1/1     Running   0          2m38s   10.44.0.2   vm821   <none>           <none>
handless无头服务,是没有群集IP的
[root@vm82 myk8s]# kubectl get svc -o wide
NAME             TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE     SELECTOR
kubernetes       ClusterIP   10.96.0.1    <none>        443/TCP   7d23h   <none>
nginx-ser-none   ClusterIP   None         <none>        80/TCP    4m34s   web=nginx18
[root@vm82 myk8s]# 
[root@vm82 myk8s]# kubectl get statefulSet -o wide
NAME   READY   AGE     CONTAINERS   IMAGES
web    2/2     4m39s   nginx        nginx:1.18

3.2 pod对应的域名

看到没状态和有状态的区别的吗,有状态它是有序列的,我们再看一下它的hosts

[root@vm82 myk8s]# kubectl exec web-0 -c nginx -- grep web /etc/hosts
#这个有域名的域名格式为<pod名>.<services名>.<namespaces>.svc.cluster.local
10.44.0.1	web-0.nginx-ser-none.default.svc.cluster.local	web-0
[root@vm82 myk8s]# kubectl exec web-1 -c nginx -- grep web /etc/hosts
10.44.0.2	web-1.nginx-ser-none.default.svc.cluster.local	web-

上面中可以看到域名绑定了本地docker的hosts文件,格式如下:

这个有域名的域名格式为<pod名>.<services名>.<namespaces>.svc.cluster.local

具体的可以看官网的staefulSet中的稳定的网络 ID

StatefulSet 中的每个 Pod 根据 StatefulSet 的名称和 Pod 的序号派生出它的主机名。组合主机名的格式为$(StatefulSet 名称)-$(序号)。上例将会创建三个名称分别为 web-0、web-1、web-2 的 Pod。 StatefulSet 可以使用 headless 服务 控制它的 Pod 的网络域。管理域的这个服务的格式为: $(服务名称).$(命名空间).svc.cluster.local,其中 cluster.local 是集群域。 一旦每个 Pod 创建成功,就会得到一个匹配的 DNS 子域,格式为:$(pod 名称).$(所属服务的 DNS 域名),其中所属服务由 StatefulSet 的 serviceName 域来设定。

下面给出一些选择集群域、服务名、StatefulSet 名、及其怎样影响 StatefulSet 的 Pod 上的 DNS 名称的示例:

Cluster DomainService (ns/name)StatefulSet (ns/name)StatefulSet DomainPod DNSPod Hostname
cluster.localdefault/nginxdefault/webnginx.default.svc.cluster.localweb-{0..N-1}.nginx.default.svc.cluster.localweb-{0..N-1}
cluster.localfoo/nginxfoo/webnginx.foo.svc.cluster.localweb-{0..N-1}.nginx.foo.svc.cluster.localweb-{0..N-1}
kube.localfoo/nginxfoo/webnginx.foo.svc.kube.localweb-{0..N-1}.nginx.foo.svc.kube.localweb-{0..N-1}

说明: 集群域会被设置为 cluster.local,除非有其他配置

3.3 访问测试

3.3.1 安装centos pod

如果想访问它,那么可以再安装一个pod,然后通过pod之间进行访问,宿主机是不能直接访问的哈

我这里再安装一个centos7的docker看一下

# 使用 kubectl explain Pod.spec.containers 得倒docker exec -it命令在k8s pod的表达方式
#   stdin	<boolean>
#  tty	<boolean>

cat>centos7.yaml<<EOF
apiVersion: v1
kind: Pod
metadata:
  name: centos7
  labels:
    sys: centos7
spec:
  containers:
  - name: centos7
    image: centos:7
    stdin: true
    tty: true
    args: ["/bin/bash"]
 

EOF
kubectl apply -f centos7.yaml

#因为没有安装centos7会去docker的hub镜像下载,我之前修改为华为云了,速度会快很多
[root@vm82 myk8s]# kubectl get po centos7 
NAME      READY   STATUS    RESTARTS   AGE
centos7   1/1     Running   0          8m27s
[root@vm82 myk8s]# 

这样就安装完了,安装完之后,那怎么访问呢,因为没有群IP,所以只能在容器间,通过域名访问,我们在安装kubeadm的时候默认就安装了dns服务,所以通过域名是可以访问的。

3.3.2 测试

上面的pod运行正常后,可以登录centos7容器中,进行测试,操作如下:

#进入容器  不懂可以 使用 kubectl exec --help
kubectl exec -it centos7 -c centos7 -- bash
#在容器中执行域名解析
yum install  bind-utils telnet -y
nslookup web-0
#查看web-0的域名,<pod名>.<services名>
[root@centos7 /]# nslookup  web-0.nginx-ser-none
Server:		10.96.0.10
Address:	10.96.0.10#53

Name:	web-0.nginx-ser-none.default.svc.cluster.local
Address: 10.44.0.1

[root@centos7 /]# nslookup  web-1.nginx-ser-none
Server:		10.96.0.10
Address:	10.96.0.10#53

Name:	web-1.nginx-ser-none.default.svc.cluster.local
Address: 10.44.0.2

[root@centos7 /]# 
#发现使用hostpath,卷都成共享的了,指向中一个目录
[root@centos7 /]# curl web-0.nginx-ser-none
vm821 index.html
[root@centos7 /]# curl web-1.nginx-ser-none
vm821 index.html

 

四、稳定的存储

上面的例子中创建的卷使用的是hostPath直接在节点上创建的,而且变成了共享目录,我们知道statefulSet是有状态的,如果变成有状态的,比如mysql主从,现在共享一个目录,没意义啊

更致命的是hostPath只是绑定所在节点上,比如你node1 pod挂了,从另一个节点起来,那么hostPath指定的目录就是空的啊!!数据还在node1上了,万一服务器磁盘坏了,那怎搞?!

再者术业有专攻,如果你并不知道有哪些 Volume 类型可以用,要怎么办呢?作为运维只些只懂一部分,也不能兼做存储专家吧,对开发者更是,不仅超越了开发者的知识储备,还会有暴露公司基础设施秘密的风险。因为会把用户名,验证字符串等敏感信息写进去。

这也是为什么,在后来的演化中,Kubernetes 项目引入了一组叫作 Persistent Volume Claim(PVC)和 Persistent Volume(PV)的 API 对象,大大降低了用户声明和使用持久化 Volume 的门槛

4.1 pv和pvc的关系

PVCPV 的设计,实际上类似于“接口”和“实现”的思想。开发者只要知道并会使用“接口”,即:PVC;而运维人员则负责给“接口”绑定具体的实现即:PV

ps:一般主流的k8s都会集成相关的pv,直接拿来用就行。不少公司的相关产品也会出现相关的pv,运维编写的时候可以参照一下,这样也降低的门槛了。

这种解耦,就避免了因为向开发者暴露过多的存储系统细节而带来的隐患。此外,这种职责的分离,往往也意味着出现事故时可以更容易定位问题和明确责任,从而避免“扯皮”现象的出现。

而 PVC、PV 的设计,也使得 StatefulSet 对存储状态的管理成为了可能。

Volume 的类型有一堆可以看官网的Volume 的类型

pv所支持的接入模式挂载选项

下面是一个带有PVC的statefulSet例子

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql8
spec:
  serviceName: "mysql8"
  replicas: 2
  selector:
    matchLabels:
     db: mysql8
  template:
    metadata:
      labels:
        db: mysql8
    spec:
      containers:
      - name: mysql8
        image: mysql:8.0.21
        env: 
          - name: MYSQL_ROOT_PASSWORD
            value: hua123
          - name: MYSQL_DATABASE
            value: /var/lib/mysql
        args: ["--default-authentication-plugin=mysql_native_password","--character-set-server=utf8mb4","--collation-server=utf8mb4_unicode_ci"]   
        ports:
        - containerPort: 3306
          name: mysql8
        volumeMounts:
        - mountPath: /var/lib/mysql
          name: data
  #和普通的statefulSet差不多,只不过多了一个pvc模板
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes:      
      - ReadWriteOnce
      storageClassName: data-mysql8          
      resources:        
        requests:          
          storage: 30Gi

凡是被这个 StatefulSet 管理的 Pod,都会声明一个对应的 PVC;而这个 PVC 的定义,就来自于 volumeClaimTemplates 这个模板字段。更重要的是,这个 PVC 的名字,会被分配一个与这个 Pod 完全一致的编号。

这个自动创建的 PVC,与 PV 绑定成功后,就会进入 Bound 状态,这就意味着这个 Pod 可以挂载并使用这个 PV 了。

当然,PVC 与 PV 的绑定得以实现的前提是,运维人员已经在系统里创建好了符合条件的 PV(比如,我们在前面用到的 pv-volume);或者,你的 Kubernetes 集群运行在公有云上,这样 Kubernetes 就会通过 Dynamic Provisioning 的方式,自动为你创建与 PVC 匹配的 PV。

4.1.1 pv生命周期

pv (持久卷)和pod资源-样,拥有生命周期,共分为以下四种:

Provisioning :正在申明
Binding :正在绑定
using :正在使用
Reclaiming :正在回收

4.1.2 pv回收策略

当pod资源被删除时,其相关pv和数据如何操作?该删除还是保留呢?

kubernetes通过persistentVolumeReclaimPolicy字段进行设置:
Delete :数据和pv都会删除
Recyle : ( 已废弃)
Retain :数据和pv都不动

4.1.3 pv的声明类型

PV的申明类型可分为以下两种:

Static (静态) :
管理员根据使用情况,人为预先进行配置

Dynamic (动态) :
基于已创建的StorageClasses (简称SC )存储类,起到动态申请和创建的作用
API server需要增加一个参数配置: - enable amission-plugins,具体类型参考: storage-classes

 

4.2 storageClassName 

StorageClass(SC)作为对存储资源的抽象定义,对用户设置的PVC申请屏蔽后端存储的细节,一方面减少了用户对于存储资源细节的关注,另一方面减轻了管理员手工管理PV的工作,由系统自动完成PV的创建和绑定,实现了动态的资源供应。基StorageClass的动态资源供应模式将逐步成为云平台的标准存储配置模式。

StorageClass的定义主要包括名称、后端存储的提供者(provisioner)和后端存储的相关参数配置。StorageClass一旦被创建出来,则将无法修改。如需更改,则只能删除原StorageClass的定义重建。

 

4.3 pv pvc sc之间的关系

简单来说

pv是用来定义持久卷的,好比接口的实现

pvc是用来使用pv的,好像调用接口

sc则是实现自动完成pv的创建和绑定的,实现动态绑定功能,如果k8s有相关的sc,那么你就可以不用写pv了,是不是很爽

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值