为了便于学习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
|
1 2 3 4 5 6 7 8 9 10 11 12 |
[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权限
## 时间同步
确保所有服务器上的时间同步
|
1 2 3 4 |
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集群
准备执行环境
|
1 2 3 4 5 6 7 8 9 10 11 12 |
# 下载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 |
初始化集群配置
|
1 |
docker exec -it kubeasz ezctl new k8s_htl |
完成后将会生成目录/etc/kubeasz/clusters/k8s_htl,需要手动配置
修改/etc/kubeasz/clusters/k8s_htl/hosts配置etcd、master、worker、ex-lb以及网段规划等
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
# '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或者域名(后续使用外部的端口映射或者域名时需要),其他参数需要自行按照需求修改。 ```bash 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节点,
|
1 |
dk ezctl setup k8s_htl all |
## 配置kube-api-server的高可用
我们规划了3个master节点,在集群内部已经实现了高可用,但是在集群外部例如我们需要通过其他方式调用k8s API时,也需要考虑到这一点,kubeasz实现了简单的配置方法
|
1 |
dk ezctl setup k8s_htl ex-lb |
完成后你可以在lb节点查看负载均衡服务的状态
bash
ss -ltp
systemctl status l4lb
## 配置nfs存储
我们需要使用NFS作为持久化的存储,首先配置一个nfs服务器,有条件可以使用netapp或者其他的文件服务器
|
1 2 3 4 5 6 7 8 9 10 |
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相关配置
|
1 2 3 4 5 6 |
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" |
开始配置
|
1 |
dk ezctl setup k8s_htl 07 |
验证
|
1 |
kubectl get pod --all-namespaces | grep nfs-client |
## 配置ingress controller
为了暴露服务到集群外部供用户使用,我们使用ngin作为ingress controller
|
1 2 3 4 5 6 7 8 9 10 |
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用于验证
|
1 2 3 4 5 6 7 8 9 10 |
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端口被映射到
|
1 2 3 |
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 80:31539/TCP,443:30179/TCP 2d16h |
修改kubeasz的配置文件
|
1 2 3 4 5 6 7 8 9 |
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节点上查询服务和端口开放状态
|
1 |
dk ezctl setup k8s_htl ex-lb |
## 配置cert-manager实现服务的自动SSL配置

首先需要安装cert-manager
|
1 2 3 4 5 6 7 8 |
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
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
apiVersion: v1 kind: Secret metadata: name: cloudflare-api-token-secret namespace: cert-manager type: Opaque stringData: api-token: '' --- 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 |
然后我们应用它即可
|
1 2 3 |
kubectl apply -f cloudflare-cluster-issuer.yaml kubectl get clusterissuer kubectl logs -n cert-manager deployment/cert-manager -f |
我们在上一节创建了一个简易的httpd服务,我们可以使用它来测试自动的ssl证书签发,创建一个demo-ingress.yaml
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
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
|
1 |
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编码)