statefulset 是为了解决有状态服务的问题,而产生的一种资源类型,当然,如果你使用单节点的数据库,你可以使用 deployment 资源类型,但如果你的有状态服务是集群,节点之间有主备或者主从之分,且每个节点分片存储的情况下,deployment 就不适合这种场景了,因为 deployment 不会保证 pod 的有序性,集群通常需要主节点先启动,从节点再加入集群,statefulset 则可以保证,其次,deployment 资源的 pod 内的 pvc 是共享的,而 statefulset 下的 pod 内 pvc 是不共享存储的,每个 pod 拥有自己独立的存储空间,正好满足分片需求,实现分片的需求的前提是 statefulset 可以保证 pod 重新调度后还是能访问到相同的持久化数据。

适用 statefulset 常用的服务有 ES 集群、mongodb 集群、redis 集群、mysql 集群等

应用场景

  • 稳定的不共享持久化存储:每个 pod 的存储资源是不共享的,且 pod 重新调度后还是能访问到相同的持久化数据,基于 pvc 实现
  • 稳定的网络标记:pod 重新调度后其 PodName 和 HostName 不变,且 PodName 和 HostName 是相同的,基于 Headless Service 来实现的
  • 有序部署,有序扩展:pod 是有顺序的,在部署或者扩展的时候是根据定义的顺序依次部署的(即从 0 到 N-1,在下一个 pod 运行之前,所有之前的 pod 必须都是 Running 或者 Ready 状态),是基于 init conntainers 来实现的
  • 有序收缩:在 pod 删除时是从最后一个依次往前删除,即从 N-1 到 0

部署 StatefulSet 类型的 Mongodb

首先是 mongo.yml

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mongodb
spec:
  replicas: 3
  serviceName: mongodb
  selector:
    matchLabels:
      app: mongodb
  template:
    metadata:
      labels:
        app: mongodb
    spec:
      containers:
        - name: mongo
          image: mongo:4.4
          # IfNotPresent 仅本地没有镜像时才远程拉,Always 永远都是从远程拉,Never 永远只用本地镜像,本地没有则报错
          imagePullPolicy: IfNotPresent
---
apiVersion: v1
kind: Service
metadata:
  name: mongodb
spec:
  selector:
    app: mongodb
  type: ClusterIP
  # HeadLess
  clusterIP: None
  ports:
    - port: 27017
      targetPort: 27017

部署 mongo

kubectl apply -f mongo.yml

StatefulSet 特性

  • Service 的 CLUSTER-IP 是空的,Pod 名字也是固定的
  • Pod 创建和销毁是有序的,创建是顺序的,销毁是逆序的
  • Pod 重建不会改变名字,除了 IP,所以不要使用 IP 直连

Endpoints 会多一个 hostname

kubectl get endpoints mongodb -o yaml

访问时,如果直接使用 Service 名字连接,会随机转发请求,要连接指定 pod,可以使用 pod-name.service-name

运行一个临时 pod 连接来查看 mongo 数据测试一下,可以使用

kubectl run mongo-client --rm --tty -i --restart='Never' --image=mongo:4.4 --command -- bash

你可以先在 docker 提前拉取 mongo:4.4 镜像

我们首先查看 mongo 里的数据

[root@master ~]# kubectl run mongo-client --rm --tty -i --restart='Never' --image=mongo:4.4 --command -- bash
If you don't see a command prompt, try pressing enter.
root@mongo-client:/# mongo --host mongodb-0.mongodb
MongoDB shell version v4.4.18
connecting to: mongodb://mongodb-0.mongodb:27017/?compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("f86e1d9a-cd62-4f20-ad58-e7d2ee18f7e5") }
MongoDB server version: 4.4.18
Welcome to the MongoDB shell.
For interactive help, type "help".
For more comprehensive documentation, see
        https://docs.mongodb.com/
Questions? Try the MongoDB Developer Community Forums
        https://community.mongodb.com
---
The server generated these startup warnings when booting: 
        2023-02-03T17:07:21.490+00:00: Access control is not enabled for the database. Read and write access to data and configuration is unrestricted
        2023-02-03T17:07:21.491+00:00: /sys/kernel/mm/transparent_hugepage/enabled is 'always'. We suggest setting it to 'never'
        2023-02-03T17:07:21.491+00:00: /sys/kernel/mm/transparent_hugepage/defrag is 'always'. We suggest setting it to 'never'
---
---
        Enable MongoDB's free cloud-based monitoring service, which will then receive and display
        metrics about your deployment (disk utilization, CPU, operation statistics, etc).
        The monitoring data will be available on a MongoDB website with a unique URL accessible to you
        and anyone you share the URL with. MongoDB may use this information to make product
        improvements and to suggest MongoDB products and deployment options to you.
        To enable free monitoring, run the following command: db.enableFreeMonitoring()
        To permanently disable this reminder, run the following command: db.disableFreeMonitoring()
---
> show dbs
admin   0.000GB
config  0.000GB
local   0.000GB

里面只有默认的数据,然后我们部署一个服务,服务里连接 mongo,以下是 app.yml

apiVersion: apps/v1
kind: Deployment
metadata:
  # 部署名字
  name: test-k8s
spec:
  replicas: 2
  # 用来查找关联的 Pod,所有标签都匹配才行
  selector:
    matchLabels:
      app: test-k8s
  # 定义 Pod 相关数据
  template:
    metadata:
      labels:
        app: test-k8s
    spec:
      # 定义容器,可以多个
      containers:
      - name: test-k8s # 容器名字
        image: docker.io/leayos/testweb:0.1 # 镜像
        imagePullPolicy: IfNotPresent
      # 等待 mongodb 起来后才启动
      initContainers:
      - name: wait-mongo
        image: busybox:1.28
        command: ['sh', '-c', "until nslookup mongodb; do echo waiting for mongo; sleep 2; done"]
---
apiVersion: v1
kind: Service
metadata:
  name: test-k8s
spec:
  selector:
    app: test-k8s
  # 默认 ClusterIp 集群内可访问,NodePort 节点可访问,LoadBalancer 负载均衡模式(需要负载均衡器才可用)
  type: NodePort
  ports:
    - nodePort: 31000   # 节点端口,范围固定 30000 ~ 32767
      port: 5000        # 本 Service 的端口
      targetPort: 5000  # 容器端口

服务的镜像我简单添加了 mongo 连接的 Flask 项目并上传到了 docker hub,mongo 的连接方式为:mongodb://mongodb-0.mongodb:27017,因为我经过测试,貌似修改了 imagePullPolicy 还是不行,它还是会在远端拉取镜像

imagePullPolicy

  • Always:总是拉取镜像
  • IfNotPresent:本地有则使用本地镜像,不拉取
  • Never:只是用本地镜像,从不拉取,即使本地没有
  • 如果忽略,策略默认为 Always
  • 官方文档:https://kubernetes.io/docs/concepts/containers/images

部署服务

[root@master statefulset]# kubectl get pod
NAME           READY   STATUS    RESTARTS   AGE
mongo-client   1/1     Running   0          99s
mongodb-0      1/1     Running   0          8m39s
mongodb-1      1/1     Running   0          8m38s
mongodb-2      1/1     Running   0          8m37s
[root@master statefulset]# kubectl apply -f app.yaml 
deployment.apps/test-k8s created
service/test-k8s created
[root@master statefulset]# kubectl get pod
NAME                        READY   STATUS    RESTARTS   AGE
mongo-client                1/1     Running   0          119s
mongodb-0                   1/1     Running   0          8m59s
mongodb-1                   1/1     Running   0          8m58s
mongodb-2                   1/1     Running   0          8m57s
test-k8s-54999476cd-2x8gs   1/1     Running   0          3s
test-k8s-54999476cd-bl7tf   1/1     Running   0          3s

OK,这时候部署没问题,我们暴漏端口访问测试一下,我们请求网页,然后查询 mongo

[root@master statefulset]# kubectl port-forward --address 0.0.0.0 test-k8s-54999476cd-2x8gs 5000:5000
Forwarding from 0.0.0.0:5000 -> 5000

我们添加一条数据

然后通过 mongo client 查询,可以发现多了一个新的数据库

切换数据库,查询集合的所有数据

我请求了几遍

可以看到,数据成功添加了

这是一个简单的案例,起了 3 个 mongo,你可以作为主从来使用,一个负责写入,其它负责读取

Edited on Views times

Give me a cup of [coffee]~( ̄▽ ̄)~*

小芳芳 WeChat Pay

WeChat Pay

小芳芳 Alipay

Alipay