k8s 对外暴露服务(service)主要有两种方式:NotePort, LoadBalance,此外 externalIPs 也可以使各类 service 对外提供服务,但是当集群服务很多的时候,NodePort 方式最大的缺点是会占用很多集群机器的端口;LoadBalance 方式最大的缺点则是每个 service 一个 LB 又有点浪费和麻烦,并且需要 k8s 之外的支持;而 ingress 则只需要一个 NodePort 或者一个 LB 就可以满足所有 service 对外服务的需求。工作机制大致可以用下图表示:
提示:如上图所示,ingress 就是 ingress 控制器 pod 的代理规则;用户请求某个后端 pod 所提供的服务时,首先会通过 ingress controller pod 把流量引入到集群内部,然后 ingress controller pod 根据 ingress 定义的规则,把对应 ingress 规则转化为对应 ingress controller pod 实现的对应应用的配置(ingress controller 可以由任何具有七层反向代理功能的服务实现,比如 nginx,haproxy 等等)然后再适配用户请求,把对应请求反代到对应 service 上;而对于 pod 的选择上,ingress 控制器可以基于对应 service 中的标签选择器,直接同 pod 通信,无须通过 service 对象 api 的再次转发,从而省去了用户请求到 kube-proxy 实现的代理开销(本质上 ingress controller 也是运行为一个 pod,和其他 pod 在同一网段中)。
Ingress 可以给 Service 提供集群外部访问的 URL、负载均衡、SSL 终止、HTTP 路由等。为了配置这些 Ingress 规则,集群管理员需要部署一个 Ingress Controller,它监听 Ingress 和 Service 的变化,并根据规则配置负载均衡并提供访问入口。
Ingress 控制器和 k8s 上的其他控制不一样,ingress 控制器并不能直接运行为 kube-controller-manager 的一部分,它类似 k8s 集群上的 coredns,需要在集群上单独部署,本质上就是一个 pod,我们可以使用 k8s 上的 ds 或 deploy 控制器来创建它。
2.Ingress的核心组件
要理解 ingress,需要区分两个概念,ingress 和 ingress-controller:
- ingress controller:核心是一个 deployment,实现方式有很多,比如 nginx, Contour, Haproxy, trafik, Istio,需要编写的 yaml 有:Deployment, Service, ConfigMap, ServiceAccount(Auth),其中 service 的类型可以是 NodePort 或者 LoadBalancer。具体是实现反向代理及负载均衡的程序,对 ingress 定义的规则进行解析,根据配置的规则来实现请求转发;简单来说,Ingress-controller 才是负责转发的组件,通过各种方式将他暴露在集群入口,外部对集群的请求流量会先到 Ingress-controller,而 Ingress 对象是用来告诉 Ingress-controller 该如何转发请求,比如那些域名那些 path 要转发到那些服务等.
- ingress:这个就是一个类型为 Ingress 的 k8s api 对象了,一般用 yaml 配置,作用是定义请求如何转发到 service 的规则,可以理解为配置模板。
假设已经有两个服务部署在了k8s集群内部:
$ kubectl get svc,deploy
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
svc/coffee-svc ClusterIP <none> <none> 80/TCP 1m
svc/tea-svc ClusterIP <none> <none> 80/TCP 1m
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
deploy/coffee 2 2 2 2 1m
deploy/tea 1 1 1 1 1m
配置 Ingress resources,即可实现多个service对外暴露服务:
1)coffee-svc
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: cafe-ingress
spec:
rules:
# 配置七层域名
- host: foo.bar.com
http:
paths:
# 配置Context Path
- path: /tea
backend:
serviceName: tea-svc
servicePort: 80
# 配置Context Path
- path: /coffee
backend:
serviceName: coffee-svc
servicePort: 80
2) tea-svc
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: cafe-ingress
spec:
rules:
# 配置七层域名
- host: tea.foo.bar.com
http:
paths:
- backend:
serviceName: tea-svc
servicePort: 80
- host: coffee.foo.bar.com
http:
paths:
- backend:
serviceName: coffee-svc
servicePort: 80
接着在hosts文件中添加一条解析规则即可在浏览器访问了。
部署Nginx Ingress Controller( 基于Nginx 来处理请求)
资料信息地址:
Ingress-Nginx-github 地址: https://github.com/kubernetes/ingress-nginx
Ingress-Nginx 官方地址: https://kubernetes.github.io/ingress-nginx
注意:选择合适的版本
下载并修改配置文件
下载整合配置文件,获取配置文件地址:https://github.com/kubernetes/ingress-nginx/tree/nginx-0.30.0/deploy/static
# wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.30.0/deploy/static/mandatory.yaml
# service-nodeport.yaml为ingress通过nodeport对外提供服务,注意默认nodeport暴露端口为随机,可以编辑该文件自定义端口。
# wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.30.0/deploy/static/provider/baremetal/service-nodeport.yaml
应用yml文件创建ingress资源:
# kubectl apply -f mandatory.yaml
# kubectl apply -f service-nodeport.yaml
# kubectl get all -n ingress-nginx -o wide
Ingress Contronler 通过与 Kubernetes API 交互,动态的去感知集群中 Ingress 规则变化,然后读取它,按照自定义的规则,规则就是写明了哪个域名对应哪个service,生成一段 Nginx 配置,再写到 Nginx-ingress-control的 Pod 里,这个 Ingress Contronler 的pod里面运行着一个nginx服务,控制器会把生成的nginx配置写入/etc/nginx.conf文件中,然后 reload 一下 使用配置生效。以此来达到域名分配置及动态更新的问题。
接下来访问Ingress的nodeport端口测试发现是404,这是正常现象,因为没有配置后台服务。下面我们来实际应用一下。
实际应用举例
3.2.1创建svc及后端deployment
# cat test-ingress-pod.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: web-a
name: web-a
spec:
replicas: 2
selector:
matchLabels:
app: web-a
template:
metadata:
labels:
app: web-a
spec:
containers:
- image: nginx
name: web-a
---
apiVersion: v1
kind: Service
metadata:
name: web-a
labels:
app: web-a
spec:
ports:
- port: 80
targetPort: 80
selector:
app: web-a
应用yaml:
# kubectl apply -f test-ingress-pod.yaml
# kubectl get pod
NAME READY STATUS RESTARTS AGE
web-a-89b89d4dd-chhhq 1/1 Running 0 38s
web-a-89b89d4dd-tjh82 1/1 Running 0 38s
查看service:
# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 15d
web-a ClusterIP 10.96.214.85 <none> 80/TCP 64s
创建Ingress规则
其实就是配置NGINX的一个过程
# ingress规则中,要指定需要绑定暴露的svc名称
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress-web-a
annotations:
kubernetes.io/ingress.class: "nginx" # 指定 Ingress Controller 的类型
nginx.ingress.kubernetes.io/use-regex: "true" # 指定我们的 rules 的 path 可以使用正则表达式
nginx.ingress.kubernetes.io/proxy-connect-timeout: "600" # 连接超时时间,默认为 5s
nginx.ingress.kubernetes.io/proxy-send-timeout: "600" # 后端服务器回转数据超时时间,默认为 60s
nginx.ingress.kubernetes.io/proxy-send-timeout: "600" # 后端服务器响应超时时间,默认为 60s
nginx.ingress.kubernetes.io/proxy-body-size: "10m" # 客户端上传文件,最大大小,默认为 20m
spec:
rules: #定义路由规则
- host: www.web-a.com # 主机名,只能是域名,修改为你自己的
http:
paths:
- path: /
backend:
serviceName: web-a # 后台部署的 Service Name
servicePort: 80 # 后台部署的 Service Port
说明:在ingress配置中,annotations很重要,前面有说ingress-controller有很多不同的实现,而不同的Ingress-controller就可以根据”kubernetes.io/ingress.class”来判断要使用那些ingress配置,同时,不同的ingress-controller也有对应的annotations配置,用于自定义一些参数。
应用yaml文件:
# kubectl apply -f test-ingress-web-a.yaml
kubectl get ingress
Warning: extensions/v1beta1 Ingress is deprecated in v1.14+, unavailable in v1.22+; use networking.k8s.io/v1 Ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
ingress-web-a <none> www.web-a.com 10.96.148.17 80 6m26s
在win主机配置hosts域名解析c:\windows\system32\drivers\etc\hosts:
192.168.0.25 www.web-a.com
访问测试:域名+nodeport的模式
总结
- 上面我们创建一个针对于nginx的deployment资源,pod为2个;
- 为nginx的pod暴露service服务,名称为web-a
- 通过ingress把nginx暴露出去
这里对于nginx创建的svc服务,其实在实际调度过程中,流量是直接通过ingress然后调度到后端的pod,而没有经过svc服务,svc只是提供一个收集pod服务的作用。
Ingress的部署,架构简谈
Ingress的部署,需要考虑两个方面:
- 1 . ingress-controller 是作为 Pod 来运行的,以什么方式部署比较好
- 2 . ingress 解决了如何请求路由到集群内部,那他自己怎么暴露给外部比较好
下面列举一些目前常用的部署和暴露方式,具体使用哪种方式还得根据实际需要考虑来决定:
- Deployment+LoadBalancer模式的service
如果要把ingress部署在公有云,那用这种方式比较合适。用Deployment部署ingress-controller,创建一个type为LoadBalancer的service关联这组pod。大部分公有云,都会为LoadBalancer的service自动创建一个负载均衡器,通常还绑定了公网地址。只要把域名解析指向该地址,就实现了集群服务的对外暴露。
- Deloyment+NodePort模式的service
同样用deployment模式部署ingress-controller,并创建对应的服务,但是type为NodePort。这样,ingress就会暴露在集群节点ip的特定端口上。由于nodeport暴露的端口是随机端口,一般会在前面再搭建一套负载均衡器来转发请求。该方式一般用于宿主机是相对固定的环境ip地址不变的场景。 NodePort方式暴露ingress虽然简单方便,但是NodePort多了一层NAT,在请求量级很大时可能对性能会有一定影响。
- DaemonSet+HostNetwork+nodeSelector
用DaemonSet结合nodeselector来部署ingress-controller到特定的node上,然后使用HostNetwork直接把该pod与宿主机node的网络打通,直接使用宿主机的80/433端口就能访问服务。这时,ingress-controller所在的node机器就很类似传统架构的边缘节点,比如机房入口的nginx服务器。该方式整个请求链路最简单,性能相对NodePort模式更好。缺点是由于直接利用宿主机节点的网络和端口,一个node只能部署一个ingress-controller pod。比较适合大并发的生产环境使用。
DaemonSet+HostNetwork+nodeSelector部署
DaemonSet简介:
DaemonSet 是指在 Kubernetes 集群里运行一个 Daemon Pod, 它有如下特征:
- 这个 Pod 默认运行在 Kubernetes 集群里的每一个Node节点上;
- 每个节点上只有一个这样的 Pod 实例;
- 当有新的节点加入集群时,会自动创建该 Pod 。
修改mandatory.yaml,主要修改pod相关:
apiVersion: apps/v1
kind: DaemonSet #修改为DaemonSet
metadata:
name: nginx-ingress-controller
namespace: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
spec:
#replicas: 1 #注销此行,DaemonSet不需要此参数
selector:
matchLabels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
template:
metadata:
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
annotations:
prometheus.io/port: "10254"
prometheus.io/scrape: "true"
spec:
# wait up to five minutes for the drain of connections
terminationGracePeriodSeconds: 300
serviceAccountName: nginx-ingress-serviceaccount
hostNetwork: true #添加该字段让docker使用物理机网络,在物理机暴露服务端口(80),注意物理机80端口提前不能被占用
dnsPolicy: ClusterFirstWithHostNet #使用hostNetwork后容器会使用物理机网络包括DNS,会无法解析内部service,使用此参数让容器使用K8S的DNS
nodeSelector: #添加节点标签,在符合标签的node上部署,所以运行此服务的node标签要打上这个标签
kubernetes.io/ingress-controller-ready: "true"
tolerations: #添加对指定node节点污点的容忍度,和node节点的污点有关系,pod指定了容忍度后,就可以在对应的打了此污点的node上部署。
- key: "node-role.kubernetes.io/master"
operator: "Equal"
value: ""
effect: "NoSchedule"
containers:
- name: nginx-ingress-controller
image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.30.0
……
修改参数如下:
- kind: Deployment #修改为DaemonSet
- replicas: 1 #注销此行,DaemonSet不需要此参数
- hostNetwork: true #添加该字段让docker使用物理机网络,在物理机暴露服务端口(80),注意物理机80端口提前不能被占用
- dnsPolicy: ClusterFirstWithHostNet #使用hostNetwork后容器会使用物理机网络包括DNS,会无法解析内部service,使用此参数让容器使用K8S的DNS
- nodeSelector:kubernetes.io/ingress-controller-ready: “true” #添加节点标签,在符合标签的node上部署,所以运行此服务的node标签要改成这个标签
- tolerations: 添加对指定node节点的污点容忍度,和node节点的污点有关系,pod指定了容忍度后,就可以在对应的打了此污点的node上部署。
示例环境: k8smaster01 k8snode01 k8snode02 k8snode03 在node01、node02部署Ingress,所以给这2台node打标签:这一步骤限定的部署的范围 先查看下:
kubectl get nodes --show-labels
# 根据上面配置文件中nodeSelector中定义的给node01和node02打上标签:
kubectl label nodes k8snode01 kubernetes.io/ingress-controller-ready=true
kubectl label nodes k8snode02 kubernetes.io/ingress-controller-ready=true
# 给node02、node02打上污点,污点内容根据上述配置文件中tolerations定义的来进行:这一步主要的作用是限定了只有容忍这个污点的pod才可以在这个节点上部署,也就是只有Ingress才能在这个node上部署。
kubectl taint nodes k8snode01 node-role.kubernetes.io/master=:NoSchedule
kubectl taint nodes k8snode02 node-role.kubernetes.io/master=:NoSchedule
# 说明:因为value是空的,所以=后面直接就是:NoSchedule
查看污点:
# kubectl describe nodes k8snode01
Name: k8snode01
Roles: <none>
Labels: beta.kubernetes.io/arch=amd64
beta.kubernetes.io/os=linux
kubernetes.io/arch=amd64
kubernetes.io/hostname=k8snode01
kubernetes.io/ingress-controller-ready=true
kubernetes.io/os=linux
Annotations: flannel.alpha.coreos.com/backend-data: {"VNI":1,"VtepMAC":"3a:bc:8d:c3:5f:47"}
flannel.alpha.coreos.com/backend-type: vxlan
flannel.alpha.coreos.com/kube-subnet-manager: true
flannel.alpha.coreos.com/public-ip: 192.168.0.27
kubeadm.alpha.kubernetes.io/cri-socket: /var/run/dockershim.sock
node.alpha.kubernetes.io/ttl: 0
volumes.kubernetes.io/controller-managed-attach-detach: true
CreationTimestamp: Sun, 05 Sep 2021 04:26:07 -0400
Taints: node-role.kubernetes.io/master:NoSchedule
Unschedulable: false……
部署Ingress:
kubectl apply -f mandatory.yaml
kubectl get pod -n ingress-nginx -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-ingress-controller-hpnz8 1/1 Running 0 7m36s 192.168.0.28 k8snode02 <none> <none>
nginx-ingress-controller-pjxr8 1/1 Running 0 7m36s 192.168.0.27 k8snode01 <none> <none>
测试:
ingress规则还是使用前面我们创建的。
在win主机上直接解析,IP地址为node01或node02任意节点ip即可,访问的时候也无需再加端口:
参考
EOF
Power by TeXt.