有几种方式可以在外部访问服务:
- 将service type设置成NodePort——对于NodePort服务,每个集群节点在节点本身(因此得名叫NodePort)上打开一个端口,并将在该端口上接收到的流量重定向到服务。该服务仅从内部集群IP和端口上才可访问,但也可通过所有节点上的专用端口访问。
- 将service type设置成LoadBalancer, NodePort类型的一种扩展——这使得服务可以通过一个专用的负载均衡器来访问,这是由Kubernetes中正在运行的云基础设施提供的。负载均衡器将流量重定向到跨所有节点的节点端口。客户端通过负载均衡器的 IP 连接到服务。
- 创建一个Ingress资源,这是一个完全不同的机制,通过一个IP地址公开多个服务——它运行在HTTP层(网络协议第7层)上,因此可以提供比工作在第4层的服务更多的功能。
5.3.1 使用 NodePort 类型的服务
通过创建NodePort服务,可以让Kubernetes在其所有节点上保留一个端口(使用相同的端口号),并将传入的连接转发给作为服务一部分的pod。
指定节点端口不是强制性的。 如果忽略它,Kubernetes将选择一个随机端口。
创建NodePort类型的服务
指定节点端口不是强制性的。 如果忽略它,Kubernetes将选择一个随机端口。
cd /root/k8s
kubectl create -f kubia-rc.yaml
cat >kubia-svc-nodeport.yaml <<'EOF'
apiVersion: v1
kind: Service
metadata:
name: kubia-nodeport
spec:
type: NodePort
ports:
- port: 80
targetPort: 8080
nodePort: 30123
selector:
app: kubia
EOF
kubectl create -f kubia-svc-nodeport.yaml
查看NodePort类型的服务
看看EXTERNAL-IP列。它显示nodes, 表明服务可通过任何集群节点的IP地址访问。 PORT(S)列显示集群IP的内部端口(80)和节点端口(30123), 可以通过以下地址访问该服务:
- CLUSTER-IP:80
- <任意节点的IP>:30123
本人环境输出中,EXTERNAL-IP为none,但是还是可以使用节点的局域网IP:30123访问服务。
查看任意节点上的端口信息
在第一个节点的端口30123收到的连接, 可能被转发到第一节点上运行的pod, 也可能是第二个节点上运行的pod。
使用JSONPath获取所有节点的 ExternalIP
kubectl get nodes -o jsonpath='{.items[*].status.addresses[?(@.type=="ExternalIP")].address}'
130.211.97.55 130.211.99.206
要了解有关kubectl使用JSONPath的更多信息,请参阅http://kubernetes.io/docs/user-guide/jsonpath上的文档。
知道节点的ExternalIP之后,互联网可以通过任何节点上的30123端口访问到集群中的pod。那么当客户端指向的节点发生故障,客户端无法再访问该服务,这就是为什么将load balancer放在节点前面以确保发送的请求转发到健康节点。
5.3.2 通过外部负载均衡器暴露服务
在云提供商上运行的Kubernetes集群通常支持从云基础设施自动提供负载平衡器。 所需要做的就是设置服务的类型为Load Balancer而不是NodePort。 负载均衡器拥有自己独一无二的可公开访问的 IP 地址,并将所有连接重定向到服务。可以通过负载均衡器的 IP 地址访问服务。
如果Kubemetes在不支持Load Balancer服务的环境中运行, 则不会提供负载均衡器, 但该服务仍将表现得像一个NodePort服务。 这是因为Load Balancer服务是NodePort服务的扩展。
创建LoadBalancer服务
创建一个前面带有负载均衡器的服务:这种类型的服务是从集群所在的云基础设施获取负载均衡器的。
这里没有指定特定的节点端口(node port),Kubernetes将会自己选择一个端口。
cd /root/k8s
kubectl create -f kubia-rc.yaml
cat >kubia-svc-loadbalancer.yaml <<'EOF'
apiVersion: v1
kind: Service
metadata:
name: kubia-loadbalancer
spec:
type: LoadBalancer
ports:
- port: 80
targetPort: 8080
selector:
app: kubia
EOF
kubectl create -f kubia-svc-loadbalancer.yaml
本人环境没有云基础设施,获取不了负载均衡器的外部IP:
通过负载均衡器连接
创建该服务后,云基础设施需要一段时间才能创建负载均衡器并将其 IP 地址写入Service对象。 一旦这样做了,IP 地址将被列为服务的外部 IP 地址:
之后可以使用外部IP地址(load balancer)访问内部服务了:
会话亲和性和Web浏览器
使用kubectl describe svc检查Session Affinity值为none,那么为什么对服务发起的不同浏览器请求总是命中到同一个pod,而curl不是这样呢? 浏览器使用keep-alive连接,并通过单个连接发送所有http请求,而curl每次都打开一个新连接。Service工作在连接级别(4层?),所以当浏览器首次打开与服务的连接时,会选择一个随机的pod,然后将属于该连接的所有网络数据包全部发送到那个pod。即使session affinity设置为None,用户也会总是命令同一个pod(直到连接关闭)。
了解 HTTP 请求如何传递到 pod
外部客户端(可以使用curl)连接到负载均衡器的80端口,并路由到其中一个节点上的隐式分配的节点端口。之后该连接被转发到一个pod实例。
5.3.3 了解外部连接的特性
了解并防止不必要的网络跳数
当外部客户端通过节点端口连接到服务时(这也包括先通过负载均衡器时的情况), 随机选择的pod并不一定在接收连接的同一节点上运行。 可能需要额外的网络跳转才能到达pod, 但这种行为并不符合期望。
可以通过将服务配置为仅将外部流量重定向到接收连接的节点上运行的pod来阻止此额外跳数。 这是通过在服务的spec部分中设置 externalTrafficPolicy字段来完成的:
spec:
externalTrafficPolicy: Local
…
如果服务定义文件包含此设置, 并且通过服务的节点端口打开外部连接, 则服务代理将选择本地运行的pod。 如果没有本地pod存在, 则连接将挂起。因此,需要确保负载平衡器将连接转发给至少具有一个这种pod的节点。
其次,使用这个配置,可能使对pod的连接变得不均匀。比如三个pod两个节点的情况。
客户端IP不被记录
通常,当集群内的客户端连接到服务时, 服务后端的pod可以获取客户端的IP地址 。 但是,当通过节点端口接收到连接时, 由于对数据包执行了源网络地址转换(SNAT), 因此数据包的源IP将发生更改。
后端的pod无法看到实际的客户端IP。比如:Web客户器的access log就无法记录浏览器的IP。
externalTrafficPolicy: Local 配置会影响客户端IP的保留,因为在接收连接的节点和托管目标pod的节点之间没有额外的跳转(不执行SNAT)。