本文发表于一年多前。旧文章可能包含过时内容。请检查页面中的信息自发布以来是否已变得不正确。

在 Kubernetes 上使用 StatefulSets 运行 MongoDB

传统观点认为你不能在容器中运行数据库。“容器是无状态的!”他们说,“没有状态的数据库毫无意义!”

当然,这根本不是真的。在 Google,一切都在容器中运行,包括数据库。你只需要正确的工具。Kubernetes 1.5 包含了新的 StatefulSet API 对象(在之前的版本中,StatefulSet 被称为 PetSet)。有了 StatefulSets,Kubernetes 让运行有状态工作负载(如数据库)变得容易得多。

如果你阅读过我之前的文章,你就会知道如何使用 Docker 创建一个 MEAN Stack 应用程序,然后 将其迁移到 Kubernetes 以提供更简单的管理和可靠性,以及 创建 MongoDB 副本集 以提供冗余和高可用性。

虽然我之前博客文章中的副本集有效,但你还需要遵循一些烦人的步骤。你必须手动为每个副本创建一个磁盘、一个 ReplicationController 和一个服务。向上或向下扩展副本集意味着手动管理所有这些资源,这很容易出错,并将使你的有状态应用程序面临风险。在前面的例子中,我们创建了一个 Makefile 来简化这些资源的管理,但如果 Kubernetes 能为我们处理所有这些事情,那就太棒了。

有了 StatefulSets,这些麻烦终于消失了。你可以在 Kubernetes 中原生创建和管理你的 MongoDB 副本集,无需脚本和 Makefile。让我们看看如何实现。

注意: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

存储类(StorageClass)告诉 Kubernetes 数据库节点使用哪种存储。您可以在许多不同的环境中设置许多不同类型的 StorageClass。例如,如果您在自己的数据中心运行 Kubernetes,您可以使用 GlusterFS。在 GCP 上,您的存储选择是 SSD 和硬盘。目前有适用于 AWSAzureGoogle CloudGlusterFSOpenStack CindervSphereCeph RBDQuobyte 的驱动程序。

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 将自动创建它!

部署此存储类

kubectl apply -f googlecloud\_ssd.yaml

无头服务 (Headless Service)

现在您已经创建了存储类,您需要创建一个无头服务。它们就像普通的 Kubernetes 服务,只是它们不为您做任何负载均衡。当与 StatefulSets 结合使用时,它们可以为您提供独特的 DNS 地址,让您可以直接访问 Pod!这非常适合创建 MongoDB 副本集,因为我们的应用程序需要单独连接到所有 MongoDB 节点。

无头服务的配置如下:

apiVersion: v1
kind: Service
metadata:
  name: mongo
  labels:
    name: mongo
spec:
  ports:
    - port: 27017
      targetPort: 27017
  clusterIP: None
  selector:
    role: mongo

您可以通过 clusterIP 设置为“None”来判断这是一个无头服务。除此之外,它看起来与任何普通的 Kubernetes Service 完全相同。

StatefulSet

压轴大戏。StatefulSet 实际上运行 MongoDB 并将所有内容协调在一起。StatefulSet 与 Kubernetes ReplicaSet(不要与 MongoDB 副本集混淆!)在某些方面有所不同,使其更适合有状态应用程序。与 Kubernetes ReplicaSet 不同,在 StatefulSet 下创建的 Pod 具有一些独特的属性。Pod 的名称不是随机的,而是每个 Pod 都有一个有序名称。结合无头服务,这使得 Pod 具有稳定的标识。此外,Pod 是一个接一个地创建,而不是一次性全部创建,这在引导有状态系统时会有所帮助。您可以在文档中阅读有关 StatefulSet 的更多信息。

就像之前一样,这个“边车”容器将自动配置 MongoDB 副本集。“边车”是一个辅助容器,它帮助主容器完成其工作。

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 对象。然后,我们进入元数据部分,您可以在其中指定标签和副本数量。

接下来是 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

由无头服务支持的 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 个,只需运行扩缩命令

kubectl scale --replicas=5 statefulset mongo

sidecar 容器将自动配置新的 MongoDB 节点加入副本集。

将两个新节点 (mongo-3.mongo & mongo-4.mongo) 包含在您的连接字符串 URI 中,即可开始使用。太简单了!

清理

要清理已部署的资源,请删除 StatefulSet、无头服务和已提供的卷。

删除 StatefulSet

kubectl delete statefulset mongo

删除服务

kubectl delete svc mongo

删除卷

kubectl delete pvc -l role=mongo

最后,您可以删除测试集群

gcloud container clusters delete "test-cluster"

祝您编程愉快!

想要了解更多 Kubernetes 和容器相关的精彩博客文章,请在 TwitterMedium 上关注我。