一、什么是service?
Service服务(微服务)是 Kubernetes 的核心资源之一,它定义了一个服务的访问入口地址,每一个Service后面由一组Pod来提供支持,通过 Kube-Proxy (智能负载均衡器)的 port 和Service label selector 决定服务请求传递给后端的Pod,外部不需要了解后端如何运行,这给扩展或维护后端带来很大的好处,如下图1所示:
图片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所示
图片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进行访问测试,如下图所示:
六、总结
从上文可以看出,Service是K8S中非常核心的一个资源,是整个集群应用访问的入口,通过灵活的配置Service和副本控制器,可以很好地满足各种部署应用的场景。
作者简介:云计算容器\K8S方向产品经理,学点技术,为更好地设计产品。