Kubeadm 部署 K8S 集群
提要
经公司技术团队研究,计划把现有服务转移部署到 K8S,但前期先在测试环境部署一套并运转一段时间,以测试具体性能以及可行性。
此处使用 Kubeadm 方式部署 K8S。
介绍
Kubernetes (后续简称 k8s) 是 Google (2014年6月) 开源的一个容器编排引擎,使用 Go 语言开发,它支持自动化部署、大规模可伸缩、以及云平台中多个主机上的容器化应用进行管理。其目标是让部署容器化的应用更加简单并且高效,提供了资源调度、部署管理、服务发现、扩容缩容、状态监控、维护等一整套功能,努力成为跨主机集群的自动化部署、自动化扩展以及运行应用程序容器的平台。
Kubeadm 是一个 K8s 部署工具,提供 kubeadm init 和 kubeadm join,用于快速部署 Kubernetes 集群。
组件
Master 组件
· API Server:负责整个集群的统一入口,协调各个组件工作,以 RESTful API 提供接口服务,所有对象资源的增删改查和监听操作都交给 API Server 处理后再提交给 Etcd 存储。
· Kube-Scheduler:根据调度算法为新创建的 Pod 选择一个 Node 节点,可以任意部署,可以部署在同一个节点上,也可以部署在不同的节点上。
· Controller-Manager:负责集群中常规后台任务,一个资源对应一个控制器,而 Controller-Manager 则负责管理这些控制器。
· Etcd:分布式键值存储系统,用于保存集群状态数据,比如 Pod、Service 等对象信息。
Node 组件
· Kubelet:是 Master 在 Node 节点上的 Agent,管理本机运行容器的生命周期,比如创建容器、Pod 挂载数据卷、下载 secret、获取容器和节点状态等,Kubelet 将每个 Pod 转换成一组容器。
· Kube-Proxy:在 Node 节点上实现 Pod 网络代理,维护网络规则和四层负载均衡工作。
· Docker:容器引擎,运行容器。
机器分配
| 主机名 | IP 地址 |
组件 |
|---|---|---|
k8s-master-1-220 |
192.168.31.220 |
kube-apiserver,kube-controller-manager,kube-scheduler,kubectl,kubelet,docker,etcd |
k8s-master-2-221 |
192.168.31.221 |
kube-apiserver,kube-controller-manager,kube-scheduler,kubectl,kubelet,docker,etcd |
k8s-master-3-226 |
192.168.31.226 |
kube-apiserver,kube-controller-manager,kube-scheduler,kubectl,kubelet,docker,etcd |
k8s-node-1-222 |
192.168.31.222 |
kubectl,kubelet,kube-proxy,docker |
k8s-node-2-223 |
192.168.31.223 |
kubectl,kubelet,kube-proxy,docker |
k8s-lb-1-224 |
192.168.31.224 |
Nginx,Keepalived,主节点 |
k8s-lb-2-225 |
192.168.31.225 |
Nginx,Keepalived,备节点 |
lb-vip |
192.168.31.250 |
VIP |
集群拓扑图

网络规划
| 子网 | 网段 | 备注 |
|---|---|---|
NodeSubnet |
192.168.31.0/24 |
宿主机节点子网 |
ServiceSubnet |
10.96.0.0/12 |
SVC 子网 |
PodSubnet |
10.244.0.0/16 |
POD 子网 |
节点初始化(所有节点)
# 关闭所有机器 SElinux
sed -i 's/enforcing/disabled/' /etc/selinux/config
# 关闭swap
sed -ri 's/.*swap.*/#&/' /etc/fstab
# 关闭防火墙
systemctl stop firewalld && systemctl disable firewalld
# 修改最大文件打开数和最大进程数
cat >> /etc/sysctl.conf <<EOF
* - nofile 65535
* - nproc 65536
EOF
sed -i 's#4096#65536#g' /etc/security/limits.d/20-nproc.conf
# 修正系统时间
(crontab -l;echo '*/30 * * * * /usr/sbin/ntpdate ntp1.aliyun.com && /usr/sbin/hwclock -w') | crontab
/usr/sbin/ntpdate ntp1.aliyun.com
# 内核参数调优
cat >> /etc/sysctl.conf <<EOF
net.ipv4.tcp_fin_timeout = 2
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_keepalive_time = 600
net.ipv4.ip_local_port_range = 4000 65000
net.ipv4.tcp_max_syn_backlog = 16384
net.ipv4.tcp_max_tw_buckets = 36000
net.ipv4.route.gc_timeout = 100
net.ipv4.tcp_syn_retries = 1
net.ipv4.tcp_synack_retries = 1
net.core.somaxconn = 16384
net.core.netdev_max_backlog = 16384
net.ipv4.tcp_max_orphans = 16384
net.netfilter.nf_conntrack_max = 25000000
net.netfilter.nf_conntrack_tcp_timeout_established = 180
net.netfilter.nf_conntrack_tcp_timeout_time_wait = 120
net.netfilter.nf_conntrack_tcp_timeout_fin_wait = 120
EOF
sysctl -p >/dev/null 2>&1
# 添加 hosts
cat >> /etc/hosts << EOF
192.168.31.220 k8s-master-1-220
192.168.31.221 k8s-master-2-221
192.168.31.226 k8s-master-3-226
192.168.31.222 k8s-node-1-222
192.168.31.223 k8s-node-2-223
192.168.31.224 k8s-lb-1-224
192.168.31.225 k8s-lb-2-225
192.168.31.250 lb-vip
EOF
# 创建目录待用
mkdir -p /data
# 允许 iptables 检查桥接流量
cat <<EOF | tee /etc/modules-load.d/k8s.conf
br_netfilter
EOF
cat <<EOF | tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
sysctl --system
# 验证每个节点上 IP、MAC 地址和 product_uuid 的唯一性,保证其能相互正常通信
# 使用命令 ip link 或 ifconfig -a 来获取网络接口的 MAC 地址
ifconfig -a
# 使用命令 查看 product_uuid 校验
cat /sys/class/dmi/id/product_uuid
# 重启机器
init 6
部署 LB 节点(224,225)
Nginx 是一个主流 Web 服务和反向代理服务器,这里用四层实现对 Master 节点实现负载均衡。
Keepalived 是一个主流高可用软件,基于 VIP 绑定实现服务器双机热备,Keepalived 主要根据 Nginx 运行状态判断是否需要故障转移(漂移 VIP),例如当 Nginx 主节点挂掉,VIP 会自动绑定在 Nginx 备节点,从而保证 VIP 一直可用,实现 Nginx 的高可用。
# 安装软件包(主/备)
yum install epel-release -y
yum install nginx keepalived nginx-mod-stream -y
# Nginx 配置文件(主/备)
cat > /etc/nginx/nginx.conf << "EOF"
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;
include /usr/share/nginx/modules/*.conf;
events {
worker_connections 1024;
}
# 四层负载均衡,为两台 Master 提供负载均衡
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-master {
server 192.168.31.220:6443;
server 192.168.31.221:6443;
}
server {
listen 16443;
proxy_pass k8s-master;
}
}
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;
server {
listen 80 default_server;
server_name _;
location / {
}
}
}
EOF
# keepalived 配置文件(主)
cat > /etc/keepalived/keepalived.conf << EOF
global_defs {
notification_email {
[email protected]
[email protected]
[email protected]
}
script_user root
enable_script_security
notification_email_from [email protected]
smtp_server 127.0.0.1
smtp_connect_timeout 30
router_id NGINX_MASTER
}
# 指定 nginx 状态检测脚本
vrrp_script check_nginx {
script "/etc/keepalived/check_nginx.sh"
}
vrrp_instance VI_1 {
# 指定为主
state MASTER
# 修改为实际网卡名称
interface enp0s3
# VRRP 路由 ID 实例,同网段中,每一个主从集群都要相同ID,且唯一
virtual_router_id 51
# 优先级
priority 100
# 指定 VRRP 心跳包通告间隔时间,默认 1 秒
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
# 虚拟IP
virtual_ipaddress {
192.168.31.250/24
}
track_script {
check_nginx
}
}
EOF
# 准备 nginx 状态检测脚本
# keepalived 根据脚本返回状态码(0 为工作正常,非 0 不正常)判断是否故障转移
cat > /etc/keepalived/check_nginx.sh << "EOF"
#!/bin/bash
count=$(ss -antp |grep 16443 |egrep -cv "grep|$$")
if [ "$count" -eq 0 ];then
exit 1
else
exit 0
fi
EOF
# 授权
chmod +x /etc/keepalived/check_nginx.sh
# keepalived 配置文件(备)
cat > /etc/keepalived/keepalived.conf << EOF
global_defs {
notification_email {
[email protected]
[email protected]
[email protected]
}
script_user root
enable_script_security
notification_email_from [email protected]
smtp_server 127.0.0.1
smtp_connect_timeout 30
router_id NGINX_BACKUP
}
# 指定 nginx 状态检测脚本
vrrp_script check_nginx {
script "/etc/keepalived/check_nginx.sh"
}
vrrp_instance VI_1 {
# 指定为备
state BACKUP
# 修改为实际网卡名称
interface enp0s3
# VRRP 路由 ID 实例,同网段中,每一个主从集群都要相同ID,且唯一
virtual_router_id 51
# 优先级
priority 90
# 指定 VRRP 心跳包通告间隔时间,默认 1 秒
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
# 虚拟IP
virtual_ipaddress {
192.168.31.250/24
}
track_script {
check_nginx
}
}
EOF
# 准备 nginx 状态检测脚本
# keepalived 根据脚本返回状态码(0 为工作正常,非 0 不正常)判断是否故障转移
cat > /etc/keepalived/check_nginx.sh << "EOF"
#!/bin/bash
count=$(ss -antp |grep 16443 |egrep -cv "grep|$$")
if [ "$count" -eq 0 ];then
exit 1
else
exit 0
fi
EOF
# 授权
chmod +x /etc/keepalived/check_nginx.sh
# 启动并设置开机自启(主/备)
systemctl daemon-reload
systemctl start nginx keepalived
systemctl enable nginx keepalived
# 查看 keepalived 工作状态(主)
ip a
inet 192.168.31.250/24 scope global secondary enp0s3
Master 节点与 Node 节点
安装 Docker
# 安装
wget https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo -O /etc/yum.repos.d/docker-ce.repo
yum -y install docker-ce
# 创建配置文件,为了加速 Docker 源或使用私有仓库
# 由于默认情况下 kubelet 使用的 cgroupdriver 是 systemd,所以需要保持 docker 和 kubelet 的 cgroupdriver 一致,我们这里修改 docker 的 cgroupdriver=systemd。如果不修改 docker 则需要修改 kubelet 的启动配置,需要保证两者一致。
mkdir /etc/docker
cat > /etc/docker/daemon.json << EOF
{
"registry-mirrors": ["https://vnarzvrd.mirror.aliyuncs.com"],
"exec-opts": ["native.cgroupdriver=systemd"]
}
EOF
# 设置开机启动并启动 Docker
systemctl daemon-reload && systemctl enable docker && systemctl start docker
安装 kubeadm,kubelet 和 kubectl
# 配置 YUM 源
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
# 安装 1.23 版本
yum install -y kubelet-1.23.9 kubeadm-1.23.9 kubectl-1.23.9
systemctl enable kubelet
# 配置 kubectl 命令自动补全
yum install -y bash-completion
source /usr/share/bash-completion/bash_completion
source <(kubectl completion bash)
echo "source <(kubectl completion bash)" >> ~/.bashrc
Master 节点部署
220 节点上操作
# 初始化
kubeadm init \
--apiserver-advertise-address=192.168.31.220 \
--image-repository registry.aliyuncs.com/google_containers \
--kubernetes-version v1.23.9 \
--service-cidr=10.96.0.0/12 \
--pod-network-cidr=10.244.0.0/16 \
--ignore-preflight-errors=all \
--control-plane-endpoint=lb-vip:16443 \
--upload-certs
• --apiserver-advertise-address 集群通告地址
• --image-repository 由于默认拉取镜像地址 k8s.gcr.io 国内无法访问,这里指定阿里云镜像仓库地址
• --kubernetes-version K8s 版本,与上面安装的一致
• --service-cidr 集群内部虚拟网络,Pod 统一访问入口
• --pod-network-cidr Pod 网络,,与下面部署的 CNI 网络组件 yaml 中保持一致
• --control-plane-endpoint 负载均衡器的地址与端口
• --upload-certs 启用自动分发证书
# 配置 kubectl
mkdir -p $HOME/.kube
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
chown $(id -u):$(id -g) $HOME/.kube/config
# 记录 Master 节点加入的命令
kubeadm join lb-vip:16443 --token dozkz5.h5ixk8avfumy97zv \
--discovery-token-ca-cert-hash sha256:433583f8d0ba98638e74ec7c866d920f28cd65cfa105dbaa103c1868faa0ae01 \
--control-plane --certificate-key a3f1e2c2ae42fec791249bf17e7d030c4ea8ac1b8a26b3664afbde0cecbc50ea
# 记录 Node 节点加入的命令
kubeadm join lb-vip:16443 --token dozkz5.h5ixk8avfumy97zv \
--discovery-token-ca-cert-hash sha256:433583f8d0ba98638e74ec7c866d920f28cd65cfa105dbaa103c1868faa0ae01
# 创建新的加入 Token,因为每个 Token 只有 24 小时时效
kubeadm token create --print-join-command
其他节点加入集群
221,226 节点上操作
# 执行 Master 节点加入的命令
kubeadm join lb-vip:16443 --token dozkz5.h5ixk8avfumy97zv \
--discovery-token-ca-cert-hash sha256:433583f8d0ba98638e74ec7c866d920f28cd65cfa105dbaa103c1868faa0ae01 \
--control-plane --certificate-key a3f1e2c2ae42fec791249bf17e7d030c4ea8ac1b8a26b3664afbde0cecbc50ea
# 配置 kubectl
mkdir -p $HOME/.kube
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
chown $(id -u):$(id -g) $HOME/.kube/config
222,223 节点上操作
# 执行 Node 节点加入的命令
kubeadm join lb-vip:16443 --token dozkz5.h5ixk8avfumy97zv \
--discovery-token-ca-cert-hash sha256:433583f8d0ba98638e74ec7c866d920f28cd65cfa105dbaa103c1868faa0ae01
220 上检查
kubectl get node
NAME STATUS ROLES AGE VERSION
k8s-master-1-220 NotReady control-plane,master 16m v1.23.9
k8s-master-2-221 NotReady control-plane,master 8m22s v1.23.9
k8s-master-3-226 NotReady control-plane,master 5m35s v1.23.9
k8s-node-1-222 NotReady <none> 2m17s v1.23.9
k8s-node-2-223 NotReady <none> 57s v1.23.9
部署 CNI 网络组件
在节点加入到集群时你会发现其节点状态为 NotReady,部署 calico 插件可以让集群正常通信。
Calico 是一个纯三层的数据中心网络方案。
在每一个计算节点利用 Linux Kernel 实现了一个高效的虚拟路由器(vRouter)来负责数据转发,而每个 vRouter 通过 BGP 协议负责把自己运行的 workload 的路由信息向整个 Calico 网络内传播。
Calico 还实现了 Kubernetes 网络策略,提供 ACL 功能。
# 220 上操作
# 下载 calico 文件并修改内容
mkdir -p /data/yaml && cd $_
curl https://docs.projectcalico.org/v3.23/manifests/calico.yaml -O
vim calico.yaml
# 多网卡服务器建议增加这条配置
# 使用本地路由来决定使用哪个 IP 地址来到达提供的目的地,此处可以使用 IP 地址或者域名
- name: IP_AUTODETECTION_METHOD
value: can-reach=114.114.114.114
- name: CALICO_IPV4POOL_CIDR
# 此处需要与前面 kubeadm init 的 --pod-network-cidr 指定的一样
value: "10.244.0.0/16"
# 部署 Calico
kubectl apply -f calico.yaml
# 查看 Calico 部署状态
kubectl get pods -n kube-system
# 检查各个节点状态(Ready 则表示 Calico 正常部署)
kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8s-master-1-220 Ready control-plane,master 40m v1.23.9
k8s-master-2-221 Ready control-plane,master 32m v1.23.9
k8s-master-3-226 Ready control-plane,master 29m v1.23.9
k8s-node-1-222 Ready <none> 26m v1.23.9
k8s-node-2-223 Ready <none> 24m v1.23.9
部署 Web UI
部署 Dashboard
Dashboard 是官方提供的一个 UI,可用于基本管理 K8s 资源。
# 220 上操作
# 下载文件并修改内容
# 默认 Dashboard 只能集群内部访问,修改 Service 为 NodePort 类型,暴露到外部
wget https://raw.githubusercontent.com/kubernetes/dashboard/v2.6.1/aio/deploy/recommended.yaml
vim recommended.yaml
kind: Service
apiVersion: v1
metadata:
labels:
k8s-app: kubernetes-dashboard
name: kubernetes-dashboard
namespace: kubernetes-dashboard
spec:
ports:
- port: 443
targetPort: 8443
selector:
k8s-app: kubernetes-dashboard
# 增加 type 参数
type: NodePort
# 部署 Dashboard
kubectl apply -f recommended.yaml
# 查看状态
kubectl get pods -n kubernetes-dashboard
kubectl get svc -n kubernetes-dashboard
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
dashboard-metrics-scraper ClusterIP 10.105.237.150 <none> 8000/TCP 3m59s
kubernetes-dashboard NodePort 10.103.123.27 <none> 443:31270/TCP 4m
# 创建 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
# 获取用户 Token,把 Token 记录下来待用
kubectl describe secrets -n kube-system $(kubectl -n kube-system get secret | awk '/dashboard-admin/{print $1}')
浏览器测试 Dashboard
地址为:https://192.168.31.220:31270/

验证集群可用性
部署 Nginx
在 Kubernetes 集群中创建一个 pod,验证是否正常运行
# 220 上操作
# 创建一个 nginx 的 pod
kubectl create deployment nginx --image=nginx
kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-85b98978db-mzgjb 1/1 Running 0 37s
# 端口映射
kubectl expose deployment nginx --port=80 --type=NodePort
# 查看映射的端口
kubectl get svc nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx NodePort 10.111.161.79 <none> 80:30664/TCP 6s
# 进入 Nginx 容器
kubectl exec -it nginx-85b98978db-mzgjb bash
# 使用 curl 命令测试容器是否有网络
curl www.baidu.com
浏览器测试 Nginx
地址为:http://192.168.31.220:30664/

测试 DNS 是否正常
# 使用 busybox
kubectl run busybox --image busybox:1.28 --restart=Never --rm -it busybox sh
# 可以直接 nslookup nginx
nslookup nginx
Server: 10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local
Name: nginx
Address 1: 10.111.161.79 nginx.default.svc.cluster.local
至此,K8S 集群环境准备就绪!
微信
支付宝