关于 Kubrenetes 系列其他文章的传送门
-
《 理解 k8s 高可用,让你的集群稳如泰山 》 您当前所在位置

虽然现在有一些部署工具,但是为了更好的理解 k8s 的高可用架构,这里全部采用手动的方式部署。
一、Master 节点中服务的高可用分析¶
k8s 核心服务¶
- kube-apiserver:因为数据存放在 ETCD 中,所以本身是无状态的;有两种高可用的方式:
- 在前端部署 Keepalived:对 apiServer 通过 1 个 VIP 来访问,同时只有一个节点在工作;也可以在每个节点都绑定一个虚拟路由器,这样三个节点都有 VIP,都能工作,只是 VIP 不同;
- 在前端使用 Nginx 做反向代理:对后端的多个 apiServer 做健康状态检测,并进行负载均衡调度;对 apiServer 的所有访问都通过 Nginx 的地址。
- kube-controller-manager:有状态。
- kube-scheduler:有状态。scheduler 一直监视着 apiServer,当用户提交资源创建清单到任意一台 apiServer 后,由 scheduler 来挑选出合适的节点,并发给运行在 Node 上的 kubelet 来创建资源。
为了防止多个 scheduler 同时发布任务导致 kubelet 混乱,同时只能有一个 scheduler 在工作,其他都为备用的。
分布式协作¶
在 zookeeper 中,分布式协作的方式是通过在一个存储系统中抢占资源的方式,选举出 Leader,这依赖一个外部的高可用存储系统。
而 k8s 内部已经有一个高可用的 ETCD 存储系统,于是在 apiServer 中保留了一个固定的 endpoint 资源,当 kube-scheduler 或者 kube-controller 选举主节点时,多个程序通过抢占这个资源来确定谁是 Leader 节点,这种分布式协作的方式称为分布式锁。
ETCD 如何高可用¶
ETCD 其实就是一个键值存储系统。
ETCD 作为储存 k8s 所有数据的中心组件,内键了一种高可用机制,是基于 Raft 协议来实现 Leader 选举和分布式协作的。每一个节点都可以读写,但是写到任何一个节点上的数据,都要被同步到同一集群的另外的节点,并且要确保数据是强一致的。而 Raft 是个简装版的 PAXOS 协议,比 PAXOS 更轻量化,更适合这个分布式的轻量化的协作应用场景当中,而且功能并不比 PAXOS 弱。
由于每个节点中都存储了一样的数据,所以在写数据时要在每个节点都写一次,所以节点数量越多,写的性能越差,一般情况下:
- 小规模:3 个节点;
- 中等规模:5 个节点;
- 大规模:7 个节点。

二、高可用部署规划¶
Master 节点的高可用规划¶
- ETCD:至少 3 个,之间使用数字证书加密通信;每个节点和客户端通信时,要有双向数字证书认证;
- ETCD-CA:专用于为每一个 ETCD 节点颁发证书;
- kube-apiserver、controller-manager、scheduler:各一主一备即可;
整体架构¶
一共 3 个 Master 节点,2 个 Node 节点。部署顺序:
- 在 3 个 Master 节点中先部署好高可用的 ETCD;
- 在 3 个 Master 节点中部署 k8s 的三个 master 组件:apiServer、scheduler、controller-manager,配置为高可用状态;
- 最后把两个 Node 节点加入到这个集群;
与 etcd 最少需要 3 个节点不同,apiServer、controller-manager、scheduler 的数量可以是随意的,他们通过分布式锁的方式抢占,这里我们把他们高可用为 3 个。
实验架构如图:

准备二进制程序¶
这次不使用 kubeadm,而是使用官方所提供的二进制程序直接进行部署。
项目官方的二进制发布页,在 releases 页面,点击当前版本中的 CHANGELOG-1.17.md,目前版本为 v1.17,在这里下载二进制程序。
- Server Binaries:提供 apiServer、scheduler、controller-manager;
- Node Binaries:提供各 Node 节点需要的程序,如 kubelet 等。
- Client Binaries:提供 kubelet 客户端程序;
三、部署 ETCD¶
1. 安装 ETCD¶
注意:以下操作在所有 Master 节点进行。
ETCD 已经收录在 epel 仓库中,有 V2 和 V3 版,要使用 V3 版的。
1 | yum install etcd |
2. 配置 ETCD¶
准备工作¶
注意:这一步要在每个 Master、Node 节点配置。
配置 Hosts 文件,还有防火墙、SELinux、时钟同步都要配置好,这里就不一一说明了:
1 | 192.168.50.11 master01 master01.monster.com etcd01 etcd01.monster.com |
/var/lib/etcd:存放默认数据路径;/etc/etcd/etcd.conf:变量配置文件,给 Unit Service 传递变量;
依次编辑三个 master 节点上 ETCD 的配置文件 /etc/etcd/etcd.conf 启用集群功能:
注意:在另外两个节点配置时,要使用节点自己的域名和 IP。
1 | 这个端口用于各节点通信使用,要使用其他节点可以访问的地址,不可以使用域名 |
启动 etcd 服务¶
注意:这里目前还没有启用 TLS 加密通信,如果你计划开启加密通信,请先完成下面的 配置 ETCD 使用 TLS 加密传输 后再来启动 ETCD 服务。
当只有一个节点启动服务时会阻塞,因为不到法定票数,最少要启动 2 个节点的服务才行:
1 | 在 etcd01、etcd02、etcd03 分别启动服务 |
验证安装¶
etcdctl是 ETCD 的客户端工具;--endpoints=‘’:指定要连接的 Server 地址,默认不指为127.0.0.1:2379;
1 | # etcdctl --endpoints='http://192.168.50.11:2379' member list |
3. 配置 ETCD 使用 TLS 加密传输¶
没有计划使用 TLS 时可以跳过此步骤。
注意:以下操作在其中一台 Master 节点操作。
k8s 中的 TLS 需要三种证书:
- peer 类型的证书:集群内各节点之间使用,既是客户端证书也是服务端证书;
- Server 证书:Client 端验证 Server 端身份时使用的证书;
- Client 证书:Server 端验证 Client 端身份时使用的证书。
生成证书¶
能够一次性生成对等证书、Server 证书和客户端证书的项目:https://github.com/iKubernetes/k8s-certs-generator
1 | # git clone https://github.com/iKubernetes/k8s-certs-generator |
启用加密传输¶
编辑三个 master 节点中的配置文件 /etc/etcd/etcd.conf,配置 #[Security] 字段中关于 TLS 的配置:
1 | #[Security] |
启动 ETCD 服务¶
在 etcd01、etcd02、etcd03 节点分别启动服务。
1 | # systemctl start etcd |
使用域名验证操作¶
1 | 使用 https 时需要同时指定证书和 CA 的文件 |
四、部署 Master 节点¶
注意:以下操作在之前生成过证书的 Master 节点上操作。
生成 k8s 的证书
还是使用刚才一键生成证书的脚本,生成 k8s 的证书,会创建专用的文件夹来存放各个节点的证书。
1 | # cd /root/k8s-certs-generator |
在刚才生成的证书里,用于 apiserver 访问 etcd 的证书和私钥文件 apiserver-etcd-client.crt 和 apiserver-etcd-client.key,是脚本从之前创建 ETCD 证书时生成的同名文件复制过来的,不用在手动复制了。
部署 Server 端¶
注意:以下操作在所有 Master 节点进行。
把之前下载的 Server 端二进制压缩包上传并解压至 /usr/local/ 目录下:
1 | # tar -zxvf kubernetes-server-linux-amd64.tar.gz -C /usr/local |
在解压后的目录中应该可以看到 apiServer、controller-manager、scheduler 的主程序了。
配置文件¶
注意:以下操作在所有 Master 节点进行。
在这个项目下有 Master 节点需要用到的配置文件的模板:
https://github.com/iKubernetes/k8s-bin-inst
1 | # tree k8s-bin-inst |
把项目克隆下来,并把配置文件模板和 Unit 文件都复制到对应目录:
1 | 复制配置文件模板 |
修改 apiServer 的配置文件 vim /etc/kubernetes/apiserver,找到 KUBE_ETCD_SERVERS= 字段,配置 etcd 的访问地址:
1 | ... |
创建一个系统用户:
1 | # useradd -r kube |
创建 k8s 临时状态保存目录:
1 | # mkdir /var/run/kubernetes |
启动 apiServer 服务:
1 | # systemctl daemon-reload |
配置 kubectl
1 | 为 kubectl 创建软连接,让 shell 可以搜索到命令 |
授权 Boot Strapper
之前用脚本创建证书时自动生成了一个 token.csc 文件,里面配置了一个用户,如下:
1 | # cat /etc/kubernetes/token.csv |
实际上这个用户没有被授权允许做 Boot Strapper,需要手动为这个用户做一下授权:
注意:这一步只在一个 Master 节点操作即可。
1 | 创建一个 ClusterRoleBinding,并授予 node-bootstrappers 角色,这个角色是内键的用于初次拉起 Node 节点时使用的: |
接下来在所有 Master 节点启动 controller-manager、sheuduler 服务。
因为之前已经把配置文件模板复制到对应位置,如无特殊需求,直接启动 controller-manager 和 sheuduler 的服务即可:
1 | # systemctl start kube-controller-manager |
注意:Master 节点中默认的通用配置文件 /etc/kubernetes/config 中定义了日志级别为 debug,投入生产后一定记得把日志级别调高,否则日志量会巨大。
至此 Master 节点就部署好并正常启动了,下面把 Node 加入到这个集群中。
五、部署 Node 节点¶
把 Node 拉入集群是通过 Boot Strapper 的方式,并且第一次登录是基于 token 认证。加入集群后,两者之间就变为使用证书进行认证。
注意:两个 Node 节点也要能通过 Hosts 文件解析其他所有节点,还有防火墙、SELinux、时钟同步都要配置好,这里就不一一讲解了:
每个 Node 节点中都会运行三个守护进程:
- kubelet
- kube-proxy
- docker
注意:以下操作在所有 Node 节点进行。
1. 部署 Docker¶
安装¶
1 | step 1: 安装必要的一些系统工具 |
配置代理¶
k8s 中的 Pos 依赖一个基础镜像叫 pause,但是因为众所周知的原因我们无法访问 Google 的仓库,所以下面就配置代理用于从 Google 仓库下载镜像使用。
编辑 docker 的 UNIT 文件 vim /usr/lib/systemd/system/docker.service,改成如下:
1 | ... |
启动 Docker 服务¶
1 | # systemctl daemon-reload |
2. 安装配置 kubelet 和 kube-proxy¶
安装程序¶
把之前下载的 Node 端二进制压缩包上传并解压至 /usr/local/ 目录下:
1 | # tar -zxvf kubernetes-node-linux-amd64.tar.gz -C /usr/local |
在解压后的目录中应该可以看到 kubelet、kube-proxy 等主程序了。
kubelet 和 kube-proxy 的配置文件¶
在 v1.12 版以后,kubelet 和 Kube-proxy 支持从本地的配置文件加载参数,和在命令行中指定参数效果一样,大大简化了部署 Node 时配置的复杂度,配置文件存储为 yaml 或 json 格式,可通过 --config 选项加载此配置文件,一般存放在 /var/lib/ 目录下,有 kubelet 和 kube-proxy 目录专门用来存放各自的配置文件:
注意:在 Master 节点上完成下列操作。
部署 master 节点时,曾经克隆了一个配置文件的模板项目,去 master 节点上把关于 kubelet 和 kube-proxy 的配置文件和证书复制到所有 Node 节点:
1 | 复制配置文件 |
创建工作目录,否则会报错:
1 | # mkdir -pv /etc/kubernetes/manifests |
3. 安装 CNI 网络插件,在所有 Node 节点操作¶
项目地址:https://github.com/containernetworking/plugins/releases
在 releases 页面中复制下载连接,之后去 Node 节点中下载:
1 | # wget https://github.com/containernetworking/plugins/releases/download/v0.8.3/cni-plugins-linux-amd64-v0.8.3.tgz |
4. 添加一个临时的域名解析¶
Node 节点上关于 BootStrap 的配置文件 /etc/kubernetes/auth/bootstrap.conf 中,定义了拉起 Node 时通过这个域名 https://kubernetes-api.monster.com:6443 来访问 apiServer,这个域名代表什么呢?
当 master 节点高可用时,就会有 3 个 apiServer,而这个地址是为这 3 个 apiServer 的前端负载均衡器使用的。在生产环境中,负载均衡器可以这样部署:
- 在 3 个 apiServer 的前边部署一个 Nginx,并工作在 4 层代理模式,把 TLS 访问负载均衡代理至后端,当然 Nginx 也要部署为高可用,之后在内部的 DNS 中将域名
kubernetes-api.monster.com映射到每个 Nginx 的地址上,通过 DNS 形成轮询解析 Nginx 地址的效果; - 在 3 个 apiServer 节点上部署 Keepalived,流动一个 VIP,在内部的 DNS 中将域名
kubernetes-api.monster.com的解析到 VIP 上; - 在内部的 DNS 中,将此域名映射到 3 个 apiServer 的 IP 上,通过 DNS 进行轮询访问,但是这样有一个最大的缺点是无法对 apiServer 的健康状态进行检测,不推荐。
但是当前测试环境没有内部的 DNS 服务器,所以临时在 Node 节点的 hosts 文件中添加一个 Hosts 记录,把此地址解析到 master 节点即可,如下:
1 | # grep "api" /etc/hosts |
5. 启动 kubectl 服务¶
在每个 Node 节点启动 kubectl 服务¶
1 | # systemctl daemon-reload |
kubectl 启动后,就会自动向 apiServer 提交一个证书签署请求。
如 kubelet 启动报错¶
如果 kubelet 启动报错,并在日志内发现如下异常日志:
1 | # journalctl -u kubelet |
网上搜索后也确实在官网上搜到了相关的 pr(#77820),其中提到的解决方式:
ACTION REQUIRED: Deprecated Kubelet security controls AllowPrivileged, HostNetworkSources, HostPIDSources, HostIPCSources have been removed. Enforcement of these restrictions should be done through admission control instead (e.g. PodSecurityPolicy).
ACTION REQUIRED: The deprecated Kubelet flag --allow-privileged has been removed. Remove any use of --allow-privileged from your kubelet scripts or manifests.
调整 k8s 配置文件后,问题得到缓解,编辑 /etc/kubernetes/config:
1 | 注释掉这一行 |
6. 签署 Node 的证书¶
在 Master 节点上,查看收到的证书签署请求:
1 | # kubectl get csr |
进行证书签署,如有多个相同的请求,签署最新的即可:
1 | # kubectl certificate approve csr-6z4dm |
签署后,在 master 节点中应该可以看到 Node 节点已经加入,只是状态还是 NotReady:
1 | # kubectl get nodes |
7. 启用 ipvs 内核模块¶
在 /var/lib/kube-proxy/config.yaml 配置文件中定义了 Pod 的网段为 10.244.0.0/24,网络模式为 ipvs。
创建载入内核模块的脚本文件 vim /etc/sysconfig/modules/ipvs.modules,设定自动载入内核模块。文件内容如下:
1 | !/bin/bash |
给予脚本执行权限并执行:
1 | # chmod +x /etc/sysconfig/modules/ipvs.modules |
8. 启动 kube-proxy¶
1 | # systemctl start kube-proxy |
9. 部署 flannel 网络插件¶
注意:先部署 kube-proxy 之后,再部署 kube-flannel。
flannel 网络插件项目地址:https://github.com/coreos/flannel
1 | # kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml |
之后在 master 节点上就可以看到,Node 的状态已经 Ready 了:
1 | # kubectl get nodes |
如果无法访问 grc 仓库¶
如果 flannel 的 Pod 卡在无法获取镜像,使用以下办法曲线救国:从其他仓库地方下载到本地,然后更改 tag,其他镜像也可以使用此方法,把要下载的镜像改成需要的名字即可:
1 | 下载 pause:3.1 镜像,如下载其他镜像该名字即可 |
六、配置 CoreDNS¶
CoreDNS 官方用于在 kubernetes 中部署的项目地址:https://github.com/coredns/deployment/tree/master/kubernetes
1 | # mkdir coredns && cd coredns |