K8s学习笔记(二十四) ingress

1 为什么需要 Ingress?

K8s 中服务(Service)的默认暴露方式有局限性,Ingress 正是为解决这些问题而生:

服务暴露方式 缺点 Ingress 的优势
NodePort 每个 Service 占用一个 Node 端口,端口多了难管理;只能用 "IP + 端口" 访问,无域名 1 个入口管理所有服务,支持 "域名 + 路径" 转发;无需暴露大量端口
LoadBalancer 每个 Service 需要一个云厂商负载均衡器,成本高;无法共享 共享 1 个 LoadBalancerr(或 NodePort),低成本;统一配置 SSL / 限流

简单说:Ingress 让你能用 http://app1.example.com 访问服务 A,http://app2.example.com 访问服务 B,或 http://example.com/app1 访问服务 A、http://example.com/app2 访问服务 B,实现 "一入口多服务" 的管理。

2 Ingress 的核心组成:2 个关键部分

Ingress 不是一个单一组件,而是 "规则定义 + 控制器实现" 的组合,二者缺一不可:

2.1 Ingress 资源(规则定义)

是 K8s 的 API 对象,用 YAML 定义 "外部流量如何转发到内部 Service" 的规则,比如:

  • 哪个域名对应哪个服务?
  • 哪个路径对应哪个服务?
  • 是否需要 SSL 证书?

它本身不干活,只负责 "写规则"。

2.2 Ingress Controller(规则执行)

是实际 "干活" 的组件(通常是一个 Pod),它会:

  1. 监听 K8s API 中 Ingress 资源的变化;
  2. 根据 Ingress 规则生成反向代理配置(比如 Nginx 配置);
  3. 接收外部流量,按照配置转发到对应的 Service。

主流 Ingress Controller:Nginx Ingress Controller(最常用,基于 Nginx)、Traefik、HAProxy Ingress 等,本文以 "Nginx Ingress Controller" 为例。

3 Ingress 的核心概念与基础配置

先从一个最简单的 Ingress 示例入手,理解核心字段:

3.1 基础示例:用路径转发流量

假设集群内有两个服务:

  • service-foo:提供 /foo 路径的服务,端口 80;
  • service-bar:提供 /bar 路径的服务,端口 80;

我们要实现:外部访问 http://myapp.com/foo 转发到service-foohttp://myapp.com/bar 转发到service-bar

步骤 1:部署 Ingress Controller(前提)

首先要部署 Nginx Ingress Controller,这是所有 Ingress 规则生效的基础:

bash 复制代码
# 部署官方Nginx Ingress Controller(适用于K8s 1.19+)
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.2/deploy/static/provider/cloud/deploy.yaml

验证部署(查看ingress-nginx命名空间下的 Pod,Running表示正常):

bash 复制代码
kubectl get pods -n ingress-nginx 

查看 Ingress Controller 的暴露方式(默认用 NodePort,会分配两个端口:HTTP (80)、HTTPS (443)):

bash 复制代码
kubectl get svc -n ingress-nginx ingress-nginx-controller

输出类似(PORT(S)列的32000:80/TCP表示 NodePort 为 32000,外部可通过 "NodeIP:32000" 访问):

plaintext 复制代码
NAME                                 TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)                      AGE
ingress-nginx-controller             NodePort   10.96.xxx.xxx   <none>        80:32000/TCP,443:32100/TCP   10m

如果部署NodePort而是LoadBalancer,需要修改yaml文件

bash 复制代码
kubectl edit svc ingress-nginx-controller -n ingress-nginx
# 将LoadBalancer修改成NodePort
步骤 2:创建 Ingress 资源(规则)

创建ingress-demo.yaml文件,定义转发规则:

yaml 复制代码
apiVersion: networking.k8s.io/v1  # 固定版本(K8s 1.19+推荐)
kind: Ingress
metadata:
  name: ingress-demo  # Ingress名称
  annotations:
    # 声明使用Nginx Ingress Controller(不同控制器注解不同)
    kubernetes.io/ingress.class: "nginx"
spec:
  rules:  # 核心规则列表(可配置多个域名)
  - host: myapp.com  # 外部访问的域名(需配置DNS或本地hosts解析到NodeIP)
    http:
      paths:  # 该域名下的路径转发规则
      - path: /foo  # 匹配路径:/foo
        pathType: Prefix  # 路径匹配类型:Prefix(前缀匹配,如/foo/1也会匹配)
        backend:
          service:
            name: service-foo  # 转发到的Service名称
            port:
              number: 80  # Service的端口
      - path: /bar
        pathType: Prefix
        backend:
          service:
            name: service-bar
            port:
              number: 80

应用 Ingress 规则:

bash 复制代码
kubectl apply -f ingress-demo.yaml

查看 Ingress 状态(ADDRESS列会显示 Ingress Controller 的访问地址,即 NodeIP):

bash 复制代码
kubectl get ingress ingress-demo
步骤 3:测试访问
  1. 配置域名解析:在本地电脑的hosts文件(Windows:C:\Windows\System32\drivers\etc\hosts;Linux/macOS:/etc/hosts)中添加一行:

    plaintext 复制代码
    192.168.1.100  myapp.com  # 192.168.1.100是K8s Node的IP
  2. 访问测试:

    • 访问 http://myapp.com:32000/foo → 流量转发到service-foo
    • 访问 http://myapp.com:32000/bar → 流量转发到service-bar

3.2 核心字段解析

字段 作用 关键说明
metadata.annotations 配置 Ingress Controller 的附加参数 kubernetes.io/ingress.class: "nginx"指定用 Nginx 控制器;还可配置 SSL、限流等
spec.rules.host 外部访问的域名 若不填,规则对 "所有域名" 生效(不推荐);支持通配符,如*.example.com
spec.rules.http.paths.pathType 路径匹配类型 3 种:- Prefix:前缀匹配(如/foo匹配/foo/foo/1)- Exact:精确匹配(如/foo只匹配/foo)- ImplementationSpecific:由控制器决定(不常用)
spec.rules.http.paths.backend 流量转发的目标 Service 必须指定service.name(Service 名称)和service.port.number(Service 端口)

3.3 完整案例:用 Ingress 路由到两个服务

目标

部署两个应用(Nginx 和 Httpd),通过 Ingress 实现:

  • 访问 example.com/nginx 路由到 Nginx 服务。
  • 访问 example.com/httpd 路由到 Httpd 服务。
环境准备
  • 一个可用的 k8s 集群(推荐用 minikube 本地测试,简单易操作)。
  • kubectl 命令行工具已配置。
步骤 1:部署 Ingress Controller(以 Nginx 为例)

如果用 minikube,可直接启用内置的 Nginx Ingress Controller:

bash 复制代码
minikube addons enable ingress

验证部署是否成功(等待所有 Pod 处于 Running 状态):

bash 复制代码
kubectl get pods -n ingress-nginx

输出类似(名称可能略有不同):

plaintext 复制代码
NAME                                       READY   STATUS    RESTARTS   AGE
ingress-nginx-controller-xxxxxxxxx-xxxxx   1/1     Running   0          5m
步骤 2:部署测试应用(Nginx 和 Httpd)

创建两个 Deployment,分别运行 Nginx 和 Httpd 容器。

新建 apps.yaml 文件:

yaml 复制代码
# Nginx 应用
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-app
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:alpine
        ports:
        - containerPort: 80

---
# Httpd 应用
apiVersion: apps/v1
kind: Deployment
metadata:
  name: httpd-app
spec:
  replicas: 2
  selector:
    matchLabels:
      app: httpd
  template:
    metadata:
      labels:
        app: httpd
    spec:
      containers:
      - name: httpd
        image: httpd:alpine
        ports:
        - containerPort: 80

部署应用:

bash 复制代码
kubectl apply -f apps.yaml

验证部署:

bash 复制代码
kubectl get deployments

输出:

plaintext 复制代码
NAME         READY   UP-TO-DATE   AVAILABLE   AGE
nginx-app    2/2     2            2           1m
httpd-app    2/2     2            2           1m
步骤 3:创建 Service(连接 Ingress 和应用)

Ingress 需要通过 Service 访问后端应用,这里创建两个 ClusterIP 类型的 Service(仅集群内部可访问,由 Ingress 转发流量)。

新建 services.yaml 文件:

yaml 复制代码
# Nginx 服务
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  selector:
    app: nginx  # 匹配 Nginx Deployment 的 label
  ports:
  - port: 80    # Service 暴露的端口
    targetPort: 80  # 容器内的端口(与 Deployment 中一致)

---
# Httpd 服务
apiVersion: v1
kind: Service
metadata:
  name: httpd-service
spec:
  selector:
    app: httpd  # 匹配 Httpd Deployment 的 label
  ports:
  - port: 80
    targetPort: 80

部署 Service:

bash 复制代码
kubectl apply -f services.yaml

验证 Service:

bash 复制代码
kubectl get services

输出:

plaintext 复制代码
NAME            TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
nginx-service   ClusterIP   10.100.xxx.xxx   <none>        80/TCP    1m
httpd-service   ClusterIP   10.100.xxx.xxx   <none>        80/TCP    1m
步骤 4:创建 Ingress 规则

定义 Ingress 资源,配置路由规则:将 example.com/nginx 路由到 nginx-serviceexample.com/httpd 路由到 httpd-service

新建 ingress.yaml 文件:

yaml 复制代码
# apiVersion:指定 Kubernetes API 版本,networking.k8s.io/v1 是当前 Ingress 资源的稳定版本
apiVersion: networking.k8s.io/v1
# kind:声明资源类型为 Ingress,用于管理外部访问集群内服务的路由规则
kind: Ingress
metadata:
  # name:Ingress 资源的名称,用于唯一标识该资源(在同一命名空间内)
  name: app-ingress
  # annotations:附加配置信息,用于向 Ingress 控制器传递额外参数(此处为 Nginx 控制器专用配置)
  annotations:
    # nginx.ingress.kubernetes.io/rewrite-target:路径重写规则
    # 作用:当请求路径匹配下方的 path 规则时,将路径重写为 /$2($2 对应 path 中第二个捕获组 (.*) 的内容)
    # 举例:访问 /nginx/test 时,重写后路径为 /test,避免后端服务(如 Nginx)因前缀 /nginx 不存在而返回 404
    nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
  # ingressClassName:指定使用的 Ingress 控制器类型
  # 必须与集群中部署的 Ingress 控制器的 class 名称一致(此处为 nginx,即 Nginx Ingress Controller)
  # 若不指定,Ingress 可能无法被正确处理(取决于集群默认配置)
  ingressClassName: nginx
  # rules:路由规则列表,定义不同主机/路径的请求如何转发
  rules:
  - host: example.com  # 仅对访问 example.com 域名的请求生效
                       # 注意:需在本地 hosts 文件或 DNS 中配置该域名指向 Ingress 控制器的 IP 才能测试
    http:  # 定义 HTTP 协议的路由规则(HTTPS 需额外配置 tls 字段)
      paths:  # 路径匹配规则列表(按顺序匹配,优先匹配靠前的规则)
      - path: /nginx(/|$)(.*)  # 路径匹配表达式
                               # 解析:
                               # - /nginx:匹配以 /nginx 开头的路径
                               # - (/|$):匹配紧跟的 / 或路径结束(避免误匹配 /nginxxyz 等无关路径)
                               # - (.*):捕获后续所有字符(作为第二个分组,对应 rewrite-target 中的 $2)
                               # 最终匹配范围:/nginx、/nginx/、/nginx/test、/nginx/a/b 等
        pathType: ImplementationSpecific  # 由 Nginx 控制器处理正则匹配
                          # 结合上述 path 表达式后,实际为精确的前缀匹配(避免默认 Prefix 可能的模糊匹配)
        backend:  # 匹配该路径时的后端服务配置
          service:
            name: nginx-service  # 转发到名为 nginx-service 的 Service
            port:
              number: 80  # 转发到该 Service 的 80 端口
      - path: /httpd(/|$)(.*)  # 与上述 /nginx 规则逻辑一致,匹配以 /httpd 开头的路径
                               # 匹配范围:/httpd、/httpd/、/httpd/docs 等
        pathType: ImplementationSpecific  # 由 Nginx 控制器处理正则匹配
        backend:  # 匹配该路径时的后端服务配置
          service:
            name: httpd-service  # 转发到名为 httpd-service 的 Service
            port:
              number: 80  # 转发到该 Service 的 80 端口

关键说明

  • annotations 中的 rewrite-target:当访问 example.com/nginx/test 时,会重写为 nginx-service:80/test(否则后端服务会收到 /nginx/test 路径,可能返回 404)。
  • ImplementationSpecific 是 Nginx Ingress 处理正则路径的标准配置,它会让 Nginx 控制器解析 path 中的正则表达式(如 (/|$)(.*)),实现需要的精确前缀匹配和路径捕获。
  • host: example.com:仅匹配该域名的请求(如果不指定 host,会匹配所有域名)。

部署 Ingress:

bash 复制代码
kubectl apply -f ingress.yaml

验证 Ingress:

bash 复制代码
kubectl get ingress

输出(ADDRESS 是 Ingress Controller 的 IP,后续访问用这个 IP):

plaintext 复制代码
NAME          CLASS   HOSTS         ADDRESS        PORTS   AGE
app-ingress   nginx   example.com   192.168.49.2   80      1m
步骤 5:测试访问

需要让本地机器识别 example.com 并指向 Ingress Controller 的 IP(即上面的 ADDRESS)。

配置本地 hosts
  • Linux:编辑/etc/hosts,添加一行:

    plaintext 复制代码
    192.168.49.2  example.com  # 替换为你的 Ingress ADDRESS
  • Windows :编辑 C:\Windows\System32\drivers\etc\hosts(需管理员权限),添加同上内容。

验证路由

curl 或浏览器访问:

  1. 访问 example.com/nginx

    bash 复制代码
    curl example.com/nginx

    输出 Nginx 的默认页面(说明路由到 Nginx 服务):

    plaintext 复制代码
    <!DOCTYPE html>
    <html>
    <head>
    <title>Welcome to nginx!</title>
    ...
  2. 访问 example.com/httpd

    bash 复制代码
    curl example.com/httpd

    输出 Httpd 的默认页面(说明路由到 Httpd 服务):

    plaintext 复制代码
    <html><body><h1>It works!</h1></body></html>

3.4 常见问题与扩展

1. Ingress 状态一直为 Pending?
  • 原因:未部署 Ingress Controller,或控制器未正常运行。
  • 解决:检查 kubectl get pods -n ingress-nginx,确保控制器 Pod 正常运行。
2. 访问返回 404?
  • 检查 Ingress 规则的 pathservice.name 是否正确(标签匹配是否错误)。
  • 若路径包含前缀(如 /nginx),需配置 rewrite-target 注解(否则后端服务可能无对应路径)。
3. 扩展:配置 HTTPS

如需启用 HTTPS,需创建 TLS 证书(可通过 cert-manager 自动生成),并在 Ingress 中添加 tls 配置:

yaml 复制代码
spec:
  tls:
  - hosts:
    - example.com
    secretName: example-tls  # 存储证书的 Secret 名称
  # 其他规则不变...

通过这个案例,你应该已经掌握了 Ingress 的核心用法:部署控制器、定义路由规则、关联服务与应用。实际生产中,可根据需求扩展更复杂的路由策略(如权重分配、跨命名空间路由等)。

4 Ingress 高级功能

4.1 SSL 证书配置(HTTPS 访问)

Ingress 支持 "TLS 终止"(在 Ingress Controller 层处理 SSL,内部服务用 HTTP),只需两步:

步骤 1:创建存储证书的 Secret

将 SSL 证书(tls.crt公钥、tls.key私钥)保存为 Secret(需与 Ingress 在同一命名空间):

bash 复制代码
kubectl create secret tls myapp-tls \
  --cert=./tls.crt \  # 你的证书文件路径
  --key=./tls.key    # 你的私钥文件路径
步骤 2:在 Ingress 中引用 TLS Secret

修改ingress-demo.yaml,添加spec.tls字段:

yaml 复制代码
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-demo
  annotations:
    kubernetes.io/ingress.class: "nginx"
spec:
  tls:  # TLS配置
  - hosts:
    - myapp.com  # 要启用HTTPS的域名(需与证书匹配)
    secretName: myapp-tls  # 引用前面创建的Secret
  rules:  # 规则不变,同上
  - host: myapp.com
    http:
      paths:
      - path: /foo
        pathType: Prefix
        backend:
          service:
            name: service-foo
            port:
              number: 80
      - path: /bar
        pathType: Prefix
        backend:
          service:
            name: service-bar
            port:
              number: 80

应用后,访问 https://myapp.com:32100/foo(32100 是 Ingress Controller 的 HTTPS NodePort),即可通过 HTTPS 访问。

4.2 域名重定向(HTTP→HTTPS)

通过注解配置,强制将 HTTP 流量重定向到 HTTPS:

yaml 复制代码
metadata:
  annotations:
    kubernetes.io/ingress.class: "nginx"
    # 开启HTTP→HTTPS重定向
    nginx.ingress.kubernetes.io/ssl-redirect: "true"

4.3 路径重写(URL Rewrite)

场景:外部访问 http://myapp.com/app1,但内部 Service 实际提供的路径是 /(如服务只认根路径),需用路径重写。

通过注解配置重写规则:

yaml 复制代码
metadata:
  annotations:
    kubernetes.io/ingress.class: "nginx"
    # 重写规则:将 /app1/(.*) 重写为 /$1
    nginx.ingress.kubernetes.io/rewrite-target: /$1
spec:
  rules:
  - host: myapp.com
    http:
      paths:
      - path: /app1/(.*)  # 用正则匹配路径,括号捕获后面的内容
        pathType: Prefix
        backend:
          service:
            name: service-app1  # 该服务的根路径是 /
            port:
              number: 80

效果:访问 http://myapp.com/app1/login → 重写为 http://service-app1/login

5 Ingress vs 传统服务暴露:核心对比

维度 Ingress(Nginx Controller) NodePort LoadBalancer
访问方式 域名 + 路径(友好) IP + 端口(难记) IP / 域名(友好)
端口管理 只暴露 1-2 个端口(80/443) 每个服务 1 个端口(混乱) 每个服务 1 个负载均衡器(成本高)
SSL 支持 统一配置(方便) 需服务自身实现(麻烦) 需云厂商配置(分散)
高级功能 路径重写、限流、缓存 部分支持(依赖云厂商)
适用场景 生产环境(多服务、需域名) 测试环境(快速暴露) 生产环境(单服务、高可用)

6 常见问题与排查

6.1 Ingress 规则不生效?

  • 检查 Ingress Controller 是否正常运行:kubectl get pods -n ingress-nginx
  • 检查 Ingress 是否关联到 Controller:kubectl describe ingress <ingress-name>,查看Events是否有 "Successfully configured";
  • 检查 Service 是否正常:kubectl get svc <service-name>,确保 Service 能正常访问(可在集群内用 Pod 测试)。

6.2 域名访问不通?

  • 检查本地hosts或 DNS 是否正确解析到 NodeIP;
  • 检查 Node 的防火墙是否开放 Ingress Controller 的 NodePort(如 32000、32100)。

6.3 HTTPS 证书无效?

  • 检查 Secret 是否正确:kubectl describe secret <tls-secret-name>,确保证书和私钥匹配;
  • 检查 Ingress 的spec.tls.hosts是否与证书的 "主题备用名称(SAN)" 一致。

总结

Ingress 的核心是 "统一入口 + 规则转发",学习时要抓住两个重点:

  1. 理解 "Ingress 资源(规则)" 和 "Ingress Controller(执行)" 的关系;
  2. 掌握基础的 "域名 + 路径转发" 和生产级的 "SSL 配置、路径重写"。
相关推荐
Olrookie4 小时前
Maven快速上手笔记
java·笔记·maven
能不能别报错4 小时前
K8s学习笔记(二十三) 网络策略 NetworkPolicy
笔记·学习·kubernetes
AI_56784 小时前
脑科学支持的Python学习法:每天2小时碎片化训练,用‘神经可塑性’打败拖延症“
开发语言·python·学习
摇滚侠4 小时前
Spring Boot3零基础教程,定制 Health 健康端点,笔记83
spring boot·笔记·spring
suknna5 小时前
记一次 Kubebuilder Operator 开发中的 CRD 注解超限问题
kubernetes
ysa0510305 小时前
利用数的变形简化大规模问题#数论
c++·笔记·算法
superior tigre5 小时前
esp32学习随笔文档1
学习·esp32
报错小能手5 小时前
计算机网络自顶向下方法10——应用层 HTTP/2 成帧 响应报文优先次序和服务器推
笔记·计算机网络
love530love5 小时前
【笔记】Podman Desktop 部署 开源数字人 HeyGem.ai
人工智能·windows·笔记·python·容器·开源·podman