Kubernetes Service 入门指南:从零开始认识 – wiki基地


Kubernetes Service 入门指南:从零开始认识

随着微服务架构和云原生应用的兴起,Kubernetes 已成为容器编排的事实标准。在使用 Kubernetes 部署应用时,我们通常会将应用的各个组件打包成容器,并在 Pod 中运行。然而,Pod 具有生命周期短、IP 地址不固定等特性。当一个 Pod 重启、扩缩容或被替换时,它的 IP 地址可能会发生变化。这就带来了一个核心问题:其他 Pod 或外部用户如何发现并可靠地访问这些动态变化的 Pod 集合?

这就是 Kubernetes Service 存在的意义。Service 是 Kubernetes 中一个至关重要的概念,它为 Pod 集合提供了一个稳定、统一的访问入口。本篇文章将带领大家从零开始,深入了解 Kubernetes Service 的方方面面。

1. 为什么需要 Service?Kubernetes 应用访问的困境

想象一下,你部署了一个 Web 应用,它由前端 Pod 和后端 API Pod 组成。前端需要调用后端的 API。在没有 Service 的情况下,前端 Pod 如何知道后端 API Pod 的 IP 地址呢?

  • Pod IP 不稳定: Pod 的 IP 地址是在 Pod 启动时由 Docker 或其他容器运行时分配的,并且这个 IP 地址仅在 Pod 的生命周期内有效。当 Pod 崩溃、被驱逐、或者通过 Deployment 进行滚动更新时,旧的 Pod 会被销毁,新的 Pod 会创建,它们将获得新的 IP 地址。
  • 服务发现困难: 如果前端 Pod 直接依赖后端 Pod 的 IP 地址,那么每当后端 Pod 的 IP 变化时,前端 Pod 就需要更新配置并重启,这显然是不可行的。在一个动态扩展的环境中,手动跟踪和管理 Pod IP 地址简直是噩梦。
  • 负载均衡问题: 当有多个后端 Pod 实例(为了高可用或处理更多流量)时,前端如何将请求分发到这些 Pod 上呢?直接访问单个 Pod IP 无法实现负载均衡。
  • 外部访问困难: 如果你想让互联网用户访问你的 Web 应用前端,如何提供一个稳定的、外部可访问的入口呢?直接暴露 Pod IP 既不安全也不稳定。

Service 就是为了解决这些问题而生的。它在 Pod 和访问者之间建立了一个抽象层。

2. Service 是什么?核心概念解析

Kubernetes Service 是一个抽象概念,它定义了访问 Pod 集合的策略。Service 为一组具有相同功能的 Pod 提供了一个固定的、不变的访问地址(通常是一个 IP 地址和端口)。无论后端的 Pod 如何变化(创建、销毁、IP 改变),Service 的访问地址保持不变。

Service 的核心作用是:

  1. 服务发现 (Service Discovery): Service 提供了一个稳定的 DNS 名称和 IP 地址,其他 Pod 或外部客户端可以通过这个固定的地址找到 Service。
  2. 负载均衡 (Load Balancing): Service 能够将收到的请求自动分发到其后端的 Pod 实例上,实现简单的负载均衡。
  3. 代理 (Proxy): Service 充当客户端和 Pod 之间的代理,将请求转发到正确的 Pod。

理解 Service,需要掌握以下几个核心概念:

  • Selector (选择器): Service 使用 Label Selector 来选择它代理的 Pod 集合。这是 Service 发现后端 Pod 的方式。例如,一个 Service 可以通过 app: my-backend 这个 Label Selector 来选择所有带有 app: my-backend 标签的 Pod。
  • Endpoint (端点): 当 Service 通过 Selector 找到匹配的 Pod 后,Kubernetes 会自动创建一个 Endpoint 对象。Endpoint 对象列出了所有匹配 Service Selector 的 Pod 的 IP 地址和端口。Service 的流量转发实际上是发送到这些 Endpoints 上的。Endpoint 是动态更新的,如果 Pod 的状态发生变化(启动、停止、IP 变化),Endpoint 列表也会自动更新。
  • Cluster IP (集群 IP): 这是 Service 在 Kubernetes 集群内部被分配的一个虚拟 IP 地址。它是 Service 的稳定标识符。Cluster IP 只能在集群内部访问。
  • Port (端口): Service 监听的端口。客户端通过 Service 的 Cluster IP 和这个 Port 来访问 Service。
  • TargetPort (目标端口): Pod 内部容器暴露的端口。Service 会将发送到其 Port 的请求转发到后端 Pod 的 TargetPort。
  • Protocol (协议): Service 使用的网络协议,常见的有 TCP 和 UDP。

简单来说: Service 通过 Selector 找到一组 Pod,然后为这组 Pod 分配一个稳定的 Cluster IP 和 Port。当有请求发往 Service 的 Cluster IP:Port 时,Service 会将请求转发到后方某个 Pod 的 TargetPort 上。这一切都是通过动态更新的 Endpoint 列表来实现的。

3. Service 的类型:不同的暴露方式

Kubernetes Service 提供了几种不同的类型,用于以不同的方式暴露 Service:

3.1 ClusterIP (默认类型)

  • 作用: 通过 Service 的 Cluster IP 在集群内部暴露 Service。
  • 访问方式: 只能在 Kubernetes 集群内部通过 Service 的 Cluster IP 和 Port 进行访问。这是默认的 Service 类型。
  • 使用场景: 集群内部服务之间的相互访问,例如前端 Pod 访问后端 API Service。
  • 特点: 提供了一个稳定的内部 IP,但不能从集群外部直接访问。

示例 YAML (ClusterIP):

yaml
apiVersion: v1
kind: Service
metadata:
name: my-internal-service # Service 的名称
namespace: default # 所在的命名空间
spec:
selector:
app: my-app # 选择带有 app=my-app 标签的 Pod
ports:
- protocol: TCP
port: 80 # Service 监听的端口 (集群内部访问 Service 用这个端口)
targetPort: 8080 # 请求转发到 Pod 内部容器的端口
type: ClusterIP # 明确指定类型,尽管它是默认值

3.2 NodePort

  • 作用: 通过每个 Node 节点的 IP 地址和静态端口暴露 Service。
  • 访问方式: 可以通过 <Node IP>:<NodePort> 从集群外部访问 Service。Kubernetes 会在集群中的每个 Node 上打开一个指定的静态端口 (NodePort),并将该端口的流量转发到 Service 的 Cluster IP:Port,最终到达后端的 Pod。
  • NodePort 范围: NodePort 的端口范围通常在 30000-32767 之间,可以通过 Kube-apiserver 的配置进行调整。如果创建 Service 时不指定 NodePort,Kubernetes 会自动分配一个范围内的端口。
  • 使用场景: 需要从集群外部访问 Service,但不依赖特定的云提供商负载均衡器时,或者用于演示和测试。
  • 特点: 简单易用,但每个 Node 都需要开放该端口,且端口范围受限,不适合大规模生产环境直接暴露应用。如果 Node 宕机,通过该 Node 的 IP 将无法访问。

示例 YAML (NodePort):

yaml
apiVersion: v1
kind: Service
metadata:
name: my-webapp-nodeport # Service 的名称
namespace: default
spec:
selector:
app: my-webapp # 选择带有 app=my-webapp 标签的 Pod
ports:
- protocol: TCP
port: 80 # Service 监听的端口 (集群内部访问 Service 用这个端口)
targetPort: 80 # 请求转发到 Pod 内部容器的端口
# nodePort: 30008 # 可选:手动指定 NodePort,如果不指定,Kubernetes 会自动分配
type: NodePort # 指定类型为 NodePort

创建此 Service 后,可以通过集群中 任意 一个 Node 的 IP 地址和分配的 NodePort (例如 192.168.1.10:30008) 来访问 Service,流量会被路由到后端的 Pod。

3.3 LoadBalancer

  • 作用: 在支持云提供商(如 AWS, GCP, Azure 等)的环境中,自动创建云提供商的负载均衡器来暴露 Service。
  • 访问方式: 云提供商会分配一个外部负载均衡器的 IP 地址,用户可以通过这个外部 IP 地址和 Service 的 Port 来访问 Service。负载均衡器会将流量转发到 Kubernetes 集群的 NodePort,再通过 Service 转发到 Pod。
  • 使用场景: 在云环境中需要从外部访问 Service,并且希望获得云提供商提供的完整的负载均衡能力(如公网 IP、流量分发、健康检查等)。
  • 特点: 最常用的生产环境外部暴露 Service 的方式,提供了稳定、高可用的外部访问入口。依赖于底层云基础设施。

示例 YAML (LoadBalancer):

yaml
apiVersion: v1
kind: Service
metadata:
name: my-public-service # Service 的名称
namespace: default
spec:
selector:
app: my-frontend # 选择带有 app=my-frontend 标签的 Pod
ports:
- protocol: TCP
port: 80 # Service 监听的端口 (外部负载均衡器监听这个端口)
targetPort: 80 # 请求转发到 Pod 内部容器的端口
type: LoadBalancer # 指定类型为 LoadBalancer
# 可以在 annotations 中添加云提供商特定的配置,例如 ELB 的类型等
# annotations:
# service.beta.kubernetes.io/aws-load-balancer-type: nlb

创建此 Service 后,如果你的 Kubernetes 集群运行在支持 LoadBalancer Service 的云提供商上,Kubernetes 会自动调用云 API 创建一个负载均衡器,并将其外部 IP 分配给 Service。你可以通过这个外部 IP 访问 Service。

3.4 ExternalName

  • 作用: 将 Service 映射到外部服务的 DNS 名称(而不是 IP 地址)。
  • 访问方式: 当客户端访问 InternalName 类型的 Service 时,Kubernetes 的 DNS 服务会返回一个 CNAME 记录到指定的 ExternalName。不会创建 Cluster IP 或代理。
  • 使用场景: 在 Kubernetes 集群中访问外部服务(如外部数据库、外部 API),但希望使用 Service 的方式来管理和引用,而不需要在 Pod 中硬编码外部服务的地址。
  • 特点: 不涉及代理或负载均衡,仅在 DNS 层面工作。

示例 YAML (ExternalName):

yaml
apiVersion: v1
kind: Service
metadata:
name: my-external-db # Service 的名称
namespace: default
spec:
type: ExternalName # 指定类型为 ExternalName
externalName: database.example.com # 外部服务的 DNS 名称

当集群内部的 Pod 试图通过 my-external-db 这个 Service 名称访问时,Kubernetes DNS 会将其解析为 database.example.com,然后客户端会直接连接到 database.example.com

4. Service 的创建与查看

创建 Service 通常使用 YAML 配置文件。

  1. 编写 YAML 文件: 根据你的需求选择 Service 类型,编写对应的 YAML 文件(如前面示例所示)。
  2. 应用 YAML 文件: 使用 kubectl apply -f <your-service-file.yaml> 命令创建 Service。

示例:创建一个简单的 nginx Pod 和一个 ClusterIP Service 来暴露它

首先,创建一个 nginx Pod 的 YAML 文件 (nginx-pod.yaml):

yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx-pod
labels:
app: nginx # 添加 label 以便 Service 选择
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80 # nginx 容器暴露的端口

然后,创建一个 Service 的 YAML 文件 (nginx-service.yaml):

yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-clusterip-service
spec:
selector:
app: nginx # 选择带有 app=nginx 标签的 Pod
ports:
- protocol: TCP
port: 80 # Service 监听 80 端口
targetPort: 80 # 转发到 Pod 的 80 端口
type: ClusterIP

执行创建命令:

bash
kubectl apply -f nginx-pod.yaml
kubectl apply -f nginx-service.yaml

查看 Service 状态:

使用 kubectl get servicekubectl get svc 命令可以查看 Service 的列表和状态:

bash
kubectl get svc

输出可能类似这样:

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 2d
nginx-clusterip-service ClusterIP 10.100.123.45 <none> 80/TCP 20s

你可以看到 nginx-clusterip-service 被分配了一个 Cluster IP (10.100.123.45),类型是 ClusterIP,监听端口是 80/TCP

查看 Service 后端 Pod 的 Endpoint:

使用 kubectl get endpointskubectl get ep 命令:

bash
kubectl get ep

输出可能类似这样:

NAME ENDPOINTS AGE
kubernetes 192.168.1.2:6443 2d
nginx-clusterip-service 172.17.0.3:80 20s # 这里的 IP:PORT 就是 nginx-pod 的 IP:targetPort

nginx-clusterip-service 对应的 Endpoints 列出了它当前发现的 Pod 的 IP 和 TargetPort。

在集群内部访问 Service:

创建一个临时的 Pod 来测试访问 nginx-clusterip-service

bash
kubectl run test-client --image=ubuntu --rm -it -- bash

进入 Pod 后,安装 curlwget 并尝试访问 Service:

“`bash
apt update && apt install -y curl
curl nginx-clusterip-service # 使用 Service 名称访问

或者使用 Cluster IP 访问

curl 10.100.123.45

“`

如果一切正常,你应该能看到 nginx 的欢迎页面 HTML 内容。这证明了 Service 通过其名称或 Cluster IP 成功地将请求转发到了后端的 nginx Pod。

5. Service Discovery (服务发现) 详解

Kubernetes 提供了两种主要的 Service Discovery 机制:

  1. DNS: 这是最常用和推荐的方式。Kubernetes 集群内部运行着一个 DNS 服务器(如 CoreDNS 或 Kube-DNS)。当 Pod 尝试访问 Service 时,它可以使用 Service 的名称作为主机名。例如,在同一个命名空间内,可以直接使用 my-service-name。跨命名空间访问时,格式是 my-service-name.my-namespace.svc.cluster.local。DNS 服务器会将 Service 名称解析为其 Cluster IP。
  2. Environment Variables (环境变量): 在较早的 Kubernetes 版本或特定场景下,当 Pod 启动时,Kubernetes 会为当前命名空间内的每个 Service 创建一组环境变量。例如,对于名为 my-service 的 Service,可能会有 MY_SERVICE_SERVICE_HOSTMY_SERVICE_SERVICE_PORT 等变量。然而,这种方式有两个限制:Pod 启动时 Service 必须已经存在,并且只包含 Pod 启动时刻的信息,后续 Service 的变化不会更新环境变量。因此,强烈建议使用 DNS 进行服务发现。

使用 DNS 的好处是:

  • 动态性: 无论 Service 何时创建,只要 DNS 记录更新,新的 Pod 就能通过 DNS 解析到 Service 的 IP。
  • 简洁: 代码中可以直接使用 Service 名称,易读易维护。
  • 不受 Pod 启动顺序影响: Pod 不需要关心 Service 是在它之前还是之后启动。

6. Headless Service (无头 Service)

在某些情况下,你可能不希望 Kubernetes 为 Service 分配一个 Cluster IP,而是希望能够直接获取到 Service 后端 Pod 的 IP 地址列表,以便进行更灵活的控制或使用外部的服务发现机制。这时可以使用 Headless Service。

  • 创建方式: 在 Service 的 spec 中设置 clusterIP: None
  • 特点: 不分配 Cluster IP,也不进行负载均衡代理。
  • 服务发现: DNS 仍然有效。但 DNS 解析的行为会发生变化:
    • 对于带有 Selector 的 Headless Service,DNS 会返回后端 Pod 的 多个 A 记录(每个 Pod IP 一个)。客户端可以直接获取所有 Pod IP,然后根据自己的逻辑进行连接或负载均衡。
    • 对于没有 Selector 的 Headless Service,DNS 会返回与 Endpoint 对象中列出的外部地址对应的记录。
  • 使用场景:
    • 需要客户端自己进行负载均衡或选择连接哪个 Pod(例如,StatefulSet 中的 Pod)。
    • 与其他服务发现系统集成。
    • 获取 Pod 的真实 IP 地址列表。

示例 YAML (Headless Service):

yaml
apiVersion: v1
kind: Service
metadata:
name: my-headless-service
spec:
selector:
app: my-stateful-app # 选择带有 app=my-stateful-app 标签的 Pod
clusterIP: None # 设置为 None,创建 Headless Service
ports:
- protocol: TCP
port: 80
targetPort: 8080

如果后端有多个 Pod (例如 Pod A IP: 172.17.0.3, Pod B IP: 172.17.0.4),当你通过 DNS 查询 my-headless-service 时,DNS 服务器会返回这两个 Pod 的 A 记录(172.17.0.3 和 172.17.0.4)。

7. Service 与 Ingress 的关系

Service 和 Ingress 都用于暴露 Kubernetes 集群内部的服务,但它们关注的层面不同:

  • Service: 专注于四层(TCP/UDP)和部分七层(通过端口转发)的负载均衡和服务发现。它主要负责将流量转发到后端 Pod 集合,解决 Pod IP 不稳定问题。Service 是 Kubernetes 的核心网络原语。
  • Ingress: 专注于七层(HTTP/HTTPS)流量的管理。它通常提供更高级的路由功能,例如基于主机名、路径的路由,SSL 终端,名称虚拟主机等。Ingress 本身不是一个服务,而是一组路由规则,需要 Ingress Controller 来实现这些规则(Ingress Controller 通常会利用 Service 来路由到后端的 Pod)。

简而言之: Ingress 处理的是外部 HTTP/HTTPS 流量如何进入集群并路由到 哪个 Service,而 Service 处理的是流量到达后如何负载均衡到后端的 哪些 Pod。对于非 HTTP/HTTPS 的流量或简单的内部服务暴露,通常只使用 Service。对于复杂的 Web 应用入口管理,Ingress 是更合适的选择。

8. 总结

Kubernetes Service 是连接 Pod 与访问者的桥梁,它解决了 Pod IP 地址不稳定的问题,提供了稳定的访问入口、服务发现和负载均衡能力。

  • 通过 Selector 动态发现后端 Pod。
  • 维护动态的 Endpoint 列表。
  • 提供稳定的 Cluster IP 和 DNS 名称进行服务发现。
  • 提供不同的 Type (ClusterIP, NodePort, LoadBalancer, ExternalName) 来满足不同的访问需求。
  • Headless Service 提供了直接访问 Pod IP 的能力。
  • Service 与 Ingress 共同协作,为应用提供全面的网络暴露方案。

理解并熟练使用 Service 是掌握 Kubernetes 网络和部署可靠应用的关键一步。希望这篇入门指南能帮助你从零开始,全面认识 Kubernetes Service,并将其应用于你的实际项目中!


发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部