在上一篇文章中
K8S - 理解ClusterIP - 集群内部service之间的反向代理和loadbalancer
介绍了 ClusterIP 的主要作用 :
在k8s 集群内部 代理 内部的多实例 service
但是ClusterIP 还有1个变种 -> 无头服务 (headless service)
它用于代理集群外部的 ip资源
当然代理外部资源的方法有很多种, 除了headless service 还有externalName等service。
本文主要讲 headless service
什么是无头服务
referring:
https://kubernetes.io/zh-cn/docs/concepts/services-networking/service/#headless-services
无头服务(Headless Services)
有时你并不需要负载均衡,也不需要单独的 Service IP。遇到这种情况,可以通过显式设置 集群 IP(spec.clusterIP)的值为 "None" 来创建无头服务(Headless Service)。你可以使用无头 Service 与其他服务发现机制交互,而不必绑定到 Kubernetes 的实现。无头 Service 不会获得集群 IP,kube-proxy 不会处理这类 Service, 而且平台也不会为它们提供负载均衡或路由支持。无头 Service 允许客户端直接连接到它所偏好的任一 Pod。 无头 Service 不使用虚拟 IP 地址和代理 配置路由和数据包转发;相反,无头 Service 通过内部 DNS 记录报告各个 Pod 的端点 IP 地址,这些 DNS 记录是由集群的 DNS 服务所提供的。 这些 DNS 记录是由集群内部 DNS 服务所提供的 要定义无头 Service,你需要将 .spec.type 设置为 ClusterIP(这也是 type 的默认值),并进一步将 .spec.clusterIP 设置为 None。字符串值 None 是一种特殊情况,与未设置 .spec.clusterIP 字段不同。DNS 如何自动配置取决于 Service 是否定义了选择器:
其实官方文档写得不明不白 还是要实操才能理解
ClusterIP, headless 和 externalName的区别
这3个都属于 k8s 的services 大类
而且, headless 是ClusterIP 的一种, 是特别的ClusterIP (属性 spec.clusterip = none)
而externalName 是专门做外部资源代理的
为何本文例子不用 externalName service 呢
原因是externalName 有个limitation, 它只能代理外部资源的域名, 而不是能是ip地址。
下面是官网的说明
https://kubernetes.io/zh-cn/docs/concepts/services-networking/service/#externalname
========================================
ExternalName 类型
类型为 ExternalName 的 Service 将 Service 映射到 DNS 名称,而不是典型的选择算符, 例如 my-service 或者 cassandra。你可以使用 spec.externalName 参数指定这些服务。
例如,以下 Service 定义将 prod 名字空间中的 my-service 服务映射到 my.database.example.com:
apiVersion: v1
kind: Service
metadata:name: my-servicenamespace: prod
spec:type: ExternalNameexternalName: my.database.example.com
说明:
type: ExternalName 的服务接受 IPv4 地址字符串,但将该字符串视为由数字组成的 DNS 名称, 而不是 IP 地址(然而,互联网不允许在 DNS 中使用此类名称)。 类似于 IPv4 地址的外部名称无法被 DNS 服务器解析。
如果你想要将服务直接映射到某特定 IP 地址,请考虑使用无头服务。
=======================================================================
考虑到本文的例子 是用 ip address , 所以不深究 externalName
应用场景
如上图, 为了让bq api service 的实例们可以访问 集群外的 cloud-user , 我们要找个solution
方法一:
把cloud-user 的ip hardcode 在 bq-api-service 配置文件中
缺点:
较难实现 load balance
当 cloud user 被部署到其他server时, 需要修改配置
方法二:
就是本文的例子, 利用headless service 去代理集群外的资源
创建和应用headless service
编写yaml
headless-cloud-user
apiVersion: v1
kind: Service
metadata:name: headless-cloud-usernamespace: defaultlabels:app: cloud-user
spec:clusterIP: None # headless service---
apiVersion: v1
kind: Endpoints
metadata:name: headless-cloud-user # the name must be same with the name of service# because the service will use this endpoint to get the target ip and portnamespace: defaultlabels:target-app: cloud-user
subsets:- addresses:- ip: 192.168.0.47- ip: 192.168.0.51 # can add more ip , support base load balance
我已经踩好坑了
-
首先, 我们要把 service 和 endpoints 两部分分开写, 所以Service 部分不能写spec.selector. 想想也对, 这个service 不是代理pods资源, 是不应该有selector的
-
clusterIP 设成0, 实际试过不写这个配置, 会分配1个ip, 但是这个service 本身还是work的
-
Service 里不需要写port 的 信息了, 灵活分发, 例如调用的是 8080 端口, 映射就是外部资源的相同端口
-
在endpoint 部分, 名字要跟service 的名字一样! 否则不work
-
labels 不需要一样, 如例子
-
被代理的ip 可以写多个
部署yaml
[gateman@manjaro-x13 bq-api-service-proxy-external]$ kubectl apply -f headless-cloud-user.yaml
service/headless-cloud-user created
endpoints/headless-cloud-user created
查看svc:
[gateman@manjaro-x13 bq-api-service-proxy-external]$ kubectl get svc -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
headless-cloud-user ClusterIP None <none> <none> 56m <none>
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 78d <none>
可以见到 这个service 仍然属于 ClusterIP , 但是CLUSTER- IP 那一列为吉, 就是没有分配虚拟ip, ports 信息也是吉
查看endpoinds:
[gateman@manjaro-x13 bq-api-service-proxy-external]$ kubectl get ep -o wide
NAME ENDPOINTS AGE
headless-cloud-user 192.168.0.47,192.168.0.51 57m
kubernetes 192.168.0.3:6443 78d
它代理了两个 外部ip, 但是没有端口信息, 实际上就是全部端口都可以代理
测试
当然最快速的测试方法还是在dns-test 容器里测试
可以见到, 多次调用这个service情况下, 返回的信息是随机来自于 不同的ip地址的实例的
而且当我关闭其中1个ip实例时,
多次调用下, 它都自动选择健康的实例, 牛!
/ # curl headless-cloud-user:8081/actuator/info
{"app":"Cloud User API","version":"1.0.1","hostname":"b1a26a0f6685","dbUrl":"jdbc:mysql://192.168.0.42:3306/demo_cloud_user?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true","des/ # curl headless-cloud-user:8081/actuator/info
{"app":"Cloud User API","version":"1.0.1","hostname":"e166316cddb1","dbUrl":"jdbc:mysql://192.168.0.42:3306/demo_cloud_user?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true","des/ # curl headless-cloud-user:8081/actuator/info
{"app":"Cloud User API","version":"1.0.1","hostname":"e166316cddb1","dbUrl":"jdbc:mysql://192.168.0.42:3306/demo_cloud_user?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true","des/ # curl headless-cloud-user:8081/actuator/info
{"app":"Cloud User API","version":"1.0.1","hostname":"e166316cddb1","dbUrl":"jdbc:mysql://192.168.0.42:3306/demo_cloud_user?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true","des/ # curl headless-cloud-user:8081/actuator/info
{"app":"Cloud User API","version":"1.0.1","hostname":"b1a26a0f6685","dbUrl":"jdbc:mysql://192.168.0.42:3306/demo_cloud_user?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true","des/ # curl headless-cloud-user:8081/actuator/info
一些见解
同过这个例子, 有一些看法
-
既然 headless 可以代理外部资源, 而headless 也属于ClusterIP, 所以认为ClusterIP 只能代理内部资源的说法是错误的
-
headless 可以写多个ip地址, 也就有了基本load balance 功能, 所以认为headless service 无负载均衡的说法也是错的