Loading

kubeadm1.22搭建高可用 k8s 架构

kubeadm1.22搭建高可用 k8s 架构

一、集群架构

1. k8s集群架构节点角色功能

image-20211129163927082

1.1 节点角色

  • Master Node

    • k8s集群控制节点,接受集群外用户去集群操作请求,并对集群进行调度管理,;

    • Master NodeAPI ServerScheduler [ˈskɛdʒʊlər]Cluster State Store(ETCD数据库)和

      Controller Manger Server所组成;

  • Worker Node

    • 集群工作节点,运行用户业务应用容器;
    • Worker Node包含kubeletkube proxyContainer Runtime

1.2 Master节点组件介绍

master节点是集群管理中心,它的组件可以在集群内任意节点运行,

Master组件包括:

  • kube-scheduler
    • 监视新创建没有分配到Node的Pod,为Pod选择一个Node
  • kube-apiserver
    • 用于暴露kubernetes API,任何的资源请求/调用操作都是通过kube-apiserver提供的接口进行
  • ETCD
    • kubernetes提供默认的分布式键值存储系统,保存所有集群数据,协助集群正常运转。使用时需要为etcd数据提供备份计划
  • kube-controller-manager
    • 运行管理控制器,它们是集群中处理常规任务的后台线程
    • 控制器包括:
      • 节点(Node)控制器
      • 副本(Replication)控制器:负责维护系统中每个副本中的pod
      • 端点(Endpoints)控制器:填充Endpoints对象(即连接service&pods)
      • Service Account和Token控制器:为新的NameSpaces创建默认帐
        户访问API Token

1.3 Node节点组件介绍

node节点用于运行以及维护Pod,提供kubernetes运行时环境

Node组件包括:

  • kubelet
    • 负责维护容器的生命周期(创建pod,销毁pod),同时也负责Volume(CVI)网络(CNI)的管理
  • kube-proxy
    • 负责写入规则至 IPTABLESIPVS 实现服务映射访问的
    • 通过在主机上维护网络规则并执行连接转发来实现service(Iptables/Ipvs)
    • 随时与API通信,把ServicePod改变提交给API(不存储在Master本地,需要保存至共享存储上),保存至etcd(可做高可用集群)中,负责service实现,从内部podservice和从外部nodeservice访问。
  • Container Runtime
    • containerd
    • CRI-O
    • Docker
      • 负责镜像管理以及Pod和容器的真正运行

单master节点架构图

image-20211129164051789

2. 高可用集群架构

官方提供了2种高可用的部署方式,一种是外部ETCD的方式,即部署一个单独的ETCD集群,另一种就是混合部署,ETCDapiserver一起部署。我们采用第二种方式部署,一是部署简单,不需要在单独部署ETCD,另一个因素就是节约服务器。

  • 堆叠ETCD:每个master节点上运行一个apiserveretcd, etcd只与本节点apiserver通信。

image-20211129195557333

  • 外部ETCD:etcd集群运行在单独的主机上,每个etcd都与apiserver节点通信。

image-20211129191532896

image-20211129194845253

  • 由外部负载均衡器提供一个vip,流量负载到keepalived master节点上。

  • keepalived节点出现故障, vip自动漂到其他可用节点。

  • haproxy负责将流量负载到apiserver节点。

  • 三个apiserver会同时工作。注意k8scontroller-managerscheduler只会有一个工作,其余处于backup状态。apiserver主要是读写数据库,数据一致性的问题由数据库保证,此外apiserverk8s中最繁忙的组件,多个同时工作也有利于减轻压力。而controller-managerscheduler主要处理执行逻辑,多个大脑同时运作可能会引发混乱

image-20211129235728599

二、准备方案

k8s-高可用性

方案一:

HAProxy + Keepalived这种组合既可以作为操作系统上的服务运行,也可以作为控制平面主机上的静态吊舱运行。对于这两种情况,服务配置是相同的。

  • 使用Kubeadm + HAProxy + Keepalived部署高可用Kubernetes集群,安装在主机上
    • keepalive的作用是虚拟IP,让多个master节点公用一个虚拟IP,当主节点挂掉之后,虚拟IP通过选举飘逸到剩下二个节点其中一个
    • HAproxy 的作用在于实现apiserve的负载均衡 权重配置,也可以用nginx
    • Kubeadminit操作就会init这个VIP节点,当IP节点可以多用时,自然实现了高可用
  • HAProxyKeepalived也可以以静态Pod的形式运行服务

方案二:

  • 使用kube-vip
    • 与传统的keepalivedhaproxy 方法相比,kube-vip 在一个服务中实现了虚拟 IP 和负载平衡的管理
    • kube-vip 将作为控制平面节点上的静态 pod 运行。

三、主机准备环境

3.0 主机规划

准备六台机器以及一个vip

注意:master节点最少三台,因为我用2台master测试了负载高可用不可用。看了好多教程都用的2台master,他们只管搭建,不管测试。由于我刚开始用的是2台master,两台node,后面测试发现有问题,所以把其中一台node节点重新输出为master(条件允许的话也可以加mater主机)。在此申明以下,如果哪个小可爱想参考这篇部署高可用k8s的话,提前做好主机规划。我也是新手,大家一起学习~~

k8s-master1	192.168.19.152	kube-apiserver kube-controller-manager kube-scheduler etcd
k8s-master2	192.168.19.153	kube-apiserver kube-controller-manager kube-scheduler etcd
k8s-node01	192.168.19.151	kubelet kube-proxy docker etcd
k8s-node02	192.168.19.154	kubelet kube-proxy docker etcd
LB(Master)	192.168.19.155	haproxy/Nginx L4  VIP:192.168.19.155	

角色 ip
k8s-master1 192.168.19.152
k8s-master2 192.168.19.153
k8s-worker1 192.168.19.151
k8s-worker2 192.168.19.154
k8s-vip 192.168.19.155
nfs、harbor 192.168.19.153

3.1 准备基础rpm包配置PS1

# sudo yum -y install wget bash-completion git net-tools ntpdate vim epel-release 
# vim /etc/bashrc
PS1="<\[\e[35m\]\u\[\e[m\]\[\e[31m\]@\[\e[m\]\[\e[32m\]\h\[\e[m\]\[\e[33m\] \W\[\e[m\]>\[\e[36m\]\\$\[\e[m\] "
# exec bash

3.2 配置ip地址

# cat /etc/sysconfig/network-scripts/ifcfg-eth0
TYPE="Ethernet"
BOOTPROTO="static"
NAME="eth0"
DEVICE="eth0"
ONBOOT="yes"
IPADDR="192.168.19.x"
PREFIX="24"  或 NETMASK=255.255.255.0
GATEWAY="192.168.19.2"
DNS1="119.29.29.29"
# systemctl restart network

3.3 所有节点主机名及绑定

<root@localhost ~># hostnamectl set-hostname k8s-master1
<root@localhost ~># hostnamectl set-hostname k8s-master2
<root@localhost ~># hostnamectl set-hostname k8s-worker1
<root@localhost ~># hostnamectl set-hostname k8s-worker2
<root@localhost ~># exec bash
<root@localhost ~># hostname 


<root@k8s-xxx ~># cat /etc/hosts
192.168.19.152  k8s-master1
192.168.19.153  k8s-master2
192.168.19.151  k8s-worker1
192.168.19.154  k8s-worker2
<root@xxx ~># ping -c2 XXX

3.4 所有节点关闭selinux和firewalld

<root@k8s-xxx ~># systemctl stop firewalld
<root@k8s-xxx ~># systemctl disable firewalld
或
<root@k8s-xxx ~># systemctl disable firewalld --now && systemctl status firewalld
<root@k8s-xxx ~># iptables -nL  确保群为accept

<root@k8s-xxx ~># getenforce
<root@k8s-xxx ~># setenforce 0
<root@k8s-xxx ~># sed -ri 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config

3.5 时间同步

<root@k8s-xxx ~># ntpdate ntp.aliyun.com 
<root@k8s-xxx ~># ntpdatecn.ntp.org.cn
<root@k8s-xxx ~># crontab -e
<root@k8s-xxx ~># crontab -l
0 */2 * * *  ntpdate ntp.aliyun.com
<root@k8s-xxx ~># tail -f /var/log/cron
<root@k8s-xxx ~># date
<root@k8s-xxx ~># hwclock -r 硬件时间
<root@k8s-xxx ~># hwclock --systohc  (== hwclock -w   系统时间写入硬件时间)

3.6 升级内核版本

rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org
rpm -Uvh http://www.elrepo.org/elrepo-release-7.0-2.el7.elrepo.noarch.rpm
#查看最新版内核:
yum --disablerepo="*" --enablerepo="elrepo-kernel" list available
#安装最新版:
yum --enablerepo=elrepo-kernel install kernel-ml kernel-ml-devel –y
#查看当前可用内核版本:
awk -F\' '$1=="menuentry " {print i++ " : " $2}' /etc/grub2.cfg
#选择最新内核版本,0代表查看当前可用内核版本列表的左侧索引号
grub2-set-default 0
#生成grub文件
grub2-mkconfig -o /boot/grub2/grub.cfg
#重启linux
reboot

uname -r

删除非当前正在使用的内核
yum remove $(rpm -qa | grep kernel | grep -v $(uname -r))

3.7 所有节点关闭swap

<root@k8s-xxx ~># swapoff -a && sed -ri 's/.*swap.*/#&/' /etc/fstab
<root@k8s-xxx ~># swapoff -a && sed -i '/swap/ s/^\(.*\)$/#\1/g' /etc/fstab

3.8 验证mac地址uuid唯一性

<root@k8s-xxx ~># ip link
<root@k8s-xxx ~># cat /sys/class/net/ens33/address  #mac地址
<root@k8s-xxx ~># cat /sys/class/dmi/id/product_uuid   #uuid

3.9 添加网桥过滤

确保 br_netfilter 模块被加载。这一操作可以通过运行 lsmod | grep br_netfilter 来完成。若要显式加载该模块,可执行 sudo modprobe br_netfilter

为了让你的 Linux 节点上的 iptables 能够正确地查看桥接流量,你需要确保在你的 sysctl 配置中将 net.bridge.bridge-nf-call-iptables 设置为 1。例如:

<root@k8s-xxx ~># modprobe br_netfilter
永久
<root@k8s-xxx ~># cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
br_netfilter
EOF
查看是否加载
<root@k8s-xxx ~># lsmod | grep br_netfilter

过滤桥接的IPv4/IPv6数据包,传递到iptables的链
<root@k8s-xxx ~># cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF

从所有系统目录中读取值
<root@k8s-xxx ~># sysctl --system
加载网桥过滤配置文件
<root@k8s-xxx ~># sysctl -p /etc/sysctl.d/k8s.conf

3.10 停止邮件服务

<root@k8s-xxx ~># systemctl stop postfix && systemctl disable postfix
或
<root@k8s-xxx ~># systemctl disable postfix --now

3.11 考虑是否关闭NUMA

cp /etc/default/grub{,.bak}
vim /etc/default/grub 
# 在 GRUB_CMDLINE_LINUX 一行添加 `numa=off` 参数,如下所示:
diff /etc/default/grub.bak /etc/default/grub
< GRUB_CMDLINE_LINUX="crashkernel=auto rd.lvm.lv=centos/root rhgb quiet"
---
> GRUB_CMDLINE_LINUX="crashkernel=auto rd.lvm.lv=centos/root rhgb quiet numa=off"
cp /boot/grub2/grub.cfg{,.bak}
grub2-mkconfig -o /boot/grub2/grub.cfg

3.12 kube-proxy开启ipvs的前置条件

IPVS:https://github.com/kubernetes/kubernetes/blob/master/pkg/proxy/ipvs/README.md

确保 IPVS 需要内核模块(注意: 对于 Linux 内核4.19及以后版本,使用 nf _ conntrack 而不是 nf _ conntrack _ ipv4)

由于ipvs已经加入到了内核的主干,所以为kube-proxy开启ipvs的前提需要加载以下的内核模块

kube-proxyIPVS 代理模式启动时,它将验证 IPVS 内核模块是否可用。 如果未检测到 IPVS 内核模块,则 kube-proxy 将退回到以 iptables 代理模式运行。

安装ipset及ipvsadm
<root@k8s-xxx ~># rpm -qa ipvsadm

<root@k8s-xxx ~># yum -y install ipset ipvsadm 
在所有节点执行如下脚本
添加需要加载的模块

#linux 4.18及以下内核版本使用:modprobe -- nf_conntrack_ipv4,
#linux 4.18以上使用 modprobe -- nf_conntrack 
<root@k8s-xxx ~>#  cat > /etc/sysconfig/modules/ipvs.modules <<EOF
#!/bin/bash
modprobe -- ip_vs
modprobe -- ip_vs_rr
modprobe -- ip_vs_wrr
modprobe -- ip_vs_sh
# modprobe -- nf_conntrack_ipv4
modprobe -- nf_ conntrack
EOF

授权、运行、检查是否加载


<root@k8s-xxx ~>#  chmod 755 /etc/sysconfig/modules/ipvs.modules && bash /etc/sysconfig/modules/ipvs.modules && lsmod | grep -e ip_vs -e nf_conntrack_ipv4

modprobe: FATAL: Module nf_conntrack_ipv4 not found.

检查是否加载
<root@k8s-xxx ~>#  lsmod | grep -e ip_s -e f_conntrack_ipv4
或
<root@k8s-xxx ~>#  cut -f1 -d " "  /proc/modules | grep -e ip_vs -e nf_conntrack_ipv4

3.12 安装容器运行时Docker

# yum remove docker \
                  docker-client \
                  docker-client-latest \
                  docker-common \
                  docker-latest \
                  docker-latest-logrotate \
                  docker-logrotate \
                  docker-engine
# yum install -y yum-utils
# yum-config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo
# yum -y install docker-ce docker-ce-cli containerd.io
# docker version 

例如:   3:20.10.6-3.el7     docker-ce-20.10.6
docker 版本排序 
# yum list docker-ce --showduplicates | sort -r    
# yum install docker-ce-<VERSION_STRING> docker-ce-cli-<VERSION_STRING> containerd.io -y 
# yum install docker-ce-20.10.6 docker-ce-cli-c-20.10.6 containerd.io  

3.13 使用 systemd 来管理容器的 cgroup

Cgroup全称Control Group(控制组),是Linux系统内核提供的一个特性(Linux 2.6.24内核开始将Cgroup加入)。

主要用于限制和隔离一组进程对系统资源的使用。可控制的资源主要包括CPU、内存、block I/O、网络带宽等等。

Systemd也是对于Cgroup接口的一个封装。systemdPID1的形式在系统启动的时候运行,并提供了一套系统管理守护程序、库和实用程序,用来控制、管理Linux计算机操作系统资源。通过systemd-cgls命令我们可以看到systemd工作的进程PID1

更改设置,令容器运行时和 kubelet 使用 systemd 作为 cgroup 驱动,以此使系统更为稳定。 对于 Docker, 设置 native.cgroupdriver=systemd 选项。

mkdir /etc/docker
sudo mkdir /etc/docker
cat <<EOF | sudo tee /etc/docker/daemon.json
{
  "exec-opts": ["native.cgroupdriver=systemd"],
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m"
  },
  "storage-driver": "overlay2"
}
EOF

sudo systemctl enable docker
sudo systemctl daemon-reload
sudo systemctl restart docker
sudo systemctl status docker

需要确保容器运行时和 kubelet 所使用的是相同的 cgroup 驱动,否则 kubelet 进程会失败。

3.14 修改docker-ce服务配置文件

配置加速器(可选)

配置镜像加速器
针对Docker客户端版本大于 1.10.0 的用户

您可以通过修改daemon配置文件/etc/docker/daemon.json来使用加速器

sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
  "registry-mirrors": ["https://s2xmxgje.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
修改其目的是为了后续使用/etc/docker/daemon.json来进行更多配置。

修改内容如下
<root@k8s-xxx ~># cat /usr/lib/systemd/system/docker.service
[Unit]
...
[Service]
...
ExecStart=/usr/bin/dockerd  #如果原文件此行后面
有-H选项,请删除-H(含)后面所有内容
...
[Install]
...
注意:有些版本不需要修改,请注意观察

<root@k8s-xxx ~># systemctl daemon-reload
systemctl restart docker

四、所有master节点部署keepalived

参考:https://github.com/kubernetes/kubeadm/blob/main/docs/ha-considerations.md#options-for-software-load-balancing

1. 了解keepalived工作原理

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

Keepalived软件主要是通过VRRP协议实现高可用功能的。通过主机之间的优先等级以及心跳检测来及时切换准备主机的工作状态,以提高集群的高可用性。VRRP保证当主机的下一条路由器出现故障时,由另一台路由器来代替出现故障的路由器进行工作,从而保持网络通信的连续性和可靠性

虚拟 IP 的所有主机都需要在同一个 IP 子网中。且没被占用

keepalived是以VRRP(虚拟路由冗余协议)协议为基础, 包括一个master和多个backup master劫持vip对外提供服务。master发送组播,backup节点收不到vrrp包时认为master宕机,此时选出剩余优先级最高的节点作为新的master, 劫持vip

2. 安装配置keepalived

<root@k8s-master1 ~># yum -y install keepalived
<root@k8s-master2 ~># yum -y install keepalived
<root@k8s-master1 ~># rpm -qa keepalived
keepalived-1.3.5-19.el7.x86_64
<root@k8s-master1 ~># rpm -ql keepalived
文件或者目录 作用
/etc/keepalived/keepalived.conf 生效的配置文件
/etc/init.d/keepalived 服务器管理脚本
/var/log/messages 日志信息
/usr/lib/systemd/system/keepalived.service 服务文件
/usr/sbin/keepalived 二进制程序

Keepalived 配置由两个文件组成:服务配置文件和一个健康检查脚本,该脚本将定期调用,以验证持有虚拟 IP 的节点是否仍在运行。

cd  /etc/keepalived
cp -a keepalived.conf{,.bak}
vim keepalived.conf
global_defs {
    router_id LVS_DEVEL  # 路由器标识,一般不用改,也可以写成每个主机自己的主机名
}


# 定期执行的脚本,它的退出码将被所有的VRRP instance记录,它以非0的权重监视
vrrp_script check_apiserver {
  script "/etc/keepalived/check_apiserver.sh"
  interval 3
  weight -2
  fall 10
  rise 2
}

# 定义实例
vrrp_instance VI_1 {      
    # 指定keepalived节点的初始状态,可选值为MASTER|BACKUP
    state ${STATE}    
    # RRP实例绑定的网卡接口,用户发送VRRP包
    interface ${INTERFACE}  
    # 虚拟路由的ID,同一集群要一致,同一子网中的所有集群中应该是唯一的
    # VRRP组名,两个节点的设置必须一样,以指明各个节点属于同一VRRP组
    virtual_router_id ${ROUTER_ID}  
    # 定义优先级(1-254之间),按优先级来决定主备角色,优先级越大越优先
    priority ${PRIORITY}    
    # 设置不抢占模式(默认是抢占模式)
    nopreempt   
    # 主备通讯时间间隔,组播信息发送间隔,两个节点设置必须一样
    advert_int 1             
    # 配置认证信息,两个节点必须一致
    authentication {      
        # 认证方式
        auth_type PASS   
        # 同一集群中的keepalived配置里的此处必须一致
        auth_pass ${AUTH_PASS} 
    }
    virtual_ipaddress {
        # 配置要集群协商的VIP地址(可以是ip或172.16.3.200/24)	
        ${APISERVER_VIP}      
    }
    # track_script用于追踪脚本
    track_script {
        check_apiserver
    }
}

上面的 keepalived 配置使用了一个健康检查脚本/etc/keepalived/check _ apiserver。 负责确保保存虚拟IP的节点上的 API 服务器是可用的。这个脚本可以是这样的:

#!/bin/sh

errorExit() {
    echo "*** $*" 1>&2
    exit 1
}

curl --silent --max-time 2 --insecure https://localhost:${APISERVER_DEST_PORT}/ -o /dev/null || errorExit "Error GET https://localhost:${APISERVER_DEST_PORT}/"
if ip addr | grep -q ${APISERVER_VIP}; then
    curl --silent --max-time 2 --insecure https://${APISERVER_VIP}:${APISERVER_DEST_PORT}/ -o /dev/null || errorExit "Error GET https://${APISERVER_VIP}:${APISERVER_DEST_PORT}/"
fi

  • ${ APISERVER _ vip }是在保存的集群主机之间协商的虚拟 IP 地址。

  • 用于 Kubernetes API 服务器对话的端口。

1、k8s-master1 配置

<root@k8s-master1 keepalived># cat /etc/keepalived/keepalived.conf
! Configuration File for keepalived

global_defs {
   router_id LVS_DEVEL
}

vrrp_instance VI_1 {
    state MASTER       # 主
    interface ens33
    virtual_router_id 51
    priority 100       # 优先级
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        192.168.19.155   # vip
    }
}

2、k8s-master2配置

<root@k8s-master2 keepalived># cat keepalived.conf
! Configuration File for keepalived

global_defs {
   router_id LVS_DEVEL
}

vrrp_instance VI_1 {
    state BACKUP       # 备
    interface ens33
    virtual_router_id 51
    priority 90        # 优先级比主低
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        192.168.19.155  # vip
    }
}

3. 分别按照顺序启动主服务器和备服务器的keepalived

# systemctl start keepalived.service && systemctl enable keepalived.service
# systemctl enable keepalived.service --now
# systemctl status keepalived.service
# systemctl reload keepalived.service  修改了配置文件,重载配置
<root@k8s-master1 haproxy># ps aux |grep keepalived
root      54499  0.0  0.0 123020  2040 ?        Ss   15:26   0:00 /usr/sbin/keepalived -D
root      54500  0.0  0.2 123020  5568 ?        S    15:26   0:00 /usr/sbin/keepalived -D
root      54501  0.0  0.1 123020  3600 ?        S    15:26   0:00 /usr/sbin/keepalived -D

3. 测试:k8s-master1挂了

Api-server:现在没发测试等,安装好k8s再测试

# 停止服务或关闭网络
<root@k8s-master1 keepalived># systemctl stop keepalived.service

image-20211130132538123

4. 几个注意

①脑裂:

image-20211130140746762

image-20211130140723886

通过抓包工具观察

# yum -y install tcpdump
# tcpdump -h
-n  不显示主机名称,以IP显示
-i  网卡接口
-w <数据包文件> 把数据包数据写入指定的文件。
-vv 更详细显示指令执行过程
# tcpdump vrrp  -i ens33 -n

可以看到k8s-master1k8s-master2 同时向组播地址发生VRRP

image-20211130141724737

image-20211130141450484

由于防火墙开启导致的脑裂,防火墙没有允许vrrp的组播

解决办法1:单播的方式

keepalived在组播模式下所有的信息都会向224.0.0.18的组播地址发送,产生众多的无用信息,并且会产生干扰和冲突,所以需要将其组播的模式改为单拨。这是一种安全的方法,避免局域网内有大量的keepalived造成虚拟路由id的冲突。
单播模式需要关闭vrrp_strict,严格遵守vrrp协议这个选项
单播需要在VIP实例配置段加入单播的源地址和目标地址

单播示例配置:注意此语法在keepalived1.2.11版本以上支持

unicast_src_ip 192.168.1.21  ##(本地IP地址)

unicast_peer {

		192.168.1.22        ##(对端IP地址)

}
# master1 配置
<root@k8s-master1 keepalived># cat keepalived.conf
! Configuration File for keepalived

global_defs {
   router_id LVS_DEVEL
   #vrrp_strict    #如果有这个选项把它删除或注释
}

vrrp_instance VI_1 {
    state MASTER
    interface ens33
    virtual_router_id 51
    priority 100
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    unicast_src_ip 192.168.19.152   # 本地IP
    unicast_peer {
        192.168.19.153              # 对端IP
    }
    virtual_ipaddress {
        192.168.19.155
    }
}

# master2 配置
<root@k8s-master2 keepalived># cat keepalived.conf
! Configuration File for keepalived

global_defs {
   router_id LVS_DEVEL
   #vrrp_strict    #如果有这个选项把它删除或注释
}

vrrp_instance VI_1 {
    state BACKUP
    interface ens33
    virtual_router_id 51
    priority 90
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    unicast_src_ip 192.168.19.153   # 本地IP
    unicast_peer {
        192.168.19.152              # 对端IP
    }

    virtual_ipaddress {
        192.168.19.155
    }
}

单播,可以发现点对点

image-20211130144957785

image-20211130144825258

如果出现下面错误就是没#vrrp_strict 导致的

image-20211130145136097

解决办法2:防火墙配置允许vrrp的组播(或直接停止防火墙)

  • 1、keepalived 默认需要使用D类组播地址224.0.0.18 进行心跳通信

  • 2、keepalived 使用vrp协议进行通信(协议号码为112)

# firewall-cmd --direct --permanent --add-rule ipv4 filter INPUT 0 --in-interface ens33 --destination 224.0.0.18 --protocol vrrp -j ACCEPT

# firewall-cmd --reload

②抢占模式和非抢占模式:

master发生异常,且后期恢复master正常后,存在抢占非抢占两种情况。

简单点说抢占模式就是,当master宕机后,backup 接管服务。后续当master恢复后,vip再漂移到master上,master重新接管服务,多了一次多余的vip切换

总结:抢占模式即MASTER从故障中恢复后,会将VIP从BACKUP节点中抢占过来。非抢占模式即MASTER恢复后不抢占BACKUP升级为MASTER后的VIP

设置非抢占模式

  • nopreempt

    在主备服务器的配置文件,vrrp_instance段中

  • 设置state工作模式都为为BACKUP
    两个keepalived节点都启动后,默认都是BACKUP状态,双方在发送组播信息后,会根据优先级来选举一个MASTER出来。由于两者都配置了nopreempt,所以MASTER从故障中恢复后,不会抢占vip。这样会避免VIP切换可能造成的服务延迟。

# k8s-master1 配置

<root@k8s-master1 keepalived># cat keepalived.conf
! Configuration File for keepalived

global_defs {
   router_id LVS_DEVEL
}

vrrp_instance VI_1 {
    state BACKUP
    interface ens33
    virtual_router_id 51
    priority 100
    advert_int 1
    nopreempt            # 非抢占模式
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    unicast_src_ip 192.168.19.152
    unicast_peer {
        192.168.19.153
    }
    virtual_ipaddress {
        192.168.19.155
    }
}


# k8s-master2 配置
<root@k8s-master2 keepalived># cat keepalived.conf
! Configuration File for keepalived

global_defs {
   router_id LVS_DEVEL
   #vrrp_strict
}

vrrp_instance VI_1 {
    state BACKUP
    interface ens33
    virtual_router_id 51
    priority 90
    advert_int 1
    nopreempt                  # 非抢占模式
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    unicast_src_ip 192.168.19.153
    unicast_peer {
        192.168.19.152
    }

    virtual_ipaddress {
        192.168.19.155
    }
}

notify的用法:

notify_master:当当前节点成为master时,通知脚本执行任务(一般用于启动某服务或发送邮件等,比如nginx,haproxy等)

notify_backup:当当前节点成为backup时,通知脚本执行任务(一一般用于启动某服务或发送邮件等,比如nginx,haproxy等)

notify_fault:当当前节点出现故障,执行的任务;

五、所有master节点部署haproxy

参考:https://github.com/kubernetes/kubeadm/blob/main/docs/ha-considerations.md#options-for-software-load-balancing

1. 安装haproxy

负载均衡方式将根据IP范围和端口转发用户通信量,流量将被转发到处理所有请求的后端所在的在端口上

<root@k8s-master1 keepalived># yum install haproxy -y
<root@k8s-master2 keepalived># yum install haproxy -y
<root@k8s-master1 haproxy># rpm -qa haproxy
haproxy-1.5.18-9.el7_9.1.x86_64
<root@k8s-master1 keepalived># rpm -qc haproxy
/etc/haproxy/haproxy.cfg
/etc/logrotate.d/haproxy
/etc/sysconfig/haproxy
<root@k8s-master1 haproxy># cp -a haproxy.cfg{,.bak}

2. 配置haproxy

修改haproxy的配置文件,因为我们的haproxy安装在2个master节点上面,haproxy实现api-server的负载均衡,所有不能用api-server的端口,我们设置端口信息为16443,默认是api-server是6443

# /etc/haproxy/haproxy.cfg
#---------------------------------------------------------------------
# Global settings
#---------------------------------------------------------------------
# #全局配置文件
global
    # #日志配置,所有的日志都记录本地,通过local2输出
    log /dev/log local0
    log /dev/log local1 notice
    daemon

#---------------------------------------------------------------------
# common defaults that all the 'listen' and 'backend' sections will
# use if not designated in their block
#---------------------------------------------------------------------
defaults
    # 默认使用协议,可以为{http|tcp|health} http:是七层协议 tcp:是四层 health:只返回OK
    mode                    http
    log                     global
    option                  httplog
    option                  dontlognull
    option http-server-close
    option forwardfor       except 127.0.0.0/8
    option                  redispatch
    retries                 1
    timeout http-request    10s
    timeout queue           20s
    timeout connect         5s
    timeout client          20s
    timeout server          20s
    timeout http-keep-alive 10s   # #默认持久连接超时时间
    timeout check           10s   # #默认检查时间间隔

#---------------------------------------------------------------------
# apiserver frontend which proxys to the control plane nodes
#---------------------------------------------------------------------
# 前端服务器
# 代理到控制节点的apiserver前端
frontend apiserver 
    # ${APISERVER_DEST_PORT} 用于 Kubernetes 与 API 服务器对话的端口。
    bind *:${APISERVER_DEST_PORT}
    mode tcp
    option tcplog
    default_backend apiserver

#---------------------------------------------------------------------
# round robin balancing for apiserver
#---------------------------------------------------------------------
#  后端服务器
backend apiserver
    option httpchk GET /healthz
    http-check expect status 200
    mode tcp
    option ssl-hello-chk
    balance     roundrobin
        # ${ apiserver_src _ port }是 API 服务器实例使用的端口
        # ${ HOST1 _ id }是第一个负载平衡的 API 服务器主机的符号名称
        # ${ HOST1 _ address }第一个负载平衡的 API 服务器主机的可解析地址(DNS 名称、 IP 地址)
        # check 启动对后端server的健康状态检测
        server ${HOST1_ID} ${HOST1_ADDRESS}:${APISERVER_SRC_PORT} check
        # [...]

master1mast2 配置一样

<root@k8s-master1 haproxy># rsync -av ./haproxy.cfg root@k8s-master2:/etc/haproxy/
<root@k8s-master1 haproxy># cat haproxy.cfg
# /etc/haproxy/haproxy.cfg
#---------------------------------------------------------------------
# Global settings
#---------------------------------------------------------------------
global
    log /dev/log local0
    log /dev/log local1 notice
    daemon

#---------------------------------------------------------------------
# common defaults that all the 'listen' and 'backend' sections will
# use if not designated in their block
#---------------------------------------------------------------------
defaults
    mode                    http
    log                     global
    option                  httplog
    option                  dontlognull
    option http-server-close
    option forwardfor       except 127.0.0.0/8
    option                  redispatch
    retries                 1
    timeout http-request    10s
    timeout queue           20s
    timeout connect         5s
    timeout client          20s
    timeout server          20s
    timeout http-keep-alive 10s
    timeout check           10s

#---------------------------------------------------------------------
# apiserver frontend which proxys to the control plane nodes
#---------------------------------------------------------------------
frontend apiserver
    bind *:16443
    mode tcp
    option tcplog
    default_backend apiserver

#---------------------------------------------------------------------
# round robin balancing for apiserver
#---------------------------------------------------------------------
backend apiserver
    option httpchk GET /healthz
    http-check expect status 200
    mode tcp
    option ssl-hello-chk
    balance     roundrobin
        server k8s-master1 192.168.19.152:6443 check
        server k8s-master2 192.168.19.153:6443 check

3. 启动并查看

# systemctl enable haproxy --now
<root@k8s-master1 haproxy># netstat -naltp|grep haproxy
tcp        0      0 0.0.0.0:16443           0.0.0.0:*               LISTEN      68836/haproxy
<root@k8s-master1 haproxy># ps aux|grep haproxy
root      68834  0.0  0.1  44752  3112 ?        Ss   16:37   0:00 /usr/sbin/haproxy-systemd-wrapper -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid
root      68835  0.0  0.1  46064  5308 ?        S    16:37   0:00 /usr/sbin/haproxy -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -Ds
root      68836  0.0  0.0  46064   988 ?        Ss   16:37   0:00 /usr/sbin/haproxy -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -Ds

六、安装kubeadm

所有节点安装kubelet,kubeadm,kubectl,并 enable kubelet 服务(注意:不要start启动)

你需要在每台机器上安装以下的软件包:

  • kubeadm:用来初始化集群的指令。
  • kubelet:在集群中的每个节点上用来启动 Pod 和容器等。
  • kubectl:用来与集群通信的命令行工具。

kubelet,kubeadm,kubectl 要版本一致

1. 安装最新版k8s

  • 谷歌源(不推荐,慢)
cat <<EOF | sudo tee /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-\$basearch
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
exclude=kubelet kubeadm kubectl
EOF

# 将 SELinux 设置为 permissive 模式(相当于将其禁用)
sudo setenforce 0
sudo sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config

sudo yum install -y kubelet kubeadm kubectl --disableexcludes=kubernetes

sudo systemctl enable kubelet
# cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
EOF
# yum list | grep kubeadm
输入 y

       
       
# yum install -y --nogpgcheck kubelet kubeadm kubectl --disableexcludes=kubernetes
# rpm -qa kubeadm
kubeadm-1.22.4-0.x86_64
# rpm -qa kubelet
kubelet-1.22.4-0.x86_64
# rpm -qa kubectl
kubectl-1.22.4-0.x86_64


# systemctl enable kubelet && systemctl start kubelet  
或
# systemctl enable kubelet

ps: 由于官网未开放同步方式, 可能会有索引gpg检查失败的情况, 这时请用 yum install -y --nogpgcheck kubelet kubeadm kubectl 安装

新建 /etc/yum.repos.d/kubernetes.repo,内容为:

[kubernetes]
name=kubernetes
baseurl=https://mirrors.tuna.tsinghua.edu.cn/kubernetes/yum/repos/kubernetes-el7-$basearch
enabled=1
# yum list | grep kubeadm
--disableexcludes=kubernetes  #禁掉除了这个之外的别的仓库
# yum install -y kubelet kubeadm kubectl --disableexcludes=kubernetes
# 安装指定版本的k8s
# yum install -y kubelet-<version> kubectl-<version> kubeadm-<version>

# yum install -y kubelet kubeadm kubectl --disableexcludes=kubernetes

# systemctl enable --now kubelet

kubectl查看k8s版本信息
# kubectl version 查看安装的版本  #  kubectl get nodes 
-------------------------------------------------------------
name=Description#一个描述,随意。
baseurl=#设置资源库的地址,可以写阿里云也可以是自己的yum
    ftp://
    http://
    file:///
enabled={1|0}#enabled=1开启本地更新模式
gpgcheck={1|0}# gpgcheck=1表示检查;可以不检查gpgcheck=0
gpgkey=#检查的key;如果上面不检查这一行可以不写。

2. 安装指定版本k8s

安装指定版本的kubeadm kubelet kubectl

# yum list | grep kubeadm
# 安装指定版本的k8s
# yum install -y kubelet-<version> kubectl-<version> kubeadm-<version> --disableexcludes=kubernetes

# yum install -y kubelet kubeadm kubectl --disableexcludes=kubernetes

# systemctl enable  kubelet

比如:

[root@XXX ~]# yum list kubeadm.x86_64 --showduplicates | sort -r
[root@XXX ~]# yum -y install --setopt=obsoletes=0 kubeadm-1.17.2-0 kubelet-1.17.2-0 kubectl-1.17.2-0

3. kubelet配置使用systemd驱动

警告:你需要确保容器运行时和kubelet所使用的是相同的 cgroup 驱动,否则 kubelet 进程会失败。

kubeadm 支持在执行 kubeadm init 时,传递一个 KubeletConfiguration 结构体。 KubeletConfiguration 包含 cgroupDriver 字段,可用于控制 kubelet 的 cgroup 驱动。

说明: 在版本 1.22 中,如果用户没有在 KubeletConfiguration 中设置 cgroupDriver 字段, kubeadm init 会将它设置为默认值 systemd

<root@k8s-master ~># systemctl status kubelet
● kubelet.service - kubelet: The Kubernetes Node Agent
   Loaded: loaded (/usr/lib/systemd/system/kubelet.service; enabled; vendor preset: disabled)
  Drop-In: /usr/lib/systemd/system/kubelet.service.d
           └─10-kubeadm.conf
   Active: activating (auto-restart) (Result: exit-code) since Wed 2021-11-10 12:33:39 CST; 1s ago
     Docs: https://kubernetes.io/docs/
  Process: 85021 ExecStart=/usr/bin/kubelet $KUBELET_KUBECONFIG_ARGS $KUBELET_CONFIG_ARGS $KUBELET_KUBEADM_ARGS $KUBELET_EXTRA_ARGS (code=exited, status=1/FAILURE)  #启动配置参数
 Main PID: 85021 (code=exited, status=1/FAILURE)


主要配置kubelet,如果不配置可能会导致k8s集群无法启动。
为了实现docker使用的cgroupdriver与kubelet使用的cgroup的一致性,建议修改如下文件内容。
查看kubelet的cgroup驱动
[root@XXX ~]# DOCKER_CGROUP=$(docker info | grep Cgroup | awk '{print $3}')
[root@XXX ~]# echo $DOCKER_CGROUP
systemd
如果不是和docker使用的cgroupdriver 一致则通过下述方法修改
[root@XXX ~]# cat /etc/sysconfig/kubelet
KUBELET_EXTRA_ARGS=
[root@XXX ~]# vim /etc/sysconfig/kubelet
KUBELET_EXTRA_ARGS="--cgroup-driver=systemd"
[root@XXX ~]# systemctl daemon-reload 
设置为开机自启动即可,千万不要start 开启,由于没有生成配置文件,集群初始化后自动启动
[root@XXX ~]# systemctl enable kubelet

五、使用 kubeadm 创建集群

使用 kubeadm 创建集群:https://kubernetes.io/zh/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/

利用 kubeadm 创建高可用集群:https://kubernetes.io/zh/docs/setup/production-environment/tools/kubeadm/high-availability/

1. 准备所需的容器镜像

方法一:编写脚本,提前拉取需要的镜像

# 在master1和master2 下载镜像
<root@k8s-master1 ~>#  kubeadm config images list |tee image.list 写入文件并输出到屏幕

# 查看已列出镜像文件列表
<root@k8s-master1 ~>#  cat image.list
# 编写镜像下载脚本(通过阿里云下载相关镜像)
<root@k8s-master1 ~>#  cat image.pull
#!/bin/bash

images=(
    kube-apiserver:v1.22.3
    kube-proxy:v1.22.3
    kube-controller-manager:v1.22.3
    kube-scheduler:v1.22.3
    etcd:3.5.0-0
    pause:3.5
)

for imageName in ${images[@]} ; do
    docker pull registry.aliyuncs.com/google_containers/$imageName
    docker tag registry.aliyuncs.com/google_containers/$imageName k8s.gcr.io/$imageName
    docker rmi registry.aliyuncs.com/google_containers/$imageName
done

docker pull coredns/coredns:1.8.4

docker tag coredns/coredns:1.8.4 k8s.gcr.io/coredns/coredns:v1.8.4

docker rmi coredns/coredns:1.8.4

# 下载镜像
<root@k8s-master1 ~># sh image.pull
<root@k8s-master1 ~># docker images

工作节点导入:kube-proxy 和 pause 镜像
保存镜像为tar
<root@k8s-master1 ~># docker save -o kube-proxy.tar k8s.gcr.io/kube-proxy:v1.21.0
<root@k8s-master1 ~># docker save -o pause.tark8s.gcr.io/pause:3.4.1
<root@k8s-master1 ~>#  ls
kube-proxy.tar  pause.tar
复制tar到worker节点
<root@k8s-master1 ~>#  scp kube-proxy.tar pause.tar worker1:/root
<root@k8s-master1 ~>#  scp kube-proxy.tar pause.tar worker2:/root

# 在worker1和worker2节点导入镜像
<root@k8s-masterx ~>#  ls
kube-proxy.tar  pause.tar
<root@k8s-masterx ~># docker load -i kube-proxy.tar
<root@k8s-masterx ~># docker load -i pause.tar

<root@k8s-masterx ~>#  ls
kube-proxy.tar  pause.tar
<root@k8s-masterx ~>#  docker load -i kube-proxy.tar
<root@k8s-masterx ~>#  docker load -i pause.tar

方法二:直接拉取初始化所需要的镜像(推荐)

不希望 kubeadm initkubeadm join 下载存放在 k8s.gcr.io 上的默认的容器镜像

1.1 查看所需镜像

# 查看最新版本所需镜像
<root@k8s-xxx ~># kubeadm config images list | tee k8s-image.list
k8s.gcr.io/kube-apiserver:v1.22.4
k8s.gcr.io/kube-controller-manager:v1.22.4
k8s.gcr.io/kube-scheduler:v1.22.4
k8s.gcr.io/kube-proxy:v1.22.4
k8s.gcr.io/pause:3.5
k8s.gcr.io/etcd:3.5.0-0
k8s.gcr.io/coredns/coredns:v1.8.4

# 查看k8s指定版本对应的镜像版本
<root@k8s-xxx ~># kubeadm config images list --kubernetes-version=v1.21.0

1.2 安装阿里云镜像

使用阿里云镜像地址

  • [地址1] registry.aliyuncs.com/google_containers
  • [地址2] registry.cn-hangzhou.aliyuncs.com/google_containers
要等一会儿可以去抽支烟
<root@k8s-xxx ~># kubeadm config images pull \
--image-repository=registry.aliyuncs.com/google_containers 

<root@k8s-xxx ~># docker images

方法三:命令初始化指定仓库地址

命令初始化的时候指定要拉取镜像的地址:--image-repository registry.aliyuncs.com/google_containe

2. 使用 kubeadm 配置集群中的每个 kubelet

kubelet:负责维护容器的生命周期(创建pod,销毁pod),同时也负责Volume(CVI)网络(CNI)的管理

kubelet 是一个守护程序,在 Kubernetes 集群中的每个节点上运行。 当 Kubernetes 初始化或升级时,kubeadm CLI 工具由用户执行,而 kubelet 始终在后台运行。

由于kubelet是守护程序,因此需要通过某种初始化系统或服务管理器进行维护。

集群中涉及的所有 kubelet 的一些配置细节都必须相同, 而其他配置方面则需要基于每个 kubelet 进行设置,以适应给定机器的不同特性(例如操作系统、存储和网络)。 你可以手动地管理 kubelet 的配置,但是 kubeadm 现在提供一种 KubeletConfiguration API 类型 用于集中管理 kubelet 的配置

通过使用 kubeadm initkubeadm join 命令为 kubelet 提供默认值。

查看kubelet默认值

# kubeadm config print init-defaults --component-configs KubeletConfiguration

apiVersion: kubeadm.k8s.io/v1beta3
bootstrapTokens:
- groups:
  - system:bootstrappers:kubeadm:default-node-token
  token: abcdef.0123456789abcdef
  ttl: 24h0m0s
  usages:
  - signing
  - authentication
kind: InitConfiguration
localAPIEndpoint:
  advertiseAddress: 1.2.3.4
  bindPort: 6443
nodeRegistration:
  criSocket: /var/run/dockershim.sock
  imagePullPolicy: IfNotPresent
  name: node
  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
kind: ClusterConfiguration
kubernetesVersion: 1.22.0
networking:
  dnsDomain: cluster.local
  serviceSubnet: 10.96.0.0/12
scheduler: {}
---
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
cgroupDriver: systemd
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
logging: {}
memorySwap: {}
nodeStatusReportFrequency: 0s
nodeStatusUpdateFrequency: 0s
rotateCertificates: true
runtimeRequestTimeout: 0s
shutdownGracePeriod: 0s
shutdownGracePeriodCriticalPods: 0s
staticPodPath: /etc/kubernetes/manifests
streamingConnectionIdleTimeout: 0s
syncFrequency: 0s
volumeStatsAggPeriod: 0s

当调用 kubeadm init 时,kubelet 配置被编组到磁盘上的 /var/lib/kubelet/config.yaml 中, 并且上传到集群中的 ConfigMap。 ConfigMap 名为 kubelet-config-1.X,其中 X 是你正在初始化的 kubernetes 版本的次版本。 在集群中所有 kubelet 的基准集群范围内配置,将 kubelet 配置文件写入 /etc/kubernetes/kubelet.conf 中。 此配置文件指向允许 kubelet 与 API 服务器通信的客户端证书。

3. kubelet 的 systemd 文件

kubeadm 中附带了有关系统如何运行 kubelet 的 systemd 配置文件

安装的配置文件被写入 /etc/systemd/system/kubelet.service.d/10-kubeadm.conf 并由系统使用

[Service]
Environment="KUBELET_KUBECONFIG_ARGS=--bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf
--kubeconfig=/etc/kubernetes/kubelet.conf"
Environment="KUBELET_CONFIG_ARGS=--config=/var/lib/kubelet/config.yaml"
# 这是 "kubeadm init" 和 "kubeadm join" 运行时生成的文件,动态地填充 KUBELET_KUBEADM_ARGS 变量
EnvironmentFile=-/var/lib/kubelet/kubeadm-flags.env
# 这是一个文件,用户在不得已下可以将其用作替代 kubelet args。
# 用户最好使用 .NodeRegistration.KubeletExtraArgs 对象在配置文件中替代。
# KUBELET_EXTRA_ARGS 应该从此文件中获取。
EnvironmentFile=-/etc/default/kubelet
ExecStart=
ExecStart=/usr/bin/kubelet $KUBELET_KUBECONFIG_ARGS $KUBELET_CONFIG_ARGS $KUBELET_KUBEADM_ARGS $KUBELET_EXTRA_ARGS

该文件为 kubelet 指定由 kubeadm 管理的所有文件的默认位置。

  • 用于 TLS 引导程序的 KubeConfig 文件为 /etc/kubernetes/bootstrap-kubelet.conf, 但仅当 /etc/kubernetes/kubelet.conf 不存在时才能使用。
  • 具有唯一 kubelet 标识的 KubeConfig 文件为 /etc/kubernetes/kubelet.conf
  • 包含 kubelet 的组件配置的文件为 /var/lib/kubelet/config.yaml
  • 包含的动态环境的文件 KUBELET_KUBEADM_ARGS 是来源于 /var/lib/kubelet/kubeadm-flags.env
  • 包含用户指定标志替代的文件 KUBELET_EXTRA_ARGS 是来源于 /etc/default/kubelet(对于 DEB),或者 /etc/sysconfig/kubelet(对于 RPM)。 KUBELET_EXTRA_ARGS 在标志链中排在最后,并且在设置冲突时具有最高优先级
Package name Description
kubeadm 给 kubelet 安装 /usr/bin/kubeadm CLI 工具和 kubelet 的 systemd 文件
kubelet 安装 kubelet 可执行文件到 /usr/bin 路径,安装 CNI 可执行文件到 /opt/cni/bin 路径。
kubectl 安装 /usr/bin/kubectl 可执行文件。
cri-tools cri-tools git 仓库中安装 /usr/bin/crictl 可执行文件。

4. 集群初始化

1. k8s-Master1节点初始化

方法一:通过配置文件

生成默认初始化配置
<root@k8s-master1 ~># kubeadm config print init-defaults |tee kubeadm-init-config.yaml
#kubeadm config print init-defaults
#kubeadm config print join-defaults
修改默认初始化配置

https://kubernetes.io/docs/reference/config-api/kubeadm-config.v1beta3/

修改之后的配置文件如下,注意以下几点advertiseAddress,name,imageRepository,kubernetesVersion,podSubnet,controlPlaneEndpoint,cgroupDriver

kubeadm 不支持将没有 --control-plane-endpoint 参数的单个控制平面集群转换为高可用性集群。

<root@k8s-master1 ~># vim kubeadm-init-config.yaml
apiVersion: kubeadm.k8s.io/v1beta3
bootstrapTokens:
- groups:
  - system:bootstrappers:kubeadm:default-node-token
  token: abcdef.0123456789abcdef
  ttl: 24h0m0s
  usages:
  - signing
  - authentication
kind: InitConfiguration
localAPIEndpoint:
  advertiseAddress: 192.168.19.152   # apiserver 所在主机IP即master地址
  bindPort: 6443              # api server 端口
nodeRegistration:
  criSocket: /var/run/dockershim.sock
  imagePullPolicy: IfNotPresent
  name: k8s-master1              # 改为master 节点的主机名(未设置默认为节点的主机名)
  taints: null
---
apiServer:
  timeoutForControlPlane: 4m0s
apiVersion: kubeadm.k8s.io/v1beta3
certificatesDir: /etc/kubernetes/pki
clusterName: kubernetes
#controlPlaneEndpoint: "master.k8s.io:16443"    # 绑定的是VIP
# 192.168.19.155是VIP,16443是在haproxy 中配置端口
# 集群内就是通过前端 192.168.19.155:16443 来与后端负载 api-server ip:6443 通信的
controlPlaneEndpoint: "192.168.19.155:16443"    
controllerManager: {}
dns: {}
etcd:
  local:
    dataDir: /var/lib/etcd
# 拉取镜像的地址 默认是k8s.gcr.io     
imageRepository: registry.aliyuncs.com/google_containers    
kind: ClusterConfiguration   
kubernetesVersion: 1.22.0      # k8s 的版本 会影响到下载镜像的版本(镜像可以提前下载)
networking:
  dnsDomain: cluster.local
  # pod网络(flanne插件,默认是100.244.0.0/16  calico 默认: 192.168.0.0/16)
  # 如果不想使用默认的,等配置网络插件的时候再去修改
  podSubnet: "172.16.0.0/16"    # 添加pod 网络
  serviceSubnet: 10.96.0.0/12   # service 网络
scheduler: {}
---
# # 配置 kubelet 的 cgroup 驱动
kind: KubeletConfiguration
apiVersion: kubelet.config.k8s.io/v1beta1
cgroupDriver: systemd
---                  
# 开启ipvs 模式(Kubernetes 1.14 版本开始默认使用 ipvs 代理)可不加
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
mode: ipvs      

说明:1.11版本之后同时支持iptablesipvs,默认使用ipvs,如果ipvs模块没有加载时,会自动降级至iptables

把下面这段加入kubeadm-config.yaml文件最后
---
apiVersion: kubeproxy.config.k8s.io/v1alpha1 #注意版本
kind: KubeProxyConfiguration
mode: ipvs

说明: 在版本 1.22 中,如果用户没有在 KubeletConfiguration 中设置 cgroupDriver 字段, kubeadm init 会将它设置为默认值 systemd

kind: KubeletConfiguration
apiVersion: kubelet.config.k8s.io/v1beta1
cgroupDriver: systemd

通过将参数 --upload-certs 添加到 kubeadm init,主控制节点的证书 被加密并上传到 kubeadm-certs Secret 中。 请注意,此 Secret 将在 2 小时后自动过期。

# 试运行看有无错误
<root@k8s-master1 ~># kubeadm init --config kubeadm-init-config.yaml --dry-run
# --upload-certs 标志用来将在所有控制节点实例之间的共享证书上传到集群。 
# 如果希望手动地通过控制平面节点或者使用自动化 工具复制证书则不加--upload-certs
# 当 --upload-certs 与 kubeadm init 一起使用时,主控制节点的证书 被加密并上传到 kubeadm-certs Secret 中。
<root@k8s-master1 ~># kubeadm init \
--config kubeadm-init-config.yaml \
--upload-certs | tee k8s-admin-init.log

# 如果初始化遇到问题,尝试使用下面的命令清理,再重新初始化
<root@k8s-master1 ~>#  kubeadm reset
<root@k8s-master1 ~>#  rm -rf $HOME/.kube/config
<root@k8s-master1 ~>#  rm -rf /etc/cni/net.d
<root@k8s-master1 ~>#  ipvsadm --clear
<root@k8s-master1 ~>#  ipvsadm -ln

重置过程不会清除CNI配置。为此,必须删除/etc/cni/net.d
重置过程不会重置或清理iptables规则或IPVS表。
如果要重置iptables,必须使用“iptables”命令手动重置。
如果您的群集设置为使用IPVS,则运行ipvsadm --clear 重置系统的IPVS表。

初始化成功后

Your Kubernetes control-plane has initialized successfully!

To start using your cluster, you need to run the following as a regular user:

  mkdir -p $HOME/.kube
  sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  sudo chown $(id -u):$(id -g) $HOME/.kube/config

Alternatively, if you are the root user, you can run:

  export KUBECONFIG=/etc/kubernetes/admin.conf

You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
  https://kubernetes.io/docs/concepts/cluster-administration/addons/

You can now join any number of the control-plane node running the following command on each as root:

  kubeadm join 192.168.19.155:16443 --token abcdef.0123456789abcdef \
        --discovery-token-ca-cert-hash sha256:8ccf4d292eab06399b61d572c27f06879c9a8dfae9a2487d7b7562b5225cada4 \
        --control-plane --certificate-key 140170149e5095b7eb96454e82153b7f2c966274965e646a330297d0c731264c

Please note that the certificate-key gives access to cluster sensitive data, keep it secret!
As a safeguard, uploaded-certs will be deleted in two hours; If necessary, you can use
"kubeadm init phase upload-certs --upload-certs" to reload certs afterward.

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

kubeadm join 192.168.19.155:16443 --token abcdef.0123456789abcdef \
        --discovery-token-ca-cert-hash sha256:8ccf4d292eab06399b61d572c27f06879c9a8dfae9a2487d7b7562b5225cada4

警告:

  • kubeadm jointoken有效期是24小时,保存证书的Secret 有效期2小时

  • kubeadmadmin.conf 中的证书进行签名时,将其配置为 Subject: O = system:masters, CN = kubernetes-adminsystem:masters 是一个例外的、超级用户组,可以绕过鉴权层(例如 RBAC)。 不要将 admin.conf 文件与任何人共享,应该使用 kubeadm kubeconfig user 命令为其他用户生成 kubeconfig 文件,完成对他们的定制授权。

方式二:通过命令行

<root@k8s-master1 ~># kubeadm version

kubeadm init --help
--apiserver-advertise-address apiserver所公布的其正在监听的IP地址。apiserver 所在主机IP即master地址
-pod-network-cidr  pod网络(flanne插件,默认是100.244.0.0/16  calico 默认: 192.168.0.0/16
)
--image-repository registry.aliyuncs.com/google_container(看情况:如要已经下载好了镜像可以不加)

<root@k8s-master1 ~>#  kubeadm init  \
--control-plane-endpoint "192.168.19.155:16443" \
--image-repository registry.aliyuncs.com/google_containers \
--service-cidr=10.96.0.0/12 \
--pod-network-cidr=172.16.0.0/16 \
--upload-certs | tee kubeadm-init.log
 
# --upload-certs 标志用来将在所有控制节点实例之间的共享证书上传到集群。 
# 如果希望手动地通过控制平面节点或者使用自动化 工具复制证书则不加--upload-certs
# --control-plane-endpoint  设置成负载均衡器的地址或 DNS 和端口
# --kubernetes-version 设置要使用的 Kubernetes 版本。 建议将 kubeadm、kebelet、kubectl 和 Kubernetes 的版本匹配。

 kubeadm init  \
--control-plane-endpoint "192.168.19.155:16443" \
--image-repository registry.aliyuncs.com/google_containers \
--service-cidr=10.96.0.0/12 \
--pod-network-cidr=172.16.0.0/16 \
--upload-certs | tee kubeadm-init.log

 kubeadm init \
  --control-plane-endpoint=192.168.100.16:8443 \ #记得对应修改你的虚拟IP和端口port
  --image-repository registry.aliyuncs.com/google_containers \
  --kubernetes-version v1.22.4 \
  --service-cidr=10.96.0.0/12 \
  --pod-network-cidr=172.16.0.0/16 \
  --upload-certs


#定义--pod-network-cidr和--service-cidr时候规划网段要记得和宿主机中已有的网段不能冲突
kubeadm init --apiserver-advertise-address=10.0.0.10 --control-plane-endpoint=10.0.0.100 --apiserver-bind-port=6443 --kubernetes-version=v1.20.5 --pod-network-cidr=10.100.0.0/16 --service-cidr=10.200.0.0/16 --service-dns-domain=zilong.local  --image-repository=registry.cn-hangzhou.aliyuncs.com/google_containers --ignore-preflight-errors=swap

再次运行 kubeadm init,你必须首先卸载集群

令牌用于控制平面节点和加入节点之间的相互身份验证。 这里包含的令牌是密钥。确保它的安全, 因为拥有此令牌的任何人都可以将经过身份验证的节点添加到你的集群中。 可以使用 kubeadm token 命令列出,创建和删除这些令牌

<root@k8s-master1 ~># kubeadm token list
TOKEN                     TTL         EXPIRES                USAGES                   DESCRIPTION                                                EXTRA GROUPS
abcdef.0123456789abcdef   23h         2021-12-01T11:03:41Z   authentication,signing   <none>                                                     system:bootstrappers:kubeadm:default-node-token
syon5h.cjn0ybfbojaoq79u   1h          2021-11-30T13:03:40Z   <none>                   Proxy for managing TTL for the kubeadm-certs secret        <none>

<root@k8s-master1 ~># kubectl get cs
Warning: v1 ComponentStatus is deprecated in v1.19+
NAME                 STATUS      MESSAGE                                                                                       ERROR
scheduler            Unhealthy   Get "http://127.0.0.1:10251/healthz": dial tcp 127.0.0.1:10251: connect: connection refused
controller-manager   Healthy     ok
etcd-0               Healthy     {"health":"true","reason":""}
# k8s-master1 状态是NotReady 是以为还没配置网络
<root@k8s-master1 ~># kubectl get nodes -o wide
NAME          STATUS     ROLES                  AGE   VERSION
k8s-master1   NotReady   control-plane,master   23m   v1.22.4

2. 安装 Pod 网络附加组件

Pod网络选择:https://kubernetes.io/zh/docs/concepts/cluster-administration/networking/#how-to-implement-the-kubernetes-networking-model

每个集群只能安装一个 Pod 网络。

安装 Pod 网络后,您可以通过在 kubectl get pods --all-namespaces 输出中检查 CoreDNS Pod 是否 Running 来确认其是否正常运行。 一旦 CoreDNS Pod 启用并运行,你就可以继续加入节点。

2.1 calico

参考官网:https://docs.projectcalico.org/getting-started/kubernetes/quickstart

1.安装Tigera Calico运算符和自定义资源定义。

<root@k8s-master1 ~># wget https://docs.projectcalico.org/manifests/tigera-operator.yaml --no-check-certificate
<root@k8s-master1 ~># kubectl create -f tigera-operator.yaml

2.通过创建必要的自定义资源来安装Calico。有关此清单中可用配置选项的详细信息,请参阅安装参考.

<root@k8s-master1 ~># wget https://docs.projectcalico.org/manifests/custom-resources.yaml --no-check-certificate

<root@k8s-master1 ~># kubectl create -f custom-resources.yaml
如果配置错可以删除再来
<root@k8s-master1 ~># kubectl delete -f https://docs.projectcalico.org/manifests/custom-resources.yaml

## 注意,如果你init配置的是 --pod-network-cidr=192.168.0.0/16,那就不用改,直接运行即可,否则你需要把文件先下来,改成你配置的,再创建

image-20211130192847190

3.使用以下命令确认所有pod都在运行

<root@k8s-master1 ~>#  watch kubectl get pods -n calico-system

CoreDNS Pod 是否 Running
<root@k8s-master1 ~># kubectl get pods --all-namespaces -o wide

要确认所有的pod为running状态
<root@k8s-master1 ~>#  kubectl get pods -n kube-system
<root@k8s-master1 ~>#  kubectl get nodes -o wide
<root@k8s-master1 ~>#  kubectl describe nodes

每个集群只能安装一个 Pod 网络。

安装 Pod 网络后,您可以通过在 kubectl get pods --all-namespaces 输出中检查CoreDNS Pod是否 Running 来确认其是否正常运行。 一旦 CoreDNS Pod 启用并运行,你就可以继续加入节点。

2.2 flannel

官网:https://github.com/flannel-io/flannel#flannel

# kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml

3. 控制节点隔离

默认情况下,出于安全原因,你的集群不会在控制平面节点上调度 Pod。 如果你希望能够在控制平面节点上调度 Pod

<root@k8s-master1 ~># kubectl taint nodes --all node-role.kubernetes.io/master-

这将从任何拥有 node-role.kubernetes.io/master taint 标记的节点中移除该标记, 包括控制平面节点,这意味着调度程序将能够在任何地方调度 Pods。

4. 加入节点

节点是你的工作负载(容器和 Pod 等)运行的地方。要将新节点添加到集群,请对每台计算机执行以下操作:

  • SSH 到机器
  • 成为 root (例如 sudo su -
  • 运行 kubeadm init 输出的命令。例如:
kubeadm join --token <token> <control-plane-host>:<control-plane-port> --discovery-token-ca-cert-hash sha256:<hash>

集群注册token的有效时间为24小时,如果集群创建完成后没有及时添加工作节点,那么我们需要重新生成token。kubeadm token list可以查看令牌token 的过期时间

令牌用于控制平面节点和加入节点之间的相互身份验证

列出所有的引导令牌。
<root@k8s-master1 ~># kubeadm token list
TOKEN     TTL         EXPIRES            USAGES             DESCRIPTION            EXTRA GROUPS
abcdef.0123  23h   2021-12-01T11:03:41Z  authentication,signing   <none>                                                                              system:bootstrappers:kubeadm:default-node-token
syon5h.cjn0  1h    2021-11-30T13:03:40Z   <none>       Proxy for managing TTL for the kubeadm-certs secret        <none>

# 删除令牌
kubeadm token delete
# 生成令牌 
kubeadm token generate

5. 令牌token 过期解决办法

生成一个令牌token    --token
# kubeadm token generate
生成一个密钥 Secret  --certificate-key
# kubeadm certs certificate-key

默认情况下,令牌会在24小时后过期。如果要在当前令牌过期后将节点加入集群, 则可以通过在控制平面节点上运行以下命令来创建新令牌:

方法一

生成token
<root@k8s-master1 ~># kubeadm token generate
zw44us.65tarz882f7yw27p

根据生成的token,输出添加命令
# kubeadm token create <token> --print-join-command --ttl=0
<root@k8s-master1 ~># kubeadm token create zw44us.65tarz882f7yw27p --print-join-command --ttl=0
kubeadm join 192.168.0.105:6443 --token zw44us.65tarz882f7yw27p --discovery-token-ca-cert-hash sha256:b90746b53da98accc1245eafc4369036739e4f2451b213166805772252235439

查看生成的令牌
<root@k8s-master1 ~># kubeadm token list

方法二

kubeadm join --token <token> <control-plane-host>:<control-plane-port> --discovery-token-ca-cert-hash sha256:<hash>
# 生成token
--token
<root@k8s-master1 ~># kubeadm token list
<root@k8s-master1 ~># kubeadm token create
kiblue.uc8sixluj7xdw8du

# 生成token-ca-cert-hash
--discovery-token-ca-cert-hash
<root@k8s-master1 ~># openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | \
   openssl dgst -sha256 -hex | sed 's/^.* //'
   
b90746b53da98accc1245eafc4369036739e4f2451b213166805772252235439   

# 把生成的token和 hash 替换
<root@k8s-master1 ~># kubeadm join --token <token> <control-plane-host>:<control-plane-port> --discovery-token-ca-cert-hash sha256:<hash>   
<root@k8s-master1 ~># kubeadm join 192.168.0.105:6443 --token kiblue.uc8sixluj7xdw8du \
> --discovery-token-ca-cert-hash b90746b53da98accc1245eafc4369036739e4f2451b213166805772252235439


<root@k8s-master1 ~># kubeadm token list

6. 加入k8s-master2节点到集群

通过将参数 --upload-certs 添加到 kubeadm init,你可以将控制平面证书临时上传到集群中的 Secret。 请注意,此 Secret 将在 2 小时后自动过期。

①证书没过期(2小时内)

--control-plane   通知 kubeadm join 创建一个新的控制平面。

--certificate-key ... 从集群中的 kubeadm-certs  Secret 下载 控制平面证书并使用给定的密钥进行解密

<root@k8s-master2 ~># kubeadm join 192.168.19.155:16443 \
--token abcdef.0123456789abcdef \
--discovery-token-ca-cert-hash sha256:8ccf4d292eab06399b61d572c27f06879c9a8dfae9a2487d7b7562b5225cada4 \
--control-plane 
--certificate-key 140170149e5095b7eb96454e82153b7f2c966274965e646a330297d0c731264c

②如果证书过期(2小时之后)重新将控制平面证书上传到集群

通过将参数 --upload-certs 添加到 kubeadm init,你可以将控制平面证书临时上传到集群中的 Secret。 请注意,此 Secret 将在 2 小时后自动过期。证书使用 32 字节密钥加密,可以使用 --certificate-key 指定。 通过将 --control-plane--certificate-key 传递给 kubeadm join, 可以在添加其他控制平面节点时使用相同的密钥下载证书。

以下阶段命令可用于证书到期后重新上传证书:

<root@k8s-master2 ~># kubeadm init phase upload-certs --upload-certs --certificate-key=SOME_VALUE --config=SOME_YAML_FILE

如果未将参数 --certificate-key 传递给 kubeadm initkubeadm init phase upload-certs, 则会自动生成一个新密钥。

以下命令可用于按需生成新密钥:

<root@k8s-master2 ~># kubeadm certs certificate-key

示例:

要重新上传证书并生成新的解密密钥,请在已加入集群节点的控制平面上使用以下命令:

# 重新上传证书并生成新的解密密钥
<root@k8s-master2 ~># sudo kubeadm init phase upload-certs --upload-certs
[upload-certs] Storing the certificates in Secret "kubeadm-certs" in the "kube-system" Namespace
[upload-certs] Using certificate key:
1973a75317d750e6fdbf3c4610feb76583533e5e0137bdfb5488e1bfcc4a4a3a

# 用上面生成的解密密钥certificate key 替换下面的--certificate-key

<root@k8s-master2 ~># kubeadm join 192.168.19.155:16443 \
--token abcdef.0123456789abcdef \
--discovery-token-ca-cert-hash sha256:8ccf4d292eab06399b61d572c27f06879c9a8dfae9a2487d7b7562b5225cada4 \
--control-plane 
--certificate-key 1973a75317d750e6fdbf3c4610feb76583533e5e0137bdfb5488e1bfcc4a4a3a

③手动分发证书到k8s-master2 适用于初始化的时候没加 --upload-certs

如果你选择不将 kubeadm init--upload-certs 命令一起使用, 则意味着你将必须手动将证书从主控制平面节点复制到 将要加入的控制平面节点上。

使用 sshscp

第一步:应该在运行过 kubeadm init 命令的第一个 控制平面节点上运行以下脚本。 该脚本会将证书从第一个控制平面节点复制到另一个控制平面节点:

在以下示例中,用其他控制平面节点的 IP 地址替换 CONTROL_PLANE_IPS

USER=ubuntu # 可定制
CONTROL_PLANE_IPS="10.0.0.7 10.0.0.8"
for host in ${CONTROL_PLANE_IPS}; do
    scp /etc/kubernetes/pki/ca.crt "${USER}"@$host:
    scp /etc/kubernetes/pki/ca.key "${USER}"@$host:
    scp /etc/kubernetes/pki/sa.key "${USER}"@$host:
    scp /etc/kubernetes/pki/sa.pub "${USER}"@$host:
    scp /etc/kubernetes/pki/front-proxy-ca.crt "${USER}"@$host:
    scp /etc/kubernetes/pki/front-proxy-ca.key "${USER}"@$host:
    scp /etc/kubernetes/pki/etcd/ca.crt "${USER}"@$host:etcd-ca.crt
    scp /etc/kubernetes/pki/etcd/ca.key "${USER}"@$host:etcd-ca.key
done

注意:

只需要复制上面列表中的证书。kubeadm 将负责生成其余证书以及加入控制平面实例所需的 SAN。 如果你错误地复制了所有证书,由于缺少所需的 SAN,创建其他节点可能会失败。

第二步:在每个即将加入集群的控制平面节点上,你必须先运行以下脚本,然后 再运行 kubeadm join。 该脚本会将先前复制的证书从主目录移动到 /etc/kubernetes/pki

USER=ubuntu # 可定制
mkdir -p /etc/kubernetes/pki/etcd
mv /home/${USER}/ca.crt /etc/kubernetes/pki/
mv /home/${USER}/ca.key /etc/kubernetes/pki/
mv /home/${USER}/sa.pub /etc/kubernetes/pki/
mv /home/${USER}/sa.key /etc/kubernetes/pki/
mv /home/${USER}/front-proxy-ca.crt /etc/kubernetes/pki/
mv /home/${USER}/front-proxy-ca.key /etc/kubernetes/pki/
mv /home/${USER}/etcd-ca.crt /etc/kubernetes/pki/etcd/ca.crt
mv /home/${USER}/etcd-ca.key /etc/kubernetes/pki/etcd/ca.key

7. 添加工作节点到集群

<root@k8s-worker1 ~># kubeadm join 192.168.19.155:16443 \
--token abcdef.0123456789abcdef \
--discovery-token-ca-cert-hash sha256:8ccf4d292eab06399b61d572c27f06879c9a8dfae9a2487d7b7562b5225cada4 | tee k8s-worker1-init.log
<root@k8s-worker1 ~># kubeadm join 192.168.19.155:16443 \
--token abcdef.0123456789abcdef \
--discovery-token-ca-cert-hash sha256:8ccf4d292eab06399b61d572c27f06879c9a8dfae9a2487d7b7562b5225cada4 | tee k8s-worker2-init.log

8. 验证集群

在控制节点上面验证:

# 查看节点状态
<root@k8s-master1 ~># kubectl describe node k8s-worker1
<root@k8s-master1 ~># kubectl get nodes -o wide
<root@k8s-master1 ~># kubectl get pods --all-namespaces -o wide 
# 组件状态
<root@k8s-master1 ~># kubectl get cs
<root@k8s-master1 ~># kubectl get componentstatuses
# 查看集群健康状态
# 如果命令 kubectl cluster-info 返回了 url,但你还不能访问集群,那可以用以下命令来检查配置是否妥当
<root@k8s-master1 ~># kubectl cluster-info
<root@k8s-master1 ~># kubectl get serviceaccount

给节点打角色标签

<root@k8s-master1 ~># kubectl get nodes -o wide
# 增加节点标签  = 代表增加标签
<root@k8s-master1 ~># kubectl label nodes k8s-worker2 node-role.kubernetes.io/worker2=
<root@k8s-master1 ~># kubectl label nodes k8s-worker1 node-role.kubernetes.io/worker1=
<root@k8s-master1 ~># kubectl get nodes
NAME          STATUS   ROLES                  AGE     VERSION
k8s-master1   Ready    control-plane,master   3h50m   v1.22.4
k8s-master2   Ready    control-plane,master   127m    v1.22.4
k8s-worker1   Ready    worker1                134m    v1.22.4
k8s-worker2   Ready    worker2                133m    v1.22.4
# 删除节点标签
<root@k8s-master1 ~># kubectl label nodes k8s-worker2 node-role.kubernetes.io/worker2-
<root@k8s-master1 ~># kubectl label nodes k8s-worker1 node-role.kubernetes.io/worker1-

可能出现的问题

报错:Unhealthy

<root@k8s-master1 ~># kubectl get cs
Warning: v1 ComponentStatus is deprecated in v1.19+
NAME                 STATUS      MESSAGE                                 ERROR
scheduler            Unhealthy   Get "http://127.0.0.1:10251/healthz": dial tcp 127.0.0.1:10251: connect: connection refused
controller-manager   Healthy     ok
etcd-0               Healthy     {"health":"true","reason":""}

解决办法:两个控制节点都要做


<root@k8s-master1 ~>#  kubectl version
<root@k8s-master1 ~># vim /etc/kubernetes/manifests/kube-controller-manager.yaml
<root@k8s-master1 ~># vim /etc/kubernetes/manifests/kube-scheduler.yaml
# 注释掉port=0这一行

所有节点重启kubelet
<root@k8s-master1 ~># systemctl restart kubelet.service
主节点再次执行kubectl get cs
<root@k8s-master1 ~># kubectl get cs
Warning: v1 ComponentStatus is deprecated in v1.19+
NAME                 STATUS    MESSAGE             ERROR
scheduler            Healthy   ok
controller-manager   Healthy   ok
etcd-0               Healthy   {"health":"true"}

9. 各个组件端口

image-20211129223608735

5. kubectl 命令补全

https://www.cnblogs.com/xlizi/p/13452518.html

  • kubelet:在集群中的每个节点上用来启动 Pod 和容器等。
  • kubectl:用来与集群通信的命令行工具。

kubectl 的可选配置

启用 shell 自动补全功能

补全脚本依赖于工具 [bash-completion](

①安装 bash-completion

检查是否安装 bash-completion

# yum install bash-completion
##上述命令将创建文件 /usr/share/bash-completion/bash_completion,它是 bash-completion 的主脚本


# source /usr/share/bash-completion/bash_completion 或  exec bash 

②启动 kubectl 自动补全功能

需要确保一点:kubectl 补全脚本已经导入(sourced)到 shell 会话中。 这里有两种验证方法:

  • 在文件 ~/.bashrc 中导入(source)补全脚本:
kubectl completion -h
echo 'source <(kubectl completion bash)' >> ~/.bashrc
source $HOME/.bash_profile 或  exec bash 
  • 将补全脚本添加到目录 /etc/bash_completion.d 中:
[root@XXX ~]# kubectl completion bash > /etc/bash_completion.d/kubectl

若要将kubectl自动补全添加到当前shell

# exec bash 
[root@XXX ~]# source <(kubectl completion bash)
查看kubectl completion
[root@XXX ~]# kubectl completion -h

六、高可用集群的验证

由于本人开始选择的是两个master节点,不管怎么验证,想了所有出现的可能都失败。看官网猜测可能是mater节点太少。

1. 官网基础设施要求

对于这两种方法,你都需要以下基础设施:

  • 配置满足 kubeadm 的最低要求 的三台机器作为控制面节点
  • 配置满足kubeadm的最低要求的三台机器作为工作节点
  • 在集群中,确保所有计算机之间存在全网络连接(公网或私网)
  • 在所有机器上具有 sudo 权限
  • 从某台设备通过 SSH 访问系统中所有节点的能力
  • 所有机器上已经安装 kubeadmkubeletkubectl 是可选的。

仅对于外部 etcd 集群来说,你还需要:

  • 给 etcd 成员使用的另外三台机器

2. 解决办法

新增一台master节点,或把一个node节点改为master节点,由于新增master节点还有要资源,而且需要重新配置环境和镜像准备,关键是没难度。所有这儿我选择nodemaster,恢复快照不可能滴!

模拟:

在硬件升级、硬件维护的情况下,我们需要将某些Node进行隔离,脱离k8s的调度范围。k8s提供了一套机制,既可以将Node纳入调度范围,也可以将Node脱离调度范围。

第一步:拷贝admin.conf

为了使 kubectl 在其他计算机,将管理员 kubeconfig 文件从控制平面节点复制到工作

#方法一:
<root@k8s-worker2 ~># scp root@192.168.19.153:/etc/kubernetes/admin.conf .
<root@k8s-worker2 ~># ls
admin.conf  anaconda-ks.cfg  k8s-image.list  kubeadm-config.yaml
<root@k8s-worker2 ~># kubectl --kubeconfig ./admin.conf get nodes
NAME          STATUS   ROLES                  AGE   VERSION
k8s-master1   Ready    control-plane,master   19h   v1.22.4
k8s-master2   Ready    control-plane,master   17h   v1.22.4
k8s-worker1   Ready    worker1                17h   v1.22.4
k8s-worker2   Ready    worker2                17h   v1.22.4

#方法二:
准备集群管理配置文件
<root@k8s-worker2 ~># mkdir .kube
<root@k8s-worker2 ~># scp master1:/root/.kube/config .kube/
使用命令验证
<root@k8s-worker2 ~># kubectl get nodes
<root@k8s-worker2 ~># kubectl cluster-info

说明:

上面的示例假定为 root 用户启用了SSH访问。如果不是这种情况, 你可以使用 scp 将 admin.conf 文件复制给其他允许访问的用户。

admin.conf 文件为用户提供了对集群的超级用户特权。 该文件应谨慎使用。对于普通用户,建议生成一个你为其授予特权的唯一证书。

第二步:清理node 节点

`让k8s-worker2 不可调度
<root@k8s-worker2 ~># kubectl --kubeconfig ./admin.conf cordon k8s-worker2
<root@k8s-worker2 ~># kubectl --kubeconfig ./admin.conf describe node k8s-worker2
<root@k8s-worker2 ~># kubectl --kubeconfig ./admin.conf get nodes
NAME          STATUS                     ROLES                  AGE   VERSION
k8s-master1   Ready                      control-plane,master   19h   v1.22.4
k8s-master2   Ready                      control-plane,master   17h   v1.22.4
k8s-worker1   Ready                      worker1                17h   v1.22.4
k8s-worker2   Ready,SchedulingDisabled   worker2                17h   v1.22.4

<root@k8s-worker2 ~># kubectl --kubeconfig ./admin.conf get pods -A
`把运行在worker2上面的负载pod 驱逐
<root@k8s-worker2 ~># kubectl taint nodes node1 key1=value1:NoExecute 
<root@k8s-worker2 ~># kubectl drain <node name> --delete-emptydir-data --force --ignore-daemonsets

# 在删除节点之前,请重置 kubeadm 安装的状态:
<root@k8s-worker2 ~># kubeadm reset

<root@k8s-worker2 ~>#  rm -rf $HOME/.kube/config
<root@k8s-worker2 ~>#  rm -rf /etc/cni/net.d
# 重置过程不会重置或清除 iptables 规则或 IPVS 表
<root@k8s-worker2 ~>#  ipvsadm -ln
<root@k8s-worker2 ~>#  ipvsadm --clear
<root@k8s-worker2 ~>#  ipvsadm -C
<root@k8s-worker2 ~>#  iptables -nL
<root@k8s-worker2 ~>#  iptables -F && iptables -t nat -F && iptables -t mangle -F && iptables -X
# 删除节点
<root@k8s-worker2 ~>#  kubectl --kubeconfig ./admin.conf delete node k8s-worker2

<root@k8s-master1 ~># kubectl get nodes
NAME          STATUS   ROLES                  AGE   VERSION
k8s-master1   Ready    control-plane,master   20h   v1.22.4
k8s-master2   Ready    control-plane,master   18h   v1.22.4
k8s-worker1   Ready    worker1                18h   v1.22.4

# 重新开始,只需运行 kubeadm init 或 kubeadm join 并加上适当的参数


第三步:把worker2 变为master3并加入集群

重新开始,只需运行 kubeadm initkubeadm join 并加上适当的参数

# 修改主机名k8s-worker2变为k8s-master3
<root@k8s-worker2 ~># hostnamectl set-hostname k8s-master3

生成token
<root@k8s-master1 ~># kubeadm token generate
zw44us.65tarz882f7yw27p

根据生成的token,输出添加命令
# kubeadm token create <token> --print-join-command --ttl=0
<root@k8s-master1 ~># kubeadm token create zw44us.65tarz882f7yw27p --print-join-command --ttl=0
kubeadm join 192.168.0.105:6443 --token zw44us.65tarz882f7yw27p --discovery-token-ca-cert-hash sha256:b90746b53da98accc1245eafc4369036739e4f2451b213166805772252235439

查看生成的令牌
<root@k8s-master1 ~># kubeadm token list

-----------------------------------
`第一步:根据生成的token,输出添加命令
<root@k8s-master1 ~># kubeadm token create $(kubeadm token generate) --print-join-command --ttl=0
kubeadm join 192.168.19.155:16443 --token ban71s.8p09rc2l9yqe92y4 --discovery-token-ca-cert-hash sha256:8ccf4d292eab06399b61d572c27f06879c9a8dfae9a2487d7b7562b5225cada4

`第二步:要重新上传证书并生成新的解密密钥,请在已加入集群节点的控制平面上使用以下命令:
由于是添加master节点还需要生成master节点所需证书,通过Secret(也可以手动分发证书到k8s-master3)
如果是添加node节点上面第一步的命令就可以了

# 重新上传证书Secret
kubeadm init phase upload-certs --upload-certs --certificate-key=SOME_VALUE --config=SOME_YAML_FILE

# 生成:--certificate-key (--certificate-key=SOME_VALUE 可以省略)
<root@k8s-master1 ~># kubeadm certs certificate-key
a222218f73363bac3801ee7699ddf3e4f36b7e4c12a8696d0e377486a838fd26
<root@k8s-master1 ~># kubeadm init phase upload-certs \
--upload-certs \
--certificate-key=a222218f73363bac3801ee7699ddf3e4f36b7e4c12a8696d0e377486a838fd26
[upload-certs] Storing the certificates in Secret "kubeadm-certs" in the "kube-system" Namespace
[upload-certs] Using certificate key:
a222218f73363bac3801ee7699ddf3e4f36b7e4c12a8696d0e377486a838fd26

# 用上面生成的解密密钥certificate key 替换下面的--certificate-key
<root@k8s-worker2 ~># kubeadm join 192.168.19.155:16443 \
--token ban71s.8p09rc2l9yqe92y4 \
--discovery-token-ca-cert-hash sha256:8ccf4d292eab06399b61d572c27f06879c9a8dfae9a2487d7b7562b5225cada4 \
--control-plane \
--certificate-key a222218f73363bac3801ee7699ddf3e4f36b7e4c12a8696d0e377486a838fd26

To start administering your cluster from this node, you need to run the following as a regular user:

        mkdir -p $HOME/.kube
        sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
        sudo chown $(id -u):$(id -g) $HOME/.kube/config

Run 'kubectl get nodes' to see this node join the cluster.



`第三步:任何master节点都可以验证
<root@k8s-master3 ~># kubectl get nodes
NAME          STATUS   ROLES                  AGE   VERSION
k8s-master1   Ready    control-plane,master   20h   v1.22.4
k8s-master2   Ready    control-plane,master   18h   v1.22.4
k8s-worker1   Ready    worker1                18h   v1.22.4
k8s-worker2   Ready    control-plane,master   69s   v1.22.4

`第四不:让这个节点恢复调度(因为重新加入的没必要做)
<root@k8s-master1 ~># kubectl cordon k8s-worker2

<root@k8s-worker2 ~># kubectl describe nodes k8s-worker2

`第四步:由于只有一个worker节点,设置允许让master3 上可以运行工作负载pod
<root@k8s-worker2 ~># kubectl taint nodes k8s-worker2 node-role.kubernetes.io/master-
node/k8s-worker2 untainted
<root@k8s-worker2 ~># kubectl describe nodes k8s-worker2

修改主机名k8s-worker2变为k8s-master3,日志这步在加入之前就应该做的

3. 验证集群的高可用

修改keepalived配置文件

三个主节点所需的脚本,有两个脚本用哪个都可以

# 检测haproxy 是否正常运行
<root@k8s-x ~># cat /etc/keepalived/check_haproxy.sh
#!/bin/bash

haproxy_status=`ps -C haproxy --no-header |wc -l`
if [ $haproxy_status -eq 0 ];then
 systemctl start haproxy
 sleep 3;
 if [ `ps -C haproxy --no-header |wc -l` -eq 0 ];then
        systemctl stop keepalived
 fi
fi
# 检测apiserver 是否正常运行(其实也就是检测haproxy是否正常运行,因为它在访问16643端口,16443端口也就是haproxy的前端端口)
<root@k8s-x ~># cat /etc/keepalived/check_apiserver.sh
#!/bin/sh

errorExit() {
    echo "*** $*" 1>&2
    exit 1
}

curl --silent --max-time 2 --insecure https://localhost:16443/ -o /dev/null || errorExit "Error GET https://localhost:16443/"
if ip addr | grep -q 192.168.19.155; then
    curl --silent --max-time 2 --insecure https://192.168.19.155:16443/ -o /dev/null || errorExit "Error GET https://192.168.19.155:16443/"
fi


<root@k8s-master1 ~># cat /etc/keepalived/keepalived.conf
! Configuration File for keepalived

global_defs {
   router_id LVS_DEVEL
}

vrrp_script check_apiserver {
  script "/etc/keepalived/check_apiserver.sh"
  #script "/etc/keepalived/aa.sh"
  interval 3   # #每个多长时间检测一次 
  # 当/etc/keepalived/check_apiserver.sh 执行脚本的执行结果为非0,并且weight为负数则优先级会减少
  # 比如 有默认设置的100 减少为70,这时候master2权重90 和 master3权重80 都比这个高,所有master1上的VIP
  # 会漂移到master2上,一定要把weight 根据实际设置合适的大小。
  # 还有就是注意不抢占模式nopreempt 的使用
  # 非抢占模式即MASTER恢复后不抢占BACKUP升级为MASTER后的VIP
  weight -30    
  fall 2       # 执行失败多少次才认为失败(非0 状态) 
  rise 2       # 执行成功多少次才认为是成功
}
#vrrp_script check_haproxy {
#  script "/etc/keepalived/aa.sh"
#  interval 10
#  weight -5
#  fall 2
#  rise 2
#}


vrrp_instance VI_1 {
    state BACKUP
    interface ens33
    virtual_router_id 51
    priority 100
    advert_int 1
    nopreempt
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    unicast_src_ip 192.168.19.152
    unicast_peer {
        192.168.19.153
        192.168.19.154
    }
    virtual_ipaddress {
        192.168.19.155
    }
    track_script {
        check_apiserver
        #check_haproxy
    }
    
    notify_master "systemctl start haproxy"     # 当成为master时就启动haproxy服务
    # 当成为backup时就关闭haproxy服务,
    notify_backup "systemctl stop haproxy"      # 当成为backup时就关闭haproxy服务
}
<root@k8s-master2 ~># cat /etc/keepalived/keepalived.conf
! Configuration File for keepalived

global_defs {
   router_id LVS_DEVEL
   #vrrp_strict
}

vrrp_script check_apiserver {
  script "/etc/keepalived/check_apiserver.sh"
  interval 3
  weight -30  
  fall 10
  rise 2
}

vrrp_instance VI_1 {
    state BACKUP
    interface ens33
    virtual_router_id 51
    priority 90
    advert_int 1
    nopreempt
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    unicast_src_ip 192.168.19.153
    unicast_peer {
        192.168.19.152
        192.168.19.154
    }

    virtual_ipaddress {
        192.168.19.155
    }
    track_script {
        check_apiserver
    }
}

<root@k8s-worker2 ~># cat /etc/keepalived/keepalived.conf
! Configuration File for keepalived

global_defs {
   router_id LVS_DEVEL
}

vrrp_script check_apiserver {
  script "/etc/keepalived/check_apiserver.sh"
  interval 3
  weight -30  
  fall 10
  rise 2
}

vrrp_instance VI_1 {
    state BACKUP         #
    interface ens33
    virtual_router_id 51
    priority 80           # 
    advert_int 1
    nopreempt
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    unicast_src_ip 192.168.19.154
    unicast_peer {
        192.168.19.152
        192.168.19.153
    }
    virtual_ipaddress {
        192.168.19.155
    }
    track_script {
        check_apiserver
    }

}



修改haproxy配置文件

master节点配置都一样

# cat /etc/haproxy/haproxy.cfg
#---------------------------------------------------------------------
# Example configuration for a possible web application.  See the
# full configuration options online.
#
#   http://haproxy.1wt.eu/download/1.4/doc/configuration.txt
#
#---------------------------------------------------------------------

#---------------------------------------------------------------------
# Global settings
#---------------------------------------------------------------------
global
    # to have these messages end up in /var/log/haproxy.log you will
    # need to:
    #
    # 1) configure syslog to accept network log events.  This is done
    #    by adding the '-r' option to the SYSLOGD_OPTIONS in
    #    /etc/sysconfig/syslog
    #
    # 2) configure local2 events to go to the /var/log/haproxy.log
    #   file. A line like the following can be added to
    #   /etc/sysconfig/syslog
    #
    #    local2.*                       /var/log/haproxy.log
    #
    log         127.0.0.1 local2

    chroot      /var/lib/haproxy
    pidfile     /var/run/haproxy.pid
    maxconn     4000
    user        haproxy
    group       haproxy
    daemon

    # turn on stats unix socket
    stats socket /var/lib/haproxy/stats

#---------------------------------------------------------------------
# common defaults that all the 'listen' and 'backend' sections will
# use if not designated in their block
#---------------------------------------------------------------------
defaults
    mode                    http
    log                     global
    option                  httplog
    option                  dontlognull
    option http-server-close
    option forwardfor       except 127.0.0.0/8
    option                  redispatch
    retries                 3
    timeout http-request    10s
    timeout queue           1m
    timeout connect         10s
    timeout client          1m
    timeout server          1m
    timeout http-keep-alive 10s
    timeout check           10s
    maxconn                 3000

#---------------------------------------------------------------------
# main frontend which proxys to the backends
#---------------------------------------------------------------------
#frontend  main *:88

frontend  apiserver
    stats uri /haproxy
    bind *:16443
    mode tcp
    default_backend             app

#---------------------------------------------------------------------
# round robin balancing between the various backends
#---------------------------------------------------------------------
backend app
    balance     roundrobin
    mode tcp
    server  k8s-master1 192.168.19.152:6443 check
    server  k8s-master2 192.168.19.153:6443 check
    server  k8s-worker2 192.168.19.154:6443 check

systemctl restart keepalived.service haproxy.service
systemctl enable keepalived.service haproxy.service

测试:

<root@k8s-worker2 ~># ip a s ens33
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 00:0c:29:83:33:9c brd ff:ff:ff:ff:ff:ff
    inet 192.168.19.154/24 brd 192.168.19.255 scope global noprefixroute dynamic ens33
       valid_lft 956sec preferred_lft 956sec
    inet 192.168.19.155/32 scope global ens33
       valid_lft forever preferred_lft forever
    inet6 fe80::6fd2:6c8a:c762:231c/64 scope link tentative noprefixroute dadfailed
       valid_lft forever preferred_lft forever
    inet6 fe80::3692:bf77:677c:fc68/64 scope link noprefixroute
       valid_lft forever preferred_lft forever
       
<root@k8s-worker2 ~># tcpdump vrrp -i ens33 -n
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ens33, link-type EN10MB (Ethernet), capture size 262144 bytes
16:25:06.356636 IP 192.168.19.154 > 192.168.19.152: VRRPv2, Advertisement, vrid 51, prio 80, authtype simple, intvl 1s, length 20
16:25:06.356895 IP 192.168.19.154 > 192.168.19.153: VRRPv2, Advertisement, vrid 51, prio 80, authtype simple, intvl 1s, length 20
16:25:07.358654 IP 192.168.19.154 > 192.168.19.152: VRRPv2, Advertisement, vrid 51, prio 80, authtype simple, intvl 1s, length 20
16:25:07.358750 IP 192.168.19.154 > 192.168.19.153: VRRPv2, Advertisement, vrid 51, prio 80, authtype simple, intvl 1s, length 20
16:25:08.359413 IP 192.168.19.154 > 192.168.19.152: VRRPv2, Advertisement, vrid 51, prio 80, authtype simple, intvl 1s, length 20
16:25:08.359497 IP 192.168.19.154 > 192.168.19.153: VRRPv2, Advertisement, vrid 51, prio 80, authtype simple, intvl 1s, length 20

<root@k8s-worker2 manifests># kubectl get pod -n kube-system
NAME                                  READY   STATUS    RESTARTS        AGE
coredns-7f6cbbb7b8-mrbpd              1/1     Running   1 (15h ago)     22h
coredns-7f6cbbb7b8-sc9mg              1/1     Running   1 (15h ago)     22h
etcd-k8s-master1                      1/1     Running   29 (83m ago)    22h
etcd-k8s-master2                      1/1     Running   21 (79m ago)    20h
etcd-k8s-worker2                      1/1     Running   1 (83m ago)     115m
kube-apiserver-k8s-master1            1/1     Running   25 (82m ago)    22h
kube-apiserver-k8s-master2            1/1     Running   28 (77m ago)    20h
kube-apiserver-k8s-worker2            1/1     Running   0               20s
kube-controller-manager-k8s-master1   1/1     Running   25 (155m ago)   22h
kube-controller-manager-k8s-master2   1/1     Running   11 (84m ago)    20h
kube-controller-manager-k8s-worker2   1/1     Running   1 (2m14s ago)   115m
kube-proxy-4wslp                      1/1     Running   1 (15h ago)     20h
kube-proxy-6mngd                      1/1     Running   1 (15h ago)     22h
kube-proxy-c9b4h                      1/1     Running   1               20h
kube-proxy-pnjzh                      1/1     Running   0               115m
kube-scheduler-k8s-master1            1/1     Running   24 (155m ago)   18h
kube-scheduler-k8s-master2            1/1     Running   9 (84m ago)     18h
kube-scheduler-k8s-worker2            1/1     Running   1 (2m14s ago)   115m

模拟当k8s-worker2节点挂了,分别在其他master节点验证集群是否可以用

  • k8s-worker2节点挂了,vip会飘逸到权重高的节点,这儿会漂移到master1节点,master2master3验证集群还是可用的
  • master2节点挂了,那就是真挂了,集群不能使用的

结论master节点高可用必须至少要三个节点。

4. kubectl proxy 让外部网络访问K8S service的ClusterIP

使用kubectl proxy命令就可以使API server监听在本地的8001端口上:

$ kubectl proxy --port=8009
Starting to serve on 127.0.0.1:8009

如果想通过其它主机访问就需要指定监听的地址:

$ kubectl proxy --address=0.0.0.0  --port=8009
Starting to serve on [::]:8009

此时通过curl访问会出现未认证的提示:

$ curl -X GET -L http://k8s-master:8009/
<h3>Unauthorized</h3>

设置API server接收所有主机的请求:

$ kubectl proxy --address='0.0.0.0'  --accept-hosts='^*$' --port=8009
Starting to serve on [::]:8009

访问正常:

<root@k8s-worker2 ~># curl -X GET -L http://k8s-master1:8009/api
{
  "kind": "APIVersions",
  "versions": [
    "v1"
  ],
  "serverAddressByClientCIDRs": [
    {
      "clientCIDR": "0.0.0.0/0",
      "serverAddress": "192.168.19.152:6443"
    }
  ]
}

两个点

  • haproxy挂了的话,要让keepalived的自动VIP切换
  • apiserver挂了的话,要让keepalived的自动VIP切换
vrrp_script chk_haproxy {
    script "/bin/bash -c 'if [[ $(netstat -nlp | grep 16443) ]]; then exit 0; else exit 1; fi'"  # haproxy 检测
    interval 2  # 每2秒执行一次检测
    weight 11 # 权重变化
}

-----------------------------
vrrp_script check_haproxy {
    script "killall -0 haproxy"
    interval 3
    weight -2
    fall 10
    rise 2
}

#!/bin/bash
# ps aux |grep -v grep|grep haproxy|wc -l
haproxy_status=`ps -C haproxy --no-header |wc -l`
if [ $haproxy_status -eq 0 ];then
 /root/nginx/sbin/nginx
 sleep 3;
 if [ `ps -C haproxy --no-header |wc -l` -eq 0 ];then
	killall keepalived
 fi
fi

#!/bin/bash

haproxy_status=`ps -C haproxy --no-header |wc -l`
if [ $haproxy_status -eq 0 ];then
 systemctl start haproxy
 sleep 3;
 if [ `ps -C haproxy --no-header |wc -l` -eq 0 ];then
        systemctl stop keepalived
 fi
fi



notify_master “systemctl start haproxy”     # 当成为master时就启动http服务
notify_backup “systemctl stop haproxy”      # 当成为backup时就关闭http服务
notify_fault “systemctl stop haproxy”       # 当节点故障时候


##VRRP script(s)
#添加一个用于定期指定的脚本,它的退出码将被所有的VRRP instance记录,它以非0的权重监视
vrrp_script<SCRIPT_NAME> {
     script   <STRING>|<QUOTED-STRING>   #执行脚本的路径
     interval <INTEGER>    #调用脚本两次之间的间隔,默认为1秒
     timeout <INEGER>     #第二次调用脚本后多长时间没有回应的超时时间
     weight  <INTEGER:-254..254>   #根据该权重,动态调整调用该脚本选项的vrrp实例优先级,默认是2. 当脚本执行码为0,权重大于0时,vrrp实例优先级增加;当脚本执行码为非0,权重小于0时,vrrp实例优先级减小,其他情况优先级不变。
                                                       #当该值为0时,不改变实例的优先级
     rise <INTEGER>    ##设置脚本返回值连续为0的次数。加入该参数后,当脚本执行码连续为0的次数为rise次,且权重大于0时,vrrp实例优先级增加
     fall  <INTEGER>    ##设置脚本返回值连续为0的次数。加入该参数后,当脚本执行码连续为非0的次数为rfall次,且权重小于0时,vrrp实例优先级增加
}

weight可正可负。为正时检测成功+weight,相当与节点检测失败时本身priority不变,但其他检测成功节点priority增加。为负时检测失败本身priority减少。

配置使用了一个健康检查脚本/etc/keepalived/check _ apiserver。负责确保保存虚拟IP的节点上的 API 服务器是可用的

#!/bin/sh

errorExit() {
    echo "*** $*" 1>&2
    exit 1
}

curl --silent --max-time 2 --insecure https://localhost:${APISERVER_DEST_PORT}/ -o /dev/null || errorExit "Error GET https://localhost:${APISERVER_DEST_PORT}/"
if ip addr | grep -q ${APISERVER_VIP}; then
    curl --silent --max-time 2 --insecure https://${APISERVER_VIP}:${APISERVER_DEST_PORT}/ -o /dev/null || errorExit "Error GET https://${APISERVER_VIP}:${APISERVER_DEST_PORT}/"
fi

七、k8s+kube-vip实现k8s高可用

https://github.com/kubernetes/kubeadm/blob/main/docs/ha-considerations.md#options-for-software-load-balancing

八、总结

其实如果基础硬件资源足够的情况下,应该把负载调度器单独用一台或多台,不应该用master节点来负载调度

image-20211201194928691

posted @ 2021-12-03 00:57  詹丶老头  阅读(83)  评论(0编辑  收藏  举报