k8s1.21搭建ingress-nginx高可用集群
在k8s1.21 安装ingress-nginx这篇文章中,我们借助ingress,通过Deployment + nodePort的方式将集群内部的服务暴露出去。此时,整个集群中只有一个ingress-nginx实例,可以通过
kubectl get svc -n ingress-nginx -o wide
查看ingress-nginx绑定的端口。如果这个ingress-nginx出现了故障,将导致整个集群不可用。本文介绍一种DaemonSet+HostNetwork+nodeSelector方式搭建的多ingress-nginx实例高可用集群。
- HostNetwork
是为了打通Cluster和node的网络,让Cluster直接监听node的端口,一般是80和443,不用再通过随机绑定的nodePort来访问集群服务。拿k8s1.21 安装ingress-nginx这里的例子来说就是
以前访问:http://www.zcx.com:30080/web1/contextpath
现在访问:http://www.zcx.com/web1/contextpath
可以看到不再需要随机绑定的nodePort了,因为hostNetwork直接监听集群的80端口。
- nodeSelector
可以通过nodeSelector确定将ingress-nginx与哪些主机的
在DNS解析的时候,一定要解析这些被select的节点,不再是解析集群中任意节点了
比如我们的nodeSelector把ingress-nginx的pod调度到k8snode02节点了,那么DNS解析时 只能 这么写
192.168.56.121 k8snode01
192.168.56.122 k8snode02 www.zcx.com
192.168.56.123 k8snode03
因为ingress-nginx的pod被调度到k8snode02上,就会把k8snode02的80端口和443端口和集群打通,而不会把k8snode01或k8snode03的80端口和443端口和集群打通。
- DaemonSet
因为Deployment可能会把多个pod调度到同一个node,那就失去高可用的意义了。而DaemonSet在一个节点上只会有一个Pod,符合我们的要求。
要根据标签调度Pod,那先打标签。我们有k8snode01(master)、k8snode02、k8snode03三个节点,计划在k8snode02和k8snode03上安装ingress-nginx。
#打标签
kubectl label node k8snode02 hasIngress=true
kubectl label node k8snode03 hasIngress=true
下载ingress-nginx镜像和yaml文件参考基于K8S 1.21.2集群安装Ingress-Nginx 0.48.1
修改yaml文件
只需要改Deployment部分,一共修改三处
# Source: ingress-nginx/templates/controller-deployment.yaml
#有文章把这里改成apiVersion: extensions/v1beta1,我这里会报错,不改不报错,应该跟k8s版本有关,这里是v1.21
apiVersion: apps/v1
#kind: Deployment
kind: DaemonSet #这里把Deployment改成DaemonSet
metadata:
labels:
helm.sh/chart: ingress-nginx-3.34.0
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/version: 0.48.1
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/component: controller
name: ingress-nginx-controller
namespace: ingress-nginx
spec:
selector:
matchLabels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/component: controller
revisionHistoryLimit: 10
minReadySeconds: 0
template:
metadata:
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/component: controller
spec:
dnsPolicy: ClusterFirst
hostNetwork: true #这里加一句
containers:
- name: controller
image: k8s.gcr.io/ingress-nginx/controller:v0.48.1
imagePullPolicy: IfNotPresent
lifecycle:
preStop:
exec:
command:
- /wait-shutdown
args:
- /nginx-ingress-controller
- --election-id=ingress-controller-leader
- --ingress-class=nginx
- --configmap=$(POD_NAMESPACE)/ingress-nginx-controller
- --validating-webhook=:8443
- --validating-webhook-certificate=/usr/local/certificates/cert
- --validating-webhook-key=/usr/local/certificates/key
.....省略
nodeSelector:
kubernetes.io/os: linux
hasIngress: "true" # 这里个面具标签选择node
serviceAccountName: ingress-nginx
terminationGracePeriodSeconds: 300
volumes:
- name: webhook-cert
secret:
secretName: ingress-nginx-admission
可以看到启动了两个ingress-nginx实例,已经调度到指定的node上了。
此时在k8snode02和k8snode03上查看端口经停情况
netstat -lntup | grep nginx
可以看到ingress-nginx已经在监听本机80和443端口了。
Deploy、Svc和Ingress
记得要自己整一个springboot项目,参见k8s1.21 安装ingress-nginx
kind: Ingress
metadata:
name: nginx-http
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/use-regex: "true"
namespace: default
spec:
rules:
- host: "www.zcx.com"
http:
paths:
- path: /web1
pathType: Prefix
backend:
service:
name: ws1-svc
port:
number: 9090
- path: /web2
pathType: Prefix
backend:
service:
name: ws2-svc
port:
number: 9090
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: ws1-deploy
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: ws1
template:
metadata:
labels:
app: ws1
spec:
containers:
- name: ws1
image: ws1:latest
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 9090
---
apiVersion: v1
kind: Service
metadata:
name: ws1-svc
namespace: default
spec:
type: ClusterIP # 默认类型
selector:
app: ws1
ports:
- name: http
port: 9090
targetPort: 9090
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: ws2-deploy
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: ws2
template:
metadata:
labels:
app: ws2
spec:
containers:
- name: ws2
image: ws2:latest
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 9090
---
apiVersion: v1
kind: Service
metadata:
name: ws2-svc
namespace: default
spec:
type: ClusterIP # 默认类型
selector:
app: ws2
ports:
- name: http
port: 9090
targetPort: 9090
DNS解析
前面也提到了,此时哪个node安装了ingress-nginx,就得解析哪个node。
#在测试机上
vim /etc/hosts
测试
curl www.zcx.com/web1/contextpath
curl www.zcx.com/web2/contextpath
此时,驱逐k8snode02
#在master上
kubectl drain k8snode02 --delete-local-data --force --ignore-daemonsets node/k8snode02
kubectl delete node k8snode02
#在k8snode02上
kubeadm reset
#在master上检查ingress-nginx的Pod是不是少了,如下图,可知k8snode02已经从集群删除
kubectl get pod -n ingress-nginx -o wide
#再发起请求
curl www.zcx.com/web1/contextpath
curl www.zcx.com/web2/contextpath
可以看到,服务仍然可用。
后记
- 把k8snode02再加回来后,可能会出现Pod无法调度的问题:failed to set bridge addr: “cni0“ already has an IP address different from xxx问题,可以参照这篇文章解决。
- 虽然ingress-nginx直接监听node的80和443端口,其实此时仍然有随机绑定的端口
kubectl get svc -n ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller NodePort 10.1.70.66 <none> 80:32034/TCP,443:31032/TCP 50m
ingress-nginx-controller-admission ClusterIP 10.1.72.104 <none> 443/TCP 50m
可以看到绑定了32034端口,此时仍然可以通过http://www.zcx.com:32034/web1/contextpath来请求服务。当然这是因为我们的安装文件是一个大而全的文件,完全可以把这个Service去掉,这样就不会随机绑定端口了。
随机绑定端口的nodePort是这个Service。
# Source: ingress-nginx/templates/controller-service.yaml
apiVersion: v1
kind: Service
metadata:
annotations:
labels:
helm.sh/chart: ingress-nginx-3.34.0
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/version: 0.48.1
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/component: controller
name: ingress-nginx-controller
namespace: ingress-nginx
spec:
type: NodePort
ports:
- name: http
port: 80
nodePort: 30080 #可以指定绑定的端口
protocol: TCP
targetPort: http
- name: https
port: 443
nodePort: 30443 #可以指定绑定的端口
protocol: TCP
targetPort: https
selector:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/component: controller