在K8s中创建有状态的MariaDB应用程序

956 阅读6分钟

在K8s中创建有状态的MariaDB应用程序

作者 : Anel Husakovic 2022-04-202022-04-20 1 评论 :在K8s中创建有状态的MariaDB应用程序

上一篇博客中,我们创建了一个无状态的应用程序,使用K8s资源部署,它允许人们复制应用程序,但当Pod重新启动时,数据会丢失,这意味着没有数据一致性。在同一篇博客中,我们使用PersistentVolumeClaim来动态配置PersistentVolume,但我们使用的是Deployment,是为无状态应用准备的,这种方式对于有状态的应用来说是不推荐的,因为每个副本都应该有自己的持久化卷。正确的方法是通过Statefulset 资源来实现,这篇文章我们将介绍这个方法。

在K8s中,人们可以创建一个有状态的应用程序,一个像数据库一样的应用程序,它需要将数据保存到持久性磁盘存储中,供服务器/客户/其他应用程序使用,以保持跟踪其状态,并能够复制和用于分布式系统中。有状态的应用程序是使用名为StatefulSet的K8s资源部署的。

StatefulSet基于容器规范部署Pod,就像Deployments一样,但为每个Pod维护一个粘性身份。Pod是根据相同的规范创建的,但不能互换,并且在重新调度时有一个持久的标识符,这意味着当一个Pod死亡时,它会被一个新的Pod所取代,但会保持其身份。

Statefulset示例

让我们看看配置文件是怎样的(在GitHub上找到它)。

apiVersion: v1
kind: Service
metadata:
  name: mariadb-service
  labels:
    app: mariadb
spec:
  ports:
  - port: 3306
    name: mariadb-port
  clusterIP: None
  selector:
    app: mariadb
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mariadb-sts
spec:
  serviceName: "mariadb-service"
  replicas: 3
  selector:
    matchLabels:
      app: mariadb
  template:
    metadata:
      labels:
        app: mariadb
    spec:
      containers:
      - name: mariadb
        image: mariadb
        ports:
        - containerPort: 3306
          name: mariadb-port
        env:
        # Using Secrets
        - name: MARIADB_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mariadb-secret
              key: mariadb-root-password
        volumeMounts:
        - name: datadir
          mountPath: /var/lib/mysql/
  volumeClaimTemplates:
  - metadata:
      name: datadir
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 300M

从上面来看,首先我们已经创建了服务,即 无头类型(集群IP等于None )服务。StatefulSet需要有这个服务来负责Pod的网络身份,但我们需要创建它。它用于MariaDB Pods和集群内客户端之间的DNS查询。

VolumeClaimTemplates是一个允许Pod引用的索赔列表。这个列表中的每一个索赔必须在模板的一个容器中至少有一个匹配的(按名称)volumeMount。这个列表中的权利要求优先于模板中任何具有相同名称的卷。因此,我们已经创建了datadir PersistentVolumeClaim,动态配置为挂载在容器路径,即默认的数据目录。对于StatefulSet中定义的每个VolumeClaimTemplate条目,每个Pod都会收到一个PersistentVolumeClaim。在上面的例子中,每个Pod都会收到一个单一的PersistentVolume,其StorageClass为default(标准),并有300MB的配置存储。Pod的名称将以挂载的卷的名称为前缀(如datadir-mariadb-sts-0 )。

应用配置文件并验证

让我们部署Statefulset,首先创建Secret,之后部署上面的清单。

# Create the Secret
$ kubectl apply -f mariadb-secret.yaml 
secret/mariadb-secret created

# Create service/sts
$ kubectl apply -f mariadb-sts.yaml 
service/mariadb-service created
statefulset.apps/mariadb-sts created

# Verify sts
$ kubectl get sts
NAME          READY   AGE
mariadb-sts   1/3     8s

# Use wide option
$ kubectl get statefulset mariadb-sts -o wide
NAME          READY   AGE   CONTAINERS   IMAGES
mariadb-sts   3/3     19m   mariadb      mariadb

# Verify pods (wait until all are in running state)
$ kubectl get pods
NAME            READY   STATUS    RESTARTS   AGE
mariadb-sts-0   1/1     Running   0          2m29s
mariadb-sts-1   1/1     Running   0          2m23s
mariadb-sts-2   1/1     Running   0          2m18s

# Verify service
$ kubectl get svc -l app=mariadb
NAME              TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
mariadb-service   ClusterIP   None         <none>        3306/TCP   22s

现在我们已经部署了一个有状态的MariaDB应用程序。注意,创建的Pod是按数字前缀排序的,而不是按随机哈希值排序的。

测试有状态的应用程序

每个Pod都有一个基于其序号索引的稳定主机名。

$ for i in 0 1 2; do kubectl exec "mariadb-sts-$i" -- bash -c "hostname"; done
mariadb-sts-0
mariadb-sts-1
mariadb-sts-2

要获得StatefulSet中每个Pod的完全合格域名(FQDN),请使用以下命令。

$ for i in 0 1 ; do kubectl exec "mariadb-sts-$i" -- hostname -f; done
mariadb-sts-0.mariadb-service.default.svc.cluster.local
mariadb-sts-1.mariadb-service.default.svc.cluster.local

mariadb-service 服务为所有的Pod创建一个域,mariadb-service.default.svc.cluster.local

现在让我们通过扩展副本的数量来缩小和扩大应用程序的规模,并观察其结果

# Scale down
$ kubectl scale sts mariadb-sts --replicas=2
# Watch the Pods
$ kubectl get pods -w
NAME            READY   STATUS    RESTARTS   AGE
mariadb-sts-0   1/1     Running   0          4m27s
mariadb-sts-1   1/1     Running   0          4m21s
mariadb-sts-2   1/1     Running   0          4m16s
mariadb-sts-2   1/1     Terminating   0          4m38s

# Scale up
$ kubectl scale sts mariadb-sts --replicas=4
# Watch the Pods
$ kubectl get pods -w
NAME            READY   STATUS    RESTARTS   AGE
mariadb-sts-0   1/1     Running   0          7m19s
mariadb-sts-1   1/1     Running   0          7m13s
mariadb-sts-2   0/1     Pending   0          0s
mariadb-sts-2   0/1     Pending   0          0s
mariadb-sts-2   0/1     ContainerCreating   0          0s
mariadb-sts-2   1/1     Running             0          5s
mariadb-sts-3   0/1     Pending             0          0s
mariadb-sts-3   0/1     Pending             0          0s
mariadb-sts-3   0/1     Pending             0          1s
mariadb-sts-3   0/1     ContainerCreating   0          1s
mariadb-sts-3   1/1     Running             0          4s

# Alternatively here we can use the kubectl edit command to change the number of replicas

我们可以得出结论,当缩小规模时,最后一个副本被终止,而当扩大规模时,数量前缀按顺序增加,在前一个Pod处于 "运行 "状态后,没有Pod被创建(比较Pod 2和3)。

Pod可以用kubectl delete命令删除,并将以相同的序号前缀重新创建。

# Since Pods retain their sticky identity
# let's remove Pod with ordinal index 0
$ kubectl delete pod mariadb-sts-0
pod "mariadb-sts-0" deleted

# Watch the Pods during deletion
$ kubectl get pods -w
NAME            READY   STATUS    RESTARTS   AGE
mariadb-sts-0   1/1     Running   0          31m
mariadb-sts-1   1/1     Running   0          31m
mariadb-sts-2   1/1     Running   0          24m
mariadb-sts-3   1/1     Running   0          24m
mariadb-sts-0   1/1     Terminating   0          31m
mariadb-sts-0   0/1     Terminating   0          31m
mariadb-sts-0   0/1     Terminating   0          31m
mariadb-sts-0   0/1     Terminating   0          31m
mariadb-sts-0   0/1     Pending       0          0s
mariadb-sts-0   0/1     Pending       0          0s
mariadb-sts-0   0/1     ContainerCreating   0          0s
mariadb-sts-0   1/1     Running             0          4s

由于PersistentVolumeClaims是动态配置的,我们也可以寻找它们

$ kubectl get pvc -l app=mariadb
NAME                    STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
datadir-mariadb-sts-0   Bound    pvc-bdc2be42-f3b6-467e-a3b2-ad5bf882c556   300M       RWO            standard       33m
datadir-mariadb-sts-1   Bound    pvc-f05d44c6-0bfe-4ba4-a6c4-8312341b8367   300M       RWO            standard       33m
datadir-mariadb-sts-2   Bound    pvc-6573cd4d-8e83-4273-8764-33fb2bce6633   300M       RWO            standard       33m

每个Pod都有自己的持久化卷创建,所以我们来测试一下。在第一个Pod中创建样本数据

$ kubectl exec -it mariadb-sts-0 -- mariadb -uroot -psecret -e "create database if not exists mytest0; use mytest0; create table t(t int); insert into t values (1),(2); select * from t;" 
+------+
| t    |
+------+
|    1 |
|    2 |
+------+

现在我们将能够从该Pod中获取数据,但不能从其他Pod中获取。为了获得与第一个Pod相同的数据,我们可以使用MariaDB的复制功能

$ kubectl exec mariadb-sts-0 -- mariadb -uroot -psecret -e "show databases like '%test%'; use mytest0; select * from t;"
Database (%test%)
mytest0
t
1
2
$ kubectl exec mariadb-sts-1 -- mariadb -uroot -psecret -e "show databases like '%test%'; use mytest0; select * from t;"
ERROR 1049 (42000) at line 1: Unknown database 'mytest0'
command terminated with exit code 1

当删除Pod时,尝试做一些作业来验证持久化卷是否会保留。

要删除statefulset,请使用kubectl delete statefulset 命令。

总结

这篇博客展示了如何创建一个MariaDB Statefulset应用程序以及如何使用它。

通过这篇博客,我们完成了关于MariaDB和K8s的小系列博客。

欢迎大家在Zulip上讨论这个问题。

帖子导航

较早的帖子 较早的帖子:
MariaDB & K8s。使用持久化卷轴部署MariaDB和WordPress

较新的帖子 较新的帖子:
MariaDB服务器文档的PDF格式