0
点赞
收藏
分享

微信扫一扫

关于K8s中Headless Service的一些笔记整理


写在前面

  • 分享一些 ​​K8s​​​ 中 ​​Headless Service​​ 的笔记
  • 博文内容涉及:
  • ​Headless Service​​ 的简单介绍
  • ​Headless Service​​ 创建
  • 集群内/外 获取 ​​Headless Service​​​对应 ​​Pod​​ 列表的 Demo
  • 理解不足小伙伴帮忙指正

在认识一经出现时,情欲就引退。 -----昂克敌·杜伯隆:《邬布涅伽研究》第二卷第216页 -----《作为意志和表象的世界》

Headless Service 简单介绍

在某些场景中,如果我们希望自己控制 Pod 实例的​​负载均衡​​​ 策略,或者希望直接和 Pod 交互但是又不希望通过端口映射的方式,比如数据库根据情况做一些读写分离,或者一些应用在客户端做流量控制等,不使用 ​​Service​​​ 提供的由 ​​Kube-proxy​​​ 代理实现的​​默认负载均衡​​​的功能。希望明确是由那几个 ​​pod​​​ 提供能力,即直接通过 ​​Pod​​​ 发布服务, 而不是只有一个 集群 IP ​​Cluster IP​​​ 或者使用 ​​NodePort​​​、​​LoadBalancer​​​、​​ExternalName​​ 来发布服务。

这个时候,K8s 提供了 ​​Headless Service​​​ ,即不为 ​​Service​​​ 设置 ​​ClusterIP(入口IP地址)​​,也叫 无头服务,这里分两种情况

有选择器

第一种是有对应的服务能力提供者,即通过标签选择器选择了对应的后端能力,比如 ​​pod​​​,​​deployment​​​,​​statefulset​​ 等

在这种情况下,会通过​​Label Selector​​​将被选择的后端 Pod 列表返回给调用的客户端, K8s 不会为这样的 Service 分配任何 IP, DNS 会为这些​​Service 的 Name​​​ 添加一系列的 A(AAA)记录(IP 地址指向),直接指向后端映射的 Pod。 当然前提是 ​​通过 标签选择器选择到了对应的 pod。​

​Kube-prosy​​​ 不会处理这类型的 ​​Service​​​ ,没有负载均衡机制也没有请求映射,这里 ​​Endpoint Controller​​​ 任然会创建 ​​pod​​​ 对应的 ​​Endpoint ​​​, 同时 Kubernetes 控制平面会在 Kubernetes API 中创建 ​​EndpointSlice​​ 对象

​EndpointSlices​​ 表示针对服务的后端网络端点的子集(切片),这是在 1.21 版本才出现的,提供了一种简单的方法来跟踪 Kubernetes 集群中的网络端点(network endpoints)。EndpointSlices 为 Endpoints 提供了一种可扩缩和可拓展的替代方案。

在 Kubernetes 中,EndpointSlice 包含对一组网络端点的引用。 控制面会自动为设置了选择算符的 Kubernetes Service 创建 EndpointSlice,EndpointSlice 将包含对与 Service 选择算符匹配的所有 Pod 的引用。 EndpointSlice 通过唯一的协议、端口号和 Service 名称将网络端点组织在一起

Headless Service 通过暴露的 Endpoints 列表 应用可以通过编码实现客户端的负载均衡。

没有选择器

第二种是没有对应的服务能力提供者,即没有通过选择运算符来获取当前 集群的能力,这个时候,系统不会创建对应 ​​Endpoint​​​ ,也不会创建对应的 ​​EndpointSlice​​.

这种情况下,DNS 系统会查找和配置以下之一

  • 对于 ​​type: ExternalName​​​ 服务,查找和配置其 ​​CNAME​​ 记录
  • 对所有其他类型的服务,针对 ​​Service​​ 的就绪端点的所有 IP 地址,查找和配置 DNS A / AAAA 条记录
  • 对于 IPv4 端点,DNS 系统创建 A 条记录。
  • 对于 IPv6 端点,DNS 系统创建 AAAA 条记录。

Headless Service 创建

定义一个 Service 里面的 ClusterIP 为 None ,并且拥有 Selector 标签选择器,这样的 Service 为 ​​ Headlsee Service​

查看当前 k8s 中是否存在 ​​Headlsee ​

┌──[root@vms81.liruilongs.github.io]-[~]
└─$kubectl get svc -A | grep None
awx awx-demo-postgres-13 ClusterIP None <none> 5432/TCP 52d
kube-system liruilong-kube-prometheus-kubelet ClusterIP None <none> 10250/TCP,10255/TCP,4194/TCP 324d

一般情况下 ​​SatefulSet​​​ 需要 Headless Service 来实现 Pod 的网络的一致性(必须创建此服务),为客户端返回多个服务端地址。可以看到当前的集群中有两个 ​​Headless Service​​​, 一个是有状态应用(SatefulSet) postgres 数据库创建,一个是搭建 ​​prometheus​​ 集群监控创建的。

┌──[root@vms81.liruilongs.github.io]-[~]
└─$kubectl get svc awx-demo-postgres-13 -o yaml
apiVersion: v1
kind: Service
metadata:
..................
name: awx-demo-postgres-13
namespace: awx
spec:
clusterIP: None
clusterIPs:
- None
internalTrafficPolicy: Cluster
ipFamilies:
- IPv4
ipFamilyPolicy: SingleStack
ports:
- port: 5432
protocol: TCP
targetPort: 5432
selector:
app.kubernetes.io/component: database
app.kubernetes.io/instance: postgres-13-awx-demo
app.kubernetes.io/managed-by: awx-operator
app.kubernetes.io/name: postgres-13
app.kubernetes.io/part-of: awx-demo
sessionAffinity: None
type: ClusterIP
status:
loadBalancer: {}

这里我们可以大概的看到一个 ​​Headless​​​ 资源文件定义,对这样的 Service 进行访问,得到的就是一个 ​​符合选择器的全部的 Pod 列表​​​,然后客户端去自行的处理这些 Pod 列表。上面的 Service 中,客户端访问 ​​postgres​​ 数据库,会返回符合当前选择器的所有 postgres pod。

下面我们来看几个实际的 Demo

有状态 Headless 服务

对于有状态服务开来讲,需要创建 ​​StatefulSet​​ 为其提供能力,资源文件定义,只是一个 Demo ,所以我们这里没有定义 存储卷相关。

apiVersion: apps/v1
kind: StatefulSet
metadata:
creationTimestamp: null
labels:
app: web-headless
name: web
spec:
serviceName: web-headless
replicas: 3
selector:
matchLabels:
app: web-headless
template:
metadata:
creationTimestamp: null
labels:
app: web-headless
spec:
containers:
- image: nginx
name: nginx-web
ports:
- containerPort: 80
name: nginx-web
resources: {}

通过资源文件创建有状态的 pod

┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$kubectl get statefulsets.apps web -o wide
NAME READY AGE CONTAINERS IMAGES
web 3/3 96s nginx-web nginx
┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$kubectl get pods -o wide | grep web*
web-0 1/1 Running 0 2m13s 10.244.217.10 vms155.liruilongs.github.io <none> <none>
web-1 1/1 Running 0 2m11s 10.244.194.67 vms156.liruilongs.github.io <none> <none>
web-2 1/1 Running 0 114s 10.244.217.11 vms155.liruilongs.github.io <none> <none>

之后我们需要创建对应的 ​​Headless Service​​​ ,这里需要注意的是 ​​clusterIP: None​​​,选择器:​​app: web-headless​

┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$cat headless.yaml
apiVersion: v1
kind: Service
metadata:
name: web-headless
labels:
app: nginx_headless
spec:
ports:
- port: 30088
targetPort: 80
name: nginx-web-headless
clusterIP: None
selector:
app: web-headless

创建对应的 Headless SVC

┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$kubectl apply -f headless.yaml
service/web-headless configured
┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$kubectl get svc web-headless
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
web-headless ClusterIP None <none> 30088/TCP 24h

可以看到,他对每个 pod 都创建了对应的 ​​Endpoint​

┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$kubectl describe svc web-headless
Name: web-headless
Namespace: awx
Labels: app=nginx_headless
Annotations: <none>
Selector: app=web-headless
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: None
IPs: None
Port: nginx-web-headless 30088/TCP
TargetPort: 80/TCP
Endpoints: 10.244.194.67:80,10.244.217.10:80,10.244.217.11:80
Session Affinity: None
Events: <none>

可以通过不同的方式获取 Headless Service 的 Pod 列表。

集群外获取 Headless Service 的 Pod 列表

可以直接通过调用 Rest 接口的 方式获取 Headless 对应的 Endpoints,这里为了方便暴露 Rest 服务,通过 ​​kubectl proxy​​ 做一个内部代理。

┌──[root@vms81.liruilongs.github.io]-[~]
└─$nohup kubectl proxy --port=30021 &
[1] 109103
┌──[root@vms81.liruilongs.github.io]-[~]
└─$nohup: 忽略输入并把输出追加到"nohup.out"

测试一下

┌──[root@vms81.liruilongs.github.io]-[~]
└─$curl http://localhost:30021/api/ -s
{
"kind": "APIVersions",
"versions": [
"v1"
],
"serverAddressByClientCIDRs": [
{
"clientCIDR": "0.0.0.0/0",
"serverAddress": "192.168.26.81:6443"
}
]
}

对于 1.21 之前的版本获取 endpoins,可以通过 ​​Endpoints​​的方式获取

┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$kubectl describe endpoints web-headless
Name: web-headless
Namespace: awx
Labels: app=nginx_headless
service.kubernetes.io/headless=
Annotations: endpoints.kubernetes.io/last-change-trigger-time: 2022-12-09T07:13:15Z
Subsets:
Addresses: 10.244.194.67,10.244.217.10,10.244.217.11
NotReadyAddresses: <none>
Ports:
Name Port Protocol
---- ---- --------
nginx-web-headless 80 TCP

Events: <none>

通过 curl 调用对应的 REST 接口

┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$curl -s http://localhost:30021/api/v1/namespaces/awx/endpoints/web-headless | jq .subsets
[
{
"addresses": [
{
"ip": "10.244.194.67",
"hostname": "web-1",
"nodeName": "vms156.liruilongs.github.io",
"targetRef": {
"kind": "Pod",
"namespace": "awx",
"name": "web-1",
"uid": "d71722db-1c41-44ee-a55d-0042c7d2086e",
"resourceVersion": "14843070"
}
},
{
"ip": "10.244.217.10",
"hostname": "web-0",
"nodeName": "vms155.liruilongs.github.io",
"targetRef": {
"kind": "Pod",
"namespace": "awx",
"name": "web-0",
"uid": "7fa21492-d0f5-4840-8517-a05ed04651a4",
"resourceVersion": "14843008"
}
},
{
"ip": "10.244.217.11",
"hostname": "web-2",
"nodeName": "vms155.liruilongs.github.io",
"targetRef": {
"kind": "Pod",
"namespace": "awx",
"name": "web-2",
"uid": "7b262352-b12c-4ad2-8d83-8143c71e8c27",
"resourceVersion": "14843135"
}
}
],
"ports": [
{
"name": "nginx-web-headless",
"port": 80,
"protocol": "TCP"
}
]
}
]

如果使用的 1.21 以及之后的版本,我们可以通过 ​​EndpointSlicp​​ 来获取对应的 pod 列表

┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$kubectl describe endpointslices web-headless-888xr
Name: web-headless-888xr
Namespace: awx
Labels: app=nginx_headless
endpointslice.kubernetes.io/managed-by=endpointslice-controller.k8s.io
kubernetes.io/service-name=web-headless
service.kubernetes.io/headless=
Annotations: endpoints.kubernetes.io/last-change-trigger-time: 2022-12-09T07:13:15Z
AddressType: IPv4
Ports:
Name Port Protocol
---- ---- --------
nginx-web-headless 80 TCP
Endpoints:
- Addresses: 10.244.217.10
Conditions:
Ready: true
Hostname: web-0
TargetRef: Pod/web-0
NodeName: vms155.liruilongs.github.io
Zone: <unset>
- Addresses: 10.244.194.67
Conditions:
Ready: true
Hostname: web-1
TargetRef: Pod/web-1
NodeName: vms156.liruilongs.github.io
Zone: <unset>
- Addresses: 10.244.217.11
Conditions:
Ready: true
Hostname: web-2
TargetRef: Pod/web-2
NodeName: vms155.liruilongs.github.ioweb
Zone: <unset>
Events: <none>

通过 curl 调用对应的 REST 接口

┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$curl -s http://localhost:30021/apis/discovery.k8s.io/v1/namespaces/awx/endpointslices/web-headless-888xr | jq .endpoints
[
{
"addresses": [
"10.244.217.10"
],
"conditions": {
"ready": true,
"serving": true,
"terminating": false
},
"hostname": "web-0",
"targetRef": {
"kind": "Pod",
"namespace": "awx",
"name": "web-0",
"uid": "7fa21492-d0f5-4840-8517-a05ed04651a4",
"resourceVersion": "14843008"
},
"nodeName": "vms155.liruilongs.github.io"
},
{
"addresses": [
"10.244.194.67"
],
"conditions": {
"ready": true,
"serving": true,
"terminating": false
},
"hostname": "web-1",
"targetRef": {
"kind": "Pod",
"namespace": "awx",
"name": "web-1",
"uid": "d71722db-1c41-44ee-a55d-0042c7d2086e",
"resourceVersion": "14843070"
},
"nodeName": "vms156.liruilongs.github.io"
},
{
"addresses": [
"10.244.217.11"
],
"conditions": {
"ready": true,
"serving": true,
"terminating": false
},
"hostname": "web-2",
"targetRef": {
"kind": "Pod",
"namespace": "awx",
"name": "web-2",
"uid": "7b262352-b12c-4ad2-8d83-8143c71e8c27",
"resourceVersion": "14843135"
},
"nodeName": "vms155.liruilongs.github.io"
}
]

集群内获取 Headless Service 的 Pod 列表

对于无头服务,客户端可以通过连接到服务的 DNS 名称来连接到其 pod,就像使用常规服务一样,因为 DNS 返回 pod 的 IP,客户端直接连接到 pod,所以不是通过服务代理。这里通过 DNS 解析获取的 Pod 列表,Headless 服务仍然提供跨 Pod 的负载平衡,但这仅仅是通过 DNS 循环机制实现的负载均衡。而不是 ​​sessionAffinity​​ 相关配置

可以通过 对 服务的 DNS 解析来获取 POD 列表。

创建一个测试 Pod

┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$kubectl run tmp01 --image=tutum/dnsutils -- sleep infinity
pod/tmp01 created

同一命令空间获取 headless Service 的 Pod 列表

┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$kubectl exec tmp01 -it -- /bin/bash
root@tmp01:/# nslookup web-headless
Server: 10.96.0.10
Address: 10.96.0.10#53

Name: web-headless.awx.svc.cluster.local
Address: 10.244.194.67
Name: web-headless.awx.svc.cluster.local
Address: 10.244.217.10
Name: web-headless.awx.svc.cluster.local
Address: 10.244.217.11

不同命名空间获取 headless Service 的 Pod 列表

┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$kubectl exec tmp01 -it -- /bin/bash
root@tmp01:/# nslookup web-headless.awx
Server: 10.96.0.10
Address: 10.96.0.10#53

Name: web-headless.awx.svc.cluster.local
Address: 10.244.217.11
Name: web-headless.awx.svc.cluster.local
Address: 10.244.194.67
Name: web-headless.awx.svc.cluster.local
Address: 10.244.217.10

root@tmp01:/# nslookup web-headless.awx.svc.cluster.local.
Server: 10.96.0.10
Address: 10.96.0.10#53

Name: web-headless.awx.svc.cluster.local
Address: 10.244.217.11
Name: web-headless.awx.svc.cluster.local
Address: 10.244.194.67
Name: web-headless.awx.svc.cluster.local
Address: 10.244.217.10

无状态的 Headless 服务

关于无状态的 Headless Service 这里我们也简单介绍,和,有状态的没什么区别,对应的 SVC 还是用之前的 ​​web-headless​​​, 在 ​​StatefulSet​​​ 的基础上,我们创建一个 ​​deloyment​​ 提供服务能力

┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$cat deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
creationTimestamp: null
labels:
app: web-headless
name: web
spec:
replicas: 3
selector:
matchLabels:
app: web-headless
strategy: {}
template:
metadata:
creationTimestamp: null
labels:
app: web-headless
spec:
containers:
- image: nginx
name: nginx-web
ports:
- containerPort: 80
name: nginx-web
resources: {}
status: {}

通过 DNS 获取IP,可以发现,对应的 A 记录增加到 6个

┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$kubectl exec tmp01 -it -- /bin/bash
root@tmp01:/# nslookup web-headless.awx
Server: 10.96.0.10
Address: 10.96.0.10#53

Name: web-headless.awx.svc.cluster.local
Address: 10.244.194.67
Name: web-headless.awx.svc.cluster.local
Address: 10.244.217.11
Name: web-headless.awx.svc.cluster.local
Address: 10.244.217.10
Name: web-headless.awx.svc.cluster.local
Address: 10.244.194.69
Name: web-headless.awx.svc.cluster.local
Address: 10.244.194.70
Name: web-headless.awx.svc.cluster.local
Address: 10.244.217.12

root@tmp01:/# exit
exit

关于 ​​Headless Service​​​ 和小伙伴分享到这里, 通过 无头服务,我们可以通过 Servcie 来动态感知 Pod 副本的变化,监听 Pod 的状态,实现部分分布式集群的动态构建, 同时在有状态应用中都会涉及 ​​Headless Service​​。

博文参考

​​https://kubernetes.io/zh-cn/docs/concepts/services-networking/service/#headless-services​​

​​https://stackoverflow.com/questions/52707840/what-is-a-headless-service-what-does-it-do-accomplish-and-what-are-some-legiti​​

​​https://cloud.tencent.com/developer/article/1638722​​

​​https://kubernetes.io/docs/reference/kubernetes-api/service-resources/endpoints-v1/​​

​​https://kubernetes.io/docs/reference/kubernetes-api/service-resources/endpoint-slice-v1/​​


举报

相关推荐

0 条评论