K8S 基础知识:Nginx Ingress Controller 初体验

Published: 2023-08-08

Tags: k8s

本文总阅读量

K8S 集群对外暴露服务有三种方式:LoadBlancer、NodePort、Ingress

LoadBlancer 依托于云服务提供的负载均衡,这个我还未验证,暂且跳过,NodePort 在之前测试时有用到过(在 K8S 运行多 Pods 负载均衡服务(Hello World)),NodePort 方式使用简单、便于测试,但是难以满足多样化的业务转发需求。

Ingress 功能强大,本篇使用 Ingress 来暴露服务,初探 Ingress

先行概念

什么是 Ingress?

Ingress 定义了流量路由规则,用于管理外部用户对 Kubernetes 集群内服务的访问,有了 Ingress,你就能轻松设置流量路由规则,它通过将 URL 映射到服务来实现这一点。

什么是 Ingress Controller?

Ingress Controller 负责通过读取和处理 Ingress 资源中的规则,将请求路由到 Kubernetes 集群中的相应服务。

Ingress 工作位置

(图例来自:Ingress Configuration)

Ingress 概念由两部分组成,其一是 Ingress 定义的 “转发规则”,另外是借助 Ingress Controller 实现功能,Ingress Controller 也是 K8S 中的 Service 实体,作为服务的服务运行(所以一般部署在单独的命名空间)

这么说来,Ingress Controller 不正是一个提供反向代理的 “Nginx” 吗?

这么理解没什么问题,Ingress 相当于对配置文件进行了抽象,当 Ingress 更新后,Ingress Controller 会从 K8S 获取到配置更新,而后自动 Reload 加载配置,Ingress 模式在设计时充分考虑到业务、场景的复杂性,所以定义了 Ingress 规则,而 Ingress Controller 以扩展方式集成,用户可以根据自己的需要选择或者开发适合自己的 Ingress Controller

目前常用的 Ingress Controller 有基于 Nginx 的两个版本 —— Kubernetes Ingress Nginx 和 Nginx NGINX Ingress Controller,从活跃度来看,前者 Kubernetes 版参与开发讨论的人更多,另外还有 HAproxy、Traefik Ingress Controller 和 Kong Ingress Controller,本篇基于 Kubernetes Ingress Nginx 进行测试。

注意事项

  1. 参考各种教程时,较早的 api 版本使用的是 extensions/v1beta1,而最新的(1.27)K8S 应该使用 networking.k8s.io/v1,注意替换。
  2. 参考本文时,假设你已经有一个可以正常访问的 Service,参考:在 K8S 运行多 Pods 负载均衡服务(Hello World)

安装 Kubernetes NGINX Ingress Controller

$ wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.1/deploy/static/provider/cloud/deploy.yaml

补充:这个地址来自于 Kubernetes 的 ingress-nginx 仓库(Kubernetes/ingress-nginx),进入后首先切换 Tag,搜索 "controller",出现的 Tag 选择最新的,而后点击进入「deploy 目录」-「static 目录」-「provider 目录」,里面有 cloudaws 等针对不同环境适配的部署脚本,进入 baremetal 目录(因为我这里是手动部署的 K8S,所以选择裸金属),点击 deploy.yaml 文件并查看 Raw 即可得到路径

(可选)替换镜像地址

如果你在国内环境,大概率无法直接下载 registry.k8s.io 地址的镜像

# 使用命令查看部署脚本所使用的镜像
$ cat deploy.yaml | grep -n image

编辑 deploy.yaml 文件(可以先 docker pull 测试镜像地址是否可用)

我这里是这样替换的

registry.k8s.io/ingress-nginx/controller:v1.8.1 => dyrnq/ingress-nginx-controller:v1.8.1
registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20230407 => dyrnq/kube-webhook-certgen:v20230407

替换后可以启动。

部署 Ingress 服务

启动服务

$ kubectl apply -f deploy.yaml

查看 Pods

查看 Deployment

查看 Services

由此可知,从外部可以通过 31805 端口访问 Ingress 服务。

访问可以看到 Nginx 输出的提示,因为还没有配置 Ingress 转发规则

配置 Ingress 规则

ing.yaml

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: hello-world
spec:
  ingressClassName: nginx
  rules:
  - http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: hello-world
            port:
              number: 8123

这个 Ingress 规则就是所有流量都转发到了 “hello-world” Service 的 8123 端口(请确认 Service 能正常请求,另外支持 host 参数能够根据主机名分流请求)

这里的 ingressClassName 是必要参数,用于 Ingress Controller 配对,翻看 deploy.yaml 部署脚本的代码,可以看到其值为 “nginx”

应用配置

$ kubectl apply -f ing.yaml

使用命令可以查看刚刚创建的 Ingress 规则

$ kubectl get ing

应用后进行访问测试,通过 Ingress 请求服务成功

调试问题多查看日志

这是 ingressClassName 跟 Ingress Controller 不匹配的日志,被忽略

I0807 09:44:31.729401       7 store.go:428] "Ignoring ingress because of error while validating ingress class" ingress="default/hello-world" error="ingress class annotation is not equal to the expected by Ingress Controller"

这是未添加 ingressClassName 字段的报错

W0807 09:56:37.880569       7 controller.go:328] ignoring ingress hello-world in default based on annotation : ingress does not contain a valid IngressClass

这是成功后,Nginx 的日志输处出

I0807 09:58:15.664806       7 store.go:477] "creating ingress" ingress="default/hello-world" ingressclass="nginx"
I0807 09:58:15.667525       7 event.go:285] Event(v1.ObjectReference{Kind:"Ingress", Namespace:"default", Name:"hello-world", UID:"35f1e4f1-dd88-4e47-b060-b6520a5cdbc8", APIVersion:"networking.k8s.io/v1", ResourceVersion:"896214", FieldPath:""}): type: 'Normal' reason: 'Sync' Scheduled for sync
I0807 09:58:15.667877       7 controller.go:190] "Configuration changes detected, backend reload required"
I0807 09:58:15.743703       7 controller.go:207] "Backend successfully reloaded"
I0807 09:58:15.746523       7 event.go:285] Event(v1.ObjectReference{Kind:"Pod", Namespace:"ingress-nginx", Name:"ingress-nginx-controller-5d986b84bd-vr7sv", UID:"fd7c6d6b-1fab-4ad4-8c5a-94a131124f87", APIVersion:"v1", ResourceVersion:"894069", FieldPath:""}): type: 'Normal' reason: 'RELOAD' NGINX reload triggered due to a change in configuration
172.17.157.115 - - [07/Aug/2023:09:58:30 +0000] "GET / HTTP/1.1" 200 67 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/115.0" 426 0.001 [default-hello-world-8123] [] 172.20.215.24:8080 67 0.002 200 a67880ab6a06dbfec55f4660aca82699
I0807 09:59:03.821804       7 status.go:300] "updating Ingress status" namespace="default" ingress="hello-world" currentValue=null newValue=[{"ip":"172.17.157.115"}]
I0807 09:59:03.827428       7 event.go:285] Event(v1.ObjectReference{Kind:"Ingress", Namespace:"default", Name:"hello-world", UID:"35f1e4f1-dd88-4e47-b060-b6520a5cdbc8", APIVersion:"networking.k8s.io/v1", ResourceVersion:"896294", FieldPath:""}): type: 'Normal' reason: 'Sync' Scheduled for sync

最后,展示一张不错的图例

(图例来自:Kubernetes Ingress Tutorial For Beginners)

参考