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

使用 StatefulSets 在 Kubernetes 上运行 MongoDB

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

当然,这根本不是真的。在 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 和硬盘。目前有针对 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 将自动创建它!

部署此 StorageClass

kubectl apply -f googlecloud\_ssd.yaml

Headless Service

现在你已经创建了 Storage Class,你需要创建一个 Headless Service。这些就像普通的 Kubernetes 服务一样,只是它们不会为你执行任何负载均衡。当与 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 服务完全相同。

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 副本集

StatefulSet 的一个巨大优势是你可以像 Kubernetes ReplicaSet 一样扩展它们。如果你想要 5 个 MongoDB 节点而不是 3 个,只需运行 scale 命令:

kubectl scale --replicas=5 statefulset mongo

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

将两个新节点(mongo-3.mongo 和 mongo-4.mongo)包含在你的连接字符串 URI 中,你就可以开始了。太容易了!

清理

要清理已部署的资源,请删除 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"

祝你编程愉快!

要了解更多酷炫的 Kubernetes 和容器博客文章,请在 TwitterMedium 上关注我。