0%

理解 k8s 高可用,让你的集群稳如泰山

关于 Kubrenetes 系列其他文章的传送门

  1. 《 部署 Kubernetes 也能如此简单吗? 》

  2. 《 初识 k8s,新时代的宠儿 》

  3. 《 深度学习 Pod 》

  4. 《 学会这 5 种 Pod 控制器,搞定发布、更新、回滚,从此告别人肉运维!! 》

  5. 《 k8s 中的网络入口 – Service 资源管理 》

  6. 《 k8s 中的 7 层调度 – Ingress 》

  7. 《 k8s 中的存储系统 – Volumes 》

  8. 《 k8s 中强大的配置中心 – ConfigMap 》

  9. 《 k8s 中的部署神器 – Statefulset 控制器 》

  10. 《 k8s 中的安全守护神 – 你不知道不代表它不在 》

  11. 《 k8s 中的图形接口 – Dashboard 》

  12. 《 k8s 中的网络插件 – flannel 和 Calico 》

  13. 《 k8s 中的调度器 – 亲和性、污点和容忍性 》

  14. 《 k8s 中对资源的监控 – 资源指标、Prometheus、弹性伸缩器 HPA 》

  15. 《 k8s 中的云原生应用管理利器 – Helm 》

  16. 《 理解 k8s 高可用,让你的集群稳如泰山 》     您当前所在位置



logo

虽然现在有一些部署工具,但是为了更好的理解 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

二、高可用部署规划

Master 节点的高可用规划

  • ETCD:至少 3 个,之间使用数字证书加密通信;每个节点和客户端通信时,要有双向数字证书认证;
  • ETCD-CA:专用于为每一个 ETCD 节点颁发证书;
  • kube-apiserver、controller-manager、scheduler:各一主一备即可;

整体架构

一共 3 个 Master 节点,2 个 Node 节点。部署顺序:

  1. 在 3 个 Master 节点中先部署好高可用的 ETCD;
  2. 在 3 个 Master 节点中部署 k8s 的三个 master 组件:apiServer、scheduler、controller-manager,配置为高可用状态;
  3. 最后把两个 Node 节点加入到这个集群;

与 etcd 最少需要 3 个节点不同,apiServer、controller-manager、scheduler 的数量可以是随意的,他们通过分布式锁的方式抢占,这里我们把他们高可用为 3 个。

实验架构如图:

diagram

准备二进制程序

这次不使用 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 仓库中,有 V2V3 版,要使用 V3 版的。

1
yum install etcd

2. 配置 ETCD

准备工作

注意:这一步要在每个 Master、Node 节点配置。

配置 Hosts 文件,还有防火墙、SELinux、时钟同步都要配置好,这里就不一一说明了:

1
2
3
4
5
192.168.50.11 master01 master01.monster.com etcd01 etcd01.monster.com
192.168.50.12 master02 master02.monster.com etcd02 etcd02.monster.com
192.168.50.13 master03 master03.monster.com etcd03 etcd03.monster.com
192.168.50.14 node01 node01.monster.com
192.168.50.15 node02 node02.monster.com
  • /var/lib/etcd:存放默认数据路径;
  • /etc/etcd/etcd.conf:变量配置文件,给 Unit Service 传递变量;

依次编辑三个 master 节点上 ETCD 的配置文件 /etc/etcd/etcd.conf 启用集群功能:

注意:在另外两个节点配置时,要使用节点自己的域名和 IP。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 这个端口用于各节点通信使用,要使用其他节点可以访问的地址,不可以使用域名
ETCD_LISTEN_PEER_URLS="http://192.168.50.11:2380"

# 向客户端提供服务的地址,不可以使用域名
ETCD_LISTEN_CLIENT_URLS="http://192.168.50.11:2379"

# 定义一个当前节点的名字
ETCE_NAME="etcd01.monster.com"

# 在集群内通告自己作为集群成员时,监听的地址是什么
ETCD_INITIAL_ADVERTISE_PEER_URLS="http://etcd01.monster.com:2380"

# 在集群内通告自己监听在哪个地址向客户端提供服务,可以使用主机名。
ETCD_ADVERTISE_CLIENT_URLS="http://etcd01.monster.com:2379"

# 指定集群内其他成员都有哪些
ETCD_INITIAL_CLUSTER="etcd01.monster.com=http://etcd01.monster.com:2380,etcd02.monster.com=http://etcd02.monster.com:2380,etcd03.monster.com=http://etcd03.monster.com:2380"

启动 etcd 服务

注意:这里目前还没有启用 TLS 加密通信,如果你计划开启加密通信,请先完成下面的 配置 ETCD 使用 TLS 加密传输 后再来启动 ETCD 服务。

当只有一个节点启动服务时会阻塞,因为不到法定票数,最少要启动 2 个节点的服务才行:

1
2
# 在 etcd01、etcd02、etcd03 分别启动服务
systemctl start etcd

验证安装

  • etcdctl 是 ETCD 的客户端工具;
    • --endpoints=‘’:指定要连接的 Server 地址,默认不指为 127.0.0.1:2379
1
2
3
4
5
-> # etcdctl --endpoints='http://192.168.50.11:2379' member list

3a4b781a9adff0f: name=etcd02 peerURLs=http://etcd02.monster.com:2380 clientURLs=http://etcd02.monster.com:2379 isLeader=false
84fabb98a766dd85: name=etcd03 peerURLs=http://etcd03.monster.com:2380 clientURLs=http://etcd03.monster.com:2379 isLeader=true # isLeader 为 true 的是当前 Leader 节点
fce1a68fe5552232: name=etcd01 peerURLs=http://etcd01.monster.com:2380 clientURLs=http://etcd01.monster.com:2379 isLeader=false

3. 配置 ETCD 使用 TLS 加密传输

没有计划使用 TLS 时可以跳过此步骤。

注意:以下操作在其中一台 Master 节点操作。

k8s 中的 TLS 需要三种证书:

  • peer 类型的证书:集群内各节点之间使用,既是客户端证书也是服务端证书;
  • Server 证书:Client 端验证 Server 端身份时使用的证书;
  • Client 证书:Server 端验证 Client 端身份时使用的证书。

生成证书

能够一次性生成对等证书、Server 证书和客户端证书的项目:https://github.com/iKubernetes/k8s-certs-generator

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
-> # git clone https://github.com/iKubernetes/k8s-certs-generator
-> # cd k8s-certs-generator
-> # chmod +x *.sh
-> # ./gencerts.sh etcd # gencerts.sh 这个脚本会根据你传递的参数,选择调用其他两个中的哪个脚本
Enter Domain Name [ilinux.io]: monster.com # 会提示让你输入域名,输入你的域名后缀即可
...
...

# 之后就会创建好证书,存放在当前目录下一个叫 etcd 的文件夹中
-> # ls ./etcd/pki
apiserver-etcd-client.crt ca.crt client.crt peer.crt server.crt
apiserver-etcd-client.key ca.key client.key peer.key server.key

# 把创建好的证书复制到所有节点中
> # scp -rp ./etcd/pki etcd01.monster.com:/etc/etcd/
> # scp -rp ./etcd/pki etcd02.monster.com:/etc/etcd/
> # scp -rp ./etcd/pki etcd03.monster.com:/etc/etcd/

启用加密传输

编辑三个 master 节点中的配置文件 /etc/etcd/etcd.conf,配置 #[Security] 字段中关于 TLS 的配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#[Security]

# 以下和面向客户端的通信有关,对 2379 端口进行加密
ETCD_CERT_FILE="/etc/etcd/pki/server.crt" # 指定 server 端证书
ETCD_KEY_FILE="/etc/etcd/pki/server.key" # 指定 server 端私钥
ETCD_CLIENT_CERT_AUTH="true" # 是否要验证客户端身份,强烈建议开启
ETCD_TRUSTED_CA_FILE="/etc/etcd/pki/ca.crt" # 指定验证客户端时信任的 CA 的证书
ETCD_AUTO_TLS="false" # 是否自动生成证书,这里使用自己制作证书,所以关掉

# 以下和集群内部通信有关,对 2380 端口进行加密
# 集群内部各节点使用 peer 类型的证书,既是客户端证书也是服务端证书
ETCD_PEER_CERT_FILE="/etc/etcd/pki/peer.crt" # 指定使用哪个对等证书
ETCD_PEER_KEY_FILE="/etc/etcd/pki/peer.key" # 指定使用哪个对等证书的私钥
ETCD_PEER_CLIENT_CERT_AUTH="true" # 是否启用双向验证身份,强烈建议开启
ETCD_PEER_TRUSTED_CA_FILE="/etc/etcd/pki/ca.crt" # 使用同一个 CA,指定信任的 CA 的证书
ETCD_PEER_AUTO_TLS="false" # 是否自动生成证书,这里使用自己制作证书,所以关掉

# 在 VIM 中把之前指定的所有 http 协议的地址改为 https
:%s/http/https/g

启动 ETCD 服务

在 etcd01、etcd02、etcd03 节点分别启动服务。

1
-> # systemctl start etcd

使用域名验证操作

1
2
3
4
5
6
7
8
9
10
# 使用 https 时需要同时指定证书和 CA 的文件
-> # etcdctl --endpoints='https://etcd01.monster.com:2379' \
--cert-file=/etc/etcd/pki/client.crt \
--key-file=/etc/etcd/pki/client.key \
--ca-file=/etc/etcd/pki/ca.crt cluster-health

member 4df21596627a59c9 is healthy: got healthy result from https://etcd03.monster.com:2379
member f3b503e57ac312f2 is healthy: got healthy result from https://etcd02.monster.com:2379
member f4cadd4dddb94d3f is healthy: got healthy result from https://etcd01.monster.com:2379
cluster is healthy

四、部署 Master 节点

注意:以下操作在之前生成过证书的 Master 节点上操作。

生成 k8s 的证书
还是使用刚才一键生成证书的脚本,生成 k8s 的证书,会创建专用的文件夹来存放各个节点的证书。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
-> # cd /root/k8s-certs-generator 
-> # ./gencerts.sh k8s # 这次选择创建 k8s 证书
Enter Domain Name [ilinux.io]: monster.com # 会提示让你输入域名,输入和刚才一样的域名后缀即可
Enter Kubernetes Cluster Name [kubernetes]: # 定义集群的名字
Enter the IP Address in default namespace
of the Kubernetes API Server[10.96.0.1]: # 定义 API Server 内部使用的地址,一般默认即可
Enter Master servers name[master01 master02 master03]: master01 master02 master03 # 填写你每个 Master 的主机名,不需要域名后缀,会自动补全
Generating CA key and self signed cert.
...
...

# 之后就会创建好证书,会为每个节点生成专用的文件夹来存放证书。
-> # cd ./kubernetes
-> # tree -d
.
├── CA
├── front-proxy
├── ingress
│   └── patches
├── kubelet
│   ├── auth
│   └── pki
├── node1
│   ├── auth
│   └── pki
├── node2
│   ├── auth
│   └── pki
└── node3
├── auth
└── pki

# 先在每个节点创建用于存放 kebernetes 配置文件的目录
> # ssh master01.monster.com "mkdir /etc/kubernetes"
> # ssh master02.monster.com "mkdir /etc/kubernetes"
> # ssh master03.monster.com "mkdir /etc/kubernetes"

# 再把刚才生成的证书目录下的所有文件,复制到对应的节点中
> # scp -rp ./master01/* master01.monster.com:/etc/kubernetes/
> # scp -rp ./master02/* master02.monster.com:/etc/kubernetes/
> # scp -rp ./master03/* master03.monster.com:/etc/kubernetes/

在刚才生成的证书里,用于 apiserver 访问 etcd 的证书和私钥文件 apiserver-etcd-client.crtapiserver-etcd-client.key,是脚本从之前创建 ETCD 证书时生成的同名文件复制过来的,不用在手动复制了。

部署 Server 端

注意:以下操作在所有 Master 节点进行。

把之前下载的 Server 端二进制压缩包上传并解压至 /usr/local/ 目录下:

1
2
3
4
5
6
7
-> # tar -zxvf kubernetes-server-linux-amd64.tar.gz -C /usr/local

-> # ls /usr/local/kubernetes/server/bin
apiextensions-apiserver kube-apiserver.tar kubectl kube-proxy.tar mounter
kubeadm kube-controller-manager kubelet kube-scheduler
kube-apiserver kube-controller-manager.docker_tag kube-proxy kube-scheduler.docker_tag
kube-apiserver.docker_tag kube-controller-manager.tar kube-proxy.docker_tag kube-scheduler.tar

在解压后的目录中应该可以看到 apiServer、controller-manager、scheduler 的主程序了。

配置文件

注意:以下操作在所有 Master 节点进行。

在这个项目下有 Master 节点需要用到的配置文件的模板:
https://github.com/iKubernetes/k8s-bin-inst

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
-> # tree k8s-bin-inst 
k8s-bin-inst
├── etcd
│   └── etcd.conf
├── images
│   ├── etcd-tls.png
│   └── k8s-tls.png
├── master
│   ├── etc
│   │   └── kubernetes
│   │   ├── apiserver
│   │   ├── config # Master 节点通用配置文件
│   │   ├── controller-manager
│   │   └── scheduler
│   └── unit-files
│   ├── kube-apiserver.service
│   ├── kube-controller-manager.service
│   └── kube-scheduler.service
├── nodes
│   ├── etc
│   │   └── kubernetes
│   │   ├── config
│   │   ├── kubelet
│   │   └── proxy
│   ├── unit-files
│   │   ├── kubelet.service
│   │   └── kube-proxy.service
│   └── var
│   └── lib
│   ├── kubelet
│   │   └── config.yaml
│   └── kube-proxy
│   └── config.yaml
└── README.md

把项目克隆下来,并把配置文件模板和 Unit 文件都复制到对应目录:

1
2
3
4
5
6
7
8
# 复制配置文件模板
-> # git clone https://github.com/iKubernetes/k8s-bin-inst.git
-> # cd ./k8s-bin-inst/master/etc/kubernetes
-> # cp * /etc/kubernetes/

# 复制 Unit 文件
-> # cd ../../unit-files
-> # cp * /usr/lib/systemd/system/

修改 apiServer 的配置文件 vim /etc/kubernetes/apiserver,找到 KUBE_ETCD_SERVERS= 字段,配置 etcd 的访问地址:

1
2
3
4
5
...
...
KUBE_ETCD_SERVERS="--etcd-servers=https://etcd01.monster.com:2379,https://etcd02.monster.com:2379,https://etcd03.monster.com:2379"
...
...

创建一个系统用户:

1
-> # useradd -r kube

创建 k8s 临时状态保存目录:

1
2
-> # mkdir /var/run/kubernetes
-> # chown kube.kube /var/run/kubernetes

启动 apiServer 服务:

1
2
-> # systemctl daemon-reload
-> # systemctl start kube-apiserver

配置 kubectl

1
2
3
4
5
6
7
8
9
10
11
# 为 kubectl 创建软连接,让 shell 可以搜索到命令
-> # ln -sv /usr/local/kubernetes/server/bin/kubectl /usr/bin/

# 查看 kubectl 的配置文件,一般保持默认即可
-> # kubectl config view --kubeconfig=/etc/kubernetes/auth/admin.conf

# 把配置文件复制到家目录下的 .kube 目录中,并重命名为 config
-> # mkdir ~/.kube
-> # cp /etc/kubernetes/auth/admin.conf ~/.kube/config

# 之后就可以直接使用 kubectl config view 命令了,而不需要再指定配置文件了

授权 Boot Strapper
之前用脚本创建证书时自动生成了一个 token.csc 文件,里面配置了一个用户,如下:

1
2
3
-> # cat /etc/kubernetes/token.csv 
fff446.b8fa4f3505d26c34,"system:bootstrapper",10001,"system:bootstrappers"
# 用户名 # 组名

实际上这个用户没有被授权允许做 Boot Strapper,需要手动为这个用户做一下授权:

注意:这一步只在一个 Master 节点操作即可。

1
2
3
4
5
6
# 创建一个 ClusterRoleBinding,并授予 node-bootstrappers 角色,这个角色是内键的用于初次拉起 Node 节点时使用的:
-> # kubectl create clusterrolebinding system:bootstrapper --user=system:bootstrapper --clusterrole=system:node-bootstrapper

# 查看是否绑定成功
-> # kubectl get clusterrolebinding | grep "bootstrapper"
system:bootstrapper 23s

接下来在所有 Master 节点启动 controller-manager、sheuduler 服务。

因为之前已经把配置文件模板复制到对应位置,如无特殊需求,直接启动 controller-manager 和 sheuduler 的服务即可:

1
2
-> # systemctl start kube-controller-manager
-> # systemctl start kube-scheduler

注意:Master 节点中默认的通用配置文件 /etc/kubernetes/config 中定义了日志级别为 debug,投入生产后一定记得把日志级别调高,否则日志量会巨大。

至此 Master 节点就部署好并正常启动了,下面把 Node 加入到这个集群中。

五、部署 Node 节点

把 Node 拉入集群是通过 Boot Strapper 的方式,并且第一次登录是基于 token 认证。加入集群后,两者之间就变为使用证书进行认证。

注意:两个 Node 节点也要能通过 Hosts 文件解析其他所有节点,还有防火墙、SELinux、时钟同步都要配置好,这里就不一一讲解了:

每个 Node 节点中都会运行三个守护进程:

  • kubelet
  • kube-proxy
  • docker

注意:以下操作在所有 Node 节点进行。

1. 部署 Docker

安装

1
2
3
4
5
6
7
8
9
# step 1: 安装必要的一些系统工具
sudo yum install -y yum-utils device-mapper-persistent-data lvm2

# Step 2: 添加软件源信息
sudo yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

# Step 3: 更新并安装 Docker-CE
sudo yum makecache fast
sudo yum -y install docker-ce

配置代理

k8s 中的 Pos 依赖一个基础镜像叫 pause,但是因为众所周知的原因我们无法访问 Google 的仓库,所以下面就配置代理用于从 Google 仓库下载镜像使用。

编辑 docker 的 UNIT 文件 vim /usr/lib/systemd/system/docker.service,改成如下:

1
2
3
4
5
6
...
Environment=HTTPS_PROXY=http://www.ik8s.io:10070 # 定义代理地址
Environment=NO_PROXY=127.0.0.0/8,192.168.0.0/16 # 把本机和本地 IP 定义不代理的地址
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock # 此行原本就有,保持不动
ExecStartPost=/usr/sbin/iptables -P FORWARD ACCEPT # 把 FORWORD 链的默认策略改为放行
...

启动 Docker 服务

1
2
-> # systemctl daemon-reload
-> # systemctl start docker

2. 安装配置 kubelet 和 kube-proxy

安装程序

把之前下载的 Node 端二进制压缩包上传并解压至 /usr/local/ 目录下:

1
2
3
4
-> # tar -zxvf kubernetes-node-linux-amd64.tar.gz -C /usr/local

-> # ls /usr/local/kubernetes/node/bin/
kubeadm kubectl kubelet kube-proxy

在解压后的目录中应该可以看到 kubelet、kube-proxy 等主程序了。

kubelet 和 kube-proxy 的配置文件

在 v1.12 版以后,kubelet 和 Kube-proxy 支持从本地的配置文件加载参数,和在命令行中指定参数效果一样,大大简化了部署 Node 时配置的复杂度,配置文件存储为 yaml 或 json 格式,可通过 --config 选项加载此配置文件,一般存放在 /var/lib/ 目录下,有 kubeletkube-proxy 目录专门用来存放各自的配置文件:

注意:在 Master 节点上完成下列操作。

部署 master 节点时,曾经克隆了一个配置文件的模板项目,去 master 节点上把关于 kubelet 和 kube-proxy 的配置文件和证书复制到所有 Node 节点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 复制配置文件
-> # ssh master01.monster.com
-> # cd /root/k8s-bin-inst/nodes/etc
-> # scp -rp ./kubernetes/ node01:/etc/
-> # scp -rp ./kubernetes/ node02:/etc/

-> # cd ../var/lib/
-> # scp -rp * node01:/var/lib/
-> # scp -rp * node02:/var/lib/

# 把之前生成的证书文件复制到 Node 节点,用于和 apiServer 通信使用
-> # cd /root/k8s-certs-generator/kubernetes/kubelet
-> # scp -r * node01:/etc/kubernetes
-> # scp -r * node02:/etc/kubernetes

# 复制 kubelet 的 UNIT file 文件
-> # cd /root/k8s-bin-inst/nodes/unit-files/
-> # scp * node01:/usr/lib/systemd/system/
-> # scp * node02:/usr/lib/systemd/system/

创建工作目录,否则会报错:

1
2
-> # mkdir -pv /etc/kubernetes/manifests
mkdir: created directory ‘/etc/kubernetes/manifests’

3. 安装 CNI 网络插件,在所有 Node 节点操作

项目地址:https://github.com/containernetworking/plugins/releases

在 releases 页面中复制下载连接,之后去 Node 节点中下载:

1
2
3
-> # wget https://github.com/containernetworking/plugins/releases/download/v0.8.3/cni-plugins-linux-amd64-v0.8.3.tgz
-> # mkdir -p /opt/cni/bin
-> # tar -zxvf cni-plugins-linux-amd64-v0.8.3.tgz -C /opt/cni/bin

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
2
-> # grep "api" /etc/hosts
192.168.50.11 master01 master01.monster.com etcd01 etcd01.monster.com kubernetes-api.monster.com

5. 启动 kubectl 服务

在每个 Node 节点启动 kubectl 服务

1
2
-> # systemctl daemon-reload
-> # systemctl start kubectl

kubectl 启动后,就会自动向 apiServer 提交一个证书签署请求。

如 kubelet 启动报错

如果 kubelet 启动报错,并在日志内发现如下异常日志:

1
2
-> # journalctl -u kubelet
Dec 13 16:05:38 node01 kubelet: F1213 16:05:38.955900 27033 server.go:156] unknown flag: --allow-privileged

网上搜索后也确实在官网上搜到了相关的 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
2
# 注释掉这一行
# KUBE_ALLOW_PRIV="--allow-privileged=true"

6. 签署 Node 的证书

在 Master 节点上,查看收到的证书签署请求:

1
2
3
4
5
-> # kubectl get csr
NAME AGE REQUESTOR CONDITION
csr-6z4dm 25s system:bootstrapper Pending
csr-c5l2m 94s system:bootstrapper Pending
csr-cb7r7 76s system:bootstrapper Pending

进行证书签署,如有多个相同的请求,签署最新的即可:

1
2
-> # kubectl certificate approve csr-6z4dm
certificatesigningrequest.certificates.k8s.io/csr-6z4dm approved

签署后,在 master 节点中应该可以看到 Node 节点已经加入,只是状态还是 NotReady

1
2
3
-> # kubectl get nodes 
NAME STATUS ROLES AGE VERSION
node01.monster.com NotReady <none> 5m47s v1.17.0

7. 启用 ipvs 内核模块

/var/lib/kube-proxy/config.yaml 配置文件中定义了 Pod 的网段为 10.244.0.0/24,网络模式为 ipvs

创建载入内核模块的脚本文件 vim /etc/sysconfig/modules/ipvs.modules,设定自动载入内核模块。文件内容如下:

1
2
3
4
5
6
7
8
#!/bin/bash
ipvs_mods_dir="/usr/lib/modules/$(uname -r)/kernel/net/netfilter/ipvs"
for i in $(ls $ipvs_mods_dir | grep -o "^[^.]*"); do
/sbin/modinfo -F filename $i &> /dev/null
if [ $? -eq 0 ]; then
/sbin/modprobe $i
fi
done

给予脚本执行权限并执行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
-> # chmod +x /etc/sysconfig/modules/ipvs.modules
-> # /etc/sysconfig/modules/ipvs.modules
-> # lsmod | grep ^ip_vs
ip_vs_wrr 12697 0
ip_vs_wlc 12519 0
ip_vs_sh 12688 0
ip_vs_sed 12519 0
ip_vs_rr 12600 0
ip_vs_pe_sip 12740 0
ip_vs_nq 12516 0
ip_vs_lc 12516 0
ip_vs_lblcr 12922 0
ip_vs_lblc 12819 0
ip_vs_ftp 13079 0
ip_vs_dh 12688 0
ip_vs 145497 24 ip_vs_dh,ip_vs_lc,ip_vs_nq,ip_vs_rr,ip_vs_sh,ip_vs_ftp,ip_vs_sed,ip_vs_wlc,ip_vs_wrr,ip_vs_pe_sip,ip_vs_lblcr,ip_vs_lblc

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
2
3
-> # kubectl get nodes
NAME STATUS ROLES AGE VERSION
node01.monster.com Ready <none> 99m v1.17.0

如果无法访问 grc 仓库

如果 flannel 的 Pod 卡在无法获取镜像,使用以下办法曲线救国:从其他仓库地方下载到本地,然后更改 tag,其他镜像也可以使用此方法,把要下载的镜像改成需要的名字即可:

1
2
3
4
5
# 下载 pause:3.1 镜像,如下载其他镜像该名字即可
docker pull mirrorgooglecontainers/pause:3.1

# 改 tag:flannel 的 yaml 文件中定义了要找指定 tag 的镜像,如果不改 tag 会认为本地没有镜像
docker tag mirrorgooglecontainers/pause:3.1 k8s.gcr.io/pause:3.1

六、配置 CoreDNS

CoreDNS 官方用于在 kubernetes 中部署的项目地址:https://github.com/coredns/deployment/tree/master/kubernetes

1
2
3
4
-> # mkdir coredns && cd coredns
-> # wget https://raw.githubusercontent.com/coredns/deployment/master/kubernetes/coredns.yaml.sed
-> # wget https://raw.githubusercontent.com/coredns/deployment/master/kubernetes/deploy.sh
-> # bash deploy.sh -i 10.96.0.10 -r "10.96.0.0/12" -s -t coredns.yaml.sed | kubectl apply -f -