Update source-ip documentation (#18760)
parent
584ee6687f
commit
872dc0b8a1
|
@ -7,7 +7,7 @@ content_template: templates/tutorial
|
|||
{{% capture overview %}}
|
||||
|
||||
|
||||
Kubernetes 集群中运行的应用通过抽象的 Service 查找彼此,相互通信和连接外部世界。本文揭示了发送到不同类型 Services 的数据包源 IP 的内幕,你可以根据需求改变这个行为。
|
||||
Kubernetes 集群中运行的应用通过 Service 抽象来互相查找、通信和与外部世界沟通。本文介绍被发送到不同类型 Services 的数据包源 IP 的变化过程,你可以根据你的需求改变这些行为。
|
||||
|
||||
{{% /capture %}}
|
||||
|
||||
|
@ -31,11 +31,14 @@ Kubernetes 集群中运行的应用通过抽象的 Service 查找彼此,相互
|
|||
## 准备工作
|
||||
|
||||
|
||||
你必须拥有一个正常工作的 Kubernetes 1.5 集群,用来运行本文中的示例。该示例使用一个简单的 nginx webserver 回送它接收到的请求的 HTTP 头中的源 IP 地址。你可以像下面这样创建它:
|
||||
你必须拥有一个正常工作的 Kubernetes 1.5 集群来运行此文档中的示例。该示例使用一个简单的 nginx webserver,通过一个HTTP消息头返回它接收到请求的源IP。你可以像下面这样创建它:
|
||||
|
||||
```console
|
||||
$ kubectl run source-ip-app --image=k8s.gcr.io/echoserver:1.4
|
||||
deployment "source-ip-app" created
|
||||
kubectl run source-ip-app --image=k8s.gcr.io/echoserver:1.4
|
||||
```
|
||||
输出结果为
|
||||
```
|
||||
deployment.apps/source-ip-app created
|
||||
```
|
||||
|
||||
{{% /capture %}}
|
||||
|
@ -45,7 +48,7 @@ deployment "source-ip-app" created
|
|||
|
||||
* 通过多种类型的 Services 暴露一个简单应用
|
||||
* 理解每种 Service 类型如何处理源 IP NAT
|
||||
* 理解保留源 IP 的折中
|
||||
* 理解保留源IP所涉及的折中
|
||||
|
||||
{{% /capture %}}
|
||||
|
||||
|
@ -59,33 +62,50 @@ deployment "source-ip-app" created
|
|||
如果你的 kube-proxy 运行在 [iptables 模式](/docs/user-guide/services/#proxy-mode-iptables)下,从集群内部发送到 ClusterIP 的包永远不会进行源地址 NAT,这从 Kubernetes 1.2 开始是默认选项。Kube-proxy 通过一个 `proxyMode` endpoint 暴露它的模式。
|
||||
|
||||
```console
|
||||
$ kubectl get nodes
|
||||
NAME STATUS AGE VERSION
|
||||
kubernetes-minion-group-6jst Ready 2h v1.6.0+fff5156
|
||||
kubernetes-minion-group-cx31 Ready 2h v1.6.0+fff5156
|
||||
kubernetes-minion-group-jj1t Ready 2h v1.6.0+fff5156
|
||||
|
||||
kubernetes-minion-group-6jst $ curl localhost:10249/proxyMode
|
||||
kubectl get nodes
|
||||
```
|
||||
输出结果与以下结果类似:
|
||||
```
|
||||
NAME STATUS ROLES AGE VERSION
|
||||
kubernetes-node-6jst Ready <none> 2h v1.13.0
|
||||
kubernetes-node-cx31 Ready <none> 2h v1.13.0
|
||||
kubernetes-node-jj1t Ready <none> 2h v1.13.0
|
||||
```
|
||||
从其中一个节点中得到代理模式
|
||||
```console
|
||||
kubernetes-node-6jst $ curl localhost:10249/proxyMode
|
||||
```
|
||||
输出结果为:
|
||||
```
|
||||
iptables
|
||||
```
|
||||
|
||||
|
||||
你可以通过在 source IP 应用上创建一个服务来测试源 IP 保留。
|
||||
你可以通过在source IP应用上创建一个Service来测试源IP保留。
|
||||
|
||||
```console
|
||||
$ kubectl expose deployment source-ip-app --name=clusterip --port=80 --target-port=8080
|
||||
service "clusterip" exposed
|
||||
|
||||
$ kubectl get svc clusterip
|
||||
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
|
||||
clusterip 10.0.170.92 <none> 80/TCP 51s
|
||||
kubectl expose deployment source-ip-app --name=clusterip --port=80 --target-port=8080
|
||||
```
|
||||
输出结果为:
|
||||
```
|
||||
service/clusterip exposed
|
||||
```
|
||||
```console
|
||||
kubectl get svc clusterip
|
||||
```
|
||||
输出结果与以下结果类似:
|
||||
```
|
||||
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
|
||||
clusterip ClusterIP 10.0.170.92 <none> 80/TCP 51s
|
||||
```
|
||||
|
||||
|
||||
从相同集群中的一个 pod 访问这个 `ClusterIP`:
|
||||
|
||||
```console
|
||||
$ kubectl run busybox -it --image=busybox --restart=Never --rm
|
||||
kubectl run busybox -it --image=busybox --restart=Never --rm
|
||||
```
|
||||
输出结果与以下结果类似:
|
||||
```
|
||||
Waiting for pod default/busybox to be running, status is Pending, pod ready: false
|
||||
If you don't see a command prompt, try pressing enter.
|
||||
|
||||
|
@ -116,21 +136,29 @@ command=GET
|
|||
## Type=NodePort 类型 Services 的 Source IP
|
||||
|
||||
|
||||
对于 Kubernetes 1.5,发送给类型为 [Type=NodePort](/docs/user-guide/services/#type-nodeport) Services 的数据包默认进行源地址 NAT。你可以创建一个 `NodePort` Service 来进行测试:
|
||||
从 Kubernetes 1.5 开始,发送给类型为 [Type=NodePort](/docs/user-guide/services/#type-nodeport) Services 的数据包默认进行源地址 NAT。你可以通过创建一个 `NodePort` Service 来进行测试:
|
||||
|
||||
```console
|
||||
$ kubectl expose deployment source-ip-app --name=nodeport --port=80 --target-port=8080 --type=NodePort
|
||||
service "nodeport" exposed
|
||||
kubectl expose deployment source-ip-app --name=nodeport --port=80 --target-port=8080 --type=NodePort
|
||||
```
|
||||
输出结果为:
|
||||
```
|
||||
service/nodeport exposed
|
||||
```
|
||||
|
||||
$ NODEPORT=$(kubectl get -o jsonpath="{.spec.ports[0].nodePort}" services nodeport)
|
||||
$ NODES=$(kubectl get nodes -o jsonpath='{ $.items[*].status.addresses[?(@.type=="ExternalIP")].address }')
|
||||
```console
|
||||
NODEPORT=$(kubectl get -o jsonpath="{.spec.ports[0].nodePort}" services nodeport)
|
||||
NODES=$(kubectl get nodes -o jsonpath='{ $.items[*].status.addresses[?(@.type=="ExternalIP")].address }')
|
||||
```
|
||||
|
||||
如果你的集群运行在一个云服务上,你可能需要为上面报告的 `nodes:nodeport` 开启一条防火墙规则。
|
||||
现在,你可以通过上面分配的节点端口从外部访问这个 Service。
|
||||
|
||||
```console
|
||||
$ for node in $NODES; do curl -s $node:$NODEPORT | grep -i client_address; done
|
||||
for node in $NODES; do curl -s $node:$NODEPORT | grep -i client_address; done
|
||||
```
|
||||
输出结果与以下结果类似:
|
||||
```
|
||||
client_address=10.180.1.1
|
||||
client_address=10.240.0.5
|
||||
client_address=10.240.0.3
|
||||
|
@ -146,7 +174,7 @@ client_address=10.240.0.3
|
|||
* Pod 的回复被发送回给客户端
|
||||
|
||||
|
||||
形象的:
|
||||
用图表示:
|
||||
|
||||
```
|
||||
client
|
||||
|
@ -167,15 +195,21 @@ client_address=10.240.0.3
|
|||
设置 `service.spec.externalTrafficPolicy` 字段如下:
|
||||
|
||||
```console
|
||||
$ kubectl patch svc nodeport -p '{"spec":{"externalTrafficPolicy":"Local"}}'
|
||||
service "nodeport" patched
|
||||
kubectl patch svc nodeport -p '{"spec":{"externalTrafficPolicy":"Local"}}'
|
||||
```
|
||||
输出结果为:
|
||||
```
|
||||
service/nodeport patched
|
||||
```
|
||||
|
||||
|
||||
现在,重新运行测试:
|
||||
|
||||
```console
|
||||
$ for node in $NODES; do curl --connect-timeout 1 -s $node:$NODEPORT | grep -i client_address; done
|
||||
for node in $NODES; do curl --connect-timeout 1 -s $node:$NODEPORT | grep -i client_address; done
|
||||
```
|
||||
输出结果为:
|
||||
```
|
||||
client_address=104.132.1.79
|
||||
```
|
||||
|
||||
|
@ -191,7 +225,7 @@ client_address=104.132.1.79
|
|||
* node1 使用正确的源 IP 地址将数据包路由到 endpoint
|
||||
|
||||
|
||||
形象的:
|
||||
用图表示:
|
||||
|
||||
```
|
||||
client
|
||||
|
@ -210,20 +244,34 @@ client_address=104.132.1.79
|
|||
## Type=LoadBalancer 类型 Services 的 Source IP
|
||||
|
||||
|
||||
对于 Kubernetes 1.5,发送给类型为 [Type=LoadBalancer](/docs/user-guide/services/#type-nodeport) Services 的数据包默认进行源地址 NAT,这是由于所有处于 `Ready` 状态的 Kubernetes 节点对于负载均衡的流量都是符合条件的。所以如果数据包到达一个没有 endpoint 的节点,系统将把这个包代理到*有* endpoint 的节点,并替换数据包的源 IP 为节点的 IP(如前面章节所述)。
|
||||
从Kubernetes1.5开始,发送给类型为 [Type=LoadBalancer](/docs/user-guide/services/#type-nodeport) Services 的数据包默认进行源地址 NAT,这是因为所有处于 `Ready` 状态的可调度 Kubernetes 节点对于负载均衡的流量都是符合条件的。所以如果数据包到达一个没有 endpoint 的节点,系统将把这个包代理到*有* endpoint 的节点,并替换数据包的源 IP 为节点的 IP(如前面章节所述)。
|
||||
|
||||
|
||||
你可以通过在一个 loadbalancer 上暴露这个 source-ip-app 来进行测试。
|
||||
|
||||
```console
|
||||
$ kubectl expose deployment source-ip-app --name=loadbalancer --port=80 --target-port=8080 --type=LoadBalancer
|
||||
service "loadbalancer" exposed
|
||||
kubectl expose deployment source-ip-app --name=loadbalancer --port=80 --target-port=8080 --type=LoadBalancer
|
||||
```
|
||||
输出结果为:
|
||||
```
|
||||
service/loadbalancer exposed
|
||||
```
|
||||
|
||||
$ kubectl get svc loadbalancer
|
||||
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
|
||||
loadbalancer 10.0.65.118 104.198.149.140 80/TCP 5m
|
||||
打印Service的IPs:
|
||||
```console
|
||||
kubectl get svc loadbalancer
|
||||
```
|
||||
输出结果与以下结果类似:
|
||||
```
|
||||
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
|
||||
loadbalancer LoadBalancer 10.0.65.118 104.198.149.140 80/TCP 5m
|
||||
```
|
||||
|
||||
$ curl 104.198.149.140
|
||||
```console
|
||||
curl 104.198.149.140
|
||||
```
|
||||
输出结果与以下结果类似:
|
||||
```
|
||||
CLIENT VALUES:
|
||||
client_address=10.240.0.5
|
||||
...
|
||||
|
@ -233,7 +281,7 @@ client_address=10.240.0.5
|
|||
然而,如果你的集群运行在 Google Kubernetes Engine/GCE 上,设置 `service.spec.externalTrafficPolicy` 字段值为 `Local` 可以强制使*没有* endpoints 的节点把他们自己从负载均衡流量的可选节点名单中删除。这是通过故意使它们健康检查失败达到的。
|
||||
|
||||
|
||||
形象的:
|
||||
用图表示:
|
||||
|
||||
```
|
||||
client
|
||||
|
@ -251,29 +299,44 @@ health check ---> node 1 node 2 <--- health check
|
|||
你可以设置 annotation 来进行测试:
|
||||
|
||||
```console
|
||||
$ kubectl patch svc loadbalancer -p '{"spec":{"externalTrafficPolicy":"Local"}}'
|
||||
kubectl patch svc loadbalancer -p '{"spec":{"externalTrafficPolicy":"Local"}}'
|
||||
```
|
||||
|
||||
|
||||
你应该能够立即看到 Kubernetes 分配的 `service.spec.healthCheckNodePort` 字段:
|
||||
|
||||
```console
|
||||
$ kubectl get svc loadbalancer -o yaml | grep -i healthCheckNodePort
|
||||
kubectl get svc loadbalancer -o yaml | grep -i healthCheckNodePort
|
||||
```
|
||||
输出结果与以下结果类似:
|
||||
```
|
||||
healthCheckNodePort: 32122
|
||||
```
|
||||
|
||||
|
||||
`service.spec.healthCheckNodePort` 字段指向每个节点在 `/healthz` 路径上提供的用于健康检查的端口。你可以这样测试:
|
||||
|
||||
```console
|
||||
kubectl get pod -o wide -l run=source-ip-app
|
||||
```
|
||||
输出结果与以下结果类似:
|
||||
```
|
||||
$ kubectl get pod -o wide -l run=source-ip-app
|
||||
NAME READY STATUS RESTARTS AGE IP NODE
|
||||
source-ip-app-826191075-qehz4 1/1 Running 0 20h 10.180.1.136 kubernetes-minion-group-6jst
|
||||
|
||||
kubernetes-minion-group-6jst $ curl localhost:32122/healthz
|
||||
source-ip-app-826191075-qehz4 1/1 Running 0 20h 10.180.1.136 kubernetes-node-6jst
|
||||
```
|
||||
使用 curl 命令发送请求到每个节点的 `/healthz` 路径。
|
||||
```console
|
||||
kubernetes-node-6jst $ curl localhost:32122/healthz
|
||||
```
|
||||
输出结果与以下结果类似:
|
||||
```
|
||||
1 Service Endpoints found
|
||||
|
||||
kubernetes-minion-group-jj1t $ curl localhost:32122/healthz
|
||||
```
|
||||
```console
|
||||
kubernetes-node-jj1t $ curl localhost:32122/healthz
|
||||
```
|
||||
输出结果与以下结果类似:
|
||||
```
|
||||
No Service Endpoints Found
|
||||
```
|
||||
|
||||
|
@ -281,7 +344,10 @@ No Service Endpoints Found
|
|||
主节点运行的 service 控制器负责分配 cloud loadbalancer。在这样做的同时,它也会分配指向每个节点的 HTTP 健康检查的 port/path。等待大约 10 秒钟之后,没有 endpoints 的两个节点的健康检查会失败,然后 curl 负载均衡器的 ip:
|
||||
|
||||
```console
|
||||
$ curl 104.198.149.140
|
||||
curl 104.198.149.140
|
||||
```
|
||||
输出结果与以下结果类似:
|
||||
```
|
||||
CLIENT VALUES:
|
||||
client_address=104.132.1.79
|
||||
...
|
||||
|
@ -291,7 +357,7 @@ client_address=104.132.1.79
|
|||
__跨平台支持__
|
||||
|
||||
|
||||
由于 Kubernetes 1.5 在类型为 Type=LoadBalancer 的 Services 中支持源 IP 保存的特性仅在 cloudproviders 的子集中实现(GCP and Azure)。你的集群运行的 cloudprovider 可能以某些不同的方式满足 loadbalancer 的要求:
|
||||
从 Kubernetes 1.5 开始,通过类型为 Type=LoadBalancer 的 Services 进行源 IP 保存的支持仅在一部分 cloudproviders 中实现(GCP and Azure)。你的集群运行的 cloudprovider 可能以某些不同的方式满足 loadbalancer 的要求:
|
||||
|
||||
|
||||
1. 使用一个代理终止客户端连接并打开一个到你的 nodes/endpoints 的新连接。在这种情况下,源 IP 地址将永远是云负载均衡器的地址而不是客户端的。
|
||||
|
@ -327,5 +393,3 @@ $ kubectl delete deployment source-ip-app
|
|||
* 学习更多关于 [通过 services 连接应用](/docs/concepts/services-networking/connect-applications-service/)
|
||||
* 学习更多关于 [负载均衡](/docs/user-guide/load-balancer)
|
||||
{{% /capture %}}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue