本文已超过一年。较旧的文章可能包含过时内容。请检查页面中的信息自发布以来是否已不再正确。
使用 StatefulSets 在 Kubernetes 上运行 MongoDB
警告
本文发表于几年前。代码示例需要修改才能在当前的 Kubernetes 集群上运行。传统观点认为不能在容器中运行数据库。“容器是无状态的!”他们说,“数据库没有状态就没意义!”
当然,这完全不是事实。在 Google,一切都运行在容器中,包括数据库。你只需要合适的工具。Kubernetes 1.5 包含了新的 StatefulSet API 对象(在早期版本中,StatefulSet 被称为 PetSet)。通过 StatefulSets,Kubernetes 让运行有状态工作负载(如数据库)变得容易得多。
如果你看过我之前的文章,你就知道如何使用 Docker 创建 MEAN Stack 应用,然后将其迁移到 Kubernetes 以提供更简易的管理和可靠性,以及创建 MongoDB 副本集以提供冗余和高可用性。
虽然我上一篇博客文章中的副本集可以工作,但你需要遵循一些烦人的步骤。你必须手动为每个副本创建一个磁盘、一个 ReplicationController 和一个服务。扩缩副本集意味着需要手动管理所有这些资源,这容易出错,并且会使你的有状态应用面临风险。在之前的例子中,我们创建了一个 Makefile 来简化这些资源的管理,但如果 Kubernetes 可以直接帮我们处理所有这些事情,那就太棒了。
有了 StatefulSets,这些麻烦终于消失了。你可以在 Kubernetes 中本地创建和管理 MongoDB 副本集,而无需脚本和 Makefiles。我们来看看如何实现。
注意:StatefulSets 目前是一个 Beta 资源。用于自动配置的Sidecar 容器也不受支持。
先决条件和设置
在我们开始之前,你需要一个 Kubernetes 1.5+ 版本和 Kubernetes 命令行工具。如果你想跟着本教程操作并使用 Google Cloud Platform,你还需要 Google Cloud SDK。
创建 Google Cloud 项目并设置好 Google Cloud SDK 后(提示:gcloud init),我们可以创建集群。
要创建 Kubernetes 1.5 集群,运行以下命令:
gcloud container clusters create "test-cluster"
这将创建一个三个节点的 Kubernetes 集群。你可以根据需要定制此命令。
然后,验证并连接到集群:
gcloud container clusters get-credentials test-cluster
设置 MongoDB 副本集
要设置 MongoDB 副本集,你需要三样东西:一个 StorageClass、一个 Headless Service 和一个 StatefulSet。
我已经为你创建了这些配置文件的示例,你可以从 GitHub 克隆:
git clone https://github.com/thesandlord/mongo-k8s-sidecar.git
cd /mongo-k8s-sidecar/example/StatefulSet/
要创建 MongoDB 副本集,运行这两个命令:
kubectl apply -f googlecloud\_ssd.yaml
kubectl apply -f mongo-statefulset.yaml
就这样!通过这两个命令,你就启动了运行高可用、高冗余 MongoDB 副本集所需的所有组件。
从高层来看,它看起来像这样:
我们来详细检查每个部分。
StorageClass
存储类告诉 Kubernetes 为数据库节点使用哪种存储。你可以在许多不同的环境中设置多种类型的 StorageClasses。例如,如果你在自己的数据中心运行 Kubernetes,可以使用 GlusterFS。在 GCP 上,你的存储选择是 SSD 和硬盘。目前有适用于 AWS、Azure、Google Cloud、GlusterFS、OpenStack Cinder、vSphere、Ceph RBD 和 Quobyte 的驱动程序。
StorageClass 的配置如下:
kind: StorageClass
apiVersion: storage.k8s.io/v1beta1
metadata:
name: fast
provisioner: kubernetes.io/gce-pd
parameters:
type: pd-ssd
此配置创建了一个名为“fast”的新 StorageClass,它由 SSD 卷支持。StatefulSet 现在可以请求一个卷,StorageClass 会自动创建它!
部署此 StorageClass:
kubectl apply -f googlecloud\_ssd.yaml
Headless Service
创建了 Storage Class 后,你需要创建一个 Headless Service。它们就像普通的 Kubernetes Service 一样,只是它们不会为你执行任何负载均衡。当与 StatefulSets 结合使用时,它们可以为你提供唯一的 DNS 地址,让你直接访问 Pod!这非常适合创建 MongoDB 副本集,因为我们的应用需要分别连接到所有 MongoDB 节点。
Headless Service 的配置如下:
apiVersion: v1
kind: Service
metadata:
name: mongo
labels:
name: mongo
spec:
ports:
- port: 27017
targetPort: 27017
clusterIP: None
selector:
role: mongo
你可以看出这是一个 Headless Service,因为 clusterIP 设置为“None”。除此之外,它看起来与任何普通 Kubernetes Service 完全相同。
StatefulSet
重头戏来了。StatefulSet 实际上运行 MongoDB 并协调一切。StatefulSets 在某些方面与 Kubernetes ReplicaSets 不同(不要与 MongoDB 副本集混淆!),这使得它们更适合有状态应用。与 Kubernetes ReplicaSets 不同,在 StatefulSet 下创建的 Pod 具有一些独特的属性。Pod 的名称不是随机的,每个 Pod 都有一个序号名称。结合 Headless Service,这使得 Pod 具有稳定的标识。此外,Pod 是一个接一个创建的,而不是一次性全部创建,这有助于引导有状态系统。你可以在文档中阅读更多关于 StatefulSets 的信息。
就像之前一样,这个“Sidecar”容器将自动配置 MongoDB 副本集。“Sidecar”是一个帮助容器,帮助主容器完成工作。
StatefulSet 的配置如下:
apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
name: mongo
spec:
selector:
matchLabels:
role: mongo
environment: test
serviceName: "mongo"
replicas: 3
template:
metadata:
labels:
role: mongo
environment: test
spec:
terminationGracePeriodSeconds: 10
containers:
- name: mongo
image: mongo
command:
- mongod
- "--replSet"
- rs0
- "--smallfiles"
- "--noprealloc"
ports:
- containerPort: 27017
volumeMounts:
- name: mongo-persistent-storage
mountPath: /data/db
- name: mongo-sidecar
image: cvallance/mongo-k8s-sidecar
env:
- name: MONGO_SIDECAR_POD_LABELS
value: "role=mongo,environment=test"
volumeClaimTemplates:
- metadata:
name: mongo-persistent-storage
spec:
storageClassName: "fast"
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 100Gi
有点长,但相当直观。
第一段描述了 StatefulSet 对象。然后,我们进入 Metadata 部分,你可以在其中指定标签和副本数量。
接下来是 Pod 规格。terminationGracePeriodSeconds 用于在你缩减副本数量时优雅地关闭 Pod,这对于数据库很重要!然后显示了两个容器的配置。第一个容器运行 MongoDB,并使用命令行参数配置副本集名称。它还将持久存储卷挂载到 /data/db,这是 MongoDB 保存数据的位置。第二个容器运行 sidecar。
最后,是 volumeClaimTemplates。这与我们之前创建的 StorageClass 通信以供给卷。它将为每个 MongoDB 副本供给一个 100 GB 的磁盘。
使用 MongoDB 副本集
此时,你的集群中应该创建了三个 Pod。它们对应于 MongoDB 副本集中的三个节点。你可以使用此命令查看它们:
kubectl get pods
NAME READY STATUS RESTARTS AGE
mongo-0 2/2 Running 0 3m
mongo-1 2/2 Running 0 3m
mongo-2 2/2 Running 0 3m
由 Headless Service 支持的 StatefulSet 中的每个 Pod 都将具有一个稳定的 DNS 名称。模板遵循以下格式:<pod-name>.<service-name>
这意味着 MongoDB 副本集的 DNS 名称为:
mongo-0.mongo
mongo-1.mongo
mongo-2.mongo
你可以在应用的连接字符串 URI 中直接使用这些名称。
在这种情况下,连接字符串 URI 为:
mongodb://mongo-0.mongo,mongo-1.mongo,mongo-2.mongo:27017/dbname\_?
就这样!
扩缩 MongoDB 副本集
StatefulSets 的一个巨大优势是你可以像 Kubernetes ReplicaSets 一样扩缩它们。如果你想要 5 个 MongoDB 节点而不是 3 个,只需运行 scale 命令:
kubectl scale --replicas=5 statefulset mongo
Sidecar 容器将自动配置新的 MongoDB 节点加入副本集。
在你的连接字符串 URI 中包含两个新的节点(mongo-3.mongo 和 mongo-4.mongo),你就可以开始了。太简单了!
清理
要清理部署的资源,删除 StatefulSet、Headless Service 和供给的卷。
删除 StatefulSet:
kubectl delete statefulset mongo
删除 Service:
kubectl delete svc mongo
删除卷:
kubectl delete pvc -l role=mongo
最后,你可以删除测试集群:
gcloud container clusters delete "test-cluster"
祝你玩得开心!