基于Ansible快速部署多节点K8S集群

基于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的高可用,实现服务的不间断访问

img

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配置

README-2021-10-14-20-29-31

首先需要安装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

image-20250417162700039

等待几分钟后访问demo.cs.metahere.xyz

image-20250417162723865

image-20250417162754789

可以看到证书是签发成功的,这里可能会遇到的坑比如

  • ingress和service需要在同一个namespace
  • let’s encrypt的速率限制
  • cloudflare的API KEY权限不足/不正确(需要配置API KEY对于DNS区域的读写权限,stringData会自动Base64无需手动Base64编码)
Comments are closed.