k8s 二进制安装
- IP规划
- 初始化
- 部署 etcd 集群
- 在 etcd01 节点上操作
- 准备cfssl证书生成工具,加权
- 生成etcd证书
- 上传etcd软件包
- 启动 etcd 服务
- 部署 Master 组件
- 部署 Worker Node 组件
- node 节点安装 docker
- 部署组件
- 部署 CNI 网络组件
- 部署 flannel
- 简介
- 部署
- 部署 Calico
- 简介
- 部署
- 部署 CoreDNS
- master02 节点部署
- 负载均衡部署
- 部署 Dashboard
IP规划
主机名 | IP |
---|---|
master01 | 192.168.110.10 |
master01 | 192.168.110.40 |
node01 | 192.168.110.20 |
node02 | 192.168.110.30 |
etcd01/lb01 | 192.168.110.50 |
etcd02/lb02 | 192.168.110.60 |
etcd03 | 192.168.110.70 |
初始化
#关闭防火墙和核心防护
systemctl stop firewalld
systemctl disable firewalld
iptables -F && iptables -t nat -F && iptables -t mangle -F && iptables -X
setenforce 0
sed -i 's/enforcing/disabled/' /etc/selinux/config#设置主机名
hostnamectl set-hostname master01
su#在master添加hosts
cat >> /etc/hosts << EOF
192.168.110.10 master01
192.168.110.40 master02
192.168.110.20 node01
192.168.110.30 node02
192.168.110.50 etcd01
192.168.110.60 etcd02
192.168.110.70 etcd03
EOF#调整内核参数
cat > /etc/sysctl.d/k8s.conf << EOF
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv6.conf.all.disable_ipv6=1
net.ipv4.ip_forward=1
EOFsysctl --system#时间同步
yum install ntpdate -y
ntpdate time.windows.com
部署 etcd 集群
etcd是一个分布式键值存储系统,用于在分布式系统中保存配置信息、元数据以及关键的共享状态。它是一个开源项目,最初由CoreOS开发并维护,现在由CNCF托管。etcd的设计目标是提供可靠的分布式存储,以支持分布式系统的一致性和高可用性。
etcd 目前默认使用2379端口提供HTTP API服务, 2380端口和peer通信(这两个端口已经被IANA(互联网数字分配机构)官方预留给etcd)。 即etcd默认使用2379端口对外为客户端提供通讯,使用端口2380来进行服务器间内部通讯。
etcd 在生产环境中一般推荐集群方式部署。由于etcd 的leader选举机制,要求至少为3台或以上的奇数台。
在 etcd01 节点上操作
准备cfssl证书生成工具,加权
#方法一,下载
wget https://pkg.cfssl.org/R1.2/cfssl_linux-amd64 -O /usr/local/bin/cfssl
wget https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64 -O /usr/local/bin/cfssljson
wget https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64 -O /usr/local/bin/cfssl-certinfo
chmod +x /usr/local/bin/cfssl*#方法二,上传已有压缩包 etcd.zip 到 /root
unzip etcd.zip
cd etcd/cfssl/
mv cfssl* /usr/local/bin/
chmod +x /usr/local/bin/cfssl*
生成etcd证书
#创建工作目录
mkdir /opt/k8s#上传 etcd-cert.sh 和 etcd.sh 到 /opt/k8s/ 目录中
cd ~/etcd/
mv etcd-cert.sh etcd.sh /opt/k8s
cd /opt/k8s
chmod +x etcd-cert.sh etcd.sh#创建用于生成CA证书、etcd 服务器证书以及私钥的目录
mkdir /opt/k8s/etcd-cert
mv etcd-cert.sh etcd-cert/
cd /opt/k8s/etcd-cert/
./etcd-cert.sh#生成的证书
ls
ca-config.json ca-csr.json ca.pem server.csr server-key.pem
ca.csr ca-key.pem etcd-cert.sh server-csr.json server.pem
上传etcd软件包
#上传 etcd-v3.4.26-linux-amd64.tar.gz 到 /opt/k8s 目录中
tar -xf etcd-v3.4.26-linux-amd64.tar.gz
cd /opt/k8sls etcd-v3.4.26-linux-amd64
Documentation etcd etcdctl README-etcdctl.md README.md READMEv2-etcdctl.md
启动 etcd 服务
#创建用于存放 etcd 配置文件,命令文件,证书的目录
mkdir -p /opt/etcd/{cfg,bin,ssl}cd etcd-v3.4.26-linux-amd64
mv etcd etcdctl /opt/etcd/bin/
cp /opt/k8s/etcd-cert/*.pem /opt/etcd/ssl/#进入卡住状态等待其他节点加入,这里需要三台etcd服务同时启动,如果只启动其中一台后,服务会卡在那里,直到集群中所有etcd节点都已启动,可忽略这个情况
cd /opt/k8s/
./etcd.sh etcd01 192.168.110.50 etcd02=https://192.168.110.60:2380,etcd03=https://192.168.110.70:2380#可另外打开一个窗口查看etcd进程是否正常
ps -ef | grep etcd
把etcd相关证书文件、命令文件和服务管理文件全部拷贝到另外两个etcd集群节点
scp -r /opt/etcd/ root@192.168.110.60:/opt/
scp -r /opt/etcd/ root@192.168.110.70:/opt/
scp /usr/lib/systemd/system/etcd.service root@192.168.110.60:/usr/lib/systemd/system/
scp /usr/lib/systemd/system/etcd.service root@192.168.110.70:/usr/lib/systemd/system/
etcd02 节点上操作
vim /opt/etcd/cfg/etcd
#[Member]
ETCD_NAME="etcd02" #修改
ETCD_DATA_DIR="/var/lib/etcd"
ETCD_LISTEN_PEER_URLS="https://192.168.110.60:2380" #修改
ETCD_LISTEN_CLIENT_URLS="https://192.168.110.60:2379" #修改#[Clustering]
ETCD_INITIAL_ADVERTISE_PEER_URLS="https://192.168.110.60:2380" #修改
ETCD_ADVERTISE_CLIENT_URLS="https://192.168.110.60:2379" #修改
ETCD_INITIAL_CLUSTER="etcd01=https://192.168.110.50:2380,etcd02=https://192.168.110.60:2380,etcd03=https://192.168.110.70:2380"
ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"
ETCD_INITIAL_CLUSTER_STATE="new"#启动etcd服务
systemctl start etcd
systemctl enable etcd
systemctl status etcd#检查etcd群集状态
ETCDCTL_API=3 /opt/etcd/bin/etcdctl --endpoints="https://192.168.110.50:2379,https://192.168.110.60:2379,https://192.168.110.70:2379" --cacert=/opt/etcd/ssl/ca.pem --cert=/opt/etcd/ssl/server.pem --key=/opt/etcd/ssl/server-key.pem endpoint health --write-out=table#查看当前的 leader
ETCDCTL_API=3 /opt/etcd/bin/etcdctl --endpoints="https://192.168.110.50:2379,https://192.168.110.60:2379,https://192.168.110.60:2379" --cacert=/opt/etcd/ssl/ca.pem --cert=/opt/etcd/ssl/server.pem --key=/opt/etcd/ssl/server-key.pem endpoint status --write-out=table
etcd03 节点操作同 etcd02
部署 Master 组件
安装 apiserver 服务,controller-manager 服务,scheduler 服务和kubectl 工具
在 etcd01 上操作
#将 cfssl 工具传到master01
scp /usr/local/bin/cfssl* 192.168.110.10:/usr/local/bin/
在 master01 上操作
#创建 /opt/k8s/ 目录上传 master.zip 并解压
mkdir /opt/k8s/
cd /opt/k8s/
unzip master.zip
chmod +x *.sh[root@master01 k8s]# ls
admin.sh apiserver.sh controller-manager.sh master.zip scheduler.sh#创建kubernetes工作目录
mkdir -p /opt/kubernetes/{bin,cfg,ssl,logs}#创建用于生成CA证书、相关组件的证书和私钥的目录,上传 k8s-cert.sh
mkdir /opt/k8s/k8s-cert
mv /opt/k8s/k8s-cert.sh /opt/k8s/k8s-cert
cd /opt/k8s/k8s-cert/
./k8s-cert.sh[root@master01 k8s-cert]# ls *pem
admin-key.pem apiserver-key.pem ca-key.pem kube-proxy-key.pem
admin.pem apiserver.pem ca.pem kube-proxy.pem#复制CA证书、apiserver相关证书和私钥到 kubernetes工作目录的 ssl 子目录中
cp ca*pem apiserver*pem /opt/kubernetes/ssl/#上传 kubernetes-server-linux-amd64.tar.gz 到 /opt/k8s/ 目录中,解压 kubernetes 压缩包
cd /opt/k8s/
tar zxvf kubernetes-server-linux-amd64.tar.gz#复制master组件的关键命令文件到 kubernetes工作目录的 bin 子目录中
cd /opt/k8s/kubernetes/server/bin
cp kube-apiserver kubectl kube-controller-manager kube-scheduler /opt/kubernetes/bin/
ln -s /opt/kubernetes/bin/* /usr/local/bin/#创建 bootstrap token 认证文件,apiserver 启动时会调用,然后就相当于在集群内创建了一个这个用户,接下来就可以用 RBAC 给他授权
cd /opt/k8s/
vim token.sh
#!/bin/bash
#获取随机数前16个字节内容,以十六进制格式输出,并删除其中空格
BOOTSTRAP_TOKEN=$(head -c 16 /dev/urandom | od -An -t x | tr -d ' ')
#生成 token.csv 文件,按照 Token序列号,用户名,UID,用户组 的格式生成
cat > /opt/kubernetes/cfg/token.csv <<EOF
${BOOTSTRAP_TOKEN},kubelet-bootstrap,10001,"system:kubelet-bootstrap"
EOFchmod +x token.sh
./token.sh
cat /opt/kubernetes/cfg/token.csv#在 master01 节点
mkdir /opt/etcd/ssl/ -p
#在 etcd 节点
cd /opt/etcd/ssl/
scp * 192.168.110.10:`pwd`
#在 master01 节点
[root@master01 k8s]# ls /opt/etcd/ssl/
ca-key.pem ca.pem server-key.pem server.pem
#二进制文件、token、证书都准备好后,开启 apiserver 服务(修改 apiserver.sh 脚本里 ip)
cd /opt/k8s/
./apiserver.sh 192.168.110.10 https://192.168.110.50:2379,https://192.168.110.60:2379,https://192.168.110.70:2379
#检查进程是否启动成功
ps aux | grep kube-apiserver
netstat -natp | grep 6443 #安全端口6443用于接收HTTPS请求,用于基于Token文件或客户端证书等认证#启动 scheduler 服务(修改 KUBE_APISERVER 的IP)
cd /opt/k8s/
./scheduler.sh
ps aux | grep kube-scheduler#启动 controller-manager 服务(修改 KUBE_APISERVER 的IP)
./controller-manager.sh
ps aux | grep kube-controller-manager#生成kubectl连接集群的kubeconfig文件(修改 KUBE_APISERVER 的IP)
./admin.sh#通过kubectl工具查看当前集群组件状态
kubectl get cs
#查看版本信息
kubectl version
#查看当前的 leader
kubectl -n kube-system get leases kube-scheduler
kubectl -n kube-system get leases kube-controller-manager
部署 Worker Node 组件
安装kubelet,kube-proxy 和 docker
node 节点安装 docker
yum install -y yum-utils device-mapper-persistent-data lvm2
yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
yum install -y docker-ce docker-ce-cli containerd.iomkdir /etc/docker
cat > /etc/docker/daemon.json <<EOF
{"registry-mirrors": ["https://docker.m.daocloud.io", "https://docker.1panel.live"],"exec-opts": ["native.cgroupdriver=systemd"],"log-driver": "json-file","log-opts": {"max-size": "1g", "max-file": "3"}
}
EOFsystemctl start docker.service
systemctl enable docker.service docker info | grep "Cgroup Driver"
部署组件
#创建kubernetes工作目录
mkdir -p /opt/kubernetes/{bin,cfg,ssl,logs}#上传 node.zip 到 /opt 目录中,解压 node.zip 压缩包,获得kubelet.sh、proxy.sh
cd /opt/
unzip node.zip
chmod +x kubelet.sh proxy.sh#在 master01 节点上操作
#把 kubelet、kube-proxy 拷贝到 node 节点
cd /opt/k8s/kubernetes/server/bin
scp kubelet kube-proxy root@192.168.110.20:/opt/kubernetes/bin/
scp kubelet kube-proxy root@192.168.110.30:/opt/kubernetes/bin/
#上传kubeconfig.sh文件到/opt/k8s/kubeconfig目录中,生成kubelet初次加入集群引导kubeconfig文件和kube-proxy.kubeconfig文件
#kubeconfig 文件包含集群参数(CA 证书、API Server 地址),客户端参数(上面生成的证书和私钥),集群 context 上下文参数(集群名称、用户名)。Kubenetes 组件(如 kubelet、kube-proxy)通过启动时指定不同的 kubeconfig 文件可以切换到不同的集群,连接到 apiserver。
mkdir /opt/k8s/kubeconfig
cd /opt/k8s/kubeconfig
chmod +x kubeconfig.sh
./kubeconfig.sh 192.168.110.10 /opt/k8s/k8s-cert/
#把配置文件 bootstrap.kubeconfig、kube-proxy.kubeconfig 拷贝到 node 节点
scp bootstrap.kubeconfig kube-proxy.kubeconfig root@192.168.110.20:/opt/kubernetes/cfg/
scp bootstrap.kubeconfig kube-proxy.kubeconfig root@192.168.110.30:/opt/kubernetes/cfg/
#RBAC授权,使用户 kubelet-bootstrap 能够有权限发起 CSR 请求证书
kubectl create clusterrolebinding kubelet-bootstrap --clusterrole=system:node-bootstrapper --user=kubelet-bootstrap
#自动批准 CSR 请求
kubectl create clusterrolebinding node-autoapprove-bootstrap --clusterrole=system:certificates.k8s.io:certificatesigningrequests:nodeclient --user=kubelet-bootstrap
kubectl create clusterrolebinding node-autoapprove-certificate-rotation --clusterrole=system:certificates.k8s.io:certificatesigningrequests:selfnodeclient --user=kubelet-bootstrap#在 node01 节点上操作
#启动 kubelet 服务
cd /opt/
./kubelet.sh 192.168.110.20
ps aux | grep kubelet
//在 master01 节点上操作,通过 CSR 请求
#检查到 node01 节点的 kubelet 发起的 CSR 请求,Pending 表示等待集群给该节点签发证书
kubectl get csr
[root@master01 kubeconfig]# kubectl get csr
NAME AGE SIGNERNAME REQUESTOR CONDITION
node-csr-HD7lIMBUCLneSmDmaoDwXpe_FWJ75FylZ1iWGR69o2s 65s kubernetes.io/kube-apiserver-client-kubelet kubelet-bootstrap Approved,Issued
#通过 CSR 请求
kubectl certificate approve node-csr-HD7lIMBUCLneSmDmaoDwXpe_FWJ75FylZ1iWGR69o2s
#Approved,Issued 表示已授权 CSR 请求并签发证书
kubectl get csr
NAME AGE SIGNERNAME REQUESTOR CONDITION
node-csr-duiobEzQ0R93HsULoS9NT9JaQylMmid_nBF3Ei3NtFE 2m5s kubernetes.io/kube-apiserver-client-kubelet kubelet-bootstrap Approved,Issued
#查看节点,由于网络插件还没有部署,节点会没有准备就绪 NotReady
[root@master01 kubeconfig]# kubectl get node
NAME STATUS ROLES AGE VERSION
192.168.110.20 NotReady <none> 97m v1.20.15//在 node01 节点上操作
#加载 ip_vs 模块
for i in $(ls /usr/lib/modules/$(uname -r)/kernel/net/netfilter/ipvs|grep -o "^[^.]*");do echo $i; /sbin/modinfo -F filename $i >/dev/null 2>&1 && /sbin/modprobe $i;done
#启动proxy服务
cd /opt/
./proxy.sh 192.168.110.20
ps aux | grep kube-proxy
在 node02 节点操作同 node01
部署 CNI 网络组件
部署 flannel
简介
Flannel 的功能是让集群中的不同节点主机创建的 Docker 容器都具有全集群唯一的虚拟 IP 地址。
Flannel 是 Overlay 网络的一种,也是将 TCP 源数据包封装在另一种网络包里面进行路由转发和通信,目前支持 UDP、VXLAN、Host-gw 3种数据转发方式。
Flannel UDP 模式的工作原理:
1、数据从主机A 上 Pod 的源容器中发出后,经由所在主机的 cni0 网桥转发到 flannel0 接口,flanneld 服务监听在 flannel0 接口的另外一端。
2、发送给 flannel0 接口的 IP 包信息将被 flanneld 进程接收,flanneld 进程接收 IP 包后在原有的基础上进行 UDP 封包
3、Flannel 通过 etcd 服务维护了一张节点间的路由表。目标容器所在宿主机的 IP 地址,flanneld 通过查询 etcd 很容易就能得到
4、flanneld 将封装好的 UDP 报文通过节点网络发往在主机B 监听 8285 端口的 flanneld 进程
5、运行在主机B 上的 flanneld 将 UDP 报文解包后得到原始 IP 包,内核通过查询本机路由表将该 IP 包转发给 cni0 网桥
6、cni0 网桥将 IP 包转发给连接在网桥上的目标Pod。至此整个流程结束。回程报文将按照上面的数据流原路返回
由于在 UDP 模式下 flanneld 进行网络的封包和解包工作,而 VXLAN 模式下封包解包的工作由内核完成,因此性能上 UDP 模式会比在内核态做转发的 VXLAN 模式差。
VXLAN 模式是 Flannel 默认和推荐的模式,flannel 会为每个节点分配一个 24 位子网,并在每个节点上创建两张虚机网卡:cni0 和 flannel.1 。 cni0 是一个网桥设备,类似于 docker0 ,节点上所有的 Pod 都通过 veth pair 的形式与 cni0 相连。 flannel.1 则是一个 VXLAN 类型的设备,充当 VTEP 设备(VXLAN Tunnel Endpoint)的角色,实现对 VXLAN 报文的封包解包。
在 VXLAN 模式下,flanneld 启动时先确保 VXLAN 设备已存在,如果不存在则创建,存在则跳过。并将 VTEP 设备的信息上报到 etcd 中,当 flannel 网络有新节点加入并向 etcd 注册时, 会在其它节点添加路由规则。
UDP 模式的 flannel0 网卡是三层转发,使用 flannel0 是在物理网络之上构建三层网络,属于 ip in udp ;VXLAN 模式是二层实现,overlay 是数据帧,属于 mac in udp 。
Flannel VXLAN 模式跨主机的工作原理:
1、数据包从主机 A 上 Pod 的源容器中发出后,经由所在主机的 cni0 网络接口转发到 flannel.1 设备
2、flannel.1 收到数据包后,在内核态为数据包添加以太网头部(目的 MAC 地址为目的 flannel.1 设备的 MAC 地址)和 VXLAN 头部,再将此数据帧进行 UDP 封装
3、flannel.1 通过查询本机的 FDB 转发数据库获取目的 flannel.1 设备对应的宿主机 IP 地址
4、通过节点网络将 UDP 报文发往在主机B 监听 8472 端口的 flannel.1 设备,并在内核态进行解封装,获取原始数据包
5、根据解包后得到的原始数据包中的目的IP,flannel.1 将数据包发送给 cni0,最后由 cni0 发送给连接在此接口上的PodB
部署
#在 node01 节点上操作
#上传 cni-plugins-linux-amd64-v1.3.0.tgz 和 flannel镜像文件 到 /opt 目录中
cd /opt/
unzip flannel-v0.21.5.zip
docker load -i flannel.tar
docker load -i flannel-cni-plugin.tarmkdir -p /opt/cni/bin
tar zxvf cni-plugins-linux-amd64-v1.3.0.tgz -C /opt/cni/bin#在 master01 节点上操作
#上传 kube-flannel.yml 文件到 /opt/k8s 目录中,部署 CNI 网络
#在 node01 节点上
scp kube-flannel.yml 192.168.110.10:/opt/k8s
#在 master01 节点
cd /opt/k8s
kubectl apply -f kube-flannel.yml
kubectl get pods -n kube-flannel
kubectl get nodes[root@master01 k8s]# kubectl get pods -n kube-flannel
NAME READY STATUS RESTARTS AGE
kube-flannel-ds-m86br 1/1 Running 0 31s
kube-flannel-ds-qkjw9 1/1 Running 0 31s
[root@master01 k8s]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
192.168.110.20 Ready <none> 132m v1.20.15
192.168.110.30 Ready <none> 22m v1.20.15#在node上查看flannel.1的端口
ip -d a show flannel.1
#通过路由信息,看到发送到对端网段的数据包都会经过 flannel.1发出,网关ip也是对端vtep设备的ip地址
route -n
#由于flannel.1 通过路由信息已经知道对端vtep的ip地址,通过查询本地arp缓存表,得到目的vtep的mac地址
ip neigh show dev flannel.1
#flannel.1可以通过查询本机的FDB表获取目的节点主机的ip
bridge fdb show flannel.1 [root@node01 opt]# ip -d a show flannel.1
8: flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN group default link/ether 9e:33:61:e2:14:9e brd ff:ff:ff:ff:ff:ff promiscuity 0 vxlan id 1 local 192.168.110.20 dev ens33 srcport 0 0 dstport 8472 nolearning ageing 300 noudpcsum noudp6zerocsumtx noudp6zerocsumrx numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535 inet 10.244.0.0/32 scope global flannel.1valid_lft forever preferred_lft forever
[root@node01 opt]# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 192.168.110.2 0.0.0.0 UG 100 0 0 ens33
10.244.1.0 10.244.1.0 255.255.255.0 UG 0 0 0 flannel.1
172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0
192.168.110.0 0.0.0.0 255.255.255.0 U 100 0 0 ens33
192.168.122.0 0.0.0.0 255.255.255.0 U 0 0 0 virbr0
[root@node01 opt]# ip neigh show dev flannel.1
10.244.1.0 lladdr 62:c5:6f:ed:55:96 PERMANENT
[root@node01 opt]# bridge fdb show flannel.1
01:00:5e:00:00:01 dev ens33 self permanent
01:00:5e:00:00:fb dev ens33 self permanent
33:33:00:00:00:01 dev ens33 self permanent
01:00:5e:00:00:01 dev virbr0 self permanent
01:00:5e:00:00:fb dev virbr0 self permanent
52:54:00:9c:44:78 dev virbr0-nic master virbr0 permanent
52:54:00:9c:44:78 dev virbr0-nic vlan 1 master virbr0 permanent
33:33:00:00:00:01 dev docker0 self permanent
01:00:5e:00:00:01 dev docker0 self permanent
01:00:5e:00:00:fb dev docker0 self permanent
02:42:1e:24:eb:65 dev docker0 vlan 1 master docker0 permanent
02:42:1e:24:eb:65 dev docker0 master docker0 permanent
33:33:00:00:00:01 dev dummy0 self permanent
33:33:00:00:00:01 dev kube-ipvs0 self permanent
62:c5:6f:ed:55:96 dev flannel.1 dst 192.168.110.30 self permanent
部署 Calico
简介
Calico不使用隧道或NAT来实现转发,而是把每个操作系统的协议栈认为是一个路由器,然后把所有的容器认为是连在这个路由器上的网络终端,在路由器之间跑标准的路由协议——BGP的协议,然后让它们自己去学习这个网络拓扑该如何转发。
它不使用 cni0 网桥,而是通过路由规则把数据包直接发送到目标节点的网卡,所以性能高。
Calico 的模式:
1、IPIP 模式:在原有 IP 报文中封装一个新的 IP 报文,新的 IP 报文中将源地址 IP 和目的地址 IP 都修改为对端宿主机 IP。Calico 默认使用 IPIP 的模式。
2、BGP 模式:将节点做为虚拟路由器通过 BGP 路由协议来实现集群内容器之间的网络访问。
3、cross-subnet(ipip-bgp混合模式):IPIP 模式和 BGP 模式都有对应的局限性,对于一些主机跨子网而又无法使网络设备使用 BGP 的场景可以使用 cross-subnet 模式,实现同子网机器使用 BGP 模式,跨子网机器使用 IPIP 模式。
Calico 工作原理:
IPIP 模式:
Calico 会将容器的 IP 数据包经过 veth pair 设备发送到 tunl0 设备,并被内核的 IPIP 驱动直接封装到宿主机网络的 IP 数据包中,新封装的 IP 数据包再根据 Felix 维护的路由规则发送给目标节点,目标节点通过 IPIP 驱动解包得到原始容器 IP 数据包,然后根据路由规则经过 veth pair 设备送达到目标容器。
BGP 模式:
Calico 是通过路由表来维护每个 Pod 的通信。Calico 的 CNI 插件会为每个容器设置一个 veth pair 设备, 然后把另一端接入到宿主机网络空间,由于没有网桥,CNI 插件还需要在宿主机上为每个容器的 veth pair 设备配置一条路由规则, 用于接收传入的 IP 包。
有了这样的 veth pair 设备以后,容器发出的 IP 包就会通过 veth pair 设备到达宿主机,然后根据容器要访问的IP和宿主机的路由规则,找到下一跳要到达的宿主机 IP。 流量到达下一跳的宿主机后,根据当前宿主机上的路由规则,直接到达对端容器的 veth pair 插在宿主机的一端,最终进入容器。
这些路由规则都是 Felix 维护配置的,而路由信息则是 Calico BIRD 组件基于 BGP 分发而来。
Calico 实际上是将集群里所有的节点都当做边界路由器来处理,他们一起组成了一个全互联的网络,彼此之间通过 BGP 交换路由, 这些节点我们叫做 BGP Peer。
相比IPIP模式,BGP模式下不需要tunl0设备参与报文传输,报文直接通过物理网卡(比如ens33)转发到目标机器上,不会进行二次IP报文的封装,因此从性能上来看,BGP是占优势的。但是由于没有二次封包,BGP模式只能在同一个子网内使用,无法跨网段使用。
部署
在 master01 节点
#上传 calico.yaml 文件到 /opt/k8s 目录中,部署 Calico
#下载地址:curl https://raw.githubusercontent.com/projectcalico/calico/v3.26.1/manifests/calico.yaml
cd /opt/k8s
vim calico.yaml#修改里面定义 Pod 的网络(CALICO_IPV4POOL_CIDR),需与前面 kube-controller-manager 配置文件指定的 cluster-cidr 网段一样- name: CALICO_IPV4POOL_CIDRvalue: "10.244.0.0/16" #Calico 默认使用的网段为 192.168.0.0/16kubectl apply -f calico.yaml
kubectl get pods -n kube-system#等 Calico Pod 都 Running,节点也会准备就绪
kubectl get nodes[root@master01 k8s]# kubectl get pods -n kube-system
NAME READY STATUS RESTARTS AGE
calico-kube-controllers-659bd7879c-khr55 1/1 Running 0 5m6s
calico-node-rf8lf 1/1 Running 0 5m6s
calico-node-rw5qp 1/1 Running 0 5m6s
[root@master01 k8s]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
192.168.110.20 Ready <none> 147m v1.20.15
192.168.110.30 Ready <none> 37m v1.20.15[root@node02 opt]# ip route
default via 192.168.110.2 dev ens33 proto static metric 100
10.244.0.0 via 192.168.110.20 dev tunl0 proto bird onlink
10.244.0.0/24 via 10.244.0.0 dev flannel.1 onlink
unreachable 10.244.1.0/24 proto bird
10.244.60.64/26 via 192.168.110.20 dev tunl0 proto bird onlink
blackhole 10.244.193.64/26 proto bird
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
192.168.110.0/24 dev ens33 proto kernel scope link src 192.168.110.30 metric 100
192.168.122.0/24 dev virbr0 proto kernel scope link src 192.168.122.1#修改calico的网络模式IPIP变为BGP
kubectl edit ds calico-node -n kube-system
......- name: CALICO_IPV4POOL_IPIPvalue: "Never" #设置为Never时为BGP模式,设置为Always时为IPIP模式,设置为Cross-SubNet时为混合模式- name: IP_AUTODETECTION_METHOD #添加下面两行value: "interface=ens33" #指定calico使用的网卡kubectl edit ippool ipipMode: Never #把ipipMode从Always修改成为Never使用BGP模式时,查看IP会发现tunl0没有IP地址了;查看route会发现不再使用tunl0了,而是直接通过物理网卡转发。
ip addr
ip route[root@node01 opt]# ip route
default via 192.168.110.2 dev ens33 proto static metric 100
10.244.0.0/24 dev cni0 proto kernel scope link src 10.244.0.1
10.244.1.0 via 192.168.110.30 dev ens33 proto bird
10.244.1.0/24 via 10.244.1.0 dev flannel.1 onlink
blackhole 10.244.60.64/26 proto bird
10.244.193.64/26 via 192.168.110.30 dev ens33 proto bird
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
192.168.110.0/24 dev ens33 proto kernel scope link src 192.168.110.20 metric 100
192.168.122.0/24 dev virbr0 proto kernel scope link src 192.168.122.1
部署 CoreDNS
CoreDNS:CoreDNS 是 Kubernetes 的默认 DNS 实现。可以为 K8S 集群内的 Pod 提供 DNS 服务。
使用 CoreDNS 可以为集群中的 service 资源创建一个资源名称 与 ClusterIP 的对应关系解析,从而避免将 service 的 ClusterIP 地址硬编码到应用程序代码中。
#在所有 node 节点上操作
#上传 coredns.tar 到 /opt 目录中
cd /opt
docker load -i coredns.tar//在 master01 节点上操作
#上传 coredns.yaml 文件到 /opt/k8s 目录中,部署 CoreDNS
cd /opt/k8s
kubectl apply -f coredns.yaml
kubectl get pods -n kube-system [root@master01 k8s]# kubectl get pods -n kube-system
NAME READY STATUS RESTARTS AGE
calico-kube-controllers-659bd7879c-khr55 1/1 Running 0 30m
calico-node-5lk72 1/1 Running 0 10m
calico-node-rddmg 1/1 Running 0 10m
coredns-7f8c5c6967-l2tpc 1/1 Running 0 #DNS 解析测试
kubectl run -it --rm dns-test --image=busybox:1.28.4 sh[root@master01 k8s]# kubectl run -it --rm dns-test1 --image=busybox sh
If you don't see a command prompt, try pressing enter.
/ # nslookup kubernetes.default.svc.cluster.local.
Server: 10.0.0.2
Address: 10.0.0.2:53Name: kubernetes.default.svc.cluster.local
Address: 10.0.0.1#若执行失败,可先给kubectl绑定默认cluster-admin管理员集群角色,授权集群操作权限
kubectl create clusterrolebinding cluster-system-anonymous --clusterrole=cluster-admin --user=system:anonymous
master02 节点部署
#从 master01 节点上拷贝证书文件、各master组件的配置文件和服务管理文件到 master02 节点
scp -r /opt/etcd/ root@192.168.110.40:/opt/
scp -r /opt/kubernetes/ root@192.168.110.40:/opt
scp -r /root/.kube root@192.168.110.40:/root
scp /usr/lib/systemd/system/{kube-apiserver,kube-controller-manager,kube-scheduler}.service root@192.168.110.40:/usr/lib/systemd/system/#修改配置文件kube-apiserver中的IP
vim /opt/kubernetes/cfg/kube-apiserver
KUBE_APISERVER_OPTS="--logtostderr=true \
--v=4 \
--etcd-servers=https://192.168.80.10:2379,https://192.168.80.11:2379,https://192.168.80.12:2379 \
--bind-address=192.168.80.20 \ #修改
--secure-port=6443 \
--advertise-address=192.168.80.20 \ #修改
......//在 master02 节点上启动各服务并设置开机自启
systemctl start kube-apiserver.service
systemctl enable kube-apiserver.service
systemctl start kube-controller-manager.service
systemctl enable kube-controller-manager.service
systemctl start kube-scheduler.service
systemctl enable kube-scheduler.service//查看node节点状态
ln -s /opt/kubernetes/bin/* /usr/local/bin/
kubectl get nodes
kubectl get nodes -o wide #-o=wide:输出额外信息;对于Pod,将输出Pod所在的Node名[root@localhost ~]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
192.168.110.20 Ready <none> 3h38m v1.20.15
192.168.110.30 Ready <none> 108m v1.20.15
[root@localhost ~]# kubectl get nodes -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
192.168.110.20 Ready <none> 3h38m v1.20.15 192.168.110.20 <none> CentOS Linux 7 (Core) 3.10.0-957.el7.x86_64 docker://26.1.4
192.168.110.30 Ready <none> 108m v1.20.15 192.168.110.30 <none> CentOS Linux 7 (Core) 3.10.0-957.el7.x86_64 docker://26.1.4
此时在master02节点查到的node节点状态仅是从etcd查询到的信息,而此时node节点实际上并未与master02节点建立通信连接,因此需要使用一个VIP把node节点与master节点都关联起来
负载均衡部署
配置load balancer集群双机热备负载均衡(nginx实现负载均衡,keepalived实现双机热备)
在lb01、lb02节点上操作
#配置nginx的官方在线yum源,配置本地nginx的yum源
cat > /etc/yum.repos.d/nginx.repo << 'EOF'
[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/centos/7/$basearch/
gpgcheck=0
EOFyum install nginx -y#修改nginx配置文件,配置四层反向代理负载均衡,指定k8s群集2台master的节点ip和6443端口
vim /etc/nginx/nginx.conf
events {worker_connections 1024;
}#添加
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.110.10:6443;server 192.168.110.40:6443;}server {listen 6443;proxy_pass k8s-apiserver;}
}http {
......//检查配置文件语法
nginx -t //启动nginx服务,查看已监听6443端口
systemctl start nginx
systemctl enable nginx
netstat -natp | grep nginx //部署keepalived服务
yum install keepalived -y//修改keepalived配置文件
vim /etc/keepalived/keepalived.conf
! Configuration File for keepalivedglobal_defs {# 接收邮件地址notification_email {acassen@firewall.locfailover@firewall.locsysadmin@firewall.loc}# 邮件发送地址notification_email_from Alexandre.Cassen@firewall.locsmtp_server 127.0.0.1smtp_connect_timeout 30router_id NGINX_MASTER #lb01节点的为 NGINX_MASTER,lb02节点的为 NGINX_BACKUP
}#添加一个周期性执行的脚本
vrrp_script check_nginx {script "/etc/nginx/check_nginx.sh" #指定检查nginx存活的脚本路径
}vrrp_instance VI_1 {state MASTER #lb01节点的为 MASTER,lb02节点的为 BACKUPinterface ens33 #指定网卡名称 ens33virtual_router_id 51 #指定vrid,两个节点要一致priority 100 #lb01节点的为 100,lb02节点的为 90advert_int 1authentication {auth_type PASSauth_pass 1111}virtual_ipaddress {192.168.80.100/24 #指定 VIP}track_script {check_nginx #指定vrrp_script配置的脚本}
}#创建nginx状态检查脚本
vim /etc/nginx/check_nginx.sh
#!/bin/bash
#egrep -cv "grep|$$" 用于过滤掉包含grep 或者 $$ 表示的当前Shell进程ID,即脚本运行的当前进程ID号
count=$(ps -ef | grep nginx | egrep -cv "grep|$$")if [ "$count" -eq 0 ];thensystemctl stop keepalived
fichmod +x /etc/nginx/check_nginx.sh#启动keepalived服务(一定要先启动了nginx服务,再启动keepalived服务)
systemctl start keepalived
systemctl enable keepalived
#lb01上查看VIP是否生成
ip a [root@etcd01 k8s-cert]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00inet 127.0.0.1/8 scope host lovalid_lft forever preferred_lft forever
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000link/ether 00:0c:29:ef:81:c3 brd ff:ff:ff:ff:ff:ffinet 192.168.110.50/24 brd 192.168.110.255 scope global noprefixroute ens33valid_lft forever preferred_lft foreverinet6 fe80::8c65:44b1:be2a:c9f5/64 scope link noprefixroute valid_lft forever preferred_lft forever
3: virbr0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default qlen 1000link/ether 52:54:00:75:aa:99 brd ff:ff:ff:ff:ff:ffinet 192.168.122.1/24 brd 192.168.122.255 scope global virbr0valid_lft forever preferred_lft forever
4: virbr0-nic: <BROADCAST,MULTICAST> mtu 1500 qdisc pfifo_fast master virbr0 state DOWN group default qlen 1000link/ether 52:54:00:75:aa:99 brd ff:ff:ff:ff:ff:ff#修改node节点上的bootstrap.kubeconfig,kubelet.kubeconfig配置文件为VIP
cd /opt/kubernetes/cfg/
vim bootstrap.kubeconfig
server: https://192.168.80.100:6443vim kubelet.kubeconfig
server: https://192.168.80.100:6443vim kube-proxy.kubeconfig
server: https://192.168.80.100:6443#重启kubelet和kube-proxy服务
systemctl restart kubelet.service
systemctl restart kube-proxy.service#在 lb01 上查看 nginx 和 node 、 master 节点的连接状态
netstat -natp | grep nginx[root@etcd01 ~]# netstat -natp | grep nginx
tcp 0 0 0.0.0.0:6443 0.0.0.0:* LISTEN 9036/nginx: master
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 9036/nginx: master
tcp 0 0 192.168.110.50:55238 192.168.110.40:6443 ESTABLISHED 9042/nginx: worker
tcp 0 0 192.168.110.50:57196 192.168.110.10:6443 ESTABLISHED 9041/nginx: worker
tcp 0 0 192.168.110.100:6443 192.168.110.20:40392 ESTABLISHED 9042/nginx: worker
tcp 0 0 192.168.110.100:6443 192.168.110.20:40374 ESTABLISHED 9041/nginx: worker
tcp 0 0 192.168.110.100:6443 192.168.110.30:41828 ESTABLISHED 9042/nginx: worker
tcp 0 0 192.168.110.100:6443 192.168.110.30:41824 ESTABLISHED 9043/nginx: worker
tcp 0 0 192.168.110.100:6443 192.168.110.30:41818 ESTABLISHED 9041/nginx: worker
tcp 0 0 192.168.110.50:55226 192.168.110.40:6443 ESTABLISHED 9041/nginx: worker
tcp 0 0 192.168.110.100:6443 192.168.110.30:41820 ESTABLISHED 9043/nginx: worker
tcp 0 0 192.168.110.100:6443 192.168.110.20:40368 ESTABLISHED 9043/nginx: worker
tcp 0 0 192.168.110.100:6443 192.168.110.30:41834 ESTABLISHED 9043/nginx: worker
tcp 0 0 192.168.110.100:6443 192.168.110.30:41822 ESTABLISHED 9042/nginx: worker
tcp 0 0 192.168.110.50:57254 192.168.110.10:6443 ESTABLISHED 9043/nginx: worker
tcp 0 0 192.168.110.100:6443 192.168.110.20:40358 ESTABLISHED 9042/nginx: worker
tcp 0 0 192.168.110.50:55218 192.168.110.40:6443 ESTABLISHED 9043/nginx: worker
tcp 0 0 192.168.110.100:6443 192.168.110.20:40336 ESTABLISHED 9043/nginx: worker
tcp 0 0 192.168.110.50:57238 192.168.110.10:6443 ESTABLISHED 9044/nginx: worker
tcp 0 0 192.168.110.50:57206 192.168.110.10:6443 ESTABLISHED 9043/nginx: worker
tcp 0 0 192.168.110.50:57198 192.168.110.10:6443 ESTABLISHED 9043/nginx: worker
tcp 0 0 192.168.110.100:6443 192.168.110.30:41826 ESTABLISHED 9043/nginx: worker
tcp 0 0 192.168.110.100:6443 192.168.110.20:40394 ESTABLISHED 9044/nginx: worker
tcp 0 0 192.168.110.50:55274 192.168.110.40:6443 ESTABLISHED 9042/nginx: worker
tcp 0 0 192.168.110.50:57360 192.168.110.10:6443 ESTABLISHED 9042/nginx: worker
tcp 0 0 192.168.110.50:57236 192.168.110.10:6443 ESTABLISHED 9042/nginx: worker
tcp 0 0 192.168.110.100:6443 192.168.110.20:40522 ESTABLISHED 9042/nginx: worker
tcp 0 0 192.168.110.50:57218 192.168.110.10:6443 ESTABLISHED 9042/nginx: worker
tcp 0 0 192.168.110.50:55258 192.168.110.40:6443 ESTABLISHED 9043/nginx: worker
tcp 0 0 192.168.110.50:55372 192.168.110.40:6443 ESTABLISHED 9043/nginx: worker
#如果有问题检查防火墙核心防护
在 master01 节点上进行检测
#测试创建pod
kubectl run nginx --image=nginx//查看Pod的状态信息
kubectl get pods[root@master01 opt]# kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx 1/1 Running 0 7skubectl get pods -o wide[root@master01 opt]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx 1/1 Running 0 70s 10.244.60.72 192.168.110.20 <none> <none>
#READY为1/1,表示这个Pod中有1个容器#在对应网段的node节点上操作,可以直接使用浏览器或者curl命令访问
curl 172.17.36.2[root@node01 logs]# curl 10.244.60.72
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
... ...
<p><em>Thank you for using nginx.</em></p>
</body>
</html>#这时在master01节点上查看nginx日志
kubectl logs nginx[root@master01 opt]# kubectl logs nginx
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
... ...
2025/02/10 08:51:22 [notice] 1#1: start worker process 31
2025/02/10 08:51:22 [notice] 1#1: start worker process 32
192.168.110.20 - - [10/Feb/2025:08:54:15 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.29.0" "-"若执行失败,可先给kubectl绑定默认cluster-admin管理员集群角色,授权集群操作权限
kubectl create clusterrolebinding cluster-system-anonymous --clusterrole=cluster-admin --user=system:anonymous
部署 Dashboard
Dashboard (仪表板)是基于Web的Kubernetes用户界面。您可以使用仪表板将容器化应用程序部署到Kubernetes集群,对容器化应用程序进行故障排除,并管理集群本身及其伴随资源。您可以使用仪表板来概述群集上运行的应用程序,以及创建或修改单个Kubernetes资源(例如deployment,job,daemonset等)。例如,您可以使用部署向导扩展部署,启动滚动更新,重新启动Pod或部署新应用程序。仪表板还提供有关群集中Kubernetes资源状态以及可能发生的任何错误的信息
#在 master01 节点上操作
#上传 recommended.yaml 文件到 /opt/k8s 目录中
cd /opt/k8s
vim recommended.yaml
#默认Dashboard只能集群内部访问,修改Service为NodePort类型,暴露到外部:
kind: Service
apiVersion: v1
metadata:labels:k8s-app: kubernetes-dashboardname: kubernetes-dashboardnamespace: kubernetes-dashboard
spec:ports:- port: 443targetPort: 8443nodePort: 30001 #添加type: NodePort #添加selector:k8s-app: kubernetes-dashboardkubectl apply -f recommended.yaml#创建service account并绑定默认cluster-admin管理员集群角色
kubectl create serviceaccount dashboard-admin -n kube-system
kubectl create clusterrolebinding dashboard-admin --clusterrole=cluster-admin --serviceaccount=kube-system:dashboard-admin
kubectl describe secrets -n kube-system $(kubectl -n kube-system get secret | awk '/dashboard-admin/{print $1}')#使用输出的token登录Dashboard
https://192.168.110.20:30001 或者 https://192.168.110.30:30001