【编者的话】原文地址:
here。本文主要介绍了 Kubernetes Ingress 的内部原理,以及一些 Ingress 的用法示例。
索引
本文有两部分:
内容摘要
Kubernetes Ingress 不是一个 Kubernetes Service,简单地说,它只是一个将请求重定向到集群内部服务的 Nginx Pod,只不过这个 Pod 是通过 Kubernetes Service 进行访问,通常使用的是 LoadBalancer 类型的 Service。
本文有必要读吗?
首先,本文将向你提供一个清晰且简单的概述,用以说明神秘的 Kubernetes Ingress 背后到底是什么,这样能够让你更容易理解你正在实施的内容。
然后,本文将向你展示一些使用样例的配置示例。
为什么使用 Ingress?
使用 Ingress 可以将你集群的内部服务对外暴露,它能够帮你节省内部宝贵的静态 IP,因为你不需要声明多个 LoadBalancer 类型的 service。而且,如我们所见,它还能够进行多样化的配置以及更加简易的设置方式。
本文将要讲述什么?
- 首先,我们将简短阐述 HTTP 服务器(尤其是 Nginx)是如何工作以及它们可以做什么。
- 然后,我们将展示在不使用 Kubernetes Ingress 资源的情况下,如何手动搭建 Ingress。
- 最后,我们将看到 Kubernetes Ingress 只是一个预配置的 Nginx 服务器。
听起来有点迷惑?那就继续往下看吧。
简要了解一个简单的 HTTP 服务器的世界
让我们回到容器、Kubernetes以及现代云计算世界之前的那个时代。跟我一起,简单回顾一下。
一个 HTTP 服务器(如 Nginx)能做什么?
如下图所示,它能够接收一个基于 HTTP 协议访问某个特定文件路径的请求,检查文件系统中该文件路径是否存在,如果存在就将其返回。
本例中 Nginx 的配置可能如下:
location /folder {
root /var/www/;
index index.html;
}
一个 HTTP 服务器(如 Nginx)还能做什么?
如下图所示,它能够接收一个访问特定文件路径的请求,然后将这个请求重定向到另一个服务器上(这里它扮演着代理角色)并且将来自于这个服务器的请求响应内容返回给客户端。客户端不需要做什么改变,如果请求的文件存在,它就能收到想要的结果。
我们不会深入解释这个过程,本例中 Nginx 作为一个代理服务器使用的配置可能如下:
location /folder {
proxy_pass http://second-nginx-server:8000;
}
这个配置表示 Nginx 能够扮演一个代理角色,对外提供文件系统服务,重定向请求到其他服务器上并返回对应的响应内容。
一个简单的 Kubernetes 示例
使用 ClusterIP 类型的 service
我们假定现在你已经对 Kubernetes Service 有所了解(可以参考另一篇文章
Kubernetes Services)。如下图所示,我们有两个 worker 节点,这里先忽略 master 节点。我们有两个服务:
service-nginx 和
service-python,它们指向了不同的 pod。Service 不会调度到任何节点上,让我们先简单地认为它们“存在于集群的任何地方”。
目前你可以看到在我们集群内部,我们能够通过 service 访问 Nginx pod 和 Python pod。如果我们想在集群外部也能够访问它们,我们就需要将service 转换为 LoadBalancer 类型。
使用 LoadBalancer 类型的 service
如下图,你可以看到我们将 ClusterIP 类型的 service 转成了 LoadBalancer 类型。我们的 Kubernetes 集群部署在能够支持 LoadBalancer 类型 Service 的云服务提供商上(例如 Gcloud、AWS、DigitalOcean 等),可以看到提供商创建了 2 个外部的负载均衡器,负载均衡器将请求转发到了集群节点的外部 IP 地址,进而转发到了内部服务上。
从上图可以看出,两个负载均衡器都有自己的 IP 地址。如果我们将请求发往地址为 22.33.44.55 的负载均衡器上,请求被转发到集群内的
service-nginx。如果我们将请求发往地址为 77.66.55.44 的负载均衡器上,请求被转发到集群内的
service-python。
这种运行方式很棒!但 IP 地址有限且负载均衡器的价格取决于云服务提供商。设想下我们集群内部不止 2 个服务,而是拥有很多内部服务。如果我们都想构建 LoadBalancer 方式,那成本将迅速上涨。
有更好的解决方案能够让我们只使用一个负载均衡器(只使用一个 IP 地址),仍然可以访问我们内部的服务吗?在继续探索之前,我们先手动实现一种方式(非 Kubernetes 方式)。
手动配置一个 Nginx 代理服务
如之前所述,Nginx 可以作为一个代理服务。如下图所示,集群内有一个新的 service 叫做
service-nginx-proxy,它作为我们集群内唯一的 LoadBalancer 类型 service。在集群内
service-nginx-proxy 也会指向一个或者多个 Nginx pod 终端,不过在下图中我们没有展示出来。其他两个 service 由之前的类型转为了简单的 ClusterIP 类型。
从图中我们能够看到,通过一个 LoadBalancer(11.22.33.44) 访问不同的 http url 返回的请求内容是不同的,因为如图中黄线所示,使用不同 url 访问相同的地址,请求重定向的服务是不同的。
service-nginx-proxy 这个服务使用 Nginx 代理配置了传入路径与重定向目标的关系,依据请求 url 决定了请求应该重定向到哪个服务。
在这个示例中(如上图所示)我们有两个选项:红色线条和蓝色线条。红色线条重定向到了
service-nginx,而蓝色线条重定向到了
service-python。Nginx 配置如下所示:
very simplified Nginx config example
location /folder {
proxy_pass http://service-nginx:3001;
}
location /other {
proxy_pass http://service-python:3002;
}
现在我们需要手动配置
service-nginx-proxy 服务,构建合适的 Nginx 配置文件指向我们 ClusterIP 类型的 service。这是一种能做到的、可行的且通用的解决方案。
基于这种通用的解决方案,Kubernetes Ingress 就是用来让构建配置文件的过程变得更简单且更加可控。现在你应该能理解上述示例的好处了,接下来我们继续讨论 Kubernetes Ingress。
使用 Kubernetes Ingress
对比下图和上述图例,其实没什么区别。我们只是使用了一个事先配置好的 Nginx(Kubernetes Ingress) 服务,不过所有代理配置已经完成,减少了很多手动配置的工作。
以上就是理解 Kubernetes Ingress 的所有内容,接下来我们介绍一些使用示例。
安装 Kubernetes Ingress Controller
Kubernetes Ingress 是 Kubernetes 附加的资源,使用以下命令进行安装:
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.24.1/deploy/mandatory.yaml
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.24.1/deploy/provider/cloud-generic.yaml
使用下面的命令,可以看到 ingress-nginx 命名空间下安装的所有 k8s 资源:
kubectl get svc,pod --namespace=ingress-nginx
如上图所示,可以看到一个具备外部 IP 地址的普通 LoadBalancer service 以及一个附属的 pod。你可以使用
kubectl exec
命令进入 pod 内部,看到它包含了一个事先配置好的 Nginx 服务。
在
nginx.conf
文件中你可以看到不同代理配置以及其他一些相关配置项。
Kubernetes Ingress 配置示例
上述示例使用的 Ingress yaml 文件如下所示:
just example, not tested
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
annotations:
kubernetes.io/ingress.class: nginx
namespace: default
name: test-ingress
spec:
rules:
- http:
paths:
- path: /folder
backend:
serviceName: service-nginx
servicePort: 3001
- http:
paths:
- path: /other
backend:
serviceName: service-python
servicePort: 3002
我们可以通过命令
kubectl create -f ingress.yaml
来构建这个 Ingress。执行完命令之后,yaml 文件就会转变为之前安装的 Ingress Controller 服务中的 Nginx 配置。
不同命令空间下 Kubernetes Ingress 示例
现在如果你的集群有一个内部服务,它和 Ingress 在不同的命令空间,该如何进行重定向呢?因为一个 Ingress 资源是命令空间化的,
在 Ingress 配置中你只能重定向到相同命令空间下的服务。
如果你定义了多个 Ingress yaml 配置,那么所有的配置会集中写入 Ingress Controller 使用的 Nginx 配置文件中。意味着:
所有配置都使用相同的 LoadBalancer IP地址。
因此,为不同命名空间下的内部服务创建不同命令空间下的 Ingress 资源即可。例如
service-nginx 在 default 命名空间下:
just example, not tested
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
annotations:
kubernetes.io/ingress.class: nginx
**namespace: default**
name: ingress1
spec:
rules:
- http:
paths:
- path: /folder
backend:
serviceName: service-nginx
servicePort: 3001
而
service-python 在 namespace2 命令空间下:
just example, not tested
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
annotations:
kubernetes.io/ingress.class: nginx
namespace: namespace2
name: ingress2
spec:
rules:
- http:
paths:
- path: /other
backend:
serviceName: service-python
servicePort: 3002
如何微调 Ingress Nginx 的配置?
你可以通过 Kubernetes Ingress 资源定义中的 annotations 属性来实现。如下所示,你可以配置不同内容就像直接在配置 Nginx 一样。
kind: Ingress
metadata:
name: ingress
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/proxy-connect-timeout: '30'
nginx.ingress.kubernetes.io/proxy-send-timeout: '500'
nginx.ingress.kubernetes.io/proxy-read-timeout: '500'
nginx.ingress.kubernetes.io/send-timeout: "500"
nginx.ingress.kubernetes.io/enable-cors: "true"
nginx.ingress.kubernetes.io/cors-allow-methods: "*"
nginx.ingress.kubernetes.io/cors-allow-origin: "*"
...
你甚至可以做一个特殊规则配置:
nginx.ingress.kubernetes.io/configuration-snippet: |
if ($host = 'www.wuestkamp.com' ) {
rewrite ^ https://wuestkamp.com$request_uri permanent;
}
这些 annotations 设置会被转换为 Nginx 配置。你可以通过手动连接(kubectl exec)到 ingress Nginx pod 内检查下这些配置项。
还有一些不同的配置示例可以参考:
https://github.com/kubernetes/ ... ation
https://github.com/kubernetes/ ... y-waf
检查 Ingress 或者 Nginx 的日志
找出问题或者错误可以通过查看 Ingress 的日志,如下所示:
kubectl logs -n ingress-nginx ingress-nginx-controller-6cfd5b6544-k2r4n
使用 curl 命令测试你的配置
如果你想测试 Ingress 或者 Nginx 的配置规则是否生效,使用
curl -v yourhost.com
是一个不错的选择,用它来取代浏览器访问,避免缓存问题。
重定向或者 Ingress Rules的不同设置方式
在本文中介绍的示例都是通过路径来重定向到不同的服务,例如
/folder
或者
/other/directory
。这是所谓的“路径列表”。
另一种方式是可以根据请求的来源地址(例如
api.myurl.com
和
website.myurl.com
) 区分不同请求,进而重定向到不同的内部 ClusterIP service 上。配置如下所示:
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: simple-fanout-example
spec:
rules:
- host: api.myurl.com
http:
paths:
- path: /foo
backend:
serviceName: service1
servicePort: 4200
- path: /bar
backend:
serviceName: service2
servicePort: 8080
- host: website.myurl.com
http:
paths:
- path: /
backend:
serviceName: service3
servicePort: 3333
从上可以看出,针对特定的地址(host)以及不同的 http 路径可以重定向到不同的内部服务。
SSL 或者 HTTPS 的配置方式
SSL,你应该听说过吧?你可能想让你的 web 服务通过 https 安全访问。Kubernetes Ingress 提供了更加简单的“TLS Termination”机制,也就是说它能够处理所有 SSL 通信,解密或者终止 SSL 请求并将解密后的内容发往集群内部服务。
如果你集群内多个内部服务使用的是相同的 SSL 证书,这种机制是很适用的,因为你只需要在你的 Ingress 上配置一次即可,而不需要修改所有内部服务。Ingress 能通过一个配置好的 TLS Kubernetes Secret 来使用 SSL 证书,如下所示:
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: tls-example-ingress
spec:
tls:
- hosts:
- sslexample.foo.com
secretName: testsecret-tls
rules:
- host: sslexample.foo.com
http:
paths:
- path: /
backend:
serviceName: service1
servicePort: 80
需要注意的是,如果在不同的命令空间中有多个不同的 Ingress 资源,你要确保你的 TLS secret 同样能够在这些命名空间中被使用。
总结
本文主要是想为你提供有关神秘的 Kubernetes Ingress 背后实现原理的概述。简单来说,它无非就是一种轻松配置 Nginx 服务器的方法,该服务器会将请求重定向到集群内的其他内部服务。
通过 Ingress,你可以节省宝贵的静态 IP 地址以及 LoadBalancer 资源。但是,你不应该将 Kubernetes Ingress 视为 Kubernetes 的一种 Service 类型。Ingress 本身不是 Kubernetes 的一种 Service 类型,它只是使用了一个 Service,主要是 LoadBalancer。
需要注意的是,还有其他 Kubernetes Ingress 类型并非基于 Nginx 服务实现,不过它们只是使用了不同的代理技术。