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

使用 Kubernetes Pet Set 实现千个 Cassandra 实例

编者按:这篇博文是关于 Kubernetes 1.3 新特性的系列深度文章之一

运行希腊神兽竞赛

为了Kubernetes 1.3 发布,我们想对新的 Pet Set 进行一番测试。通过测试上千个 Cassandra 实例,我们可以确保 Kubernetes 1.3 已为生产做好准备。请继续阅读,了解我们如何将 Cassandra 适配到 Kubernetes,并进行了有史以来最大规模的部署。

如今,将容器与基本有状态应用程序一起使用相当简单。使用持久卷,您可以在 Pod 中挂载磁盘,并确保您的数据在 Pod 生命周期结束后仍然存在。然而,对于分布式有状态应用程序的部署,事情可能会变得更加棘手。通过 Kubernetes 1.3,新的 Pet Set 组件让一切变得容易得多。为了大规模测试这项新功能,我们决定举办希腊神兽竞赛!我们在数百个可用区内进行了数十万场比赛,赛马和许多其他古希腊怪物参加了比赛。

File:Cassandra1.jpeg
众所周知,Kubernetes 一词源于古希腊语:κυβερνήτης。这意味着舵手、领航员、船长或船主。因此,为了记录比赛结果,我们需要一个数据存储,我们选择了 Cassandra。Κασσάνδρα,卡桑德拉,她是特洛伊国王普里阿摩斯和赫卡柏女王的女儿。鉴于多次提及古希腊语,我们认为举办古希腊怪物比赛是合适的。

从那时起,故事就有点偏离了,因为卡桑德拉实际上也是 Pet。请继续阅读,我们将进行解释。

Kubernetes 1.3 中一项令人兴奋的新功能是 Pet Set。为了组织 Kubernetes 内部容器的部署,提供了不同的部署机制。这些组件的例子包括 Resource Controller 和 Daemon Set。Pet Sets 是一项新功能,它提供了在 Kubernetes 内部部署容器作为 Pet 的能力。Pet Sets 保证了 Pet/Pod 部署的各个方面的身份:DNS 名称、一致存储和有序的 Pod 索引。以前,使用 Deployment 和 Replication Controller 等组件,只会部署一个具有弱解耦身份的应用程序。弱身份非常适合管理微服务等应用程序,在这种情况下,服务发现很重要,应用程序是无状态的,并且单个 Pod 的命名无关紧要。许多软件应用程序确实需要强身份,包括许多不同类型的分布式有状态系统。Cassandra 是一个需要一致网络身份和稳定存储的分布式应用程序的绝佳例子。

Pet Sets 提供以下功能

  • 一个稳定的主机名,可在 DNS 中供他人使用。编号基于 Pet Set 名称,从零开始。例如 cassandra-0。
  • Pet 的序数索引。0、1、2、3 等。
  • 与 Pet 的序数和主机名相关联的稳定存储。
  • 通过 DNS 可以进行对等发现。对于 Cassandra,在创建 Pet 之前就知道对等体的名称。
  • 启动和拆除顺序。知道下一个要创建的 Pet 的编号,以及在缩小 Pet Set 大小时将被销毁的 Pet。此功能对于在缩小集群大小时,从 Pet 中排出数据等管理任务非常有用。

如果您的应用程序具有其中一项或多项要求,则它可能是 Pet Set 的候选者。
一个相关的类比是,一个 Pet Set 由宠物狗组成。如果您有一只白色、棕色或黑色的狗,而棕色的狗跑掉了,您可以换一只棕色的狗,没有人会注意到。如果随着时间的推移,您只用白色的狗来替换您的狗,那么有人就会注意到。Pet Set 允许您的应用程序保持 Pet 独特的身份或毛色。

Pet Set 的示例工作负载

  • 像 Cassandra、Zookeeper、etcd 或 Elastic 这样的集群软件需要稳定的成员资格。
  • 像 MySQL 或 PostgreSQL 这样的数据库,需要随时连接到持久卷的单个实例。

只有当您的应用程序需要这些属性中的部分或全部时,才使用 Pet Set。将 Pod 作为无状态副本进行管理要容易得多。

回到我们的比赛!

正如我们所提到的,Cassandra 是通过 Pet Set 部署的完美候选者。Pet Set 非常类似于 Replica Controller,但增加了一些新功能。下面是一个 YAML 清单示例

提供 DNS 查找的无头服务

apiVersion: v1

kind: Service

metadata:

  labels:

    app: cassandra

  name: cassandra

spec:

  clusterIP: None

  ports:

    - port: 9042

  selector:

    app: cassandra-data

----

# new API name

apiVersion: "apps/v1alpha1"

kind: PetSet

metadata:

  name: cassandra

spec:

  serviceName: cassandra

  # replicas are the same as used by Replication Controllers

  # except pets are deployed in order 0, 1, 2, 3, etc

  replicas: 5

  template:

    metadata:

      annotations:

        pod.alpha.kubernetes.io/initialized: "true"

      labels:

        app: cassandra-data

    spec:

      # just as other component in Kubernetes one

      # or more containers are deployed

      containers:

      - name: cassandra

        image: "cassandra-debian:v1.1"

        imagePullPolicy: Always

        ports:

        - containerPort: 7000

          name: intra-node

        - containerPort: 7199

          name: jmx

        - containerPort: 9042

          name: cql

        resources:

          limits:

            cpu: "4"

            memory: 11Gi

          requests:

           cpu: "4"

           memory: 11Gi

        securityContext:

          privileged: true

        env:

          - name: MAX\_HEAP\_SIZE

            value: 8192M

          - name: HEAP\_NEWSIZE

            value: 2048M

          # this is relying on guaranteed network identity of Pet Sets, we

          # will know the name of the Pets / Pod before they are created

          - name: CASSANDRA\_SEEDS

            value: "cassandra-0.cassandra.default.svc.cluster.local,cassandra-1.cassandra.default.svc.cluster.local"

          - name: CASSANDRA\_CLUSTER\_NAME

            value: "OneKDemo"

          - name: CASSANDRA\_DC

            value: "DC1-Data"

          - name: CASSANDRA\_RACK

            value: "OneKDemo-Rack1-Data"

          - name: CASSANDRA\_AUTO\_BOOTSTRAP

            value: "false"

          # this variable is used by the read-probe looking

          # for the IP Address in a `nodetool status` command

          - name: POD\_IP

            valueFrom:

              fieldRef:

                fieldPath: status.podIP

        readinessProbe:

          exec:

            command:

            - /bin/bash

            - -c

            - /ready-probe.sh

          initialDelaySeconds: 15

          timeoutSeconds: 5

        # These volume mounts are persistent. They are like inline claims,

        # but not exactly because the names need to match exactly one of

        # the pet volumes.

        volumeMounts:

        - name: cassandra-data

          mountPath: /cassandra\_data

  # These are converted to volume claims by the controller

  # and mounted at the paths mentioned above.  Storage can be automatically

  # created for the Pets depending on the cloud environment.

  volumeClaimTemplates:

  - metadata:

      name: cassandra-data

      annotations:

        volume.alpha.kubernetes.io/storage-class: anything

    spec:

      accessModes: ["ReadWriteOnce"]

      resources:

        requests:
          storage: 380Gi

您可能会注意到这些容器的尺寸相当大,在生产环境中运行具有 8 个 CPU 和 16GB 内存的 Cassandra 并不罕见。您会注意到上面有两个关键的新功能:动态卷配置,当然还有 Pet Set。上面的清单将创建 5 个 Cassandra Pet/Pod,从数字 0 开始:cassandra-data-0、cassandra-data-1 等。

为了生成比赛数据,我们使用了 Kubernetes 的另一个功能,称为 Jobs。编写了简单的 Python 代码来生成比赛中怪物每秒的随机速度。然后将这些数据、位置信息、获胜者、其他数据点和指标存储在 Cassandra 中。为了可视化数据,我们使用 JHipster 生成了一个带有 Java 服务的 AngularJS UI,然后使用 D3 进行图形绘制。

其中一个 Job 的示例

apiVersion: batch/v1

kind: Job

metadata:

  name: pet-race-giants

  labels:

    name: pet-races

spec:

  parallelism: 2

  completions: 4

  template:

    metadata:

      name: pet-race-giants

      labels:

        name: pet-races

    spec:

      containers:

      - name: pet-race-giants

        image: py3numpy-job:v1.0

        command: ["pet-race-job", --length=100", "--pet=Giants", "--scale=3"]

        resources:

          limits:

            cpu: "2"

          requests:

            cpu: "2"

      restartPolicy: Never

File:Polyphemus.gif既然我们谈论的是怪物,就必须做得更大。我们向 Google Compute Engine (GCE) 部署了 1,009 个 Minion 节点,分布在 4 个区域,运行自定义版本的 Kubernetes 1.3 beta。我们在 beta 代码上运行这个演示,因为演示是在 1.3 发布日期之前设置的。对于 Minion 节点,选择了 GCE 虚拟机 n1-standard-8 机器尺寸,即具有 8 个虚拟 CPU 和 30GB 内存的虚拟机。它允许在一个节点上运行单个 Cassandra 实例,这对于磁盘 I/O 是推荐的。

然后部署了 Pet!一千个 Pet,分布在两个不同的 Cassandra 数据中心。Cassandra 分布式架构是专门为多数据中心部署量身定制的。通常,为了分离工作负载,多个 Cassandra 数据中心部署在相同的物理或虚拟数据中心内。数据在所有数据中心之间复制,但数据中心之间工作负载可能不同,因此应用程序调优也可能不同。名为“DC1-Analytics”和“DC1-Data”的数据中心各部署了 500 个 Pet。比赛数据由连接到 DC1-Data 的 Python Batch Jobs 创建,JHipster UI 连接到 DC1-Analytics。

以下是最终数据

  • 8,072 个核心。Master 使用 24 个,Minion 节点使用其余的
  • 1,009 个 IP 地址
  • Kubernetes 在 Google Cloud Platform 上设置了 1,009 条路由
  • Minion 和 Master 使用了 100,510 GB 的持久磁盘
  • 380,020 GB SSD 持久磁盘。Master 使用 20 GB,每个 Cassandra Pet 使用 340 GB。
  • 部署了 1,000 个 Cassandra 实例。是的,我们部署了 1,000 个 Pet,但有一个真的不想加入派对!从技术上讲,在 Cassandra 设置中,我们可以在不损失服务或数据的情况下损失 333 个节点。

1.3 版本中 Pet Set 的限制

  • Pet Set 是一个 alpha 资源,在 Kubernetes 1.3 之前的任何版本中都不可用。
  • 给定 Pet 的存储必须由基于所请求存储类的动态存储提供程序进行配置,或由管理员预先配置。
  • 删除 Pet Set 不会删除任何 Pet 或 Pet 存储。您需要手动删除您的 Pet 及其存储。
  • 所有 Pet Set 目前都需要一个“管理服务”,即负责 Pet 网络身份的服务。用户负责此服务。
  • 更新现有 Pet Set 目前是一个手动过程。您要么需要部署一个具有新镜像版本的新 Pet Set,要么逐个孤立 Pet 并更新其镜像,然后将其重新加入集群。

资源和参考资料

  • 演示的源代码可在 GitHub 上获取:(Pet Set 示例将合并到 Kubernetes Cassandra 示例中)。
  • 有关 Jobs 的更多信息
  • Pet Set 的文档
  • 图片来源:Cassandra 图片 和 Cyclops 图片