准备工作

你必须拥有一个正常工作的 Kubernetes 1.5 集群,用来运行本文中的示例。该示例使用一个简单的 nginx webserver 回送它接收到的请求的 HTTP 头中的源 IP 地址。你可以像下面这样创建它:

$ kubectl run ``source``-ip-app --image=k8s.gcr.io``/echoserver``:1.4``deployment ``"source-ip-app"` `created

Type=ClusterIP 类型 Services 的 Source IP

如果你的 kube-proxy 运行在 iptables 模式下,从集群内部发送到 ClusterIP 的包永远不会进行源地址 NAT,这从 Kubernetes 1.2 开始是默认选项。Kube-proxy 通过一个 proxyMode endpoint 暴露它的模式。

$ 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``iptables

你可以通过在 source IP 应用上创建一个服务来测试源 IP 保留。

$ 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

从相同集群中的一个 pod 访问这个 ClusterIP:

$ 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.``# ip addr``1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue``    ``link``/loopback` `00:00:00:00:00:00 brd 00:00:00:00:00:00``    ``inet 127.0.0.1``/8` `scope host lo``       ``valid_lft forever preferred_lft forever``    ``inet6 ::1``/128` `scope host``       ``valid_lft forever preferred_lft forever``3: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1460 qdisc noqueue``    ``link``/ether` `0a:58:0a:f4:03:08 brd ff:ff:ff:ff:ff:ff``    ``inet 10.244.3.8``/24` `scope global eth0``       ``valid_lft forever preferred_lft forever``    ``inet6 fe80::188a:84ff:feb0:26a5``/64` `scope link``       ``valid_lft forever preferred_lft forever
# wget -qO - 10.0.170.92``CLIENT VALUES:``client_address=10.244.3.8``command``=GET``...

如果客户端 pod 和 服务端 pod 在相同的节点上,client_address 就是客户端 pod 的 IP 地址。但是,如果它们在不同的节点上, client_address 将会是客户端 pod 所在节点的 flannel IP 地址。

Type=NodePort 类型 Services 的 Source IP

​ 对于 Kubernetes 1.5,发送给类型为 Type=NodePort Services 的数据包默认进行源地址 NAT。你可以创建一个 NodePort Service 来进行测试:

$ 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 }'``)

如果你的集群运行在一个云服务上,你可能需要为上面报告的 nodes:nodeport 开启一条防火墙规则。 现在,你可以通过上面分配的节点端口从外部访问这个 Service。

$ ``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

请注意,这些并不是正确的客户端 IP,它们是集群的内部 IP。这是所发生的事情:

1、客户端发送数据包到 node2:nodePort

2、node2 使用它自己的 IP 地址替换数据包的源 IP 地址(SNAT)

3、node2 使用 pod IP 地址替换数据包的目的 IP 地址

4、数据包被路由到 node 1,然后交给 endpoint

5、Pod 的回复被路由回 node2

6、Pod 的回复被发送回给客户端

形象的:

​ 为了防止这种情况发生,Kubernetes 提供了一个特性来保留客户端的源 IP 地址(点击此处查看可用特性)。设置 service.spec.externalTrafficPolicy 的值为 Local,请求就只会被代理到本地 endpoints 而不会被转发到其它节点。这样就保留了最初的源 IP 地址。如果没有本地 endpoints,发送到这个节点的数据包将会被丢弃。这样在应用到数据包的任何包处理规则下,你都能依赖这个正确的 source-ip 使数据包通过并到达 endpoint。

设置 service.spec.externalTrafficPolicy 字段如下:

$ kubectl patch svc nodeport -p ``'{"spec":{"externalTrafficPolicy":"Local"}}'``service ``"nodeport"` `patched

现在,重新运行测试:

$ ``for` `node ``in` `$NODES; ``do` `curl --connect-timeout 1 -s $node:$NODEPORT | ``grep` `-i client_address; ``done``client_address=104.132.1.79

请注意,你只从 endpoint pod 运行的那个节点得到了一个回复,这个回复有正确的客户端 IP。

这是发生的事情:

1、客户端发送数据包到 node2:nodePort,它没有任何 endpoints

2、数据包被丢弃

3、客户端发送数据包到 node1:nodePort,它endpoints

4、node1 使用正确的源 IP 地址将数据包路由到 endpoint

形象的:

Type=LoadBalancer 类型 Services 的 Source IP

​ 对于 Kubernetes 1.5,发送给类型为 Type=LoadBalancer Services 的数据包默认进行源地址 NAT,这是由于所有处于 Ready 状态的 Kubernetes 节点对于负载均衡的流量都是符合条件的。所以如果数据包到达一个没有 endpoint 的节点,系统将把这个包代理到有 endpoint 的节点,并替换数据包的源 IP 为节点的 IP(如前面章节所述)。

​ 你可以通过在一个 loadbalancer 上暴露这个 source-ip-app 来进行测试。

$ 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``$ curl 104.198.149.140``CLIENT VALUES:``client_address=10.240.0.5``...

​ 然而,如果你的集群运行在 Google Kubernetes Engine/GCE 上,设置 service.spec.externalTrafficPolicy 字段值为 Local 可以强制使没有 endpoints 的节点把他们自己从负载均衡流量的可选节点名单中删除。这是通过故意使它们健康检查失败达到的。

形象的:

你可以设置 annotation 来进行测试:

$ kubectl patch svc loadbalancer -p ``'{"spec":{"externalTrafficPolicy":"Local"}}'

你应该能够立即看到 Kubernetes 分配的 service.spec.healthCheckNodePort 字段:

$ kubectl get svc loadbalancer -o yaml | ``grep` `-i healthCheckNodePort``  ``healthCheckNodePort: 32122

service.spec.healthCheckNodePort 字段指向每个节点在 /healthz 路径上提供的用于健康检查的端口。你可以这样测试:

$ 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``1 Service Endpoints found``kubernetes-minion-group-jj1t $ curl localhost:32122``/healthz``No Service Endpoints Found

​ 主节点运行的 service 控制器负责分配 cloud loadbalancer。在这样做的同时,它也会分配指向每个节点的 HTTP 健康检查的 port/path。等待大约 10 秒钟之后,没有 endpoints 的两个节点的健康检查会失败,然后 curl 负载均衡器的 ip:

$ curl 104.198.149.140``CLIENT VALUES:``client_address=104.132.1.79``...

跨平台支持

​ 由于 Kubernetes 1.5 在类型为 Type=LoadBalancer 的 Services 中支持源 IP 保存的特性仅在 cloudproviders 的子集中实现(GCP and Azure)。你的集群运行的 cloudprovider 可能以某些不同的方式满足 loadbalancer 的要求:

1、使用一个代理终止客户端连接并打开一个到你的 nodes/endpoints 的新连接。在这种情况下,源 IP 地址将永远是云负载均衡器的地址而不是客户端的。

2、使用一个包转发器,因此从客户端发送到负载均衡器 VIP 的请求在拥有客户端源 IP 地址的节点终止,而不被中间代理。

​ 第一类负载均衡器必须使用一种它和后端之间约定的协议来和真实的客户端 IP 通信,例如 HTTP X-FORWARDED-FOR 头,或者 proxy 协议。 第二类负载均衡器可以通过简单的在保存于 Service 的 service.spec.healthCheckNodePort 字段上创建一个 HTTP 健康检查点来使用上面描述的特性。

原文地址:

http://www.damonyi.cc/kubernetes中的source-ip机制/

【转】干货,Kubernetes中的Source Ip机制。的更多相关文章

  1. kubernetes 中的证书工作机制

    一文带你彻底厘清 Kubernetes 中的证书工作机制 搬砖者: 张首富 时 间: 2020-05-26 w x: y18163201 原文地址:https://zhaohuabing.com/po ...

  2. Kubernetes中的垃圾回收机制

    本文所讨论垃圾回收(GC,Garbage Collection)机制针对Kubernetes1.1.7,docker容器. 一.Tips 01. Kubernetes的垃圾回收由kubelet进行管理 ...

  3. 深入解析kubernetes中的选举机制

    Overview 在 Kubernetes的 kube-controller-manager , kube-scheduler, 以及使用 Operator 的底层实现 controller-rumt ...

  4. 关于 Kubernetes 中的 Volume 与 GlusterFS 分布式存储

    容器中持久化的文件生命周期是短暂的,如果容器中程序崩溃宕机,kubelet 就会重新启动,容器中的文件将会丢失,所以对于有状态的应用容器中持久化存储是至关重要的一个环节:另外很多时候一个 Pod 中可 ...

  5. Prometheus在Kubernetes下的服务发现机制

    Prometheus作为容器监控领域的事实标准,随着以Kubernetes为核心的云原生热潮的兴起,已经得到了广泛的应用部署.灵活的服务发现机制是Prometheus和Kubernetes两者得以连接 ...

  6. 探索 OpenStack 之(17):计量模块 Ceilometer 中的数据收集机制

    本文将阐述 Ceilometer 中的数据收集机制.Ceilometer 使用三种机制来收集数据: Notifications:Ceilometer 接收 OpenStack 其它服务发出的 noti ...

  7. Kubernetes 中的核心组件与基本对象概述

    Kubernetes 是 Google 基于 Borg 开源的容器编排调度,用于管理容器集群自动化部署.扩容以及运维的开源平台.作为云原生计算基金会 CNCF(Cloud Native Computi ...

  8. Kubernetes中的网络

    一.引子 既然Kubernetes中将容器的联网通过插件的方式来实现,那么该如何解决这个的联网问题呢? 如果你在本地单台机器上运行docker容器的话注意到所有容器都会处在docker0网桥自动分配的 ...

  9. [转帖]Kubernetes中安装Helm及使用

    Kubernetes中安装Helm及使用 2018年07月02日 17:41:09 灬勿忘丶心安 阅读数 3699更多 分类专栏: K8S   版权声明:本文为博主原创文章,遵循CC 4.0 BY-S ...

随机推荐

  1. java之Spring(IOC)装配Bean(手动装配、自动装配、注解装配)

    在上一篇控制反转中我们看到了依靠一个Bean文件来实现对代码的控制,可谓十分便捷,再也不用去实例化对象了,2333~~~ 1.手动装配 <bean id="todo" cla ...

  2. Scrapy爬虫框架第一讲(Linux环境)

    1.What is Scrapy? 答:Scrapy是一个使用python语言(基于Twistec框架)编写的开源网络爬虫框架,其结构清晰.模块之间的耦合程度低,具有较强的扩张性,能满足各种需求.(前 ...

  3. 转 Web用户的身份验证及WebApi权限验证流程的设计和实现

    前言:Web 用户的身份验证,及页面操作权限验证是B/S系统的基础功能,一个功能复杂的业务应用系统,通过角色授权来控制用户访问,本文通过Form认证,Mvc的Controller基类及Action的权 ...

  4. 论文笔记(2):A fast learning algorithm for deep belief nets.

    论文笔记(2):A fast learning algorithm for deep belief nets. 这几天继续学习一篇论文,Hinton的A Fast Learning Algorithm ...

  5. linux内核裁剪及编译可加载模块

    一:linux内核裁剪: 1:编译内核源码: 今天的重点内容是内核驱动的编写,在编写驱动之前首先的了解linux内核源码,linux主要是由五个子系统组成:进程调度,内存管理,文件系统,网络接口以及进 ...

  6. 使用Docker镜像和仓库

    https://www.rhel.cc/2014/11/50/   学习目标: 什么是镜像 docker镜像是由文件系统折叠加而成的,最低端是一个引导文件系统,即bootfs: 如何对镜像进行管理 使 ...

  7. sap 内表

    内表的目的在ABAP/4中,主要使用表格.表格是R/3系统中的关键数据结构.长期使用的数据存储在关系数据库表格中.关于如何读取和处理数据库表格的详细信息,参见读取并处理数据库表.除了数据库表格,还可以 ...

  8. jQuery学习之旅 Item7 区别this和$(this)

    刚开始以为this和$(this)就是一模子刻出来.但是我在阅读时,和coding时发现,总不是一回事,这里就谈谈this与$(this)的区别. 1.jQuery中this与$(this)的区别 $ ...

  9. ftp研究

    工作中经常用到ftp,最近闲下心来,仔细研究下ftp这个协议. FTP(文件传输协议)工作原理 目前在网络上,如果你想把文件和其他人共享.最方便的办法莫过于将文件放FTP服务器上,然后其他人通过FTP ...

  10. C++雾中风景9:emplace_back与可变长模板

    C++11的版本在vector容器添加了emplace_back方法,相对于原先的push_back方法能够在一定程度上提升vector容器的表现性能.所以我们从STL源码角度来切入,看看这两种方法有 ...