MariaDB & K8s: 使用持久卷部署 MariaDB 和 WordPress
在之前的博客文章 MariaDB & K8s: 创建 Secret 并在 MariaDB 部署中使用它 中,我们使用 Secrets 资源来隐藏机密的 root 用户数据。而在该系列更早的一篇博客文章 MariaDB & K8s: 容器/部署之间的通信 中,我们在一个 Pod 中创建了 2 个容器(即 MariaDB 和 phpmyadmin)。那种部署方式没有使用任何持久卷。
在本篇博客文章中,我们将为 MariaDB 和 WordPress 应用程序创建单独的 Deployment,并为两者创建一个 Service 以便连接它们。此外,我们还将在 MariaDB Deployment 的 Pod 中创建 Volume。
配置文件
在前一篇博客文章和再前一篇博客文章的基础上,我们已经创建了 MariaDB Secret 和 MariaDB ConfigMap,这里我们假设它们已经在集群中存在。
$ kubectl describe secret/mariadb-secret
Name: mariadb-secret
Namespace: default
Labels: <none>
Annotations: <none>
Type: Opaque
Data
====
mariadb-root-password: 6 bytes
$ kubectl describe cm mariadb-configmap
Name: mariadb-configmap
Namespace: default
Labels: <none>
Annotations: <none>
Data
====
database_url:
----
mariadb-internal-service
BinaryData
====
Events: <none>
让我们添加一个用于 MariaDB Deployment 和 Service 的配置文件(GitHub 文件)。
apiVersion: v1
kind: Service
metadata:
name: mariadb-internal-service
spec:
selector:
app: mariadb
ports:
- protocol: TCP
port: 3306
targetPort: 3306
clusterIP: None
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: mariadb-deployment
spec: # specification for deployment resource
replicas: 1
selector:
matchLabels:
app: mariadb
template: # blueprint for Pod
metadata:
labels:
app: mariadb # service will look for this label
spec: # specification for Pod
containers:
- name: mariadb
image: mariadb
ports:
- containerPort: 3306 #default one
env:
- name: MARIADB_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mariadb-secret
key: mariadb-root-password
- name: MARIADB_DATABASE
value: wordpress
创建的 Service 是一个Headless 类型的 Service,没有分配集群 IP,也不需要负载均衡。与之前的博客文章唯一不同的是在容器启动期间创建了数据库“wordpress”,这是 WordPress Deployment 的一个要求。
让我们添加一个用于 WordPress Deployment 和 Service 的配置文件(GitHub 文件)。
apiVersion: v1
kind: Service
metadata:
name: wordpress
spec:
selector:
app: wordpress
ports:
- port: 80
targetPort: 80
protocol: TCP #default
nodePort: 31000
type: LoadBalancer
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: wordpress-deployment
spec: # specification for deployment resource
replicas: 1
selector:
matchLabels:
app: wordpress
template: # blueprint for Pod
metadata:
labels:
app: wordpress
spec: # specification for Pod
containers:
- name: wordpress
image: wordpress:latest
ports:
- containerPort: 80
env:
- name: WORDPRESS_DB_HOST
valueFrom:
configMapKeyRef:
name: mariadb-configmap
key: database_url
- name: WORDPRESS_DB_PASSWORD
valueFrom:
secretKeyRef:
name: mariadb-secret
key: mariadb-root-password
- name: WORDPRESS_DB_USER
value: root
- name: WORDPRESS_DEBUG
value: "1"
创建的 Service 是一个LoadBalancer 类型(Minikube 支持它),用于平衡 Service 的负载。我们暴露了一个固定的节点端口,用作外部端口。此端口号应在 30000-32767 之间。创建的 Deployment 使用 ConfigMap 中的信息作为环境变量 WORDPRESS_DB_HOST
的值,使用 Secret 中的信息作为环境变量 WORDPRESS_DB_PASSWORD
的值,并引用容器端口 80。
创建资源并验证
$ kubectl apply -f mariadb-configmap.yaml
configmap/mariadb-configmap created
$ kubectl apply -f mariadb-secret.yaml
secret/mariadb-secret created
$ kubectl apply -f mariadb-deployment-pvc.yaml
service/mariadb-internal-service created
deployment.apps/mariadb-deployment created
$ kubectl apply -f wordpress-deployment-pvc.yaml
service/wordpress created
deployment.apps/wordpress-deployment created
$ minikube service wordpress
|-----------|-----------|-------------|---------------------------|
| NAMESPACE | NAME | TARGET PORT | URL |
|-----------|-----------|-------------|---------------------------|
| default | wordpress | 80 | http://192.168.49.2:31000 |
|-----------|-----------|-------------|---------------------------|
🎉 Opening service default/wordpress in default browser...
执行最后一个命令后,我们将获得一个 URL,可以通过该 URL 安装 WordPress。

添加持久卷
当 Pod 崩溃时,kubectl 会重启容器并从一个干净的状态开始,这可能会导致数据一致性问题。为了解决这个问题,存在与 Pod 相关的 Volume 抽象。Pod 可以有多种卷类型,例如默认且与 Pod 生命周期绑定的临时卷类型、其数据可以通过 Pod 中的文件消费的 ConfigMap 和 Secret 类型,以及本篇博客关注的 PersistentVolumeClaim 类型,它表示在 Pod 中挂载 PersistentVolume 的请求。
一个 PersistentVolume (PV) 资源是集群中的一块存储,它由管理员手动配置,或者由 Kubernetes 使用 StorageClass 进行动态配置(集群中有一个默认的 StorageClass 使用 hostPath provisioner)。
一个 PersistentVolumeClaim (PVC) 是用户对存储的请求,可以由 PV 满足。Claim 请求特定的容量和访问模式。
PersistentVolumes 和 PersistentVolumeClaims 独立于 Pod 的生命周期,并且在 Pod 重启、重新调度甚至删除后保留数据。
为了验证这一点,让我们添加一些数据并重启 MariaDB Pod。
$ kubectl get pods -l app=mariadb
NAME READY STATUS RESTARTS AGE
mariadb-deployment-74f8c57cbf-t4whv 1/1 Running 0 73m
$ kubectl exec mariadb-deployment-74f8c57cbf-t4whv -- mariadb -uroot -psecret -e "create database if not exists mytest;use mytest; create table t(t int); insert into t values (1),(2); select * from t";
t
1
2
a
# Watch in first terminal state of Pods (MariaDB Pod will be restarted, wordpress Pod will be running)
$ kubectl get pods -w
NAME READY STATUS RESTARTS AGE
mariadb-deployment-74f8c57cbf-t4whv 1/1 Running 0 78m
wordpress-deployment-79697d4fd5-4qsn7 1/1 Running 0 78m
wordpress-deployment-79697d4fd5-s4gc9 1/1 Running 0 78m
mariadb-deployment-74f8c57cbf-t4whv 1/1 Terminating 0 79m
mariadb-deployment-74f8c57cbf-t4whv 0/1 Terminating 0 79m
mariadb-deployment-74f8c57cbf-t4whv 0/1 Terminating 0 79m
# Scaling deployment replicas to 0, will restart the Pod.
# Start command in the second terminal
$ kubectl scale deployment mariadb-deployment --replicas=0
deployment.apps/mariadb-deployment scaled
# Watch again MariaDB Pod creation before starting command to scale new replicas
# wordpress Pod is again in running state, not affected
$ kubectl get pods -w
NAME READY STATUS RESTARTS AGE
wordpress-deployment-79697d4fd5-4qsn7 1/1 Running 0 83m
wordpress-deployment-79697d4fd5-s4gc9 1/1 Running 0 83m
mariadb-deployment-74f8c57cbf-qd786 0/1 Pending 0 0s
mariadb-deployment-74f8c57cbf-qd786 0/1 Pending 0 0s
mariadb-deployment-74f8c57cbf-qd786 0/1 ContainerCreating 0 0s
mariadb-deployment-74f8c57cbf-qd786 1/1 Running 0 3s
# In other terminal
$ kubectl scale deployment mariadb-deployment --replicas=1
deployment.apps/mariadb-deployment scaled
# Check existence of already created database 'mytest'
$ kubectl exec svc/mariadb-internal-service -- mariadb -uroot -psecret -e "show databases"
Database
information_schema
mysql
performance_schema
sys
wordpress
如上所示,在对 ReplicaSet 进行零扩容(zero scaling)时,旧的 Pod 被终止,然后创建了一个新的 Pod,这类似于 Pod 的重启。
在扩容到单个副本之前,可以看到只有 WordPress Pod 在运行,没有 MariaDB Pod;在扩容到单个副本之后,我们获得了一个 MariaDB Pod,其 Status
列显示了 Pod 生命周期的创建阶段。
现在让我们修改脚本,添加 PersistentVolumeClaim,然后运行相同的场景。
首先让我们删除旧的 MariaDB Deployment
$ kubectl delete -f mariadb-deployment-pvc.yaml
service "mariadb-internal-service" deleted
deployment.apps "mariadb-deployment" deleted
许多集群环境都安装了默认的 StorageClass 。当 PersistentVolumeClaim 中未指定 StorageClass 时,将使用集群的默认 StorageClass。我们可以检查 StorageClass
$ kubectl get storageclass
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
standard (default) k8s.io/minikube-hostpath Delete Immediate false 145d
创建 PersistentVolumeClaim 时,将根据 StorageClass 配置动态配置一个 PersistentVolume。
让我们添加一个名为“mariadb-pv-claim”的 PersistentVolumeClaim 资源,具有 RW 访问模式和 300 MB 的存储容量,并使用默认的 StorageClass(Persistent Volume 的动态配置)。
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mariadb-pv-claim
labels:
app: mariadb
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 300M
此外,我们将使用以下内容更新容器规范
containers:
- name: mariadb
image: mariadb
ports:
- containerPort: 3306 #default one
env:
- name: MARIADB_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mariadb-secret
key: mariadb-root-password
- name: MARIADB_DATABASE
value: wordpress
volumeMounts:
- name: mariadb-pv
mountPath: /var/lib/mysql
volumes:
- name: mariadb-pv
persistentVolumeClaim:
claimName: mariadb-pv-claim
在容器“mariadb”中,有一个名为“mariadb-pv”的挂载,用于容器内部的默认数据目录路径,其存储大小将等于 Claim 的大小,因为卷挂载名称“mariadb-pv”是“mariadb”容器特有的,并且类型为 persistentVolumeClaim,后者为 Pod 定义并引用了集群中已创建的 PersistentVolumeClaim 资源“mariadb-pv-claim”。
现在让我们应用此部署,验证结果,并再次尝试在 Pod 中创建数据库,重启 Pod,然后尝试获取旧数据。
$ kubectl apply -f mariadb-deployment-pvc.yaml
persistentvolumeclaim/mariadb-pv-claim created
service/mariadb-internal-service created
deployment.apps/mariadb-deployment created
# Get information about pvc resource - pvc is bound to the pv, check kubectl get pv
$ kubectl get pvc -l app=mariadb
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
mariadb-pv-claim Bound pvc-57467ba5-bf6e-4b91-8741-c6f24e9c5861 300M RWO standard 31s
# Get the pods
$ kubectl get pods -l app=mariadb
NAME READY STATUS RESTARTS AGE
mariadb-deployment-58c7c4d75c-h45nl 1/1 Running 0 100s
# Watch in first terminal
$ kubectl get pods -w -l app=mariadb
NAME READY STATUS RESTARTS AGE
mariadb-deployment-58c7c4d75c-h45nl 1/1 Running 0 3m53s
mariadb-deployment-58c7c4d75c-h45nl 1/1 Terminating 0 4m13s
mariadb-deployment-58c7c4d75c-lsfdw 0/1 Pending 0 0s
mariadb-deployment-58c7c4d75c-lsfdw 0/1 Pending 0 0s
mariadb-deployment-58c7c4d75c-lsfdw 0/1 ContainerCreating 0 0s
mariadb-deployment-58c7c4d75c-h45nl 0/1 Terminating 0 4m15s
mariadb-deployment-58c7c4d75c-h45nl 0/1 Terminating 0 4m15s
mariadb-deployment-58c7c4d75c-h45nl 0/1 Terminating 0 4m15s
mariadb-deployment-58c7c4d75c-lsfdw 1/1 Running 0 4s
# Execute command in the second terminal - note above that new Pod has been created
$ kubectl scale deploy mariadb-deployment --replicas=0 && kubectl scale deploy mariadb-deployment --replicas=1
deployment.apps/mariadb-deployment scaled
deployment.apps/mariadb-deployment scaled
# Verify results from newly created Pod by inspecting data created in the old Pod
$ kubectl exec mariadb-deployment-58c7c4d75c-lsfdw -- mariadb -uroot -psecret -e "show databases like '%test%'; use mytest; select * from t;"
Database (%test%)
mytest
t
1
2
如上所示,我们能够获取到对 Pod 重启/终止具有抵抗力的数据。
对于一个 Deployment,我们可以拥有多个 PVC 资源。尝试为 WordPress Deployment 创建一个 PVC 资源,用于卷挂载路径 /var/www/html。
通过这种方式,我们实现了数据一致性并部署了有状态应用(statefulset application)。
不建议使用 Deployment 来部署有状态应用(statefulset applications),因为它们是为无状态应用(stateless applications)设计的,所以 Deployment 的所有副本共享同一个 PersistentVolumeClaim,只有 ReadOnlyMany 或 ReadWriteMany 模式的卷才能在这种设置下工作。即使是使用 ReadWriteOnce 卷的单副本 Deployment 也不推荐,因为默认的 Deployment 策略会导致第二个 Pod 被创建,可能会发生死锁(更多信息请参考此链接)。
结论与未来工作
本篇博客文章展示了如何创建 2 个 Deployment 以及如何通过 Service 将它们连接起来。我们还应用了之前学到的 K8s 概念,以便更充分地利用 K8s API。
在数据库示例中,我们展示了数据一致性的必要性和 Pod 的临时性,并介绍了用于在 K8s 集群上进行动态配置的 PersistentVolumeClaim 资源,它有助于解决这类问题。然而,本篇博客仅用于演示和学习概念。在接下来的博客文章中,我们将创建一个不使用 Deployment 的StatefulSet 应用,它用于确保数据一致性。
欢迎在 Zulip 上进行讨论。
阅读更多
- 在 K8s 中启动 MariaDB
- MariaDB & K8s: 容器/Deployment 之间的通信
- MariaDB & K8s: 创建 Secret 并在 MariaDB 部署中使用它
- MariaDB & K8s: 使用持久卷部署 MariaDB 和 WordPress
- 在 K8s 中创建有状态 MariaDB 应用
- 使用容器进行 MariaDB 复制
- MariaDB & K8s: 如何在 K8s 中复制 MariaDB