前言
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 集群的架构特点,下图是官方推荐的高可用集群架构图。
其核心思想是让 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 争夺资源导致节点资源短缺。
使用 RKE 构建高可用 k8s 集群
节点规划
构建集群的第一步是将拥有的服务器按节点功能进行划分,下表展示了笔者环境下的节点规划情况。
| 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
在完成环境准备后,需要通过 cluster.yml 描述集群的组成和 k8s 的部署方式。
配置集群组成
配置文件 cluster.yml 中的 nodes 配置项用于描述集群的组成。根据节点规划,对于 k8s master 节点,指定其角色为controlplane和etcd。对于 k8s worker 节点,指定其角色为worker。
nodes:
- address: 192.168.0.1
user: admin
role:
- controlplane
- etcd
...
- address: 192.168.0.7
user: admin
role:
- 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: True
cgroup-driver: cgroupfs
kube-reserved: cpu=1,memory=2Gi,ephemeral-storage=1Gi
kube-reserved-cgroup: /runtime.service
system-reserved: cpu=1,memory=1Gi,ephemeral-storage=1Gi
system-reserved-cgroup: /system.slice
enforce-node-allocatable: pods,kube-reserved,system-reserved
eviction-hard: memory.available
关注
打赏
