你好,我是Chrono。
课程已经完结三个多月了,还记得结课时我说的那句话吗:“是终点,更是起点”,课程的完结绝不意味着我们终止对Kubernetes的钻研,相反,不论你我,都会在这个学习的道路上持续地走下去。
当初开课时,我计划了很多内容,不过Kubernetes的领域实在太广,加上我日常工作比较忙,时间和精力有限,所以一些原定的知识点没有来得及展现,比较可惜,我一直想找机会做个补偿。
这几天开发任务略微空闲了些,我就又回到了专栏,准备使用另一个流行的工具:Kong Ingress Controller,再来讲讲对Kubernetes集群管理非常重要的Ingress。
我们先快速回顾一下Ingress的知识([第21讲])。
Ingress类似Service,基于HTTP/HTTPS协议,是七层负载均衡规则的集合,但它自身没有管理能力,必须要借助Ingress Controller才能控制Kubernetes集群的进出口流量。
所以,基于Ingress的定义,就出现了各式各样的Ingress Controller实现。
我们已经见过了Nginx官方开发的Nginx Ingress Controller,但它局限于Nginx自身的能力,Ingress、Service等对象更新时必须要修改静态的配置文件,再重启进程(reload),在变动频繁的微服务系统里就会引发一些问题。
而今天要说的Kong Ingress Controller,则是站在了Nginx这个巨人的肩膀之上,基于OpenResty和内嵌的LuaJIT环境,实现了完全动态的路由变更,消除了reload的成本,运行更加平稳,而且还有很多额外的增强功能,非常适合那些对Kubernetes集群流量有更高、更细致管理需求的用户(图片来源Kong官网)。
接下来我们就来看看如何在Kubernetes集群里引入Kong Ingress Controller。
简单起见,这次我选择了minikube环境,版本还是1.25.2,对应的Kubernetes也是之前的1.23.3:
目前Kong Ingress Controller的最新版本是2.7.0,你可以从GitHub上(https://github.com/Kong/kubernetes-ingress-controller)直接获取它的源码包:
wget https://github.com/Kong/kubernetes-ingress-controller/archive/refs/tags/v2.7.0.tar.gz
Kong Ingress Controller安装所需的YAML文件,都存放在解压缩后的“deploy”目录里,提供“有数据库”和“无数据库”两种部署方式,这里我选择了最简单的“无数据库”方式,只需要一个 all-in-one-dbless.yaml
就可以完成部署工作,也就是执行这条 kubectl apply
命令:
kubectl apply -f all-in-one-dbless.yaml
我们可以再对比一下两种 Ingress Controller的安装方式。Nginx Ingress Controller是由多个分散的YAML文件组成的,需要顺序执行多次 kubectl apply
命令,有点麻烦;而Kong Ingress Controller则把Namespace、RBAC、Secret、CRD等对象都合并在了一个文件里,安装很方便,同时也不会发生遗忘创建资源的错误。
安装之后,Kong Ingress Controller会创建一个新的名字空间“kong”,里面有一个默认的Ingress Controller,还有对应的Service,你可以用 kubectl get
来查看:
看这里的截图,你可能会注意到,在 kubectl get pod
输出的“READY”列里显示的是“2/2”,意思是这个Pod里有两个容器。
这也是Kong Ingress Controller与Nginx Ingress Controller在实现架构方面的一个明显不同点。
Kong Ingress Controller,在Pod里使用两个容器,分别运行管理进程Controller和代理进程Proxy,两个容器之间使用环回地址(Loopback)通信;而Nginx Ingress Controller则是因为要修改静态的Nginx配置文件,所以管理进程和代理进程必须在一个容器里(图片表示Kong架构设计)。
两种方式并没有优劣之分,但Kong Ingress Controller分离的好处是两个容器彼此独立,可以各自升级维护,对运维更友好一点。
Kong Ingress Controller还创建了两个Service对象,其中的“kong-proxy”是转发流量的服务,注意它被定义成了“LoadBalancer”类型,显然是为了在生产环境里对外暴露服务,不过在我们的实验环境(无论是minikube还是kubeadm)中只能使用NodePort的形式,这里可以看到80端口被映射到了节点的32201。
现在让我们尝试访问一下Kong Ingress Controller,IP就用worker节点的地址,如果你和我一样用的是minikube,则可以用 $(minikube ip)
的形式简单获取:
curl $(minikube ip):32201 -i
从curl获取的响应结果可以看到, Kong Ingress Controller 2.7内部使用的Kong版本是3.0.1,因为现在我们没有为它配置任何Ingress资源,所以返回了状态码404,这是正常的。
我们还可以用 kubectl exec
命令进入Pod,查看它的内部信息:
虽然Kong Ingress Controller里有两个容器,但我们不需要特意用 -c
选项指定容器,它会自动进入默认的Proxy容器(另一个Controller容器里因为不包含Shell,也是无法进入查看的)。
安装好了,我们看如何使用。和第21讲里的一样,我们仍然不使用默认的Ingress Controller,而是利用Ingress Class自己创建一个新的实例,这样能够更好地理解掌握Kong Ingress Controller的用法。
首先,定义后端应用,还是用Nginx来模拟,做法和[第20讲]里的差不多,用ConfigMap定义配置文件再加载进Nginx Pod里,然后部署Deployment和Service,比较简单,你也比较熟悉,就不列出YAML 代码了,只看一下运行命令后的截图:
显示我创建了两个Nginx Pod,Service对象的名字是ngx-svc。
接下来是定义Ingress Class,名字是“kong-ink”, “spec.controller”字段的值是Kong Ingress Controller的名字“ingress-controllers.konghq.com/kong”,YAML的格式可以参考[第21讲]:
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
name: kong-ink
spec:
controller: ingress-controllers.konghq.com/kong
然后就是定义Ingress对象了,我们还是可以用 kubectl create
来生成YAML 样板文件,用 --rule
指定路由规则,用 --class
指定Ingress Class:
kubectl create ing kong-ing --rule="kong.test/=ngx-svc:80" --class=kong-ink $out
生成的Ingress对象大概就是下面这样,域名是“kong.test”,流量会转发到后端的ngx-svc服务:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: kong-ing
spec:
ingressClassName: kong-ink
rules:
- host: kong.test
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: ngx-svc
port:
number: 80
最后,我们要从 all-in-one-dbless.yaml
这个文件中分离出Ingress Controller的定义。其实也很简单,只要搜索“Deployment”就可以了,然后把它以及相关的Service代码复制一份,另存成“kic.yml”。
当然了,刚复制的代码和默认的Kong Ingress Controller是完全相同的,所以我们必须要参考帮助文档做一些修改,要点我列在了这里:
kong-ink
,同时用“CONTROLLER_PUBLISH_SERVICE”指定Service的名字 ingress-kong-svc
。改了这些之后,一个新的Kong Ingress Controller就完成了,大概是这样,修改点我也加注释了你可以对照着看:
apiVersion: apps/v1
kind: Deployment
metadata:
name: ingress-kong-dep # 重命名
namespace: kong
spec:
replicas: 1
selector:
matchLabels:
app: ingress-kong-dep # 重命名
template:
metadata:
labels:
app: ingress-kong-dep # 重命名
spec:
containers:
- env: # 第一个容器, Proxy
...
image: kong:3.1 # 改镜像
- env: # 第二个容器,Controller
- name: CONTROLLER_INGRESS_CLASS
value: kong-ink # 改Ingress Class
- name: CONTROLLER_PUBLISH_SERVICE
value: kong/ingress-kong-svc # 改Service
...
---
apiVersion: v1
kind: Service
metadata:
name: ingress-kong-svc # 重命名
namespace: kong
spec:
...
selector:
app: ingress-kong-dep # 重命名
type: NodePort # 改类型
在我们专栏的配套GitHub项目里,你也可以直接找到改好的YAML 文件。- 把这些都准备好,我们就可以来测试验证Kong Ingress Controller了:
kubectl apply -f ngx-deploy.yml
kubectl apply -f ingress.yml
kubectl apply -f kic.yml
这个截图显示了这些对象的创建结果,其中,新Service对象的NodePort端口是32521。
下面我们就来用curl发送HTTP请求,注意,应该用“–resolve”或者“-H”参数指定Ingress里定义的域名“kong.test”,否则Kong Ingress Controller会找不到路由:
curl $(minikube ip):32521 -H 'host: kong.test' -v
你可以看到,Kong Ingress Controller正确应用了Ingress路由规则,返回了后端Nginx应用的响应数据,而且从响应头“Via”里还可以发现,它现在用的是Kong 3.1。
到这里,Kong Ingress Controller的基本用法你就掌握了。
不过,只使用Kubernetes标准的Ingress资源来管理流量,是无法发挥出Kong Ingress Controller的真正实力的,它还有很多非常好用、非常实用的增强功能。
我们在[第27讲]里曾经说过annotation,是Kubernetes为资源对象提供的一个方便扩展功能的手段,所以,使用annotation就可以在不修改Ingress自身定义的前提下,让Kong Ingress Controller更好地利用内部的Kong来管理流量。
目前Kong Ingress Controller支持在Ingress和Service这两个对象上添加annotation,相关的详细文档可以参考官网(https://docs.konghq.com/kubernetes-ingress-controller/2.7.x/references/annotations/),这里我只介绍两个annotation。
第一个是“konghq.com/host-aliases”,它可以为Ingress规则添加额外的域名。
你应该知道吧,Ingress的域名里可以使用通配符 *
,比如 *.abc.com
,但问题在于 *
只能是前缀,不能是后缀,也就是说我们无法写出 abc.*
这样的域名,这在管理多个域名的时候就显得有点麻烦。
有了“konghq.com/host-aliases”,我们就可以用它来“绕过”这个限制,让Ingress轻松匹配有不同后缀的域名。
比如,我修改一下Ingress定义,在“metadata”里添加一个annotation,让它除了“kong.test”,还能够支持“kong.dev”“kong.ops”等域名,就是这样:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: kong-ing
annotations:
konghq.com/host-aliases: "kong.dev, kong.ops" #注意这里
spec:
...
使用 kubectl apply
更新Ingress,再用curl来测试一下:
你就会发现Ingress已经支持了这几个新域名。
第二个是“konghq.com/plugins”,它可以启用Kong Ingress Controller内置的各种插件(Plugins)。
插件,是Kong Ingress Controller的一个特色功能,你可以理解成是“预制工件”,能够附加在流量转发的过程中,实现各种数据处理,并且这个插件机制是开放的,我们既可以使用官方插件,也可以使用第三方插件,还可以使用Lua、Go等语言编写符合自己特定需求的插件。
Kong公司维护了一个经过认证的插件中心(https://docs.konghq.com/hub/),你可以在这里找到涉及认证、安全、流控、分析、日志等多个领域大约100多个插件,今天我们看两个常用的 Response Transformer、Rate Limiting。
Response Transformer插件实现了对响应数据的修改,能够添加、替换、删除响应头或者响应体;Rate Limiting插件就是限速,能够以时分秒等单位任意限制客户端访问的次数。
定义插件需要使用CRD资源,名字是“KongPlugin”,你也可以用kubectl api-resources
、kubectl explain
等命令来查看它的apiVersion、kind等信息:
下面我就给出这两个插件对象的示例定义:
apiVersion: configuration.konghq.com/v1
kind: KongPlugin
metadata:
name: kong-add-resp-header-plugin
plugin: response-transformer
config:
add:
headers:
- Resp-New-Header:kong-kic
---
apiVersion: configuration.konghq.com/v1
kind: KongPlugin
metadata:
name: kong-rate-limiting-plugin
plugin: rate-limiting
config:
minute: 2
KongPlugin对象,因为是自定义资源,所以和标准Kubernetes对象不一样,不使用“spec”字段,而是用“plugin”来指定插件名,用“config”来指定插件的配置参数。
比如在这里,我就让Response Transformer插件添加一个新的响应头字段,让Rate Limiting插件限制客户端每分钟只能发两个请求。
定义好这两个插件之后,我们就可以在Ingress对象里用annotations来启用插件功能了:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: kong-ing
annotations:
konghq.com/plugins: |
kong-add-resp-header-plugin,
kong-rate-limiting-plugin
现在让我们应用这些插件对象,并且更新Ingress:
kubectl apply -f crd.yml
再发送curl请求:
curl $(minikube ip):32521 -H 'host: kong.test' -i
你就会发现响应头里多出了几个字段,其中的 RateLimit-*
是限速信息,而 Resp-New-Header
就是新加的响应头字段。
把curl连续执行几次,就可以看到限速插件生效了:
Kong Ingress Controller会返回429错误,告诉你访问受限,而且会用“Retry-After”等字段来告诉你多少秒之后才能重新发请求。
好了,今天我们学习了另一种在Kubernetes管理集群进出流量的工具:Kong Ingress Controller,小结一下要点内容:
作为一个CNCF云原生项目,Kong Ingress Controller已经得到了广泛的应用和认可,而且在近年的发展过程中,它也开始支持新的Gateway API,等下次有机会我们再细聊吧。
最后是课下作业时间,给你留两个思考题:
好久不见了,期待看到你的想法,我们一起讨论,留言区见。