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 {
acassen@firewall.loc
failover@firewall.loc
sysadmin@firewall.loc
}
script_user root
enable_script_security
notification_email_from Alexandre.Cassen@firewall.loc
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 {
acassen@firewall.loc
failover@firewall.loc
sysadmin@firewall.loc
}
script_user root
enable_script_security
notification_email_from Alexandre.Cassen@firewall.loc
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 集群环境准备就绪!