基于Ansible快速部署多节点K8S集群
为了便于学习k8s相关技术,需要构建一个简易的k8s学习集群,使用下面的方法我们可以实现快速拉起一套准生产的k8s集群,相较于k3s等all in one集群,有更多的细节和可见性,更加适合学习和理解相关概念。
- 3主2从k8s集群
- 独立的3节点etcd集群
- 独立的2个负载均衡节点
- 独立的NFS存储
环境准备
需要准备10台虚拟机,以及一台能够使用ansible的控制节点,我们全部使用Ubuntu 24.04 LTS作为操作系统
用途 | 数量 |
---|---|
负载均衡 4C4G | 2 |
ETCD集群 4C4G | 3 |
Master节点 4C4G | 3 |
Worker节点 8C16G | 2 |
节点规划
k8s的高可用由控制平面决定,kube-apiserver无状态,提供API服务,通过负载均衡,我们可以实现kube-apiserver的高可用
etcd是k8s集群的分布式存储,使用raft算法实现高可用和数据一致性
nginx是常见的负载均衡器,为一组kube-apiserver提供转发(负载均衡)服务。我们还需要考虑nginx自身的高可用,keepalived是一个基于VRRP协议的解决方案,将多台服务器组成一个虚拟服务器,使用虚拟IP(VIP)绑定到这个虚拟服务器上,然后通过VRRP协议实现IP地址在服务器间的漂移
IP地址 | 主机名 | 用途 |
---|---|---|
192.168.2.51 | k8s_slb1.cs.metahere.xyz | 负载均衡 |
192.168.2.52 | k8s_slb2.cs.metahere.xyz | |
192.168.2.53 | k8s_etcd1.cs.metahere.xyz | etcd |
192.168.2.54 | k8s_etcd2.cs.metahere.xyz | |
192.168.2.55 | k8s_etcd3.cs.metahere.xyz | |
192.168.2.56 | k8s_master1.cs.metahere.xyz | k8s控制节点 |
192.168.2.57 | k8s_master2.cs.metahere.xyz | |
192.168.2.58 | k8s_master3.cs.metahere.xyz | |
192.168.2.59 | k8s_worker1.cs.metahere.xyz | k8s负载节点 |
192.168.2.60 | k8s_worker2.cs.metahere.xyz |
网络规划
网络 | 作用 |
---|---|
192.168.2.50 | 负载均衡器VIP |
192.168.2.0/24 | k8s宿主机网段 |
172.16.0.0/14 | Pod网段 |
10.88.0.0/16 | Servic网段 |
ansible环境准备
首先编写inventory
[slb]
k8s_slb1.cs.metahere.xyz
k8s_slb2.cs.metahere.xyz
[etcd]
k8s_etcd[1:3].cs.metahere.xyz
[master]
k8s_master[1:3].cs.metahere.xyz
[worker]
k8s_worker[1:2].cs.metahere.xyz
确保ansible控制节点能够使用以一个免密的普通用户SSH远程登录到这些节点,这个普通用户应当具有sudo权限
时间同步
确保所有服务器上的时间同步
ansible all -i inventory -m apt -a "name=chrony state=present" -b
ansible all -i inventory -m shell -a "sudo timedatectl set-timezone Asia/Shanghai" -b
ansible all -i inventory -m shell -a "chronyc makestep" -b
ansible all -i inventory -m shell -a "date -R" -b
使用kubeasz部署k8s集群
准备执行环境
# 下载kubeasz
export release=3.6.6
wget https://github.com/easzlab/kubeasz/releases/download/${release}/ezdown
chmod +x ./ezdown
# 下载容器镜像等
./ezdown -D
# 下载额外的镜像
./ezdown -X nfs-provisioner
./ezdown -X prometheus
# 启动容器
./ezdown -S
source ~/.bashrc
初始化集群配置
docker exec -it kubeasz ezctl new k8s_htl
完成后将会生成目录/etc/kubeasz/clusters/k8s_htl,需要手动配置
修改/etc/kubeasz/clusters/k8s_htl/hosts配置etcd、master、worker、ex-lb以及网段规划等
# 'etcd' cluster should have odd member(s) (1,3,5,...)
[etcd]
192.168.2.53
192.168.2.54
192.168.2.55
# master node(s), set unique 'k8s_nodename' for each node
# CAUTION: 'k8s_nodename' must consist of lower case alphanumeric characters, '-' or '.',
# and must start and end with an alphanumeric character
[kube_master]
192.168.2.56 k8s_nodename='master-01'
192.168.2.57 k8s_nodename='master-02'
192.168.2.58 k8s_nodename='master-03'
# work node(s), set unique 'k8s_nodename' for each node
# CAUTION: 'k8s_nodename' must consist of lower case alphanumeric characters, '-' or '.',
# and must start and end with an alphanumeric character
[kube_node]
192.168.2.59 k8s_nodename='worker-01'
192.168.2.60 k8s_nodename='worker-02'
# [optional] harbor server, a private docker registry
# 'NEW_INSTALL': 'true' to install a harbor server; 'false' to integrate with existed one
[harbor]
#192.168.1.8 NEW_INSTALL=false
# [optional] loadbalance for accessing k8s from outside
[ex_lb]
192.168.2.51 LB_ROLE=backup EX_APISERVER_VIP=192.168.2.50 EX_APISERVER_PORT=8443
192.168.2.52 LB_ROLE=master EX_APISERVER_VIP=192.168.2.50 EX_APISERVER_PORT=8443
# K8S Service CIDR, not overlap with node(host) networking
SERVICE_CIDR="10.88.0.0/16"
# Cluster CIDR (Pod CIDR), not overlap with node(host) networking
CLUSTER_CIDR="172.16.0.0/16"
# Cluster DNS Domain
CLUSTER_DNS_DOMAIN="htl.cluster.local"
修改/etc/kubeasz/clusters/k8s_htl/config.yml配置k8s参数,我们需要配置的是其中的镜像下载地址(国内环境需要使用代理)、用于自签证书的IP或者域名(后续使用外部的端口映射或者域名时需要),其他参数需要自行按照需求修改。
INSECURE_REG:
- "http://easzlab.io.local:5000"
- "https://docker.cs.metahere.xyz"
- "https://artifactory.internal.kvmcn.com:50443"
- "https://dockerhub.mirror.432000.xyz"
- "https://dk1.cedu.ac.cn"
- "https://pulldocker.chillifish.cn"
- "https://harbor.cs.metahere.xyz"
MASTER_CERT_HOSTS:
- "192.168.2.50"
- "k8s.cs.metahere.xyz"
- "k8s.cs.432000.xyz"
- "k8s.cs.kvmcn.com"
开始安装,将会执行ansible roles开始安装etcd和k8s节点,
dk ezctl setup k8s_htl all
配置kube-api-server的高可用
我们规划了3个master节点,在集群内部已经实现了高可用,但是在集群外部例如我们需要通过其他方式调用k8s API时,也需要考虑到这一点,kubeasz实现了简单的配置方法
dk ezctl setup k8s_htl ex-lb
完成后你可以在lb节点查看负载均衡服务的状态
ss -ltp
systemctl status l4lb
配置nfs存储
我们需要使用NFS作为持久化的存储,首先配置一个nfs服务器,有条件可以使用netapp或者其他的文件服务器
sudo apt install nfs-kernel-server -y
systemctl status nfs-mountd.service
sudo mkdir -p /k8s_data
sudo chown nobody:nogroup /k8s_data
sudo chmod 777 /k8s_data
sudo vim /etc/exports
/k8s_data *(rw,sync,insecure,no_subtree_check,no_root_squash)
sudo exportfs -ra
sudo systemctl enable --now nfs-kernel-server
修改/etc/kubeasz/clusters/k8s_htl/config.yml增加nfs相关配置
nfs_provisioner_install: "yes"
nfs_provisioner_namespace: "kube-system"
nfs_provisioner_ver: "v4.0.2"
nfs_storage_class: "managed-nfs-storage"
nfs_server: "192.168.2.49"
nfs_path: "/k8s_data"
开始配置
dk ezctl setup k8s_htl 07
验证
kubectl get pod --all-namespaces | grep nfs-client
配置ingress controller
为了暴露服务到集群外部供用户使用,我们使用ngin作为ingress controller
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm search repo ingress-nginx/ingress-nginx --versions
# 使用NodePort暴露ingress的端口
helm upgrade --install ingress-nginx ingress-nginx \
--repo https://kubernetes.github.io/ingress-nginx \
--namespace nginx --create-namespace \
--set controller.service.type=NodePort
kubectl get pods --namespace=nginx
创建一个deployment用于验证
kubectl create deployment demo --image=httpd --port=80
kubectl expose deployment demo
kubectl create ingress demo-localhost --class=nginx \
--rule="demo.localdev.me/*=demo:80"
kubectl port-forward --namespace=ingress-nginx service/ingress-nginx-controller 8080:80
# 验证结果是否成功,应该输出一个It works!
curl --resolve demo.localdev.me:8080:127.0.0.1 http://demo.localdev.me:8080
配置ingress controller的高可用
前面我们配置了kube api server的高可用,现在我们还需要配置ingress的高可用,实现服务的不间断访问
kubeasz已经实现了这部分的简化操作,我们只需要简单的修改配置文件,即可实现
首先我们需要知道ingress controller的服务端口,结果如下,80端口被映射到
kubectl get service ingress-nginx-controller --namespace=nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller NodePort 10.88.193.16 <none> 80:31539/TCP,443:30179/TCP 2d16h
修改kubeasz的配置文件
vim /etc/kubeasz/roles/ex-lb/defaults/main.yml
# 启用 ingress NodePort服务的负载均衡 (yes/no)
INGRESS_NODEPORT_LB: "yes"
# ingress NodePort 的端口号
INGRESS_NODEPORT_LB_PORT: 31539
# 启用 ingress tls NodePort服务的负载均衡 (yes/no)
INGRESS_TLS_NODEPORT_LB: "yes"
# ingress tls NodePort 的端口号
INGRESS_TLS_NODEPORT_LB_PORT: 30179
重新配置ex-lb即可,需要按照之前的方式继续到lb节点上查询服务和端口开放状态
dk ezctl setup k8s_htl ex-lb
配置cert-manager实现服务的自动SSL配置
首先需要安装cert-manager
helm repo add jetstack https://charts.jetstack.io --force-update
helm install cert-manager jetstack/cert-manager \
--namespace cert-manager \
--create-namespace \
--version v1.17.0 \
--set crds.enabled=true \
--set prometheus.enabled=true \
--set webhook.timeoutSeconds=30
创建issuer,我使用cloudflare托管了1个域名,使用cloudflare的API Key创建一个证书签发器,编辑cloudflare-cluster-issuer.yaml
apiVersion: v1
kind: Secret
metadata:
name: cloudflare-api-token-secret
namespace: cert-manager
type: Opaque
stringData:
api-token: '<YOUR CLOUDFLARE API KEY>'
---
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-dns01
spec:
acme:
privateKeySecretRef:
name: letsencrypt-dns01
email: lixxxx@qq.com
server: https://acme-v02.api.letsencrypt.org/directory
solvers:
- dns01:
cloudflare:
apiTokenSecretRef:
key: api-token
name: cloudflare-api-token-secret
然后我们应用它即可
kubectl apply -f cloudflare-cluster-issuer.yaml
kubectl get clusterissuer
kubectl logs -n cert-manager deployment/cert-manager -f
我们在上一节创建了一个简易的httpd服务,我们可以使用它来测试自动的ssl证书签发,创建一个demo-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: demo
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-dns01"
namespace: default
spec:
rules:
- host: demo.cs.metahere.xyz
http:
paths:
- backend:
service:
port:
number: 80
name: demo
path: /
pathType: Prefix
tls:
- hosts:
- demo.cs.metahere.xyz
secretName: demo-http-tls
ingressClassName: nginx
应用这个ingress
kubectl apply -f demo-ingress.yaml
我们需要还需要设置DNS解析到负载均衡VIP的记录,为了简化后续的操作成本,我们在cloudflare中解析*.cs.metahere.xyz*
到192.168.2.50
等待几分钟后访问demo.cs.metahere.xyz
可以看到证书是签发成功的,这里可能会遇到的坑比如
- ingress和service需要在同一个namespace
- let’s encrypt的速率限制
- cloudflare的API KEY权限不足/不正确(需要配置API KEY对于DNS区域的读写权限,stringData会自动Base64无需手动Base64编码)