1. 概述

在本教程中,我们将了解容器编排系统的基本需求。

我们将评估此类系统所需的特性。由此,我们将尝试比较当今使用的两种最流行的容器编排系统:Apache Mesos 和 Kubernetes。

2. 容器编排

在开始比较 Mesos 和 Kubernetes 之前,让我们花一些时间来了解什么是容器以及为什么我们需要容器编排。

 2.1. 集装箱

容器是一种标准化的软件单元,用于打包代码及其所有必需的依赖项。

因此,它提供了平台独立性和操作简单性。 Docker 是最流行的容器平台之一。

 

Docker 利用 CGroup 和命名空间等 Linux 内核功能来提供不同进程的隔离。因此,多个容器可以独立、安全地运行。

创建 docker 镜像非常简单,我们只需要一个 Dockerfile:

FROM openjdk:8-jdk-alpine
VOLUME /tmp
COPY target/hello-world-0.0.1-SNAPSHOT.jar app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
EXPOSE 9001Copy

因此,这几行足以使用 Docker CLI 创建 Spring Boot 应用程序的 Docker 映像:

docker build -t hello_world .Copy

2.2. 容器编排

因此,我们已经了解了容器如何使应用程序部署可靠且可重复。但为什么我们需要容器编排呢?

现在,虽然我们需要管理一些容器,但我们可以使用 Docker CLI。我们也可以自动化一些简单的杂务。但是当我们必须管理数百个容器时会发生什么?

 

例如,考虑具有多个微服务的架构,所有微服务都具有不同的可扩展性和可用性要求。

因此,事情很快就会失去控制,这就是容器编排系统的优势所在。容器编排系统将具有多容器应用程序的机器集群视为单个部署实体。它提供从初始部署、调度、更新到监控、扩展和故障转移等其他功能的自动化。

3. Mesos 简要概述

Apache Mesos 是一个开源集群管理器,最初是在加州大学伯克利分校开发的。它为应用程序提供 API,用于跨集群的资源管理和调度。 Mesos 使我们能够灵活地以分布式方式运行容器化和非容器化工作负载。

 3.1. 建筑学

Mesos 架构由 Mesos Master、Mesos Agent 和应用程序框架组成:

Mesos Architecture

让我们在这里了解架构的组成部分:

 
  • 框架:这些是需要分布式执行任务或工作负载的实际应用程序。典型的例子是 Hadoop 或 Storm。 Mesos 中的框架由两个主要组件组成:
    • 调度程序:负责向主节点注册,以便主节点可以开始提供资源
    • 执行器:这是在代理节点上启动以运行框架任务的进程
  • Mesos 代理:这些代理负责实际运行任务。每个代理都会将其可用资源(例如 CPU 和内存)发布给主代理。从主节点接收任务后,它们将所需的资源分配给框架的执行器。
  • Mesos Master:负责调度从可用代理节点之一上的框架接收的任务。 Master 向框架提供资源。框架的调度程序可以选择在这些可用资源上运行任务。

 3.2. 马拉松

正如我们刚才看到的,Mesos 非常灵活,允许框架通过定义良好的 API 来调度和执行任务。然而,直接实现这些原语并不方便,特别是当我们想要调度自定义应用程序时。例如,编排打包为容器的应用程序。

这就是像 Marathon 这样的框架可以帮助我们的地方。 Marathon 是一个运行在 Mesos 上的容器编排框架。在这方面,Marathon 充当了 Mesos 集群的框架。 Marathon 提供了我们通常期望从编排平台获得的多项优势,例如服务发现、负载平衡、指标和容器管理 API。

Marathon 将长时间运行的服务视为应用程序,将应用程序实例视为任务。典型的场景可以有多个具有依赖关系的应用程序,形成所谓的应用程序组。

 3.3. 例子

那么,让我们看看如何使用 Marathon 来部署我们之前创建的简单 Docker 镜像。请注意,安装 Mesos 集群可能涉及很少的内容,因此我们可以使用更简单的解决方案,例如 Mesos Mini。 Mesos Mini 使我们能够在 Docker 环境中启动本地 Mesos 集群。它包括 Mesos Master、单个 Mesos Agent 和 Marathon。

一旦我们启动并运行了 Marathon 的 Mesos 集群,我们就可以将容器部署为长期运行的应用程序服务。我们只需要一个小的 JSON 应用程序定义:

#hello-marathon.json
{
  "id": "marathon-demo-application",
  "cpus": 1,
  "mem": 128,
  "disk": 0,
  "instances": 1,
  "container": {
    "type": "DOCKER",
    "docker": {
      "image": "hello_world:latest",
      "portMappings": [
        { "containerPort": 9001, "hostPort": 0 }
      ]
    }
  },
  "networks": [
    {
      "mode": "host"
    }
  ]
}Copy

让我们了解一下这里到底发生了什么:

  • 我们已经为我们的应用程序提供了一个 ID
  • 然后,我们定义了应用程序的资源需求
  • 我们还定义了要运行的实例数量
  • 然后,我们提供了用于启动应用程序的容器详细信息
  • 最后,我们定义了网络模式,以便我们能够访问应用程序

我们可以使用 Marathon 提供的 REST API 启动此应用程序:

curl -X POST \
  http://localhost:8080/v2/apps \
  -d @hello-marathon.json \
  -H "Content-type: application/json"Copy

4. Kubernetes 简介

Kubernetes 是最初由 Google 开发的开源容器编排系统。它现在是云原生计算基金会 (CNCF) 的一部分。它提供了一个平台,用于跨主机集群自动部署、扩展和操作应用程序容器。

 

 4.1. 建筑学

Kubernetes 架构由 Kubernetes Master 和 Kubernetes Node 组成:

Kubernetes Architecture

让我们看一下这个高级架构的主要部分:

  • Kubernetes Master:Master 负责维护集群所需的状态。它管理集群中的所有节点。我们可以看到,master 是三个进程的集合:
    • kube-apiserver:这是管理整个集群的服务,包括处理 REST 操作、验证和更新 Kubernetes 对象、执行身份验证和授权
    • kube-controller-manager:这是嵌入 Kubernetes 附带的核心控制循环的守护进程,进行必要的更改以使当前状态与集群的所需状态相匹配
    • kube-scheduler:该服务监视未调度的 pod,并根据请求的资源和其他约束将它们绑定到节点
  • Kubernetes 节点:Kubernetes 集群中的节点是运行容器的机器。每个节点都包含运行容器所需的服务:
    • kubelet:这是主节点代理,确保 kube-apiserver 提供的 PodSpecs 中描述的容器正在运行且健康
    • kube-proxy:这是在每个节点上运行的网络代理,并在一组后端之间执行简单的 TCP、UDP、SCTP 流转发或循环转发
    • 容器运行时:这是运行 Pod 内容器的运行时,Kubernetes 有几种可能的容器运行时,包括最广泛使用的 Docker 运行时

 4.2. Kubernetes 对象

在上一节中,我们看到了几个 Kubernetes 对象,它们是 Kubernetes 系统中的持久实体。它们反映了集群在任何时间点的状态。

让我们讨论一些常用的 Kubernetes 对象:

  • Pods:Pod 是 Kubernetes 中的基本执行单元,可以由一个或多个容器组成,Pod 内的容器部署在同一台主机上
  • 部署:部署是在 Kubernetes 中部署 pod 的推荐方式,它提供了诸如不断协调 pod 的当前状态与所需状态等功能
  • 服务:Kubernetes 中的服务提供了一种公开一组 pod 的抽象方法,其中分组基于针对 pod 标签的选择器

还有其他几个 Kubernetes 对象可以有效地以分布式方式运行容器。

 4.3. 例子

所以,现在我们可以尝试将 Docker 容器启动到 Kubernetes 集群中。 Kubernetes 提供了 Minikube,一个在虚拟机上运行单节点 Kubernetes 集群的工具。我们还需要 kubectl,即 Kubernetes 命令行界面来与 Kubernetes 集群配合使用。

安装 kubectl 和 Minikube 后,我们可以将容器部署在 Minikube 内的单节点 Kubernetes 集群上。我们需要在 YAML 文件中定义基本的 Kubernetes 对象:

# hello-kubernetes.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello-world
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: hello-world
    spec:
      containers:
      - name: hello-world
        image: hello-world:latest
        ports:
        - containerPort: 9001
---
apiVersion: v1
kind: Service
metadata:
  name: hello-world-service
spec:
  selector:
    app: hello-world
  type: LoadBalancer
  ports:
  - port: 9001
    targetPort: 9001Copy

这里无法详细分析这个定义文件,但让我们看一下重点内容:

 
  • 我们在选择器中定义了带有标签的 Deployment
  • 我们定义此部署所需的副本数量
  • 此外,我们还提供了容器映像详细信息作为部署模板
  • 我们还定义了一个具有适当选择器的服务
  • 我们将服务的性质定义为 LoadBalancer

最后,我们可以部署容器并通过 kubectl 创建所有定义的 Kubernetes 对象:

kubectl apply -f yaml/hello-kubernetes.yamlCopy

5. Mesos 与 Kubernetes

现在,我们已经了解了足够的上下文,并且还在 Marathon 和 Kubernetes 上执行了基本部署。我们可以尝试了解它们之间的比较。

但需要注意的是,直接将 Kubernetes 与 Mesos 进行比较并不完全公平。我们寻求的大多数容器编排功能都是由 Mesos 框架之一(例如 Marathon)提供的。因此,为了保持正确的视角,我们将尝试将 Kubernetes 与 Marathon 进行比较,而不是直接与 Mesos 进行比较。

我们将根据此类系统的一些所需属性来比较这些编排系统。

5.1. 支持的工作负载

Mesos 旨在处理各种类型的工作负载,这些工作负载可以是容器化的,也可以是非容器化的。这取决于我们使用的框架。正如我们所见,使用 Marathon 等框架在 Mesos 中支持容器化工作负载非常容易。

另一方面,Kubernetes 专门处理容器化工作负载。最广泛的是,我们将它与 Docker 容器一起使用,但它也支持其他容器运行时,例如 Rkt。未来,Kubernetes 可能会支持更多类型的工作负载。

5.2. 支持可扩展性

Marathon 支持通过应用程序定义或用户界面进行扩展。 Marathon 还支持自动缩放。我们还可以扩展应用程序组,它会自动扩展所有依赖项。

正如我们之前看到的,Pod 是 Kubernetes 中的基本执行单元。 Pod 在由 Deployment 管理时可以进行扩展,这就是 Pod 总是被定义为部署的原因。缩放可以是手动的或自动的。

 

5.3. 处理高可用性

Marathon 中的应用程序实例分布在 Mesos 代理之间,提供高可用性。通常,Mesos 集群由多个代理组成。此外,ZooKeeper 通过仲裁和领导者选举为 Mesos 集群提供高可用性。

同样,Kubernetes 中的 Pod 可以跨多个节点进行复制,从而提供高可用性。通常,Kubernetes 集群由多个工作节点组成。而且,集群还可以有多个 master。因此,Kubernetes 集群能够为容器提供高可用性。

5.4. 服务发现和负载均衡

Mesos-DNS 可以为应用程序提供服务发现和基本的负载平衡。 Mesos-DNS 为每个 Mesos 任务生成一条 SRV 记录,并将其转换为运行该任务的计算机的 IP 地址和端口。对于 Marathon 应用程序,我们还可以使用 Marathon-lb 通过 HAProxy 提供基于端口的发现。

Kubernetes 中的部署会动态创建和销毁 Pod。因此,我们一般通过 Service 来暴露 Kubernetes 中的 pod,Service 提供服务发现功能。 Kubernetes 中的服务充当 Pod 的调度程序,因此也提供负载平衡。

5.5. 执行升级和回滚

Marathon 中应用程序定义的更改作为部署进行处理。部署支持应用程序的启动、停止、升级或扩展。 Marathon 还支持滚动启动部署较新版本的应用程序。然而,回滚也很简单,通常需要部署更新的定义。

Kubernetes 中的部署支持升级和回滚。我们可以提供用新 Pod 替换旧 Pod 时要采取的部署策略。典型的策略是重新创建或滚动更新。默认情况下,部署的部署历史记录在 Kubernetes 中维护,这使得回滚到以前的版本变得很简单。

5.6. 记录和监控

Mesos 有一个诊断实用程序,可以扫描所有集群组件并提供与运行状况和其他指标相关的可用数据。可以通过可用的 API 查询和聚合数据。我们可以使用 Prometheus 等外部工具收集大部分数据。

Kubernetes 将与不同对象相关的详细信息作为资源指标或完整指标管道发布。典型的做法是在 Kubernetes 集群上部署 ELK 或 Prometheus+Grafana 等外部工具。此类工具可以获取集群指标并以用户友好的方式呈现它们。

 

 5.7. 贮存

Mesos 具有用于有状态应用程序的持久本地卷。我们只能从保留的资源创建持久卷。它还可以支持外部存储,但有一些限制。 Mesos 对容器存储接口 (CSI) 提供实验性支持,这是存储供应商和容器编排平台之间的一组通用 API。

Kubernetes 为有状态容器提供多种类型的持久卷。这包括 iSCSI、NFS 等存储。此外,它还支持 AWS、GCP 等外部存储。 Kubernetes 中的 Volume 对象支持这个概念,并且有多种类型,包括 CSI。

 5.8. 联网

Mesos 中的容器运行时提供两种类型的网络支持:每个容器的 IP 和网络端口映射。 Mesos 定义了一个通用接口来指定和检索容器的网络信息。 Marathon 应用程序可以以主机模式或桥接模式定义网络。

Kubernetes 中的网络为每个 Pod 分配一个唯一的 IP。这不需要将容器端口映射到主机端口。它进一步定义了这些 Pod 如何跨节点相互通信。这是通过 Cilium、Contiv 等网络插件在 Kubernetes 中实现的。

6. 何时使用什么?

最后,通过比较,我们通常期待一个明确的判决!然而,无论如何,宣称一种技术优于另一种技术并不完全公平。正如我们所看到的,Kubernetes 和 Mesos 都是强大的系统,并且提供了相当有竞争力的功能。

然而,性能是一个非常重要的方面。 Kubernetes 集群可以扩展到 5000 个节点,而 Mesos 集群上的 Marathon 已知可支持多达 10,000 个代理。在大多数实际情况下,我们不会处理如此大的集群。

最后,它归结为我们所拥有的灵活性和工作负载类型。如果我们重新开始并且只计划使用容器化工作负载,Kubernetes 可以提供更快的解决方案。然而,如果我们现有的工作负载是容器和非容器的混合体,Mesos 和 Marathon 可能是更好的选择。

 7. 其他选择

Kubernetes 和 Apache Mesos 非常强大,但它们并不是这个领域唯一的系统。我们有很多有前途的替代方案。虽然我们不会详细介绍它们,但让我们快速列出其中的一些:

 
  • Docker Swarm:Docker Swarm 是一个用于 Docker 容器的开源集群和调度工具。它附带一个命令行实用程序来管理 Docker 主机集群。与 Kubernetes 和 Mesos 不同,它仅限于 Docker 容器。
  • Nomad:Nomad 是 HashiCorp 的灵活工作负载编排器,用于管理任何容器化或非容器化应用程序。 Nomad 支持声明性基础设施即代码来部署 Docker 容器等应用程序。
  • OpenShift:OpenShift 是 Red Hat 的一个容器平台,由 Kubernetes 进行编排和管理。除了 Kubernetes 提供的功能之外,OpenShift 还提供了许多功能,例如集成映像注册表、源到映像构建、本机网络解决方案等等。

 八、结论

总而言之,在本教程中,我们讨论了容器和容器编排系统。我们简要介绍了两个最广泛使用的容器编排系统:Kubernetes 和 Apache Mesos。我们还根据几个特性对这些系统进行了比较。
最后,我们看到了这个领域的一些其他替代方案。

在结束之前,我们必须明白,这样比较的目的是提供数据和事实。这绝不是说某一个比其他更好,这通常取决于用例。
因此,我们必须根据问题的背景来确定最适合我们的解决方案。

Mesos 与 Kubernetes |行动中的贝尔东 --- Mesos vs. Kubernetes | Baeldung on Ops