K8S 基础知识:Endpoint 的作用与实践

Published: 2024-04-17

Tags: k8s

本文总阅读量

Endpoint 简介

Service 并非和 Pod 直接相连,有一种资源介于两者之间 —— 它就是 Endpoint

Endpoints 一般由 Kubernetes 根据 Service 选择器自动生成(未指定选择器的 Service 不会自动生成 Endpoint),它包含了与该 Service 相关联的一组 Pod 资源 IP 和端口,当 Service 收到请求时,它会查询 Endpoints 来获取可用的 Pod 地址,然后将请求转发到这些 Pod 上。

可以通过以下命令确认 Pod、Endpoint、Service 之间的关系

  • kubectl describe pods 查看 Pod 的 IP 地址
  • external-service 查看 Endpoint 引用的 IP 地址
  • kubectl describe service myServiceName 查看 Service 的 Endpoints 引用 IP 信息

一般不直接手动创建维护 Endpoint,而是由 Service 自动创建,不少刚刚接触 K8S 的使用者都容易忽略它的存在。

集群内通过 Serivce 的请求链路

在进一步了解 Endpoint 之前,先梳理下服务内 Pod 间访问的链路

当集群 Pod 内部的程序访问集群中的 Service 时,集群内 DNS 将 Service 名称转换为 Cluster IP(Service 的虚拟 IP),Pod 所在节点 Node 上的 kube-proxy 会捕获发往 Cluster IP 的请求,它会考虑与 Service 关联的 Endpoints 和负载均衡机制,最终选择目标 Pod,请求被转发到 Pod,完成集群内的访问。

补充 1)这些 Endpoints 实质上是提供服务的 Pod 的 IP 地址列表。

2)当 Pod 被创建或销毁时,Endpoint Controller 会更新 Endpoint IP 地址列表,kube-proxy 会定期从 API Server 拉取最新的 Endpoints 变更,更新 IPVS 规则(也可能使用的是 iptables),实现请求的转发。

接下来实践手动创建没有选择器的 Service、并创建对应的 Endpoint 让其自动关联

实践:手动配置 Endpoint 访问集群外服务

背景:我们可能会从集群内部访问集群外部的服务(外部的服务后期会迁移到集群内部),对于内网可以通过 IP 访问的服务,一个可行的方案是手动创建 Service 和 Endpoint,示例如下:

首先,创建一个没有选择器的 Service

创建一个服务,它监听 80 端口,它没有定义 Pod 选择器。

external-service.yml

apiVersion: v1
kind: Service
metadata:
  name: external-service
spec:
  ports:
  - port: 80

在集群中应用配置

$ kubectl apply -f external-service.yml
service/external-service created

然后为没有选择器的服务创建 Endpoint 资源

我准备了一个外部 HTTP 服务,有公网 IP,访问其 8000 端口输出 “hello world!\n”

app.py

from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'hello world!\n'

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8000)

在外部服务器部署

$ nohup python3 app.py &

注意 Endpoint 的名称(name: external-service)需要跟 Service 名称相同,否则不会自动关联

apiVersion: v1
kind: Endpoints
metadata:
  name: external-service
subsets:
  - addresses:
      - ip: 150.11.22.33
    ports:
      - port: 8000

应用配置

$ kubectl apply -f endpoint.yml
endpoints/external-service created

之后在集群启动一个容器,从这个容器内部请求服务(同一个 namespace 命名空间)

看到其请求到了外部服务,同时查看 Service 详情可见其自动绑定了 Endpoints

➜ kubectl describe service external-service
Name:              external-service
Namespace:         default
Endpoints:         150.11.22.33:8000
...

通过这个方式,我们可以定义一个 Service,它将流量转发到集群外部,待后续外部服务迁移到集群内时,更新 Service 添加选择器配置即可平滑迁移,拥有 Pod 选择器的 Service 可以自动管理 Endpoint,另外迁移无需修改程序的配置文件。

扩展:借助 Service 设置域名服务映射

在上小节手动配置 Service + Endpoint 实现了请求转发,如果外部服务拥有域名,K8S 提供了更便捷的方式,无需手动配置 Endpoint,Service 内置的 ExternalName 类型即可实现,以下是一个示例:

apiVersion: v1
kind: Service
metadata:
  name: httpbin
spec:
  type: ExternalName
  externalName: httpbin.org

创建 Service 后,我们可以访问 httpbin/get 代替 httpbin.org/get ,转发域名的好处是平滑迁移和集成外部服务,使用 ExternalName Service 应该仅限于与外部服务集成并需要简单别名映射的场景。

从输出看,httpbin 服务没有 CLUSTER-IP 和 PORT,取而代之的是 EXTERNAL-IP

另外 Service 详情信息也很简单

参考

  • 《Kubernetes in Action 中文版.pdf》- 七牛容器云团队译