Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update source-ip documentation #18760

Merged
merged 1 commit into from
Feb 2, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
168 changes: 116 additions & 52 deletions content/zh/docs/tutorials/services/source-ip.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ content_template: templates/tutorial
{{% capture overview %}}


Kubernetes 集群中运行的应用通过抽象的 Service 查找彼此,相互通信和连接外部世界。本文揭示了发送到不同类型 Services 的数据包源 IP 的内幕,你可以根据需求改变这个行为
Kubernetes 集群中运行的应用通过 Service 抽象来互相查找、通信和与外部世界沟通。本文介绍被发送到不同类型 Services 的数据包源 IP 的变化过程,你可以根据你的需求改变这些行为

{{% /capture %}}

Expand All @@ -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 %}}
Expand All @@ -45,7 +48,7 @@ deployment "source-ip-app" created

* 通过多种类型的 Services 暴露一个简单应用
* 理解每种 Service 类型如何处理源 IP NAT
* 理解保留源 IP 的折中
* 理解保留源IP所涉及的折中

{{% /capture %}}

Expand All @@ -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.

Expand Down Expand Up @@ -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
Expand All @@ -146,7 +174,7 @@ client_address=10.240.0.3
* Pod 的回复被发送回给客户端


形象的
用图表示

```
client
Expand All @@ -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
```

Expand All @@ -191,7 +225,7 @@ client_address=104.132.1.79
* node1 使用正确的源 IP 地址将数据包路由到 endpoint


形象的
用图表示

```
client
Expand All @@ -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
...
Expand All @@ -233,7 +281,7 @@ client_address=10.240.0.5
然而,如果你的集群运行在 Google Kubernetes Engine/GCE 上,设置 `service.spec.externalTrafficPolicy` 字段值为 `Local` 可以强制使*没有* endpoints 的节点把他们自己从负载均衡流量的可选节点名单中删除。这是通过故意使它们健康检查失败达到的。


形象的
用图表示

```
client
Expand All @@ -251,37 +299,55 @@ 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
```


主节点运行的 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
...
Expand All @@ -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 地址将永远是云负载均衡器的地址而不是客户端的。
Expand Down Expand Up @@ -327,5 +393,3 @@ $ kubectl delete deployment source-ip-app
* 学习更多关于 [通过 services 连接应用](/docs/concepts/services-networking/connect-applications-service/)
* 学习更多关于 [负载均衡](/docs/user-guide/load-balancer)
{{% /capture %}}