在这篇博客中,我们将为MariaDB和WordPress应用程序创建单独的部署,并为两者创建一个服务,以便连接它们。此外,我们将在MariaDB部署的Pod中创建卷。
配置文件
在最新的博客和之前的博客中,我们已经创建了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部署和服务添加一个配置文件(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
创建的服务是一个Headless 类型的服务,没有分配集群IP,也不需要进行负载均衡。与上一篇博客唯一不同的是,在容器启动时创建了数据库 "wordpress",这是WordPress部署的一个要求。
让我们为WordPress的部署和服务添加一个配置文件(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"
创建的服务是一个LoadBalancer类型*(Minikube* 支持它),它可以平衡一个服务的负载。我们正在暴露一个固定的节点端口,作为一个外部端口使用。这个数字应该在30000-32767的范围内。创建的部署使用环境变量WORDPRESS_DB_HOST 的ConfigMap信息,环境变量WORDPRESS_DB_PASSWORD的Secret和参考容器端口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...
在最后一条命令之后,我们将得到一个可以安装WordPress的URL。
添加持久性卷
当Pod崩溃时,kubectl会重新启动容器,并从一个干净的状态开始,这对数据的一致性是一个问题。为了解决这个问题,有一个与Pod相关的卷的抽象概念。Pod可以有多种卷的类型,比如ephemeral卷类型,它是默认的,与Pod的寿命有关;ConfigMap和Secret类型,其数据可以被Pod中的文件所消耗;以及本博客感兴趣的PersistentVolumeClaim类型,作为在Pod中装载PersistentVolume 的一个请求。
PersistentVolume(PV)资源是集群中的一块存储,它由管理员手动配置,或由Kubernetes使用StorageClass动态配置(在集群中有一个默认的StorageClass,使用hostPath配置器)。
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的零扩展时,旧的Pod被终止,新的Pod被创建,这与Pod的重启类似。
在使用单个副本进行扩展之前,可以看到只有WordPress Pod在运行,没有MariaDB Pod,而在扩展到单个副本之后,我们得到了一个MariaDB Pod,其Status列显示了Pod生命周期的创建阶段。
现在让我们通过添加PersistenceVolumeClaim来改变脚本,之后再运行同样的场景。
首先让我们删除旧的MariaDB部署。
$ 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时,PersistentVolume是根据StorageClass配置动态配置的。
让我们添加一个名为 "mariadb-pv-claim"的PersistentVolumeClaim资源,它具有RW访问权限,资源存储量为300MB,使用默认的StorageClass(动态配置持久性卷)。
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"的挂载,用于容器内的默认数据目录路径,它的存储大小等于索赔的大小,因为卷挂载名称 "mariadb-pv"是 "mariadb"容器所特有的,属于persistentVolumeClaim类型,它是为Pod定义的,引用集群中已经创建的PersistentVolumeClaim资源 "mariadb-pv-索赔"。
现在让我们应用这个部署,验证结果并再次尝试在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重启/终止有抵抗力的数据。
我们可以在一个部署中拥有多个PVC资源。尝试为WordPress部署创建一个PVC资源,用于卷挂载路径*/var/www/html*。
这样我们就实现了数据的一致性,并部署了statefulset应用程序。
不建议使用Deployment作为部署statefulset应用程序的方式,因为它们是为无状态的应用程序设计的,所以一个Deployment的所有副本共享相同的PersistentVolumeClaim,只有ReadOnlyMany或ReadWriteMany的卷才能在这种设置下工作。甚至不建议使用ReadWriteOnce卷的一个副本的部署,因为由于默认的部署策略,第二个Pod将被创建,并可能发生死锁(更多信息请见此链接)。
结论和未来工作
这篇博客展示了如何创建2个部署以及如何通过服务连接它们。我们还应用了以前学过的K8s概念,以便更多地利用K8s的API。
在数据库的例子中,我们展示了数据一致性的需求和Pod的短暂性,以及介绍了PersistentVolumeClaim资源在K8s集群上的动态配置,这有助于解决这类问题。然而,这篇博客只是为了演示和学习这些概念。在接下来的博客中,我们将创建一个没有部署的StatefulSet应用程序,用来确保数据的一致性。
欢迎大家在Zulip上聊一聊。
