k8s的无头服务

Headless Services是一种特殊的service,其spec:clusterIP表示为None,这样在实际运行时就不会被分配ClusterIP,也被称为无头服务,通过DNS解析提供服务发现。与普通服务不同的是Headless Services不提供负载均衡功能,每个Pod都有唯一的DNS记录,直接映射到其IP地址,适用于有状态应用的场景,如与StatefulSet一起部署数据库。这种服务使得直接访问单个Pod成为可能,而不经过负载均衡器。

因为 Headless Service 属于 Service ClusterIp 类型,所以在讲解Headless Service前,先简单说下 Service 和服务发现。

构建镜像

[root@chenby ~]# cat > Dockerfile <<EOF
FROM nginx
RUN echo '这是一个本地构建的nginx镜像,第一版' > /usr/share/nginx/html/index.html
EOF

docker build -t z.oiox.cn:18082/library/cby:v1 .

docker push z.oiox.cn:18082/library/cby:v1

编写yaml文件

我这里只是创建了一个最简单的容器,由StatefulSet控制器来管理,同时创建了无头服务的svc

cat > cby.yaml <<EOF
apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None   #这使得服务成为无头服务
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  serviceName: "nginx"
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: z.oiox.cn:18082/library/cby:v1
        ports:
        - containerPort: 80
          name: web
EOF

查看已经创建的资源

[root@k8s-master01 ~]# kubectl get statefulsets
NAME   READY   AGE
web    2/2     12m
[root@k8s-master01 ~]# 
[root@k8s-master01 ~]# kubectl get pod 
NAME                            READY   STATUS    RESTARTS      AGE
web-0                           1/1     Running   0             12m
web-1                           1/1     Running   0             12m
[root@k8s-master01 ~]# 
[root@k8s-master01 ~]# kubectl get svc
NAME           TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
nginx          ClusterIP   None             <none>        80/TCP     12m
[root@k8s-master01 ~]# 

修改web-1的html内容

statefulsets控制器是可以将存储持久化的,我这里没做存储持久化,这里就进入容器内进行修改页面信息

kubectl exec web-1 -- sh -c 'echo 这是一个本地构建的nginx镜像,第二版 > /usr/share/nginx/html/index.html'

测试修改是否成功

[root@k8s-master01 ~]# kubectl get pod -o wide |grep web
web-0                           1/1     Running   0             40m   10.0.0.28    k8s-node02   <none>           <none>
web-1                           1/1     Running   0             40m   10.0.3.243   k8s-node01   <none>           <none>
[root@k8s-master01 ~]# 
[root@k8s-master01 ~]# 
[root@k8s-master01 ~]# curl 10.0.0.28
这是一个本地构建的nginx镜像,第一版
[root@k8s-master01 ~]# 
[root@k8s-master01 ~]# curl 10.0.3.243
这是一个本地构建的nginx镜像,第二版
[root@k8s-master01 ~]# 
[root@k8s-master01 ~]# 

查看svc的详细

这里可以看到Endpoints已经关联到了后端的pod容器

[root@k8s-master01 ~]# kubectl describe svc nginx
Name:                     nginx
Namespace:                default
Labels:                   app=nginx
Annotations:              <none>
Selector:                 app=nginx
Type:                     ClusterIP
IP Family Policy:         SingleStack
IP Families:              IPv4
IP:                       None
IPs:                      None
Port:                     web  80/TCP
TargetPort:               80/TCP
Endpoints:                10.0.0.28:80,10.0.3.243:80
Session Affinity:         None
Internal Traffic Policy:  Cluster
Events:                   <none>
[root@k8s-master01 ~]#

创建busybox测试容器

cat<<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: busybox
  namespace: default
spec:
  containers:
  - name: busybox
    image: docker-ce.chenby.cn/library/busybox:1.28
    command:
      - sleep
      - "3600"
    imagePullPolicy: IfNotPresent
  restartPolicy: Always
EOF

进入测试容器

[root@k8s-master01 ~]# kubectl exec -ti busybox -- sh
/ # 
/ # 
/ # ping nginx.default.svc.cluster.local 
PING nginx.default.svc.cluster.local (10.0.0.28): 56 data bytes
64 bytes from 10.0.0.28: seq=0 ttl=63 time=0.066 ms
64 bytes from 10.0.0.28: seq=1 ttl=63 time=0.077 ms
64 bytes from 10.0.0.28: seq=2 ttl=63 time=0.070 ms
^C
--- nginx.default.svc.cluster.local ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.066/0.071/0.077 ms
/ # 
/ # 
/ # 
/ # ping web-0.nginx.default.svc.cluster.local 
PING web-0.nginx.default.svc.cluster.local (10.0.0.28): 56 data bytes
64 bytes from 10.0.0.28: seq=0 ttl=63 time=0.046 ms
64 bytes from 10.0.0.28: seq=1 ttl=63 time=0.079 ms
64 bytes from 10.0.0.28: seq=2 ttl=63 time=0.064 ms
^C
--- web-0.nginx.default.svc.cluster.local ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.046/0.063/0.079 ms
/ # 
/ # 
/ # 
/ # ping web-1.nginx.default.svc.cluster.local 
PING web-1.nginx.default.svc.cluster.local (10.0.3.243): 56 data bytes
64 bytes from 10.0.3.243: seq=0 ttl=63 time=0.369 ms
64 bytes from 10.0.3.243: seq=1 ttl=63 time=0.373 ms
64 bytes from 10.0.3.243: seq=2 ttl=63 time=0.328 ms
^C
--- web-1.nginx.default.svc.cluster.local ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.328/0.356/0.373 ms
/ # 
/ # 
/ # 
/ # 
/ # cat /etc/hosts
# Kubernetes-managed hosts file.
127.0.0.1    localhost
::1    localhost ip6-localhost ip6-loopback
fe00::0    ip6-localnet
fe00::0    ip6-mcastprefix
fe00::1    ip6-allnodes
fe00::2    ip6-allrouters
10.0.0.238    busybox
/ # 
/ # 
/ # 
/ # 

进行访问性测试

[root@k8s-master01 ~]# kubectl exec -ti nginx-demo-cccbdc67f-6nkgd -- sh
/ # 
/ # 
/ # curl nginx.default.svc.cluster.local
这是一个本地构建的nginx镜像,第二版
/ # curl nginx.default.svc.cluster.local
这是一个本地构建的nginx镜像,第一版
/ # 
/ # 
/ # 
/ # curl web-0.nginx.default.svc.cluster.local 
这是一个本地构建的nginx镜像,第一版
/ # curl web-0.nginx.default.svc.cluster.local
这是一个本地构建的nginx镜像,第一版
/ # curl web-0.nginx.default.svc.cluster.local
这是一个本地构建的nginx镜像,第一版
/ # 
/ # 
/ # 
/ # curl web-1.nginx.default.svc.cluster.local 
这是一个本地构建的nginx镜像,第二版
/ # curl web-1.nginx.default.svc.cluster.local
这是一个本地构建的nginx镜像,第二版
/ # curl web-1.nginx.default.svc.cluster.local
这是一个本地构建的nginx镜像,第二版

总结

在某些场景中,无需对外提供访问能力,只需要在内部找到自己想找到的Pod资源时,可以通过Headless Service来实现。这种不具有ClusterIP的Service资源就是Headless Service,该 Service 的请求流量不需要 kube-proxy 处理,也不会有负载均衡和路由规则,而是由ClusterDNS的域名解析机制直接去访问固定的Pod资源。

既然是Headless Service,那首先它是Service,一般的Service能被内部和外部访问。之所以叫Headless Service,是因为只对内提供访问,既然只对内访问,那肯定就需要提供稳定的访问能力了,否则就没什么作用了。比如说拥有固定的Pod名称和存储,所以一般会结合StatefulSet一起使用,用来部署有状态的应用。

如果想让部署的有状态应用暴露给集群外部客户端访问的话,可以新建个普通(有ClusterIP)的服务,通过标签选择关联有状态服务实例。

关于

https://www.oiox.cn/

https://www.oiox.cn/index.php/start-page.html

CSDN、GitHub、51CTO、知乎、开源中国、思否、掘金、简书、华为云、阿里云、腾讯云、哔哩哔哩、今日头条、新浪微博、个人博客

全网可搜《小陈运维》

文章主要发布于微信公众号


小陈运维
27 声望9 粉丝