关于 Kubernetes 系列其他文章的传送门
-
《 k8s 中的 7 层调度 – Ingress 》 您当前所在位置

一、Ingress 是什么?¶
在使用 Service 时,无论是 iptables 还是 ipvs 都是四层调度,这就代表它们都无法作为 ssl 会话卸载器使用,这就要引入另一个资源来使用 7 层的调度。
Ingress 可以想象成是一种七层的路由规则,对于 http 和 https 的流量,能够把来自客户端的对某个 URL 的访问,转换为对内部某个特定 Pod 的访问。
Ingress 能够让你用一种 通用配置格式 来配置规则,这个语法不是 Nginx 格式,也不是 Apache 格式。但是写好通用配置后,它能够自动转为 Nginx 或 Apache 等程序的配置格式。所以你只需要告诉它,需要把哪个 Url 代理到哪个 Url 就完了,至于怎么转换,那是自动进行的。
负责 入站,在 7 层把外部的访问代理到 Pod 中。
Ingress Controller 是什么?¶
Ingress 只是一种规则,而这个规则需要一个实实在在存在的进程,这个进程能够理解 Ingress 定义的配置信息(规则),并将 Ingress 规则转为 Nginx、HAProxy 等程序能够识别的配置文件,这就是 Ingress Controller。
注意:
- Ingress Controller 并不是所谓的控制器,它只是一个 Pod,被称为 Ingress Controller 而已。
- 这个 Pod 的作用是专门用来确保 Ingress 中定义的配置,能够实时加载并生效的。他与后端 Pod 是一对多的,后边可能有无数个 Pod。
- 这个控制器没有被打包进 Controller-manager 中统一管理,它既可以运行为一个系统级的守护进程,也可以运行在 Pod 中。但是因为它很复杂,还经常需要变动,为了便于配置,所以不得不将它运行为 Pod。
- Ingress Controller 也是 Pod,它也应该有自己的 Pod Controller,或许使用 DeployMent 类型来维持多个副本,或许使用 DaemonSet 类型,在每个节点上都运行一份。
综上所述,如果要在 k8s 集群中使用 ssl 会话卸载器之类的 7 层代理功能,就需要使用以下两个组件,而不是用 Service。
- Ingress Controller:用来把客户端的访问代理到其他的 Pod 上;
- Ingress:用来定义代理规则。
但是 Ingress 并不支持使用标签选择器来挑选后端的 Pod,所以当后端有多个 Pod 时,它需要由 Service 辅助完成此类功能。在这里 Service 还有另一个功能,就是当 Pod 发生变化时,比如数量增加了一个,也需要通过 Service 来 帮助 Ingress 感知的。
- 注意:这时 Service 仅仅为 Ingress 提供标签选择器的功能,并不代理网络流量,它不是 Ingress 真正的后端,客户端的访问不会到达 Service,而是从 Ingress 直接到达 Pod。

二、Ingress 在生产环境中的使用方法¶
方案一¶
这是标准方法之一。
假设目前有多个后端 Pod 需要 7 层的代理服务,这时启动一个 Ingress Controller 来代理对后端 Pod 的访问。
这个 Ingress Controller 也是一个 Pod,为这个 Pod 添加一个 NodePort 类型的 Service,这样任何一台 Node 都可以作为引入外部流量的入口。
但是缺点是 NodePort 一般情况下使用的不是标准端口,于是就需要在节点外再加一个代理服务器,来将非标准端口转换成 80、443 之类的标准端口。
大致流程如下:
客户端->代理节点(监听 80 标准端口)->Node 节点->Service(Nodeport 监听着非标准端口)->Ingress Controller 的 Pod->后端 Pod
这个流程太繁琐了,一点都不让人省心,所以为了简化这个过程,就有了下面的方案二
方案二¶
在集群中准备几个特殊的 node,这几个 node 不仅有私网地址,还有公网地址。
然后为这几个节点打上污点或使用标签(ds 支持节点标签选择器),通过 ds 控制器让 Ingress Controller 的 Pod 只在这几个有公网地址的节点上运行,并为 Pod 添加一个 hostPort 类型的 Service,直接映射到物理节点的某个端口。
如果为这几个 Node 节点预留了 80 或 443 这样的标准端口,这样当客户端访问这几个特殊节点中某个节点上的某个公网端口时,就等于访问当前节点中的 Ingress Controller 的端口,而后 Ingress Controller 就可以把访问代理至后端的 Pod 上了。
大致流程如下:
客户端->Node 节点->Service(hostPort)->Ingress Controller 的 Pod->后端 Pod
方案三¶
还有一个更简化的方案。
在方案二的基础上,不使用 hostPort,而是让 Pod 共享节点的网络名称空间(hostNetwork)。
实现了无需代理,一次直达 Pod。
大致流程如下:
客户端->Node 节点(hostNetwork)==访问 Ingress Controller 的 Pod->后端 Pod
Ingress 版 Nginx¶
现在有一个问题摆在眼前,现有的 Nginx、Haproxy 等都不是云原生的程序,所以并不知道如何从 API Server 中读取 Ingress 的规则,也无法看懂 Ingress 中定义的规则。这可怎么办呢?
好在 k8s 官方把 Nginx 改造,变成一个自带 Ingress Controller 功能的 Nginx。所以要在 k8s 中使用的话,需要使用经过改造的 Nginx。
k8s 官方经过改造的 Ingress-Nginx,项目地址。
不过,虽然经过改造,但是他们本身并非云原生应用,不太适合这种动态的规则不断变化的场景。因此产生了很多专门用来运行在 k8s 上云原生的 Ingress Controller,比如 traefik、Kong、voyager、contour 等,对比如图:

更多详细的比较信息可以看这篇:
Kubernetes 的 Ingress 控制器比较
不过对于中小企业,Ingress-nginx 也够用了,这里介绍部署方式。
Ingress-Nginx 的部署示例¶
在 Ingress-nginx 项目的 deploy/static/ 目录下有 4 个部署的配置文件,其中 mandatory.yaml 是这 4 个配置文件的集合,使用此配置文件可直接在集群的 master 节点上进行部署。
裸格式的地址为:https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/mandatory.yaml
直接使用在线文件,创建 Ingress-Nginx:
1 | kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/mandatory.yaml |
创建好后,所有的资源都在 ingress-nginx 这个名称空间下:
1 | kubectl get pods -n ingress-nginx |
至此这个自带 Ingress Controller 功能的 Nginx 就安装好了,但是默认情况下,这只是一个普通的 Deployment 类型的 Pod,没有对外开放任何访问入口。
如果想让 k8s 之外的客户端访问这个 Ingress Controller,接下来就需要为其添加一个 Service,可以用的三个网络模型有:
- NodePort
- hostPort
- hostNetwork
Service 配置示例:方案一,使用 NodePort¶
这里演示方案一,使用 NodePort 网络模型,这也是最 Low 的哪种 😜
网络逻辑图¶

这里演示的映射关系很复杂,可以简化一点的方法就是让 Ingress-nginx 的 Service 直接使用节点的网络名称空间(hostNetwork),也就是方案三中描述的那样。不过这里就不演示方案三了,因为只要理解方案一、二,方案三理解起来也不是问题了。
为 Ingress-Nginx 创建 Service¶
先查一下刚才部署的 ingress-nginx 的 Pod 标签,用于给 Service 连接到此 Pod 时使用:
1 | kubectl get pods -n ingress-nginx --show-labels |
创建 Service 配置文件:vim ingress-nginx-svc.yaml
1 | apiVersion: v1 |
创建 Service:
1 | kubectl apply -f ingress-nginx-svc.yaml |
查看刚才创建的 svc,可以看到映射的端口了,这里随机到了 31315 和 32329
1 | root@master [12:49:54 PM] [~] |
不过随机的端口号很不好记,如果不冲突,可以指定让 NodePort 使用一个好记的端口号:
- 比如
30080和30443,方法是把配置文件改成下面这样就行了,不过这里就先不改了:
1 | ... |
因为使用的是 Nodeport,所以可以通过任意一台 Node 节点的 IP,加 31315 或者 32329 端口来访问它。只不过目前这个 Ingress-nginx 还没有网页文件,会提示 404,但是如果能看到 Nginx 的信息即可。
至此这个自带 Ingress Controller 功能的 Nginx 就部署好了。
不过就如方案一中说的那样,有一个缺点,因为自己没有使用通用端口,所以还需要在集群外额外部署负载均衡器来代理监听 80 和 443 端口,这里就不做演示了。
开头说过,Ingress Controller 的功能只是把通用规则转换为 Nginx 等程序能够识别的配置文件,但是目前都还没有定义任何 Ingress 规则,所以接下来需要配置 Ingress 规则。
定义 Ingress 规则¶
Ingress 规则本身也是 k8s 中的一种资源,它的 API 版本是 extensions/v1beta1。
注意:一个集群中可能运行着不止一个 Ingress Controller,比如同时部署了 Ingress-Nginx 和 Ingress-HAProxy,那么该由哪个来转换规则呢?
Ingress 支持在 annotations 中定义一些规则,那么在这里就可以定义由谁来转换的规则:
kubernetes.io/ingress.class: "nginx":让 Ingress-Nginx 来转换;kubernetes.io/ingress.class: "haproxy":让 Ingress-HAProxy 来转换。
Ingress-Nginx 其实还支持很多转换规则,可以定义的很细节,在这里可以查看全部:
定义 Ingress 通用规则,下面结合示例,也顺便讲下 Ingress 的语法:
1 | ~]# vim myapp-ingress.yaml |
根据配置文件创建 Ingress 规则:
1 | # kubectl apply -f myapp-ingress.yaml |
定义好 Ingress 规则后,去 ingress-nginx 验证一下是否已经更新到配置:
1 | 先查看 ingress-nginx 的 Pod 名是什么: |
创建后端的 Pod¶
这两个是后端的业务 Pod,由前边的 Ingress-Nginx 来反向代理。
后端 Pod 的配置文件:backend-pod-example.yaml
1 | # 定义后端 Pod |
创建并查看:
1 | # kubectl apply -f backend-pod-example.yaml |
验证¶
结合拓扑图来看下:

接下来在外网访问 Ingress-Nginx 的 Service 地址,也就是 Node 节点 IP + 30315 或 32329 端口。
- 这个 Service 是之前使用 NodePort 方式创建,并绑定在 Ingress-Nginx 上,使用
kubectl get svc -n ingress-nginx -o wide可以看到。
注意:因为在 Ingress 规则中显式定义了 - host: www.monster.com,这代表使用基于域名的虚拟主机,所以不要通过 IP 访问,否则会调度到默认的虚拟主机上去
1 | 我这里通过内网 DNS 将域名解析到了 Node 节点 |
而且当前副本数是 2,访问时应该有负载均衡效果:
1 | # curl master.monster.com:30315/hostname.html |
Service 配置示例:方案二,使用 hostPort¶
在生产环境中,其实方案二是比较妥当的方法,因为预留了 80、443 等端口,所以前端不需要部署负载均衡器来转换端口了。
先把 nginx-ingress 的配置文件下载到本地:
1 | wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/mandatory.yaml |
进行一些修改:~]# vim mandatory.yaml
1 | #- 在 name: nginx-ingress-controller 配置内,最少应该更改的地方有 3 处 |
之后再创建 Ingress 规则、后端 Pod 等操作就好了,这里就不赘述了。
三、其他 Ingress 使用技巧¶
SSL 会话卸载器¶
创建证书:
1 | openssl genrsa -out myapp.key 2048 |
导入证书到 k8s:
1 | kubectl create secret tls ssltest-cert --cert=myapp.crt --key=myapp.key |
创建一个新的 Ingress 规则,并指明使用 tls:
1 | ~]# vim myapp-tls-ingress.yaml |
查看刚才创建的:
1 | # kubectl get ingress |
访问 https 连接:https://ssltest.monster.com:32329,就会被代理到 myapp 的 80 端口。
访问流程:
_ https://ssltest.monster.com:32329 (NodePort) -> Ingress-Nginx 的 Service :443 -> 由 Ingress-Nginx Pod 代理至 -> myapp :80
使用 Ingress 结合 Tomcat 配置示例¶
创建一个 tomcat 的后端 Pod¶
1 | # 创建一个新的 ns |
开始创建:
1 | # kubectl apply -f tomcat.yaml |
查看:
1 | # kubectl get all -n tomcat |
创建 Ingress 规则¶
创建配置文件:
1 | -> # vim tomcat-ingress.yaml |
创建 Ingress 规则,并查看应该有如下信息:
1 | # kubectl apply -f tomcat-ingress.yaml |
之后在外部网络访问即可:
1 | curl https://master.monster.com:32329/tomcat |