K8S-Serivce的原理和实践

一、什么是service?

Service服务(微服务)是 Kubernetes 的核心资源之一,它定义了一个服务的访问入口地址,每一个Service后面由一组Pod来提供支持,通过 Kube-Proxy (智能负载均衡器)的 port 和Service label selector 决定服务请求传递给后端的Pod,外部不需要了解后端如何运行,这给扩展或维护后端带来很大的好处,如下图1所示:

K8S-Serivce的原理和实践

 

图片1:来源于K8S权威指南第四版

二、为什么需要service?

  在集群内部,直接通过Pod(K8S部署的最小的单元)的IP地址和端口也可以访问到容器应用,但是Pod的IP并非固定的,当因异常情况导致的Pod重启或者被调度到其他node节点,其IP地址会发生变化,因此直接通过Pod的IP和端口访问服务是不可靠的,另外,在实际的应用中,通常会是多个Pod实例提供服务,需要在这些实例的前面设置一个负载均衡器来实现请求到后端Pod的转发,Service就是为解决这些问题而设计的,它可以屏蔽后端Pod的变化,提供集群内部唯一固定的虚拟IP——Cluster IP,可以通过Cluster IP加端口来访问服务,另外请求会通过负载均衡器(Kube-Proxy )自动转发到后端Pod,如图2所示

K8S-Serivce的原理和实践

图片2:来源于网络素材

三、service的使用场景

Service有多种类型,不同类型面对不同应用场景.

1、Cluster IP:集群默认类型,为集群内部提供一个唯一的虚拟IP,集群内部可以相互访问,但是集群外部无法进行访问,如果提供的服务只需要在集群内部进行访问,可采用此类,Cluster IP支持2种Service如下

1)普通Service:Cluster IP非NONE,服务接受到请求,通过kube-proxy负载均衡转发后,只会返回后端其中1个Pod的IP和端口(注意是1个),如果通过DNS解析Service的名称,会发现只会返回1个IP,且是此Service的cluster-ip,后文会有验证

2)Headless Service:Cluster IP为NONE,在某些场景中,开发人员希望自己可以控制负载均衡器,不采用kube-proxy作为负载均衡器、或者希望返回同组Pod的所有Pod,Headless Service适用于此类场景,Cluster IP设置为NONE,kube-proxy将不会进行转发,

也不会有具体的Cluster IP地址,对它的访问,将会返回所有Pod IP的1个列表,然后由客户端程序自行决定如何处理Pod列表。

2、NodePort:适用于想从集群外部访问Service,需要将type设置为nodeport类型和指定node的port,设置成功后,用户可通过集群内部任何一台nodeIP:nodeport进行访问

3、LoadBalance:通过设置LoadBalance映射到公有云服务提供的LoadBalance地址,对Service的访问将会转发到后端Pod上,而具体的负载均衡分发的方式由云服务商提供的LoadBalance的实现机制。

四、创建和使用service

1、普通Service类型

创建Service

 

[root@k8s-master zhanglei]# cat svc-clusterip.yaml 
apiVersion: v1
kind: Service
metadata:
  name: nginx-svc-ord
  labels:
    app: nginx
spec:
  type: ClusterIP          # 指定service的类型
  ports:
    - port: 80             # 指定service的端口
      targetPort: 80       # 指定目标Pod端口
  selector:  
    app: nginx-deployment-ord  # 此处就是与Pod建立关联的地方,对service的访问会转发到拥有此标签的Pod

查看Service

[root@k8s-master zhanglei]# kubectl get svc nginx-svc-ord -o wide
NAME            TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE     SELECTOR
nginx-svc-ord   ClusterIP   10.10.30.161   <none>        80/TCP    4d15h   app=nginx-deployment-ord

可以看到,在创建的时候,并未指定Cluster-IP具体是多少,创建完成之后,系统会自动生成一个10.10.30.161,可以根据自己的需求,是否手动设置具体Cluster-IP,这是一个虚拟的IP,非物理的IP,可以通过ping验证下

[root@k8s-master zhanglei]# ping 10.10.30.161
PING 10.10.30.161 (10.10.30.161) 56(84) bytes of data.
^C
--- 10.10.30.161 ping statistics ---
12 packets transmitted, 0 received, 100% packet loss, time 288ms

[root@k8s-master zhanglei]# ping www.baidu.com
PING www.a.shifen.com (14.215.177.39) 56(84) bytes of data.
64 bytes from 14.215.177.39 (14.215.177.39): icmp_seq=1 ttl=128 time=12.2 ms
64 bytes from 14.215.177.39 (14.215.177.39): icmp_seq=2 ttl=128 time=54.5 ms
^C
--- www.a.shifen.com ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 2ms
rtt min/avg/max/mdev = 12.231/33.353/54.476/21.123 ms

可以看到是10.10.30.161这个虚拟的IP是ping不通的,在看下详细Service信息

[root@k8s-master zhanglei]# kubectl describe svc nginx-svc-ord 
Name:              nginx-svc-ord
Namespace:         default
Labels:            app=nginx       # Sercice自己的标签
Annotations:       <none>
Selector:          app=nginx-deployment-ord   # 指定后端关联的Pod标签
Type:              ClusterIP
IP:                10.10.30.161
Port:              <unset>  80/TCP
TargetPort:        80/TCP
Endpoints:         10.122.235.240:80,10.122.235.241:80,10.122.235.242:80
Session Affinity:  None
Events:            <none>

 看到Endpoints这里有3组IP和端口,这里记录的是serivce和后端Pod的的对应表,那大家可能好奇了,这个具体是如何生成的,接下来看下如何和Pod关联的

[root@k8s-master zhanglei]# cat dep-ord.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment-ord
  namespace: default
  labels:
    app: nginx-deployment
spec:
  replicas: 3                          # 指定副本数为3,创建成功后
  selector:
    matchLabels:
      app: nginx-deployment-ord
  template:                           # 设置的是Pod模板
    metadata:
      labels:
        app: nginx-deployment-ord    # Pod的标签,这个标签需要和上面副本控制器matchLabels、service的selector保持一致
    spec:
      containers:
      - name: nginx                  # 指定Pod中容器镜像
        image: nginx:latest
        ports:
        - containerPort: 80          # 指定容器的端口

副本控制器deployment可以保证用户期望的实例数,如replicas设置的是3,如删除了Pod,副本控制器会一直尝试拉起新的Pod来保证期望数,关于更多deployment的介绍,在另外的文章再继续深究,deployment新建成功后,就可以通过service访问后端的Pod了

[root@k8s-master zhanglei]# curl 10.10.30.161:80                      # curl cluster-ip:port
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

可以看到,通过service可以访问到后端Pod的nginx容器,再解析下具体这个service的域名

[root@k8s-master zhanglei]# dig -t A nginx-svc-ord.default.svc.cluster.local. @10.10.0.12    # @后面是kube-dns的服务器地址     

; <<>> DiG 9.11.4-P2-RedHat-9.11.4-26.P2.el8 <<>> -t A nginx-svc-ord.default.svc.cluster.local. @10.10.0.12
;; 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: 17934
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; COOKIE: 5c8fb317c825d518 (echoed)
;; QUESTION SECTION:
;nginx-svc-ord.default.svc.cluster.local. IN A

;; ANSWER SECTION:
nginx-svc-ord.default.svc.cluster.local. 30 IN A 10.10.30.161     #解析成功,返回了域名对应的Cluster-ip

;; Query time: 23 msec
;; SERVER: 10.10.0.12#53(10.10.0.12)
;; WHEN: 六 6月 06 16:38:22 CST 2020
;; MSG SIZE  rcvd: 135

dig -t A nginx-svc-ord.default.svc.cluster.local. @10.10.0.10,nginx-svc-ord为service的名称,default为命名空间的名称,若未对集群重命名,则默认集群默认的名称为cluster,@后面是kube-dns服务器地址

2、Headless Service

创建一个Headless Service,其后端同样指向含有nginx-deployment-ord 标签的Pod

[root@k8s-master zhanglei]# cat svc-headless-dep.yaml 
apiVersion: v1
kind: Service
metadata:
  name: myapp-headless-service-dep
  labels:
    app: deployment
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: nginx-deployment-ord      
[root@k8s-master zhanglei]# kubectl create -f svc-headless-dep.yaml
service/myapp-headless-service-dep created
[root@k8s-master zhanglei]# kubectl get svc -o wide|grep myapp-headless-service-dep
myapp-headless-service-dep   ClusterIP   None           <none>        80/TCP    3m36s   app=nginx-deployment-ord

通过查看,可以看到Headless Service的ClusterIP为None,查看下详细信息

[root@k8s-master zhanglei]# kubectl describe svc myapp-headless-service-dep 
Name:              myapp-headless-service-dep
Namespace:         default
Labels:            app=deployment
Annotations:       <none>
Selector:          app=nginx-deployment-ord
Type:              ClusterIP
IP:                None
Port:              web  80/TCP
TargetPort:        80/TCP
Endpoints:         10.122.235.240:80,10.122.235.241:80,10.122.235.242:80
Session Affinity:  None
Events:            <none>

可以看到其Endpoints所对应的列表与前面nginx-svc-ord的Endpoints是一样的,也就是说,支持多个service通过Selector关联到后端同一组Pod,解析下myapp-headless-service-dep 

[root@k8s-master zhanglei]# dig -t A myapp-headless-service-dep.default.svc.cluster.local. @10.10.0.10

; <<>> DiG 9.11.4-P2-RedHat-9.11.4-26.P2.el8 <<>> -t A myapp-headless-service-dep.default.svc.cluster.local. @10.10.0.10
;; 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: 2879
;; 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
; COOKIE: 52294ca2d8aec856 (echoed)
;; QUESTION SECTION:
;myapp-headless-service-dep.default.svc.cluster.local. IN A

;; ANSWER SECTION:                                                                            # 返回了后端所有的Pod,这里是区别普通service,普通service返回的Cluster-ip
myapp-headless-service-dep.default.svc.cluster.local. 30 IN A 10.122.235.241
myapp-headless-service-dep.default.svc.cluster.local. 30 IN A 10.122.235.240
myapp-headless-service-dep.default.svc.cluster.local. 30 IN A 10.122.235.242

;; Query time: 0 msec
;; SERVER: 10.10.0.10#53(10.10.0.10)
;; WHEN: 六 6月 06 16:57:12 CST 2020
;; MSG SIZE  rcvd: 297

可以看到这里解析返回了后端所有的Pod的IP,这里是区别普通service,普通service返回的Cluster-ip,拿到这个列表之后,可以做进一步的处理,有状态负载(statefulset)会给每个Pod配置DNS,结合返回所有的Pod,不同的Pod之间就可以通过域名互相访问了

,这里后续会在statefulset详细讲。

3、NodePort

创建一个type为nodeport类型的service

[root@k8s-master zhanglei]# cat svc-nodeport-dep.yaml
apiVersion: v1
kind: Service
metadata:
  name: nginx-service-nodeport
spec:
  selector:
      app: nginx-deployment-ord  
  ports:
    - name: http
      port: 8000               # service端口1
      protocol: TCP            # 指定端口的协议
      targetPort: 80           # 指定容器的端口
    - name: https
      port: 8443               # service端口2
      protocol: TCP            
      targetPort: 443
  type: NodePort      # 指定NodePort类型
[root@k8s-master zhanglei]# kubectl create -f svc-nodeport-dep.yaml 
service/nginx-service-nodeport created
[root@k8s-master zhanglei]# kubectl get svc nginx-service-nodeport  -o wide
NAME                     TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)                         AGE   SELECTOR
nginx-service-nodeport   NodePort   10.10.38.153   <none>        8000:31128/TCP,8443:31642/TCP   48s   app=nginx-deployment-ord
[root@k8s-master zhanglei]# curl 192.168.126.129: 31128   # 192.168这个为node节点的IP,nodeport范围为:30000~32767
curl: (7) Failed to connect to 192.168.126.129 port 80: Connection refused
curl: (7) Couldn't connect to server
[root@k8s-master zhanglei]# curl 192.168.126.129:31128
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

这里nodeport的端口范围必须是在30000~32767范围之内,建议在创建的不用手动设置指定此端口,让系统自动生成,可以避免端口冲突,我们也可以在宿主机的浏览器上输入nodeip:nodeport进行访问测试,如下图所示:

K8S-Serivce的原理和实践

 

六、总结

从上文可以看出,Service是K8S中非常核心的一个资源,是整个集群应用访问的入口,通过灵活的配置Service和副本控制器,可以很好地满足各种部署应用的场景。

 

作者简介:云计算容器\K8S方向产品经理,学点技术,为更好地设计产品。

上一篇:《自拍教程62》Python adb反复断网联网测试


下一篇:k8s跨namespace访问服务