搞microservice怎么能不了解一下Kubernetes

1,299 阅读21分钟
原文链接: github.com

container orchestration

Kubernetes的主要职责是容器编排。即,确保执行各种工作负载的所有容器都按计划运行物理或虚拟机。 按照部署环境和群集配置的限制来高效地打包容器。 此外,Kubernetes必须密切关注所有正在运行的容器,并更换挂了的,无反应的或不健康的容器。

Physical machines virtual machines and containers

硬件是基础。为了运行你的工作负载,你需要一些真实的硬件配置,包括具有计算能力(CPU),内存以及本地永久存储(磁盘或SSD)的物理机。另外,你需要一些共享的永久性存储,并使用网络连接所有这些机器,以便它们可以互相交谈。 此时,你可以在物理机器上运行多个虚拟机,或者停留在裸机级别(无虚拟机)。Kubernetes可以部署在裸机集群或虚拟机集群上。Kubernetes可以直接在裸机或虚拟机上编排它管理的容器。 理论上,Kubernetes集群可以由裸机和虚拟机组成,但这不是很常见。

Containers in the cloud

容器是打包微服务的理想选择,在为微服务提供隔离的同时,它们非常轻便,并且在部署许多微服务时不会像虚拟机一样产生过多的开销。这使得容器成为云部署的理想之选,比较而言为每个微服务分配整个虚拟机的方法成本过高。 所有主要的云提供商,如AWS,GCE和Azure,现在都提供容器托管服务。 其中一些是基于Kubernetes,如Google的GKE。其他的,如微软Azure的容器服务,则基于其他解决方案(Apache Mesos)。而AWS拥有ECS(EC2上的容器服务),它使用自己的编排解决方案。Kubernetes的好处在于它可以部署在所有云端。Kubernetes有一个cloud provider interface,允许任何云提供商实现它并无缝集成Kubernetes。

Cattle versus pets

在过去,当系统很小时,每个服务器都有一个名称。开发人员和用户确切知道每台机器上运行的软件。你将你的服务器视为喜欢的宠物。当服务器挂了,每个人都争先恐后试图找出在哪里获得另一台服务器,甚至在哪里运行挂了的服务器,以及如何让它在新的服务器上工作。如果服务器存储了一些重要的数据,如果你有一个最新的备份,也许你甚至能够恢复它。
显然,这种方法不能扩展。当你有几十或几百台服务器时,你必须开始像牛群一样对待它们。你考虑集体而不是个人。你可能还有一些宠物(也就是你的机器),但你的网络服务器只是牛群。
Kubernetes将牛群方法发挥到了极致,并负责将容器分配给特定的机器。大多数时候你不需要与单个机器(节点)进行交互。这很适合无状态工作负载。对于有状态的应用程序,Kubernetes提供了一个名为StatefulSet的解决方案。

Kubernetes concepts

Cluster

集群是Kubernetes用来运行系统的各种工作负载的存储和网络资源的集合。你的整个系统可能包含多个集群。

Node

节点是单个主机,它可能是一台物理或虚拟机器,它的工作是运行pod。每个Kubernetes节点运行若干Kubernetes组件,例如kubelet和kube proxy。节点由Kubernetes master进行管理。节点是Kubernetes的工蜂,肩负着所有繁重的工作。过去他们被称为minions。

Master

master是Kubernetes的control plane,它由若干组件组成,如API server,scheduler,controller manager。master负责全局集群级别的pods调度和事件处理。通常,所有master组件都设置在一台主机上。在考虑高可用性方案或超大型集群时,您需要拥有master冗余。

Pod

pod是Kubernetes的工作单元。每个pod包含一个或多个容器。pods始终安排在一起(始终运行在同一台机器上)。一个pod中的所有容器具有相同的IP地址和端口,可以使用localhost或标准ipc进行通信。一个pod中的所有容器都可以访问容纳该pod的节点上的共享本地存储。共享存储将挂载在每个容器上。 pod为管理密切相关的容器组提供了一个很好的解决方案,这些容器相互依赖,在同一主机上合作以完成目标。记住,pod是短暂的一次性的实体,可以随意丢弃和更换。任何pod storage都会随着pod一起被销毁。每个pod都有一个唯一的ID(UID),因此你仍然可以区分它们。

Label

标签是键值对,用于将一组对象组合在一起,如Pod。这个概念非常重要,例如replication controller,replica sets和对动态对象组进行操作并需要识别组成员的服务。对象和标签之间有NxN关系。每个对象可以有多个标签,每个标签可以应用于不同的对象。标签设计有一定的限制,对象上的每个标签都必须具有唯一的键,标签键必须遵守严格的语法。它有两个部分:前缀(pre x)和名称,pre x是可选的,如果存在,则它通过斜杠(/)与名称分开,并且它必须是有效的DNS子域。 前缀最多只能有253个字符。名称是强制性的,最多只能有63个字符。名称必须以字母数字字符(a-z,A-Z,0-9)开头和结尾,并且只包含字母数字字符,dot,dash和underscore。值遵循与名称相同的限制。请注意,标签专用于标识对象,而不是用于将任意元数据附加到对象。

Annotation

注释可以将任意元数据与Kubernetes对象相关联。Kubernetes只是存储注释并使其元数据可用。与标签不同,它们对于允许的字符和大小限制没有严格的限制。对于复杂的系统,你需要这样的元数据,而Kubernetes提供了开箱即用的功能,所以你不必自己去实现这一功能。

Label selector

标签选择器用于根据标签选择对象。选择器指定一个键名和一个值,这里有两个运算符=(或==)和!=,用于相等或不等式。例如:role = webserver 将选择具有该标签键和值的所有对象。
标签选择器可以有多个用逗号分隔的需求。例如:role = webserver, application != foo
基于集合的选择器允许基于多个值进行选择:
role in (webserver, backend)

Replication controller and replica set

Replication controller和replica set都管理由标签选择器标识的一组pods,并确保它们始终处于运行状态。它们之间的主要区别是replication controller通过名称相等测试成员资格,replica set可以使用基于集合的选择,replica set较新,并被指定为下一代replication controller。
Kubernetes保证你将始终拥有与你在replication controller或replica set中指定的相同数量的pods。只要由于托管节点或Pod本身的问题而导致数量下降,Kubernetes就会重新创建新的实例。请注意,如果手动启动Pod并超出指定数量,则replication controller将结束一些额外的Pod。
Replication controller曾经是许多工作的核心,比如rolling updates滚动更新和运行one-off jobs一次性作业。随着Kubernetes的发展,它引入了对这些工作的直接支持,并使用诸如Deployment,Job和DaemonSet等专用对象。

Service

服务用于向用户或其他服务公开某些功能。它们通常包含一组pods,通常通过标签标识。你可以提供访问外部资源的服务,或者在virtual IP级别直接控制的Pod。原生Kubernetes服务通过端点endpoints进行访问。请注意,服务在第3层(TCP / UDP)上运行。Kubernetes 1.2添加了Ingress对象,该对象提供对HTTP对象的访问。服务通过以下两种机制之一发布或发现:DNS或环境变量environment variables。服务可以由Kubernetes进行负载平衡。但在使用外部资源的服务或需要特殊处理的情况下,开发人员可以选择自行管理负载平衡。

Volume

在pod上的本地存储是短暂的,并随着pod而消失。如果目标只是在节点的容器之间交换数据,这就是你所需要的,但有时数据存在于pod之外很重要,或者需要在pod之间共享数据。volume概念支持这种需求。请注意,尽管Docker也有一个volume概念,但它非常有限。Kubernetes使用自己的独立volume。Kubernetes还支持其他容器类型,例如rkt,因此原则上也不能依赖Docker volume。 Kubernetes目前直接支持每种卷类型。emptyDir卷类型挂载卷在托管机器上默认支持的每个容器上。如果需要,你可以请求存储介质。此存储在pod终止时会被删除。一个有趣的卷类型是persistentDiskClaim,它使用环境中的默认永久存储。

StatefulSet

Pods创建又终止,如果你关心它们的数据,那么你可以使用持久存储。但有时候你希望Kubernetes能够管理分布式数据存储库,如Kubernetes或MySQL Galera。这些集群存储将数据分布在唯一标识的节点中。你不能用普通的pods和服务来建模。StatefulSet确保(类似于replication controller)在给定的时间内有给定数量的具有唯一身份的宠物正在运行。宠物有以下属性:

  • 一个稳定的在DNS中可用的主机名
  • 有序的索引
  • 连接索引和主机名的稳定存储
    StatefulSet可以帮助你发现对等点以及添加或移除宠物

Secret

Secret是包含敏感信息的小对象,例如凭证和令牌。它们作为明文存储在etcd中,可由Kubernetes API服务器访问,并且可以作为文件挂载到需要访问它们的pods中(使用在常规数据卷上背负的专用secret卷)。同一个secret可以挂载到多个Pod中。Kubernetes本身为其组件创建了secret,并且你可以创建自己的secret。另一种方法是将secret用作环境变量。请注意,pod中的secret始终存储在内存中(在挂载的secret情况下为tmpfs)以提高安全性。

Name

Kubernetes中的每个对象都由一个UID和一个名称来标识。该名称用于引用API调用中的对象。名称最长为253个字符,并使用小写字母数字字符,dash和dot。如果你删除了一个对象,则可以创建另一个与删除对象具有相同名称的对象,但UID在集群的整个生命周期内必须是唯一的。UID由Kubernetes生成,所以你不必担心。

Namespace

命名空间是一个虚拟集群。你可以拥有一个包含多个由命名空间隔离的虚拟集群的物理集群。每个虚拟集群都与其他虚拟集群完全隔离,只能通过公共接口进行通信。请注意,Node对象和持久卷不在命名空间中。Kubernetes可能会安排来自不同名称空间的pods在同一节点上运行。同样,来自不同名称空间的Pod可以使用相同的持久性存储。 使用名称空间时,必须考虑网络策略和资源配额,以确保正确访问和分配物理集群资源。

Kubernetes architecture

Kubernetes旨在管理和简化各种环境和云提供商的分布式系统的编排,部署和管理。它提供了许多适用于各种场合的功能和服务,同时不断发展并保持简单,足以让大众使用。这是一项艰巨的任务。Kubernetes通过遵循水晶般清晰的高级设计和经过深思熟虑的架构来实现这一点,该架构可提高可扩展性和可插拔性。Kubernetes的许多部分仍然是硬编码或环境感知的,但趋势是将其重构为插件并保留核心通用和抽象。

Distributed systems design patterns

Sidecar pattern

Sidecar pattern是将另一个容器共同定位在pod中的主应用程序容器。主应用程序容器不知道sidecar容器,只是开展业务。一个很好的例子是一个中央日志代理。你的主容器只能记录日志到标准输出,但sidecar容器将所有日志发送到中央日志记录服务,在那里它们将与来自整个系统的日志聚合。相比将中央记录服务添加到主应用程序容器,使用sidecar容器的好处是巨大的。首先,应用程序没有中央日志记录的负担,这可能是一个麻烦事。如果您想要升级或更改中央日志记录策略或切换到全新的程序,则只需更新sidecar容器并进行部署即可。你的应用程序容器都不会更改。

Ambassador pattern

ambassador pattern是好像本地服务一样去呈现远程服务,并可能加入某种限制。ambassador pattern的一个很好的例子是,如果你拥有一个Redis集群,其中一个master用于写入,多个副本用于读取。本地大使容器可以充当代理并将Redis公开给本地主机上的主应用程序容器。 主应用程序容器在localhost:6379(Redis默认端口)上简单地连接到Redis,但它连接到在同一个pod中运行的大使容器,该容器迭代请求,并向真实的Redis主服务器发送写入请求,并随机将读取请求发送到一个只读副本。就像sidecar模式一样,主应用程序不知道发生了什么。在对真正的本地Redis进行测试时,这会有很大帮助。另外,如果Redis集群配置发生变化,则只需要修改大使身份,主要应用程序不需要改变。

Adapter pattern

适配器模式用于标准化主应用程序容器的输出。考虑递增发布服务的情况:它可能以不符合先前版本的格式生成报告。其他使用该输出的服务和应用程序尚未升级。 适配器容器可以与新的应用程序容器一起部署在同一个pod中,并调整它们的输出来匹配旧版本,直到所有消费者都已升级。 适配器容器与主应用程序容器共享文件系统,因此它可以观察本地文件系统,每当新应用程序写入内容时,它立即调整它。

Multi-node patterns

单节点模式由Kubernetes直接通过pod支持。诸如选举,工作队列等多节点模式不直接支持,但使用标准接口组合pod来完成多节点模式是在Kubernetes中是可行的。

Kubernetes APIs

如果你想了解一个系统和它提供的功能,你必须非常关注它的API。该API提供了作为系统用户你可以执行的操作的全面视图。Kubernetes针对不同的目的和受众展示了几套REST API。一些API主要由工具使用,一些可以由开发人员直接使用。Kubernetes开发人员通过尝试扩展(向现有对象添加新对象和新字段)并避免重命名或删除现有对象和域,从而保持其可管理性。所有的API端点都是版本化的。

Kubernetes API

这是Kubernetes的主要API。我们之前讨论的所有概念都有相应的API对象和操作。如果你拥有权限,则可以列出,获取,创建和更新对象。这里列举一个最常见操作的文档,获取所有pods的列表:
GET /api/v1/pods
它接受各种查询参数(全部可选):

  • pretty:如果为true,则输出结果相当漂亮
  • labelSelector:一个选择器表达式来限制结果
  • watch:如果true,则观察变化并返回一系列事件
  • resourceVersion:和watch一起使用,仅返回该版本之后发生的事件
  • timeoutSeconds:列表或监视操作超时时间

Autoscaling API

Autoscaling API专注于让你控制pod水平地自动缩放,根据CPU利用率甚至应用程序特定度量标准管理一组pod自动缩放。你可以通过/apis/autoscaling/v1端点来列出,查询,创建,更新和销毁自动调节对象。

Batch API

批处理API用来管理作业(jobs)。作业是执行一些活动并终止的pods。与replication controller管理的常规Pod不同,它们会在作业完成时终止。批处理API使用pod模板指定作业,允许你通过/apis/batch/v1端点来列出,查询,创建和删除作业。

Kubernetes components

Kubernetes集群具有多个用于控制集群的主组件以及在每个集群节点上运行的节点组件。

Master components

主组件通常在一个节点上运行,但是在高可用或非常大的集群中,它们可能分布在多个节点上。

API server

API服务器发布Kubernetes REST API。它可以轻松地进行水平扩展,因为它是无状态的,并将所有数据存储在etcd群集中。API服务器是Kubernetes控制平面(control plane)的实例。

Etcd

Etcd是一个高度可靠的分布式数据存储。Kubernetes使用它来存储整个集群状态。在小的暂时的集群中,一个单一的etcd实例可以与其他所有主组件在同一个节点上运行。但是,对于大的集群,通常是运行具有3节点甚至5节点的etcd集群以实现冗余和高可用性。

Controller manager

Controller manager是各种管理器汇集成的一个二进制文件的集合。它包含replication控制器,pod控制器,服务控制器,端点控制器等。所有这些管理器都通过API监视集群的状态,他们的任务是将集群引导到所需的状态。

Scheduler

kube-scheduler负责将pod安排到节点中。这是一个非常复杂的任务,因为它需要考虑多个相互作用的因素,例如:

  • 资源需求
  • 服务要求
  • 硬件/软件限制
  • Affinity and anti-affinity specifications
  • 数据位置
  • 截止日期

DNS

从Kubernetes 1.3开始,DNS服务是标准Kubernetes集群的一部分。它被定义为一个常规pod。每项服务(headless service除外)都会有DNS名称。Pods也可以有DNS名称。这对于自动发现非常有用。

Node components

集群中的节点需要一些组件才能与集群中的主组件进行交互,接收要执行的工作负载并反馈自身的状态给集群。

Proxy

kube代理在每个节点上进行底层网络管理。它在本地提供Kubernetes服务,并且可以执行TCP和UDP转发。它通过环境变量或DNS查找集群IP。

Kubelet

Kubelet是节点上的Kubernetes代表。它监督与主组件的通信并管理正在运行的pods。这包括以下内容:

  • 从API服务器下载pod secrets
  • 挂载卷
  • 运行pod的容器(Docker或Rkt)
  • 报告节点和每个pod的状态
  • 探测容器存活

Kubernetes runtimes

Kubernetes最初只支持Docker作为容器运行时引擎。但现在不是这样。Rkt是另一个被支持的运行时引擎,并且有尝试通过Hypernetes来处理Hyper.sh容器。一个主要的设计策略是Kubernetes本身应该与特定的运行时间完全分离。Kubernetes和运行时之间的交互是通过相对通用的接口,运行时引擎必须实现这些接口。大部分通信都使用pod和容器中的概念以及可以在容器上执行的操作。每个运行时引擎负责实现兼容的Kubernetes运行时接口。

runtime interface

Kubernetes项目中指定了容器的运行时接口。 第一组方法提供有关运行时的一般信息:

type Runtime interface {
    Type() string
    Version() (Version, error)
    APIVersion() (Version, error)
    Status() error
    GetPods(all bool) ([]*Pod, error)
}

接下来的一组方法主要是处理pods,因为这是Kubernetes概念模型的主要抽象:

SyncPod (pod *api.Pod, apiPodStatus api.PodStatus, podStatus *PodStatus, pullSecrets []api.Secret, backOff *flowcontrol.Backoff) PodSyncResult

KillPod (pod *api.Pod, runningPod Pod, gracePeriodOverride *int64) error

GetPodStatus (uid types.UID, name, namespace string) (*PodStatus, error)

GetNetNS (containerID ContainerID) (string, error)

GetPodContainerID (*Pod) (ContainerID, error)

GetContainerLogs (pod *api.Pod, containerID ContainerID, logOptions *api.PodLogOptions, stdout, stderr io.Writer) (err error)

DeleteContainer (containerID ContainerID) error

最后三项ContainerCommandRunner,ContainerAttacher和ImageService是运行时接口继承的接口。这意味着实现运行时接口时也需要实现这些接口的方法。接口名称表明了它们所做的事情。Kubernetes需要在容器中运行命令,并且需要将容器附加到其pods并拉取容器图像。

Docker

Docker是容器世界的大猩猩。Kubernetes最初设计为仅管理Docker容器。支持多个运行时功能首先在Kubernetes 1.3中引入。在此之前,Kubernetes只能管理Docker容器。

从Docker 1.11开始,Docker改变了它运行容器的方式。运行时现在使用containerd和runC在容器中运行Open Container Initiative(OCI)image。用户与Docker Engine进行交互,Engine与containerd通信,containerd启动runC或其他OCI兼容的运行时来运行容器。

Rkt

Rkt是CoreOS新的容器管理器。rkt运行时以其简单性而自豪并强调安全和隔离。它没有像Docker引擎那样的守护进程,并依靠OS init system(如systemd)来启动rkt可执行文件。Rkt可以下载image(App Container (appc) image和OCI image),并在容器中运行它们。

App container

CoreOS开始了标准化工作,命名为appc。这包括标准image格式(ACI),运行时,签名和发现。Docker也开始了与OCI的标准化工作。这是一件好事,因为工具,image和运行时可以自由地进行互操作。

Rktnetes

Rktnetes是Kubernetes加上rkt作为运行时引擎。Kubernetes仍在剥离运行时引擎。Rktnetes并不是一个独立的产品。从外部来看,只需要在每个节点上用命令行运行kubelet。但由于Docker和rkt之间存在根本差异,因此可能会遇到各种问题。

Hyper containers

Hyper containers是另一种选择。Hyper container有一个轻量级VM(它自己的guest kernel),它运行在裸机上。它不依赖Linux cgroups进行隔离,而是依赖于hypervisor。与难以设置的标准裸机集群以及将容器部署在重量级虚拟机上的公共云相比,这种方法呈现出一种有趣的混合体。

Hypernetes

Hypernetes是一个多租户Kubernetes发行版,它使用Hyper container以及一些OpenStack组件进行身份验证,持久存储和联网。由于容器不共享主机内核,因此在同一物理主机上运行不同租户的容器是安全的。

Continuous integration and deployment

Kubernetes是运行基于微服务的应用程序的绝佳平台。用户(通常是开发人员)可能不知道系统部署在Kubernetes上。

CI/CD pipeline

CI/CD管道是一组步骤,开发人员对一系列代码,数据或系统配置的更改进行测试并将其部署到生产环境中。有些管道是完全自动化的,有些管道是半自动的,需要人工检查。在大型组织中,可能会有测试和staging环境,在这些环境中更改是自动部署的,但发布到生产环境时需要手动干预。

Designing CI/CD pipeline for Kubernetes

当你的部署目标是Kubernetes集群时,你应该重新考虑一些传统的做法,例如包装是不同的。通过使用标签,恢复代码更改非常简单,比如一个有问题的变动通过了测试环境,你将能够立即恢复到以前的版本,但Schema更改和数据迁移不能自动回滚。Kubernetes的另一个独特功能是开发人员可以在本地运行整个集群。如果你的系统是数据驱动的,你需要提供给开发人员可以使用的数据快照和合成数据。