1. 容器编排技术
容器编排是指对多个容器的部署,管理和监控。
之所以有容器编排技术,其实是和业务量与系统复杂度与日俱增推动服务部署的演进方式息息相关的,下图是服务部署方式的演进过程。
更高效的利用系统资源、一致的运行环境、更轻松的迁移和拓展等诸多优点,使容器部署方式越来越成为主流。
Docker 是开源的被广泛应用的容器引擎,在实际生产环境中,在多台物理主机中协调容器资源成为首要要解决的问题,这一问题被统称为容器编排。
容器领域现阶段争论的重点也正在于为容器主机群管理提供怎样容器编排功能。
目前比较流行的容器编排工具包括 Docker Swarm,Kubernetes 和 Mesos+Marathon。容器使用的最核心问题也恰是容器编排及如何部署和管理容器。Docker Swarm,Kubernetes,Mesos+Marathon 都可用于容器的部署、管理以及实现容器的扩缩容,但这三种编排工具着重处理的问题和使用场景也是不同的。
2. 初识 Kubernetes
2.1 Kubernetes 概述
Kubernetes 也简称为 k8s,它可以提供⽤户所需的容器部署,管理和扩缩容等编排功能。
借助 Kubernetes 的编排功能,⽤户可以构建多个容器的应⽤服务,跨集群调度、扩展这些容器,并长期持续管理这些容器和检测健康状况 。
2.2 Kubernetes 能做什么?
Kubernetes 为⽤户主要提供了以下功能:
- 服务发现和负载均衡:Kubernetes 可以使⽤DNS 名称或集群的 IP 公开容器,如果进⼊容器的流量很⼤, Kubernetes 可以用负载均衡并均衡分配流量使服务稳定。
- 存储编排:Kubernetes 允许你⾃动挂载选择的存储系统,例如本地存储、公共云提供商等。
- 动部署和回滚:你可以使⽤Kubernetes 描述已部署容器的所需状态,它可以以受控的速率将实际状态更改为期望状态。例如,你可以⾃动化的方式来部署创建新容器, 删除现有容器并将它们的所有资源⽤于新容器。
- 动完成装箱计算:Kubernetes 允许你指定每个容器所需 CPU 和内存(RAM)。当容 器指定了资源请求时,Kubernetes 可以做出更好的决策来管理容器的资源。
- 我修复:Kubernetes 能重启容器、替换容器、杀死不符合定义运⾏状况检查的容器,并且这些功能对客户端都是无感知的。
- 密钥与配置管理:Kubernetes 允许你存储和管理敏感信息,例如密码、OAuth 令牌和 ssh 密钥。你可以在不重建容器镜像的情况下部署和更新密钥和应⽤程序配置,也⽆需 在堆栈配置中暴露密钥。
2.3 为什么是 Kubernetes?
回归根本问题:why Kubernetes?
可扩展性
Kubernetes 具有很好的可扩展性。K8s 内置一组资源,例如 Pod,Deployment、StatefulSets、Secrets、ConfigMaps 等,用户和开发人员也可以以“Custom Resource Definition”的形式添加更多自定义资源。Kubernetes 可扩展性的另一种形式是开发人员具有编写自己的 Operators 的能力,operator 允许用户通过与 Kubernetes API 进行对话来自动管理 Custom Resource Definition。
强大的社区
Kubernetes 受欢迎的另一个重要方面是其强大的社区。Kubernetes 于 2015 年在 1.0 版本时就捐赠给了与供应商无关的基金会,Kubernetes 是活跃在 Github 中前几名的项目之一:占有在所有项目前 0.01%的排名。随着项目的推进,还有各种各样的社区 SIG(特殊兴趣小组)针对 Kubernetes 中的不同领域。他们不断添加新功能,并使其对用户更加友好。
技术与革新
Kubernetes 基于谷歌的 Borg 的开源技术,它累积了 Borg15 年的深耕细作的发展和生产实践。在过去的几年中,Kubernetes 每年都有三到四个大小版本发布。引入的新功能的速度到目前也并未减慢,这些新功能使集群运营商在运行各种不同的工作负载时具有更大的灵活性。软件工程师也能拥有更多控件,以将其应用程序直接部署到生产环境中。
3. Kubernetes 构成与基本原理
3.1 Kubernetes 集群
上图可以看到一个集群主要包括两个部分:
- 一个 Master 节点,也称为控制平⾯,是 Kubernetes 集群的⼤脑。
其中包括如下组件:
- Api server:是整个系统的对外接口,供客户端和其它组件调用,相当于“营业厅”;
- Etcd:Api server 的后台数据存储,相当于 Kubernetes 集群的数据中⼼;
- Scheduler:负责对集群内部的资源进行调度,相当于“调度室”;
- Controller-manager:控制管理器,保证 Kubernetes 集群中的资源按照要求运⾏。
2、一群 Node 节点,也称为 Worker 节点 ,主要负责 Pod 的运。
主要包括以下组件:
- Kubelet:负责与 Master 节点交互,进⽽执⾏具体的任务;
- Kubelet:负责与 Master 节点交互,进⽽执⾏具体的任务;
- Kube-proxy:负责 Kubernetes 集群中的负载均衡;
- Container-runtime:提供容器运行环境,有 docker,rkt 或者其他容器实现标准,负责容器的操作,启动或者关闭等;
- Pod:k8s 的最小调度单元,1 个 pod 可以包含 1 个或多个容器,pod 可以理解为容器的集合。
3.2 Master 节点及运行机制
我们知道了 Master 节点包括 Api Server、Scheduler、Controller manager、Etcd。
下面对这几种组件一一做介绍。
Api server
Kubernetes API Server 是集群的统一入口,各组件协调者,以 HTTP API 提供接口服务,所有对象资源的增删改查和监听操作首先交给 API Server 处理,下一步再给 Etcd 存储。
Controller-manager
Controller-manager 是 Kubernetes 中的资源管理器。Kubernetes 集群中有很多
的资源,如 Node、Pod 副本、服务端点 Endpoint、命名空间 namespace、服务账号
ServiceAccount 等。Controller-manager 负责这些资源的管理,以保证这些资源实际运⾏的状态达到被期望的状态。
Etcd
Etcd 是一个高可用的键值存储系统,主要用于共享配置和服务发现。它使用 Go 语言编写,并通过 Raft 一致性算法处理日志复制以保证强一致性。
Etcd 可以理解为 Kubernetes 集群的数据中⼼,用于保存集群状态信息,比如 Pod、Service 等对象信息。Etcd 主要和 Api server 交互比较多,Api server 接受操作命令后,会将信息存储到 Etcd 中。从下文的 pod 的状态流程可看到,Api server 构建出完整的 Pod 信息,并将该信息存储到 etcd 中。
Scheduler
Scheduler 是 Kubernetes 集群中的调度器。Scheduler 的作⽤是将待调度的 Pod 按照特定的调度算法和调度策略绑定到集群中某个合适的 Node 上,并将绑定信息通过调⽤Api server 写⼊etcd 中。scheduler 在整个系统中承担了承上启下的重要功能,承上是指它负责接收 Controller manager 创建新的 Pod,为其安排一个落脚的目标 Node,启下是指安置 Node 工作完成后,目标 Node 上的 kubelet 服务进程接管 Scheduler 的后继工作。
3.3 node 节点及运行机制
Node 节点主要包括 Kubelet、Kube-proxy、Pod,其中最重要的也就是 Pod 了。
下边我们来一一进行介绍:
Kubelet
kubelet 是 Master 在 Node 节点上的 Agent,每个节点都会启动 kubelet 进程,该进程⽤于处理 Master 下发到本节点的任务,管理 Pod 及 Pod 中的容器。每个 kubelet 进程都会在 Api server 上注册节点信息,定期向 Master 汇报节点资源的使⽤情况,并监控容器和节点资源。
kubelet 在启动时通过 Api server 注册节点信息,并定时向 Api server 发送节点的新信息,Api server 在接收到这些信息后,将这些信息写⼊etcd。kubelet 监听 etcd,所有针对 Pod 的操作都会被 kubelet 监听。如果发现有新的绑定到本节点的 Pod,则按照 Pod 清单的要求创建该 Pod。如果发现本地的 Pod 被修改,则 kubelet 会做出相应的修改,⽐如在删除 Pod 中的某个容器时,会通过操作本地容器运⾏时删除该容器。
Kube-proxy
在 Kubernetes 集群的每个 Node 上都会运⾏⼀个 kube-proxy 服务进程,其核⼼功能是将到某个 Service 的访问请求转发到后端的多个 Pod 实例上。kube-proxy 本质上类似一个反向代理。我们可以把每个节点上运行的 kube-proxy 看作 service 的透明代理兼负载均衡器。kube-proxy 也监听 api server 中 service 与 Endpoint 的信息, 通过配置的 iptables 规则,请求通过 iptables 直接转发给 pod。
Pod
Pod 是最小部署单元,一个 Pod 有一个或多个容器组成,Pod 中的容器共享存储和网络,在同一台 Docker 主机上运行。
pod 的构成示意图如下所示:
可以看到由一个 pause 的根容器,加上一个或多个业务容器构成。
kubernetes 中的 pause 容器主要为每个业务容器提供以下功能:
- PID 命名空间:Pod 中的不同应用程序可以看到其他应用程序的进程 ID。
- 网络命名空间:Pod 中的多个容器能够访问同一个 IP 和端口范围。
- IPC 命名空间:Pod 中的多个容器能够使用 System IPC 或 POSIX 消息队列进行通信。
- UTS 命名空间:Pod 中的多个容器共享一个主机名 Volumes(共享存储卷)
Pod 里多个业务容器共享 pause 容器的网络栈和 Volume 挂载卷。
正是由于业务容器共享这些资源,同一个 Pod 里的容器之间仅需通过 localhost 就能互相通信,所以他们之间通信和数据交换更为高效,在设计时我们可以充分利用这一特性将一组密切相关的服务进程放入同一个 Pod 中。
3.4 Pod 的数量和版本控制
ReplicationController 用来确保容器应用的副本数始终保持在用户定义的副本数,即如果有容器异常退出,会自动创建新的 Pod 来替代。
在新版本的 Kubernetes 中建议使用 ReplicaSet 来取代 ReplicationController ,ReplicaSet ReplicaSet 跟 ReplicationController 没有本质的不同,只是名字不一样,并且 ReplicaSet 支持集合式的 selector。虽然 ReplicaSet 可以独立使用,但一般还是建议使用 Deployment 来自动管理 ReplicaSet,这样就无需担心跟其他机制的不兼容问题。Deployment 为 Pod 和 ReplicaSet 提供了一个 声明式定义方法,用来替代以前的 ReplicationController 来方便的管理应用。Deployment 典型的场景:滚动更新
Deployment 不仅可以滚动更新,还可以进行回滚,如何发现升级到 V2 版本后,发现服务不可用,可以回滚到 V1 版本。
3.5 Pod 的创建流程
以创建 Pod 为例,来理解 Kubernetes 中的各组件的运行流程:
- 通过 api server 向集群发起创建 Pod 的指令,api server 将 yaml 中的配置的属性信息(metadata)写入 etcd 中。
- apiserver 触发 watch 机制准备创建 pod,信息转发给调度器 Scheduler,Scheduler 使用调度算法选择 node,然后将 node 信息给 api server,api server 将绑定的 node 信息写入 etcd。
- api server 又通过 watch 机制,调用 kubelet,指定 pod 信息,触发 docker run 命 令创建容器。
- 创建完成之后反馈给 kubelet, kubelet 又将 pod 的状态信息给 api server, api server 又将 pod 的状态信息写入 etcd。
- ⾄此 Pod 真正的创建完毕。如果因为某些原因 Pod 出现了问题,集群中的 controller-manager 会向 apiserver 发起创建的请求。
4. Kubernetes 的服务暴露
Pod 能创建成功了,也能很好的对 Pod 做管理了,那么客户端如何找出相应的 Pod 调用其服务呢?
Kubernetes 支持多种服务暴露的方式。
下面介绍ClusterIP、NodePort和Ingress是三种广泛使用的服务暴露方式。其中 ClusterIP 和 NodePort 方式属于 Service 资源类型,Ingress 属于 Ingress 资源类型。
4.1 Service—ClusterIP 服务暴露
Kubernetes 集群会为一组 Pod 分配对应的集群 IP,同时产⽣⼀个域名。集群内部可以通过这集群 IP 或者域名访问 Service 对应的 Pod。
该类型只能在集群内部访问,也是默认的 ServiceType。
apiVersion: v1kind: Servicespec:
metadata:
name: my-service
selector:
app: my-app
type: ClusterIP
ports:
- name: http
port: 80
targetPort: 8080
protocol: TCP
上面的示例定义了一个 ClusterIP Service。ClusterIP 上端口 80 的流量将转发到 Pod 上的 8080 端口 (targetPort 配置项)上,携带 app: my-app标签的 Pod 将被添加到 Service 中作为作为服务的可用节点。
常见使用场景:
集群内部服务的暴露。
4.2 Service—NodePort 服务暴露
NodePort 在固定端口号上公开向集群外部暴露服务,它允许从集群外部访问该服务,在集群外部需要使用集群的 IP 地址和 NodePort 指定的端口才能访问。
创建 NodePort Service 将在集群中的每个 Node 上开放该端口。
创建 NodePort Service 的同时还会自动创建一个 ClusterIP 类型的 Service,NodePort 会将端口上的流量路由给 ClusterIP` 类型的 Service。
常见使用场景:
可以使用 NodePort 快速设置开发,测试环境的服务或在其上公开 TCP 或 UDP 服务,但是对于公开 HTTP 服务来说 NodePort 不是一个的理想选择,因为其使用的都是非 HTTP 标准的端口。
4.3 Ingress 服务暴露
Ingress 实际上是与 Service 完全不同的资源,算是 Service 上面的一层代理,通常在 Service 前使用 Ingress 来提供 HTTP 路由配置。它让我们可以设置外部 URL、基于域名的虚拟主机、SSL 和负载均衡。
为了让 Ingress 资源⼯作,集群必须有⼀个正在运⾏的 Ingress 控制器。与 Controller-manager 控制器不同,Ingress 控制器不是随集群⾃动启动的,可以选择不同外部组件来做 Ingress 控制器。
比如:nginx-ingress,它使用 nginx 服务器作为反向代理来把流量路由给后面的 Service。
常见使用场景:
可以处理 http,https 请求的流量,并且可以处理多个域名的流量。一个 IP 可以暴露多个应用,支持同域名不同 uri,支持证书等功能。使用场景比较广泛。
5. Kubernetes 在企业中的应用
无论是中小型企业,还是大型互联网为了提高资源利用率,实现降本增效,利用 Kubernetes 构建一套自动化运维环境已成为最佳选择。
下图是企业服务云平台正在实现的云上架构能力的一幅蓝图,其中服务容器化作为云平台基础中的基础能力。在容器的编排和管理上最基础能力上实现扩容、限流等可操作 api 和日志、监控等可观测的 api。上层能力层可通过这些 api 提供服务构建,部署,流量控制等功能。
可见 Kubernetes 在整个架构图中重要的位置。
从容器编排管理的实现细节来看,如下图,经过 7 层负载,请求会打到一组 Ingress 控制器上,由于容器的 ip 会经常变动,所以 Ingress 会向上屏蔽掉变动,到达了 Ingress 请求就由 Kubernetes 来控制了,Kubernetes 会将此请求路由到相应服务的某个 Pod 上,同一个服务会有多个 Pod,这些 Pod 也会分部在不容物理机上。目前贝壳采用 Eureka 注册,每个 Pod 对应的服务节点,都会在 Eureka 上注册, 如果服务 A 需要调用服务 B,则请求到服务 A 的 pod 后,会从 Eureka 上寻找服务 B 的 Pod 的节点(目前通过 Eureka+DNS 来实现)拿到上游服务 Pod 信息后就直接调用服务 B 某一个 Pod 上的服务了。