(十三)Ingress
在前面我们学习了 Service 可以给 Pod 提供稳定的入口,NodePort 还可以把一个 Service 暴露到集群外
但是网站多了以后,如果每个 Service 都分配一个 NodePort,用户就要记住很多 IP 和端口,防火墙和负载均衡器也会越来越乱;我们真正想要的通常是让用户只访问 80 和 443,再根据域名或路径把请求交给不同 Service
# 没有 Ingress,依赖不同的 NodePort 区分不同 Service
http://web.example.test:30001 -> web1
http://web.example.test:30002 -> web2
# 有 Ingress,统一访问 80 端口,根据路径决定转发到哪个 Service
http://web.example.test/web1/ -> web1 Service
http://web.example.test/web2/ -> web2 Service
Ingress 解决的问题就是 HTTP、HTTPS 请求,如何按照域名、路径进入不同的 Service
Ingress 和 Ingress Controller
Ingress 中保存的是 HTTP、HTTPS 路由规则,例如什么域名、什么路径应该交给哪个 Service;但 Ingress 只是 API Server 中的一份数据,它自己不会监听端口,也不会转发请求
真正接收请求并执行这些规则的程序叫 Ingress Controller
Ingress
-> 保存域名、路径、Service 等路由规则
IngressClass
-> 说明这份 Ingress 应该交给哪个 Controller
Ingress Controller
-> 真正运行的程序
-> 读取 Ingress
-> 接收请求
-> 按规则把请求转发给 Service
IngressClass 代表集群中某一种 Ingress 的实现;先查看集群中 有没有 IngressClass
kubectl get ingressclass
当前没有任何 IngressClass,说明还没有可供 Ingress 使用的控制器,后面即使成功创建 Ingress 对象,也不会有人接收和转发流量

安装 Traefik
这里使用 Traefik 作为 Ingress Controller,并且使用 Helm 进行安装
# 添加 Traefik Helm 仓库
helm repo add traefik https://traefik.github.io/charts --force-update
helm repo update
# 当前是本地 Kubernetes 集群,没有云厂商提供 LoadBalancer
# 因此这里让 Traefik 使用 NodePort 暴露 HTTP、HTTPS 入口
helm upgrade --install traefik traefik/traefik --version 41.3.0 --namespace traefik --create-namespace --set providers.kubernetesIngress.enabled=true --set service.spec.type=NodePort --wait --timeout 10m
# 查看 Traefik
kubectl get pods,svc -n traefik
# 再查看 IngressClass
kubectl get ingressclass
安装完成后,可以看到,Traefik Pod 已经运行,同时存在一个 NodePort Service,并且存在一个名为 traefik 的 IngressClass

ingressClassName: traefik
准备两个 Service
下面准备两个简单的 Nginx 服务
# 创建命名空间
kubectl create namespace ingress-lab
# 创建两个 Deployment
kubectl create deployment web1 --image=nginx:latest -n ingress-lab
kubectl create deployment web2 --image=nginx:latest -n ingress-lab
# 创建两个 ClusterIP Service
kubectl expose deployment web1 --port=80 --target-port=80 -n ingress-lab
kubectl expose deployment web2 --port=80 --target-port=80 -n ingress-lab
为了区分两个服务,我们修改一下 Pod 中的 首页内容
# web1
kubectl exec -n ingress-lab deployment/web1 -- sh -c 'echo "Web1 test ingress\n" > /usr/share/nginx/html/web1/index.html'
# web2
kubectl exec -n ingress-lab deployment/web2 -- sh -c 'echo "Web2 test ingress\n" > usr/share/nginx/html/web2/index.html'

创建 ingress
cat > ingress-demo.yaml <<'EOF'
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web
spec:
ingressClassName: traefik
rules:
- host: web.example.test
http:
paths:
- path: /web1
pathType: Prefix
backend:
service:
name: web1
port:
number: 80
- path: /web2
pathType: Prefix
backend:
service:
name: web2
port:
number: 80
EOF
# 应用,并指定命名空间
kubectl apply -n ingress-lab -f ingress-demo.yaml
# 查看
kubectl get ingress -n ingress-lab
kubectl describe ingress web-n ingress-lab
ingressClassName
ingressClassName: traefik,表示,将这份 Ingress 交给 traefik IngressClass,由 Traefik Controller 执行
host
host: web.example.test,表示只匹配 HTTP 请求中的 Host: web.example.test
path
path: /web1、pathType: Prefix,Prefix 表示按照路径前缀匹配
- /web1、/web1/、/web1/test 都会匹配 /web1 这条规则
## backend
表示匹配成功以后,把请求交给 web1 Service: 80,web2 同理
创建完后应该能看到以下信息,可以看到,在我们预想情况下,对
web.example.test/web1/*进行访问时,应该走的是 web1 的 Service # 访问 Ingress ``` # 查 ingress Node IP(80后面的端口) kubectl get service -n traefik
查 master IP
kubectl get node -o wide
手动指定 HTTP Host 请求头,因为没有 DNS 解析
curl -H 'Host: web.example.test' "http://${NODEIP}:${HTTPNODE_PORT}/web1/"
curl -H 'Host: web.example.test' "http://${NODEIP}:${HTTPNODE_PORT}/web2/"
可以看到,两次请求访问的是同一个 节点 IP + Traefik NodePort,区别只是请求路径不同,但 Traefik 读取 Ingress 后,会根据路径选择不同的 Service

整体的流程如图所示,看着很复杂,其实非常简单

# 基于域名分流
上述例子使用的是同一域名下按照路径进行分流,但 Ingress 也可以根据不同域名进行分流
web1.example.test -> web1 Service
web2.example.test -> web2 Service
只需要将 yaml 文件中的 host: 字段进行修改即可
- host: web1.example.test
http:
paths:
- path: / pathType: Prefix backend: service: name: web1 port: number: 80 ```
但本质依然是 Ingress Controller ——> 根据 Host / Path ——> 选择 Service
defaultBackend
如果希望没有匹配到任何规则的请求进入一个默认 Service,可以在 spec 字段进行配置
spec:
defaultBackend:
service:
name: web1
port:
number: 80
没有配置 defaultBackend 时,未匹配请求通常由 Ingress Controller 返回 404
关于 HTTPS
Ingress 也可以通过 TLS Secret 配置 HTTPS,由 Ingress Controller 在入口处理 TLS,基本结构为
spec:
tls:
- hosts:
- web.example.test
secretName: web-example-tls
这里只需要先知道这么个流程,后续再深入了解
HTTPS 请求
↓
Ingress Controller
↓
读取 TLS Secret
↓
完成 HTTPS 入口处理
↓
继续按照 Ingress 规则转发
如果熟悉 TLS 的话,肯定是不陌生的,依赖 TCP 的三握,但关闭使用自己的独立的安全流程,做个基本的了解即可
总结
本章内容并不算难,主要是实验多而已,目的是让大家可以明白 Service 解决的是一个服务后面有哪些 Pod,而 Ingress 解决的是外部 HTTP、HTTPS 请求应该进入哪一个 Service
Ingress 只是一个规则,而 Ingress Controller,才是真正接收和转发流量的程序
随着 Kubernetes 的发展,Gateway API 成了 Ingress 之后的发展路线,它把入口基础设施和应用路由进行拆解,能力更标准,也更适合多团队协作;但,无论使用哪一个 API,都必须先有对应的 Controller 才能进行后续的路由操作
愿诸君顺遂