Ingress 是 Kubernetes 的一种 API 对象,将集群内部的 Service 通过 HTTP/HTTPS 方式暴露到集群外部,并通过规则定义 HTTP/HTTPS 的路由。Ingress 具备如下特性:集群外部可访问的 URL、负载均衡、SSL Termination、按域名路由。
Background
Kubernetes 暴露服务的有三种方式:分别为 LoadBlancer Service、NodePort Service、Ingress。
LoadBlancer Service 是 Kubernetes 结合云平台的组件,如国外 GCE、AWS、国内阿里云等等,使用它向使用的底层云平台申请创建负载均衡器来实现,有局限性,对于使用云平台的集群比较方便,但是和云平台强绑定。
NodePort Service 是通过在节点上暴露端口,然后通过将端口映射到具体某个服务上来实现服务暴露,比较直观方便,但是对于集群来说,随着 Service 的不断增加,需要的端口越来越多,很容易出现端口冲突,而且不容易管理。此外,打开节点的端口,也会面临安全上的风险,生产环境并不推荐使用。
Ingress 作为 Kubernetes 基本API对象,是外部请求访问集群的入口,负责为进入集群的请求提供路由规则集合和转发,将外部的请求转发到集群内部Service上。相对于上述两种暴露方式,有以下特点:
- 动态配置服务:增加新的服务时,不再需要在流量入口新增反向代理指向新的服务,只需配置好Ingress即可
- 减少不必要端口暴露:只需要将Ingress服务映射出去,即可代理所有后端服务,更加安全,便于管理端口
Ingress
组成模块
Ingress 通过反向代理负载均衡服务器实现对外暴露服务目的,一般有三个组件组成:
- 反向代理负载均衡服务器:拦截外部请求,通常以
Deployment
或者DaemonSet
的方式部署到集群中,常见的有Nginx
、Apache
、Traefik
等。 - Ingress:定义路由规则,将路由配置抽象成一个 Ingress 对象。
- Ingress Controller:实时监控集群,获取
Service
、Pod
和Ingress
等的变化,将 Ingress 的规则动态更新到反向代理负载均衡服务器上,刷新其路由配置信息,实现 服务发现。
语法详解
下面是一个Ingress资源示例:
1 | apiVersion: networking.k8s.io/v1 |
与所有其他 Kubernetes 资源一样,Ingress 需要使用 apiVersion
、kind
和 metadata
字段。 Ingress 对象的命名必须是合法的 DNS 子域名名称。 Ingress 经常使用注解(annotations)来配置一些选项,具体取决于 Ingress 控制器, 不同的 Ingress 控制器 支持不同的注解。
Ingress 规约 提供了配置负载均衡器或者代理服务器所需的所有信息。 最重要的是,其中包含与所有传入请求匹配的规则列表。 Ingress 资源仅支持用于转发 HTTP 流量的规则。
Ingress Rules
每个 HTTP 规则都包含以下信息:
- 可选主机。在此示例第一个规则中,未指定主机,因此该规则适用于通过指定 IP 地址的所有入站 HTTP 通信。 如果提供了主机(例如 foo.bar.com),则规则适用于该主机。
- 路径列表(例如,
/testpath
),每个路径都有一个由serviceName
和servicePort
定义的关联后端。 在负载均衡器将流量定向到引用的服务之前,主机和路径都必须匹配传入请求的内容。 - 后端是 Service 文档中所述的服务和端口名称的组合。 与规则的主机和路径匹配的对 Ingress 的 HTTP(和 HTTPS )请求将发送到列出的后端。
通常在 Ingress 控制器中会配置默认后端,以服务任何不符合规范中路径的请求。
DefaultBackend
没有规则的 Ingress 将所有流量发送到同一个默认后端。 默认后端通常是 Ingress 控制器 的配置选项,并且未在 Ingress 资源中指定。
如果主机或路径都没有与 Ingress 对象中的 HTTP 请求匹配,则流量将路由到默认后端。
Path Types
Ingress 中的每个路径都有对应的路径类型。当前支持的路径类型有三种:
ImplementationSpecific
(默认):对于这种类型,匹配取决于 IngressClass。 具体实现可以将其作为单独的pathType
处理或者与Prefix
或Exact
类型作相同处理。Exact
:精确匹配 URL 路径,且对大小写敏感。Prefix
:基于以/
分隔的 URL 路径前缀匹配。匹配对大小写敏感,并且对路径中的元素逐个完成。 路径元素指的是由/
分隔符分隔的路径中的标签列表。 如果每个 p 都是请求路径 p 的元素前缀,则请求与路径 p 匹配。
Traefik实战
Traefik
是一个用Go语言开发的轻量级的Http反向代理和负载均衡器,能够监听后端的变化并自动更新服务配置。它的特点如下:
- 天然拥抱
kubernetes
,直接与集群k8s的Api Server
通信,反应非常迅速,实时感知集群中Ingress
定义的路由规则集合和后端Service
、Pod
的变化,自动热更新Traefik
后端配置,根本不用创建Ingress controller
对象 - 提供了友好的控制面板和监控界面,不仅可以方便地查看
Traefik
根据Ingress
生成的路由配置信息,还可以查看统计的一些性能指标数据 - 支持丰富的
annotations
配置,可配置众多出色的特性,例如:自动熔断、负载均衡策略、黑名单、白名单 - 支持许多后端存储,如:zookeeper、eureka、consul、rancher、docker等,它会自动感知这些统一配置中心的变化,热更新自己的路由配置
如何在 K8S 集群中部署 Traefik Ingress Controller 详细介绍了如何在 k8s 集群部署 Traefik Ingress Controller 的详细过程,本文在此复现其操作。
前期准备
在本文的后半部分,我将演示如何在一个 Kubernetes 集群部署 Traefik 作为 Ingress Controller,同时使用 Ingress 实现按域名路由和流量分配。在此之前,你需要一个已经部署成功的 Kubernetes 集群和一个能够与集群通信的kubectl 工具。
软文时间:你可以在MiniKube创建运行 Kubernetes 集群,也欢迎使用腾讯云的TKE容器服务,快速实现1分钟内自动创建好 Kubernetes 集群。
启用RBAC
为了能够使 Traefik 能够访问集群中运行的 Pod、Endpoint、Ingress和 Service等资源,需要向 Traefik 授予一些权限。这里我们创建了一个具有一组权限的ClusterRole,授予其管理和监视集群中所有命名空间的资源。同时,我们创建一个新的ServiceAccount,为Traefik 提供集群中的身份。最后,通过 ClusterRoleBinding 将二者绑定在一起。
1 | apiVersion: v1 |
在命令行执行以下命令:
1 | [root@VM-1-28-centos traefik]# kubectl apply -f rbac.yaml |
部署Traefik
官方 Traefik 文档支持三种类型的部署:使用 Deployment 对象、使用 DaemonSet 对象或使用 Helm Chart。这里我们使用 Deployment manifest。
1 | apiVersion: extensions/v1beta1 |
在命令行执行以下命令:
1 | [root@VM-1-28-centos traefik]# kubectl apply -f deployment.yaml |
为外部访问创建NodePorts
接下来创建一个服务来从集群外部访问 Traefik,这里我们只暴露Traefik给外部,其他的内部服务都可以通过定义Ingress规则来通过 Traefik 暴露出来,极大避免了 NodePort的冲突。在实际生产环境中,这里的 NodePort 可以替换成云服务商提供的LB。
1 | kind: Service |
在命令行执行以下命令:
1 | [root@VM-1-28-centos traefik]# kubectl create -f service.yaml |
这个时候,已经可以在浏览器访问 Traefik 服务了,在浏览器输入 http://<NodeIP>:<AdminNodePort>
就可以看到 Traefik 的 WebUI了。
这个时候WebUI还没有管理任何Ingress,接下来我们创建Ingress。
实现按域名的路由
1 | apiVersion: extensions/v1beta1 |
在命令行执行命令:
1 | [root@VM-1-28-centos traefik]# kubectl create -f animals-ingress.yaml |
这个时候只创建了 Ingress,还没有创建 Frontend 的 Service 和后端服务的 EndPoint,查看Traefik看到
创建对应的 Service:
1 |
|
在命令行执行命令:
1 | [root@VM-1-28-centos traefik]# kubectl create -f animals-service.yaml |
这个时候 Ingress 中 Frontend 对应的 Service已经正常工作,但是 Backend 的EndPoint还没有就绪:
修改本机Host,添加记录如下:
1 | <NodeIP> hare.houmin moose.houmin bear.houmin |
这个时候在浏览器访问 http://hare.houmin:<WebNodePort>
,在这里 WebNodePort
也就对应于 30993,显示 Service Unavailable
。符合预期,因为Backend的Endpoint还没有起来。
创建对应的 Deployment:
1 |
|
在命令行中执行命令:
1 | [root@VM-1-28-centos traefik]# kubectl apply -f animals-deployment.yaml |
这个是否分别访问Bear、Hare和Moose服务:
Ingress Class
Ingress 资源的设计初衷就是易用性,尝试使用简单的字段为所有应用提供支持。随着应用场景的不断增加,为了适应更广泛的需求,越来越多的 Ingress 控制器要靠大量的自定义注解来完成更复杂的配置。IngressClass
资源提供了一种替换部分注解的思路。
每个 IngressClass
中都指明了用于实现 Ingress 的控制器类型,并且可以引用自定义资源来使用更多参数。
1 | apiVersion: networking.k8s.io/v1 |
Ingress 规范中加入了 ingressClassName
字段,用来指定实现这个 Ingress 资源的的 IngressClass
。
1 | apiVersion: networking.k8s.io/v1 |
在 1.18 加入 IngressClass
之前,需要在 Ingess 资源中使用 kubernetes.io/ingress.class
注解来指定 Ingress 控制器。在没有官方定义的情况下,这个注解被大量的 Ingress 控制器所支持。现在是时候淘汰他了。
可以使用 ingressclass.kubernetes.io/is-default-class
注解,将其设置为 True
,就代表所在的 IngressClass
为缺省控制器。没有显示指定 IngressClassName
的新的 Ingress 资源都会使用该控制器。