Service类型详解


Service类型详解

在 Kubernetes 中,Service 是一种抽象概念,用于将一组运行在 Pods 上的应用程序暴露为网络服务。Kubernetes 提供了四种主要的 Service 类型,每种类型适用于不同的使用场景。

k8s 四种 Service 类型:

  • ClusterIP
  • NodePort
  • LoadBalancer
  • ExternalName

ClusterIP

它是 Kubernetes Service的默认类型,也就是说如果不指定 type,Kubernetes 会默认创建 ClusterIP 类型的 Service。

仅在集群内部可访问,提供一个集群内部的虚拟 IP 地址(ClusterIP)。适用于集群内部的服务发现和通信,例如微服务之间的调用。

image-20251110114549912

简单易用,默认类型,无需额外配置。它提供稳定的虚拟 IP 和 DNS 名称,便于服务发现。且负载均衡和故障转移由 Kubernetes 自动处理。

工作原理

  • 虚拟 IP 分配:当创建一个 ClusterIP Service 时,Kubernetes 会为其分配一个虚拟 IP(ClusterIP),这个 IP 来自集群的 Service CIDR 范围(例如 10.96.0.0/12)。
  • Endpoints 管理:Service 通过 selector 字段选择一组 Pod 作为后端。Kubernetes 会自动创建与 Service 同名的 Endpoints 对象,其中包含所有匹配 selector 的 Pod 的 IP 和端口。如果 Pod 发生变化(例如扩容或重启),Endpoints 会自动更新。
  • 流量转发:每个节点上的 kube-proxy 组件会监听 Service 和 Endpoints 的变化,并在节点上配置 iptables 或 IPVS 规则,将流量从 ClusterIP 转发到后端 Pod。

使用场景

  • 微服务架构:在微服务架构中,服务之间通过 ClusterIP 进行内部通信。例如,前端服务通过 ClusterIP 访问后端 API 服务。
  • 数据库服务:数据库服务(如 MySQL、PostgreSQL)通常只在集群内部暴露,使用 ClusterIP 可以确保外部无法直接访问。
  • 监控和日志系统:监控系统(如 Prometheus)、日志系统(如 Elasticsearch)等,通常只在集群内部使用。

配置 ClusterIP 服务

使用 YAML 文件配置 ClusterIP 服务

编写 Service 定义文件:

  • 指定 type: ClusterIP(默认值,可省略)。
  • 配置 selector 以匹配目标 Pod 的标签。
  • 定义 ports,包括 port(Service 的端口)和 targetPort(Pod 的端口)。

应用 YAML 文件:

vim my-service.yaml

apiVersion: v1
kind: Service
metadata:
  name: my-clusterip-service
spec:
  type: ClusterIP #可省略,默认就是 ClusterIP
  selector:
    app: my-app #匹配 Pod 的标签
  ports:
    - protocol: TCP
      port: 80         #Service 的端口,集群内的其他 Pod 通过该端口访问服务。
      targetPort: 8080 #Pod 的端口,Service 将流量转发到该端口。
kubectl apply -f my-service.yaml
使用 kubectl 命令行工具配置 ClusterIP 服务

创建 Service

kubectl expose deployment my-deployment \
 --type=ClusterIP \
 --name=my-clusterip-service \
 --port=80 \
 --target-port=8080

验证 Service

kubectl get svc my-clusterip-service

获取 ClusterIP 地址:

kubectl get svc my-clusterip-service

输出示例:

NAME                   TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
my-clusterip-service   ClusterIP   10.96.123.45    <none>        80/TCP     10s

从集群内部访问服务

假设有一个 Pod 运行在集群中,可以通过 ClusterIP 地址访问服务:

curl http://10.96.123.45:80

检查 Service 详情

kubectl describe svc my-clusterip-service

高级特性

指定 ClusterIP

如果需要手动指定 ClusterIP,可以在 YAML 文件中添加 clusterIP 字段:

apiVersion: v1
kind: Service
metadata:
  name: my-clusterip-service
spec:
  type: ClusterIP
  clusterIP: 10.96.0.100  #手动指定 ClusterIP
  selector:
    app: my-app
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080

注意:指定的 ClusterIP 必须在 Kubernetes 配置的地址范围内,且不能与其他服务冲突。

多端口支持

Service 可以暴露多个端口,每个端口可以映射到后端 Pod 的不同端口。

spec:
  ports:
    - name: http
      protocol: TCP
      port: 80
      targetPort: 8080
    - name: https
      protocol: TCP
      port: 443
      targetPort: 8443

NodePort

Kubernetes Service 的 NodePort 类型 是一种用于将服务暴露到集群外部的 Service 类型。在每个节点上开放一个静态端口(NodePort),允许通过 <NodeIP>:<NodePort> 的方式从集群外部访问服务。NodePort 会在 ClusterIP 的基础上额外暴露一个端口。

image-20251110114837876

NodePort 的端口范围默认是 30000-32767,也可以通过手工配置调整。由于每个服务占用一个节点端口,会导致端口资源有限,手动管理端口可能会导致冲突。总体性能和可用性不如 LoadBalancer。

工作原理

  • NodePort 分配:当创建一个 NodePort Service 时,Kubernetes 会为其分配一个 ClusterIP,并在每个节点上开放一个静态端口(NodePort)。NodePort 的默认范围是 30000-32767,但可以手动指定。
  • 流量转发:每个节点上的 kube-proxy 组件会监听 Service 和 Endpoints 的变化,并通过 iptables 或 IPVS 规则将流量从 NodePort 转发到后端 Pod。
  • 负载均衡:NodePort 会根据后端 Pod 的数量和状态,将流量均匀分配到可用的 Pod。

使用场景

  • 开发和测试环境:NodePort 可以快速暴露服务,方便外部访问和调试。
  • 小型生产环境:在没有负载均衡器的情况下,可以使用 NodePort 暴露服务。
  • 直接访问节点:某些场景下,可能需要直接通过节点的 IP 地址访问服务,例如某些网络设备或旧系统。
  • 云环境:NodePort 通常与 LoadBalancer 结合使用,LoadBalancer 会将外部流量导入 NodePort。

配置 NodePort 服务

在 Kubernetes 中,配置 NodePort 类型的服务可以通过 YAML 文件或 kubectl 命令行工具完成。

使用 YAML 文件配置 NodePort 服务

编写 Service 定义文件:

  • 指定 type: NodePort
  • 配置 selector 以匹配目标 Pod 的标签。
  • 定义 ports,包括 targetPort(Pod 的端口)和 nodePort

应用 YAML 文件:

kubectl apply -f your-service.yaml
示例 YAML 文件
apiVersion: v1
kind: Service
metadata:
  name: my-nodeport-service
spec:
  type: NodePort  #指定服务类型为 NodePort
  selector:       #用于匹配目标 Pod 的标签
    app: my-app   #匹配 Pod 的标签
  ports:
    - protocol: TCP
      port: 80         #Service 的端口,客户端通过该端口访问服务
      targetPort: 8080 #Pod 的端口,Service 将流量转发到该端口
      nodePort: 30007  #可选,指定节点端口(范围 30000-32767)
使用 kubectl 命令行工具配置 NodePort 服务

创建 Service:

kubectl expose deployment my-deployment \
 --type=NodePort \
 --name=my-nodeport-service \
 --port=80 \
 --target-port=8080 \
 --node-port=30007

参数详解

expose deployment:#基于现有的 Deployment 创建 Service。
--type=NodePort:#指定服务类型为 NodePort。
--name:#指定 Service 的名称。
--port:#Service 的端口。
--target-port:#Pod 的端口。
--node-port:#指定节点端口(可选)。

验证 Service:

kubectl get svc my-nodeport-service

验证 NodePort 服务

获取节点 IP 地址:

kubectl get nodes -o wide

访问服务:使用节点 IP 和 NodePort 访问服务。

http://<Node-IP>:30007

检查 Service 详情:

kubectl describe svc my-nodeport-service

LoadBalancer

Kubernetes Service 的 LoadBalancer 类型 是一种用于将服务暴露到集群外部的 Service 类型,在 NodePort 的基础上,通过云服务商(如 AWS、GCP、Azure)的负载均衡器(Load Balancer)将外部流量导入到服务。提供一个外部可访问的 IP 地址,通常用于生产环境。

image-20251110115048888

裸机环境或本地 Kubernetes 集群(如 Minikube)通常无法使用。

在支持 LoadBalancer 的云环境中,Kubernetes 会自动创建和管理负载均衡器,无需手动配置。

工作原理

  • Service 创建:当创建一个 LoadBalancer 类型的 Service 时,Kubernetes 会根据云提供商的支持,自动创建一个负载均衡器。
  • 外部 IP 分配:云提供商会为负载均衡器分配一个外部 IP 地址,该地址会关联到 Service。
  • 流量转发:负载均衡器会将外部流量转发到 Service 的 ClusterIP,再由 kube-proxy 或 IPVS 将流量分发到后端的 Pod。
  • 健康检查:负载均衡器会定期检查后端 Pod 的健康状态,确保流量只转发到健康的 Pod。

使用场景

  • 云环境:在支持 LoadBalancer 的云提供商(如 AWS、GCP、Azure)中,LoadBalancer 类型 Service 是暴露服务的推荐方式。
  • 需要外部访问的服务:例如 Web 应用、API 网关等,需要从集群外部访问的服务。
  • 高可用性需求:需要负载均衡和高可用性的服务。

配置 LoadBalancer 类型服务

创建 LoadBalancer 类型的 Service。

以下是一个简单的 LoadBalancer 服务配置示例:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  type: LoadBalancer#指定服务类型为 LoadBalancer
  selector:    #选择要暴露的 Pod,标签 app: my-app 用于匹配目标 Pod
    app: my-app
  ports:
    - protocol: TCP
      port: 80    #服务暴露的端口(外部访问的端口)
      targetPort: 8080 #Pod 中应用监听的端口
应用配置

将上述配置保存为 service.yaml 文件,然后使用 kubectl 命令应用配置:

kubectl apply -f service.yaml
检查服务状态

应用配置后,Kubernetes 会请求云服务提供商创建一个外部负载均衡器,并分配一个外部 IP 地址。可以通过以下命令查看服务状态:

kubectl get svc my-service

输出示例:

NAME         TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)        AGE
my-service   LoadBalancer   10.96.0.1      203.0.113.1     80:30007/TCP   10s

# EXTERNAL-IP #表示负载均衡器的外部 IP 地址,外部用户可以通过该地址访问服务。
# PORT(S) #显示服务端口和映射的节点端口。
访问服务

一旦 EXTERNAL-IP 可用,外部用户可以通过以下方式访问服务:

http://<EXTERNAL-IP>:80

例如,如果 EXTERNAL-IP203.0.113.1,则访问地址为:

http://203.0.113.1

配置注意事项

  • 云服务提供商支持LoadBalancer 类型服务依赖于云服务提供商的负载均衡器。如果在本地或不支持 LoadBalancer 的环境中运行(如 Minikube),可以使用 NodePortIngress,或者安装 MetalLB 等解决方案。
  • 安全组和防火墙规则:确保云服务提供商的安全组或防火墙规则允许外部流量访问负载均衡器的端口(如 80 或 443)。
  • DNS 配置:可以将 EXTERNAL-IP 映射到一个域名,方便用户访问。
  • 健康检查:云服务提供商通常会自动配置健康检查,确保流量只转发到健康的 Pod。如果需要自定义健康检查,可以参考云服务提供商的文档。
示例:暴露 HTTPS 服务

如果需要暴露 HTTPS 服务,可以修改 Service 配置并使用 Ingress 控制器管理 SSL 证书。例如:

apiVersion: v1
kind: Service
metadata:
  name: my-https-service
spec:
  type: LoadBalancer
  selector:
    app: my-app
  ports:
    - protocol: TCP
      port: 443
      targetPort: 8443

然后使用 Ingress 控制器(如 NGINX Ingress 或 Traefik)管理 SSL 证书和域名。

本地环境使用 MetalLB

如果在本地环境(如 Minikube 或裸机 Kubernetes 集群)中运行,可以使用 MetalLB 模拟 LoadBalancer 类型服务。

MetalLB 是一款专为 Kubernetes 集群设计的开源负载均衡器解决方案,主要解决裸机 Kubernetes 集群(非云厂商托管的集群)中 LoadBalancer 类型 Service 无法获取外部 IP的问题。

核心作用

Kubernetes 中,LoadBalancer 类型的 Service 用于将集群内服务暴露给外部网络,但这一功能依赖底层基础设施(如 AWS、GCP 等云厂商)提供的负载均衡器(Cloud LB)。而在裸机集群(物理机、虚拟机等非云环境)中,由于没有云厂商的 LB 支持,LoadBalancer 类型的 Service 会一直处于 Pending 状态,无法分配外部 IP。

MetalLB 的核心作用就是填补这一空白:它能为裸机 Kubernetes 集群中的 LoadBalancer 类型 Service 自动分配外部 IP,并通过网络协议(ARP/NDP 或 BGP)将这些 IP 通告给集群外部的网络,使外部流量能通过这些 IP 访问集群内的服务。

工作原理

MetalLB 的工作流程分为两大阶段:IP 地址分配IP 通告

1.IP 地址分配

管理员需预先配置一个或多个“IP 地址池”(如 192.168.100.10-192.168.100.20)。当集群中创建 LoadBalancer 类型的 Service 时,MetalLB 会从这些 IP 池中选择一个未被使用的 IP 分配给该 Service,使其状态从 Pending 变为 Running(类似云厂商 LB 分配公网 IP 的过程)。

2.IP 通告 分配 IP 后,MetalLB 需要让集群外部的网络(如企业内网、物理交换机)知道“这个 IP 对应的服务在 Kubernetes 集群的某个节点上”,这一过程称为“IP 通告”。它支持两种通告模式:

  • 二层模式(Layer 2):通过 ARP(IPv4)或 NDP(IPv6)协议,在集群所在的局域网内广播“IP 与节点 MAC 地址的映射关系”,使外部设备将目标为该 IP 的流量转发到集群内的某个节点(节点再通过 Kubernetes Service 规则转发到后端 Pod)。
    优点:配置简单,无需网络设备支持;缺点:同一 IP 流量只会路由到单个节点(存在单点瓶颈风险)。

  • BGP 模式(Layer 3):通过 BGP 协议与集群外部的路由器建立连接,将“IP 路由到 Kubernetes 节点”的规则通告给路由器,使流量可以负载均衡到多个节点。
    优点:支持流量分散到多个节点(无单点瓶颈);缺点:需要路由器支持 BGP 协议,配置较复杂。

主要特点

  • 原生集成 Kubernetes:通过自定义控制器(Controller)监听 Service 资源变化,自动完成 IP 分配和管理,与 Kubernetes 生命周期深度适配。
  • 轻量灵活:部署简单(可通过 Helm 或 YAML 清单安装),配置仅需定义 IP 池和通告模式,无需复杂依赖。
  • 开源免费:基于 MIT 协议开源,无商业授权限制。
  • 适配场景广:支持 IPv4/IPv6,兼容大多数主流网络环境(局域网、数据中心网络等)。

适用场景

  • 裸机 Kubernetes 集群(物理机、虚拟机部署,无云厂商 LB 支持)。
  • 需要将集群内服务(如 Web 应用、API 服务)通过固定外部 IP 暴露给集群外访问的场景。
  • 替代“NodePort + 反向代理”等临时方案,提供更稳定、原生的 LoadBalancer 体验。

总结

MetalLB 是裸机 Kubernetes 集群的“负载均衡器刚需组件”,它通过简单的配置解决了外部 IP 分配和通告问题,让裸机环境也能像云厂商集群一样使用 LoadBalancer 类型的 Service,大幅降低了裸机集群暴露服务的复杂度。

安装使用 MetalLB:

wget https://raw.githubusercontent.com/metallb/metallb/v0.14.9/config/manifests/metallb-native.yaml

kubectl get configmap kube-proxy -n kube-system -o yaml | \
sed -e "s/strictARP: false/strictARP: true/"  | \
sed -e 's#mode: ""#mode: "ipvs"#' | \
kubectl apply -f - -n kube-system

kubectl apply -f  metallb-native.yaml

kubectl get all -o wide -n metallb-system


vim 21.metallb-ip-pool.yaml

apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: address
  namespace: metallb-system
spec:
  addresses:
  - 192.168.8.80-192.168.8.100

---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: l2a
  namespace: metallb-system
spec:
  ipAddressPools:
  - address

kubectl apply -f 21.metallb-ip-pool.yaml


vim 22.deployment-svc-lb.yaml

---
apiVersion: apps/v1
kind: Deployment
metadata:
  creationTimestamp: null
  labels:
    app: myapp
  name: myapp
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
  strategy: {}
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: myapp
    spec:
      containers:
      - image: 192.168.57.200:8099/library/nginx:1.24
        name: nginx
        resources: {}
status: {}
---
apiVersion: v1
kind: Service
metadata:
  name: my-nodeport-service
spec:
  type: LoadBalancer
  selector:
    app: myapp  # 匹配 Pod 的标签
  ports:
    - protocol: TCP
      port: 80        # Service的端口
      targetPort: 80  # Pod的端口,内部容器开发的端口

kubectl apply -f  22.deployment-svc-lb.yaml

配置 IP 地址池:创建一个 ConfigMap 来定义 MetalLB 可分配的 IP 地址范围:

vim metallb-config.yaml

apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: my-ip-pool
  namespace: metallb-system
spec:
  addresses:
  - 192.168.1.240-192.168.1.250

应用配置:

kubectl apply -f metallb-config.yaml

ExternalName

Kubernetes Service 的 ExternalName 类型是一种特殊的 Service 类型,用于将集群内部的服务映射到集群外部的 DNS 名称,而不是将流量路由到集群内的 Pod。

简单易用,无需创建实际的 Service 或 Pod,隐藏外部服务的复杂性,提供统一的 DNS 访问方式。仅支持 DNS 名称,无法映射到 IP 地址。也不提供负载均衡或故障转移。

工作原理

  • DNS 映射ExternalName 类型的 Service 通过 DNS 解析将请求转发到指定的外部域名。
  • 无代理或负载均衡:与 ClusterIPNodePortLoadBalancer 类型不同,ExternalName 不会创建代理或负载均衡器,也不会分配集群 IP。
  • 简单配置:只需指定 externalName 字段,Kubernetes 会自动将 Service 的 DNS 名称解析为指定的外部域名。

使用场景

  • 访问外部服务:例如,访问外部数据库(如 MySQL、PostgreSQL)、消息队列(如 RabbitMQ)或第三方 API。
  • 服务迁移:在将服务从外部迁移到 Kubernetes 集群时,可以先使用 ExternalName 类型 Service 进行过渡。
  • 简化服务发现:通过 Kubernetes 的 DNS 服务发现机制,客户端可以像访问集群内服务一样访问外部服务。

示例配置

以下是一个 ExternalName 类型 Service 的示例:

---
apiVersion: apps/v1
kind: Deployment
metadata:
  creationTimestamp: null
  labels:
    app: myapp
  name: myapp
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
  strategy: {}
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: myapp
    spec:
      containers:
      - image: 192.168.57.200:8099/library/nginx:1.24
        name: nginx


---
apiVersion: v1
kind: Service
metadata:
  name: web-service  # Headless Service名称
  namespace: default
spec:
  clusterIP: None  # 关键:设置为None,声明为Headless Service
  selector:
    app: myapp  # 关联标签为app=myapp的Pod
  ports:
  - name: http
    port: 80  # 服务端口
    targetPort: 80  # Pod内应用监听的端口

curl http://web-service:80

总结

Service 类型 访问方式 适用场景
ClusterIP 仅集群内部 内部服务通信
NodePort 集群外部通过 <NodeIP>:<NodePort> 开发和测试环境
LoadBalancer 集群外部通过负载均衡器 IP 生产环境,需要外部负载均衡
ExternalName 通过 DNS CNAME 映射到外部服务 访问集群外部服务

在 Kubernetes 中,四种 Service 类型(ClusterIP、NodePort、LoadBalancer、ExternalName)没有绝对的“好用类型”,而是根据具体业务场景需求来选择最合适的类型。