Kubernetes(简称 K8s)是 Google 推出的开源容器管理系统,提供了强大的容器编排能力。这篇文章主要是读<Kubernetes in Action> 和官方文档以及Kubernetes Handbook和Istio Handbook的一些总结,图例均取网上资料。
微服务
在介绍 K8s 之前,先聊聊后台架构的演化历程。随着业务的复杂性不断提高,后台架构也从单体应用(monolithic app)逐渐发展成微服务架构(microservices)。把单体应用切分成颗粒度更小可独立部署的微服务,能给系统带来更高的扩展性:
每个微服务可以由不同的团队进行开发和部署,因此也加快了整个系统的迭代速度。但同时这样也会带来问题,比如,每个团队所依赖的库版本不一致:
如果用的是动态库,同时又部署在同一台机器上的话,那无疑会给运维工作带来巨大的麻烦。另一方面,开发环境和正式环境也可能存在差异,正式环境的机器通常由运维管理,而开发环境的机器通常由开发人员自行维护,这也给部署工作带来一些问题。
为了解决这个问题,我们需要容器。
容器
什么是容器?官方解释,容器即将软件打包成标准化单元,以用于开发、交付和部署。简单的说,容器将应用以及其依赖打包成一个容器,以实现应用程序的运行环境无关。容器的出现解决了开发环境和生产环境存在差异的问题。
容器 vs 虚拟机
同样是虚拟化技术,容器是在操作系统层面上实现虚拟化,直接复用本地主机的操作系统,虚拟机则是在硬件层面实现:
隔离机制
容器通过 命名空间(Linux Namespaces)和 cgroup(Linux Control Groups) 来实现隔离。命名空间隔离系统资源,包括挂载点、进程树、网络接口、文件、IPC 等,cgroup 隔离硬件资源,包括 CPU、内存、网络带宽等。
Docker
Docker 是现在比较流行的容器构建分发平台。Docker 把应用和依赖环境打包成镜像(image),镜像可以推送到仓库(Registries)进行分发。
Docker 镜像是由一系列只读的层组成的。而 Docker 容器则在镜像层的最上方新增一个可写层,即容器层,所有对于运行时容器的修改都是对该容器层的修改:
AUFS
AUFS 即联合文件系统,能够将不同文件夹联合(Union)到同一个文件夹中。Docker 采用 AUFS 来管理镜像:
镜像中的每一层以及容器层,都挂载到 /var/lib/docker/
的子目录下,通过 AUFS 为多个镜像层提供了统一的目录视图。关于这部分的资料可以参考<Use the AUFS storage driver>
Kubernetes
介绍完微服务和容器,终于到了这篇文章的主题-Kubernetes。这里不打算深入 K8s 的细节,主要简单聊聊 K8s 设计思想和架构。
声明式设计
K8s 采用声明式的设计理念。声明式(Declarative)和命令式(Imperative)的区别,从字面意思可以得知,声明式是描述的是结果,而命令式描述的是过程。
我认为 K8s 采用声明式的设计,好处如下:
- 通过配置文件,可以清晰的了解整个系统的目标状态
- 分布式网络环境下,声明式更容易保证幂等性,支持失败超时重试
架构
K8s 集群可以划分为两个部分:
- 主节点:集群的控制平面,包括
- etcd:负责集群信息的持久化
- API Server:提供集群管理的 REST API 接口
- Scheduler:负责调度 Pod 到 Node
- Controller Manager:通过 API Server 监控集群状态,致力将当前状态转变为期望的状态
- 工作节点:运行实际部署的应用,包括
- Kubelet:确保 Pod 的正常运行
- Kubernetes Service Proxy(kube-proxy):网络代理,实现 Service 概念的一部分
- Container Runtime:负责运行容器的软件,例如 Docker
调度原理
下图取自 Scheduling Framework 所展示的 K8s 调度框架:
一次调度过程分为两个周期:调度周期和绑定周期。调度周期为 Pod 选择一个节点,绑定周期将该决策应用于集群。调度周期是串行的,而绑定周期可能是并行的。
整个调度过程分为四个步骤:
- 队列排序
- 节点过滤
- 节点评分&排序
- 节点绑定
具体的调度过程可以参考《五分钟了解k8s调度器kube-scheduler》
另外,《调度器性能调优》 中介绍了采用节点打分阈值来优化打分性能的方法,简单的说就是采样。
HPA
K8s 支持节点自动水平伸缩(Horizontal Pod Autoscaler),即 HPA。
HPA 目前支持的指标包括 CPU 使用率、内存和自定义指标,每周期(默认 15 秒)采集一次指标值,通过下列公式:
目标副本数 = ceil[当前副本数 * ( 当前指标值 / 目标指标值 )]
计算出期望的目标,通过控制器对 Pod 进行缩扩容。
服务网格
服务网格(Service Mesh)是一个专门处理服务通讯的基础设施层。它的职责是在由云原生应用组成服务的复杂拓扑结构下进行可靠的请求传送。在实践中,它是一组和应用服务部署在一起的轻量级的网络代理,对应用服务透明。
服务网格从架构上分为控制平面(Control Plane)和数据平面(Data Plane)。控制平面下发策略和配置,将所有数据平面转变为分布式系统;数据平面则主要负责处理和转发数据包。Istio 是由 Google、IBM、Lyft 等共同开源的服务网格框架,下图为 istio 的架构图:
Kubernetes vs Service Mesh
Kubernetes 的本质是应用的生命周期管理,即应用的部署和管理(扩缩容、自动恢复、发布)。Kubernetes 为微服务提供了可扩展、高弹性的部署和管理平台。
Service Mesh 的基础是透明代理,通过 sidecar proxy 拦截到微服务间流量后再通过控制平面配置管理微服务的行为。
Service Mesh 将流量管理从 Kubernetes 中解耦,Service Mesh 内部的流量无需 kube-proxy 组件的支持,通过为更接近微服务应用层的抽象,管理服务间的流量、安全性和可观察性。Service Mesh 是对 Kubernetes 中的 service 更上层的抽象,它的下一步是 serverless。
kube-proxy 的缺陷
- 四层负载均衡
- 失败不会自动重试
- 不支持金丝雀(灰度)发布/蓝绿发布