首先需要明确一件事,那就是Service、DNS与服务发现中说明的Service的访问信息只能在k8s集群当中使用。也可以说Service是集群当中的Pod互相访问使用的对象。
那么外界肯定是要与集群交互的,而我们当然也想要它能够通过Service来访问,这样才能够充分利用Service的功能。
所以,在使用 Kubernetes 的 Service 时,一个必须要面对和解决的问题就是:如何从外部(Kubernetes 集群之外),访问到 Kubernetes 里创建的 Service?。
而解决这个问题的常见方法就是==NodePort==。
NodePort
在Service的定义里,声明类型是type=NodePort
apiVersion: v1
kind: Service
metadata:
name: my-nginx
labels:
run: my-nginx
spec:
type: NodePort
ports:
- nodePort: 8080
targetPort: 80
protocol: TCP
name: http
- nodePort: 443
protocol: TCP
name: https
selector:
run: my-nginx在ports字段里声明了Service的8080端口代理Pod的80端口,Service的443端口代理Pod的443端口。如果你不显式地声明 nodePort 字段,Kubernetes 就会为你分配随机的可用端口来设置代理。这个端口的范围默认是 30000-32767,你可以通过 kube-apiserver 的–service-node-port-range 参数来修改它。
要想访问这个Service,只要
<任何一台宿主机的IP地址>:8080就可以访问到某一个被代理的Pod的80端口了。
NodePort模式就是在每台宿主机上生成这样一条iptables规则:
-A KUBE-NODEPORTS -p tcp -m comment --comment "default/my-nginx: nodePort" -m tcp --dport 8080 -j KUBE-SVC-67RL4FN6JRUPOJYMKUBE-SVC-67RL4FN6JRUPOJYM 其实就是一组随机模式的 iptables 规则。
NodePort模式下,k8s会在IP包离开宿主机发往Pod时,进行一次SNAT操作,将IP包的源IP地址替换成这台宿主机的IP或者cni网桥的IP。这是因为外界是与这个宿主机进行交互的,所以将IP包发送给Pod之后,响应需要先到达宿主机,再返回给外界客户端,否则外界客户端会发生错误。这种情景下也说明了
如果Pod需要明确知道请求来源,那么需要将 Service 的 spec.externalTrafficPolicy 字段设置为 local,这就保证了所有 Pod 通过 Service 收到请求之后,一定可以看到真正的、外部 client 的源地址。这个机制的实现原理也非常简单:这时候,一台宿主机上的 iptables 规则,会设置为只将 IP 包转发给运行在这台宿主机上的 Pod。所以这时候,Pod 就可以直接使用源地址将回复包发出,不需要事先进行 SNAT 了。这也意味着,如果请求的Node上没有代理的Pod,那么请求就会失败。
指定LoadBalancer类型的Service
这种方式适用于公有云上的k8s服务。
---
kind: Service
apiVersion: v1
metadata:
name: example-service
spec:
ports:
- port: 8765
targetPort: 9376
selector:
app: example
type: LoadBalancer公有云上使用了一个叫做CloudProvider 的转接层,这个转接层是用来跟公有云本身的API进行对接的。当创建了一个LoadBalancer类型的Service后,k8s就会调用CloudProvider的接口在公有云上创建一个负载均衡服务,并把被代理的Pod的IP地址配置给负载均衡服务。
其实就是在公有云上启动了一个负载均衡服务,让外界能够通过这个服务获取到被Service代理的Pod的IP列表。
ExternalName
这是k8s在1.7之后推出的新特性。
kind: Service
apiVersion: v1
metadata:
name: my-service
spec:
type: ExternalName
externalName: my.database.example.com这里不需要指定selector。ExternalName 类型的 Service,其实是在 kube-dns 里为你添加了一条 CNAME 记录。这时,访问 my-service.default.svc.cluster.local 就和访问 my.database.example.com 这个域名是一个效果了。
相当于一个外部可以访问的DNS域名别名。还可以为Service分配共有IP地址。比如下面:
kind: Service
apiVersion: v1
metadata:
name: my-service
spec:
selector:
app: MyApp
ports:
- name: http
protocol: TCP
port: 80
targetPort: 9376
externalIPs:
- 80.11.12.10可以通过访问 80.11.12.10:80 访问到被代理的 Pod 了。不过,在这里 Kubernetes 要求 externalIPs 必须是至少能够路由到一个 Kubernetes 的节点,不然如何与k8s的Node通信。