K8S headless 服务
1. 定义
在某些场景下,开发人员可能不想使用 Service 提供的负载均衡功能,而希望自己来控制负载均衡策略,针对这种情况,Kubernetes 提供了 Headless Service,这类 Service 不会分配 ClusterIP,如果想要访问 Service,只能通过 service 的域名进行查询。
因为没有 ClusterIP,kube-proxy 并不处理此类服务,因为没有 load balancing 或 proxy 代理设置,在访问服务的时候回返回后端的全部的 Pods IP 地址,主要用于开发者自己根据 pods 进行负载均衡器的开发 (设置了 selector),相比于 clusterIP,headless 少了一层,直接解析的 podip 所以速度更快。
需要注意的是,不管是 headless 类型的 service 还是 clusterip 类型的 service,都可以在相同命名空间的容器内解析 service 名称;headless 解析出来的是 podip,ping servicename 可以通,clusterIP 类型的 service 解析出来的是分配的 clusterip,ping 不通。通过 headless 类型的 service 访问的直接是容器内端口 headless-service-name containerport,这时候在 service 里面指定的 port 和 targetport 都是无用的;通过 clusterIP 类型的 service 名称是无法直接访问容器内端口的,需要先通过 targetPort 指定容器内端口,port 指定转发后的端口,访问的是 cluterip-service-name port
2. 创建 headless 服务
将 service spec 中 的 clusterIP 字段设置为 None 会使服务成为 headless 服务,因为 Kubernetes 不会为其分配集群 IP,DNS 服务器将返回 podIP 而不是单个服务 IP。
open1-headless.yaml
apiVersion: v1
kind: Namespace
metadata:
name: dev
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: open1-headless
namespace: dev
spec:
replicas: 3
selector:
matchLabels:
app: open1-headless
template:
metadata:
labels:
app: open1-headless
spec:
containers:
- name: open1-headless
image: openresty:1.21.4.1
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: open1-headless
namespace: dev
spec:
selector:
app: open1-headless
clusterIP: None
type: ClusterIP
ports:
- port: 80
targetPort: 80
open2-headless.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: open2-headless
namespace: dev
spec:
replicas: 3
selector:
matchLabels:
app: open2-headless
template:
metadata:
labels:
app: open2-headless
spec:
containers:
- name: open2-headless
image: openresty:1.21.4.1
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: open2-headless
namespace: dev
spec:
selector:
app: open2-headless
clusterIP: None
type: ClusterIP
ports:
- port: 80
targetPort: 80
查看
[root@master ~/openresty]#kubectl get pods,svc,deploy -o wide -n dev
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod/open1-headless-79b845f7fc-bkv4g 1/1 Running 0 14s 10.244.0.8 master <none> <none>
pod/open1-headless-79b845f7fc-jqtqv 1/1 Running 0 14s 10.244.1.9 node01 <none> <none>
pod/open1-headless-79b845f7fc-m24x2 1/1 Running 0 14s 10.244.2.8 node02 <none> <none>
pod/open2-headless-69bb4dcf5c-l9gtc 1/1 Running 0 14s 10.244.1.10 node01 <none> <none>
pod/open2-headless-69bb4dcf5c-n6bdn 1/1 Running 0 14s 10.244.0.9 master <none> <none>
pod/open2-headless-69bb4dcf5c-qw8mr 1/1 Running 0 14s 10.244.2.9 node02 <none> <none>
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
service/open1-headless ClusterIP None <none> 80/TCP 15s app=open1-headless
service/open2-headless ClusterIP None <none> 80/TCP 14s app=open2-headless
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
deployment.apps/open1-headless 3/3 3 3 15s open1-headless openresty:1.21.4.1 app=open1-headless
deployment.apps/open2-headless 3/3 3 3 14s open2-headless openresty:1.21.4.1 app=open2-headless
3. 访问服务
headless 服务因为没有 ClusterIP,所以不能使用 ip 去访问了,只能使用域名,而且只能在容器内部生效,域名格式:
[service的名字].[命名空间].svc.cluster.local
进入容器
[root@master ~]#kubectl exec -it open1-headless-79b845f7fc-m24x2 bash -n dev
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
[root@open1-headless-79b845f7fc-m24x2 /]# cat /etc/resolv.conf
nameserver 10.96.0.10
search dev.svc.cluster.local svc.cluster.local cluster.local localdomain
options ndots:5
域名解析
[root@open1-headless-79b845f7fc-m24x2 /]# dig @10.96.0.10 open1-headless.dev.svc.cluster.local
; <<>> DiG 9.11.4-P2-RedHat-9.11.4-26.P2.el7_9.9 <<>> @10.96.0.10 open1-headless.dev.svc.cluster.local
; (1 server found)
;; global options: +cmd
;; Got answer:
;; WARNING: .local is reserved for Multicast DNS
;; You are currently testing what happens when an mDNS query is leaked to DNS
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 44669
;; flags: qr aa rd; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;open1-headless.dev.svc.cluster.local. IN A
;; ANSWER SECTION:
open1-headless.dev.svc.cluster.local. 30 IN A 10.244.1.9
open1-headless.dev.svc.cluster.local. 30 IN A 10.244.2.8
open1-headless.dev.svc.cluster.local. 30 IN A 10.244.0.8
;; Query time: 1 msec
;; SERVER: 10.96.0.10#53(10.96.0.10)
;; WHEN: Sat Aug 20 05:34:34 UTC 2022
;; MSG SIZE rcvd: 221
# 通过域名解析可以看到三个后端 podIP
[root@open1-headless-79b845f7fc-m24x2 /]# nslookup open1-headless 10.96.0.10
Server: 10.96.0.10
Address: 10.96.0.10#53
Name: open1-headless.dev.svc.cluster.local
Address: 10.244.1.9
Name: open1-headless.dev.svc.cluster.local
Address: 10.244.0.8
Name: open1-headless.dev.svc.cluster.local
Address: 10.244.2.8
通过域名访问另一个服务
[root@open1-headless-79b845f7fc-m24x2 /]# telnet open2-headless 80
Trying 10.244.2.9...
Connected to open2-headless.
Escape character is '^]'.
^CConnection closed by foreign host.
[root@open1-headless-79b845f7fc-m24x2 /]# ping open2-headless -c 3
PING open2-headless.dev.svc.cluster.local (10.244.1.10) 56(84) bytes of data.
64 bytes from 10-244-1-10.open2-headless.dev.svc.cluster.local (10.244.1.10): icmp_seq=1 ttl=62 time=0.438 ms
64 bytes from 10-244-1-10.open2-headless.dev.svc.cluster.local (10.244.1.10): icmp_seq=2 ttl=62 time=0.454 ms
64 bytes from 10-244-1-10.open2-headless.dev.svc.cluster.local (10.244.1.10): icmp_seq=3 ttl=62 time=0.470 ms
--- open2-headless.dev.svc.cluster.local ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2000ms
rtt min/avg/max/mdev = 0.438/0.454/0.470/0.013 ms
4. 详细配置
apiVersion: v1
kind: Service
metadata:
name: open-headless
namespace: dev
spec:
clusterIP: None
ports:
- name: open-headless
port: 80
protocol: TCP
targetPort: 80
selector:
app: open-headless
type: ClusterIP
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: open-headless
namespace: dev
labels:
app: open-headless
spec:
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
app: open-headless
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
template:
metadata:
labels:
app: open-headless
spec:
containers:
- name: open-headless
image: openresty:1.21.4.1
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
name: open-headless
protocol: TCP
resources:
limits:
cpu: 1000m
memory: 1024Mi
requests:
cpu: 200m
memory: 128Mi
livenessProbe:
tcpSocket:
port: 80
initialDelaySeconds: 5
timeoutSeconds: 5
periodSeconds: 5
successThreshold: 1
failureThreshold: 5
readinessProbe:
tcpSocket:
port: 80
initialDelaySeconds: 5
timeoutSeconds: 5
periodSeconds: 5
successThreshold: 1
failureThreshold: 5