ingress的安装、资源讲解、为什么需要ingress资源 In 战利商城 @2025-10-12 09:46:26
目录
前言什么是ingress资源,为什么需要ingress资源NodePort的缺点LoadBalancer 的缺点ingress资源(简写ing)Ingress包含的组件ingress(以nginx为例)的工作原理方法一、安装ingress-nginx-controller方法二、安装ingress-nginx-controller(新版本可参考这种方式安装ingress-nginx-controller)方法三、helm 安装ingress-nginx-controller创建ingress资源对象查看ingress同一个主机的不同路径,映射到不同的service不同的主机,映射到不同的service更改ingress-nginx-controller的默认80端口配置ingress处理TLS传输(https)使用负载均衡器将流量负载到到多个ingress节点解决-域名只能解析到ingress-nginx-controller pod所在宿主机节点IP才能访问通的问题
前言
环境:centos7.9 docker-ce-20.10.9 kubernetes-version v1.22.6
本篇来讲解k8s中ingress资源。
什么是ingress资源,为什么需要ingress资源
在 Kubernetes 中,为了使外部的应用能够访问集群内的service,最为常用的是使用 NodePort 和 LoadBalancer 两种类型的service,但它们在使用上还是有一些限制:如,对外提供访问时,NodePort 类型需要在外部搭建额外的负载均衡,比如F5或nginx反向代理,NodePort方式最大的缺点是会占用很多集群机器的端口;而 LoadBalancer 要求 Kubernetes 必须跑在支持的 Cloud Provider 上,由云厂商提供公网IP地址,同时当存在多个LoadBalancer 的类型service时,就会占用大量公网ip地址,而ingress正是为解决以上这种问题而存在的。
NodePort的缺点
这种方式的service要求集群中部分节点有被外网访问的能力。Kubernetes为每一个NodePort类型的服务在集群中的每一个节点上分配至少一个主端口号。客户经过能被外网访问的节点IP加上节点端口的方式访问服务。大多数状况下不会经过这种方式向集群外暴露服务,缘由有四: 1、大多状况下,为了安全起见,集群中的节点位于完全封闭的内网环境中,不该有被外网直接访问的能力。通常外网访问集群中的节点都是经过边界服务器如网关、跳板等,而这种边界服务器须要经过各类方式进行层层安全加固; 2、若是集群内节点能够被外网直接访问的话,将会使集群内节点地址、服务名称、端口号等信息直接暴露在外,很不安全; 3、服务端口号通常由系统自动分配,并不是固定,而服务名称也可能发生变动,此时外部客户端须要跟踪变动并修改,属于重试耦合; 4、NodePort这种类型的service,每一个服务都会在node节点起一个端口号,当创建的service很多时会占用很多集群节点机器的端口。
LoadBalancer 的缺点
LoadBalancer通常由云服务供应商提供或者用户自定义,运行在集群以外。在建立service时其type配置为LoadBalancer,k8s集群会自动分配一个loadbalancer实例,并给service分配该实例,所以当从外网访问集群内servcie时,用户直接链接到LoadBalancer服务器IP,LoadBalancer服务器再将流量转发到集群内service。Loadbalancer配置及使用方法与各云服务供应商有关,本文不详细描述。
ingress资源(简写ing)
除了使用NodePort和LoadBalancer来暴露service,k8s还支持使用ingress资源来暴露service,想要使用ingress,首先必须先创建ingress -controller,即ingress控制器,ingress控制器有很多种,目前k8s官方的ingress控制器是ingress-nginx-controller,由此可见ingress其实也是使用了nginx的7层反向代理来实现的。
ingress的简写为ing,ingress可以实现暴露多个service,即当外部客户端向一个ingress发送连接请求时,ingress会根据请求的主机和路径来决定将请求转发给对应的service。
Ingress包含的组件
Ingress Controller:Ingress控制器,具体实现反向代理即负载均衡的程序,实现七层转发的Edge Router,通过调用k8s的api动态感知集群中Pod的变化而动态更新配置文件并重载, Controller需要部署在k8s集群中以实现和集群中的pod通信,通常以DaemonSets或Deployments的形式部署,并对外暴露80和443端口,对于DaemonSets来说,一般是以hostNetwork或者hostPort的形式暴露,Deployments则以NodePort的方式暴露,Ingress控制器的多个节点则借助外部负载均衡ExternalLB以实现统一接入;
Ingress资源对象:配置规则,即定义如何转发到service的规则;
ingress(以nginx为例)的工作原理
1、用户编写ingress规则,说明哪个域名对应kubernetes集群中的哪个service; 2、ingress控制器动态感知ingress服务的规则的变化,然后生成一段对应的nginx配置; 3、ingress控制器会将生成的nginx配置写入到一个运行着的nginx服务中,并动态更新; 4、到此为止,其实真正工作的就是一个nginx了,内部配置了用户定义的请求转发规则;
方法一、安装ingress-nginx-controller
根据官网的yaml文件来部署即可,不过yaml文件使用的k8s官网的镜像,可能下载不到。
https://kubernetes.github.io/ingress-nginx/deploy
这种部署方式其实是使用DaemonSets 来部署的,同时配置了 ingress-nginx Pod 使用它们运行的主机的网络,而不是专用的网络命名空间。 这种方法的好处是 NGINX Ingress 控制器可以将端口 80 和 443 直接绑定到 Kubernetes 节点的宿主机网络接口,而无需使用 NodePort 类型的服务强加的额外网络转换。
# 从官网下载下俩的yaml文件,需要修改下面这两点:
dnsPolicy: ClusterFirstWithHostNet #默认是ClusterFirst,如果hostNetwork: true则必须改为ClusterFirstWithHostNet
hostNetwork: true #使用宿主机网络,表示直接将端口绑定到主机上
[root@master ingress-nginx]# kubectl get ds -n ingress-nginx
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
ingress-nginx-controller 2 2 2 2 2 kubernetes.io/os=linux 15h
[root@master ingress-nginx]#
[root@master ingress-nginx]# kubectl get pods -n ingress-nginx
NAME READY STATUS RESTARTS AGE
ingress-nginx-controller-lv5m8 1/1 Running 1 (5h19m ago) 15h
ingress-nginx-controller-qcjsr 1/1 Running 1 (5h18m ago) 15h
[root@master ingress-nginx]#
方法二、安装ingress-nginx-controller(新版本可参考这种方式安装ingress-nginx-controller)
新版的ingress-nginx-controller居然是使用deployment部署的,而且其副本数是一个pod:
#官网
https://kubernetes.github.io/ingress-nginx/deploy/
#注意不同的k8s版本对应不同的ingress-nginx版本,我这里属于测试环境,直接部署最新的版本
wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.5.1/deploy/static/provider/cloud/deploy.yaml
#编辑文件,我们需要修改一些文件
vim deploy.yml
dnsPolicy: ClusterFirstWithHostNet #默认是ClusterFirst,改为ClusterFirstWithHostNet
#方法一、在deployment下添加deployment.spec.template.spec.hostNetwork,表示将deployment的容器端口号绑定到宿主机上
#但是这样会有一个问题,那就是pod副本数只能是一个,不然多个pod在同一个宿主机上岂不是端口冲突了,除非你做pod反亲和性;这里官方的yaml文件pod副本数也是一个;
#方法二、修改service的类型为NodePort类型,默认是公有云上的LoadBalancer;同时,绑定nodePort端口,这里我指定了http的30080和https的30443这两个nodePort端口
#以上两者,我选择了方法二
kubectl apply -f deploy.yml #创建资源
方法三、helm 安装ingress-nginx-controller
#添加仓库
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
#由于kubernetes.github.io网站是国外的,所以访问很慢
#可以去https://www.ipaddress.com/site/kubernetes.github.io上搜索对应的ID做/etc/hosts域名解析
#搜索ingress-nginx的chart包
[root@master ~]# helm search repo ingress-nginx
NAME CHART VERSION APP VERSION DESCRIPTION
ingress-nginx/ingress-nginx 4.6.0 1.7.0 Ingress controller for Kubernetes using NGINX a...
#拉去chart包
helm pull ingress-nginx/ingress-nginx --version=4.6.0
#解压chart包
tar xf ingress-nginx-4.6.0.tgz
cd ingress-nginx/
vim values.yaml
工具内容修改
(待完善)
kubectl create namespace ingress-nginx
helm install ingress-nginx xx/
创建ingress资源对象
确保已经存在运行的ingress-nginx-controller了,下面来创建一个ingress对象。
[root@master ingress-nginx]# vim ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress #创建一个ingress资源
metadata:
name: my-ingress #资源名称叫my-ingress
spec:
rules: #定义访问规则,-横杠表示下面可以是多个数组
- host: www.lsp.com #主机域名
http: #网络协议类型
paths:
- path: /lsp #将访问/lsp的请求转发到lsp服务的80端口
pathType: Exact
backend:
service:
name: lsp #service的名称
port:
number: 80 #service的端口
#以上就创建一个单一规则的ingress
查看ingress
[root@master ingress-nginx]# kubectl get ing #ingress的简写为ing
NAME CLASS HOSTS ADDRESS PORTS AGE
my-ingress
[root@master ingress-nginx]#
#这样,windows进行www.lsp.com域名进行解析到192.168.118.132,然后浏览器就能进行www.lsp.com+端口+访问路径进行访问了
注意:
在ingress创建成功之后,查看其ADDRESS栏,如果显示了所有ingress-nginx-controller pod 的IP则表示ingress-nginx-controller里
的nginx程序已经设置好了后端service的endpoint,改ingress可以正常工作,如果ADDRESS栏为空,则表示ingress-nginx-controller里nginx未能正确连接到后端service,需要排查错误。
注意:
如果ingress controller 是使用ds部署直接绑定宿主机网络的,则ADDRESS栏就是全部所有ingress controller pod 的IP
如果ingress controller 是使用service+deployment部署的,则ADDRESS栏就是service的IP地址。
同一个主机的不同路径,映射到不同的service
创建一个ingress,将不同的service映射到相同主机的不同路径,如下:
[root@master ingress-nginx]# vim ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress #创建一个ingress资源
metadata:
name: my-ingress #资源名称叫my-ingress
spec:
rules: #定义访问规则,-横杠表示下面可以是多个数组
- host: www.lsp.com #主机域名,下面是将同一个主机的不同访问路径映射到不同的service中去
http: #网络协议类型
paths:
- path: /lsp #将访问www.lsp.com/lsp的请求转发到lsp服务的80端口
pathType: Exact
backend:
service:
name: lsp #service的名称
port:
number: 80 #service的端口
- path: /dossier #将访问路径www.lsp.com/dossier的请求转发到Tomcat服务的8080端口
pathType: Exact
backend:
service:
name: dossier #service的名称
port:
number: 8080 #service的端口
#以上,根据请求的URL中的路径,请求将发送到2个不同的服务,因此,客户端可以通过一个IP地址(ingress控制器的ip地址)访问2个不同的服务。
不同的主机,映射到不同的service
同理,也可以在一个ingress中定义不同的主机,来映射到不同的service,如下所示:
[root@master ingress-nginx]# vim ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress #创建一个ingress资源
metadata:
name: my-ingress #资源名称叫my-ingress
spec:
rules: #定义访问规则,-横杠表示下面可以是多个数组
- host: www.lsp.com #第一个主机域名
http: #网络协议类型
paths:
- path: /lsp #将访问www.lsp.com/lsp的请求转发到lsp服务的80端口
pathType: Exact
backend:
service:
name: lsp #service的名称
port:
number: 80 #service的端口
- host: www.dossier.com #第二个主机域名
http:
paths:
- path: /dossier #将访问路径www.dossier.com/dossier的请求转发到Tomcat服务的8080端口
pathType: Exact
backend:
service:
name: dossier #service的名称
port:
number: 8080 #service的端口
#以上,根据请求中的host头,请求将被分发到对应的主机的service中去,因此,客户端可以通过一个IP地址(ingress控制器的ip地址)访问多个主机/域名中的service。
更改ingress-nginx-controller的默认80端口
前面我们在ingress的yaml文件中定义了访问域名,如www.lsp.com,然后在浏览器就能指定路径访问了,如:http://www.lsp.com/lsp,但是细心的人会发现,我们访问的时候并没有指定端口,这是因为http协议默认就是80端口,如果客户不给使用80端口,或者80端口已经被占用了,那这么办?这时就需要修改ingress的默认端口了,我们知道ingress使用的是nginx(官网推荐就是nginx),所以80端口其实就是ingress的默认端口,我们如果有需求要改变该端口,可以在ingress控制器中做修改,如下:
然后kubectl apply -f ingress-nginx-controller-deployment.yaml,pod就会被重新创建,然后页面访问就需要指定端口访问了,如:http://www.lsp.com:8012/lsp
配置ingress处理TLS传输(https)
https是需要秘钥和证书的,所以首先需要创建秘钥和证书,如下:
#创建证书
[root@master ingress-nginx]# openssl genrsa -out tls.key 2048
[root@master ingress-nginx]# openssl req -new -x509 -key tls.key -out tls.cert -days 365 -subj /CN=www.lsp.com
#创建秘钥
[root@master ingress-nginx]# kubectl create secret tls tls-secret --cert=tls.cert --key=tls.key
secret/tls-secret created
#创建一个使用https协议链接的ingress资源
[root@master ingress-nginx]# cat my-ingress-tls.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-ingress
spec:
tls: #定义tls参数
- hosts:
- www.lsp.com #指定访问的主机
secretName: tls-secret #指定使用的秘钥
rules: #下面这些和之前的保持不变
- host: www.lsp.com
http:
paths:
- path: /
pathType: Exact
backend:
service:
name: svc-nginx-clusterip-ingress2
port:
number: 8099
[root@master ingress-nginx]#
此时,网页访问:https://www.lsp.com/ 就能正常访问了,https默认端口是443。
使用负载均衡器将流量负载到到多个ingress节点
ingress上定义了域名,那么客户端浏览器必须使用域名进行访问,生产中可以需要多个ingress节点接收流量,所以整个流程是这样的:
1、用户访问http://www.fuyu.com域名
2、LB解析了www.fuyu.com域名,所以请求到达LB服务器上,LB服务器可以是nginx或F5等软硬件;
3、LB服务器上根据域名配置了对多个node节点80端口进行请求转发;
4、80端口是ingress绑定的,所以ingress接收流量,并根据定义的规则转发给对应的service;
5、service将流量转发到后端的pod。
解决-域名只能解析到ingress-nginx-controller pod所在宿主机节点IP才能访问通的问题
我们使用deployment部署的ingress-nginx,修改svc为NodePort类型用于暴露80、443端口:
# ingress-nginx-controller的pod现在在node2节点上而且现在我们只有一个pod
[root@master ~]# kubectl -n ingress-nginx get po -owide
NAME READY STATUS RESTARTS AGE IP NODE
ingress-nginx-controller-74696c6dbc-bpr78 1/1 Running 0 56m 10.244.1.71 node2
# ingress-nginx-controller的service
[root@master ~]# kubectl -n ingress-nginx get svc ingress-nginx-controller
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller NodePort 10.106.156.222
# 创建了一个ingress资源对象
[root@master ~]# kubectl get ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
nginx-ingress nginx www.nginx-test.com 10.106.156.222 80 17d
1、在Windows上的hosts文件配置:node2-IP www.nginx-test.com 这条解析域名的记录,发现谷歌浏览器可以正常访问;
2、在Windows上的hosts文件配置:node1-IP www.nginx-test.com 这条解析域名的记录,发现谷歌浏览器不能正常访问;
3、在Windows上的hosts文件配置:master-IP www.nginx-test.com 这条解析域名的记录,发现谷歌浏览器不能正常访问;
这就有点想不通了,80是service的nodeport暴露的端口,按理说该端口会暴露在每个节点上的呀,怎么解析的域名要绑定的IP是ingress-nginx-
controller pod的所在宿主机IP才能通呢?依次排查,将ingress-nginx-controller-74696c6dbc-bpr78 pod调度到master节点继续测试,发现
问题依然如此,域名要绑定到pod所在的宿主机IP才能访问的通,奇怪了。这里尝试将pod的副本增加到2个并且利用pod的反亲和性将不同pod分配到两个
节点上,现在还有一个节点没有跑pod,继续测试,仍然发现只要域名绑定了没有pod的宿主机IP的时候访问不通,其他情况通。
奇怪了,service不是会转发流量到后端的pod的吗,怎么现在需要在每个节点上都部署一个ingress-nginx-controller pod才能正常访问呢?
问题解决:
问题出现在service的流量转发策略上。并不是说svc没有在全部的宿主机上暴露NodePort端口。
# 查看svc的流量转发策略
kubectl -n ingress-nginx get svc ingress-nginx-controller -oyaml | grep TrafficPolicy
externalTrafficPolicy: Local # 这个改成Cluster
internalTrafficPolicy: Cluster
#然后就正常了,现在即使ingress-nginx-controller pod只要一个,无法在Windows上的hosts文件配置了哪个宿主机IP,都能正常了。
解读一下这个两个参数:
externalTrafficPolicy: Cluster 默认值,表示外部流量可以转发到其他节点上的pod (适用于负载均衡,但用于转发会有点网络性能损耗)
externalTrafficPolicy: Local 表示外部流量只转发给本机上的pod (性能好,没有网络流量转发损耗)
internalTrafficPolicy:Cluster 默认值,表示内部流量可以转发到其他节点上的pod
internalTrafficPolicy: Local 表示内部流量只转发给本机上的pod
本质生说,这些参数值的控制会导致kube-proxy生成的iptables或者ipvs规则会有点区别。
这篇文章讲解的很好:https://blog.51cto.com/u_15481067/11748833
官网对这两个参数的讲解:
https://kubernetes.io/zh-cn/docs/reference/networking/virtual-ips/#external-traffic-policy
https://kubernetes.io/zh-cn/docs/reference/networking/virtual-ips/#internal-traffic-policy
当我们创建一个ClusterIP 类型的service的时候,没有定义internalTrafficPolicy参数,该参数自动生成并且值默认为Cluster;
当我们将svc改成NodePort时,自动创建了externalTrafficPolicy: Cluster参数。
所以由此得知,不管service类型是什么,都有internalTrafficPolicy参数,而且当service的类型是NodePort和LoadBalancer时,自动添加
externalTrafficPolicy: Cluster。
所以,以上,我们就知道为什么ingress-nginx 的域名访问不通了吧,因为ingress-nginx创建的service默认定义了externalTrafficPolicy:
Local