ToolkitX
知识库工具箱

Service 与网络

ClusterIP, NodePort, LoadBalancer, Ingress

25min·进阶

01. Service 是什么、为什么需要它

Pod 是临死重生的——挂掉重建后 IP 全变了。你怎么保证客户端总能找到正确的 Pod?这就是 Service 存在的意义。 Service 给一组 Pod 提供稳定的访问入口——一个固定不变的虚拟 IP(ClusterIP)和一 DNS 名。你访问 Service 的名字,Service 把请求转发到后端健康的 Pod。 Service 通过 Label Selector 找到它要代理的 Pod。你在 Service 里写 selector: app: myapp,这个 Service 就代理所有 label 为 app=myapp 的 Pod。这就是为什么 Pod 要打 label 的原因之一。 Service 有三种常用类型:ClusterIP(集群内部访问,默认)、NodePort(在每个 Node 上开一个端口对外暴露)、LoadBalancer(云厂商的负载均衡器,生产环境标准方式)。
yaml
# Service YAML
apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: myapp
  ports:
  - port: 80        # Service 的端口
    targetPort: 8080 # 后端 Pod 的端口
  type: ClusterIP

02. ClusterIP、NodePort、LoadBalancer 的区别

ClusterIP——默认类型,只在集群内部可达。其他 Pod 可以访问,集群外面的客户端不行。适合内部服务(数据库、后端 API)。 NodePort——在每个 Node 上开一个高端口(默认范围 30000-32767),外部客户端访问任意 Node 的 IP 加这个端口就进去了。NodePort 会自动创建 ClusterIP。适合开发测试或简单的对外暴露,生产环境不够灵活。 LoadBalancer——云厂商(AWS、GCP、Azure)提供的外部负载均衡器。自动创建 ClusterIP 和 NodePort,然后云厂商分配一个公网 IP 或域名。生产环境的标准方式,但每个 LoadBalancer 都要花钱。 还有一种 ExternalName——不代理 Pod,而是把一个 Service 名映射到一个外部 DNS 域名上。
yaml
# NodePort 示例
spec:
  type: NodePort
  selector:
    app: myapp
  ports:
  - port: 80
    targetPort: 8080
    nodePort: 30080  # 可选,不写就随机分配

# LoadBalancer 示例
spec:
  type: LoadBalancer
  selector:
    app: myapp
  ports:
  - port: 80
    targetPort: 8080
云上的生产环境用 LoadBalancer。自建机房用 NodePort + 外部 Nginx/HAProxy 或者 MetalLB(裸金属的负载均衡方案)。

03. Ingress——HTTP/HTTPS 路由

Service 是四层(传输层)的负载均衡,Ingress 是七层(应用层)的 HTTP 路由。Ingress 能根据域名和 URL 路径把请求路由到不同的 Service。 比如 example.com/api 去后端 API Service,example.com/web 去前端 Service。一个 Ingress 管理多个域名和路径。 Ingress 本身只是一个配置规则,真正干活的是 Ingress Controller——Nginx Ingress、Traefik、HAProxy 这些。你需要先部署 Ingress Controller,然后创建 Ingress 资源。 现在 K8s 也在推 Gateway API——Ingress 的下一代,功能更丰富、角色更清晰,但 Ingress 仍是主流。
yaml
# Ingress YAML
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-ingress
spec:
  rules:
  - host: api.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: api-service
            port:
              number: 80
  - host: web.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: web-service
            port:
              number: 80

04. Service 发现与 CoreDNS

K8s 集群里有个内置的 DNS 服务(CoreDNS),每个 Service 创建时自动注册一条 DNS 记录。Pod 之间通过 Service 名字就找到对方,完全不需要硬编码 IP。 DNS 名规则:服务名.命名空间.svc.cluster.local。同 Namespace 下直接用服务名就行,跨 Namespace 用 服务名.命名空间。 比如 dev 命名空间里有个叫 db 的 Service,同 Namespace 的 Pod 直接连 db。prod 里的 Pod 想连 dev 的 db 就写 db.dev。 如果 Service 是 Headless Service(clusterIP: None),DNS 不会返回虚拟 IP 而是返回所有后端 Pod 的 IP。适合需要自己控制负载均衡的场景(如数据库集群主从发现)。
yaml
# 同 Namespace 访问:直接用服务名
curl http://my-service/api

# 跨 Namespace:用 服务名.命名空间
curl http://my-service.dev.svc.cluster.local

# Headless Service
apiVersion: v1
kind: Service
metadata:
  name: db-headless
spec:
  clusterIP: None
  selector:
    app: mysql
  ports:
  - port: 3306
CoreDNS 默认部署为一个 Deployment(多个副本),保证 DNS 高可用。如果 Pod 的 DNS 解析有问题,先查 CoreDNS 的 Pod 是否 Running。

05. 网络策略——NetworkPolicy

默认情况下 K8s 集群里所有 Pod 之间可以互相访问(扁平网络),这在安全上是个隐患。NetworkPolicy 像防火墙规则——控制 Pod 的出入流量。 你可以定义:允许哪些来源的流量进入 Pod(ingress),允许 Pod 的流量去哪些目的地(egress)。源和目的地可以用 Pod Selector、Namespace Selector、IP 块来定义。 注意:NetworkPolicy 是命名空间级别的资源。要让它生效,你需要安装一个支持 NetworkPolicy 的网络插件(如 Calico、Cilium、Weave Net)。Flannel 默认不支持。
yaml
# NetworkPolicy——只允许 app=frontend 的 Pod 访问
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: api-allow
spec:
  podSelector:
    matchLabels:
      app: api
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: frontend
    ports:
    - protocol: TCP
      port: 8080
NetworkPolicy 默认是允许所有流量的。你创建第一条 deny-all 策略后,只放行你明确允许的流量。白名单模式比黑名单安全。

知识测验

1/5正确 0

Service 的第一种类型 ClusterIP 能在集群外访问吗?

下一节

ConfigMap 与 Secret

下一节