learn-tech/专栏/Kubernetes入门实战课/21Ingress:集群进出流量的总管.md
2024-10-16 06:37:41 +08:00

17 KiB
Raw Blame History

                        因收到Google相关通知网站将会择期关闭。相关通知内容
                        
                        
                        21 Ingress集群进出流量的总管
                        你好我是Chrono。

上次课里我们学习了Service对象它是Kubernetes内置的负载均衡机制使用静态IP地址代理动态变化的Pod支持域名访问和服务发现是微服务架构必需的基础设施。

Service很有用但也只能说是“基础设施”它对网络流量的管理方案还是太简单离复杂的现代应用架构需求还有很大的差距所以Kubernetes就在Service之上又提出了一个新的概念Ingress。

比起ServiceIngress更接近实际业务对它的开发、应用和讨论也是社区里最火爆的今天我们就来看看Ingress还有与它关联的Ingress Controller、Ingress Class等对象。

为什么要有Ingress

通过上次课程的讲解我们知道了Service的功能和运行机制它本质上就是一个由kube-proxy控制的四层负载均衡在TCP/IP协议栈上转发流量Service工作原理示意图

但在四层上的负载均衡功能还是太有限了只能够依据IP地址和端口号做一些简单的判断和组合而我们现在的绝大多数应用都是跑在七层的HTTP/HTTPS协议上的有更多的高级路由条件比如主机名、URI、请求头、证书等等而这些在TCP/IP网络栈里是根本看不见的。

Service还有一个缺点它比较适合代理集群内部的服务。如果想要把服务暴露到集群外部就只能使用NodePort或者LoadBalancer这两种方式而它们都缺乏足够的灵活性难以管控这就导致了一种很无奈的局面我们的服务空有一身本领却没有合适的机会走出去大展拳脚。

该怎么解决这个问题呢?

Kubernetes还是沿用了Service的思路既然Service是四层的负载均衡那么我再引入一个新的API对象在七层上做负载均衡是不是就可以了呢

不过除了七层负载均衡,这个对象还应该承担更多的职责,也就是作为流量的总入口,统管集群的进出口数据,“扇入”“扇出”流量(也就是我们常说的“南北向”),让外部用户能够安全、顺畅、便捷地访问内部服务(图片来源):

所以这个API对象就顺理成章地被命名为 Ingress意思就是集群内外边界上的入口。

为什么要有Ingress Controller

再对比一下Service我们就能更透彻地理解Ingress。

Ingress可以说是在七层上另一种形式的Service它同样会代理一些后端的Pod也有一些路由规则来定义流量应该如何分配、转发只不过这些规则都使用的是HTTP/HTTPS协议。

你应该知道Service本身是没有服务能力的它只是一些iptables规则真正配置、应用这些规则的实际上是节点里的kube-proxy组件。如果没有kube-proxyService定义得再完善也没有用。

同样的Ingress也只是一些HTTP路由规则的集合相当于一份静态的描述文件真正要把这些规则在集群里实施运行还需要有另外一个东西这就是 Ingress Controller它的作用就相当于Service的kube-proxy能够读取、应用Ingress规则处理、调度流量。

按理来说Kubernetes应该把Ingress Controller内置实现作为基础设施的一部分就像kube-proxy一样。

不过Ingress Controller要做的事情太多与上层业务联系太密切所以Kubernetes把Ingress Controller的实现交给了社区任何人都可以开发Ingress Controller只要遵守Ingress规则就好。

这就造成了Ingress Controller“百花齐放”的盛况。

由于Ingress Controller把守了集群流量的关键入口掌握了它就拥有了控制集群应用的“话语权”所以众多公司纷纷入场精心打造自己的Ingress Controller意图在Kubernetes流量进出管理这个领域占有一席之地。

这些实现中最著名的就是老牌的反向代理和负载均衡软件Nginx了。从Ingress Controller的描述上我们也可以看到HTTP层面的流量管理、安全控制等功能其实就是经典的反向代理而Nginx则是其中稳定性最好、性能最高的产品所以它也理所当然成为了Kubernetes里应用得最广泛的Ingress Controller。

不过因为Nginx是开源的谁都可以基于源码做二次开发所以它又有很多的变种比如社区的Kubernetes Ingress Controllerhttps://github.com/kubernetes/ingress-nginx、Nginx公司自己的Nginx Ingress Controllerhttps://github.com/nginxinc/kubernetes-ingress、还有基于OpenResty的Kong Ingress Controllerhttps://github.com/Kong/kubernetes-ingress-controller等等

根据Docker Hub上的统计Nginx公司的开发实现是下载量最多的Ingress Controller所以我将以它为例讲解Ingress和Ingress Controller的用法。

下面的这张图就来自Nginx官网比较清楚地展示了Ingress Controller在Kubernetes集群里的地位

为什么要有IngressClass

那么到现在有了Ingress和Ingress Controller我们是不是就可以完美地管理集群的进出流量了呢

最初Kubernetes也是这么想的一个集群里有一个Ingress Controller再给它配上许多不同的Ingress规则应该就可以解决请求的路由和分发问题了。

但随着Ingress在实践中的大量应用很多用户发现这种用法会带来一些问题比如

由于某些原因项目组需要引入不同的Ingress Controller但Kubernetes不允许这样做 Ingress规则太多都交给一个Ingress Controller处理会让它不堪重负 多个Ingress对象没有很好的逻辑分组方式管理和维护成本很高 集群里有不同的租户他们对Ingress的需求差异很大甚至有冲突无法部署在同一个Ingress Controller上。

所以Kubernetes就又提出了一个 Ingress Class 的概念让它插在Ingress和Ingress Controller中间作为流量规则和控制器的协调人解除了Ingress和Ingress Controller的强绑定关系。

现在Kubernetes用户可以转向管理Ingress Class用它来定义不同的业务逻辑分组简化Ingress规则的复杂度。比如说我们可以用Class A处理博客流量、Class B处理短视频流量、Class C处理购物流量。

这些Ingress和Ingress Controller彼此独立不会发生冲突所以上面的那些问题也就随着Ingress Class的引入迎刃而解了。

如何使用YAML描述Ingress/Ingress Class

我们花了比较多的篇幅学习Ingress、 Ingress Controller、Ingress Class这三个对象全是理论你可能觉得学得有点累。但这也是没办法的事情毕竟现实的业务就是这么复杂而且这个设计架构也是社区经过长期讨论后达成的一致结论是我们目前能获得的最佳解决方案。

了解了这三个概念之后我们就可以来看看如何为它们编写YAML描述文件了。

和之前学习Deployment、Service对象一样首先应当用命令 kubectl api-resources 查看它们的基本信息,输出列在这里了:

kubectl api-resources

NAME SHORTNAMES APIVERSION NAMESPACED KIND ingresses ing networking.k8s.io/v1 true Ingress ingressclasses networking.k8s.io/v1 false IngressClass

你可以看到Ingress和Ingress Class的apiVersion都是“networking.k8s.io/v1”而且Ingress有一个简写“ing”但Ingress Controller怎么找不到呢

这是因为Ingress Controller和其他两个对象不太一样它不只是描述文件是一个要实际干活、处理流量的应用程序而应用程序在Kubernetes里早就有对象来管理了那就是Deployment和DaemonSet所以我们只需要再学习Ingress和Ingress Class的的用法就可以了。

先看Ingress。

Ingress也是可以使用 kubectl create 来创建样板文件的和Service类似它也需要用两个附加参数

--class指定Ingress从属的Ingress Class对象。 --rule指定路由规则基本形式是“URI=Service”也就是说是访问HTTP路径就转发到对应的Service对象再由Service对象转发给后端的Pod。

现在我们就执行命令看看Ingress到底长什么样

export out="--dry-run=client -o yaml" kubectl create ing ngx-ing --rule="ngx.test/=ngx-svc:80" --class=ngx-ink $out

apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: ngx-ing

spec:

ingressClassName: ngx-ink

rules:

  • host: ngx.test http: paths:
    • path: / pathType: Exact backend: service: name: ngx-svc port: number: 80

在这份Ingress的YAML里有两个关键字段“ingressClassName”和“rules”分别对应了命令行参数含义还是比较好理解的。

只是“rules”的格式比较复杂嵌套层次很深。不过仔细点看就会发现它是把路由规则拆散了有host和http path在path里又指定了路径的匹配方式可以是精确匹配Exact或者是前缀匹配Prefix再用backend来指定转发的目标Service对象。

不过我个人觉得Ingress YAML里的描述还不如 kubectl create 命令行里的 --rule 参数来得直观易懂而且YAML里的字段太多也很容易弄错建议你还是让kubectl来自动生成规则然后再略作修改比较好。

有了Ingress对象那么与它关联的Ingress Class是什么样的呢

其实Ingress Class本身并没有什么实际的功能只是起到联系Ingress和Ingress Controller的作用所以它的定义非常简单在“spec”里只有一个必需的字段“controller”表示要使用哪个Ingress Controller具体的名字就要看实现文档了。

比如如果我要用Nginx开发的Ingress Controller那么就要用名字“nginx.org/ingress-controller”

apiVersion: networking.k8s.io/v1 kind: IngressClass metadata: name: ngx-ink

spec: controller: nginx.org/ingress-controller

Ingress和Service、Ingress Class的关系我也画成了一张图方便你参考

如何在Kubernetes里使用Ingress/Ingress Class

因为Ingress Class很小所以我把它与Ingress合成了一个YAML文件让我们用 kubectl apply 创建这两个对象:

kubectl apply -f ingress.yml

然后我们用 kubectl get 来查看对象的状态:

kubectl get ingressclass kubectl get ing

命令 kubectl describe 可以看到更详细的Ingress信息

kubectl describe ing ngx-ing

可以看到Ingress对象的路由规则Host/Path就是在YAML里设置的域名“ngx.test/”而且已经关联了第20讲里创建的Service对象还有Service后面的两个Pod。

另外不要对Ingress里“Default backend”的错误提示感到惊讶在找不到路由的时候它被设计用来提供一个默认的后端服务但不设置也不会有什么问题所以大多数时候我们都忽略它。

如何在Kubernetes里使用Ingress Controller

准备好了Ingress和Ingress Class接下来我们就需要部署真正处理路由规则的Ingress Controller。

你可以在GitHub上找到Nginx Ingress Controller的项目https://github.com/nginxinc/kubernetes-ingress因为它以Pod的形式运行在Kubernetes里所以同时支持Deployment和DaemonSet两种部署方式。这里我选择的是Deployment相关的YAML也都在我们课程的项目https://github.com/chronolaw/k8s_study/tree/master/ingress里复制了一份

Nginx Ingress Controller的安装略微麻烦一些有很多个YAML需要执行但如果只是做简单的试验就只需要用到4个YAML

kubectl apply -f common/ns-and-sa.yaml kubectl apply -f rbac/rbac.yaml kubectl apply -f common/nginx-config.yaml kubectl apply -f common/default-server-secret.yaml

前两条命令为Ingress Controller创建了一个独立的名字空间“nginx-ingress”还有相应的账号和权限这是为了访问apiserver获取Service、Endpoint信息用的后两条则是创建了一个ConfigMap和Secret用来配置HTTP/HTTPS服务。

部署Ingress Controller不需要我们自己从头编写DeploymentNginx已经为我们提供了示例YAML但创建之前为了适配我们自己的应用还必须要做几处小改动

metadata里的name要改成自己的名字比如 ngx-kic-dep。 spec.selector和template.metadata.labels也要修改成自己的名字比如还是用 ngx-kic-dep。 containers.image可以改用apline版本加快下载速度比如 nginx/nginx-ingress:2.2-alpine。 最下面的args要加上 -ingress-class=ngx-ink也就是前面创建的Ingress Class的名字这是让Ingress Controller管理Ingress的关键。

修改完之后Ingress Controller的YAML大概是这个样子

apiVersion: apps/v1 kind: Deployment metadata: name: ngx-kic-dep namespace: nginx-ingress

spec: replicas: 1 selector: matchLabels: app: ngx-kic-dep

template: metadata: labels: app: ngx-kic-dep ... spec: containers: - image: nginx/nginx-ingress:2.2-alpine ... args: - -ingress-class=ngx-ink

有了Ingress Controller这些API对象的关联就更复杂了你可以用下面的这张图来看出它们是如何使用对象名字联系起来的

确认Ingress Controller 的YAML修改完毕之后就可以用 kubectl apply 创建对象:

kubectl apply -f kic.yml

注意Ingress Controller位于名字空间“nginx-ingress”所以查看状态需要用“-n”参数显式指定否则我们只能看到“default”名字空间里的Pod

kubectl get deploy -n nginx-ingress kubectl get pod -n nginx-ingress

现在Ingress Controller就算是运行起来了。

不过还有最后一道工序因为Ingress Controller本身也是一个Pod想要向外提供服务还是要依赖于Service对象。所以你至少还要再为它定义一个Service使用NodePort或者LoadBalancer暴露端口才能真正把集群的内外流量打通。这个工作就交给你课下自己去完成了。

这里,我就用[第15讲]里提到的命令kubectl port-forward它可以直接把本地的端口映射到Kubernetes集群的某个Pod里在测试验证的时候非常方便。

下面这条命令就把本地的8080端口映射到了Ingress Controller Pod的80端口

kubectl port-forward -n nginx-ingress ngx-kic-dep-8859b7b86-cplgp 8080:80 &

我们在curl发测试请求的时候需要注意因为Ingress的路由规则是HTTP协议所以就不能用IP地址的方式访问必须要用域名、URI。

你可以修改 /etc/hosts 来手工添加域名解析,也可以使用 --resolve 参数指定域名的解析规则比如在这里我就把“ngx.test”强制解析到“127.0.0.1”,也就是被 kubectl port-forward 转发的本地地址:

curl --resolve ngx.test:8080:127.0.0.1 http://ngx.test:8080

把这个访问结果和上一节课里的Service对比一下你会发现最终效果是一样的都是把请求转发到了集群内部的Pod但Ingress的路由规则不再是IP地址而是HTTP协议里的域名、URI等要素。

小结

好了今天就讲到这里我们学习了Kubernetes里七层的反向代理和负载均衡对象包括Ingress、Ingress Controller、Ingress Class它们联合起来管理了集群的进出流量是集群入口的总管。

小结一下今天的主要内容:

Service是四层负载均衡能力有限所以就出现了Ingress它基于HTTP/HTTPS协议定义路由规则。 Ingress只是规则的集合自身不具备流量管理能力需要Ingress Controller应用Ingress规则才能真正发挥作用。 Ingress Class解耦了Ingress和Ingress Controller我们应当使用Ingress Class来管理Ingress资源。 最流行的Ingress Controller是Nginx Ingress Controller它基于经典反向代理软件Nginx。

再补充一点目前的Kubernetes流量管理功能主要集中在Ingress Controller上已经远不止于管理“入口流量”了它还能管理“出口流量”也就是 egress甚至还可以管理集群内部服务之间的“东西向流量”。

此外Ingress Controller通常还有很多的其他功能比如TLS终止、网络应用防火墙、限流限速、流量拆分、身份认证、访问控制等等完全可以认为它是一个全功能的反向代理或者网关感兴趣的话你可以找找这方面的资料。

课下作业

最后是课下作业时间,给你留两个思考题:

四层负载均衡Service与七层负载均衡Ingress有哪些异同点 你认为Ingress Controller作为集群的流量入口还应该做哪些事情

欢迎留言写下你的想法,思考题闭环是你巩固所学的第一步,进步从完成开始。

下节课是我们这个章节的实战演练课,我们下节课再见。