基于kubeadm构建K8S高可用集群:从理论到实践

本文提供了一份完整的、基于kubeadm工具构建生产级Kubernetes高可用集群的详细指南。内容涵盖从底层容器运行时技术原理(CNI、OCI、CRI)的解析,到集群规划、系统环境准备、内核优化,再到核心组件部署(Containerd、高可用API-Server负载均衡、kubeadm初始化)以及最终的集群验证。指南特别聚焦于Kubernetes 1.24+版本已弃用Docker-shim的背景下,如何部署并配置Containerd作为容器运行时,并采用Nginx+Keepalived方案实现控制平面的高可用。通过循序渐进的步骤和丰富的脚本示例,旨在帮助系统管理员和DevOps工程师构建一个稳定、可扩展的Kubernetes生产环境。

一、CNI,OCI,CRI技术词汇

1 CNI(Container Networker Interface)

CNI (Container Network Interface): 定义了容器网络插件的标准,K8s依靠遵循此规范的插件(如Flannel、Calico)实现Pod网络。

sh 复制代码
CNI全称为"Container Networker Interface"。主要是用于容器跨主机互联提供基础网络服务。

k8s本身并不提供容器跨主机互联,而是声明了CNI规范,凡事遵循了CNI规范都可以被k8s集群用作网络基础组件。

目前市面上比较流行且符合CNI标准的代表有: Flannel,Calico。

推荐阅读:
    https://kubernetes.io/zh-cn/docs/concepts/cluster-administration/addons
2 OCI(Open Container Initative)

OCI (Open Container Initiative): 制定了容器镜像和运行时标准,旨在推动容器格式的开放与标准化,其参考实现为runc。

sh 复制代码
  业内最早的容器运行时环境(Runtime)是LXC,起初Docker就是利用LXC做容器管理引擎,但是在创建容器用户空间时不在用LXC的模板现场安装生成容器,而是先通过一种镜像技术,把一个操作系统用户空间所要用到的所有组件事先准备编排好打包成一个文件,这个文件Docker称之为镜像文件。

  但后来觉得隔离性差,后来采用libcontainer组件,不过此时Docker已被CNCF挟持了,当然容器的话语权依旧归Docker公司,这并不是说CNCF组织没有能力定制Docker的标准,只不过他们真那样做就太欺负Docker公司了。

  后来Docker又转型到runC,所以说到目前为止,runC是Docker的独生子。

  随着LXC,LXD,Docker,Rkt等容器运行时环境各有不同,这时候容器运行时的确是有点多了,开放容器倡议(Open Container Initiative,简称"OCI")由多家公司共同成立的项目,并由linux基金会进行管理。   

  OCI(全称为"Open Container Initative")是建立围绕容器格式和运行时的开放式行业标准的明确目的的开放式的治理结构。OCI由Docker和其他容器行业的领导者于2015年6月建立,但在2015年12月18日才在GitHub上发布的"v0.1.1"。   
  
  所谓container runtime,主要负责的是容器的生命周期的管理。OCI目前包含两个规范:运行时规范(runtime-spec)和映像规范(image-spec)。
    (1)OCI的runtime spec标准中对于容器的状态描述,创建,删除,查看等操作进行了定义,以及容器运行时如何运行指定文件系统上的包;
    (2)OCI的image-spec标准在定义了如何创建一个OCI运行时可运行的文件系统上的包;

  Docker公司将容器镜像格式和runtime(也就是runc)都捐献给了Open Container Initiative组织。runc是对于OCI标准的一个参考实现,是一个可以用于创建和运行容器的CLI(command-line interface)工具。

  runc直接与容器所依赖的cgroup/linux kernel等进行交互,负责为容器配置cgroup/namespace等启动容器所需的环境,创建启动容器的相关进程。
  
  为了兼容OCI标准,docker也做了架构调整。2016年12月14日,Docker公司宣布将将容器运行时相关的程序从docker daemon剥离出来,形成了containerd。而早在同年的3月份,Docker 1.11的Docker Engine里就包含了containerd。

  Containerd向docker提供运行容器的API,二者通过grpc进行交互。containerd最后会通过runc来实际运行容器
3 CRI(全称为"Container Runtime Interface")

CRI (Container Runtime Interface): Kubernetes定义的容器运行时接口,用于抽象和接入不同的容器引擎(如Containerd、CRI-O),是K8s弃用Docker后对接运行时的关键。

sh 复制代码
 kubernetes在初期版本里,就对多个容器引擎做了兼容,因此可以使用docker、rkt对容器进行管理。以docker为例,kubelet中会启动一个docker manager,通过直接调用docker的api进行容器的创建等操作。

  在kubernetes中,pod是由一组进行了资源限制的,在隔离环境中的容器组成。而这个隔离环境,称之为PodSandbox。

  据说是Google(对K8S源码贡献第一)和RedHat(对K8S源码贡献第二)这两家公司有意将Docker边缘化,因此大力扶持由CoreOS公司2014年开源的轻量级rkt容器工具引擎。

  在Kubernetes早期版本,主要是支持docker和rkt两种容器引擎,这需要Kubernetes官方做大量的工作来兼容这两种容器,而兼容会带来很多维护性工作。

  于是在OCI提出一年后,大概在2016年12月19日,即在k8s 1.5版本之后,kubernetes推出了自己的运行时接口Container Runtime Interface(下面简称"CRI")。

  凡是支持CRI皆可作为K8S的底层运行时,CRI接口的推出,隔离了各个容器引擎之间的差异,而通过统一的接口与各个容器引擎之间进行互动。

  与OCI不同,CRI与kubernetes的概念更加贴合,并紧密绑定。CRI不仅定义了容器的生命周期的管理,还引入了k8s中pod的概念,并定义了管理pod的生命周期。

  但Docker本身并未实现CRI,因此使用临时解决方案: 其中kubelet是通过CRI接口,调用docker-shim,并进一步调用docker api实现的。但这增大了K8S官方的工作负担,于是在2020年12月宣布将来会弃用docker-shim。

  如上文所述,docker独立出来了containerd。kubernetes也顺应潮流,孵化了cri-containerd项目,用以将containerd接入到cri的标准中。

  为了进一步与oci进行兼容,kubernetes还孵化了cri-o,成为了架设在cri和oci之间的一座桥梁。通过这种方式,可以方便更多符合oci标准的容器运行时,接入kubernetes进行集成使用。

  可以预见到,通过cri-o,kubernetes在使用的兼容性和广泛性上将会得到进一步加强。

  大概在2017年左右,Docker将自身的容器运行时(即"containerd")捐给了CNCF组织(该组织维护的是Kubernetes开源产品)。同年,Docker的网络组建(libnetwork)增加了CNI的支持,同时实现基于IPVS的SERVICE负载均衡。

  来自谷歌、Docker、IBM、中兴通讯和ZJU的工程师们致力于为containerd实现CRI。该项目名为cri containerd,其特性在2017年9月25日发布了完整的v1.0.0-alpha.0版本。

  在2018年5月24日,Kubernetes GA版本正式集成了cri containerd架构。使用cri containerd,用户可以运行Kubernetes集群,使用containerd作为底层运行时,而无需安装Docker。

  2019年8月16日,CNCF组织正式将rkt归档,结束了rkt容器的在Kubernetes的生命周期,在此之前,2018年4月16日是发布的最新rkt容器工具。

二、基础环境准备

1 集群规划
主机名 IP地址 角色
master241 10.0.0.241 control plane
master242 10.0.0.242 control plane
master243 10.0.0.243 control plane
node51 10.0.0.51 worker
node52 10.0.0.52 worker
node53 10.0.0.53 worker
master-lb 10.0.0.240 keepalived VIP
sh 复制代码
cat >> /etc/hosts <<'EOF'
10.0.0.241     master241
10.0.0.242     master242
10.0.0.243     master243
10.0.0.51      node51
10.0.0.52      node52
10.0.0.53      node53
10.0.0.240     master-lb
EOF

参考链接:
    https://kubernetes.io/zh/docs/setup/production-environment/tools/kubeadm/ha-topology/
2 禁用不必要的服务(所有节点)
sh 复制代码
(1)禁用防火墙,网络管理,邮箱
systemctl disable  --now firewalld NetworkManager postfix

(2)禁用selinux
sed -i 's/^SELINUX=enforcing$/SELINUX=disabled/' /etc/selinux/config 
grep ^SELINUX= /etc/selinux/config

(3)禁用swap分区
swapoff -a && sysctl -w vm.swappiness=0 
sed -ri '/^[^#]*swap/s@^@#@' /etc/fstab
grep swap /etc/fstab
3 master241配置免密钥登录集群并配置同步脚本
sh 复制代码
master241节点免密钥登录集群节点,安装过程中生成配置文件和证书均在master241上操作,集群管理也在master241上操作。

阿里云或者AWS上需要单独一台kubectl服务器。密钥配置如下:

    (1)配置批量免密钥登录
cat > password_free_login.sh <<'EOF'
#!/bin/bash
# auther: liux

# 创建密钥对
ssh-keygen -t rsa -P "" -f /root/.ssh/id_rsa -q

# 声明你服务器密码,建议所有节点的密码均一致,否则该脚本需要再次进行优化
export mypasswd=12366

# 定义主机列表
k8s_host_list=(master241 master242 master243 node51 node52 node53)

# 配置免密登录,利用expect工具免交互输入
for i in ${k8s_host_list[@]};do
expect -c "
spawn ssh-copy-id -i /root/.ssh/id_rsa.pub root@$i
  expect {
    \"*yes/no*\" {send \"yes\r\"; exp_continue}
    \"*password*\" {send \"$mypasswd\r\"; exp_continue}
  }"
done
EOF
sh password_free_login.sh

    (2)编写同步脚本
cat > /usr/local/sbin/data_rsync.sh <<'EOF'
#!/bin/bash
# Auther: liux

if  [ $# -ne 1 ];then
   echo "Usage: $0 /path/to/file(绝对路径)"
   exit
fi 

if [ ! -e $1 ];then
    echo "[ $1 ] dir or file not find!"
    exit
fi

fullpath=`dirname $1`

basename=`basename $1`

cd $fullpath

k8s_host_list=(master241 master242 master243 node51 node52 node53)

for host in ${k8s_host_list[@]};do
  tput setaf 2
    echo ===== rsyncing ${host}: $basename =====
    tput setaf 7
    rsync -az $basename  `whoami`@${host}:$fullpath
    if [ $? -eq 0 ];then
      echo "命令执行成功!"
    fi
done
EOF
chmod +x /usr/local/sbin/data_rsync.sh
4 Linux基础优化(所有节点)
sh 复制代码
(1)修改sshd服务优化
sed -ri  's@^#UseDNS yes@UseDNS no@g' /etc/ssh/sshd_config
sed -ri 's#^GSSAPIAuthentication yes#GSSAPIAuthentication no#g' /etc/ssh/sshd_config
grep ^UseDNS /etc/ssh/sshd_config 
grep ^GSSAPIAuthentication  /etc/ssh/sshd_config

(2)修改文件打开数量的限制(退出当前会话立即生效)
cat > /etc/security/limits.d/k8s.conf <<'EOF'
*       soft    nofile     65535
*       hard    nofile    131070
EOF
ulimit -Sn
ulimit -Hn

(3)修改终端颜色
cat <<EOF >>  ~/.bashrc 
PS1='[\[\e[34;1m\]\u@\[\e[0m\]\[\e[32;1m\]\H\[\e[0m\]\[\e[31;1m\] \W\[\e[0m\]]# '
EOF
source ~/.bashrc 

(4)所有节点配置模块自动加载,此步骤不做的话(kubeadm init时会直接失败!)
modprobe br_netfilter
modprobe ip_conntrack
cat >>/etc/rc.sysinit<<EOF
#!/bin/bash
for file in /etc/sysconfig/modules/*.modules ; do
[ -x $file ] && $file
done
EOF
echo "modprobe br_netfilter" >/etc/sysconfig/modules/br_netfilter.modules
echo "modprobe ip_conntrack" >/etc/sysconfig/modules/ip_conntrack.modules
chmod 755 /etc/sysconfig/modules/br_netfilter.modules
chmod 755 /etc/sysconfig/modules/ip_conntrack.modules
lsmod | grep br_netfilter

(5)所有节点配置集群时间同步
基于ntpdate配合crontab实现集群同步:
    (1)手动同步时区和时间
yum -y install ntpdate     
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
echo 'Asia/Shanghai' >/etc/timezone
ntpdate ntp.aliyun.com

    (2)定期任务同步("crontab -e")
*/5 * * * * /usr/sbin/ntpdate ntp.aliyun.com


基于chronyd守护进程实现集群时间同步:
    (1)手动同步时区和时间
\cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime 

    (2)安装服务chrony
#master节点开启下载软件包缓存
[root@master231 ~]# vim /etc/yum.conf 
...
keepcache=1
[root@master241 ~]# yum -y install ntpdate chrony
[root@master241 ~]# mkdir rpm && find /var/cache/yum/ -name '*.rpm' |xargs mv -t rpm
[root@master241 ~]# tar zcvf rpm.tar.gz rpm
#同步到其他节点,安装chrony服务,开启发送键到所有会话
[root@master241 ~]# data_rsync.sh rpm.tar.gz 
[root@master241 ~]# tar xf rpm.tar.gz 
[root@master241 ~]# yum -y localinstall rpm/*.rpm


    (3)修改配置文件
vim /etc/chrony.conf 
...
server ntp.aliyun.com iburst
server ntp1.aliyun.com iburst
server ntp2.aliyun.com iburst
server ntp3.aliyun.com iburst
server ntp4.aliyun.com iburst
server ntp5.aliyun.com iburst
    
    (4)启动服务
systemctl enable --now chronyd  

    (5)查看服务状态
systemctl status chronyd
5 配置软件源并安装集群常用软件(所有节点)
sh 复制代码
(1)配置阿里源
curl -s -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo

(2)配置docker软件源(可跳过,因为k8s 1.24已经弃用docker-shim,因此也不需要部署docker啦)
curl -o /etc/yum.repos.d/docker-ce.repo https://download.docker.com/linux/centos/docker-ce.repo
sed -i 's+download.docker.com+mirrors.tuna.tsinghua.edu.cn/docker-ce+' /etc/yum.repos.d/docker-ce.repo

(3)配置K8S软件源
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
repo_gpgcheck=0
EOF

(4)安装集群常用软件
[root@master241 ~]# yum -y install expect wget jq psmisc vim net-tools telnet yum-utils device-mapper-persistent-data lvm2 git ntpdate chrony bind-utils rsync
[root@master241 ~]# find /var/cache/yum/ -name '*.rpm' |xargs mv -t rpm
[root@master241 ~]# tar zcvf rpm.tar.gz rpm
#同步到其他节点,安装chrony服务
[root@master241 ~]# data_rsync.sh rpm.tar.gz 
#开启发送键到所有会话
[root@master241 ~]# tar xf rpm.tar.gz &&  yum -y localinstall rpm/*.rpm

(5)下载配置文件及脚本
https://gitee.com/liuxing88/linux-cloud_-native-master.git
6 所有节点的Centos 7.9系统内核升级
sh 复制代码
    (1)所有节点升级系统并重启,此处升级建议排除内核的升级,我们后续会单独对内核手动升级,若不跳过默认安装最新版本的内核(此步可跳过)
yum -y update --exclude=kernel* && reboot

    (2)CentOS7需要升级内核版本为4.18+
[root@master241 ~]# data_rsync.sh linux-cloud_-native-master
[root@master241 ~]# cd linux-cloud_-native-master/kernel/4.19.12/ && yum -y localinstall *.rpm


    (3)更改内核的启动顺序
grub2-set-default 0 && grub2-mkconfig -o /etc/grub2.cfg && reboot

    (4)检查默认的内核版本
grubby --default-kernel

    (5)检查当前正在使用的内核版本
uname -r

温馨提示:   
    (1)大家可以去官网自行下载"https://www.kernel.org/"内核文件手动升级哈,当然,也可以使用我提供的rpm包哟。
    (2)截止2022年6月22日,我下载的rpm版本最新的是5.18,但是由于该版本仅仅迭代了2个版本,此处我选择迭代较多的5.17版本进行部署,这里给出的rpm包下载地址为"http://mirror.mat.mines-paristech.fr/elrepo/kernel/el7/x86_64/RPMS/"。
7 所有节点安装ipvsadm以实现kube-proxy的负载均衡
sh 复制代码
(1)安装ipvsadm等相关工具
yum -y install ipvsadm ipset sysstat conntrack libseccomp 
[root@master241 ~]# mkdir rpm && find /var/cache/yum/ -name '*.rpm' |xargs mv -t rpm
[root@master241 ~]# tar zcvf rpm.tar.gz rpm
#同步到其他节点,安装chrony服务
[root@master241 ~]# data_rsync.sh rpm.tar.gz 
#开启发送键到所有会话
[root@master241 ~]# tar xf rpm.tar.gz &&  yum -y localinstall rpm/*.rpm
(2)创建要开机自动加载的模块配置文件
cat > /etc/modules-load.d/ipvs.conf << 'EOF'
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
ip_vs_sh
nf_conntrack
ip_tables
ip_set
xt_set
ipt_set
ipt_rpfilter
ipt_REJECT
ipip
EOF

(3)将"systemd-modules-load"服务设置为开机自启动
systemctl enable --now systemd-modules-load 
systemctl status systemd-modules-load

(4)启动模块
lsmod | grep --color=auto -e ip_vs -e nf_conntrack
8 所有节点修改Linux内核参数调优
sh 复制代码
(1)所有节点修改Linux内核参数调优
cat > /etc/sysctl.d/k8s.conf <<'EOF'
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
fs.may_detach_mounts = 1
vm.overcommit_memory=1
vm.panic_on_oom=0
fs.inotify.max_user_watches=89100
fs.file-max=52706963
fs.nr_open=52706963
net.netfilter.nf_conntrack_max=2310720
net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_keepalive_probes = 3
net.ipv4.tcp_keepalive_intvl =15
net.ipv4.tcp_max_tw_buckets = 36000
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_max_orphans = 327680
net.ipv4.tcp_orphan_retries = 3
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 16384
net.ipv4.ip_conntrack_max = 65536
net.ipv4.tcp_max_syn_backlog = 16384
net.ipv4.tcp_timestamps = 0
net.core.somaxconn = 16384
EOF
sysctl --system

(2)重启虚拟机
reboot

(3)拍快照
如果所有节点都可以正常重启,说明我们的配置是正确的!接下来就是拍快照。

三、单独部署containerd(所有节点)

1 升级libseccomp版本
sh 复制代码
在centos7中yum下载libseccomp的版本是2.3的,版本不满足我们最新containerd的需求。
[root@master242 ~]# rpm -qa | grep libseccomp
libseccomp-2.3.1-4.el7.x86_64


在安装containerd前,我们需要优先升级libseccomp,需要下载2.4以上的版本即可,我这里部署2.5.1版本。
    (1)卸载旧的containerd
rpm -e libseccomp-2.3.1-4.el7.x86_64 --nodeps

    (2)下载libseccomp-2.5.1版本的软件包(此次可省略,安装包里有)
wget http://rpmfind.net/linux/centos/8-stream/BaseOS/x86_64/os/Packages/libseccomp-2.5.1-1.el8.x86_64.rpm

    (3)安装libseccomp-2.5.1软件包
[root@master241 ~]# cd linux-cloud_-native-master/containerd/ && rpm -ivh libseccomp-2.5.1-1.el8.x86_64.rpm


    (4)检查安装的版本
rpm -qa | grep libseccomp
2 安装containerd组件
sh 复制代码
 (1)下载containerd工具包
wget https://github.com/containerd/containerd/releases/download/v1.6.4/cri-containerd-cni-1.6.4-linux-amd64.tar.gz
wget https://d.frps.cn/file/kubernetes/containerd/cri-containerd-cni-1.6.4-linux-amd64.tar.gz

    (2)解压软件包(此处我们直接让它给我们对应的目录给替换掉)
tar zxvf cri-containerd-cni-1.6.4-linux-amd64.tar.gz -C /  
    
软件包说明:
    containerd-1.6.1-linux-amd64.tar.gz 
        只包含containerd
    cri-containerd-cni-1.6.4-linux-amd64.tar.gz 
        包含containerd以及cri runc等相关工具包,建议下载本包。
        换句话说,cri-containerd-cni会将我们整个containerd相关的依赖都进行下载下来。
 
温馨提示:
    若有节点不安装containerd,则集群会安装失败的!
3 配置Containerd
sh 复制代码
 (1)创建配置文件目录
mkdir -pv /etc/containerd 

    (2)生成默认配置文件
containerd config default > /etc/containerd/config.toml

温馨提示:
    配置文件的默认路径位于/etc/containerd/config.toml,我们可以使用"--config,-c"可以在启动守护程序时更改此路径。
4 替换默认pause镜像地址
sh 复制代码
sed -i 's/k8s.gcr.io/registry.cn-beijing.aliyuncs.com\/abcdocker/' /etc/containerd/config.toml 
grep sandbox_image /etc/containerd/config.toml

温馨提示:
    所有节点更换默认镜像地址,我这里使用阿里云地址。
5 配置systemd作为容器的cgroup driver
sh 复制代码
sed -i 's/SystemdCgroup \= false/SystemdCgroup \= true/' /etc/containerd/config.toml
grep SystemdCgroup /etc/containerd/config.toml

温馨提示:
    所有节点配置systemd作为容器的cgroup driver。
6 配置containerd开机自启动
sh 复制代码
    (1)启动containerd服务并配置开机自启动
systemctl enable --now containerd 

    (2)查看containerd状态
systemctl status containerd 

    (3)查看containerd的版本
ctr version
7 containerd的基础操作
sh 复制代码
具体的使用方法参考帮助信息
ctr -h
containerd官方操作地址:
    https://github.com/containerd/containerd/blob/main/docs/getting-started.md

四、API-Server高可用组件部署

nginx代理后端3台apiserver,所以需要在每台apiserver中安装nginx。keeplived起到vip的作用

1 部署nginx环境(所有master节点)
01 编译安装nginx
sh 复制代码
为了方便后面扩展插件,我这里使用编译安装nginx。

(1)所有的master节点创建运行nginx的用户
useradd nginx -s /sbin/nologin -M

(2)安装依赖
yum -y install pcre pcre-devel openssl openssl-devel gcc gcc-c++ automake autoconf libtool make
[root@master241 ~]# rm -rf rpm*
[root@master241 ~]# mkdir rpm && find /var/cache/yum/ -name '*.rpm' |xargs mv -t rpm
[root@master241 ~]# tar zcvf rpm.tar.gz rpm
#同步到其他节点,安装chrony服务
[root@master241 ~]# data_rsync.sh rpm.tar.gz 
#开启发送键到所有会话
[root@master241 ~]# tar xf rpm.tar.gz &&  yum -y localinstall rpm/*.rpm

(3)下载nginx软件包
wget http://nginx.org/download/nginx-1.21.6.tar.gz

(4)解压软件包
cd linux-cloud_-native-master/nginx+keepalived/
tar xf nginx-1.21.6.tar.gz

(5配置nginx
cd nginx-1.21.6
./configure --prefix=/usr/local/nginx/ \
            --with-pcre \
            --with-http_ssl_module \
            --with-http_stub_status_module \
            --with-stream \
            --with-http_stub_status_module \
            --with-http_gzip_static_module

(6)编译并安装nginx
make -j 4 &&  make install 

(7)使用systemctl管理,并设置开机启动
cat >/usr/lib/systemd/system/nginx.service <<'EOF'
[Unit]
Description=The nginx HTTP and reverse proxy server
After=network.target sshd-keygen.service

[Service]
Type=forking
EnvironmentFile=/etc/sysconfig/sshd
ExecStartPre=/usr/local/nginx/sbin/nginx -t -c /usr/local/nginx/conf/nginx.conf
ExecStart=/usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf
ExecReload=/usr/local/nginx/sbin/nginx -s reload
ExecStop=/usr/local/nginx/sbin/nginx -s stop
Restart=on-failure
RestartSec=42s

[Install]
WantedBy=multi-user.target
EOF

(8)配置nginx的开机自启动
systemctl enable --now nginx 

(9)检查nginx服务是否启动
systemctl status nginx 
ps -ef|grep nginx

(10)同步nginx软件包和脚本到集群的其他master节点
data_rsync.sh /usr/local/nginx/
data_rsync.sh /usr/lib/systemd/system/nginx.service
02 修改nginx的配置文件
sh 复制代码
(1)创建nginx配置文件
cat > /usr/local/nginx/conf/nginx.conf <<'EOF'
user nginx nginx;
worker_processes auto;

events {
    worker_connections  20240;
    use epoll;
}

error_log /var/log/nginx_error.log info;

stream {
    upstream kube-servers {
        hash $remote_addr consistent;
        
        server master241:6443 weight=5 max_fails=1 fail_timeout=3s;
        server master242:6443 weight=5 max_fails=1 fail_timeout=3s;
        server master243:6443 weight=5 max_fails=1 fail_timeout=3s;
    }
    server {
        listen 8443 reuseport;
        proxy_connect_timeout 3s;
        proxy_timeout 3000s;
        proxy_pass kube-servers;
    }
}
EOF

    (2)同步nginx的配置文件到其他master节点
data_rsync.sh /usr/local/nginx/conf/nginx.conf

    (3)其他master节点启动nginx服务
systemctl enable --now nginx 

温馨提示:
    (1)将nginx同步到其他节点后别忘记设置开机自启动哈。
    (2)启动服务时别忘记在别的节点创建nginx用户哟;
2 部署keepalived
01 安装keepalived
sh 复制代码
yum  -y install  keepalived
02 修改keepalive的配置文件
sh 复制代码
  (1)编写配置文件,各个master节点需要执行修改router_id和mcast_src_ip的值即可。
cat > /etc/keepalived/keepalived.conf <<EOF
! Configuration File for keepalived
global_defs {
   router_id 10.0.0.243
}
vrrp_script chk_nginx {
    script "/etc/keepalived/check_port.sh 8443"
    interval 2
    weight -20
}
vrrp_instance VI_1 {
    state MASTER
    interface eth0
    virtual_router_id 251
    priority 100
    advert_int 1
    mcast_src_ip 10.0.0.243
    nopreempt
    authentication {
        auth_type PASS
        auth_pass 11111111
    }
    track_script {
         chk_nginx
    }
    virtual_ipaddress {
        10.0.0.240
    }
}
EOF

    (2)各节点编写健康检查脚本
cat > /etc/keepalived/check_port.sh  <<EOF
iCHK_PORT=$1
if [ -n "$CHK_PORT" ];then
    PORT_PROCESS=\`ss -lt|grep $CHK_PORT|wc -l\`
    if [ $PORT_PROCESS -eq 0 ];then
        echo "Port $CHK_PORT Is Not Used,End."
        exit 1
    fi
else
    echo "Check Port Cant Be Empty!"
fi
EOF

    (3)启动keepalived
systemctl enable --now keepalived
 
 
 
温馨提示:
    router_id:
        节点ip,master每个节点配置自己的IP
    mcast_src_ip:
        节点IP,master每个节点配置自己的IP
    virtual_ipaddress:
        虚拟IP,即VIP。
        
        
        
        
待参考检测脚本
cat > /etc/keepalived/check_apiserver.sh <<'EOF'
#!/bin/bash
err=0
for k in $(seq 1 3)
do
    check_code=$(pgrep haproxy)
    if [[ $check_code == "" ]]; then
        err=$(expr $err + 1)
        sleep 1
        continue
    else
        err=0
        break
    fi
done

if [[ $err != "0" ]]; then
    echo "systemctl stop keepalived"
    /usr/bin/systemctl stop keepalived
    exit 1
else
    exit 0
fi
EOF
03 测试VIP是否正常
sh 复制代码
[root@master243 ~]# systemctl stop keepalived.service 
[root@master242 ]# ip a
[root@master243 ~]# ping master-lb
可以ping通咱们的负载均衡器!
可以尝试停止keepalived节点,观察ping是否会中断,我测试发现会有短暂的延迟哟~

五、kubeadm组件初始化K8S集群

1 安装kubeadm(master节点)
sh 复制代码
    (1)master241节点安装kubeadm和master相关依赖组建
yum install -y kubelet-1.24.1 kubeadm-1.24.1 kubectl-1.24.1
[root@master241 ~]# rm -rf rpm*
[root@master241 ~]# mkdir rpm && find /var/cache/yum/ -name '*.rpm' |xargs mv -t rpm
[root@master241 ~]# tar zcvf rpm.tar.gz rpm
#同步到其他节点,安装chrony服务
[root@master241 ~]# data_rsync.sh rpm.tar.gz 
#开启发送键到所有会话
[root@master241 ~]# tar xf rpm.tar.gz &&  yum -y localinstall rpm/*.rpm
    (2)所有master节点kubelet设置成开机启动
systemctl enable --now kubelet 
systemctl status kubelet

    (3)所有master节点检查kubectl工具版本号
[root@master241 ~]# kubectl version --client --output=yaml
clientVersion:
  buildDate: "2022-05-24T12:26:19Z"
  compiler: gc
  gitCommit: 3ddd0f45aa91e2f30c70734b175631bec5b5825a
  gitTreeState: clean
  gitVersion: v1.24.1
  goVersion: go1.18.2
  major: "1"
  minor: "24"
  platform: linux/amd64
kustomizeVersion: v4.5.4
2 配置kubeadm文件
sh 复制代码
    (1)在master241节点上配置打印init默认配置信息
kubeadm config print init-defaults > kubeadm-init.yaml

    (2)根据默认的配置格式进行自定义修改即可
cat > kubeadm-init.yaml <<'EOF'
apiVersion: kubeadm.k8s.io/v1beta3
bootstrapTokens:
- groups:
  - system:bootstrappers:kubeadm:default-node-token
  token: abcdef.0123456789abcdef
  ttl: 24h0m0s
  usages:
  - signing
  - authentication
kind: InitConfiguration
localAPIEndpoint:
  # master241的IP地址
  advertiseAddress: 10.0.0.241
  bindPort: 6443
nodeRegistration:
  criSocket: unix:///var/run/containerd/containerd.sock
  imagePullPolicy: IfNotPresent
  name: master241
  taints: null
---
apiServer:
  timeoutForControlPlane: 4m0s
apiVersion: kubeadm.k8s.io/v1beta3
certificatesDir: /etc/kubernetes/pki
clusterName: kubernetes
controllerManager: {}
dns: {}
etcd:
  local:
    dataDir: /var/lib/etcd
# imageRepository: k8s.gcr.io
imageRepository: registry.aliyuncs.com/google_containers
kind: ClusterConfiguration
kubernetesVersion: 1.24.1
controlPlaneEndpoint: master-lb:8443
networking:
  dnsDomain: cluster.local
  serviceSubnet: 10.96.0.0/12
  podSubnet: 10.244.0.0/16
scheduler: {}
---
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
# kube-proxy 模式
mode: ipvs
---
apiVersion: kubelet.config.k8s.io/v1beta1
authentication:
  anonymous:
    enabled: false
  webhook:
    cacheTTL: 0s
    enabled: true
  x509:
    clientCAFile: /etc/kubernetes/pki/ca.crt
authorization:
  mode: Webhook
  webhook:
    cacheAuthorizedTTL: 0s
    cacheUnauthorizedTTL: 0s
clusterDNS:
- 10.96.0.10
clusterDomain: cluster.local
cpuManagerReconcilePeriod: 0s
evictionPressureTransitionPeriod: 0s
fileCheckFrequency: 0s
healthzBindAddress: 127.0.0.1
healthzPort: 10248
httpCheckFrequency: 0s
imageMinimumGCAge: 0s
kind: KubeletConfiguration
# 配置 cgroup driver
cgroupDriver: systemd
logging: {}
memorySwap: {}
nodeStatusReportFrequency: 0s
nodeStatusUpdateFrequency: 0s
rotateCertificates: true
runtimeRequestTimeout: 0s
shutdownGracePeriod: 0s
shutdownGracePeriodCriticalPods: 0s
staticPodPath: /etc/kubernetes/manifests
streamingConnectionIdleTimeout: 0s
syncFrequency: 0s
volumeStatsAggPeriod: 0s
EOF
    
    
    (3)检查配置文件是否有错误
[root@master241 ~]# kubeadm init --config kubeadm-init.yaml --dry-run
...
You can now join any number of control-plane nodes by copying certificate authorities
and service account keys on each node and then running the following as root:

  kubeadm join master-lb:8443 --token abcdef.0123456789abcdef \
	--discovery-token-ca-cert-hash sha256:b5f46777e087b18d06d5d244ea127248887c7c959444df6dd38e8d4ec122a17f \
	--control-plane 

Then you can join any number of worker nodes by running the following on each as root:

kubeadm join master-lb:8443 --token abcdef.0123456789abcdef \
	--discovery-token-ca-cert-hash sha256:b5f46777e087b18d06d5d244ea127248887c7c959444df6dd38e8d4ec122a17f 
说明配置成功

温馨提示:
    (1)创建默认的kubeadm-config.yaml文件:
kubeadm config print init-defaults  > kubeadm-config.yaml

    (2)生成KubeletConfiguration示例文件 
kubeadm config print init-defaults --component-configs KubeletConfiguration

    (3)生成KubeProxyConfiguration示例文件 
kubeadm config print init-defaults --component-configs KubeProxyConfiguration

    (4)如果kubeadm-config.yaml文件的版本较低,可以尝试更新哟
kubeadm config migrate --old-config kubeadm-config.yaml --new-config new.yaml

    (5)kubeadm和etcdadm
虽然kubeadm作为etcd节点的管理工具,但请注意kubeadm不打算支持此类节点的证书轮换或升级。
长期计划是使用etcdadm来工具来进行管理。

参考链接:
    https://kubernetes.io/docs/reference/config-api/kubeadm-config.v1beta3/
3 预先拉取镜像
sh 复制代码
检查网络的连通性,不是拉取镜像
[root@master241 ~]# kubeadm config images list --config kubeadm-init.yaml
[root@master241 ~]# ctr ns ls
NAME LABELS 
[root@master241 ~]# ctr i ls
REF TYPE DIGEST SIZE PLATFORMS LABELS 
4 基于kubeadm配置文件初始化集群
sh 复制代码
官方基于配置文件给出的初始化方式,参考链接:
    https://kubernetes.io/zh/docs/setup/production-environment/tools/kubeadm/high-availability/
    
官方的初始化方式我们在部署单点的master中已经使用过了,本次我打算使用上一步生成的kubeadm-config.yaml文件来初始化:
    kubeadm init --config kubeadm-init.yaml  --upload-certs
    
注意:
    若初始化报错,可以重置集群,解决报错之后,重新初始化
    kubeadm reset
    kubeadm init --config kubeadm-init.yaml  --upload-certs
5 查看本地镜像和容器信息
sh 复制代码
ctr ns ls
    查看镜像的名称空间。
ctr -n k8s.io i ls -q
    查看k8s.io名称空间的镜像信息。
    
ctr -n k8s.io c ls
    查看k8s.io名称空间的容器信息。
6 复制kubectl的kubeconfig
sh 复制代码
    (1)复制kubectl的kubeconfig,便于管理集群
  mkdir -p $HOME/.kube
  sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  sudo chown $(id -u):$(id -g) $HOME/.kube/config

    (2)查看configmap资源
kubectl -n kube-system get cm kubeadm-config -o yaml

    (3)查看node资源
kubectl get nodes

六、master节点加入集群

1复制token到其他master节点
sh 复制代码
	(1)其他master节点加入集群
[root@master242 ~]# kubeadm join master-lb:8443 --token abcdef.0123456789abcdef \
	--discovery-token-ca-cert-hash sha256:1bcf5dbc292e280a3948851f0c275a6c0ba21e281e3fb67ce21fa3c7ebdcce5c \
	--control-plane --certificate-key 4de14d6d03af4481ebb7ce62cdd63fa74ae24dafa0414974dbe3e31b84f10bc7
	

    (2)设置kubectl config文件
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

    (3)查看加入的master节点
[root@master243 ~]# kubectl get nodes
NAME        STATUS   ROLES           AGE     VERSION
master241   Ready    control-plane   20m     v1.24.1
master242   Ready    control-plane   7m53s   v1.24.1
master243   Ready    control-plane   30s     v1.24.1

#至此,所有master已成功加入集群
2 token创建(token过期重新创建)
sh 复制代码
    (1)声明一个永不过期的KEY
kubeadm token create --print-join-command --ttl 0
kubeadm token list
    (2)master节点使用咱们新生成的token加入集群
kubeadm join master-lb:8443 --token 8kd0b0.n8s2jdgwimeks9io --discovery-token-ca-cert-hash sha256:9fef272602094cc549770c2127aabe83c62be297661428740d36c87416e7de01

    (3)kubeadm生成的token数据将存储在k8s集群
kubectl -n kube-system get secret 
kubectl -n kube-system get secret bootstrap-token-8kd0b0 -o yaml 
3 将master证书上传到kubeadm的证书文件中(token过期则操作)
sh 复制代码
 	(1)将控制面板证书文件上传到kubeadm证书
kubeadm init phase upload-certs --upload-certs 

    (2)会生成一个KEY,我们后期会使用到。
d1702d0ec2259cf9d769a9ff8e58d01338246f5042be0b9dcd835a1d3151563a

温馨提示:
    在生成之前,建议使用"kubectl -n kube-system get secret kubeadm-certs -o wide"查看一下基本信息。

七、node节点加入集群

1 安装相关软件包(已安装可跳过)
sh 复制代码
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
repo_gpgcheck=0
EOF

yum install -y kubelet-1.27.1 kubeadm-1.27.1 kubectl-1.27.1 --disableexcludes=kubernetes
2 node节点加入集群
sh 复制代码
kubeadm join master-lb:8443 --token abcdef.0123456789abcdef \
	--discovery-token-ca-cert-hash sha256:1bcf5dbc292e280a3948851f0c275a6c0ba21e281e3fb67ce21fa3c7ebdcce5c
	
#master验证,所有节点已加入集群
[root@master243 ~]# kubectl get nodes
NAME        STATUS   ROLES           AGE     VERSION
master241   Ready    control-plane   29m     v1.24.1
master242   Ready    control-plane   17m     v1.24.1
master243   Ready    control-plane   9m44s   v1.24.1
node51      Ready    <none>          97s     v1.24.1
node52      Ready    <none>          7s      v1.24.1
node53      Ready    <none>          3s      v1.24.1

八、部署网络插件

sh 复制代码
#我就不下载了,直接运行本地下载好的
[root@master241 ~]# cd linux-cloud_-native-master/flannel/
[root@master241 flannel]# kubectl apply -f kube-flannel.yml 

#也可直接创建官网的插件
kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml

[root@master241 flannel]# kubectl get pods -A
NAMESPACE     NAME                                READY   STATUS    RESTARTS   AGE
kube-system   kube-flannel-ds-7m49q               1/1     Running   0          85s
kube-system   kube-flannel-ds-cj6t2               1/1     Running   0          85s
kube-system   kube-flannel-ds-mz6vc               1/1     Running   0          85s
kube-system   kube-flannel-ds-qgvtg               1/1     Running   0          85s
kube-system   kube-flannel-ds-sjw66               1/1     Running   0          85s
kube-system   kube-flannel-ds-zglnv               1/1     Running   0          85s

参考链接:
    https://kubernetes.io/zh/docs/concepts/cluster-administration/addons/
    
https://github.com/projectcalico/calico/releases?page=2

九、验证集群是否正常工作

1 编写资源清单
sh 复制代码
[root@master241 ~]# cat 01-nginx-demo.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - image: harbor.liux.com/liux-apps/apps:v3
        name: nginx
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: nginx
spec:
  selector:
    app: nginx
  type: NodePort
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
      nodePort: 30001
---
apiVersion: v1
kind: Pod
metadata:
  name: alpine
  namespace: default
spec:
  containers:
  - name: busybox
    image: harbor.liux.com/liux-web/nginx:1.25.1-alpine
    command:
      - sleep
      - "3600"
    imagePullPolicy: IfNotPresent
  restartPolicy: Always
2 containerd配置私有harbor仓库证书
sh 复制代码
1.编辑配置文件/etc/containerd/config.toml
[root@node53 containerd]# vim config.toml
[plugins."io.containerd.grpc.v1.cri".registry]
 config_path = ""
 [plugins."io.containerd.grpc.v1.cri".registry.auths]
 [plugins."io.containerd.grpc.v1.cri".registry.configs]
   [plugins."io.containerd.grpc.v1.cri".registry.configs."harbor.liux.com".tls]
     insecure_skip_verify = true
   [plugins."io.containerd.grpc.v1.cri".registry.configs."harbor.liux.com".auth]
     username = "admin"
     password = "12366"
 [plugins."io.containerd.grpc.v1.cri".registry.headers]
 [plugins."io.containerd.grpc.v1.cri".registry.mirrors]
   [plugins."io.containerd.grpc.v1.cri".registry.mirrors."harbor.liux.com"]
      endpoint = ["https://harbor.liux.com"]

2. 重启containerd服务
[root@node53 containerd]# systemctl daemon-reload && systemctl restart containerd.service  

3.创建资源清单,查看是否正常拉取镜像
[root@master241 ~]# kubectl delete -f 01-nginx-demo.yaml 
[root@master241 ~]# kubectl get pods -o wide

4.containerd常用命令
crictl pull image                # 下载容器镜像
crictl images list               # 查看本地容器镜像列表
crictl pods list                 # 列出所有Pods
crictl inspect container         # 检查容器元数据

十、总结

本问成功演示了如何从零开始,使用kubeadm工具构建一个符合当前Kubernetes社区最佳实践的高可用生产集群。整个过程突出了几个关键现代技术选择:

  • 弃用Docker,拥抱Containerd: 集群采用Containerd作为容器运行时,顺应了K8s的发展方向,架构更简洁,资源消耗更低。
  • 稳固的高可用架构: 通过"Nginx (负载均衡) + Keepalived (VIP)"的组合,为API-Server提供了无单点故障的访问入口,确保了控制平面的稳定性。
  • 全面的生产环境调优 : 从操作系统内核、网络模块、系统参数到Kubernetes组件配置,都进行了针对生产环境的优化,例如使用systemd cgroup驱动、ipvs代理模式、内核参数调整等。
  • 清晰的自动化与文档化: 提供了大量可复用的脚本(如环境同步、健康检查)和详细的配置文件示例,极大地提升了部署的可重复性和效率。

遵循此指南构建的Kubernetes集群,不仅具备了高可用性、可扩展性,也为后续部署各类云原生应用和服务奠定了坚实的基础。文档中穿插的技术原理解释和配置说明,也有助于读者深化对Kubernetes底层架构的理解。

相关推荐
岁岁种桃花儿1 小时前
构建SpringBoot项目Docker镜像并发布到k8s集群中进行运行
spring boot·docker·kubernetes
海兰1 小时前
Docker单节点部署Elasticsearch 9.0+(开发环境)
运维·docker·容器
人间打气筒(Ada)2 小时前
Docker主机集群化方案 Docker Swarm
运维·docker·容器·docker swarm·docker stack
Bypass--2 小时前
攻击篇 | 云原生安全攻防实战
安全·云原生
white-persist2 小时前
【内网运维 联合解析】Docker 全体系详解(功能原理 + 命令参数 + 实战攻防)
运维·docker·容器
AllData公司负责人3 小时前
【亲测好用】云原生数据平台能力演示
数据库·云原生·开源
java_logo11 小时前
OpenCode 企业级 Docker 部署完整指南
运维·docker·容器·opencode·opencode本地化部署·opencode部署手册·opencode部署方案
再战300年11 小时前
docker下创建redis集群方案
redis·docker·容器
qq_2290580113 小时前
docker中检测进程的内存使用量
java·docker·容器