Service
Kubernetes 在设计之初就充分考虑了针对容器的服务发现与负载均衡机制,提供了 Service 资源,对于内部容器资源提供负载均衡机制,对外提供统一访问入口。
Service 是对一组提供相同功能的 Pods 的抽象,并为它们提供一个统一的入口。借助 Service,应用可以方便的实现服务发现与负载均衡,并实现应用的零宕机升级。Service 通过标签来选取服务后端,一般配合 Replication Controller 或者 Deployment 来保证后端容器的正常运行。这些匹配标签的 Pod IP 和端口列表组成 endpoints,由 kube-proxy 负责将服务 IP 负载均衡到这些 endpoints 上。 #通过标签选择对应的pod Service 有四种类型: ClusterIP:默认类型,自动分配一个仅 cluster 内部可以访问的虚拟 IP NodePort:在 ClusterIP 基础上为 Service 在每台机器上绑定一个端口,这样就可以通过 <NodeIP>:NodePort 来访问该服务。如果 kube-proxy 设置了 --nodeport-addresses=10.240.0.0/16(v1.10 支持),那么仅该 NodePort 仅对设置在范围内的 IP 有效。 LoadBalancer:在 NodePort 的基础上,借助 cloud provider 创建一个外部的负载均衡器,并将请求转发到 <NodeIP>:NodePort,依赖与云环境 ExternalName:将服务通过 DNS CNAME 记录方式转发到指定的域名(通过 spec.externlName 设定)。需要 kube-dns 版本在 1.7 以上。 另外,也可以将已有的服务以 Service 的形式加入到 Kubernetes 集群中来,只需要在创建 Service 的时候不指定 Label selector,而是在 Service 创建好后手动为其添加 endpoint。
Service 定义
ClusterIP
[root@kube test]# cat nginx-svc.yaml apiVersion: v1 kind: Service metadata: name: nginx-service namespace: default spec:
#默认ClusterIP 类型 ports: - port: 80 protocol: TCP targetPort: 80 selector: app: nginx type: ClusterIP [root@kube test]# kubectl apply -f nginx-svc.yaml [root@kube test]# kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 38d nginx-service ClusterIP 10.107.27.48 <none> 80/TCP 8s php-apache ClusterIP 10.97.196.195 <none> 80/TCP 86m [root@kube test]#
查看pods 标签
root@kube test]# kubectl get pods --show-labels NAME READY STATUS RESTARTS AGE LABELS liveness-exec 0/1 CrashLoopBackOff 8923 24d test=liveness liveness-http 1/1 Running 16 24d test=liveness nginx-deployment-5754944d6c-c797m 1/1 Running 0 3d22h app=nginx,pod-templ nginx-deployment-5754944d6c-jspst 1/1 Running 0 3d22h app=nginx,pod-templ nginx-deployment-5754944d6c-kkvx5 1/1 Running 0 3d22h app=nginx,pod-templ php-apache-d89b5f47b-dllcg 0/1 ImagePullBackOff 0 4h44m pod-template-hash=d pod-test 1/1 Running 0 31d app=myapp,tier=fron [root@kube test]#
NodePort:
[root@kube test]# cat nginx-svc-nodeport.yaml apiVersion: v1 kind: Service metadata: name: nginx-service-nodeport namespace: default spec: ports: - port: 80 protocol: TCP targetPort: 80 nodePort: 30080
//指定node 对外映射端口,确认没有被占用
selector: app: nginx type: NodePort [root@kube test]# kubectl get service NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 38d nginx-service ClusterIP 10.107.27.48 <none> 80/TCP 3h46m nginx-service-nodeport NodePort 10.109.241.119 <none> 80:30080/TCP 14m php-apache ClusterIP 10.97.196.195 <none> 80/TCP 5h12m [root@kube test]# curl http://10.2.61.22:30080 <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> body { 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> [root@kube test]#
[root@kube test]# kubectl describe service nginx-service-nodeport Name: nginx-service-nodeport Namespace: default Labels: <none> Annotations: kubectl.kubernetes.io/last-applied-configuration: {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"nginx-service-nodeport","namespace":"default"},"spec":{"ports":[{... Selector: app=nginx Type: NodePort IP: 10.109.241.119 Port: <unset> 80/TCP TargetPort: 80/TCP NodePort: <unset> 30080/TCP Endpoints: 10.244.1.7:80,10.244.2.28:80,10.244.2.29:80 Session Affinity: None External Traffic Policy: Cluster Events: <none> [root@kube test]#
不指定 Selectors 的服务
在创建 Service 的时候,也可以不指定 Selectors,用来将 service 转发到 kubernetes 集群外部的服务(而不是 Pod)。目前支持两种方法
(1)自定义 endpoint,即创建同名的 service 和 endpoint,在 endpoint 中设置外部服务的 IP 和端口
[root@kube test]# cat endpoint_demo.yaml kind: Service apiVersion: v1 metadata: name: my-service-end spec: ports: - protocol: TCP port: 80 targetPort: 8080 --- kind: Endpoints apiVersion: v1 metadata: name: my-service-end subsets: - addresses: - ip: 10.1.255.156 ports: - port: 8080 [root@kube test]# curl -I http://10.100.60.230 HTTP/1.1 403 Forbidden Server: nginx Date: Tue, 20 Aug 2019 08:49:01 GMT Content-Type: text/html; charset=utf-8 Connection: keep-alive Vary: Accept-Encoding [root@kube test]#
Headless 服务
Headless 服务即不需要 Cluster IP 的服务,即在创建服务的时候指定 spec.clusterIP=None
。包括两种类型
- 不指定 Selectors,但设置 externalName,即上面的(2),通过 CNAME 记录处理
- 指定 Selectors,通过 DNS A 记录设置后端 endpoint 列表
CNAME 方式
保留源 IP
各种类型的 Service 对源 IP 的处理方法不同:
- ClusterIP Service:使用 iptables 模式,集群内部的源 IP 会保留(不做 SNAT)。如果 client 和 server pod 在同一个 Node 上,那源 IP 就是 client pod 的 IP 地址;如果在不同的 Node 上,源 IP 则取决于网络插件是如何处理的,比如使用 flannel 时,源 IP 是 node flannel IP 地址。
- NodePort Service:默认情况下,源 IP 会做 SNAT,server pod 看到的源 IP 是 Node IP。为了避免这种情况,可以给 service 设置
spec.ExternalTrafficPolicy=Local
(1.6-1.7 版本设置 Annotationservice.beta.kubernetes.io/external-traffic=OnlyLocal
),让 service 只代理本地 endpoint 的请求(如果没有本地 endpoint 则直接丢包),从而保留源 IP。 - LoadBalancer Service:默认情况下,源 IP 会做 SNAT,server pod 看到的源 IP 是 Node IP。设置
service.spec.ExternalTrafficPolicy=Local
后可以自动从云平台负载均衡器中删除没有本地 endpoint 的 Node,从而保留源 IP。
工作原理
kube-proxy 负责将 service 负载均衡到后端 Pod 中,如下图所示