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 www.lsp.com 192.168.118.132,192.168.118.133 80 24m

[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 80:80/TCP,443:443/TCP 66m

# 创建了一个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

More translations of巨人in English
Paragon 开源项目使用教程