learn-tech/专栏/Serverless进阶实战课/10小试牛刀(二):如何突破VPC网络的速度限制?.md
2024-10-16 06:37:41 +08:00

13 KiB
Raw Blame History

                        因收到Google相关通知网站将会择期关闭。相关通知内容
                        
                        
                        10 小试牛刀如何突破VPC网络的速度限制
                        你好,我是静远。

在冷启动这节课里我跟你聊到了影响函数第一次执行速度的因素有很多其中一个关键因素就是VPC网络的建联时间。

我们知道VPC是云厂商提供给用户的一个安全隔离的网络环境用户可以在VPC空间中部署自己的服务如Redis、数据库、MQ等。而我们的函数通常是创建在函数专属的VPC中的。一个生产级别的函数服务应用少不了需要和其他的资源和中间件打交道。那么跨VPC之间的通信效率就显得尤为重要了。

今天这节课我就针对VPC之间的通信效率通过案例实操的方式为你剖析函数在跨VPC上的技术要点。

希望通过这节课能够让你得到一些在Serverless的实战和后续优化工作的启发能举一反三地找到更多优化方法进一步突破函数访问速度的上限。

跨VPC问题的演进

针对云上网络连接的方式相信你已经了解到不少如专线高速通道、VPN网关、对等连接等方式。这些方式有的用来解决IDC和VPC的连接有的用来解决VPC与VPC之间的互通。

这其中对等连接的确可以解决函数跨VPC的问题但我们试想一下如果每个用户都通过这种方式来解决这种问题那么平台每次都需要搭建两端的连接并且还可能会遇到IP网段冲突的问题。这样的做法显然不具备普适性成本也过高。

为此针对Serverless的函数计算场景我们通常会通过弹性网卡ENI的方式来打通。弹性网卡是绑定在云主机或者容器实例上的也就是说我们是通过弹性网卡ENI来关联用户的VPC和用户的设备实例使得设备和VPC互通的。

但实际上,我们暂且抛开这两者在网络速率上的差别,不论是在容器实例还是在主机上,他们都会面临同样的问题。

冷启动耗时较长:弹性网卡这种虚拟化的资源,它的创建时间是避免不了的; 资源受限正因为资源虚拟化也就意味着要依赖于硬件的基本参数受限于内核、CPU等因素为此几乎所有的云厂商都会限制VPC内弹性网卡的数量也就从一定程度上导致了此方法的局限性 资源浪费:由于容器或者主机上要绑定弹性网卡,也就意味着多个实例就得占用多个网卡资源,造成资源浪费。

我们可以看出它们虽然解决了跨VPC的问题但还是会带来性能和资源成本上的压力。那么我们是否可以不创建这么多弹性网卡探索一种共用的方式呢通常我们立马能想到的方式就是出口代理了。

什么是代理?

如何代理呢?我们可以了解一下它的整体流程。

简单来说就是我们在函数计算的集群与用户的VPC之间添加Proxy Node在Proxy Node上构建网卡如eth1网卡会提前在用户VPC所在的子网Subnet中创建但租户的权限属于函数计算VPC。

有了弹性网卡接下来就需要解决函数到代理节点的打通问题了也就是在函数的主机和代理主机之间构建一条链路将数据包传递过去且保证源IP和目的IP保持不变。

我们通常采用的是隧道技术。隧道打通后我们还得注意两件事情。其一就是在Proxy Node中多张网卡的转发处理其二由于函数通常以容器的方式运行所以需要有一个容器网络转发的过程。

接下来,我将一步一步介绍它们是如何起作用的。

动手实操

这节课我会通过一个打通跨VPC网络的实验案例带你实现函数通过Proxy机器代理的方式访问用户VPC的资源的全过程。在这个案例中我选取了百度智能云服务器BCC来进行实验。

在实验之前我们需要准备两个VPC网段模拟创建函数集群和用户集群并在云上购买三台云服务器BCC其中2台BCC在VPC1另外1台在VPC2。下面这个图可以清晰的描述部署的逻辑

隧道技术

准备工作完成之后,我们来看隧道技术。

什么是隧道?在网络中,隧道是一种使用网络不支持的协议,构建网络之间高效和安全的链接,实现在网络中传输数据的方法。

内核中的tunnel可以通过ip tunnel help看到当前有五个IPIP、GRE、SIT、ISATAP和VTI。

本节课我们选择使用GREGeneric Routing Encapsulation它是IPIPIP in IP协议的更进一步版本从官方的描述上来看可以取代IPIP。隧道技术本身的概念和区别不是本节的重点。不过如果感兴趣的话你也可以通过我文末给的延伸阅读深入了解。

接下来我们来创建隧道使得func bcc1和proxy bcc能通过隧道打通连接。我们先在func bcc1中执行如下命令来创建GRE隧道

加载GRE模块

modprobe ip_gre lsmod | grep gre

创建gre模式的通道tunnel关联上IP并赋予虚拟本端IP为10.0.0.3

ip tunnel add gre0 mode gre local 192.168.80.8 remote 192.168.4.4 dev eth0 ip addr add 10.0.0.3 peer 10.0.0.4 dev mgre0 ip link set dev mgre0 up

在proxy bcc上执行如下命令来打通GRE隧道

modprobe ip_gre lsmod | grep gre ip tunnel add mgre0 mode gre local 192.168.4.4 dev eth0 ip link set dev mgre0 up ip address add 10.0.0.4/24 dev mgre0 ip neigh replace 10.0.0.3 lladdr 192.168.80.8 dev mgre0

最后我们还可以通过ping命令来验证proxy bcc与func bcc1的GRE隧道是否打通。

到这里我们隧道的打通任务已经完成一半了下面我们要看proxy bcc如何与user bcc打通。

转发技术

我们注意到proxy bcc和user bcc是不在一个VPC集群内的。那我们要怎么解决这个问题呢

构建的逻辑就是在user vpc下构建一个虚拟网卡eth1而eth1又隶属于proxy bcc下。这个方法和我们在上面“跨VPC的演进”中讲到的“动态创建网卡”的方法是一致的。

这里需要说明一点,如果你是一个业务函数的开发者,可能云厂商没有开放相应的特权账号,没有创建弹性网卡的能力,实操不了,那么,这里你只要了解过程即可。如果你是云平台维护工作者,可以体验一下这其中的操作方式。

弹性网卡创建并绑定好后我们会发现有2个网卡存在于proxy bcc中。

我们通过ping命令可以验证是否能够和user bcc打通。这里要注意proxy bcc与user bcc可以互通是因为弹性网卡是基于user bcc所在的VPC的子网Subnet创建的但租户还是属于proxy bcc。

打通之后我们就需要用到转发技术了也就是将func bcc1的数据包通过proxy bcc的请求到达user bcc。

首先我们需要通过将ip_forward设置为1开启proxy bcc的转发能力。

echo 1 > /proc/sys/net/ipv4/ip_forward

这里需要说明一下出于安全考虑Linux系统默认的是禁止数据包转发的。所谓转发就是当主机拥有多于一块网卡时其中一块收到数据包根据数据包的目的IP地址将包发往本机的另一网卡该网卡即会根据路由表继续发送数据包。

我们这里因为是eth0接收云主机的数据通过eth1来发送出去所以需要开启转发能力。当然为了能让func bcc1的路由触达user bcc我们还需要继续完善路由规则。

我们在func bcc1中通过ip route add命令添加一条到user bcc子网172.16.101.0/24的路由

ip route add 172.16.101.0/24 dev mgre0

此时 func bcc1 访问 user bcc 172.16.101.4 仍然访问不通,在 proxy bcc 侧监听 eth1 流量可以发现eth1 发往用户 VPC 的包的源 IP 仍是 func1 bcc gre0 设备的 IP 10.0.0.3,这个 IP实际只存在于我们 bcc 内部,因此用户 bcc 收到请求后,它的返回流量无法路由到这个 IP。

因此我们这里需要在proxy bcc添加一条 iptables 规则,在 POSTROUTING 链里把从 eth1 出口的流量的源 IP SNAT 设置为 eth1 的IP 地址,即 172.16.101.4。

iptables -t nat -A POSTROUTING -o eth1 -j MASQUERADE

到这里我们就可以在func bcc1中ping通user bcc了。

容器网络

在虚机上,我们已经操作体验一遍了,但通常我们的函数是在容器实例里面运行的 的容器在访问用户vpc服务的时候是存在一点差别的。我们也可以验证一下会发现pod 发起的请求正常路由到了 proxy bcc 下的 tunnel 设备,但是没有继续转发到 eth1 中。

为什么会出现这样的情况呢通常是因为Pod的IP和我们在proxy bcc上构建的路由规则是不一致的。至于怎么解答我先不做回答你可以先思考一下。我们先来看一个概念ip-masq-agent。

ip-masq-agent 是一个用来管理 IP 伪装的扩展即管理Kubernetes集群节点中 IP 网段的 SNAT 规则。它以 DaemonSet 形式部署在Kubernetes的集群中在每个节点上启动一个代理程序。代理程序通过配置 iptables 规则,再将容器流量发送到集群节点的 IP 和集群 IP 范围之外的目标时,就可以选择以容器自己的 IP 地址或伪装成节点的 IP 地址作为出向流量的源地址。

看完ip-masq-agent的解说相信你已经有答案了。解决办法就是把 Pod IP SNAT 设置成 gre0 设备的 IP再转给 proxy bcc再对 proxy bcc 隐藏 Pod的IP即可。

ip-masq-agent 的配置来自于 configMap可以通过 kubectl edit cm 命令来查看或修改配置。

到这里我们在Pod中也能访问user bcc的服务了。

高可用

如果我们要上生产环境你还需要考虑服务的高可用。在我们这节课反复提到的案例图中我只画了一个Proxy Node用来实验还可以但生产环境肯定是不够的。为此我们需要为每个VPC/Subnet建立一对主备Proxy。主备Proxy要使用HaVip进行容灾切换。

HaVip是一种可以独立创建和释放的内网IP可以与高可用软件例如KeepAlived配合使用搭建高可用主备服务提高业务的可用性。

另外从Serverless的角度看你还需要依据函数访问的流量对Proxy Node进行自动扩缩容。当流量增大时需要自动对Proxy Node进行扩容当流量降低时需要自动对Proxy Node进行缩容。

我们继续在上面的架构图上进一步完善一下增加主备Proxy Node和容器Pod之后的架构示意图如下所示。

小结

最后,我来小结一下我们今天的内容。

这节课主要围绕着优化函数在跨VPC场景的访问速度来做讲解。我们学习了跨VPC打通的演进过程以及代理的四大要素最后也结合一个案例的实操过程了解了函数代理技术的实现过程。

在演进过程的回顾里,我们会发现,对等连接由于其在函数计算上的构建不具备普适性,只有在极少数大客户的场景下会使用。而弹性的网卡实时创建,虽然具备一定的通用能力,但由于其高延迟和对资源的浪费,我们也不太推荐使用。

这两个技术,给我们来带来的思考是,如果我们能提前准备好访问的通路且可复用,是不是就可以解决这样的问题了?是的。为此,我们提出了函数代理技术。

其实代理技术需要解决的技术卡点还不少主要包括我们提到的隧道通路、网卡转发、容器网络转发技术以及最后在生产环境下还需要考虑到的高可用的问题。我们可以通过高可用的虚拟IP和主备Proxy集群解决。同时从弹性的角度我也建议你引入扩缩容的能力这个能力你也可以贯穿在整个Serverless的学习生涯中可以说它就是Serverless的精髓所在。

最后函数计算由于其胶水语言的特性跨介质跨网络的访问是不可忽视的一件事情。虽然我们已经在不断的演进解决但函数代理技术也有其缺点的比如资源一定程度上的增加。因此希望通过这节课的内容和思考的过程能让你打开眼界提出更多的优化手段让Serverless的技术积淀越来越深厚。

思考题

好了,这节课到这里也就结束了,最后我给你留了一个思考题。

如果我们在隧道打通那里采用IPIP隧道协议会有什么问题

欢迎在留言区写下你的思考和答案,我们一起交流讨论。感谢你的阅读,也欢迎你把这节课分享给更多的朋友一起交流学习。

延伸阅读

你可以通过VPC的对等链接继续深入了解一下对等链接的概念。 这一篇隧道技术,可以帮助你进一步了解隧道的知识要点。 腾讯云的这一篇VPC网络优化的文章也是值得我们一看的。 文中提到的ip-masq-agent感兴趣的话你可以继续看这一篇ip-mast-agent指南写得也是非常详细的。