容器的好处不胜枚举:一致的运行时环境、节省磁盘空间、低开销、良好的隔离性,等等。了解完这些优势,您以及您的同事可能都开始跃跃欲试要把应用程序打包到容器中并准备运行它。然后突然之间或许您会发现,容器运行起来之后有一些问题也接踵而来,您需要一种方法来管理所有正在运行的容器及其生命周期:它们如何相互连接,它们应该运行在什么硬件之上,它们如何获取数据存储,容器因各种原因停止运行的话您该如何处理错误……
这就是 Kubernetes 大显身手的地方了。
在本文中,我们将了解 Kubernetes 是什么,它如何解决容器编排问题,它背后是由哪些理论支撑,如何将该理论直接与实际操作绑定,最终帮助您充分理解 Kubernetes 的各个细节与部分。
Kubernetes: 历史
Kubernetes,也被称为 k8s(k… 8 个字母…和 s)或 kube,是希腊语中的单词,意为 州长、舵手或船长。拿真正的航海的情景来理解,大型船舶装载着大量现实生活的容器,而船长或舵手则是负责船舶的人。因此,在信息技术的语境下,Kubernetes 就是 Docker 容器的船长、编排者。
Kubernetes 最初是谷歌公司在内部使用的,基于谷歌运行容器 15 年的经验,Kubernetes 于 2014 年开始作为 Google 的开源项目提供给社区。四年过去,Kubernetes 飞速发展,下载及使用量惊人,被大量的中小型企业用户用于开发或生产环境,并已成为业界公认的容器编排管理的标准框架。
Kubernetes 的发展势头
Kubernetes 的增长态势惊人,在 GitHub 上拥有超过 40,000 颗星,在 2018 年拥有超过 60,000 个 commit,并且比 GitHub 上的任何其他项目都有更多的 pull request 和 issue。其增长背后的部分原因是其不凡的可扩展性和强大的设计模式,这些我们将在后文中进一步解读。您可以在此链接了解一些大型软件公司的 Kubernetes 应用案例:
https://kubernetes.io/case-studies/
Kubernetes 提供的服务
让我们来看看是什么功能及特性让 Kubernetes 吸引到业界的如此关注。
Kubernetes 的核心是以容器为中心的管理环境。它代表用户的工作负载来编排计算、网络和存储基础架构。这 提供了平台即服务(PaaS)的简单性和基础架构即服务(IaaS)的灵活性 ,并实现了跨基础架构提供商的可移植性。Kubernetes 不仅仅是一个编排系统。实际上,它让用户不再需要“编排”。“编排”的技术定义是“执行定义的工作流程”:首先执行 A,然后运行 B,然后运行 C。而有了 Kubernetes 之后,Kubernetes 由一组独立的、可组合的控制流程组成,这些流程可将当前状态持续推向所需状态。而你是如何从 A 进行到 C 的,在此无关紧要。而且,用户也不再需要集中控制,整个系统也变得更易于使用、功能更强大、可扩展性更佳。
Kubernetes 的基本概念
要使用 Kubernetes,您可以使用 Kubernetes API 对象来描述集群的所需状态:您想要运行的应用程序或服务,它们使用的容器镜像、副本数量,您希望提供的网络和磁盘资源等等。您可以通过使用 Kubernetes API——kubectl(通常通过命令行界面)创建对象来设置所需的状态。您还可以直接使用 Kubernetes API 与集群交互,并设置或修改所需的状态。
设置完所需状态后,Kubernetes 控制面板会让集群的当前状态与所需状态相匹配。为此,Kubernetes 会自动执行各种任务,例如启动或重新启动容器、扩展给定应用程序的副本数量等等。
基本的 Kubernetes 对象包括:
节点
Pod
服务
卷
命名空间
此外,Kubernetes 包含许多称为 controller 的高级抽象。controller 基于基本对象构建,并提供其他功能和便利的特性。它们包括:
ReplicaSet
Deployment
StatefulSet
DaemonSet
Job
下文中我们会逐个介绍这些概念,然后再尝试一些动手练习。
节点
节点( Node)是 Kubernetes 中的 worker machine,以前称为 minion。节点可以是虚拟机(VM)或物理机(具体取决于集群)。每个节点都包含运行 pod 所需的服务,并由主组件管理。你可以这样理解节点:节点对于 pod 就像是 Hypervisor 对于虚拟机。
Pod
Pod 是 Kubernetes 的基本构建块,它是您创建或部署的 Kubernetes 对象模型中最小和最简单的单元。一个 Pod 代表着一个部署单元:Kubernetes 中的单个应用程序实例,可能包含单个容器或少量紧密组合并共享资源的容器。
Docker 是 Kubernetes Pod 中最常用的容器运行时,但 Pods 也支持其他容器运行时。
Kubernetes 集群中的 Pod 主要以两种方式使用: 第一种是运行单个容器的 Pod 。 “one-container-per-Pod”模式是最常见的 Kubernetes 用例; 在这种情况下,您可以将 Pod 视为单个容器的打包,由 Kubernetes 而非容器来管理 Pods。 第二种是运行多个需要协同工作的容器的 Pod。 一个 Pod 可能包含了一个应用程序,这个应用程序是由多个紧密耦合并且需要共享资源的容器组成的。这些共存的容器可能形成一个单一的内聚服务单元——一个容器负责从共享卷将文件公开,而另一个单独的“sidecar”容器负责刷新或更新这些文件。该 Pod 将这些容器和存储资源一起封装为一个可管理的实体。
Pod 为其组成容器提供两种共享资源: 网络和存储 。
网络 :每个 Pod 都分配了一个唯一的 IP 地址。一个 Pod 中的所有容器都共享网络命名空间,包括 IP 地址和网络端口。Pod 内的容器可以使用 localhost 相互通信。当 Pod 内的容器与 Pod 外的实体通信时,它们必须协调如何使用共享网络资源(例如端口)。
存储 :Pod 可以指定一组共享存储卷。Pod 中的所有容器都可以访问共享卷,允许这些容器共享数据。如果需要重新启动其中一个容器,卷还可以让 Pod 中的持久数据一直保存着。
服务
Kubernetes Pods 不是不变的,它们被创建又被杀死后并不会重生。即使每个 Pod 都有自己的 IP 地址,你也不能完全指望它会随着时间推移却永不改变。这会产生一个问题,如果一组 Pods(比如说后端)为 Kubernetes 集群中的另一组 Pods(比如说前端)提供了功能,那些前端 pod 如何能够与后端 pod 保持可靠的通信?
这就是 服务 发挥作用的地方。
Kubernetes 服务定义了一个逻辑集 Pods 和访问它们的策略(有时称为微服务),通常由 Label Selector 确定。
例如,如果你有一个带有 3 个 Pod 的后端应用程序,那些 pod 是可替代的,前端并不关心它们使用哪个后端。虽然 Pods 组成后端集的实际情况可能会发生变化,但前端客户端不应该知道这一点,也不需要跟踪后端列表本身。该 Service 抽象可实现这种分离。
对那些处于同样的 Kubernetes 集群中的应用,Kubernetes 提供了一个简单的 Endpoints API,每当服务中的一套 Pods 改变时,API 就会相应地更新。对于集群外的应用程序,Kubernetes 提供基于虚拟 IP 的桥接器,Services 可将其重定向到后端 Pods。
卷
容器中的磁盘文件是不是永久的,这会给运行在容器中的应用程序带来一些问题。首先,当容器崩溃时,它将由 Kubernetes 重新启动,但文件会丢失,因为容器总是以干净状态启动的。其次,当在一个 pod 中运行多个容器时,通常需要在这些容器之间共享文件。Kubernetes Volume 就是来解决这两个问题的。
从本质上讲,卷只是一个目录,可能包含一些数据,在 Pod 中,它可以访问容器。该目录是如何形成的、支持它的介质是什么以及它的内容,是由所使用的特定卷类型决定的。
Kubernetes 卷具有明确的生命周期,与创建它的 Pod 的生命周期相同。总而言之,一个卷超过了在 Pod 中运行的任何容器,并且在容器重启时保留了数据。通常,当一个 Pod 不再存在时,卷也将不复存在。Kubernetes 支持多种类型的卷,并且 Pod 可以同时使用任意数量的卷。
命名空间
Kubernetes 支持由同一物理集群支持的多个虚拟集群。这些虚拟集群称为命名空间。
命名空间提供了名称范围。在一个命名空间内,每个资源名称都需要是唯一的,不过跨命名空间时就没有这种要求了。
没有必要只是为了分离略有不同的资源而使用多个命名空间,比如说同一软件的不同版本:可以用标签来区分同一命名空间内的不同资源。
ReplicaSet
ReplicaSet 确保一次运行指定数量的 pod 副本。换句话说,ReplicaSet 确保 pod 或同类 pod 组始终可用。但是,Deployment 是一个更高级别的概念,它可以管理 ReplicaSets,并为 Pods 提供声明性更新以及许多其他有用的功能。因此,除非您需要自定义更新编排或根本不需要更新,否则我建议您使用 Deployments 而不是直接使用 ReplicaSets。
这实际上意味着您可能永远不需要直接操纵 ReplicaSet 对象,而是可以使用 Deployment 作为替代。
Deployment
Deployment controller 为 Pods 和 ReplicaSets 提供声明性更新。
您在 Deployment 对象中描述了所需状态,Deployment controller 就将以受控速率将现阶段实际状态更改为所需状态。您可以定义 Deployments 来创建新的 ReplicaSets,或删除现有的 Deployments 并使用新的 Deployments 来使用所有资源。
StatefulSets
StatefulSet 用于管理有状态应用程序,它管理一组 Pods 的部署和扩展,并提供有关这些 Pod 的排序和唯一性的保证。
StatefulSet 的运行模式和 controller 相同。您可以在 StatefulSet 对象中定义所需的状态,StatefulSet controller 就会进行各种必要的更新以从当前状态到达更新为所需状态。和 Deployment 类似,StatefulSet 管理那些基于相同容器规范的 Pods。与 Deployment 不同的是,StatefulSet 为每个 Pods 保留一个粘性身份。这些 Pods 是根据相同的规范创建的,但不可互换:每个都有一个永久的标识符,不论如何重新调度,这个标识都保持不变。
DaemonSet
DaemonSet 确保所有(或某些)节点运行 Pod 的副本。随着节点添加到群集中,Pod 会随之添加。当将节点从集群中删除后,Pods 就成为了垃圾。此时,删除一个 DaemonSet 就将清理它所创建的 Pods。
DaemonSet 的一些典型用途有:
在每个节点上运行集群存储 daemon,例如 glusterd、ceph。
在每个节点上运行日志收集 daemon,例如 fluentd 或 logstash。
在每个节点上运行节点监控 daemon,例如 Prometheus Node Exporter 或 collectd。
Job
job 创建一个或多个 pod,并确保在需要的时候成功终止指定数量的 pod。Pods 成功完成后,job 会追踪顺利完成的情况。当达到指定数量时,job 本身的任务就完成了。删除 Job 将清除它所创建的 Pods。
一个简单的例子是创建一个 Job 对象,以便可靠地运行一个对象 Pod。如果第一个 Pod 失败或被删除(例如由于节点硬件故障或节点重启),那么该 Job 对象将启动一个新 Pod。
实际操作中的挑战
现在您已经了解了 Kubernetes 中的关键对象及概念了,很明显,想要玩转 Kubernetes 需要了解大量的信息。当你尝试使用 Kubernetes 时,可能会遇到如下挑战:
如何在不同的基础架构中一致地部署?
如何跨多个集群(和名称空间)实现和管理访问控制?
如何与中央身份验证系统集成?
如何分区集群以更有效地使用资源?
如何管理多租户、多个专用和共享集群?
如何创建高可用集群?
如何跨集群/命名空间实施安全策略?
如何良好地进行监控,以确保有足够的可见性来检测和解决问题?
如何跟上 Kubernetes 快速发展的步伐?
这就是 Rancher 可以帮助您的地方。 Rancher 是一个 100%开源的容器管理平台,用于在生产中运行 Kubernetes。 通过 Rancher,你可以:
拥有易于使用的 kubernetes 配置和部署界面;
跨多个集群和云的基础架构管理;
自动部署最新的 kubernetes 版本;
工作负载、RBAC、政策和项目管理;
24x7 企业级支持。
Rancher 可以成为一个单一控制点,而您可以在多种、多个基础架构上运行多个 Kubernetes 集群:
上手 Rancher 和 Kubernetes
现在让我们看看如何在 Rancher 的帮助下轻松使用前文描述的 Kubernetes 对象。首先,您需要一个 Rancher 实例。按照本指南,在几分钟之内即可启动一个 Rancher 实例并使用它创建一个 Kubernetes 集群:
https://rancher.com/docs/rancher/v2.x/en/quick-start-guide/deployment/quickstart-manual-setup/
启动集群后,您应该在 Rancher 中看到 Kubernetes 集群的资源:
要从第一个 Kubernetes 对象——节点开始,请单击顶部菜单上的 Nodes。你应该可以组成了你的 Kubernetes 集群的 Nodes 的整体情况:
在那里,您还可以看到已从 Kubernetes 集群部署到每个节点的 pod 的数量。这些 pod 由 Kubernetes 和 Rancher 内部系统使用。通常情况下你不需要处理那些。
下面让我们继续 Pod 的例子。要做到这一点,转到 Kubernetes 集群的 Default 项目,然后进入 Workloads 选项卡。下面让我们部署一个工作负载。点击 Deploy 并将 Name 和 Docker image 设置为 nginx,剩下的一切都使用默认值,然后点击 Launch。
创建后,Workloads 选项卡会显示 nginx 工作负载。
如果单击 nginx 工作负载,您将会看到 Rancher 实际创建了一个 Deployment ,就像 Kubernetes 推荐的那样用来管理 ReplicaSet ,您还将看到该 ReplicaSet 创建的 Pod :
现在你有一个 Deployment,它将确保我们所需的状态在集群中正确显示。现在,点击 Scale 附近+号,将此 Workload 扩容到 3。一旦你这样做,你应该能立即看到另外 2 个 Pods 被创建出来,另外还多了 2 个 ReplicaSet 来缩放事件。使用 Pod 右侧菜单,尝试删除其中一个 pod,并注意 ReplicaSet 是如何重新创建它以匹配所需状态的。
现在,您的应用程序已启动并运行,并且已经扩展到 3 个实例。下一个问题是,您如何访问它?在这里,我们将尝试使用下一个 Kubernetes 对象——服务。为了暴露我们的 nginx 工作负载,我们需要先编辑它,从 Workload 右侧菜单中选择 Edit。您将看到 Deploy Workload 页面,且已填好了您的 nginx 工作负载的详细信息:
请注意,现在您有 3 个 pod 在 Scalable Deployment 旁边,但是当您启动时,默认值为 1。这是因为你的扩展工作刚完成不久。
现在单击 Add Port,并按如下所示填充值:
将 Publish the container port 值设置为 80;
Protocol 仍为 TCP;
将 As a 值设置为 Layer-4 Load Balancer;
将 On listening port 值设置为 80。
然后自信地点击 Upgrade 吧!这将在您的云提供商中创建一个外部负载均衡器,并将流量引至您的 Kubernetes 集群内的 nginx Pods 中。要对此进行测试,请再次访问 nginx 工作负载概述页面,现在您应该看到 Endpoints 旁的 80/tcp 链接:
如果点击 80/tcp,它会导向您刚刚创建的负载均衡器的外部 IP,并且向您展示一个默认的 nginx 页面,确认一切都按预期正常工作。
至此,你已经搞定了上半篇文章中介绍的大多数 Kubernetes 对象。你可以在 Rancher 中好好玩玩卷和命名空间,相信您一定能很快掌握如何通过 Rancher 更简单快捷地使用它们。至于 StatefulSet、DaemonSet 和 Job,它们和 Deployments 非常类似,你同样可以在 Workloads 选项卡中通过选择 Workload type 来创建它们。
结语
让我们回顾一下你在上面的动手练习中所做的一切。你已经创建了我们描述的大多数 Kubernetes 对象:
1、最开始,你在 Rancher 中创建了 kubernetes 集群;
2、然后你浏览了集群的 Nodes;
3、你创造了一个 Workload;
4、你已经看到了一个 Workload 实际上创建了 3 个独立的 Kubernetes 对象:一个 Deployment 管理着 ReplicaSet,同时按需保持着所需数量的 Pods 正常运行;
5、在那之后你扩展了你的 Deployment 数量,并观察它是如何改变了 ReplicaSet 并相应地扩展 Pods 的数量的;
6、最后你创建了一个 Load Balancer 类型的 Service 类型,用于平衡 Pods 之间的客户端请求。
所有这些都可以通过 Rancher 轻松完成,您只需进行一些点击的操作,无需在本地安装任何软件来复制身份验证配置或在终端中运行命令行。您只需一个浏览器,玩转 Kubernetes 唾手可得。
评论