Kubernetes Service和Ingress有什么区别?集群服务访问机制解析

刚接触 Kubernetes 的人常被一组名词绕晕:Pod 有 IP,Service 也有 IP,Ingress 还要配域名,到底谁负责什么?为什么 Pod 重启后 IP 变了应用却不用改配置?为什么内网用 Service 就够,对外却一定要上 Ingress?

这些问题的答案藏在 Kubernetes 的分层网络模型里。简单说:Service 解决的是「集群内稳定访问一组 Pod」的问题,工作在网络四层(TCP/UDP);Ingress 解决的是「把集群外的 HTTP/HTTPS 流量按域名和路径分发到不同 Service」的问题,工作在网络七层(HTTP/HTTPS)。两者不是替代关系,而是分工关系,实际生产里几乎总是配合使用。

这篇文章会把 Service 的几种类型(ClusterIP、NodePort、LoadBalancer、ExternalName)、Ingress 的工作机制、两者的边界讲清楚,配上一套「用户请求从外到内」的完整链路图解析、真实 YAML 示例和选型建议,帮你彻底理清集群服务访问这件事。

一、为什么需要 Service

Pod 是「易失」的:扩缩容、滚动更新、节点故障都会导致 Pod 重建,重建后 IP 会变。如果应用之间直接写 Pod IP,那配置将永无宁日。

Service 的核心价值有两个:
1. 提供稳定的虚拟 IP(ClusterIP)和 DNS 名字,屏蔽后端 Pod 的变化;
2. 做四层负载均衡,把请求分发到一组由标签选择器(selector)选中的 Pod。

它背后依赖 kube-proxy 组件,在每个节点上维护转发规则(早期用 iptables,现代集群多用 IPVS),把发往 ClusterIP 的流量转给某个真实的 Pod。

apiVersion: v1
kind: Service
metadata:
  name: web-svc
spec:
  selector:
    app: web
  ports:
    - protocol: TCP
      port: 80          # Service 暴露的端口
      targetPort: 8080  # 容器实际监听端口
  type: ClusterIP

这里三个端口概念要分清:
- port:Service 自己的端口;
- targetPort:Pod 容器监听的端口;
- 若是 NodePort 类型,还有 nodePort:每个节点上开放的端口。

二、Service 的四种类型

类型 访问范围 分配 IP/端口 典型用途
ClusterIP 仅集群内部 一个虚拟 IP 内部服务互访(默认)
NodePort 集群外(经节点 IP) 每个节点开 30000-32767 端口 测试、无 LB 时对外
LoadBalancer 集群外(经云 LB) 由云厂商分配外网 IP 生产对外暴露
ExternalName 集群内 无,做 CNAME 映射 把外部服务接入集群

ClusterIP 是默认类型,只在内网可达。内部服务互访一律用它,配 DNS 名(..svc.cluster.local)即可。

NodePort 在每个节点上开一个端口,外部通过 节点IP:NodePort 访问。它的缺点是端口范围受限、不易管理、暴露的是节点而非服务。示例:

spec:
  type: NodePort
  ports:
    - port: 80
      targetPort: 8080
      nodePort: 30080

LoadBalancer 是云上最常用的对外方式,Kubernetes 会调用云厂商的 API 创建一个外部负载均衡器。它适合直接暴露单一服务,但每加一个服务就多一个 LB,成本高、IP 多,管理不便——这正是 Ingress 要解决的问题。

ExternalName 不做转发,只返回一个 CNAME。用于把集群外的数据库等接入集群:

spec:
  type: ExternalName
  externalName: db.example.com

三、Ingress 在做什么

Ingress 是七层入口,本质是一条「路由规则」:根据 Host 和 Path,把 HTTP 请求转发到对应的 Service。注意 Ingress 资源本身只是规则,真正干活的是 Ingress Controller(如 nginx-ingress、Traefik、云厂商的控制器),它是一个跑在集群里的反向代理。

一个典型的 Ingress:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - www.example.com
      secretName: example-tls
  rules:
    - host: www.example.com
      http:
        paths:
          - path: /api
            pathType: Prefix
            backend:
              service:
                name: api-svc
                port:
                  number: 80
          - path: /static
            pathType: Prefix
            backend:
              service:
                name: static-svc
                port:
                  number: 80

这条规则的含义是:访问 www.example.com/api 走 api-svc,访问 /static 走 static-svc,并用 TLS Secret 做 HTTPS 终止。

关键点:
- Ingress Controller 本身通常也要以 Service(NodePort 或 LoadBalancer 类型)对外暴露,流量路径是「外部 → Ingress Controller Service → 按规则转发到后端 Service → Pod」;
- Ingress 只处理 HTTP/HTTPS,非 HTTP 协议(如数据库、MQTT)不能用 Ingress,要用 Service 的 L4 暴露方式;
- 一个集群通常只需一个 Ingress Controller,却可以承载成百上千个 Ingress 规则,这正是它相对「每个服务一个 LoadBalancer」的最大优势。

四、Service 与 Ingress 对比

维度 Service Ingress
工作层级 四层(TCP/UDP) 七层(HTTP/HTTPS)
核心职责 内部稳定访问 + 负载均衡 外部 HTTP 路由入口
路由依据 标签选择器选 Pod Host + Path
支持协议 任意 TCP/UDP 仅 HTTP/HTTPS
是否需额外组件 否(kube-proxy 内置) 需部署 Ingress Controller
TLS 终止 不涉及 支持,用 Secret 配置证书
典型类型 ClusterIP/NodePort/LB 一组规则
数量关系 每个服务一个 一个控制器承载多规则

五、一次请求的完整链路

把两者串起来看,从浏览器到容器的完整路径是:

  1. 用户在浏览器访问 https://www.example.com/api/user;
  2. DNS 解析到 Ingress Controller 对外暴露的地址(云 LB 的 IP 或节点 IP);
  3. 流量到达 Ingress Controller(它自己通过一个 LoadBalancer 或 NodePort 类型的 Service 暴露);
  4. Ingress Controller 按 Ingress 规则匹配 Host=www.example.com、Path=/api,选择后端 api-svc;
  5. kube-proxy 把发往 api-svc ClusterIP 的请求转发到某个 api Pod;
  6. Pod 处理请求并返回,原路返回给用户。

这条链路上任何一环配置错,都会表现为「网站打不开」。排查时按这个顺序定位很有效:DNS → LB/NodePort → Ingress Controller → Ingress 规则 → Service → Pod readiness。

排障命令:

# 看 Ingress 是否有地址分配
kubectl get ingress -A

# 看 Ingress 详情与事件
kubectl describe ingress app-ingress

# 看 Service 与 Endpoints(后端 Pod 是否挂上)
kubectl get svc -A
kubectl get endpoints api-svc

# 看 Ingress Controller 日志
kubectl logs -n ingress-nginx deploy/ingress-nginx-controller --tail=100

特别注意 Endpoints 为空是常见故障:Service 存在,但 selector 没匹配到任何 Ready 的 Pod,请求就会 503。

六、怎么选:三层建议

内网服务互访:一律用 ClusterIP。不要用 NodePort 干内网的事,安全性和可维护性都差。

对外暴露少量服务、无域名需求:可以用 LoadBalancer 直接暴露。成本可接受时最简单。

对外暴露多个 HTTP 服务、要域名/路径路由/TLS:上 Ingress。这是现代集群的标准做法。一个 LB 承载所有 HTTP 入口,节省云成本也便于统一做限流、鉴权、灰度。

非 HTTP 协议对外:Ingress 帮不上忙,要用 LoadBalancer 或 NodePort,或者用专门的 L4 代理(如 MetalLB 配合 Service)。

一个常见误区是「有了 Ingress 就不需要 Service 了」。这是错的:Ingress 的后端必须是 Service,它自己不做 Pod 发现,最终还是要靠 Service 把请求落到 Pod 上。

七、踩坑与注意事项

Ingress 与 Ingress Controller 名字混淆。kubectl get ingress 看不到 Ingress Controller,它跑在特定命名空间(如 ingress-nginx)里,是 Deployment/Service 资源。装集群时别忘了装 Controller。

ingressClassName 不匹配。spec.ingressClassName 必须与已安装的 IngressClass 一致,否则规则不生效:

kubectl get ingressclass

pathType 选错导致路由不匹配。Prefix 是前缀匹配,Exact 是精确匹配,ImplementationSpecific 由控制器决定。多数场景用 Prefix。

TLS Secret 命名空间问题。Ingress 引用的 TLS Secret 必须与 Ingress 在同一命名空间,且格式是 kubernetes.io/tls 类型:

kubectl create secret tls example-tls \
  --cert=fullchain.pem --key=privkey.pem -n default

风险提示:替换证书 Secret 会立即影响线上 HTTPS。建议先在测试环境验证证书链完整,替换前备份旧 Secret,出问题用 kubectl apply 恢复旧证书。

改端口/防火墙。NodePort 会占用 30000-32767 区间端口,若节点有防火墙(如 firewalld、iptables),需放行对应端口,否则外部访问不通:

# RHEL/CentOS/Rocky,临时放行示例
sudo firewall-cmd --add-port=30080/tcp
# 永久生效
sudo firewall-cmd --permanent --add-port=30080/tcp && sudo firewall-cmd --reload

风险提示:开放端口会扩大攻击面,建议同时限制来源 IP(--add-rich-rule)。回滚方式:firewall-cmd --remove-port=30080/tcp 后 reload。

八、从「能用」到「好用」:入口层的进阶考量

当服务规模上来后,Service 和 Ingress 的配置就不只是「通不通」的问题,还涉及稳定性、成本和治理。

稳定性方面,Ingress Controller 是所有外部流量的单点,必须做高可用:控制器要以多副本 Deployment 运行并设置反亲和性,避免全挤在一个节点;对外暴露的 Service 若是 LoadBalancer 类型,要确保云 LB 后端健康检查能正确剔除异常节点。同时给控制器配置合理的超时、连接数与请求体大小,防止个别大请求拖垮整个入口。

成本方面,最大的坑是「每个服务一个 LoadBalancer」。公有云里每个 LB 都有固定开销,服务多了这笔钱很可观。用 Ingress 收敛到一个入口,是控制成本最直接的手段。如果连一个云 LB 都想省,可以在自建集群用 MetalLB 之类的方案提供裸金属 LoadBalancer 能力,或者干脆用 NodePort 加外部反向代理。

治理方面,Ingress 是统一做限流、鉴权、灰度、灰度回滚、WAF 接入的天然位置。把这些能力集中在入口层,比在每个服务里重复实现要高效得多。很多团队会在 Ingress Controller 前再加一层七层网关(如 Nginx/APISIX),把跨域、认证、黑白名单下沉,让 Ingress 专注做路由。

需要提醒的是,能力越集中,风险也越集中。入口层一旦配置错误,影响的往往是全部服务而非单个服务,所以对 Ingress 规则的变更要走评审和灰度,改完先在小流量验证再全量。

常见问题

Q1:Service 的 ClusterIP 能 ping 通吗?
A:通常不能。ClusterIP 是虚拟 IP,由 iptables/IPVS 规则实现,不响应 ICMP。能 curl 通但不一定 ping 通是正常的。

Q2:NodePort 和 LoadBalancer 都能对外,选哪个?
A:自建集群(无云 LB)常用 NodePort 或 MetalLB;公有云推荐 LoadBalancer 或直接用 Ingress + 一个 LB,前者适合少量服务,后者适合多服务。

Q3:Ingress 能转发 gRPC 吗?
A:可以,gRPC 基于 HTTP/2,nginx-ingress、Traefik 等支持。但要注意注解里开启 HTTP/2 和后端协议(如 nginx.ingress.kubernetes.io/backend-protocol: GRPC)。

Q4:为什么我的 Service 没有 ClusterIP?
A:可能是 Headless Service(clusterIP: None)。它不分配虚拟 IP,DNS 直接返回所有 Pod IP,常用于 StatefulSet 的有状态服务。

Q5:Ingress 配好了,访问返回 404 而不是 503,说明什么?
A:404 说明流量已经到达 Ingress Controller,但 Host/Path 没匹配上任何规则;503 通常是匹配上了但后端 Service 无可用 Endpoints。两者定位方向不同。

总结

  1. Service 是四层、面向集群内,提供稳定的虚拟 IP 和负载均衡;Ingress 是七层、面向集群外,按域名和路径把 HTTP 流量路由到 Service。
  2. Service 有 ClusterIP、NodePort、LoadBalancer、ExternalName 四种类型,内网互访首选 ClusterIP,对外少量服务可用 LB。
  3. Ingress 必须配合 Ingress Controller 才能生效,一个 Controller 可承载大量 Ingress 规则,是节省云 LB 成本、统一入口治理的关键。
  4. 排障时按「DNS → LB/NodePort → Controller → Ingress 规则 → Service/Endpoints → Pod」链路逐层查,Endpoints 为空是 503 的高频原因。
  5. 改防火墙、换证书等操作务必加风险提示与回滚预案,尤其是对外端口和 TLS 证书,影响面直接触达线上用户。