5.4 通过Ingress暴露服务
5.4 通过Ingress暴露服务
现在已经介绍了向集群外部的客户端公开服务的两种方法,还有另一种方法——创建Ingress资源。
定义 Ingress(名词)——进入或进入的行为;进入的权利;进入的手段或地点;入口。
接下来解释为什么需要另一种方式从外部访问Kubernetes服务。
为什么需要Ingress
一个重要的原因是每个LoadBalancer 服务都需要自己的负载均衡器,以及独有的公有IP地址,而Ingress只需要一个公网IP就能为许多服务提供访问。当客户端向Ingress发送HTTP请求时,Ingress会根据请求的主机名和路径决定请求转发到的服务, 通过一个Ingress暴露多个服务
Ingress在网络栈(HTTP)的应用层操作,并且可以提供一些服务不能实现的功能,诸如基于cookie的会话亲和性(session affinity)等功能。
Ingress控制器是必不可少的
在介绍Ingress对象提供的功能之前,必须强调只有Ingress控制器在集群中运行,Ingress资源才能正常工作。不同的Kubernetes环境使用不同的控制器实现,但有些并不提供默认控制器。
例如,Google Kubernetes Engine使用Google Cloud Platform带有的HTTP负载平衡模块来提供Ingress功能。最初,Minikube没有提供可以立即使用的控制器,但它现在包含一个可以启用的附加组件,可以试用Ingress功能。请根据下面的补充信息里的说明确保Ingress功能已启用。
在minikube上启动Ingress的扩展功能
如果使用Minikube运行本书中的示例,则需要确保已启用Ingress附加组件。可以通过列出所有附件来检查Ingress是否已启动:
$ minikube addons list
| ingress | minikube | enabled ✅ | unknown (third-party) |
通过本书可以了解这些附加组件,但应该对dashboard和kube-dns附件的用途十分清楚。启用Ingress附加组件,并查看正在运行的Ingress:
$ minikube addons enable ingress
这应该会在另一个pod上运行一个Ingress控制器。控制器pod很可能位于kube-system命名空间中,但也不一定是这样,所以使用 --all-namespaces
选项列出所有命名空间中正在运行的pod:
$ kubectl get po --all-namespaces
在输出的底部,会看到Ingress控制器pod。该名称暗示Nginx(一种开源HTTP服务器并可以做反向代理)用于提供Ingress功能。
提示 当不知道pod(或其他类型的资源)所在的命名空间,或者是否希望跨所有命名空间列出资源时,利用补充说明中提到的 --all-namespaces
选项非常方便。
5.4.1 创建Ingress资源
已经确认集群中正在运行Ingress控制器,因此现在可以创建一个Ingress资源。下面的代码清单显示了Ingress的示例YAML:
代码清单5.13 Ingress资源的定义:kubia-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: kubia-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: kubia.example.com #ingress 将域名映射到你的服务
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: kubia-nodeport #将所有请求发送到kubia-nodeport服务的80端口
port:
number: 80
定义了一个单一规则的Ingress,确保Ingress控制器收到的所有请求主机http://kubia.example.com的HTTP请求,将被发送到端口80上的kubia-nodeport服务。
每个 HTTP 规则都包含以下信息:
- 可选的
host
。在此示例中,未指定host
,因此该规则适用于通过指定 IP 地址的所有入站 HTTP 通信。 如果提供了host
(例如 foo.bar.com),则rules
适用于该host
。 - 路径列表 paths(例如,
/testpath
),每个路径都有一个由serviceName
和servicePort
定义的关联后端。 在负载均衡器将流量定向到引用的服务之前,主机和路径都必须匹配传入请求的内容。 -
backend
(后端)是 Service 文档中所述的服务和端口名称的组合。 与规则的host
和path
匹配的对 Ingress 的 HTTP(和 HTTPS )请求将发送到列出的backend
。
通常在 Ingress 控制器中会配置 defaultBackend
(默认后端),以服务于任何不符合规约中 path
的请求。
5.4.2 通过Ingress访问服务
要通过 http://kubia.example.com
访问服务,需要确保域名解析为Ingress控制器的IP。
获取Ingress的IP地址
要查找IP,需要列出Ingress:
$ kubectl get ingresses
NAME CLASS HOSTS ADDRESS PORTS AGE
kubia-ingress <none> kubia.example.com 172.17.0.2 80 16m
注意 在云提供商的环境上运行时,地址可能需要一段时间才能显示,因为Ingress控制器在幕后调配负载均衡器。
IP在ADDRESS列中显示出来。
确保在Ingress中配置的Host指向Ingress的IP地址
一旦知道IP地址,通过配置DNS服务器将 kubia.example.com
解析为此IP地址,或者在 /ect/hosts
文件(Windows系统为 C:\windows\system32\drivers\etc\hosts
)中添加下面一行内容:
172.17.0.2 kubia.example.com
通过Ingress访问pod
环境都已经建立完毕,可以通过 http://kubia.example.com
地址访问服务(使用浏览器或者curl命令):
$ curl http://kubia.example.com
现在已经通过Ingress成功访问了该服务,接下来对其展开深层次的研究。
了解Ingress的工作原理
客户端首先对 kubia.example.com
执行DNS查找,DNS服务器(或本地操作系统)返回了Ingress控制器的IP。客户端然后向Ingress控制器发送HTTP请求,并在Host头中指定 kubia.example.com
。控制器从该头部确定客户端尝试访问哪个服务,通过与该服务关联的Endpoint对象查看pod IP,并将客户端的请求转发给其中一个pod。
如你所见,Ingress控制器不会将请求转发给该服务,只用它来选择一个pod。大多数(即使不是全部)控制器都是这样工作的。
[图片上传失败...(image-c5bba7-1626740818033)]
图5.10 通过Ingress访问pod
5.4.3 通过相同的Ingress暴露多个服务
如果仔细查看Ingress规范,则会看到rules和paths都是数组,因此它们可以包含多个条目。一个Ingress可以将多个主机和路径映射到多个服务,我们先来看看paths字段。
将不同的服务映射到相同主机的不同路径
将不同的服务映射到相同主机的不同paths,以下面的代码清单为例。
代码清单5.14 在同一个主机、不同的路径上,Ingress暴露出多个服务
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: kubia-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: kubia.example.com #ingress 将域名映射到你的服务
http:
paths:
- path: /kubia
pathType: Prefix
backend:
service:
name: kubia-nodeport #将 kubia.example.com/kubia 的请求转发到该服务
port:
number: 80
- path: /admin
pathType: Prefix
backend:
service:
name: kubia-nodeport #将 kubia.example.com/admin 的请求转发到该服务
port:
number: 80
在这种情况下,根据请求的URL中的路径,请求将发送到两个不同的服务。因此,客户端可以通过一个IP地址(Ingress控制器的IP地址)访问两种不同的服务。
将不同的服务映射到不同的主机上
同样,可以使用Ingress根据HTTP请求中的主机而不是(仅)路径映射到不同的服务,如下面的代码清单所示。
代码清单5.15 Ingress根据不同的主机(host)暴露出多种服务
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: kubia-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: kubia.example.com #ingress 将域名映射到你的服务
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: kubia-nodeport
port:
number: 80
- host: kubia2.example.com #ingress 将域名映射到你的服务
http:
paths:
- path: /kubia
pathType: Prefix
backend:
service:
name: kubia-nodeport
port:
number: 80
根据请求中的Host头(虚拟主机在网络服务器中处理的方式),控制器收到的请求将被转发到foo服务或bar服务。DNS需要将 kubia.example.com
和 kubia2.example.com
域名都指向Ingress控制器的IP地址。
5.4.4 配置Ingress处理TLS传输
我们已经知道Ingress如何转发HTTP流量。但是HTTPS呢?接下来了解一下如何配置Ingress以支持TLS。
为Ingress创建TLS认证
当客户端创建到Ingress控制器的TLS连接时,控制器将终止TLS连接。客户端和控制器之间的通信是加密的,而控制器和后端pod之间的通信则不是。运行在pod上的应用程序不需要支持TLS。例如,如果pod运行web服务器,则它只能接收HTTP通信,并让Ingress控制器负责处理与TLS相关的所有内容。要使控制器能够这样做,需要将证书和私钥附加到Ingress。这两个必需资源存储在称为Secret的Kubernetes资源中,然后在Ingress manifest中引用它。我们将在第7章中详细介绍Secret。现在,只需创建Secret,而不必太在意。
首先,需要创建私钥和证书:
$ openssl genrsa -out tls.key 2048
$ openssl req -new -x509 -key tls.key -out tls.cert -days 360 -subj "/C=ZH/ST=ShenZhen/L=ShenZhen/O=Global Security/OU=IT Department/CN=kubia.example.com"
像下述两个文件一样创建Secret:
$ kubectl create secret tls my-secret --cert=tls.cert --key=tls.key
通过CertificateSigningRequest资源签署证书
可以不通过自己签署证书,而是通过创建CertificateSigningRequest(CSR)资源来签署。用户或他们的应用程序可以创建一个常规证书请求,将其放入CSR中,然后由人工操作员或自动化程序批准请求,像这样:
$ kubectl certificate approve <name of the CSR>
然后可以从CSR的status.certificate字段中检索签名的证书。
请注意,证书签署者组件必须在集群中运行,否则创建CertificateSigningRequest以及批准或拒绝将不起作用。
私钥和证书现在存储在名为tls的Secret中。现在,可以更新Ingress对象,以便它也接收 kubia.example.com
的HTTPS请求。Ingress现在看起来应该像下面的代码清单。
代码清单5.16 Ingress处理TLS传输:kubia-ingress-tls.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: kubia-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
tls:
- hosts:
- kubia.example.com #接收tls 连接
secretName: my-secret # 从tls 中获得之前创建的私钥和证书
rules:
- host: kubia.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: kubia-nodeport
port:
number: 80
提示 可以调用 kubectl apply -f kubia-ingress-tls.yaml
使用文件中指定的内容来更新Ingress资源,而不是通过删除并从新文件重新创建的方式。
现在可以使用HTTPS通过Ingress访问服务:
$ curl -k -v https://kubia.example.com/kubia
* Trying 172.17.0.2...
* TCP_NODELAY set
* Connected to kubia.example.com (172.17.0.2) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: /etc/pki/tls/certs/ca-bundle.crt
CApath: none
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, [no content] (0):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, [no content] (0):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, [no content] (0):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, [no content] (0):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, [no content] (0):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
* ALPN, server accepted to use h2
* Server certificate:
* subject: O=Acme Co; CN=Kubernetes Ingress Controller Fake Certificate
* start date: Jul 10 05:05:32 2021 GMT
* expire date: Jul 10 05:05:32 2022 GMT
* issuer: O=Acme Co; CN=Kubernetes Ingress Controller Fake Certificate
* SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* TLSv1.3 (OUT), TLS app data, [no content] (0):
* TLSv1.3 (OUT), TLS app data, [no content] (0):
* TLSv1.3 (OUT), TLS app data, [no content] (0):
* Using Stream ID: 1 (easy handle 0x55e5fc9e14c0)
* TLSv1.3 (OUT), TLS app data, [no content] (0):
> GET /kubia HTTP/2
> Host: kubia.example.com
> User-Agent: curl/7.61.1
> Accept: */*
>
* TLSv1.3 (IN), TLS handshake, [no content] (0):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.3 (IN), TLS handshake, [no content] (0):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.3 (IN), TLS app data, [no content] (0):
* Connection state changed (MAX_CONCURRENT_STREAMS == 128)!
* TLSv1.3 (OUT), TLS app data, [no content] (0):
* TLSv1.3 (IN), TLS app data, [no content] (0):
< HTTP/2 200
< date: Sat, 10 Jul 2021 07:44:49 GMT
<
Hey there, this is kubia-7d5b548867-m4bfz. Your IP is ::ffff:172.18.0.7.
* TLSv1.3 (IN), TLS app data, [no content] (0):
* Connection #0 to host kubia.example.com left intact
该命令的输出显示应用程序的响应,以及配置的Ingress的证书服务器的响应。
注意 对Ingress功能的支持因不同的Ingress控制器实现而异,因此请检查特定实现的文档以确定支持的内容。
Ingress是一个相对较新的Kubernetes功能,因此可以预期将来会看到许多改进和新功能。虽然目前仅支持L7(网络第7层)(HTTP /HTTPS)负载平衡,但也计划支持L4(网络第4层)负载平衡。
清理
kubectl delete secret my-secret