郑文峰的博客 郑文峰的博客
首页
  • python之路
  • go之路
  • 其他
  • redis
  • mysql
  • docker
  • k8s
读书破万卷
周刊
关于
  • 导航 (opens new window)
  • 代码片段 (opens new window)
  • 收藏
  • 友链
  • 外部页面

    • 开往 (opens new window)
  • 索引

    • 分类
    • 标签
    • 归档
GitHub (opens new window)

zhengwenfeng

穷则变,变则通,通则久
首页
  • python之路
  • go之路
  • 其他
  • redis
  • mysql
  • docker
  • k8s
读书破万卷
周刊
关于
  • 导航 (opens new window)
  • 代码片段 (opens new window)
  • 收藏
  • 友链
  • 外部页面

    • 开往 (opens new window)
  • 索引

    • 分类
    • 标签
    • 归档
GitHub (opens new window)
  • docker

  • k8s

    • k8s之Pod
    • k8s之Deployment
    • k8s之Service
    • k8s之ConfigMap和Secret
    • k8s之Job和CronJob
    • k8s之DaemonSet
    • k8s之PV、PVC和StorageClass
    • k8s之StatefulSet
      • 什么是StatefulSet?
      • 使用StatefulSet
        • 创建StatefulSet
        • Service配置
        • 持久化配置
    • 使用kubeadm安装k8s
    • pod中将代码与运行环境分离
    • django后端服务、logstash和flink接入VictoriaMetrics指标监控
  • 云原生
  • k8s
zhengwenfeng
2022-08-31
目录

k8s之StatefulSet

# 什么是StatefulSet?

是用来创建有状态应用,可以通过过某种方式记录这些状态,然后在 Pod 被重新创建时,能够为新 Pod 恢复这些状态。

什么是有状态应用?

首先是需要有数据的持久化,即使Pod被重启后,也能恢复,与重启前保持一致。然后是应用创建的所有pod有依赖关系,顺序的创建、需要运行在指定的宿主机上,并且都有对应的网络标志。

应用场景?

分布式应用,它的多个实例之间,往往有依赖关系,比如:主从关系、主备关系。

# 使用StatefulSet

# 创建StatefulSet

创建yaml文件定义StatefulSet对象如下,与Deployment比较,多了一个serviceName字段,这个是用来指定StatefulSet所管理的pod是用域名访问是通过该service所设定的。


apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: redis-sts

spec:
  serviceName: redis-svc
  replicas: 2
  selector:
    matchLabels:
      app: redis-sts

  template:
    metadata:
      labels:
        app: redis-sts
    spec:
      containers:
      - image: redis
        name: redis
        ports:
        - containerPort: 6379
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

创建StatefulSet对象

[root@k8s-worker1 zwf]# kubectl apply -f statefulset.yaml  -n zwf
statefulset.apps/redis-sts created

[root@k8s-worker1 zwf]# kubectl get sts -n zwf
NAME        READY   AGE
redis-sts   2/2     54s
1
2
3
4
5
6

查看创建的Pod会发现,命名不再是随机创建的名字,而是有了顺序号,从0开始,而k8s也会按照这个顺序一次创建。

[root@k8s-worker1 zwf]# kubectl get pods -n zwf
NAME          READY   STATUS    RESTARTS   AGE
redis-sts-0   1/1     Running   0          61s
redis-sts-1   1/1     Running   0          54s
1
2
3
4

输出pod中的hostname发现与pod的名称也保持一致,也就是应用可以自行决定依赖关系,比如该例子中可以使用0号pod作为主实例,而1号pod作为从实例。

[root@k8s-worker1 zwf]# kubectl exec -it redis-sts-0 -n zwf -- hostname
redis-sts-0
1
2

# Service配置

定义匹配上面的创建StatefulSet对象所有管理的Service,也就是标签筛选需要和pod的标签保持一致,并且这里的metadata.name也要与StatefulSet中的serviceName一样。


apiVersion: v1
kind: Service
metadata:
  name: redis-svc

spec:
  selector:
    app: redis-sts

  ports:
  - port: 6379
    protocol: TCP
    targetPort: 6379
1
2
3
4
5
6
7
8
9
10
11
12
13
14

创建Service对象,我们可以看到已经将StatefulSet所创建的pod加入到端点列表了,也就是可以稳定的通过Service来访问到Pod

[root@k8s-worker1 zwf]# kubectl apply -f service_statefulset.yaml -n zwf
kservice/redis-svc created

[root@k8s-worker1 zwf]# kubectl get svc -n zwf
NAME                   TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)        AGE
redis-svc              ClusterIP   10.0.0.246   <none>        6379/TCP       5s

[root@k8s-worker1 zwf]# kubectl describe svc redis-svc -n zwf
Name:              redis-svc
Namespace:         zwf
Labels:            <none>
Annotations:       <none>
Selector:          app=redis-sts
Type:              ClusterIP
IP Family Policy:  SingleStack
IP Families:       IPv4
IP:                10.0.0.246
IPs:               10.0.0.246
Port:              <unset>  6379/TCP
TargetPort:        6379/TCP
Endpoints:         10.222.126.51:6379,10.222.194.84:6379
Session Affinity:  None
Events:            <none>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

但是我们的Pod不是一般的的应用,是有状态的应用,需要有稳定的网络标识,所以会为每一个Pod也创建一个域名,格式是:<podName>.<serviceName>.<namesapce>.svc.cluster.local。

我们进入pod中验证一下,通过ping redis-sts-1.redis-svc.zwf.svc.cluster.local发现是可以ping通的,虽然Pod的IP会变化,但是通过固定的域名就能访问到指定Pod了。

[root@k8s-worker1 zwf]# kubectl exec -it redis-sts-0 -n zwf -- ping redis-sts-1.redis-svc.zwf.svc.cluster.local
PING redis-sts-1.redis-svc.zwf.svc.cluster.local (10.222.126.51) 56(84) bytes of data.
64 bytes from redis-sts-1.redis-svc.zwf.svc.cluster.local (10.222.126.51): icmp_seq=1 ttl=62 time=0.964 ms
64 bytes from redis-sts-1.redis-svc.zwf.svc.cluster.local (10.222.126.51): icmp_seq=2 ttl=62 time=0.932 ms
1
2
3
4

既然我们的pod有了稳定的网络标识,Service也就不需要再分配ClusterIP了,这个时候,只需要添加字段clusterIP: None,这样就不会再分配IP了,这样的Service称为Headless Service


apiVersion: v1
kind: Service
metadata:
  name: redis-svc

spec:
  clusterIP: None
  selector:
    app: redis-sts

  ports:
  - port: 6379
    protocol: TCP
    targetPort: 6379
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

创建Headless Service,可以看到CLUSTER-IP为None

[root@k8s-worker1 zwf]# kubectl get svc -n zwf
NAME                   TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)        AGE
demoapp-nodeport-svc   NodePort    10.0.0.144   <none>        80:31999/TCP   21h
demoapp-svc            ClusterIP   10.0.0.74    <none>        80/TCP         22h
redis-svc              ClusterIP   None         <none>        6379/TCP       4s
1
2
3
4
5

Service和StatefulSet配置图如下:

# 持久化配置

接下来是给StatefulSet对象添加持久化配置。

定义StatefulSet描述如下:


apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: redis-pv-sts

spec:
  serviceName: redis-pv-svc

  volumeClaimTemplates:
  - metadata:
      name: redis-100m-pvc
    spec:
      storageClassName: nfs-client
      accessModes:
        - ReadWriteMany
      resources:
        requests:
          storage: 100Mi

  replicas: 2
  selector:
    matchLabels:
      app: redis-pv-sts

  template:
    metadata:
      labels:
        app: redis-pv-sts
    spec:
      containers:
      - image: redis:5-alpine
        name: redis

        volumeMounts:
        - name: redis-100m-pvc
          mountPath: /data
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

参数:

  • volumeClaimTemplates,用来将PVC的定义嵌入到StatefulSet中的字段,是创建PVC的模板,可以让每一个Pod都能自动创建PVC
  • voulumeMounts,是用来选择上面的PVC挂载在容器的/data目录中

创建StatefulSet对象,可以看到pod已经创建起来了。

[root@k8s-worker1 zwf]# kubectl apply -f statefulset_pvc.yaml -n zwf
statefulset.apps/redis-pv-sts created

[root@k8s-worker1 zwf]# kubectl get sts -n zwf
NAME           READY   AGE
redis-pv-sts   2/2     25

[root@k8s-worker1 zwf]# kubectl get pods -n zwf
NAME             READY   STATUS    RESTARTS   AGE
redis-pv-sts-0   1/1     Running   0          63s
redis-pv-sts-1   1/1     Running   0          54s
1
2
3
4
5
6
7
8
9
10
11

我们查看pvc,会发现创建了2个对象 ,以PVC名称-pod名称命名。

[root@k8s-worker1 zwf]# kubectl get pvc -n zwf
NAME                            STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
redis-100m-pvc-redis-pv-sts-0   Bound    pvc-2712daa4-36b4-4a63-ac2f-7d3b31e2a887   100Mi      RWX            nfs-client     109s
redis-100m-pvc-redis-pv-sts-1   Bound    pvc-a3781354-e182-42f7-b6f6-983999603653   100Mi      RWX            nfs-client     100s
1
2
3
4

进入到pod中,使用redis-cli运行redis客户端,添加一些数据

[root@k8s-worker1 zwf]# kubectl exec -it redis-pv-sts-0 -n zwf /bin/bash
[ root@redis-pv-sts-0:/data ]$ redis-cli  
127.0.0.1:6379> set name zhangsan
OK
127.0.0.1:6379> set age 18
OK
127.0.0.1:6379> keys *
1) "name"
2) "age"
127.0.0.1:6379> quit
1
2
3
4
5
6
7
8
9
10

将pod删除,然后再重新进入Pod中,查询之前创建的redis的key,还是能够查询到。

[root@k8s-worker1 zwf]# kubectl exec -it redis-pv-sts-0  -n zwf -- /bin/bash
[ root@redis-pv-sts-0:/data ]$ redis-cli  
127.0.0.1:6379> keys *
1) "name"
2) "age"
127.0.0.1:6379> get name
"zhangsan"
127.0.0.1:6379> get age
"18"
1
2
3
4
5
6
7
8
9

配置的关系图如下:

#k8s#容器#云原生
上次更新: 2023/01/15, 15:47:48
k8s之PV、PVC和StorageClass
使用kubeadm安装k8s

← k8s之PV、PVC和StorageClass 使用kubeadm安装k8s→

最近更新
01
django rest_framework 分页
03-20
02
学习周刊-第03期-第09周
03-03
03
学习周刊-第02期-第08周
02-24
更多文章>
Theme by Vdoing | Copyright © 2022-2023 zhengwenfeng | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式