CKS认证 | 使用kubeadm部署K8s高可用集群(v1.26)

一、前置知识点

1.1 生产环境可部署Kubernetes集群的两种方式

目前生产部署Kubernetes集群主要有两种方式:
1)kubeadm

Kubeadm是一个K8s部署工具,提供 kubeadm init 和 kubeadm join,用于快速部署Kubernetes集群(这里采用kubeadm搭建集群)。
2)二进制包

从github下载发行版的二进制包,手动部署每个组件,组成Kubernetes集群。


kubeadm工具功能:

  • **kubeadm init:**初始化一个Master节点

  • **kubeadm join:**将工作节点加入集群

  • **kubeadm upgrade:**升级K8s版本

  • **kubeadm token:**管理 kubeadm join 使用的令牌

  • **kubeadm reset:**清空 kubeadm init 或者 kubeadm join 对主机所做的任何更改

  • **kubeadm version:**打印 kubeadm 版本

  • **kubeadm alpha:**预览可用的新功能

1.2 准备环境

1)服务器要求:

  • 建议最小硬件配置:2核CPU、4G内存、30G硬盘

  • 服务器最好可以访问外网,会有从网上拉取镜像需求,如果服务器不能上网,需要提前下载对应镜像并导入节点。

2)软件环境:

|------------|----------------------|
| 软件 | 版本 |
| 操作系统 | CentOS7.9_x64 (mini) |
| Docker | 21-ce |
| Kubernetes | 1.26 |

3)服务器规划:

|-------------|--------------------|------------------------------|
| 角色 | IP | 主机名 |
| k8s-master1 | 192.168.1.81 | docker,etcd,nginx,keepalived |
| k8s-master2 | 192.168.1.82 | docker,etcd,nginx,keepalived |
| k8s-node1 | 192.168.1.83 | docker,etcd |
| 负载均衡器对外IP | 192.168.1.88 (VIP) | |

4)架构图:

二、环境准备

2.1 操作系统初始化配置(所有节点)

1)设置网卡命名(规范化)

bash 复制代码
cat /etc/default/grub
...
GRUB_CMDLINE_LINUX="crashkernel=auto rd.lvm.lv=centos/root rd.lvm.lv=centos/swap rhgb quiet net.ifnames=0 biosdevname=0"

...
grub2-mkconfig -o /boot/grub2/grub.cfg
reboot    //重启生效

2)配置IP地址(以1.71为例)

javascript 复制代码
[root@k8s-master1 ~]# cat /etc/sysconfig/network-scripts/ifcfg-eth0

IPADDR=192.168.1.81
PREFIX=24
GATEWAY=192.168.1.254
DNS1=223.5.5.5
...

3)关闭防火墙

javascript 复制代码
systemctl stop firewalld
systemctl disable firewalld

# 查看防火墙策略
iptables -vnL

4)关闭 selinux

javascript 复制代码
setenforce 0  # 临时配置
sed -i 's/enforcing/disabled/' /etc/selinux/config  # 永久配置

5)关闭 swap

javascript 复制代码
swapoff -a  # 临时
sed -ri 's/.*swap.*/#&/' /etc/fstab    # 永久

6)根据规划设置主机名

javascript 复制代码
hostnamectl set-hostname <hostname>

7)添加 hosts 解析

javascript 复制代码
cat >> /etc/hosts << EOF
192.168.1.81 k8s-master1 k8s-master1-1-81
192.168.1.82 k8s-master2 k8s-master2-1-82
192.168.1.83 k8s-node1 k8s-node1-1-83
EOF

8)将桥接的IPv4流量传递到 iptables 的链

javascript 复制代码
cat > /etc/sysctl.d/k8s.conf << EOF
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
sysctl --system  # 生效

9)配置YUM源

javascript 复制代码
wget -O /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo
wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo

10)时间同步

javascript 复制代码
yum install ntpdate -y
ntpdate time.windows.com
(echo "*/5 * * * * /usr/sbin/ntpdate -u time.windows.com") | crontab
crontab -l
*/5 * * * * /usr/sbin/ntpdate -u time.windows.com

11)配置内核模块

javascript 复制代码
cat >> /etc/modules-load.d/ipvs.conf <<EOF
ip_vs
ip_vs_rr
ip_vs_wrr
ip_vs_sh
nf_conntrack
ip_tables
ip_set
xt_set
ipt_set
ipt_rpfilter
ipt_REJECT
ipip
EOF

systemctl restart systemd-modules-load.service

lsmod | grep -e ip_vs -e nf_conntrack
ip_vs_sh 16384 0
ip_vs_wrr 16384 0
ip_vs_rr 16384 0
ip_vs 155648 6 ip_vs_rr,ip_vs_sh,ip_vs_wrr
nf_conntrack 139264 1 ip_vs
nf_defrag_ipv6 24576 2 nf_conntrack,ip_vs
nf_defrag_ipv4 16384 1 nf_conntrack
libcrc32c 16384 4 nf_conntrack,btrfs,raid456,ip_vs

12)内核参数调优

javascript 复制代码
cat <<EOF >> /etc/sysctl.conf
fs.file-max = 6815744
fs.nr_open = 1048576
net.ipv4.tcp_max_tw_buckets = 102400
net.ipv4.tcp_max_syn_backlog = 20480
net.core.somaxconn = 102400
net.core.netdev_max_backlog = 32768
net.ipv6.conf.all.disable_ipv6 = 0
net.ipv6.conf.default.disable_ipv6 = 0
net.ipv6.conf.lo.disable_ipv6 = 0
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv6.conf.all.forwarding = 1
EOF

13)下载基本软件

javascript 复制代码
yum -y install wget vim net-tools jq telnet lvm2 git tar curl nfs-kernel-server chrony bash-completion

三、部署 K8S集群 高可用

前言: Kubernetes 作为容器集群系统,通过 健康检查+重启策略 实现了Pod故障自我修复能力,通过调度算法实现将Pod分布式部署,并保持预期副本数,根据 Node失效状态自动在其他Node拉起Pod,实现了应用层的高可用性

针对Kubernetes集群,高可用性还应包含以下两个层面的考虑:Etcd数据库的高可用性 和 Kubernetes Master组件的高可用性。

  1. kubeadm搭建的K8s集群,Etcd只起了一个,存在单点,所以我们这里会独立再搭建一个Etcd集群。

  2. Master节点扮演着总控中心的角色,通过不断与工作节点上的 kubelet 和 kube-proxy 进行通信来维护整个集群的健康工作状态。如果Master节点故障,将无法使用 kubectl工具 或者 API做任何集群管理。

Master节点主要有三个服务:kube-apiserver、kube-controller-manager和kube-scheduler,其中kube-controller-manager和kube-scheduler组件自身通过选举机制已经实现了高可用,所以Master高可用主要针对kube-apiserver组件,而该组件是以HTTP API提供服务 ,因此对他高可用与Web服务器类似,增加负载均衡器对其负载均衡即可,并且可水平扩容。

--- kube-apiserver 高可用架构图:

  • Nginx 是一个主流Web服务和反向代理服务器,这里用四层实现对apiserver实现负载均衡。

  • Keepalived 是一个主流高可用软件,基于VIP绑定实现服务器双机热备,在上述拓扑中,Keepalived主要根据Nginx运行状态判断是否需要故障转移(偏移VIP),例如当Nginx主节点挂掉,VIP会自动绑定在Nginx备节点,从而保证VIP一直可用,实现Nginx高可用。

注:为了节省机器,这里与 K8s master节点机器复用。也可以独立于k8s集群之外部署,只要nginx与apiserver能通信就行。

3.1 安装 Nginx / keepalived ,实现 Master组件的高可用性

3.1.1 安装软件包(k8s-master1 主 / k8s-master2 备)

javascript 复制代码
yum install epel-release -y
yum install nginx keepalived -y

3.1.2 修改 Nginx 配置文件**(k8s-master1 主 / k8s-master2 备,文件一样)**

bash 复制代码
cat > /etc/nginx/nginx.conf << "EOF"
# For more information on configuration, see:
#   * Official English Documentation: http://nginx.org/en/docs/
#   * Official Russian Documentation: http://nginx.org/ru/docs/

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


# Load dynamic modules. See /usr/share/doc/nginx/README.dynamic.

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

events {
    worker_connections 1024;
}

# 使用Nginx的stream模块实现四层负载均衡,为两台Master apiserver组件提供负载均衡
stream {

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

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

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

# 不涉及http七层协议,所以无需配置
http {
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

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

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

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

注意:由于新版本的nginx没有stream模块,需要提前安装

javascript 复制代码
[root@k8s-master1 ~]# nginx -t    //查看服务状态(检测不到stream模块)
nginx: [emerg] unknown directive "stream" in /etc/nginx/nginx.conf:12
nginx: configuration file /etc/nginx/nginx.conf test failed

[root@k8s-master1 ~]# yum search stream | grep nginx   //通过yum找到nginx完整stream包名
nginx-mod-stream.x86_64 : Nginx stream modules
bash 复制代码
yum -y install nginx-mod-stream

3.1.3 修改 keepalived 配置文件( k8s-master1 主

  • vrrp_script:指定检查nginx工作状态脚本(16443是Nginx为实现负载均衡监听的端口,所以根据nginx状态判断是否故障转移)

  • virtual_ipaddress:指定虚拟IP(VIP)

**前提:**修改配置文件前,需要为【vrrp_script】字段准备检查nginx运行状态的脚本:

bash 复制代码
cat > /etc/keepalived/check_nginx.sh  << EOF
#!/bin/bash
code=$(curl -k https://127.0.0.1:16443/version -s -o /dev/null -w %{http_code})

if [ "$code" -ne 200 ];then
    exit 1
else
    exit 0    //0为工作正常
fi
EOF

chmod +x /etc/keepalived/check_nginx.sh    # 授予执行权限

注:keepalived 根据脚本返回状态码(0为工作正常,非0不正常),判断是否故障转移


bash 复制代码
cat > /etc/keepalived/keepalived.conf << EOF
global_defs { 
   notification_email { 
     acassen@firewall.loc 
     failover@firewall.loc 
     sysadmin@firewall.loc 
   } 
   notification_email_from Alexandre.Cassen@firewall.loc  
   smtp_server 127.0.0.1 
   smtp_connect_timeout 30 
   router_id NGINX_MASTER
} 

# 指定健康检查的脚本
vrrp_script check_nginx {
    script "/etc/keepalived/check_nginx.sh"    # 判断返回状态码
}

# 高可用配置
vrrp_instance VI_1 { 
    state MASTER              # 角色为 Nginx Master
    interface eth0            # 修改为实际网卡名
    virtual_router_id 51      # VRRP 路由 ID实例,每个实例是唯一的 
    priority 100              # 优先级,备服务器设置 90 
    advert_int 1              # 指定VRRP 心跳包通告间隔时间,默认1秒 
    authentication {          # 健康检查时,双击热备的认证口令(集群中存在多套keepalived)
        auth_type PASS      
        auth_pass 1111 
    }  
    # 虚拟IP
    virtual_ipaddress { 
        192.168.1.88/24       # 指定虚拟IP(VIP)
    } 
    track_script {            # 执行上面指定的脚本
        check_nginx
    } 
}
EOF

3.1.4 修改 keepalived 配置文件(k8s-master2 备)

**前提:**修改配置文件前,需要为【vrrp_script】字段准备检查nginx运行状态的脚本:

bash 复制代码
cat > /etc/keepalived/check_nginx.sh  << EOF
#!/bin/bash
code=$(curl -k https://127.0.0.1:16443/version -s -o /dev/null -w %{http_code})

if [ "$code" -ne 200 ];then
    exit 1
else
    exit 0
fi
EOF

chmod +x /etc/keepalived/check_nginx.sh    # 授予执行权限

注:keepalived 根据脚本返回状态码(0为工作正常,非0不正常)判断是否故障转移


bash 复制代码
cat > /etc/keepalived/keepalived.conf << EOF
global_defs { 
   notification_email { 
     acassen@firewall.loc 
     failover@firewall.loc 
     sysadmin@firewall.loc 
   } 
   notification_email_from Alexandre.Cassen@firewall.loc  
   smtp_server 127.0.0.1 
   smtp_connect_timeout 30 
   router_id NGINX_BACKUP
} 

vrrp_script check_nginx {
    script "/etc/keepalived/check_nginx.sh"    # 判断返回状态码
}

vrrp_instance VI_1 { 
    state BACKUP             # 角色为 Nginx Master
    interface eth0           # 修改为实际网卡名
    virtual_router_id 51     # VRRP 路由 ID实例,每个实例是唯一的 
    priority 90              # 优先级,备服务器设置 90,比master优先级低
    advert_int 1             # 指定VRRP 心跳包通告间隔时间,默认1秒 
    authentication { 
        auth_type PASS      
        auth_pass 1111 
    }  
    virtual_ipaddress { 
        192.168.1.88/24      # 指定虚拟IP(VIP)
    } 
    track_script {
        check_nginx
    } 
}
EOF

3.1.5 启动 Nginx + keepalived 并设置开机启动

bash 复制代码
systemctl daemon-reload
systemctl enable nginx --now
systemctl enable keepalived --now

查看nginx 和 keepalived 工作状态

bash 复制代码
# 检查nginx
ps -ef | grep nginx
journalctl -u nginx -f
    //查看日志

# 检查keepalived
ss -anptu | grep keepalived
journalctl -u keepalived -f
ip a s

通过查看IP信息,如果在eth0网卡上绑定了虚拟IP:192.168.1.88,说明 keepalived 工作正常。(但理论上,没完成k8s集群部署的情况下,keepalivec是无法分配VIP的)

3.2 部署Etcd数据库集群

Etcd 是一个分布式键值存储系统,Kubernetes使用Etcd数据库进行数据存储,kubeadm搭建默认情况下只启动一个Etcd Pod,存在单点故障,生产环境强烈不建议,所以这里使用3台服务器组建集群,可容忍1台机器故障(当然也可使用5台组建集群,可容忍2台机器故障)

|----------|--------------|
| 节点名称 | IP |
| etcd-1 | 192.168.1.81 |
| etcd-2 | 192.168.1.82 |
| etcd-3 | 192.168.1.83 |

**注意:**为了节省机器,这里与K8s节点机器复用。也可以独立于k8s集群之外部署,只要apiserver能连接到就行。

3.2.1 准备cfssl工具生成Etcd 证书(k8s-master1操作)

  • cfssl 是一个开源的证书管理工具,使用json文件生成证书,相比openssl更方便使用(任意一台操作,这里用 k8s-master节点)
bash 复制代码
wget https://pkg.cfssl.org/R1.2/cfssl_linux-amd64
wget https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64
wget https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64

chmod +x cfssl_linux-amd64 cfssljson_linux-amd64 cfssl-certinfo_linux-amd64

mv cfssl_linux-amd64 /usr/local/bin/cfssl
mv cfssljson_linux-amd64 /usr/local/bin/cfssljson
mv cfssl-certinfo_linux-amd64 /usr/bin/cfssl-certinfo
  • 自签证书颁发机构(CA)
bash 复制代码
# 创建工作目录
mkdir -p ~/etcd_tls
cd ~/etcd_tls

# 在/etcd_tls目录下,创建自签CA证书
cat > ca-config.json << EOF      //根证书的配置文件
{
  "signing": {
    "default": {
      "expiry": "87600h"
    },
    "profiles": {
      "www": {
         "expiry": "87600h",
         "usages": [
            "signing",
            "key encipherment",
            "server auth",
            "client auth"
        ]
      }
    }
  }
}
EOF

cat > ca-csr.json << EOF     //根证书的请求文件
{
    "CN": "etcd CA",
    "key": {
        "algo": "rsa",
        "size": 2048
    },
    "names": [
        {
            "C": "CN",
            "L": "Beijing",
            "ST": "Beijing"
        }
    ]
}
EOF

# 使用工具生成根证书
cfssl gencert -initca ca-csr.json | cfssljson -bare ca -

## 生成 ca.pem 和 ca-key.pem 文件
[root@k8s-master1-1-81 etcd_tls]# ls
ca-config.json  ca.csr  ca-csr.json  ca-key.pem  ca.pem
  • 使用自签CA签发Etcd HTTPS证书
bash 复制代码
# 在/etcd_tls目录下,创建证书申请文件
cat > server-csr.json << EOF
{
    "CN": "etcd",
    "hosts": [
    "192.168.1.81",
    "192.168.1.82",
    "192.168.1.83"
    ],
    "key": {
        "algo": "rsa",
        "size": 2048
    },
    "names": [
        {
            "C": "CN",
            "L": "BeiJing",
            "ST": "BeiJing"
        }
    ]
}
EOF

# 生成证书
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=www server-csr.json | cfssljson -bare server

## 会生成server.pem和server-key.pem文件
[root@k8s-master1-1-81 etcd_tls]# ls
ca-config.json  ca-csr.json  ca.pem      server-csr.json  server.pem
ca.csr          ca-key.pem   server.csr  server-key.pem

注:上述文件【hosts字段】中IP为所有etcd节点的集群内部通信IP,一个都不能少!为了方便后期扩容可以多写几个预留的IP。

3.2.2****准备二进制部署Etcd

注意: 为简化操作,将k8s-master1生成的所有文件拷贝到另外2个节点

  • 创建工作目录并解压二进制包( etcd,etcdctl ---> /opt/etcd/bin
javascript 复制代码
[root@k8s-master1 ~]# mkdir /opt/etcd/{bin,cfg,ssl} -p      //创建工作目录

[root@k8s-master1 ~]# tar zxvf etcd-v3.4.9-linux-amd64.tar.gz     //解压二进制包
[root@k8s-master1 ~]# ls etcd-v3.4.9-linux-amd64
Documentation  etcd  etcdctl  README-etcdctl.md  README.md  READMEv2-etcdctl.md

# 将etcd的控制工具etcdctl拷贝到创建的 /opt/etcd/bin/ 工作目录下
[root@k8s-master1 ~]# mv etcd-v3.4.9-linux-amd64/{etcd,etcdctl} /opt/etcd/bin/
  • 创建etcd配置文件( etcd.conf ---> /opt/etcd/cfg
javascript 复制代码
[root@k8s-master1 ~]# cat > /opt/etcd/cfg/etcd.conf << EOF
#[Member]
ETCD_NAME="etcd-1"
ETCD_DATA_DIR="/var/lib/etcd/default.etcd"
ETCD_LISTEN_PEER_URLS="https://192.168.1.81:2380"     //集群内部之间通信
ETCD_LISTEN_CLIENT_URLS="https://192.168.1.81:2379"   //集群外部通信

#[Clustering]
ETCD_INITIAL_ADVERTISE_PEER_URLS="https://192.168.1.81:2380"
ETCD_ADVERTISE_CLIENT_URLS="https://192.168.1.81:2379"
ETCD_INITIAL_CLUSTER="etcd-1=https://192.168.1.81:2380,etcd-2=https://192.168.1.82:2380,etcd-3=https://192.168.1.83:2380"
ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"
ETCD_INITIAL_CLUSTER_STATE="new"
EOF

ETCD_NAME:节点名称,集群中唯一
ETCD_DATA_DIR:数据目录
ETCD_LISTEN_PEER_URLS:集群通信监听地址
ETCD_LISTEN_CLIENT_URLS:客户端访问监听地址
ETCD_INITIAL_ADVERTISEPEERURLS:集群通告地址
ETCD_ADVERTISE_CLIENT_URLS:客户端通告地址
ETCD_INITIAL_CLUSTER:集群节点地址
ETCD_INITIAL_CLUSTER_TOKEN:集群Token
ETCD_INITIAL_CLUSTER_STATE:加入集群的当前状态,new是新集群,existing表示加入已有集群

3.2.3 拷贝生成证书

将自签CA签发Etcd HTTPS生成的证书拷贝到ssl路径(ca-key.pem、ca.pem 、server-key.pem、server.pem---> /opt/etcd/ssl)

javascript 复制代码
[root@k8s-master1 ~]# cp ~/etcd_tls/ca*pem ~/etcd_tls/server*pem /opt/etcd/ssl/
[root@k8s-master1 ~]# ls /opt/etcd/ssl/
ca-key.pem  ca.pem  server-key.pem  server.pem

3.2.4 通过systemd管理etcd

bash 复制代码
cat > /usr/lib/systemd/system/etcd.service << EOF
[Unit]
Description=Etcd Server
After=network.target
After=network-online.target
Wants=network-online.target

[Service]
Type=notify
EnvironmentFile=/opt/etcd/cfg/etcd.conf
ExecStart=/opt/etcd/bin/etcd \
--cert-file=/opt/etcd/ssl/server.pem \        # 端口2379,对集群外部的一套证书提供HTTPS服务
--key-file=/opt/etcd/ssl/server-key.pem \
--trusted-ca-file=/opt/etcd/ssl/ca.pem \
--peer-cert-file=/opt/etcd/ssl/server.pem \   # 端口2380,对集群内部的一套证书提供HTTPS服务
--peer-key-file=/opt/etcd/ssl/server-key.pem \
--peer-trusted-ca-file=/opt/etcd/ssl/ca.pem \
--logger=zap
Restart=on-failure
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
EOF

3.2.5 启动etcd服务,并设置开机启动

bash 复制代码
systemctl daemon-reload
systemctl enable etcd --now

提示:Job for etcd.service failed because a timeout was exceeded. See "systemctl status etcd.service" and "journalctl -xe" for details. 提示报错,可暂时忽略,关于etcd的问题。

3.2.6 将上面节点1所有生成的文件拷贝到节点2和节点3

bash 复制代码
## 包括/opt/etcd/bin目录下的:etcd、etcdctl
## 包括/opt/etcd/cfg目录下的:etcd.conf 
## 包括/opt/etcd/ssl目录下的:ca-key.pem、ca.pem  、server-key.pem、server.pem
scp -r /opt/etcd/ root@192.168.1.82:/opt/
scp /usr/lib/systemd/system/etcd.service root@192.168.1.82:/usr/lib/systemd/system/

scp -r /opt/etcd/ root@192.168.1.83:/opt/
scp /usr/lib/systemd/system/etcd.service root@192.168.1.83:/usr/lib/systemd/system/

## 然后在节点2和节点3分别修改etcd.conf配置文件中的节点名称和当前服务器IP:
vi /opt/etcd/cfg/etcd.conf
#[Member]
ETCD_NAME="etcd-X"   # 修改此处,节点2改为etcd-2,节点3改为etcd-3
ETCD_DATA_DIR="/var/lib/etcd/default.etcd"
ETCD_LISTEN_PEER_URLS="https://192.168.1.8X:2380"      # 修改此处为当前服务器IP
ETCD_LISTEN_CLIENT_URLS="https://192.168.1.8X:2379"    # 修改此处为当前服务器IP

#[Clustering]
ETCD_INITIAL_ADVERTISE_PEER_URLS="https://192.168.1.8X:2380"    # 修改此处为当前服务器IP
ETCD_ADVERTISE_CLIENT_URLS="https://192.168.1.8X:2379"          # 修改此处为当前服务器IP
ETCD_INITIAL_CLUSTER="etcd-1=https://192.168.1.81:2380,etcd-2=https://192.168.1.82:2380,etcd-3=https://192.168.1.83:2380"
ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"
ETCD_INITIAL_CLUSTER_STATE="new"

补充:最后启动etcd并设置开机启动

bash 复制代码
systemctl daemon-reload systemctl enable etcd --now

3.2.7 查看集群状态

bash 复制代码
ETCDCTL_API=3 /opt/etcd/bin/etcdctl --cacert=/opt/etcd/ssl/ca.pem \
--cert=/opt/etcd/ssl/server.pem \
--key=/opt/etcd/ssl/server-key.pem \
--endpoints="https://192.168.1.81:2379,https://192.168.1.82:2379,https://192.168.1.83:2379" \
endpoint health --write-out=table
+---------------------------+--------+-------------+-------+
|         ENDPOINT          | HEALTH |    TOOK     | ERROR |
+---------------------------+--------+-------------+-------+
| https://192.168.1.81:2379 |   true | 18.112815ms |       |
| https://192.168.1.83:2379 |   true | 17.104352ms |       |
| https://192.168.1.82:2379 |   true | 22.267223ms |       |
+---------------------------+--------+-------------+-------+

补充:如果输出上面信息,就说明集群部署成功。 如果有问题第一步先看日志/var/log/message 或 journalctl -u etcd

四、 安装 Docker / kubeadm / kubelet(所有节点)

使用Docker作为容器引擎,也可以换成别的容器引擎,例如containerd

4.1 安装Docker

bash 复制代码
# 下载 docker 镜像源
wget https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo -O /etc/yum.repos.d/docker-ce.repo

# 安装 docker-ce
yum -y install docker-ce

# 开启 docker 开机自启
systemctl enable docker && systemctl start docker

# 查看docker版本
docker --version
Docker version 24.0.1, build 6802122

4.2 配置镜像下载加速器

bash 复制代码
cat > /etc/docker/daemon.json << EOF
{
  "registry-mirrors": ["https://b9pmyelo.mirror.aliyuncs.com"],
  "exec-opts": ["native.cgroupdriver=systemd"]
}
EOF
systemctl restart docker

# 查看docker信息
docker info

参考:配置Docker镜像加速器-阿里云开发者社区

4.3 安装cri-dockerd(CRI容器运行时)

Kubernetes v1.24 移除 docker-shim 的支持,而Docker Engine默认又不支持CRI标准,因此二者默认无法再直接集成。为此,Mirantis和Docker联合创建了cri-dockerd项目,用于为Docker Engine提供一个能够支持到CRI规范的桥梁,从而能够让Docker作为Kubernetes容器引擎。如图所示:

补充:containerd 是自带CRI,不需要 cri-docker 的支持,且 containerd 是集成在dockerd 中的;

bash 复制代码
wget https://github.com/Mirantis/cri-dockerd/releases/download/v0.3.1/cri-dockerd-0.3.1-3.el7.x86_64.rpm
rpm -ivh cri-dockerd-0.3.1-3.el7.x86_64.rpm

指定依赖镜像地址:

bash 复制代码
vi /usr/lib/systemd/system/cri-docker.service
ExecStart=/usr/bin/cri-dockerd --container-runtime-endpoint fd:// --pod-infra-container-image=registry.aliyuncs.com/google_containers/pause:3.7

systemctl daemon-reload 
systemctl enable cri-docker && systemctl start cri-docker

4.4 添加阿里云YUM软件源

bash 复制代码
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
gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
EOF

4.5 安装kubeadm、kubelet 和 kubectl

由于版本更新频繁,这里指定版本号部署(注意,是所有节点安装)

bash 复制代码
yum install -y kubelet-1.26.0 kubeadm-1.26.0 kubectl-1.26.0
systemctl enable kubelet

五、部署 k8s 集群

安装目录:/etc/kubernetes/

组件配置文件目录:/etc/kubernetes/manifests/

5.1 初始化 Master1

在 k8s-master1 工作节点执行(192.168.1.81)

5.1.1 生成初始化配置文件

kubeadm config print init-defaults 生成初始配置YAML文件示例

bash 复制代码
cat > kubeadm-config.yaml << EOF
apiVersion: kubeadm.k8s.io/v1beta2
bootstrapTokens:
- groups:
  - system:bootstrappers:kubeadm:default-node-token
  token: 9037x2.tcaqnpaqkra9vsbw
  ttl: 24h0m0s
  usages:
  - signing
  - authentication
kind: InitConfiguration
localAPIEndpoint:
  advertiseAddress: 192.168.1.81
  bindPort: 6443
nodeRegistration:
  criSocket: /var/run/cri-dockerd.sock
  name: k8s-master1
  taints:
  - effect: NoSchedule
    key: node-role.kubernetes.io/master
---
apiServer:
  certSANs:     # 包含所有 Master/LB/VIP IP,为了方便后期扩容可以多写几个预留的IP。
  - k8s-master1
  - k8s-master2
  - 192.168.1.81
  - 192.168.1.82
  - 192.168.1.83
  - 192.168.1.88
  - 127.0.0.1
  extraArgs:
    authorization-mode: Node,RBAC
  timeoutForControlPlane: 4m0s
apiVersion: kubeadm.k8s.io/v1beta2
certificatesDir: /etc/kubernetes/pki
clusterName: kubernetes
controlPlaneEndpoint: 192.168.1.88:16443       # 负载均衡虚拟IP(VIP)和端口
controllerManager: {}
dns:
  type: CoreDNS
etcd:
  external:       # 使用外部etcd
    endpoints:
    - https://192.168.1.81:2379            # etcd集群3个节点
    - https://192.168.1.82:2379
    - https://192.168.1.83:2379
    caFile: /opt/etcd/ssl/ca.pem           # 连接etcd所需根证书
    certFile: /opt/etcd/ssl/server.pem     # 连接etcd所需域名证书
    keyFile: /opt/etcd/ssl/server-key.pem
imageRepository: registry.aliyuncs.com/google_containers  # 由于默认拉取镜像地址k8s.gcr.io国内无法访问,这里指定阿里云镜像仓库地址
kind: ClusterConfiguration
kubernetesVersion: v1.26.0     # K8s版本,与上面安装的一致
networking:
  dnsDomain: cluster.local
  podSubnet: 10.244.0.0/16     # Pod网络,与下面部署的CNI网络组件yaml中保持一致
  serviceSubnet: 10.96.0.0/12  # 集群内部虚拟网络,Pod统一访问入口
scheduler: {}
EOF

5.1.2 使用配置文件引导

javascript 复制代码
[root@k8s-master1 ~]# kubeadm init --config kubeadm-config.yaml    //等待拉取镜像
...
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 control-plane nodes by copying certificate authorities
and service account keys on each node and then running the following as root:

  kubeadm join 192.168.1.88:16443 --token 9037x2.tcaqnpaqkra9vsbw \
        --discovery-token-ca-cert-hash sha256:a888c8a2da8861bdaab009dfe8838ecd4812567b00fc8d3a050d220479c3c6db \
        --control-plane

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

kubeadm join 192.168.1.88:16443 --token 9037x2.tcaqnpaqkra9vsbw \
        --discovery-token-ca-cert-hash sha256:a888c8a2da8861bdaab009dfe8838ecd4812567b00fc8d3a050d220479c3c6db
  • 初始化完成后,会有两个join的命令,带有 --control-plane 是用于加入组建多master集群的(k8s-master2),不带的是加入节点的。

  • 拷贝kubectl使用的连接k8s认证文件到默认路径:

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

[root@k8s-master1 ~]# kubectl get node
NAME          STATUS     ROLES           AGE     VERSION
k8s-master1   NotReady   control-plane   2m45s   v1.26.0

5.2 初始化 Master2

在 k8s-master2 工作节点执行(192.168.1.82)

1)将Master1节点生成的证书拷贝到Master2

javascript 复制代码
[root@k8s-master1 ~]# ls /etc/kubernetes/pki/
apiserver.crt                 ca.crt              front-proxy-client.crt
apiserver.key                 ca.key              front-proxy-client.key
apiserver-kubelet-client.crt  front-proxy-ca.crt  sa.key
apiserver-kubelet-client.key  front-proxy-ca.key  sa.pub

[root@k8s-master1 ~]# scp -r /etc/kubernetes/pki/ 192.168.1.82:/etc/kubernetes/

2)复制加入master join命令在master2执行

  • --cri-socket=unix:///var/run/cri-dockerd.sock
javascript 复制代码
[root@k8s-master2 ~]# kubeadm join 192.168.1.88:16443 --token 9037x2.tcaqnpaqkra9vsbw \
--discovery-token-ca-cert-hash sha256:a888c8a2da8861bdaab009dfe8838ecd4812567b00fc8d3a050d220479c3c6db \
--control-plane --cri-socket=unix:///var/run/cri-dockerd.sock

3)拷贝kubectl 使用的连接k8s认证文件到默认路径

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

[root@k8s-master2 ~]# kubectl get node
NAME          STATUS     ROLES           AGE    VERSION
k8s-master1   NotReady   control-plane   12m    v1.26.0
k8s-master2   NotReady   control-plane   2m1s   v1.26.0

5.3 访问负载均衡器测试

找K8s集群中任意一个节点,使用curl查看K8s API版本测试,使用VIP访问:

bash 复制代码
curl -k https://192.168.1.88:16443/version
{
  "major": "1",
  "minor": "26",
  "gitVersion": "v1.26.0",
  "gitCommit": "b46a3f887ca979b1a5d14fd39cb1af43e7e5d12d",
  "gitTreeState": "clean",
  "buildDate": "2022-12-08T19:51:45Z",
  "goVersion": "go1.19.4",
  "compiler": "gc",
  "platform": "linux/amd64"
}

可以正确获取到K8s版本信息,说明负载均衡器搭建正常。该请求数据流程:curl -> vip(nginx) -> apiserver

通过查看Ngin-master(192.168.1.88)的Nginx日志也可以看到转发apiserver IP:

bash 复制代码
tail /var/log/nginx/k8s-access.log -f
192.168.1.81 192.168.1.81:6443 - [02/Apr/2021:19:17:57 +0800] 200 423
192.168.1.81 192.168.1.82:6443 - [02/Apr/2021:19:18:50 +0800] 200 423

5.4 加入Kubernetes Node

参考资料:kubeadm join | Kubernetes

在 k8s-Node 工作节点执行(192.168.1.83)

向集群添加新节点,将刚刚执行在 kubeadm init 输出中的 kubeadm join 命令,并手动添加:

bash 复制代码
kubeadm join 192.168.1.88:16443 --token 9037x2.tcaqnpaqkra9vsbw \
        --discovery-token-ca-cert-hash sha256:a888c8a2da8861bdaab009dfe8838ecd4812567b00fc8d3a050d220479c3c6db --cri-socket=unix:///var/run/cri-dockerd.sock

补充:后续其他节点也是这样加入

**注意:**默认token有效期为24小时,当过期之后,该token就不可用了。这时就需要重新创建token,可以直接使用命令快捷生成:

bash 复制代码
kubeadm token create --print-join-command

拷贝 kubectl 使用的连接k8s认证文件到默认路径(将 admin.conf 文件拷贝到所有节点的 $HOME/.kube/config,即可以在每个节点使用 kubectl get nodes查看集群)

javascript 复制代码
[root@k8s-node1 ~]# mkdir -p $HOME/.kube
[root@k8s-master1 ~]# scp -r /etc/kubernetes/admin.conf root@192.168.1.83:$HOME/.kube/config
[root@k8s-node1 ~]# ls $HOME/.kube/config
/root/.kube/config

[root@k8s-node1 ~]# sudo chown $(id -u):$(id -g) $HOME/.kube/config

# 查看集群节点信息
[root@k8s-node1 ~]# kubectl get nodes
NAME          STATUS     ROLES           AGE     VERSION
k8s-master1   NotReady   control-plane   26m     v1.26.0
k8s-master2   NotReady   control-plane   15m     v1.26.0
k8s-node1     NotReady   <none>          5m46s   v1.26.0

5.5 部署容器网络(CNI)(k8s-master1操作)

注意:由于网络插件还没有部署,还没有准备就绪 NotReady

参考资料:

Calico是一个纯三层的数据中心网络方案,是目前Kubernetes主流的网络方案

bash 复制代码
# 下载 calico 相关yaml文件
wget https://docs.projectcalico.org/v3.23/manifests/calico.yaml

下载完后还需要修改里面定义Pod网络(CALICO_IPV4POOL_CIDR),与前面kubeadm init的 --pod-network-cidr指定的一样(例如:10.244.0.0/16 )

javascript 复制代码
- name: CALICO_IPV4POOL_CIDR
  value: "10.244.0.0/16"

修改完后文件后进行部署:

bash 复制代码
kubectl apply -f calico.yaml
kubectl get pods -n kube-system

等Calico Pod都Running,节点也会准备就绪:

javascript 复制代码
[root@k8s-master1 ~]# kubectl get node
NAME          STATUS   ROLES           AGE   VERSION
k8s-master1   Ready    control-plane   34m   v1.26.0
k8s-master2   Ready    control-plane   24m   v1.26.0
k8s-node1     Ready    <none>          14m   v1.26.0

参考资料:Creating a cluster with kubeadm | Kubernetes

补充:2台Master共用etcd,只需要在1台Master节点安装即可。

5.6 部署 Dashboard

Dashboard是官方提供的一个UI,可用于基本管理K8s资源。

下载地址:https://raw.githubusercontent.com/kubernetes/dashboard/v2.7.0/aio/deploy/recommended.yaml

默认Dashboard只能集群内部访问,修改Service为NodePort类型,暴露到外部:

bash 复制代码
vi recommended.yaml
...
kind: Service
apiVersion: v1
metadata:
  labels:
    k8s-app: kubernetes-dashboard
  name: kubernetes-dashboard
  namespace: kubernetes-dashboard
spec:
  ports:
    - port: 443
      targetPort: 8443
      nodePort: 30001      # 添加 nodePort: 30001
  selector:
    k8s-app: kubernetes-dashboard
  type: NodePort      # 添加 nodePort
...
kubectl apply -f recommended.yaml

[root@k8s-master1 ~]# kubectl get pods -n kubernetes-dashboard
NAME                                        READY   STATUS    RESTARTS   AGE
dashboard-metrics-scraper-7bc864c59-qr4fm   1/1     Running   0          58s
kubernetes-dashboard-6c7ccbcf87-v952t       1/1     Running   0          58s

创建 service account 并绑定默认 cluster-admin 管理员集群角色:

bash 复制代码
# 创建用户
kubectl create serviceaccount dashboard-admin -n kubernetes-dashboard

# 用户授权
kubectl create clusterrolebinding dashboard-admin --clusterrole=cluster-admin --serviceaccount=kubernetes-dashboard:dashboard-admin

# 获取用户Token
kubectl create token dashboard-admin -n kubernetes-dashboard

访问地址:https://NodeIP:30001,使用输出的 token 登录 Dashboard

六、 Nginx + Keepalived 高可用测试

通过测试关闭Nginx Master节点的Nginx、Keepalived,测试VIP是否漂移到备节点服务器。

在Nginx Master执行 pkill nginx、keepalived。

javascript 复制代码
[root@k8s-master1 ~]# pkill nginx keepalived
[root@k8s-master1 ~]# ps -ef | grep nginx
[root@k8s-master2 ~]# ip a s | grep 192.168.1.88
    inet 192.168.1.88/24 scope global secondary eth0

在Nginx Master重新开启nginx、keepalived重新进行VIP绑定

javascript 复制代码
[root@k8s-master1-1-81 ~]# systemctl start nginx
[root@k8s-master1-1-81 ~]# systemctl start keepalived
[root@k8s-master1-1-81 ~]# ip a s | grep 192.168.1.88
    inet 192.168.1.88/24 scope global secondary eth0

注意:因为K8S集群暂未搭建起来,所以高可用脚本检测失败,无法分配虚拟VIP,需要继续完成以下步骤才能实现高可用。

小结

本篇为**【Kubernetes CKS认证】**的开篇学习笔记,希望这篇笔记可以让您初步了解到 如何使用kubeadm部署1.26版本的高可用K8s集群,不妨跟着我的笔记步伐亲自实践一下吧!


Tip:毕竟两个人的智慧大于一个人的智慧,如果你不理解本章节的内容或需要相关笔记、视频,可私信小安,请不要害羞和回避,可以向他人请教,花点时间直到你真正的理解。

相关推荐
chuanauc6 小时前
Kubernets K8s 学习
java·学习·kubernetes
小张是铁粉6 小时前
docker学习二天之镜像操作与容器操作
学习·docker·容器
烟雨书信6 小时前
Docker文件操作、数据卷、挂载
运维·docker·容器
IT成长日记6 小时前
【Docker基础】Docker数据卷管理:docker volume prune及其参数详解
运维·docker·容器·volume·prune
这儿有一堆花6 小时前
Docker编译环境搭建与开发实战指南
运维·docker·容器
LuckyLay6 小时前
Compose 高级用法详解——AI教你学Docker
运维·docker·容器
Uluoyu7 小时前
redisSearch docker安装
运维·redis·docker·容器
IT成长日记11 小时前
【Docker基础】Docker数据持久化与卷(Volume)介绍
运维·docker·容器·数据持久化·volume·
疯子的模样15 小时前
Docker 安装 Neo4j 保姆级教程
docker·容器·neo4j
虚伪的空想家16 小时前
rook-ceph配置dashboard代理无法访问
ceph·云原生·k8s·存储·rook