手把手教你二进制安装生产环境 K8s 多 master 节点高可用集群详细图文教程

news2024/11/26 6:12:35

目录

一、集群环境准备

1.1 kubeadm 和二进制安装 k8s 适用场景分析

1.2 多 master 节点高可用架构图

二、基础环境配置(以下操作所有节点都得执⾏)

2.1 初步的环境初始化

2.2  关闭交换分区 swap 提升性能

2.3 修改机器内核参数

2.4 配置阿里云的 repo 源

2.5 配置安装 k8s 组件需要的阿里云的 repo 源

2.6 主机系统优化

2.7 开启 ipvs

2.8 清除邮件提示消息

2.9 升级 Linux 内核

2.9.1 安装 5.x 内核

2.9.2 切换新内核启动

2.10 配置免密登录(在 k8s-master1 上操作 )

三、安装 Docker 和容器运行时 containerd(所有节点)

3.1 安装 docker-ce

3.2 配置 docker 镜像加速器和驱动

四、搭建 etcd 集群(在 master1 上操作)

4.1 配置 etcd 工作目录

4.2 安装签发证书工具 cfssl

4.3 配置 CA 证书

4.3.1 创建 ca 证书请求文件

4.3.2 配置 ca 证书策略

4.4 创建 etcd 证书

4.5 部署 etcd 集群

4.5.1 下载 etcd 软件包

4.5.2 安装 etcd 软件

4.5.3 分发 etcd 软件

4.5.4 创建配置文件

4.5.5 创建服务配置文件

4.5.6 同步 etcd 配置到集群其它 master 节点

4.5.7 启动 etcd 集群(三个 master 节点都需要操作)

4.5.8 验证集群状态

五、Kubernetes 组件部署

5.1 Kubernetes 软件包下载

5.2 Kubernetes 软件包安装

5.3 Kubernetes 软件分发

5.4 在集群节点上创建目录(所有节点)

5.5 部署 api-server

5.5.1 创建 apiserver 证书请求文件

5.5.2 生成 apiserver 证书及 token 文件

5.5.3 创建 apiserver 服务配置文件

5.5.4 创建 apiserver 服务管理配置文件 

5.5.5 同步文件到集群 master 节点 

5.5.6 启动 apiserver 服务

5.6 部署 kubectl

5.6.1 创建 kubectl 证书请求文件

5.6.2 生成证书文件

5.6.3 复制文件到指定目录

5.6.4 生成 kubeconfig 配置文件

5.6.5 准备 kubectl 配置文件并进行角色绑定

5.6.6 查看集群状态

5.6.7 同步 kubectl 配置文件到集群其它 master 节点

5.6.8 配置 kubectl 命令补全(可选)

5.7 部署 kube-controller-manager

5.7.1 创建 kube-controller-manager 证书请求文件 

5.7.2 创建 kube-controller-manager 证书文件

5.7.3 创建 kube-controller-manager 的 kube-controller-manager.kubeconfig

5.7.4 创建 kube-controller-manager 配置文件 

5.7.5 创建服务启动文件

5.7.6 同步文件到集群 master 节点

5.7.7 启动服务(在三个 master 执行)

5.8 部署 kube-scheduler

5.8.1 创建 kube-scheduler 证书请求文件

5.8.2 生成 kube-scheduler 证书 

5.8.3 创建 kube-scheduler 的 kubeconfig

5.8.4 创建服务配置文件

5.8.5 创建服务启动配置文件 

5.8.6 同步文件至集群 master 节点

5.8.7 启动服务

六、工作节点(worker node)部署

6.1 部署 kubelet(在 k8s-master1 上操作)

6.1.1 创建 kubelet-bootstrap.kubeconfig

6.2.2 创建 kubelet 配置文件

6.1.3 创建 kubelet 服务启动管理文件

6.1.4 同步文件到集群节点

6.1.5 创建目录及启动服务(所有节点执行)

6.1.6 验证集群状态

6.2 部署 kube-proxy

6.2.1 创建 kube-proxy 证书请求文件

6.2.2 生成证书

6.2.3 创建 kubeconfig 文件

6.2.4 创建服务配置文件

6.2.5 创建服务启动管理文件

6.2.6 同步文件到集群节点

6.2.7 服务启动(所有节点)

6.3 网络组件部署 Calico

6.3.1 下载 calico.yaml

6.3.2 修改 calico.yaml 里的 pod 网段

6.3.3 提前下载好所需的镜像(可省略,直接执行下一步)

6.3.4 安装 calico 网络

6.3.5 验证结果

6.4 部署 CoreDNS

6.4.1 下载 coredns.yaml

6.4.2 修改 coredns.yaml

6.4.3 安装 coredns

6.4.5 测试 coredns 域名解析功能

七、安装 keepalived+nginx 实现 k8s apiserver 高可用

7.1 keepalived+nginx 高可用介绍

7.1.1 什么是高可用?

7.1.2 解决的问题?

7.1.3 双机热备方案

7.1.4 keepalived 是什么?

7.1.5 故障转移机制

7.2 安装 nginx 与 keeplived(所有 master 节点)

7.3 nginx 配置(三个 master 节点的 nginx 配置一样)

7.4 配置 KeepAlived(所有 Master 节点)

7.4.1 Master1

7.4.2 Master2

7.4.3 Master3

7.5 健康检查脚本(三个 master 执行)

7.6 启动 nginx 和 keepalived(所有 master 节点)

7.7 查看 vip 是否绑定成功

7.8 测试 keepalived


本篇文章所用到的资料文件下载地址:https://download.csdn.net/download/weixin_46560589/87450425

一、集群环境准备

  • 操作系统:CentOS 7.6
  • 最低配置: 2Gib 内存 / 2vCPU / 30G 硬盘
  • 虚拟机网络:NAT 模式
  • podSubnet( pod 网段):10.0.0.0/16
  • serviceSubnet(service 网段):10.255.0.0/16
  • k8s 版本:1.25.x
K8s 集群角色IP主机名安装的组件
控制节点(master)192.168.78.150k8s-master1kube-apiserver、kube-controller-manager、kube-scheduler、etcd、kubelet、kube-proxy、docker、KeepAlive、nginx
控制节点(master)192.168.78.151k8s-master2kube-apiserver、kube-controller-manager、kube-scheduler、etcd、kubelet、kube-proxy、docker、KeepAlive、nginx
控制节点(master)192.168.78.152k8s-master3kube-apiserver、kube-controller-manager、kube-scheduler、etcd、kubelet、kube-proxy、docker、KeepAlive、nginx
工作节点(node)192.168.78.153k8s-node1kubelet、kube-proxy、docker、calico、coredns
工作节点(node)192.168.78.154k8s-node2kubelet、kube-proxy、docker、calico、coredns
虚拟 IP(VIP)192.168.78.155备注:在 master 节点生成,不需要另外新开一台主机

注意:VIP(虚拟 IP)不要和公司内⽹ IP 重复,⾸先去 ping ⼀下,不通才可⽤。VIP 需要和主机在同⼀个局域⽹内!

1.1 kubeadm 和二进制安装 k8s 适用场景分析

        kubeadm 是官方提供的开源工具,是一个开源项目,用于快速搭建 kubernetes 集群,目前是比较方便和推荐使用的。kubeadm init 以及 kubeadm join 这两个命令可以快速创建 kubernetes 集群。Kubeadm 初始化 k8s,所有的组件都是以 pod 形式运行的,具备故障自恢复能力。

        kubeadm 是工具,可以快速搭建集群,也就是相当于用程序脚本帮我们装好了集群,属于自动部署,简化部署操作,证书、组件资源清单文件都是自动创建的,自动部署屏蔽了很多细节,使得对各个模块感知很少,如果对 k8s 架构组件理解不深的话,遇到问题比较难排查。

kubeadm 适合需要经常部署 k8s,或者对自动化要求比较高的场景下使用。

二进制:在官网下载相关组件的二进制包,如果手动安装,对 kubernetes 理解也会更全面。

        Kubeadm 和二进制都适合生产环境,在生产环境运行都很稳定,具体如何选择,可以根据实际项目进行评估。

kubeadm 安装 k8s:【Kubernetes 企业项目实战】01、使用 kubeadm 安装 K8s-v1.23 高可用集群_Stars.Sky的博客-CSDN博客

1.2 多 master 节点高可用架构图

二、基础环境配置(以下操作所有节点都得执⾏)

2.1 初步的环境初始化

可以查看我的这篇文章所有步骤都执行一遍:CentOS 7 初始化系统_centos7初始化_Stars.Sky的博客-CSDN博客

2.2  关闭交换分区 swap 提升性能

# 临时关闭
swapoff -a
 
# 永久关闭:注释 swap 挂载,给 swap 这行开头加一下 "#" 注释
sed -ri 's/.*swap.*/#&/' /etc/fstab
 
# 查看效果
free -m

为什么要关闭 swap 交换分区?
        Swap 交换分区,如果机器内存不够,会使用 swap 分区,但是 swap 分区的性能较低,k8s 设计的时候为了能提升性能,默认是不允许使用交换分区的。Kubeadm 初始化的时候会检测 swap 是否关闭,如果没关闭,那就初始化失败。如果不想要关闭交换分区,安装k8s 的时候可以指定 --ignore-preflight-errors=Swap 来解决。

2.3 修改机器内核参数

# 加载 br_netfilter 模块
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF
 
sudo modprobe overlay
sudo modprobe br_netfilter

# 验证模块是否加载成功:
lsmod | grep br_netfilter

# 修改内核参数 
echo "modprobe br_netfilter" >> /etc/profile

cat > /etc/sysctl.d/k8s.conf <<EOF
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
EOF

# 使刚才修改的内核参数生效 
sysctl -p /etc/sysctl.d/k8s.conf

# 设置开机自启
systemctl enable --now systemd-modules-load.service

问题 1 :sysctl 是做什么的?
        在运行时配置内核参数 -p 从指定的文件加载系统参数,如不指定即从 /etc/sysctl.conf 中加载。

问题 2 :为什么要执行 modprobe br_netfilter ?
修改 /etc/sysctl.d/k8s.conf 文件,增加如下三行参数:
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1

sysctl -p /etc/sysctl.d/k8s.conf 出现报错:
sysctl: cannot stat /proc/sys/net/bridge/bridge-nf-call-ip6tables: No such file or directory
sysctl: cannot stat /proc/sys/net/bridge/bridge-nf-call-iptables: No such file or directory

解决方法:
modprobe br_netfilter

问题 3 :为什么开启 net.bridge.bridge-nf-call-iptables 内核参数?
在 centos 下安装 docker,执行 docker info 出现如下警告:
WARNING: bridge-nf-call-iptables is disabled
WARNING: bridge-nf-call-ip6tables is disabled
解决办法:
vim /etc/sysctl.d/k8s.conf

net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1

问题 4 :为什么要开启 net.ipv4.ip_forward = 1 参数?
kubeadm 初始化 k8s 如果报错:

就表示没有开启 ip_forward,需要开启。

net.ipv4.ip_forward 是数据包转发:
        出于安全考虑,Linux 系统默认是禁止数据包转发的。所谓转发即当主机拥有多于一块的网卡时,其中一块收到数据包,根据数据包的目的 ip 地址将数据包发往本机另一块网卡,该网卡根据路由表继续发送数据包。这通常是路由器所要实现的功能。
        要让 Linux 系统具有路由转发功能,需要配置一个 Linux 的内核参数net.ipv4.ip_forward。这个参数指定了 Linux 系统当前对路由转发功能的支持情况;其值为 0时表示禁止进行 IP 转发;如果是 1,则说明 IP 转发功能已经打开。

2.4 配置阿里云的 repo 源

配置国内安装 docker 和 containerd 的阿里云的 repo 源:

yum install yum-utils -y
 
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

2.5 配置安装 k8s 组件需要的阿里云的 repo 源

cat > /etc/yum.repos.d/kubernetes.repo <<EOF
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/
enabled=1
gpgcheck=0
EOF

2.6 主机系统优化

# limit 优化
ulimit -SHn 65535

cat <<EOF >> /etc/security/limits.conf
* soft nofile 655360
* hard nofile 131072
* soft nproc 655350
* hard nproc 655350
* soft memlock unlimited
* hard memlock unlimited
EOF

2.7 开启 ipvs

不开启 ipvs 将会使用 iptables 进行数据包转发,效率低,所以官网推荐需要开通 ipvs:

# 编写 ipvs 脚本
vim /etc/sysconfig/modules/ipvs.modules
#!/bin/bash
ipvs_modules="ip_vs ip_vs_lc ip_vs_wlc ip_vs_rr ip_vs_wrr ip_vs_lblc ip_vs_lblcr ip_vs_dh ip_vs_sh ip_vs_nq ip_vs_sed ip_vs_ftp nf_conntrack"
for kernel_module in ${ipvs_modules}; do
 /sbin/modinfo -F filename ${kernel_module} > /dev/null 2>&1
 if [ 0 -eq 0 ]; then
 /sbin/modprobe ${kernel_module}
 fi
done
 
# 执行脚本
chmod 755 /etc/sysconfig/modules/ipvs.modules && bash /etc/sysconfig/modules/ipvs.modules && lsmod | grep ip_vs

问题 1 :ipvs 是什么?

        ipvs (IP Virtual Server) 实现了传输层负载均衡,也就是我们常说的 4 层 LAN交换,作为 Linux 内核的一部分。ipvs 运行在主机上,在真实服务器集群前充当负载均衡器。ipvs 可以将基于 TCP 和 UDP 的服务请求转发到真实服务器上,并使真实服务器的服务在单个 IP 地址上显示为虚拟服务。

问题 2 :ipvs 和 iptable 对比分析

        kube-proxy 支持 iptables 和 ipvs 两种模式, 在 kubernetes v1.8 中引入了 ipvs 模式,在 v1.9 中处于 beta 阶段,在 v1.11 中已经正式可用了。iptables 模式在 v1.1 中就添加支持了,从 v1.2 版本开始 iptables 就是 kube-proxy 默认的操作模式,ipvs 和 iptables 都是基于netfilter 的,但是 ipvs 采用的是 hash 表,因此当 service 数量达到一定规模时,hash 查表的速度优势就会显现出来,从而提高 service 的服务性能。那么 ipvs 模式和 iptables 模式之间有哪些差异呢?

  •     ipvs 为大型集群提供了更好的可扩展性和性能

  •     ipvs 支持比 iptables 更复杂的复制均衡算法(最小负载、最少连接、加权等等)

  •     ipvs 支持服务器健康检查和连接重试等功能

2.8 清除邮件提示消息

# 清除 "您在 /var/spool/mail/root 中有新邮件"信息
echo "unset MAILCHECK" >> /etc/profile

source /etc/profile

# 清空邮箱数据站空间
cat /dev/null > /var/spool/mail/root

2.9 升级 Linux 内核

安装 kubernetes 1.24及以上版本,Linux Kernel 需要在 5.x 以上的版本才可满足需求。

ELRepo 官网:ELRepo | HomePage

2.9.1 安装 5.x 内核

#1. 查看内核版本
[root@k8s-master1 ~]# uname -r
3.10.0-957.el7.x86_64

#2. 查看操作系统版本
[root@k8s-master1 ~]# cat /etc/redhat-release
CentOS Linux release 7.6.1810 (Core) 

#3. 更新软件源,导入 ElRepo 仓库公钥
rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org

# ElRepo 是一个分发企业版 Linux 内核的社区仓库,在系统中导入 ElRepo 仓库的公钥,后续将从这个仓库中获取升级内核相关的资源。

#4. 安装 ELRepo
yum install https://www.elrepo.org/elrepo-release-7.el7.elrepo.noarch.rpm -y

#5. 查看可用的稳定版镜像,kernel-ml 为长期稳定版本,lt 为长期维护版本
yum --disablerepo="*" --enablerepo="elrepo-kernel" list available

#6. 安装最新版内核
yum --enablerepo=elrepo-kernel install kernel-ml

# 此次安装最新长期支持版内核(推荐)
yum --enablerepo=elrepo-kernel install -y kernel-lt

#7. 查看当前系统已安装内核
[root@k8s-master1 ~]# awk -F\' '$1=="menuentry " {print i++ " : " $2}' /etc/grub2.cfg
0 : CentOS Linux (5.4.231-1.el7.elrepo.x86_64) 7 (Core)
1 : CentOS Linux (3.10.0-957.el7.x86_64) 7 (Core)
2 : CentOS Linux (0-rescue-89c97f8441be4a6588d371b35bbb6a48) 7 (Core)

2.9.2 切换新内核启动

        修改 /etc/default/grub 文件,设置 GRUB_DEFAULT 的值为 2.3.2 中输出的最新版内核对应的序号。CentOS Linux (5.4.231-1.el7.elrepo.x86_64) 7 (Core) 前边的序号是 0,所以设置GRUB_DEFAULT = 0 。

vim /etc/default/grub 
GRUB_TIMEOUT=5
GRUB_DISTRIBUTOR="$(sed 's, release .*$,,g' /etc/system-release)"
GRUB_DEFAULT=0        # 只修改此次
GRUB_DISABLE_SUBMENU=true
GRUB_TERMINAL_OUTPUT="console"
GRUB_CMDLINE_LINUX="rd.lvm.lv=centos/root rd.lvm.lv=centos/swap rhgb quiet"
GRUB_DISABLE_RECOVERY="true"

# 重新配置内核参数
grub2-mkconfig -o /boot/grub2/grub.cfg

# 重启系统
reboot

# 再次查看系统内核是否为最新版本
[root@k8s-master1 ~]# uname -r
5.4.231-1.el7.elrepo.x86_64

2.10 配置免密登录(在 k8s-master1 上操作 )

        Master1 节点免密钥登录其他节点,安装过程中⽣成配置⽂件和证书均在 Master1 上操作,集群管理也在 Master01上操作。密钥配置如下:

# ⼀直回⻋就⾏
ssh-keygen -t rsa

# 等待一会,然后输入 yes,再输入各主机的密码即可
for i in k8s-master1 k8s-master2 k8s-master3 k8s-node1 k8s-node2;do ssh-copy-id -i .ssh/id_rsa.pub $i;done

三、安装 Docker 和容器运行时 containerd所有节点

docker 的安装可以看我这篇文章更详细:【云原生 | Docker 基础篇】02、CentOS 7 安装 Docker 详细图文教程_Stars.Sky的博客-CSDN博客_centos7安装docker

3.1 安装 docker-ce

yum -y install docker-ce docker-ce-cli containerd.io docker-compose-plugin

# 设置 Docker 开机自启并启动
systemctl enable docker.service --now

#1. 接下来生成 containerd 的配置文件:
mkdir -p /etc/containerd
containerd config default > /etc/containerd/config.toml
 
# 修改配置文件:
vim /etc/containerd/config.toml
把 SystemdCgroup = false 修改成 SystemdCgroup = true
把 sandbox_image = "k8s.gcr.io/pause:3.6" 修改成 sandbox_image="registry.aliyuncs.com/google_containers/pause:3.7"
 
# 配置 containerd 开机启动,并启动 containerd
systemctl enable containerd --now
 
#2. 使用 crictl 对 Kubernetes 节点进行调试
cat > /etc/crictl.yaml <<EOF
runtime-endpoint: unix:///run/containerd/containerd.sock
image-endpoint: unix:///run/containerd/containerd.sock
timeout: 10
debug: false
EOF
 
systemctl restart containerd
 
#3. 配置 containerd 镜像加速器
vim /etc/containerd/config.toml
# 找到 config_path = "",修改成如下目录:
config_path = "/etc/containerd/certs.d"
# 保存退出 wq
 
mkdir /etc/containerd/certs.d/docker.io/ -p

vim /etc/containerd/certs.d/docker.io/hosts.toml
# 写入如下内容:
[host."https://vh3bm52y.mirror.aliyuncs.com",host."https://registry.docker-cn.com"]
capabilities = ["pull"]

# 重启containerd:
systemctl restart containerd

3.2 配置 docker 镜像加速器和驱动

docker 镜像加速器可以看我这篇文章更详细:【云原生 | Docker 基础篇】03、Docker 阿里云镜像加速器_Stars.Sky的博客-CSDN博客_docker 镜像加速

# 修改 docker 文件驱动为 systemd,默认为 cgroupfs,kubelet 默认使用 systemd,两者必须一致才可
mkdir -p /etc/docker

vim /etc/docker/daemon.json
{
 "registry-mirrors":["https://hlfcd01t.mirror.aliyuncs.com","https://registry.docker-cn.com","https://docker.mirrors.ustc.edu.cn","https://dockerhub.azk8s.cn","http://hub-mirror.c.163.com"],
  "exec-opts": ["native.cgroupdriver=systemd"]
}

# 重新加载配置文件并重启 docker 
systemctl daemon-reload  && systemctl restart docker

四、搭建 etcd 集群(在 master1 上操作)

        etcd 是一个高可用的键值数据库,存储 k8s 的资源状态信息和网络信息的,etcd 中的数据变更是通过 api server 进行的。

4.1 配置 etcd 工作目录

# 创建 etcd 配置文件、证书文件、数据存放目录(三个 master 节点上都创建以下目录)
mkdir -p /etc/etcd
mkdir -p /etc/etcd/ssl
mkdir -p /var/lib/etcd/default.etcd

4.2 安装签发证书工具 cfssl

cfssl GitHub下载官网:Releases · cloudflare/cfssl · GitHub

cfssl 是使用 go 编写,由 CloudFlare 开源的一款 PKI/TLS 工具。主要程序有:

  • cfssl 是 CFSSL 的命令行工具;
  • cfssljson 用来从 cfssl 程序获取 JSON 输出,并将证书、密钥、CSR 和 bundle 写入文件中。
[root@k8s-master1 ~]# mkdir -p /data/work
[root@k8s-master1 ~]# cd /data/work/

# 如果在虚机上下载不了,就复制链接到浏览器上下载再上传
[root@k8s-master1 work]# wget https://pkg.cfssl.org/R1.2/cfssl_linux-amd64
[root@k8s-master1 work]# wget https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64
[root@k8s-master1 work]# wget https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64
[root@k8s-master1 work]# ls
cfssl-certinfo_linux-amd64  cfssljson_linux-amd64  cfssl_linux-amd64

# 添加可执行权限
[root@k8s-master1 work]# chmod +x cfssl*
[root@k8s-master1 work]# mv cfssl_linux-amd64 /usr/local/bin/cfssl
[root@k8s-master1 work]# mv cfssljson_linux-amd64 /usr/local/bin/cfssljson
[root@k8s-master1 work]# mv cfssl-certinfo_linux-amd64 /usr/local/bin/cfssl-certinfo

# 验证
[root@k8s-master1 work]# cfssl version
Version: 1.2.0
Revision: dev
Runtime: go1.6

4.3 配置 CA 证书

4.3.1 创建 ca 证书请求文件

[root@k8s-master1 work]# vim ca-csr.json
{
  "CN": "kubernetes",
  "key": {
      "algo": "rsa",
      "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "Hubei",
      "L": "Wuhan",
      "O": "k8s",
      "OU": "system"
    }
  ],
  "ca": {
          "expiry": "87600h"
  }
}

# 创建 ca 证书
[root@k8s-master1 work]# cfssl gencert -initca ca-csr.json | cfssljson -bare ca

注:

  • CN:Common Name(公用名称),kube-apiserver 从证书中提取该字段作为请求的用户名 (User Name);浏览器使用该字段验证网站是否合法;对于 SSL 证书,一般为网站域名;而对于代码签名证书则为申请单位名称;而对于客户端证书则为证书申请者的姓名。

  • O:Organization(单位名称),kube-apiserver 从证书中提取该字段作为请求用户所属的组 (Group);对于 SSL 证书,一般为网站域名;而对于代码签名证书则为申请单位名称;而对于客户端单位证书则为证书申请者所在单位名称。

  • L 字段:所在城市。

  • S 字段:所在省份。

  • C 字段:只能是国家字母缩写,如中国:CN

4.3.2 配置 ca 证书策略

[root@k8s-master1 work]# vim ca-config.json
{
  "signing": {
      "default": {
          "expiry": "87600h"
        },
      "profiles": {
          "kubernetes": {
              "usages": [
                  "signing",
                  "key encipherment",
                  "server auth",
                  "client auth"
              ],
              "expiry": "87600h"
          }
      }
  }
}
  • server auth 表示 client 可以对使用该 ca 对 server 提供的证书进行验证。
  • client auth 表示 server 可以使用该 ca 对 client 提供的证书进行验证。

4.4 创建 etcd 证书

配置 etcd 证书请求,hosts 的 ip 改为自己 etcd 所在 master 节点的 IP 和虚拟 IP:

# 配置 etcd 请求文件
[root@k8s-master1 work]# vim etcd-csr.json
{
  "CN": "etcd",
  "hosts": [
    "127.0.0.1",
    "192.168.78.150",
    "192.168.78.151",
    "192.168.78.152",
    "192.168.78.155"
  ],
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [{
    "C": "CN",
    "ST": "Hubei",
    "L": "Wuhan",
    "O": "k8s",
    "OU": "system"
  }]
}

# 上述文件 hosts 字段中 IP 为所有 etcd 节点的集群内部通信IP,可以预留几个 ip,方便日后做扩容用

# 生成 etcd 证书
[root@k8s-master1 work]# cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes etcd-csr.json | cfssljson -bare etcd

[root@k8s-master1 work]# ls
ca-config.json  ca.csr  ca-csr.json  ca-key.pem  ca.pem  etcd.csr  etcd-csr.json  etcd-key.pem  etcd.pem

4.5 部署 etcd 集群

4.5.1 下载 etcd 软件包

[root@k8s-master1 work]# wget https://github.com/etcd-io/etcd/releases/download/v3.5.2/etcd-v3.5.2-linux-amd64.tar.gz

4.5.2 安装 etcd 软件

[root@k8s-master1 work]# tar -zxvf etcd-v3.5.2-linux-amd64.tar.gz 
[root@k8s-master1 work]# cp -p etcd-v3.5.2-linux-amd64/etcd* /usr/local/bin/

4.5.3 分发 etcd 软件

[root@k8s-master1 work]# scp etcd-v3.5.2-linux-amd64/etcd* k8s-master2:/usr/local/bin/
[root@k8s-master1 work]# scp etcd-v3.5.2-linux-amd64/etcd* k8s-master3:/usr/local/bin/

4.5.4 创建配置文件

[root@k8s-master1 work]# cat > /etc/etcd/etcd.conf <<"EOF"
#[Member]
ETCD_NAME="etcd1"
ETCD_DATA_DIR="/var/lib/etcd/default.etcd"
ETCD_LISTEN_PEER_URLS="https://192.168.78.150:2380"
ETCD_LISTEN_CLIENT_URLS="https://192.168.78.150:2379,http://127.0.0.1:2379"

#[Clustering]
ETCD_INITIAL_ADVERTISE_PEER_URLS="https://192.168.78.150:2380"
ETCD_ADVERTISE_CLIENT_URLS="https://192.168.78.150:2379"
ETCD_INITIAL_CLUSTER="etcd1=https://192.168.78.150:2380,etcd2=https://192.168.78.151:2380,etcd3=https://192.168.78.152:2380"
ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"
ETCD_INITIAL_CLUSTER_STATE="new"
EOF

说明:

  • ETCD_NAME:节点名称,集群中唯一

  • ETCD_DATA_DIR:数据目录

  • ETCD_LISTEN_PEER_URLS:集群通信监听地址

  • ETCD_LISTEN_CLIENT_URLS:客户端访问监听地址

  • ETCD_INITIAL_ADVERTISE_PEER_URLS:集群通告地址

  • ETCD_ADVERTISE_CLIENT_URLS:客户端通告地址

  • ETCD_INITIAL_CLUSTER:集群节点地址

  • ETCD_INITIAL_CLUSTER_TOKEN:集群 Token

  • ETCD_INITIAL_CLUSTER_STATE:加入集群的当前状态,new 是新集群,existing 表示加入已有集群(扩容时用)

4.5.5 创建服务配置文件

#5. 创建服务配置文件
[root@k8s-master1 work]# cat > /usr/lib/systemd/system/etcd.service <<"EOF"
[Unit]
Description=Etcd Server
After=network.target
After=network-online.target
Wants=network-online.target

[Service]
Type=notify
EnvironmentFile=-/etc/etcd/etcd.conf
WorkingDirectory=/var/lib/etcd/
ExecStart=/usr/local/bin/etcd \
  --cert-file=/etc/etcd/ssl/etcd.pem \
  --key-file=/etc/etcd/ssl/etcd-key.pem \
  --trusted-ca-file=/etc/etcd/ssl/ca.pem \
  --peer-cert-file=/etc/etcd/ssl/etcd.pem \
  --peer-key-file=/etc/etcd/ssl/etcd-key.pem \
  --peer-trusted-ca-file=/etc/etcd/ssl/ca.pem \
  --peer-client-cert-auth \
  --client-cert-auth
Restart=on-failure
RestartSec=5
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
EOF

[root@k8s-master1 work]# cp ca*.pem /etc/etcd/ssl/
[root@k8s-master1 work]# cp etcd*.pem /etc/etcd/ssl/
[root@k8s-master1 work]# ls /etc/etcd/ssl/
ca-key.pem  ca.pem  etcd-key.pem  etcd.pem

4.5.6 同步 etcd 配置到集群其它 master 节点

# 传送服务配置文件后,需要手动修改 etcd 节点名称及 IP 地址
[root@k8s-master1 work]# for i in k8s-master2 k8s-master3;do scp /etc/etcd/etcd.conf $i:/etc/etcd/; done; 

# 传送证书文件
[root@k8s-master1 work]# for i in k8s-master2 k8s-master3; do scp /etc/etcd/ssl/* $i:/etc/etcd/ssl; done

# 传送服务启动配置文件
[root@k8s-master1 work]# for i in k8s-master2 k8s-master3;do scp /usr/lib/systemd/system/etcd.service $i:/usr/lib/systemd/system/; done

k8s-master2 etcd.conf(修改 etcd 节点名称和 ip 即可):

[root@k8s-master2 ~]# vim /etc/etcd/etcd.conf 
#[Member]
ETCD_NAME="etcd2"
ETCD_DATA_DIR="/var/lib/etcd/default.etcd"
ETCD_LISTEN_PEER_URLS="https://192.168.78.151:2380"
ETCD_LISTEN_CLIENT_URLS="https://192.168.78.151:2379,http://127.0.0.1:2379"

#[Clustering]
ETCD_INITIAL_ADVERTISE_PEER_URLS="https://192.168.78.151:2380"
ETCD_ADVERTISE_CLIENT_URLS="https://192.168.78.151:2379"
ETCD_INITIAL_CLUSTER="etcd1=https://192.168.78.150:2380,etcd2=https://192.168.78.151:2380,etcd3=https://192.168.78.152:2380"
ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"
ETCD_INITIAL_CLUSTER_STATE="new"

k8s-master3 etcd.conf(修改 etcd 节点名称和 ip 即可):

[root@k8s-master3 ~]# vim /etc/etcd/etcd.conf
#[Member]
ETCD_NAME="etcd3"
ETCD_DATA_DIR="/var/lib/etcd/default.etcd"
ETCD_LISTEN_PEER_URLS="https://192.168.78.152:2380"
ETCD_LISTEN_CLIENT_URLS="https://192.168.78.152:2379,http://127.0.0.1:2379"

#[Clustering]
ETCD_INITIAL_ADVERTISE_PEER_URLS="https://192.168.78.152:2380"
ETCD_ADVERTISE_CLIENT_URLS="https://192.168.78.152:2379"
ETCD_INITIAL_CLUSTER="etcd1=https://192.168.78.150:2380,etcd2=https://192.168.78.151:2380,etcd3=https://192.168.78.152:2380"
ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"
ETCD_INITIAL_CLUSTER_STATE="new"

4.5.7 启动 etcd 集群(三个 master 节点都需要操作)

        启动 etcd 服务的时候,先启动的 master1 的 etcd 服务,会一直卡住在启动的状态,然后接着再启动 master2 的 etcd,这样 master1 这个节点 etcd 才会正常起来(最好是三个节点同时一起执行):

systemctl daemon-reload
systemctl enable --now etcd.service
systemctl status etcd

4.5.8 验证集群状态

[root@k8s-master1 work]# ETCDCTL_API=3 /usr/local/bin/etcdctl --write-out=table --cacert=/etc/etcd/ssl/ca.pem --cert=/etc/etcd/ssl/etcd.pem --key=/etc/etcd/ssl/etcd-key.pem --endpoints=https://192.168.78.150:2379,https://192.168.78.151:2379,https://192.168.78.152:2379 endpoint health

# 检查 ETCD 数据库性能
ETCDCTL_API=3 /usr/local/bin/etcdctl --write-out=table --cacert=/etc/etcd/ssl/ca.pem --cert=/etc/etcd/ssl/etcd.pem --key=/etc/etcd/ssl/etcd-key.pem --endpoints=https://192.168.78.150:2379,https://192.168.78.151:2379,https://192.168.78.152:2379 check perf

ETCDCTL_API=3 /usr/local/bin/etcdctl --write-out=table --cacert=/etc/etcd/ssl/ca.pem --cert=/etc/etcd/ssl/etcd.pem --key=/etc/etcd/ssl/etcd-key.pem --endpoints=https://192.168.78.150:2379,https://192.168.78.151:2379,https://192.168.78.152:2379 member list

ETCDCTL_API=3 /usr/local/bin/etcdctl --write-out=table --cacert=/etc/etcd/ssl/ca.pem --cert=/etc/etcd/ssl/etcd.pem --key=/etc/etcd/ssl/etcd-key.pem --endpoints=https://192.168.78.150:2379,https://192.168.78.151:2379,https://192.168.78.152:2379 endpoint status

五、Kubernetes 组件部署

5.1 Kubernetes 软件包下载

k8s 各版本软件包下载官网:kubernetes/CHANGELOG at master · kubernetes/kubernetes · GitHub

找到需要安装的 k8s 版本(本次安装 k8s-v1.25 版本):

点击 Server Binaries:

 点击 adm64 版本下载即可:

# 在线下载安装包
[root@k8s-master1 ~]# wget https://storage.googleapis.com/kubernetes-release/release/v1.25.6/kubernetes-server-linux-amd64.tar.gz

5.2 Kubernetes 软件包安装

[root@k8s-master1 ~]# tar -zxvf kubernetes-server-linux-amd64.tar.gz 
[root@k8s-master1 ~]# cd kubernetes/server/bin/
[root@k8s-master1 bin]# cp kube-apiserver kube-controller-manager kube-scheduler kubectl /usr/local/bin/

5.3 Kubernetes 软件分发

[root@k8s-master1 bin]# scp kube-apiserver kube-controller-manager kube-scheduler kubectl k8s-master2:/usr/local/bin/

[root@k8s-master1 bin]# scp kube-apiserver kube-controller-manager kube-scheduler kubectl k8s-master3:/usr/local/bin/

[root@k8s-master1 bin]# scp kubelet kube-proxy k8s-master1:/usr/local/bin

[root@k8s-master1 bin]# scp kubelet kube-proxy k8s-master2:/usr/local/bin
  
[root@k8s-master1 bin]# scp kubelet kube-proxy k8s-master3:/usr/local/bin

[root@k8s-master1 bin]# scp kubelet kube-proxy k8s-node1:/usr/local/bin
  
[root@k8s-master1 bin]# scp kubelet kube-proxy k8s-node2:/usr/local/bin

5.4 在集群节点上创建目录(所有节点)

mkdir -p /etc/kubernetes/        
mkdir -p /etc/kubernetes/ssl     
mkdir -p /var/log/kubernetes

5.5 部署 api-server

        apiserver:提供 k8s api,是整个系统的对外接口,提供资源操作的唯一入口,供客户端和其它组件调用,提供了 k8s 各类资源对象(pod、deployment、Service 等)的增删改查,是整个系统的数据总线和数据中心,并提供认证、授权、访问控制、API 注册和发现等机制,并将操作对象持久化到 etcd 中。相当于“营业厅”。

5.5.1 创建 apiserver 证书请求文件

[root@k8s-master1 work]# pwd
/data/work
[root@k8s-master1 work]# cat > kube-apiserver-csr.json << "EOF"
{
"CN": "kubernetes",
  "hosts": [
    "127.0.0.1",
    "192.168.78.150",
    "192.168.78.151",
    "192.168.78.152",
    "192.168.78.153",
    "192.168.78.154",
    "192.168.78.155",
    "192.168.78.156",
    "192.168.78.157",
    "10.255.0.1",
    "kubernetes",
    "kubernetes.default",
    "kubernetes.default.svc",
    "kubernetes.default.svc.cluster",
    "kubernetes.default.svc.cluster.local"
  ],
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "Hubei",
      "L": "Wuhan",
      "O": "k8s",
      "OU": "system"
    }
  ]
}
EOF

说明

        如果 hosts 字段不为空则需要指定授权使用该证书的 IP(含 VIP) 或域名列表。由于该证书被 kubernetes master 集群使用,需要将节点的 IP 都填上,为了方便后期扩容可以多写几个预留的 IP。同时还需要填写 service 网络的首个 IP(一般是 kube-apiserver 指定的 service-cluster-ip-range 网段的第一个 IP,如 10.255.0.1)。

5.5.2 生成 apiserver 证书及 token 文件

# 创建 token.csv 文件
[root@k8s-master1 work]# cat > token.csv << EOF
$(head -c 16 /dev/urandom | od -An -t x | tr -d ' '),kubelet-bootstrap,10001,"system:kubelet-bootstrap"
EOF

[root@k8s-master1 work]# cat token.csv 
78c75da77de755539a346161f05894b8,kubelet-bootstrap,10001,"system:kubelet-bootstrap"

# 生成证书
[root@k8s-master1 work]# cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes kube-apiserver-csr.json | cfssljson -bare kube-apiserver

说明:

  • 启动 TLS Bootstrapping 机制

        Master apiserver 启用 TLS 认证后,每个节点的 kubelet 组件都要使用由 apiserver 使用的 CA 签发的有效证书才能与 apiserver 通讯,当 Node 节点很多时,这种客户端证书颁发需要大量工作,同样也会增加集群扩展复杂度。

        为了简化流程,Kubernetes引入了TLS bootstraping 机制来自动颁发客户端证书,kubelet 会以一个低权限用户自动向 apiserver 申请证书,kubelet 的证书由 apiserver 动态签署。所以强烈建议在 Node 上使用这种方式,目前主要用于 kubelet,kube-proxy 还是由我们统一颁发一个证书。

        Bootstrap 是很多系统中都存在的程序,比如 Linux 的 bootstrap,bootstrap 一般都是作为预先配置在开启或者系统启动的时候加载,这可以用来生成一个指定环境。Kubernetes 的 kubelet 在启动时同样可以加载一个这样的配置文件,这个文件的内容类似如下形式:

apiVersion: v1
clusters: null
contexts:
- context:
    cluster: kubernetes
    user: kubelet-bootstrap
  name: default
current-context: default
kind: Config
preferences: {}
users:
- name: kubelet-bootstrap
  user: {}
  • TLS bootstrapping 具体引导过程

1.TLS 作用

        TLS 的作用就是对通讯加密,防止中间人窃听;同时如果证书不信任的话根本就无法与 apiserver 建立连接,更不用提有没有权限向 apiserver 请求指定内容。

2. RBAC 作用

        当 TLS 解决了通讯问题后,那么权限问题就应由 RBAC 解决(可以使用其他权限模型,如 ABAC);RBAC 中规定了一个用户或者用户组(subject)具有请求哪些 api 的权限;在配合 TLS 加密的时候,实际上 apiserver 读取客户端证书的 CN 字段作为用户名,读取 O 字段作为用户组。

        以上说明:第一,想要与 apiserver 通讯就必须采用由 apiserver CA 签发的证书,这样才能形成信任关系,建立 TLS 连接;第二,可以通过证书的 CN、O 字段来提供 RBAC 所需的用户与用户组。

  • kubelet 首次启动流程

        TLS bootstrapping 功能是让 kubelet 组件去 apiserver 申请证书,然后用于连接 apiserver;那么第一次启动时没有证书如何连接 apiserver ?

        在 apiserver 配置中指定了一个 token.csv 文件,该文件中是一个预设的用户配置;同时该用户的 Token 和由 apiserver 的 CA 签发的用户被写入了 kubelet 所使用的 bootstrap.kubeconfig 配置文件中;这样在首次请求时,kubelet 使用 bootstrap.kubeconfig 中被 apiserver CA 签发证书时信任的用户来与 apiserver 建立 TLS 通讯,使用 bootstrap.kubeconfig 中的用户 Token 来向apiserver 声明自己的 RBAC 授权身份。

token.csv 格式:

# 格式:token,用户名,UID,用户组
3940fd7fbb391d1b4d861ad17a1f0613,kubelet-bootstrap,10001,"system:kubelet-bootstrap"

        首次启动时,可能与遇到 kubelet 报 401 无权访问 apiserver 的错误;这是因为在默认情况下,kubelet 通过 bootstrap.kubeconfig 中的预设用户 Token 声明了自己的身份,然后创建 CSR 请求;但是不要忘记这个用户在我们不处理的情况下他没任何权限的,包括创建 CSR 请求;所以需要创建一个 ClusterRoleBinding,将预设用户 kubelet-bootstrap 与内置的 ClusterRole system:node-bootstrapper 绑定到一起,使其能够发起 CSR 请求。稍后安装 kubelet的时候演示。

5.5.3 创建 apiserver 服务配置文件

kube-apiserver 配置文件官方文档:kube-apiserver | Kubernetes

[root@k8s-master1 work]# cat > /etc/kubernetes/kube-apiserver.conf << "EOF"
KUBE_APISERVER_OPTS="--enable-admission-plugins=NamespaceLifecycle,NodeRestriction,LimitRanger,ServiceAccount,DefaultStorageClass,ResourceQuota \
  --anonymous-auth=false \
  --bind-address=192.168.78.150 \
  --secure-port=6443 \
  --advertise-address=192.168.78.150 \
  --authorization-mode=Node,RBAC \
  --runtime-config=api/all=true \
  --enable-bootstrap-token-auth \
  --service-cluster-ip-range=10.255.0.0/16 \
  --token-auth-file=/etc/kubernetes/token.csv \
  --service-node-port-range=30000-32767 \
  --tls-cert-file=/etc/kubernetes/ssl/kube-apiserver.pem  \
  --tls-private-key-file=/etc/kubernetes/ssl/kube-apiserver-key.pem \
  --client-ca-file=/etc/kubernetes/ssl/ca.pem \
  --kubelet-client-certificate=/etc/kubernetes/ssl/kube-apiserver.pem \
  --kubelet-client-key=/etc/kubernetes/ssl/kube-apiserver-key.pem \
  --service-account-key-file=/etc/kubernetes/ssl/ca-key.pem \
  --service-account-signing-key-file=/etc/kubernetes/ssl/ca-key.pem  \
  --service-account-issuer=api \
  --etcd-cafile=/etc/etcd/ssl/ca.pem \
  --etcd-certfile=/etc/etcd/ssl/etcd.pem \
  --etcd-keyfile=/etc/etcd/ssl/etcd-key.pem \
  --etcd-servers=https://192.168.78.150:2379,https://192.168.78.151:2379,https://192.168.78.152:2379 \
  --allow-privileged=true \
  --apiserver-count=3 \
  --audit-log-maxage=30 \
  --audit-log-maxbackup=3 \
  --audit-log-maxsize=100 \
  --audit-log-path=/var/log/kube-apiserver-audit.log \
  --event-ttl=1h \
  --alsologtostderr=true \
  --logtostderr=false \
  --log-dir=/var/log/kubernetes \
  --v=4"
EOF

说明:

  • --logtostderr:启用日志

  • --v:日志等级

  • --log-dir:日志目录

  • --etcd-servers:etcd 集群地址

  • --bind-address:监听地址

  • --secure-port:https 安全端口

  • --advertise-address:集群通告地址

  • --allow-privileged:启用授权

  • --service-cluster-ip-range:Service 虚拟 IP 地址段

  • --enable-admission-plugins:准入控制模块

  • --authorization-mode:认证授权,启用 RBAC 授权和节点自管理

  • --enable-bootstrap-token-auth:启用 TLS bootstrap 机制

  • --token-auth-file:bootstrap token 文件

  • --service-node-port-range:Service nodeport 类型默认分配端口范围

  • --kubelet-client-xxx:apiserver 访问 kubelet 客户端证书

  • --tls-xxx-file:apiserver https 证书

  • --etcd-xxxfile:连接 Etcd 集群证书

  • -audit-log-xxx:审计日志

5.5.4 创建 apiserver 服务管理配置文件 

[root@k8s-master1 work]# cat > /usr/lib/systemd/system/kube-apiserver.service << "EOF"
[Unit]
Description=Kubernetes API Server
Documentation=https://github.com/kubernetes/kubernetes
After=etcd.service
Wants=etcd.service

[Service]
EnvironmentFile=-/etc/kubernetes/kube-apiserver.conf
ExecStart=/usr/local/bin/kube-apiserver $KUBE_APISERVER_OPTS
Restart=on-failure
RestartSec=5
Type=notify
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
EOF

5.5.5 同步文件到集群 master 节点 

[root@k8s-master1 work]# cp ca*.pem /etc/kubernetes/ssl/
[root@k8s-master1 work]# cp kube-apiserver*.pem /etc/kubernetes/ssl/
[root@k8s-master1 work]# cp token.csv /etc/kubernetes/

[root@k8s-master1 work]# scp /etc/kubernetes/token.csv k8s-master2:/etc/kubernetes  
[root@k8s-master1 work]# scp /etc/kubernetes/token.csv k8s-master3:/etc/kubernetes
  
[root@k8s-master1 work]# scp /etc/kubernetes/ssl/kube-apiserver*.pem k8s-master2:/etc/kubernetes/ssl   
[root@k8s-master1 work]# scp /etc/kubernetes/ssl/kube-apiserver*.pem k8s-master3:/etc/kubernetes/ssl

[root@k8s-master1 work]# scp /etc/kubernetes/ssl/ca*.pem k8s-master2:/etc/kubernetes/ssl
[root@k8s-master1 work]# scp /etc/kubernetes/ssl/ca*.pem k8s-master3:/etc/kubernetes/ssl

[root@k8s-master1 work]# scp /etc/kubernetes/kube-apiserver.conf k8s-master2:/etc/kubernetes/
[root@k8s-master1 work]# scp /etc/kubernetes/kube-apiserver.conf k8s-master3:/etc/kubernetes/

[root@k8s-master1 work]# scp /usr/lib/systemd/system/kube-apiserver.service k8s-master2:/usr/lib/systemd/system/
[root@k8s-master1 work]# scp /usr/lib/systemd/system/kube-apiserver.service k8s-master3:/usr/lib/systemd/system/

注意: master2 和 master3 的配置文件 kube-apiserver.conf 中的 IP 地址需要修改为实际的本机 IP。

[root@k8s-master2 ~]# vim /etc/kubernetes/kube-apiserver.conf 
KUBE_APISERVER_OPTS="--enable-admission-plugins=NamespaceLifecycle,NodeRestriction,LimitRanger,ServiceAccount,DefaultStorageClass,ResourceQuota \
  --anonymous-auth=false \
  --bind-address=192.168.78.151 \    # 修改此处
  --secure-port=6443 \
  --advertise-address=192.168.78.151 \    # 修改此处
  --authorization-mode=Node,RBAC \
  --runtime-config=api/all=true \
  --enable-bootstrap-token-auth \
  --service-cluster-ip-range=10.255.0.0/16 \
  --token-auth-file=/etc/kubernetes/token.csv \
  --service-node-port-range=30000-32767 \
  --tls-cert-file=/etc/kubernetes/ssl/kube-apiserver.pem  \
  --tls-private-key-file=/etc/kubernetes/ssl/kube-apiserver-key.pem \
  --client-ca-file=/etc/kubernetes/ssl/ca.pem \
  --kubelet-client-certificate=/etc/kubernetes/ssl/kube-apiserver.pem \
  --kubelet-client-key=/etc/kubernetes/ssl/kube-apiserver-key.pem \
  --service-account-key-file=/etc/kubernetes/ssl/ca-key.pem \
  --service-account-signing-key-file=/etc/kubernetes/ssl/ca-key.pem  \
  --service-account-issuer=api \
  --etcd-cafile=/etc/etcd/ssl/ca.pem \
  --etcd-certfile=/etc/etcd/ssl/etcd.pem \
  --etcd-keyfile=/etc/etcd/ssl/etcd-key.pem \
  --etcd-servers=https://192.168.78.150:2379,https://192.168.78.151:2379,https://192.168.78.152:2379 \
  --allow-privileged=true \
  --apiserver-count=3 \
  --audit-log-maxage=30 \
  --audit-log-maxbackup=3 \
  --audit-log-maxsize=100 \
  --audit-log-path=/var/log/kube-apiserver-audit.log \
  --event-ttl=1h \
  --alsologtostderr=true \
  --logtostderr=false \
  --log-dir=/var/log/kubernetes \
  --v=4"

[root@k8s-master3 ~]# vim /etc/kubernetes/kube-apiserver.conf 
KUBE_APISERVER_OPTS="--enable-admission-plugins=NamespaceLifecycle,NodeRestriction,LimitRanger,ServiceAccount,DefaultStorageClass,ResourceQuota \
  --anonymous-auth=false \
  --bind-address=192.168.78.152 \        # 修改此处
  --secure-port=6443 \
  --advertise-address=192.168.78.152 \   # 修改此处
  --authorization-mode=Node,RBAC \
  --runtime-config=api/all=true \
  --enable-bootstrap-token-auth \
  --service-cluster-ip-range=10.255.0.0/16 \
  --token-auth-file=/etc/kubernetes/token.csv \
  --service-node-port-range=30000-32767 \
  --tls-cert-file=/etc/kubernetes/ssl/kube-apiserver.pem  \
  --tls-private-key-file=/etc/kubernetes/ssl/kube-apiserver-key.pem \
  --client-ca-file=/etc/kubernetes/ssl/ca.pem \
  --kubelet-client-certificate=/etc/kubernetes/ssl/kube-apiserver.pem \
  --kubelet-client-key=/etc/kubernetes/ssl/kube-apiserver-key.pem \
  --service-account-key-file=/etc/kubernetes/ssl/ca-key.pem \
  --service-account-signing-key-file=/etc/kubernetes/ssl/ca-key.pem  \
  --service-account-issuer=api \
  --etcd-cafile=/etc/etcd/ssl/ca.pem \
  --etcd-certfile=/etc/etcd/ssl/etcd.pem \
  --etcd-keyfile=/etc/etcd/ssl/etcd-key.pem \
  --etcd-servers=https://192.168.78.150:2379,https://192.168.78.151:2379,https://192.168.78.152:2379 \
  --allow-privileged=true \
  --apiserver-count=3 \
  --audit-log-maxage=30 \
  --audit-log-maxbackup=3 \
  --audit-log-maxsize=100 \
  --audit-log-path=/var/log/kube-apiserver-audit.log \
  --event-ttl=1h \
  --alsologtostderr=true \
  --logtostderr=false \
  --log-dir=/var/log/kubernetes \
  --v=4"

5.5.6 启动 apiserver 服务

systemctl daemon-reload
systemctl enable --now kube-apiserver
systemctl status kube-apiserver

# 测试
[root@k8s-master1 work]# curl --insecure https://192.168.78.150:6443/
{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {},
  "status": "Failure",
  "message": "Unauthorized",
  "reason": "Unauthorized",
  "code": 401
}

[root@k8s-master1 work]# curl --insecure https://192.168.78.151:6443/
[root@k8s-master1 work]# curl --insecure https://192.168.78.152:6443/

上面看到 401,这个是正常的的状态,只是还没认证。

5.6 部署 kubectl

        Kubectl 是客户端工具,操作 k8s 资源的,如增删改查等。Kubectl 操作资源的时候,怎么知道连接到哪个集群呢?这时需要一个文件 /etc/kubernetes/admin.conf,kubectl 会根据这个文件的配置,去访问 k8s 资源。/etc/kubernetes/admin.con 文件记录了访问的 k8s 集群和用到的证书。

5.6.1 创建 kubectl 证书请求文件

[root@k8s-master1 work]# cat > admin-csr.json << "EOF"
{
  "CN": "admin",
  "hosts": [],
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "Hubei",
      "L": "Wuhan",
      "O": "system:masters",             
      "OU": "system"
    }
  ]
}
EOF

        说明:后续 kube-apiserver 使用 RBAC 对客户端(如 kubelet、kube-proxy、Pod)请求进行授权;kube-apiserver 预定义了一些 RBAC 使用的 RoleBindings,如 cluster-admin 将 Group system:masters 与 Role cluster-admin 绑定,该 Role 授予了调用 kube-apiserver 的所有 API 的权限;O 指定该证书的 Group 为 system:masters,kubelet 使用该证书访问 kube-apiserver 时 ,由于证书被 CA 签名,所以认证通过,同时由于证书用户组为经过预授权的 system:masters,所以被授予访问所有 API 的权限。

        注: 这个 admin 证书,是将来生成管理员用的 kube config 配置文件用的,现在我们一般建议使用 RBAC 来对 kubernetes 进行角色权限控制, kubernetes 将证书中的 CN 字段 作为 User, O 字段作为 Group; "O": "system:masters", 必须是 system:masters,否则后面 kubectl create clusterrolebinding 报错。

        证书 O 配置为 system:masters 在集群内部 cluster-admin 的 clusterrolebinding 将 system:masters 组和 cluster-admin clusterrole 绑定在一起。

5.6.2 生成证书文件

[root@k8s-master1 work]# cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes admin-csr.json | cfssljson -bare admin

5.6.3 复制文件到指定目录

[root@k8s-master1 work]# cp admin*.pem /etc/kubernetes/ssl/

5.6.4 生成 kubeconfig 配置文件

        kubeconfig 为 kubectl 的配置文件,包含访问 apiserver 的所有信息,如 apiserver 地址、CA 证书和自身使用的证书(这里如果报错找不到 kubeconfig 路径,请手动复制到相应路径下,没有则忽略)。

# 设置集群参数
[root@k8s-master1 work]# kubectl config set-cluster kubernetes --certificate-authority=ca.pem --embed-certs=true --server=https://192.168.78.150:6443 --kubeconfig=kube.config

# 设置客户端认证参数
[root@k8s-master1 work]# kubectl config set-credentials admin --client-certificate=admin.pem --client-key=admin-key.pem --embed-certs=true --kubeconfig=kube.config

# 设置上下文参数
[root@k8s-master1 work]# kubectl config set-context kubernetes --cluster=kubernetes --user=admin --kubeconfig=kube.config

# 设置当前上下文
[root@k8s-master1 work]# kubectl config use-context kubernetes --kubeconfig=kube.config

5.6.5 准备 kubectl 配置文件并进行角色绑定

[root@k8s-master1 work]# mkdir ~/.kube
[root@k8s-master1 work]# cp kube.config ~/.kube/config

# 授权 kubernetes 证书访问 kubelet api 权限
[root@k8s-master1 work]# kubectl create clusterrolebinding kube-apiserver:kubelet-apis --clusterrole=system:kubelet-api-admin --user kubernetes --kubeconfig=/root/.kube/config

5.6.6 查看集群状态

# 查看集群信息
[root@k8s-master1 ~]# kubectl cluster-info 
Kubernetes control plane is running at https://192.168.78.150:6443

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.

# 查看集群组件状态
[root@k8s-master1 ~]# kubectl get componentstatuses 
Warning: v1 ComponentStatus is deprecated in v1.19+
NAME                 STATUS      MESSAGE                                                                                        ERROR
scheduler            Unhealthy   Get "https://127.0.0.1:10259/healthz": dial tcp 127.0.0.1:10259: connect: connection refused   
controller-manager   Unhealthy   Get "https://127.0.0.1:10257/healthz": dial tcp 127.0.0.1:10257: connect: connection refused   
etcd-0               Healthy     {"health":"true","reason":""}                                                                  
etcd-1               Healthy     {"health":"true","reason":""}                                                                  
etcd-2               Healthy     {"health":"true","reason":""}                                                                  

# 查看命名空间中资源对象
[root@k8s-master1 ~]# kubectl get all --all-namespaces 
NAMESPACE   NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
default     service/kubernetes   ClusterIP   10.255.0.1   <none>        443/TCP   16h

5.6.7 同步 kubectl 配置文件到集群其它 master 节点

[root@k8s-master2 ~]# mkdir /root/.kube
[root@k8s-master3 ~]# mkdir /root/.kube

[root@k8s-master1 ~]# scp /root/.kube/config k8s-master2:/root/.kube/
[root@k8s-master1 ~]# scp /root/.kube/config k8s-master3:/root/.kube/

5.6.8 配置 kubectl 命令补全(可选)

Kubectl 自动补全官方文档:kubectl 备忘单 | Kubernetes

# 注意:在三个 master 节点上执行
# 在 bash 中设置当前 shell 的自动补全,要先安装 bash-completion 包
yum install -y bash-completion
source /usr/share/bash-completion/bash_completion

# 在你的 bash shell 中永久地添加自动补全
source <(kubectl completion bash)
echo "source <(kubectl completion bash)" >> ~/.bashrc

5.7 部署 kube-controller-manager

        controller-manager:作为集群内部的管理控制中心,负责集群内的 Node、Pod 副本、服务端点(Endpoint)、命名室间(Namespace)、服务账号(ServiceAccount)、资源定额(ResourceQuota) 的管理,当某个 Node 意外宕机时,Controller Manager 会及时发现并执行自动化修复流程,确保集群始终处于预期的工作状态。与 apiserver 交互,实时监控和维护 k8s 集群的控制器的健康情况,对有故障的进行处理和恢复,相当于“大总管”。

5.7.1 创建 kube-controller-manager 证书请求文件 

[root@k8s-master1 work]# pwd
/data/work

[root@k8s-master1 work]# cat > kube-controller-manager-csr.json << "EOF"
{
    "CN": "system:kube-controller-manager",
    "key": {
        "algo": "rsa",
        "size": 2048
    },
    "hosts": [
      "127.0.0.1",
      "192.168.78.150",
      "192.168.78.151",
      "192.168.78.152",
      "192.168.78.155"
    ],
    "names": [
      {
        "C": "CN",
        "ST": "HuBei",
        "L": "Wuhan",
        "O": "system:kube-controller-manager",
        "OU": "system"
      }
    ]
}
EOF

说明

  • hosts 列表包含所有 kube-controller-manager 节点 IP;

  • CN 为 system:kube-controller-manager;

  • O 为 system:kube-controller-manager,kubernetes 内置的 ClusterRoleBindings system:kube-controller-manager 赋予 kube-controller-manager 工作所需的权限。

5.7.2 创建 kube-controller-manager 证书文件

[root@k8s-master1 work]# cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes kube-controller-manager-csr.json | cfssljson -bare kube-controller-manager

5.7.3 创建 kube-controller-manager 的 kube-controller-manager.kubeconfig

# 设置集群参数
[root@k8s-master1 work]# kubectl config set-cluster kubernetes --certificate-authority=ca.pem --embed-certs=true --server=https://192.168.78.150:6443 --kubeconfig=kube-controller-manager.kubeconfig

# 设置客户端认证参数
[root@k8s-master1 work]# kubectl config set-credentials system:kube-controller-manager --client-certificate=kube-controller-manager.pem --client-key=kube-controller-manager-key.pem --embed-certs=true --kubeconfig=kube-controller-manager.kubeconfig

# 设置上下文参数
[root@k8s-master1 work]# kubectl config set-context system:kube-controller-manager --cluster=kubernetes --user=system:kube-controller-manager --kubeconfig=kube-controller-manager.kubeconfig

# 设置当前上下文
[root@k8s-master1 work]# kubectl config use-context system:kube-controller-manager --kubeconfig=kube-controller-manager.kubeconfig

5.7.4 创建 kube-controller-manager 配置文件 

kube-controller-manager 配置文件官方文档:kube-controller-manager | Kubernetes

[root@k8s-master1 work]# cat > kube-controller-manager.conf << "EOF"
KUBE_CONTROLLER_MANAGER_OPTS=" \
  --secure-port=10257 \
  --bind-address=127.0.0.1 \
  --kubeconfig=/etc/kubernetes/kube-controller-manager.kubeconfig \
  --service-cluster-ip-range=10.255.0.0/16 \
  --cluster-name=kubernetes \
  --cluster-signing-cert-file=/etc/kubernetes/ssl/ca.pem \
  --cluster-signing-key-file=/etc/kubernetes/ssl/ca-key.pem \
  --allocate-node-cidrs=true \
  --cluster-cidr=10.0.0.0/16 \
  --root-ca-file=/etc/kubernetes/ssl/ca.pem \
  --service-account-private-key-file=/etc/kubernetes/ssl/ca-key.pem \
  --leader-elect=true \
  --feature-gates=RotateKubeletServerCertificate=true \
  --controllers=*,bootstrapsigner,tokencleaner \
  --horizontal-pod-autoscaler-sync-period=10s \
  --tls-cert-file=/etc/kubernetes/ssl/kube-controller-manager.pem \
  --tls-private-key-file=/etc/kubernetes/ssl/kube-controller-manager-key.pem \
  --use-service-account-credentials=true \
  --alsologtostderr=true \
  --logtostderr=false \
  --log-dir=/var/log/kubernetes \
  --v=2"
EOF
  • –secure-port=10257、–bind-address=127.0.0.1: 在本地网络接口监听 10257 端口的 https /metrics 请求;
  • –kubeconfig:指定 kubeconfig 文件路径,kube-controller-manager 使用它连接和验证 kube-apiserver;
  • –authentication-kubeconfig 和 --authorization-kubeconfig:kube-controller-manager 使用它连接 apiserver,对 client 的请求进行认证和授权。kube-controller-manager 不再使用 --tls-ca-file 对请求 https metrics 的 Client 证书进行校验。如果没有配置这两个 kubeconfig 参数,则 client 连接 kube-controller-manager https 端口的请求会被拒绝(提示权限不足)。
  • –cluster-signing-*-file:签名 TLS Bootstrap 创建的证书;
  • –experimental-cluster-signing-duration:指定 TLS Bootstrap 证书的有效期;
  • –root-ca-file:放置到容器 ServiceAccount 中的 CA 证书,用来对 kube-apiserver 的证书进行校验;
  • –service-account-private-key-file:签名 ServiceAccount 中 Token 的私钥文件,必须和 kube-apiserver 的 --service-account-key-file 指定的公钥文件配对使用;
  • –service-cluster-ip-range :指定 Service Cluster IP 网段,必须和 kube-apiserver 中的同名参数一致;
  • –leader-elect=true:集群运行模式,启用选举功能;被选为 leader 的节点负责处理工作,其它节点为阻塞状态;
  • –controllers=*,bootstrapsigner,tokencleaner:启用的控制器列表,tokencleaner 用于自动清理过期的 Bootstrap token;
  • –horizontal-pod-autoscaler-*:custom metrics 相关参数,支持 autoscaling/v2alpha1;
  • –tls-cert-file、–tls-private-key-file:使用 https 输出 metrics 时使用的 Server 证书和秘钥;
  • –use-service-account-credentials=true: kube-controller-manager 中各 controller 使用 serviceaccount 访问 kube-apiserver。

5.7.5 创建服务启动文件

[root@k8s-master1 work]# cat > /usr/lib/systemd/system/kube-controller-manager.service << "EOF"
[Unit]
Description=Kubernetes Controller Manager
Documentation=https://github.com/kubernetes/kubernetes

[Service]
EnvironmentFile=-/etc/kubernetes/kube-controller-manager.conf
ExecStart=/usr/local/bin/kube-controller-manager $KUBE_CONTROLLER_MANAGER_OPTS
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF

5.7.6 同步文件到集群 master 节点

[root@k8s-master1 work]# cp kube-controller-manager*.pem /etc/kubernetes/ssl/
[root@k8s-master1 work]# cp kube-controller-manager.kubeconfig /etc/kubernetes/
[root@k8s-master1 work]# cp kube-controller-manager.conf /etc/kubernetes/

[root@k8s-master1 work]# scp  kube-controller-manager*.pem k8s-master2:/etc/kubernetes/ssl/
[root@k8s-master1 work]# scp  kube-controller-manager*.pem k8s-master3:/etc/kubernetes/ssl/

[root@k8s-master1 work]# scp  kube-controller-manager.kubeconfig kube-controller-manager.conf k8s-master2:/etc/kubernetes/
[root@k8s-master1 work]# scp  kube-controller-manager.kubeconfig kube-controller-manager.conf k8s-master3:/etc/kubernetes/

[root@k8s-master1 work]# scp  /usr/lib/systemd/system/kube-controller-manager.service k8s-master2:/usr/lib/systemd/system/
[root@k8s-master1 work]# scp  /usr/lib/systemd/system/kube-controller-manager.service k8s-master3:/usr/lib/systemd/system/

5.7.7 启动服务(在三个 master 执行)

systemctl daemon-reload 
systemctl enable --now kube-controller-manager
systemctl status kube-controller-manager

[root@k8s-master1 work]# kubectl get componentstatuses 
Warning: v1 ComponentStatus is deprecated in v1.19+
NAME                 STATUS      MESSAGE                                                                                        ERROR
scheduler            Unhealthy   Get "https://127.0.0.1:10259/healthz": dial tcp 127.0.0.1:10259: connect: connection refused   
etcd-0               Healthy     {"health":"true","reason":""}                                                                  
etcd-2               Healthy     {"health":"true","reason":""}                                                                  
controller-manager   Healthy     ok                                                                                             
etcd-1               Healthy     {"health":"true","reason":""} 

5.8 部署 kube-scheduler

        scheduler:负责 k8s 集群中 pod 调度 , scheduler 通过与 apiserver 交互监听到创建 Pod 副本的信息后,它会检索所有符合该 Pod 要求的工作节点列表,开始执行 Pod 调度逻辑。调度成功后将 Pod 绑定到目标节点上,相当于“调度室”。

5.8.1 创建 kube-scheduler 证书请求文件

[root@k8s-master1 work]# cat > kube-scheduler-csr.json << "EOF"
{
    "CN": "system:kube-scheduler",
    "hosts": [
      "127.0.0.1",
      "192.168.78.150",
      "192.168.78.151",
      "192.168.78.152",
      "192.168.78.155"
    ],
    "key": {
        "algo": "rsa",
        "size": 2048
    },
    "names": [
      {
        "C": "CN",
        "ST": "Hubei",
        "L": "Wuhan",
        "O": "system:kube-scheduler",
        "OU": "system"
      }
    ]
}
EOF

注:

  • hosts 列表包含所有 kube-scheduler 节点 IP 和 VIP;

  • CN 为 system:kube-scheduler;

  • O 为 system:kube-scheduler,kubernetes 内置的 ClusterRoleBindings system:kube-scheduler 将赋予 kube-scheduler 工作所需的权限。

5.8.2 生成 kube-scheduler 证书 

[root@k8s-master1 work]# cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes kube-scheduler-csr.json | cfssljson -bare kube-scheduler

5.8.3 创建 kube-scheduler 的 kubeconfig

# 设置集群参数
[root@k8s-master1 work]# kubectl config set-cluster kubernetes --certificate-authority=ca.pem --embed-certs=true --server=https://192.168.78.150:6443 --kubeconfig=kube-scheduler.kubeconfig

# 设置客户端认证参数
[root@k8s-master1 work]# kubectl config set-credentials system:kube-scheduler --client-certificate=kube-scheduler.pem --client-key=kube-scheduler-key.pem --embed-certs=true --kubeconfig=kube-scheduler.kubeconfig

# 设置上下文参数
[root@k8s-master1 work]# kubectl config set-context system:kube-scheduler --cluster=kubernetes --user=system:kube-scheduler --kubeconfig=kube-scheduler.kubeconfig

# 设置当前上下文
[root@k8s-master1 work]# kubectl config use-context system:kube-scheduler --kubeconfig=kube-scheduler.kubeconfig

5.8.4 创建服务配置文件

kube-scheduler 配置文件官方文档:kube-scheduler | Kubernetes

[root@k8s-master1 work]# cat > kube-scheduler.conf << "EOF"
KUBE_SCHEDULER_OPTS=" \
--kubeconfig=/etc/kubernetes/kube-scheduler.kubeconfig \
--leader-elect=true \
--alsologtostderr=true \
--logtostderr=false \
--log-dir=/var/log/kubernetes \
--v=2"
EOF

5.8.5 创建服务启动配置文件 

[root@k8s-master1 work]# cat > /usr/lib/systemd/system/kube-scheduler.service << "EOF"
[Unit]
Description=Kubernetes Scheduler
Documentation=https://github.com/kubernetes/kubernetes

[Service]
EnvironmentFile=-/etc/kubernetes/kube-scheduler.conf
ExecStart=/usr/local/bin/kube-scheduler $KUBE_SCHEDULER_OPTS
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF

5.8.6 同步文件至集群 master 节点

[root@k8s-master1 work]# cp kube-scheduler*.pem /etc/kubernetes/ssl/
[root@k8s-master1 work]# cp kube-scheduler.kubeconfig /etc/kubernetes/
[root@k8s-master1 work]# cp kube-scheduler.conf /etc/kubernetes/

[root@k8s-master1 work]# scp  kube-scheduler*.pem k8s-master2:/etc/kubernetes/ssl/
[root@k8s-master1 work]# scp  kube-scheduler*.pem k8s-master3:/etc/kubernetes/ssl/

[root@k8s-master1 work]# scp  kube-scheduler.kubeconfig kube-scheduler.conf k8s-master2:/etc/kubernetes/
[root@k8s-master1 work]# scp  kube-scheduler.kubeconfig kube-scheduler.conf k8s-master3:/etc/kubernetes/

[root@k8s-master1 work]# scp  /usr/lib/systemd/system/kube-scheduler.service k8s-master2:/usr/lib/systemd/system/
[root@k8s-master1 work]# scp  /usr/lib/systemd/system/kube-scheduler.service k8s-master3:/usr/lib/systemd/system/

5.8.7 启动服务

systemctl daemon-reload
systemctl enable --now kube-scheduler
systemctl status kube-scheduler

[root@k8s-master1 work]# kubectl get componentstatuses 
Warning: v1 ComponentStatus is deprecated in v1.19+
NAME                 STATUS    MESSAGE                         ERROR
scheduler            Healthy   ok                              
controller-manager   Healthy   ok                              
etcd-0               Healthy   {"health":"true","reason":""}   
etcd-2               Healthy   {"health":"true","reason":""}   
etcd-1               Healthy   {"health":"true","reason":""}

六、工作节点(worker node)部署

6.1 部署 kubelet(在 k8s-master1 上操作)

        kubelet:每个 Node 节点上的 kubelet 定期就会调用 API Server 的 REST 接口报告自身状态,API Server 接收这些信息后,将节点状态信息更新到 etcd 中。kubelet 也通过 API Server 监听 Pod 信息,从而对 Node 机器上的 POD 进行管理,如创建、删除、更新 Pod。

6.1.1 创建 kubelet-bootstrap.kubeconfig

[root@k8s-master1 work]# BOOTSTRAP_TOKEN=$(awk -F "," '{print $1}' /etc/kubernetes/token.csv)

[root@k8s-master1 work]# kubectl config set-cluster kubernetes --certificate-authority=ca.pem --embed-certs=true --server=https://192.168.78.150:6443 --kubeconfig=kubelet-bootstrap.kubeconfig

[root@k8s-master1 work]# kubectl config set-credentials kubelet-bootstrap --token=${BOOTSTRAP_TOKEN} --kubeconfig=kubelet-bootstrap.kubeconfig

[root@k8s-master1 work]# kubectl config set-context default --cluster=kubernetes --user=kubelet-bootstrap --kubeconfig=kubelet-bootstrap.kubeconfig

[root@k8s-master1 work]# kubectl config use-context default --kubeconfig=kubelet-bootstrap.kubeconfig

# 添加权限
[root@k8s-master1 work]# kubectl create clusterrolebinding kubelet-bootstrap --clusterrole=system:node-bootstrapper --user=kubelet-bootstrap

6.2.2 创建 kubelet 配置文件

# "cgroupDriver": "systemd" 要和 docker 的驱动一致。address 替换为自己 master1 的 IP 地址。

[root@k8s-master1 work]# cat > kubelet.json << "EOF"
{
  "kind": "KubeletConfiguration",
  "apiVersion": "kubelet.config.k8s.io/v1beta1",
  "authentication": {
    "x509": {
      "clientCAFile": "/etc/kubernetes/ssl/ca.pem"
    },
    "webhook": {
      "enabled": true,
      "cacheTTL": "2m0s"
    },
    "anonymous": {
      "enabled": false
    }
  },
  "authorization": {
    "mode": "Webhook",
    "webhook": {
      "cacheAuthorizedTTL": "5m0s",
      "cacheUnauthorizedTTL": "30s"
    }
  },
  "address": "192.168.78.150",
  "port": 10250,
  "readOnlyPort": 10255,
  "cgroupDriver": "systemd",                    
  "hairpinMode": "promiscuous-bridge",
  "serializeImagePulls": false,
  "clusterDomain": "cluster.local.",
  "clusterDNS": ["10.255.0.2"]
}
EOF

6.1.3 创建 kubelet 服务启动管理文件

kubelet 配置官方文档:kubelet | Kubernetes

[root@k8s-master1 work]# cat > /usr/lib/systemd/system/kubelet.service << "EOF"
[Unit]
Description=Kubernetes Kubelet
Documentation=https://github.com/kubernetes/kubernetes
After=docker.service
Requires=docker.service

[Service]
WorkingDirectory=/var/lib/kubelet
ExecStart=/usr/local/bin/kubelet \
  --bootstrap-kubeconfig=/etc/kubernetes/kubelet-bootstrap.kubeconfig \
  --cert-dir=/etc/kubernetes/ssl \
  --kubeconfig=/etc/kubernetes/kubelet.kubeconfig \
  --config=/etc/kubernetes/kubelet.json \
  --container-runtime-endpoint=unix:///run/containerd/containerd.sock \
  --pod-infra-container-image=registry.aliyuncs.com/google_containers/pause:3.2 \
  --alsologtostderr=true \
  --logtostderr=false \
  --log-dir=/var/log/kubernetes \
  --v=2
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF

注:

  • –network-plugin:启用 CNI

  • –kubeconfig:用于连接 apiserver

  • –bootstrap-kubeconfig:首次启动向 apiserver 申请证书

  • –config:配置参数文件

  • –cert-dir:kubelet 证书生成目录

  • –pod-infra-container-image:管理 Pod 网络容器的镜像地址 

6.1.4 同步文件到集群节点

        如果不想 master 节点也安装 kubelet 组件,可以只给 node 节点传送文件,只在 node 节点安装也可。

[root@k8s-master1 work]# cp kubelet-bootstrap.kubeconfig kubelet.json /etc/kubernetes/

[root@k8s-master1 work]# for i in  k8s-master2 k8s-master3 k8s-node1 k8s-node2;do scp /etc/kubernetes/kubelet-bootstrap.kubeconfig /etc/kubernetes/kubelet.json $i:/etc/kubernetes/;done

[root@k8s-master1 work]# for i in  k8s-master2 k8s-master3 k8s-node1 k8s-node2;do scp ca.pem $i:/etc/kubernetes/ssl/;done

[root@k8s-master1 work]# for i in k8s-master2 k8s-master3 k8s-node1 k8s-node2;do scp /usr/lib/systemd/system/kubelet.service $i:/usr/lib/systemd/system/;done

        注意:传送后到各个节点后的 kubelet.json 中 address 需要自己去手动修改为当前主机 IP 地址。 

6.1.5 创建目录及启动服务(所有节点执行)

mkdir -p /var/lib/kubelet

systemctl daemon-reload
systemctl enable --now kubelet
systemctl status kubelet

6.1.6 验证集群状态

        可以看到一个各节点发送了 CSR 请求,如果状态是 Pending,则需要 Approve 一下 bootstrap 请求:

[root@k8s-master1 work]# kubectl get csr
NAME                                                   AGE   SIGNERNAME                                    REQUESTOR           REQUESTEDDURATION   CONDITION
node-csr-ELAsquFkboi3FVWEvtjHcYPhZkroHFZvtw0HSfmQcU8   22s   kubernetes.io/kube-apiserver-client-kubelet   kubelet-bootstrap   <none>              Pending
node-csr-IHAdsHaotk8Z3_tW7rYvSmeKGdXqbPB0m98YdwfBs9Y   22s   kubernetes.io/kube-apiserver-client-kubelet   kubelet-bootstrap   <none>              Pending
node-csr-RH7808N9oudntus38bSsUuhw5xxInRlSyzBtLPQzlf8   22s   kubernetes.io/kube-apiserver-client-kubelet   kubelet-bootstrap   <none>              Pending
node-csr-X9GbXTxND15dH5bH1Fe-Qq9XMdl084qlHo8YpNeW5fw   22s   kubernetes.io/kube-apiserver-client-kubelet   kubelet-bootstrap   <none>              Pending
node-csr-Z0KsK-FCseOEi1UfrCIkzC-HtxBijchx0tnS4eIpq8g   22s   kubernetes.io/kube-apiserver-client-kubelet   kubelet-bootstrap   <none>              Pending

# approve 方法:
[root@k8s-master1 work]# kubectl certificate approve node-csr-ELAsquFkboi3FVWEvtjHcYPhZkroHFZvtw0HSfmQcU8
[root@k8s-master1 work]# kubectl certificate approve node-csr-IHAdsHaotk8Z3_tW7rYvSmeKGdXqbPB0m98YdwfBs9Y
[root@k8s-master1 work]# kubectl certificate approve node-csr-RH7808N9oudntus38bSsUuhw5xxInRlSyzBtLPQzlf8
[root@k8s-master1 work]# kubectl certificate approve node-csr-X9GbXTxND15dH5bH1Fe-Qq9XMdl084qlHo8YpNeW5fw
[root@k8s-master1 work]# kubectl certificate approve node-csr-Z0KsK-FCseOEi1UfrCIkzC-HtxBijchx0tnS4eIpq8g

# 再次查看状态是否变为 Approved,Issued
[root@k8s-master1 work]# kubectl get csr

[root@k8s-master1 work]# kubectl get nodes
NAME          STATUS     ROLES    AGE    VERSION
k8s-master1   NotReady   <none>   94s    v1.25.6
k8s-master2   NotReady   <none>   72s    v1.25.6
k8s-master3   NotReady   <none>   84s    v1.25.6
k8s-node1     NotReady   <none>   2m4s   v1.25.6
k8s-node2     NotReady   <none>   61s    v1.25.6

注意:STATUS 是 NotReady 表示还没有安装网络插件。

6.2 部署 kube-proxy

        kube-proxy:提供网络代理和负载均衡,是实现 service 的通信与负载均衡机制的重要组件,kube-proxy 负责为 Pod 创建代理服务,从 apiserver 获取所有 service 信息,并根据 service 信息创建代理服务,实现 service 到 Pod 的请求路由和转发,从而实现 K8s 层级的虚拟转发网络,将到 service 的请求转发到后端的 pod 上。

6.2.1 创建 kube-proxy 证书请求文件

[root@k8s-master1 work]# cat > kube-proxy-csr.json << "EOF"
{
  "CN": "system:kube-proxy",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "Hubei",
      "L": "Wuhan",
      "O": "k8s",
      "OU": "system"
    }
  ]
}
EOF

6.2.2 生成证书

[root@k8s-master1 work]# cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes kube-proxy-csr.json | cfssljson -bare kube-proxy

6.2.3 创建 kubeconfig 文件

[root@k8s-master1 work]# kubectl config set-cluster kubernetes --certificate-authority=ca.pem --embed-certs=true --server=https://192.168.78.150:6443 --kubeconfig=kube-proxy.kubeconfig

[root@k8s-master1 work]# kubectl config set-credentials kube-proxy --client-certificate=kube-proxy.pem --client-key=kube-proxy-key.pem --embed-certs=true --kubeconfig=kube-proxy.kubeconfig

[root@k8s-master1 work]# kubectl config set-context default --cluster=kubernetes --user=kube-proxy --kubeconfig=kube-proxy.kubeconfig

[root@k8s-master1 work]# kubectl config use-context default --kubeconfig=kube-proxy.kubeconfig

6.2.4 创建服务配置文件

[root@k8s-master1 work]# cat > kube-proxy.yaml << "EOF"
apiVersion: kubeproxy.config.k8s.io/v1alpha1
bindAddress: 192.168.78.150
clientConnection:
  kubeconfig: /etc/kubernetes/kube-proxy.kubeconfig
clusterCIDR: 192.168.78.0/24
healthzBindAddress: 192.168.78.150:10256
kind: KubeProxyConfiguration
metricsBindAddress: 192.168.78.150:10249
mode: "ipvs"
EOF

6.2.5 创建服务启动管理文件

[root@k8s-master1 work]# cat > /usr/lib/systemd/system/kube-proxy.service << "EOF"
[Unit]
Description=Kubernetes Kube-Proxy Server
Documentation=https://github.com/kubernetes/kubernetes
After=network.target

[Service]
WorkingDirectory=/var/lib/kube-proxy
ExecStart=/usr/local/bin/kube-proxy \
  --config=/etc/kubernetes/kube-proxy.yaml \
  --alsologtostderr=true \
  --logtostderr=false \
  --log-dir=/var/log/kubernetes \
  --v=2
Restart=on-failure
RestartSec=5
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
EOF

6.2.6 同步文件到集群节点

        如果不想 master 节点也安装 kube-proxy 组件,可以只给 node 节点传送文件,只在 node 节点安装也可。

[root@k8s-master1 work]# cp kube-proxy*.pem /etc/kubernetes/ssl/
[root@k8s-master1 work]# cp kube-proxy.kubeconfig kube-proxy.yaml /etc/kubernetes/

[root@k8s-master1 work]# for i in k8s-master2 k8s-master3 k8s-node1 k8s-node2;do scp kube-proxy.kubeconfig kube-proxy.yaml $i:/etc/kubernetes/;done

[root@k8s-master1 work]# for i in k8s-master2 k8s-master3 k8s-node1 k8s-node2;do scp  /usr/lib/systemd/system/kube-proxy.service $i:/usr/lib/systemd/system/;done

注意:传送后需要手动修改 kube-proxy.yaml 中三个 Address IP 地址为当前主机 IP.

6.2.7 服务启动(所有节点)

mkdir -p /var/lib/kube-proxy

systemctl daemon-reload
systemctl enable --now kube-proxy
systemctl status kube-proxy

6.3 网络组件部署 Calico

Calico 各版本官方文档:System requirements | Calico Documentation

可以通过这里来查看 Calico 所支持的 k8s 版本:

6.3.1 下载 calico.yaml

[root@k8s-master1 ~]# curl https://raw.githubusercontent.com/projectcalico/calico/v3.24.5/manifests/calico.yaml -O
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0curl: (7) Failed connect to raw.githubusercontent.com:443; 拒绝连接

我自己的虚机下载不了,所以换了方法:

复制这个连接:https://raw.githubusercontent.com/projectcalico/calico/v3.24.5/manifests/calico.yaml 到浏览器下载,再上传到 master1 即可:

6.3.2 修改 calico.yaml 里的 pod 网段

        在命令模式下输入 /192 就可以快速定位到需要修改的地址了,不然慢慢找太麻烦了,文件很长;然后把注释取消掉,把 value 修改成我们的 pod ip 地址即可:

[root@k8s-master1 ~]# vim calico.yaml 
······
            # no effect. This should fall within `--cluster-cidr`.
            - name: CALICO_IPV4POOL_CIDR
              value: "10.0.0.0/16"
            # Disable file logging so `kubectl logs` works.
            - name: CALICO_DISABLE_FILE_LOGGING
              value: "true"
······

注意:改的时候请看清缩进关系,即这里的对齐关系。

6.3.3 提前下载好所需的镜像(可省略,直接执行下一步)

        ctr 是 containerd 自带的工具,有命名空间的概念,若是 k8s 相关的镜像,都默认在 k8s.io 这个命名空间,所以导入镜像时需要指定命令空间为 k8s.io:

[root@k8s-master1 ~]# grep image calico.yaml
          image: docker.io/calico/cni:v3.24.5
          imagePullPolicy: IfNotPresent
          image: docker.io/calico/cni:v3.24.5
          imagePullPolicy: IfNotPresent
          image: docker.io/calico/node:v3.24.5
          imagePullPolicy: IfNotPresent
          image: docker.io/calico/node:v3.24.5
          imagePullPolicy: IfNotPresent
          image: docker.io/calico/kube-controllers:v3.24.5
          imagePullPolicy: IfNotPresent

# 在所有节点(包括 master)上把这些镜像下载下来:
for i in docker.io/calico/cni:v3.24.5 docker.io/calico/node:v3.24.5 docker.io/calico/kube-controllers:v3.24.5 ; do ctr -n=k8s.io images pull $i ; done

6.3.4 安装 calico 网络

[root@k8s-master1 ~]# kubectl apply -f calico.yaml 

6.3.5 验证结果

[root@k8s-master1 work]# kubectl get pods -A 
NAMESPACE     NAME                                       READY   STATUS    RESTARTS   AGE
kube-system   calico-kube-controllers-798cc86c47-s8hb2   1/1     Running   0          98s
kube-system   calico-node-7n6hg                          1/1     Running   0          98s
kube-system   calico-node-7r6tt                          1/1     Running   0          98s
kube-system   calico-node-9sx5w                          1/1     Running   0          98s
kube-system   calico-node-fbbjq                          1/1     Running   0          98s
kube-system   calico-node-frf6p                          1/1     Running   0          98s

[root@k8s-master1 work]# kubectl get nodes
NAME          STATUS   ROLES    AGE   VERSION
k8s-master1   Ready    <none>   53m   v1.25.6
k8s-master2   Ready    <none>   53m   v1.25.6
k8s-master3   Ready    <none>   53m   v1.25.6
k8s-node1     Ready    <none>   54m   v1.25.6
k8s-node2     Ready    <none>   52m   v1.25.6

6.4 部署 CoreDNS

6.4.1 下载 coredns.yaml

coredns.yaml 下载官网:kubernetes/cluster/addons/dns/coredns at master · kubernetes/kubernetes · GitHub

# wget 不下来的手动去复制吧(我就是复制的),wget 下载的文件格式不对
[root@k8s-master1 work]# wget https://github.com/kubernetes/kubernetes/blob/master/cluster/addons/dns/coredns/coredns.yaml.base

# 或者复制内容
[root@k8s-master1 work]# vim coredns.yaml

6.4.2 修改 coredns.yaml

拿到 yaml 文件需要修改几处配置:

#1. 修改 k8s 集群后缀名称 __DNS__DOMAIN__ 为 cluster.local
#77         kubernetes __DNS__DOMAIN__ in-addr.arpa ip6.arpa {
# 修改后:kubernetes cluster.local in-addr.arpa ip6.arpa {

#2. 修改 coredns 谷歌地址为 dockerhub 地址,容易下载
#142         image: registry.k8s.io/coredns/coredns:v1.10.0   
# 修改后:image: coredns/coredns:1.10.0

#3. 修改 pod 启动内存限制大小,300Mi 即可(这步可省略不做,我修改了~)
#146             memory: __DNS__MEMORY__LIMIT__
# 修改后:memory: 300Mi

#4. 修改 coredns 的 svcIP 地址,一般为 svc 网段的第二位(一开始我们规划好的 10.255.0.0),10.255.0.2,第一位为 apiserver 的 svc
#212   clusterIP: __DNS__SERVER__
# 修改后:clusterIP: 10.255.0.2

#5. 修改 coredns 副本数,默认是 1,且默认没有 replicas 字段(这步可省略不做)
102 spec:
103   # replicas: not specified here:
104   # 1. In order to make Addon Manager do not reconcile this replicas parameter.
105   # 2. Default is 1.
106   # 3. Will be tuned in real time if DNS horizontal auto-scaling is turned on.
      replicas: 3
107   strategy:

完整 coredns.yaml 配置文件如下:

[root@k8s-master1 work]# cat coredns.yaml 
# __MACHINE_GENERATED_WARNING__

apiVersion: v1
kind: ServiceAccount
metadata:
  name: coredns
  namespace: kube-system
  labels:
      kubernetes.io/cluster-service: "true"
      addonmanager.kubernetes.io/mode: Reconcile
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  labels:
    kubernetes.io/bootstrapping: rbac-defaults
    addonmanager.kubernetes.io/mode: Reconcile
  name: system:coredns
rules:
- apiGroups:
  - ""
  resources:
  - endpoints
  - services
  - pods
  - namespaces
  verbs:
  - list
  - watch
- apiGroups:
  - ""
  resources:
  - nodes
  verbs:
  - get
- apiGroups:
  - discovery.k8s.io
  resources:
  - endpointslices
  verbs:
  - list
  - watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  annotations:
    rbac.authorization.kubernetes.io/autoupdate: "true"
  labels:
    kubernetes.io/bootstrapping: rbac-defaults
    addonmanager.kubernetes.io/mode: EnsureExists
  name: system:coredns
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: system:coredns
subjects:
- kind: ServiceAccount
  name: coredns
  namespace: kube-system
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: coredns
  namespace: kube-system
  labels:
      addonmanager.kubernetes.io/mode: EnsureExists
data:
  Corefile: |
    .:53 {
        errors
        health {
            lameduck 5s
        }
        ready
        kubernetes cluster.local in-addr.arpa ip6.arpa {
            pods insecure
            fallthrough in-addr.arpa ip6.arpa
            ttl 30
        }
        prometheus :9153
        forward . /etc/resolv.conf {
            max_concurrent 1000
        }
        cache 30
        loop
        reload
        loadbalance
    }
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: coredns
  namespace: kube-system
  labels:
    k8s-app: kube-dns
    kubernetes.io/cluster-service: "true"
    addonmanager.kubernetes.io/mode: Reconcile
    kubernetes.io/name: "CoreDNS"
spec:
  # replicas: not specified here:
  # 1. In order to make Addon Manager do not reconcile this replicas parameter.
  # 2. Default is 1.
  # 3. Will be tuned in real time if DNS horizontal auto-scaling is turned on.
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1
  selector:
    matchLabels:
      k8s-app: kube-dns
  template:
    metadata:
      labels:
        k8s-app: kube-dns
    spec:
      securityContext:
        seccompProfile:
          type: RuntimeDefault
      priorityClassName: system-cluster-critical
      serviceAccountName: coredns
      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 100
            podAffinityTerm:
              labelSelector:
                matchExpressions:
                  - key: k8s-app
                    operator: In
                    values: ["kube-dns"]
              topologyKey: kubernetes.io/hostname
      tolerations:
        - key: "CriticalAddonsOnly"
          operator: "Exists"
      nodeSelector:
        kubernetes.io/os: linux
      containers:
      - name: coredns
        image: coredns/coredns:1.10.0
        imagePullPolicy: IfNotPresent
        resources:
          limits:
            memory: 300Mi
          requests:
            cpu: 100m
            memory: 70Mi
        args: [ "-conf", "/etc/coredns/Corefile" ]
        volumeMounts:
        - name: config-volume
          mountPath: /etc/coredns
          readOnly: true
        ports:
        - containerPort: 53
          name: dns
          protocol: UDP
        - containerPort: 53
          name: dns-tcp
          protocol: TCP
        - containerPort: 9153
          name: metrics
          protocol: TCP
        livenessProbe:
          httpGet:
            path: /health
            port: 8080
            scheme: HTTP
          initialDelaySeconds: 60
          timeoutSeconds: 5
          successThreshold: 1
          failureThreshold: 5
        readinessProbe:
          httpGet:
            path: /ready
            port: 8181
            scheme: HTTP
        securityContext:
          allowPrivilegeEscalation: false
          capabilities:
            add:
            - NET_BIND_SERVICE
            drop:
            - all
          readOnlyRootFilesystem: true
      dnsPolicy: Default
      volumes:
        - name: config-volume
          configMap:
            name: coredns
            items:
            - key: Corefile
              path: Corefile
---
apiVersion: v1
kind: Service
metadata:
  name: kube-dns
  namespace: kube-system
  annotations:
    prometheus.io/port: "9153"
    prometheus.io/scrape: "true"
  labels:
    k8s-app: kube-dns
    kubernetes.io/cluster-service: "true"
    addonmanager.kubernetes.io/mode: Reconcile
    kubernetes.io/name: "CoreDNS"
spec:
  selector:
    k8s-app: kube-dns
  clusterIP: 10.255.0.2
  ports:
  - name: dns
    port: 53
    protocol: UDP
  - name: dns-tcp
    port: 53
    protocol: TCP
  - name: metrics
    port: 9153
    protocol: TCP

6.4.3 安装 coredns

[root@k8s-master1 work]# kubectl apply -f coredns.yaml 

[root@k8s-master1 work]# kubectl get pods -A
NAMESPACE     NAME                                       READY   STATUS    RESTARTS   AGE
kube-system   calico-kube-controllers-798cc86c47-s8hb2   1/1     Running   0          54m
kube-system   calico-node-7n6hg                          1/1     Running   0          54m
kube-system   calico-node-7r6tt                          1/1     Running   0          54m
kube-system   calico-node-9sx5w                          1/1     Running   0          54m
kube-system   calico-node-fbbjq                          1/1     Running   0          54m
kube-system   calico-node-frf6p                          1/1     Running   0          54m
kube-system   coredns-65bc566d6f-f6qvh                   1/1     Running   0          53s

# 节点⻆⾊名字更改
[root@k8s-master1 work]# kubectl label nodes k8s-master1 node-role.kubernetes.io/master=master
[root@k8s-master1 work]# kubectl label nodes k8s-node1 node-role.kubernetes.io/work=work
node/k8s-node1 labeled
[root@k8s-master1 work]# kubectl label nodes k8s-node2 node-role.kubernetes.io/work=work
node/k8s-node2 labeled

[root@k8s-master1 work]# kubectl get nodes
NAME          STATUS   ROLES    AGE   VERSION
k8s-master1   Ready    master   17h   v1.25.6
k8s-master2   Ready    <none>   17h   v1.25.6
k8s-master3   Ready    <none>   17h   v1.25.6
k8s-node1     Ready    work     17h   v1.25.6
k8s-node2     Ready    work     17h   v1.25.6

6.4.5 测试 coredns 域名解析功能

[root@k8s-master1 work]# cat<<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: busybox
  namespace: default
spec:
  containers:
  - name: busybox
    image: busybox:1.28
    command:
    - sleep
    - "3600"
    imagePullPolicy: IfNotPresent
  restartPolicy: Always
EOF

# ⾸先查看 pod 是否安装成功
[root@k8s-master1 work]# kubectl get pods 
NAME      READY   STATUS    RESTARTS   AGE
busybox   1/1     Running   0          57s

# 通过下面可以看到能访问网络
[root@k8s-master1 work]# kubectl exec busybox -it -- sh
/ # ping www.baidu.com
PING www.baidu.com (182.61.200.6): 56 data bytes
64 bytes from 182.61.200.6: seq=0 ttl=127 time=84.916 ms
64 bytes from 182.61.200.6: seq=1 ttl=127 time=35.237 ms
64 bytes from 182.61.200.6: seq=2 ttl=127 time=43.888 ms
64 bytes from 182.61.200.6: seq=3 ttl=127 time=69.534 ms
64 bytes from 182.61.200.6: seq=4 ttl=127 time=33.419 ms
^C
--- www.baidu.com ping statistics ---
5 packets transmitted, 5 packets received, 0% packet loss
round-trip min/avg/max = 33.419/53.398/84.916 ms

# 10.255.0.2 就是我们 coreDNS 的 clusterIP,说明 coreDNS 配置好了。解析内部 Service 的名称,是通过 coreDNS 去解析的:
/ # nslookup kubernetes.default.svc.cluster.local
Server:    10.255.0.2
Address 1: 10.255.0.2 kube-dns.kube-system.svc.cluster.local

Name:      kubernetes.default.svc.cluster.local
Address 1: 10.255.0.1 kubernetes.default.svc.cluster.local

        注意:busybox 要用指定的 1.28 版本,不能用最新版本,最新版本,nslookup 会解析不到 dns 和 ip.

七、安装 keepalived+nginx 实现 k8s apiserver 高可用

7.1 keepalived+nginx 高可用介绍

7.1.1 什么是高可用?

        高可用 HA(High Availability)是分布式系统架构设计中必须考虑的因素之一,它通常是指,通过设计减少系统不能提供服务的时间。如果一个系统能够一直提供服务,那么这个可用性则是百分之百,但是天有不测风云。所以我们只能尽可能的去减少服务的故障。

7.1.2 解决的问题?

        在生产环境上很多时候是以 Nginx 做反向代理对外提供服务,但是一天 Nginx 难免遇见故障,如:服务器宕机。当 Nginx 宕机那么所有对外提供的接口都将导致无法访问。

        虽然我们无法保证服务器百分之百可用,但是也得想办法避免这种悲剧,今天我们使用keepalived 来实现 Nginx 的高可用。

7.1.3 双机热备方案

        这种方案是国内企业中最为普遍的一种高可用方案,双机热备其实就是指一台服务器在提供服务,另一台为某服务的备用状态,当一台服务器不可用另外一台就会顶替上去。

7.1.4 keepalived 是什么?

        Keepalived 软件起初是专为 LVS 负载均衡软件设计的,用来管理并监控 LVS 集群系统中各个服务节点的状态,后来又加入了可以实现高可用的 VRRP (Virtual Router Redundancy Protocol ,虚拟路由器冗余协议)功能。因此,Keepalived 除了能够管理 LVS 软件外,还可以作为其他服务(例如:Nginx、Haproxy、MySQL等)的高可用解决方案软件。

7.1.5 故障转移机制

        Keepalived 高可用服务之间的故障切换转移,是通过 VRRP 来实现的。在 Keepalived 服务正常工作时,主 Master 节点会不断地向备节点发送(多播的方式)心跳消息,用以告诉备 Backup 节点自己还活着,当主 Master 节点发生故障时,就无法发送心跳消息,备节点也就因此无法继续检测到来自主 Master 节点的心跳了,于是调用自身的接管程序,接管主 Master 节点的 IP 资源及服务。而当主 Master 节点恢复时,备 Backup 节点又会释放主节点故障时自身接管的 IP资源及服务,恢复到原来的备用角色。

7.2 安装 nginx 与 keeplived(所有 master 节点)

yum install -y nginx keepalived nginx-all-modules.noarch

7.3 nginx 配置(三个 master 节点的 nginx 配置一样)

cat /etc/nginx/nginx.conf
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;

include /usr/share/nginx/modules/*.conf;

events {
    worker_connections 1024;
}

# 四层负载均衡,为三台 Master apiserver 组件提供负载均衡
stream {

    log_format  main  '$remote_addr $upstream_addr - [$time_local] $status $upstream_bytes_sent';

    access_log  /var/log/nginx/k8s-access.log  main;

    upstream k8s-apiserver {
       server 192.168.78.150:6443;
       server 192.168.78.151:6443;
       server 192.168.78.152:6443;

    }
    
    server {
       listen 16443; # 由于 nginx 与 master 节点复用,这个监听端口不能是 6443,否则会冲突
       proxy_pass k8s-apiserver;
    }
}

http {
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile            on;
    tcp_nopush          on;
    tcp_nodelay         on;
    keepalive_timeout   65;
    types_hash_max_size 4096;

    include             /etc/nginx/mime.types;
    default_type        application/octet-stream;

    include /etc/nginx/conf.d/*.conf;

    server {
        listen       80;
        listen       [::]:80;
        server_name  _;
        root         /usr/share/nginx/html;

        include /etc/nginx/default.d/*.conf;

        error_page 404 /404.html;
        location = /404.html {
        }

        error_page 500 502 503 504 /50x.html;
        location = /50x.html {
        }
    }


}

7.4 配置 KeepAlived(所有 Master 节点)

主从配置不一致,需要注意。

7.4.1 Master1

[root@k8s-master1 work]# vim /etc/keepalived/keepalived.conf 
global_defs {
   notification_email {
     acassen@firewall.loc
     failover@firewall.loc
     sysadmin@firewall.loc
   }
   notification_email_from Alexandre.Cassen@firewall.loc
   smtp_server 127.0.0.1
   smtp_connect_timeout 30
   router_id NGINX_MASTER
}

vrrp_script check_nginx {
    script "/etc/keepalived/check_nginx.sh"    # 心跳执行的脚本,检测 nginx 是否启动
    interval 2                                 #(检测脚本执行的间隔,单位是秒)
    weight 2                                   # 权重
}

# vrrp 实例定义部分
vrrp_instance VI_1 {
    state MASTER         # 指定 keepalived 的角色,MASTER 为主,BACKUP 为备
    interface ens32      # 当前进行vrrp通讯的网络接口卡(当前centos的网卡) 用ifconfig查看你具体的网卡
    virtual_router_id 51 # VRRP 路由 ID 实例,每个实例是唯一的,主从要一致
    priority 100         # 优先级,优先级,数值越大,获取处理请求的优先级越高。备服务器设置 90
    advert_int 1         # 指定 VRRP 心跳包通告间隔时间,默认 1 秒
    # 授权访问
    authentication {
        auth_type PASS   # 设置验证类型和密码,MASTER 和 BACKUP 必须使用相同的密码才能正常通信
        auth_pass 1111
    }
    # 虚拟 IP
    virtual_ipaddress {
        192.168.78.155/24 # 定义虚拟 ip(VIP),可多设,每行一个
    }
    track_script {
        check_nginx      #(调用检测脚本)
    }
}

7.4.2 Master2

[root@k8s-master2 ~]# vim /etc/keepalived/keepalived.conf 
global_defs {
   notification_email {
     acassen@firewall.loc
     failover@firewall.loc
     sysadmin@firewall.loc
   }
   notification_email_from Alexandre.Cassen@firewall.loc
   smtp_server 127.0.0.1
   smtp_connect_timeout 30
   router_id NGINX_MASTER
}

vrrp_script check_nginx {
    script "/etc/keepalived/check_nginx.sh"    # 心跳执行的脚本,检测 nginx 是否启动
    interval 2                                 #(检测脚本执行的间隔,单位是秒)
    weight 2                                   # 权重
}

# vrrp 实例定义部分
vrrp_instance VI_1 {
    state BACKUP         # 指定 keepalived 的角色,MASTER 为主,BACKUP 为备
    interface ens32      # 当前进行vrrp通讯的网络接口卡(当前centos的网卡) 用ifconfig查看你具体的网卡
    virtual_router_id 51 # VRRP 路由 ID 实例,每个实例是唯一的,主从要一致
    priority 90         # 优先级,优先级,数值越大,获取处理请求的优先级越高。备服务器设置 90
    advert_int 1         # 指定 VRRP 心跳包通告间隔时间,默认 1 秒
    # 授权访问
    authentication {
        auth_type PASS   # 设置验证类型和密码,MASTER 和 BACKUP 必须使用相同的密码才能正常通信
        auth_pass 1111
    }
    # 虚拟 IP
    virtual_ipaddress {
        192.168.78.155/24 # 定义虚拟 ip(VIP),可多设,每行一个
    }
    track_script {
        check_nginx      #(调用检测脚本)
    }
}

7.4.3 Master3

[root@k8s-master3 ~]# vim /etc/keepalived/keepalived.conf 
global_defs {
   notification_email {
     acassen@firewall.loc
     failover@firewall.loc
     sysadmin@firewall.loc
   }
   notification_email_from Alexandre.Cassen@firewall.loc
   smtp_server 127.0.0.1
   smtp_connect_timeout 30
   router_id NGINX_MASTER
}

vrrp_script check_nginx {
    script "/etc/keepalived/check_nginx.sh"    # 心跳执行的脚本,检测 nginx 是否启动
    interval 2                                 #(检测脚本执行的间隔,单位是秒)
    weight 2                                   # 权重
}

# vrrp 实例定义部分
vrrp_instance VI_1 {
    state BACKUP         # 指定 keepalived 的角色,MASTER 为主,BACKUP 为备
    interface ens32      # 当前进行vrrp通讯的网络接口卡(当前centos的网卡) 用ifconfig查看你具体的网卡
    virtual_router_id 51 # VRRP 路由 ID 实例,每个实例是唯一的,主从要一致
    priority 90         # 优先级,优先级,数值越大,获取处理请求的优先级越高。备服务器设置 90
    advert_int 1         # 指定 VRRP 心跳包通告间隔时间,默认 1 秒
    # 授权访问
    authentication {
        auth_type PASS   # 设置验证类型和密码,MASTER 和 BACKUP 必须使用相同的密码才能正常通信
        auth_pass 1111
    }
    # 虚拟 IP
    virtual_ipaddress {
        192.168.78.155/24 # 定义虚拟 ip(VIP),可多设,每行一个
    }
    track_script {
        check_nginx      #(调用检测脚本)
    }
}

7.5 健康检查脚本(三个 master 执行)

vim /etc/keepalived/check_nginx.sh
#!/bin/bash
#1、判断 Nginx 是否存活
counter=`ps -C nginx --no-header | wc -l`
if [ $counter -eq 0 ]; then
    #2、如果不存活则尝试启动 Nginx
    systemctl start nginx
    sleep 2
    #3、等待 2 秒后再次获取一次 Nginx 状态
    counter=`ps -C nginx --no-header | wc -l`
    #4、再次进行判断,如 Nginx 还不存活则停止 Keepalived,让地址进行漂移
    if [ $counter -eq 0 ]; then
        systemctl stop keepalived
    fi
fi

# 授权
chmod +x /etc/keepalived/check_nginx.sh

7.6 启动 nginx 和 keepalived(所有 master 节点)

systemctl daemon-reload
systemctl enable --now nginx
systemctl enable --now keepalived

7.7 查看 vip 是否绑定成功

# 看到有 VIP 绑定到 ens32 ⽹卡上了
[root@k8s-master1 work]# ip a | grep ens32
2: ens32: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    inet 192.168.78.150/24 brd 192.168.78.255 scope global noprefixroute ens32
    inet 192.168.78.155/24 scope global secondary ens32

7.8 测试 keepalived

        先停掉 master1 上的 keepalived,然后 vip 会随机漂移到 master2 或 master3 上,因为它两的权重相同:

[root@k8s-master1 work]# systemctl stop keepalived.service 
[root@k8s-master1 work]# ip a | grep ens32
2: ens32: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    inet 192.168.78.150/24 brd 192.168.78.255 scope global noprefixroute ens32

[root@k8s-master2 ~]# ip a | grep ens32
2: ens32: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    inet 192.168.78.151/24 brd 192.168.78.255 scope global noprefixroute ens32

# vip 在 master 3 上:
[root@k8s-master3 ~]# ip addr | grep ens32
2: ens32: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    inet 192.168.78.152/24 brd 192.168.78.255 scope global noprefixroute ens32
    inet 192.168.78.155/24 scope global secondary ens32

# 重新启动 keepalive,vip 回来了
[root@k8s-master1 work]# systemctl start keepalived.service 
[root@k8s-master1 work]# ip a | grep ens32
2: ens32: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    inet 192.168.78.150/24 brd 192.168.78.255 scope global noprefixroute ens32
    inet 192.168.78.155/24 scope global secondary ens32

        目前所有的 Worker Node 组件连接都还是 master1 Node,如果不改为连接 VIP 走负载均衡器,那么 Master 还是单点故障。

        因此接下来就是要改所有 Worker Node(kubectl get node 命令查看到的节点)组件配置文件,由原来 192.168.78.150 修改为 192.168.78.155(VIP)。

在 node1 和 node2 执行:

[root@k8s-node1 ~]# ls /etc/kubernetes/
kubelet-bootstrap.kubeconfig  kubelet.json  kubelet.kubeconfig  kube-proxy.kubeconfig  kube-proxy.yaml  ssl

sed -i 's#192.168.78.150:6443#192.168.78.155:16443#' /etc/kubernetes/kubelet-bootstrap.kubeconfig

sed -i 's#192.168.78.150:6443#192.168.78.155:16443#' /etc/kubernetes/kubelet.json 

sed -i 's#192.168.78.150:6443#192.168.78.155:16443#' /etc/kubernetes/kubelet.kubeconfig 

sed -i 's#192.168.78.150:6443#192.168.78.155:16443#' /etc/kubernetes/kube-proxy.kubeconfig 

sed -i 's#192.168.78.150:6443#192.168.78.155:16443#' /etc/kubernetes/kube-proxy.yaml

# 重启服务即可
systemctl restart kubelet kube-proxy

这样 k8s-v1.25 高可用集群就安装好了!!!

上一篇文章:【云原生 | Kubernetes 实战】01、K8s-v1.25集群搭建和部署基于网页的 K8s 用户界面 Dashboard_containerd镜像加速_Stars.Sky的博客-CSDN博客

下一篇文章:2023 K8s 认证工程师 CKA 考题分析和题库练习(上)_Stars.Sky的博客-CSDN博客_k8s认证考试

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/345624.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

深入理解 Handler(java 层 + native 层)

文章目录回顾线程消息队列时怎样实现的消息是怎么传递的&#xff1f;Handle 的延迟消息是怎么处理的&#xff1f;IdleHandler 的原理主线程进入了 Looper 循环为什么没有 ANR&#xff1f;消息屏障是什么&#xff1f;回顾 之前学习过Handler相关的基础知识&#xff0c;今天再学…

ESP-IDF + Vscode ESP32 开发环境搭建以及开发入门

ESP-IDF Vscode ESP32 开发环境搭建以及开发入门 文章目录ESP-IDF Vscode ESP32 开发环境搭建以及开发入门1. 前言2. 下载开发工具3. 配置工具4. 创建工程5. 解决vscode找不到头文件&#xff0c;波浪线警告6. 添加自己的组件6.1 组件说明6.2 添加项目组件6.3 添加扩展组件7. …

Python进阶篇(一)-- Django快速上手

1 Django概述 Web框架&#xff0c;就是用于开发Web服务器端应用的基础设施&#xff0c;说得通俗一点就是一系列封装好的模块和工具。事实上&#xff0c;即便没有Web框架&#xff0c;我们仍然可以通过socket或CGI来开发Web服务器端应用&#xff0c;但是这样做的成本和代价在商业…

Stable Diffusion 1 - 初始跑通 文字生成图片

文章目录关于 Stable DiffusionLexica代码实现安装依赖库登陆 huggingface查看 huggingface token下载模型计算生成设置宽高测试迭代次数生成多列图片关于 Stable Diffusion A latent text-to-image diffusion model Stable Diffusion 是一个文本到图像的潜在扩散模型&#xff…

撕开市场缺口,认养一头牛“犟心”能给谁?

随着疫情防控政策优化&#xff0c;2023年以来中国消费力和投资活动均迎来复苏。其中&#xff0c;乳制品赛道受益于国内消费者健康消费理念的加强&#xff0c;呈现出稳步增长的势头。一方面&#xff0c;乳制品消费需求旺盛&#xff0c;市场未来可期。据中商研究院预计&#xff0…

【Hello Linux】Linux环境下写的第一个程序 -- 进度条

作者&#xff1a;小萌新 专栏&#xff1a;Linux 作者简介&#xff1a;大二学生 希望能和大家一起进步&#xff01; 本篇博客简介&#xff1a;写出Linux中的第一个小程序 进度条 进度条小程序行缓冲区概念\r 和 \n进度条代码和演示行缓冲区概念 我们首先用两段代码来感受下行缓…

结合ENVI和PIE Hyp讲述高光谱遥感信息处理技术,包括光谱恢复、光谱库建立、光谱特征提取、混合像元分解、图像分类及精度检验

大气温室气体浓度不断增加&#xff0c;导致气候变暖加剧&#xff0c;随之会引发一系列气象、生态和环境灾害。如何降低温室气体浓度和应对气候变化已成为全球关注的焦点。海洋是地球上最大的“碳库”,“蓝碳”即海洋活动以及海洋生物&#xff08;特别是红树林、盐沼和海草&…

05 OpenCV色彩空间处理

色彩空间&#xff08;Color Space&#xff09;是一种用于描述颜色的数学模型&#xff0c;它将颜色表示为多维向量或坐标&#xff0c;通常由三个或四个独立的分量来表示。不同的色彩空间在颜色的表示方式、可表达颜色的范围、计算速度和应用场景等方面存在差异&#xff0c;不同的…

ChatGPT写代码水平惊艳到我,很性感但有点危险

这几天属实是被ChatGPT刷屏了&#xff0c;十年寒窗无人问&#xff0c;一举成名天下知。不少人和ChatGPT对话后&#xff0c;都觉得自己像个傻逼。这位“最强懂哥”可以轻松应答各种问题&#xff0c;给出的答案不仅条理清晰&#xff0c;还会引用例子支撑观点。让它帮忙写程序&…

九、初识卷积

文章目录1、通过边缘检测认识卷积2、Padding3、Strid Convelution4、RGB图像的卷积THE END1、通过边缘检测认识卷积 \qquad在使用神经网络进行图像识别时&#xff0c;神经网络的前几层需要完成对图像的边缘检测任务&#xff0c;所谓的边缘检测就是让计算机识别出一张图片的垂直…

【智能计算数学】微积分

高数问题解决流程引例&#xff1a;回归回归引例&#xff1a;分类分类线性可分FLD线性不可分智能计算讨论范围下降法为什么要用下降法&#xff1f;- 解析解很难写出公式或很复杂难计算有哪些常用的下降法&#xff1f;- 梯度下降&高斯-牛顿法梯度下降&#xff08;Gradient De…

初步认识操作系统(Operator System)

操作系统一&#xff0c;冯诺依曼体系结构内存的重要作用二&#xff0c;操作系统的概念三&#xff0c;设计操作系统的目的三&#xff0c;操作系统在计算机体系中的定位四&#xff0c;操作系统是如何进行管理的一&#xff0c;冯诺依曼体系结构 在众多计算机相关的书籍中&#xff…

linux安装docker和Docker Compose

1、安装环境 此处在Centos7进行安装&#xff0c;可以使用以下命令查看CentOS版本 lsb_release -a 在 CentOS 7安装docker要求系统为64位、系统内核版本为 3.10 以上&#xff0c;可以使用以下命令查看 uname -r 2、用yum源安装 2.1 查看是否已安装docker列表 yum list inst…

Doom流量回放工具导致的测试环境服务接口无响应的排查过程

Doom流量回放工具导致的测试环境服务接口无响应的排查过程 现象描述&#xff1a; a)部分接口&#xff08;A组接口&#xff09;无响应 b)部分接口&#xff08;B组接口&#xff09;正常响应 c)还有一部分接口&#xff08;C组接口&#xff09;,场景1无响应&#xff0c;场景2正常响…

ChatGPT 桌面应用程序 for macOS, Linux, Windows v0.10

请访问原文链接&#xff1a;https://sysin.org/blog/chatgpt/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;www.sysin.org ChatGPT 是什么 ChatGPT 是 OpenAI 公司开发的一种基于对话优化的语言模型。用俗话说就是“人工智障”聊天机器…

三次握手-升级详解-注意问题

TCP建立连接的过程就是三次握手&#xff08;Three-way Handshake&#xff09;&#xff0c;在建立连接的过程实际上就是客户端和服务端之间总共发送三个数据包。进行三次握手主要是就是为了确认双方都能接收到数据包和发送数据包&#xff0c;而客户端和服务端都会指定自己的初始…

Tomcat构建

软件架构C/S:Client/Server.需要安装才能使用。B/S:Brower/Server。有浏览器就可以。资源分类动态资源&#xff1a;每个用户访问相同的资源后&#xff0c;得到的结果可能不一样&#xff0c;称为动态资源。动态资源被访问后&#xff0c;先转换为静态资源&#xff0c;再被浏览器解…

ESP-IDF: 基于计数型信号量的生产者和消费者模型代码

ESP-IDF: 基于计数型信号量的生产者和消费者模型代码 SemaphoreHandle_t freeBowl NULL;//初始状态有5个空碗 SemaphoreHandle_t Mantou NULL;//初始状态没有馒头&#xff0c;从零开始计数 int queue[5]; //用数组模拟空碗&#xff0c;对数组取余操作&#xff0c;模拟循环链…

如何利用 ESLint 规范 TypeScript 代码

ESLint 是一种 JavaScript linter&#xff0c;可以用来规范 JavaScript 或 TypeScript 代码&#xff0c;本文教你怎么在项目中快速把 ESLint 安排上。 前导 怎么写出优雅的代码是一个老生常谈的话题&#xff0c;这其中包含了太多内容可聊&#xff0c;但搞一套标准规范绝对是万…

opencv锁定鼠标定位

大家好&#xff0c;我是csdn的博主&#xff1a;lqj_本人 这是我的个人博客主页&#xff1a; lqj_本人的博客_CSDN博客-微信小程序,前端,python领域博主lqj_本人擅长微信小程序,前端,python,等方面的知识https://blog.csdn.net/lbcyllqj?spm1011.2415.3001.5343哔哩哔哩欢迎关注…