Kubernetes(k8s) 凭借着其优良的架构,灵活的扩展能力,丰富的应用编排模型,成为了容器编排领域的事实标准。越来越多的企业拥抱这一趋势,选择 k8s 作为容器化应用的基础设施,逐渐将自己的核心服务迁移到 k8s 之上。
可用性对基础设施而言至关重要。各大云计算厂商纷纷推出了高可用、可扩展的 k8s 托管服务,其中比较有代表性的有 Amazon EKS、Azure Kubernetes Service (AKS)、Google Kubernetes Engine、阿里云容器服务 Kubernetes 版等。
虽然公有云托管的 k8s 服务百花齐放,但很多企业仍有自建集群的需求。正是这样的原因,促进了一大批出色的 k8s 集群部署方案的诞生,他们的特点如下表所示。
| 部署方案 | 特点 |
|---|---|
| Kubeadm | 1. 官方出品的部署工具,提供了 k8s 集群生命周期管理的领域知识。 2. 旨在成为更高级别工具的可组合构建块。 |
| Kubespray | 1. 支持在裸机和 AWS、GCE、Azure 等众多云平台上部署 k8s。 2. 基于 Ansible Playbook 定义 k8s 集群部署任务。 3. 支持大部分流行的 Linux 发行版。 |
| Kops | 1. 仅支持在 AWS、GCE 等少数云平台上部署 k8s。 2. 建立在状态同步模型上,用于 dry-run 和自动幂等性。 3. 能够自动生成 Terraform 配置。 |
| Rancher Kubernetes Engine(RKE) | 1. 著名的开源企业级容器管理平台 Rancher 提供的轻量级 k8s 安装工具。 2. 支持在裸机、虚拟机、公有云上部署和管理 k8s 集群。 |
上述方案中,RKE 在易用性和灵活性上占有优势。本文接下来将介绍如何通过 RKE 部署一套高可用 k8s 集群,文中使用的 RKE 版本为v0.2.2。
首先需要了解高可用 k8s 集群的架构特点,下图是官方推荐的高可用集群架构图。

其核心思想是让 k8s master 节点中的各类组件具备高可用性,消除单点故障。
kube-apiserver - 对外暴露了 k8s API,是整个集群的访问入口。由于 apiserver 本身无状态,可以通过启动多个实例并结合负载均衡器实现高可用。etcd - 用于存储 k8s 集群的网络配置和对象的状态信息,是整个集群的数据中心。可以通过启动奇数个 etcd 实例建立一个冗余的,可靠的数据存储层。kube-scheduler - 为新创建的 pod 选择一个供他们运行的节点。一个集群只能有一个活跃的 kube-scheduler 实例,可以同时启动多个 kube-scheduler 并利用领导者选举功能实现高可用。kube-controller-manager - 集群内部的管理控制中心。一个集群只能有一个活跃的 kube-controller-manager 实例,可以同时启动多个 kube-controller-manager 并利用领导者选举功能实现高可用。此外,构建集群的时还需要注意下列问题。
节点上 k8s 进程的可靠性。需要让 kubelet、kube-scheduler、kube-controller-manager 等进程在出现故障后能自动重启。为 worker node 中的非 pod 进程预留资源,防止他们将与 pod 争夺资源导致节点资源短缺。构建集群的第一步是将拥有的服务器按节点功能进行划分,下表展示了笔者环境下的节点规划情况。
| IP | 角色 |
|---|---|
| 192.168.0.10 | 部署节点 |
| 192.168.0.11 | k8s master - api-server, etcd, scheduler, controller-manager |
| 192.168.0.12 | k8s master - api-server, etcd, scheduler, controller-manager |
| 192.168.0.13 | k8s master - api-server, etcd, scheduler, controller-manager |
| 192.168.0.14 | k8s worker - kubelet, kube-proxy |
| 192.168.0.15 | k8s worker - kubelet, kube-proxy |
| 192.168.0.16 | k8s worker - kubelet, kube-proxy |
| 192.168.0.17 | k8s worker - kubelet, kube-proxy |
规划说明:
单独选择了一台机器192.168.0.10作为部署节点。如果机器数不多,可以将部署节点加入到 k8s 集群中。为了保证可用性,择了三台机器部署 k8s master 组件。如果有条件,可以将 etcd 和 master 中的其他组件分开部署,这样可以根据需要更灵活地控制实例个数。例如,在访问压力不大但对数据可靠性要求比较高的情况下,可以专门选择 5 台机器部署 etcd,另外选择 3 台机器部署 master 中的其他组件。剩余四台机器作为 k8s worker 节点。节点个数需要根据实际情况动态调整。当有 pod 因资源不足一直处于 pending 状态时,可以对 worker 进行扩容。当 node 的资源利用率较低时,且此 node 上存在的 pod 都能被重新调度到其他 node 上运行时,可以对 worker 进行缩容。 在完成节点规划后,需要进行环境准备工作,主要包含以下内容:
安装 RKE - 需要在部署节点(192.168.0.10)上安装 RKE 二进制包,具体安装方法可参考 download-the-rke-binary。配置 SSH 免密登录 - 由于 RKE 通过 SSH tunnel 安装部署 k8s 集群,需要配置 RKE 所在节点到 k8s 各节点的 SSH 免密登录。如果 RKE 所在节点也需要加入到 k8s 集群中,需要配置到本机的 SSH 免密登录。安装 docker - 由于 RKE 通过 docker 镜像rancher/hyperkube启动 k8s 组件,因此需要在 k8s 集群的各个节点(192.168.0.11 ~ 192.168.0.17 这 7 台机器)上安装 docker。关闭 swap - k8s 1.8 开始要求关闭系统的 swap,如果不关闭,默认配置下 kubelet 将无法启动。这里需要关闭所有 k8s worker 节点的 swap。 在完成环境准备后,需要通过 cluster.yml 描述集群的组成和 k8s 的部署方式。
配置文件 cluster.yml 中的 nodes 配置项用于描述集群的组成。根据节点规划,对于 k8s master 节点,指定其角色为controlplane和etcd。对于 k8s worker 节点,指定其角色为worker。
nodes:- address: 192.168.0.1user: adminrole:- controlplane- etcd...- address: 192.168.0.7user: adminrole:- worker
K8s 的 worker node 除了运行 pod 类进程外,还会运行很多其他的重要进程,包括 k8s 管理进程,如 kubelet、dockerd,以及系统进程,如 systemd。这些进程对整个集群的稳定性至关重要,因此需要为他们专门预留一定的资源。
笔者环境中的 worker 设置如下:
节点拥有 32 核 CPU,64Gi 内存和 100Gi 存储。为 k8s 管理进程预留了 1 核 CPU,2Gi 内存和 1Gi 存储。为系统进程预留了 1 核 CPU,1Gi 内存和 1Gi 存储。为内存资源设置了 500Mi 的驱逐阈值,为磁盘资源设置了 10% 的驱逐阈值。在此场景下,节点可分配的 CPU 资源是 29 核,可分配的内存资源是 60.5Gi,可分配的磁盘资源是 88Gi。对于不可压缩资源,当 pod 的内存使用总量超过 60.5Gi 或者磁盘使用总量超过 88Gi 时,QoS 较低的 pod 将被优先驱逐。对于可压缩资源,如果节点上的所有进程都尽可能多的使用 CPU,则 pod 类进程加起来不会使用超过 29 核的 CPU 资源。
上述资源预留设置在 cluster.yml 中具体形式如下。
services:kubelet:extra_args:cgroups-per-qos: Truecgroup-driver: cgroupfskube-reserved: cpu=1,memory=2Gi,ephemeral-storage=1Gikube-reserved-cgroup: /runtime.servicesystem-reserved: cpu=1,memory=1Gi,ephemeral-storage=1Gisystem-reserved-cgroup: /system.sliceenforce-node-allocatable: pods,kube-reserved,system-reservedeviction-hard: memory.available<500Mi,nodefs.available<10%
关于资源预留更详细的内容可参考文章 Reserve Compute Resources for System Daemons。
当 cluster.yml 文件配置完成后,可以通过命令rke up完成集群的部署任务。下图展示了通过 RKE 部署的 k8s 集群架构图。

该架构有如下特点:
集群中的各个组件均通过容器方式启动,并且设置重启策略为always。这样当他们出现故障意外退出后,能被自动拉起。Master 节点上的 kube-scheduler、kube-controller-manager 直接和本机的 API server 通信。Worker 节点上的 nginx-proxy 拥有 API server 的地址列表,负责袋里 kubelet、kube-proxy 发往 API server 的请求。为了让集群具有灾备能力,master 节点上的 etcd-rolling-snapshots 会定期保存 etcd 的快照至本地目录/opt/rke/etcd-snapshots中。 在完成了集群部署后,可以通过 API server 访问 k8s。由于环境中启动了多个 kube-apiserver 实例以实现高可用,需要为这些实例架设一个负载均衡器。这里在192.168.0.10上部署了一台 nginx 实现了负载均衡的功能,nginx.conf 的具体配置如下。
...stream {upstream apiserver {server 192.168.0.11:6443 weight=5 max_fails=3 fail_timeout=60s;server 192.168.0.12:6443 weight=5 max_fails=3 fail_timeout=60s;server 192.168.0.13:6443 weight=5 max_fails=3 fail_timeout=60s;}server {listen 6443;proxy_connect_timeout 1s;proxy_timeout 10s;proxy_pass apiserver;}}...
这时,通过负载均衡器提供的端口访问 API server 会出现异常Unable to connect to the server: x509: certificate is valid for xxx, not 192.168.0.10。这里需要将负载均衡器的 IP 地址或域名加入到 API server 的 PKI 证书中,可以通过 cluster.yml 中的 authentication 配置项完成此功能。
authentication:strategy: x509sans:- "192.168.0.10"
修改完 cluster.yml 后,运行命令rke cert-rotate。
在完成上述所有步骤后,可以通过命令kubectl get nodes查看节点状态。如果所有节点的状态均为 Ready,则表示集群部署成功。
NAMESTATUSROLESAGEVERSION192.168.0.11Ready controlplane,etcd1d v1.13.5192.168.0.12Ready controlplane,etcd1d v1.13.5192.168.0.13Ready controlplane,etcd1d v1.13.5192.168.0.14Ready worker 1d v1.13.5192.168.0.15Ready worker 1d v1.13.5192.168.0.16Ready worker 1d v1.13.5192.168.0.17Ready worker 1d v1.13.5
Rancher Kubernetes Engine(RKE)为用户屏蔽了创建 k8s 集群的复杂细节,简化了部署步骤,降低了构建门槛。对于那些有自建 k8s 集群需求的企业是一个不错的选择。