标签 SSH 下的文章

Hello,WireGuard

2020年1月28日,Linux之父Linus Torvalds正式将WireGuard merge到Linux 5.6版本内核主线

img{512x368}

图:WireGuard被加入linux kernel 5.6主线的commit log

这意味着在Linux 5.6内核发布时,linux在内核层面将原生支持一个新的VPN协议栈:WireGuard

img{512x368}

图:WireGuard Logo

一. VPN与WireGuard的创新

VPN,全称Virtual Private Network(虚拟专用网络)。提起VPN,大陆的朋友想到的第一件事就是fan qiang。其实fan qiang只是VPN的一个“小众”应用罢了^_^,企业网络才是VPN真正施展才能的地方。VPN支持在不安全的公网上建立一条加密的、安全的到企业内部网络的通道(隧道tunnel),这就好比专门架设了一个专用网络那样。在WireGuard出现之前,VPN的隧道协议主要有PPTPL2TPIPSec等,其中PPTP和L2TP协议工作在OSI模型的第二层,又称为二层隧道协议;IPSec是第三层隧道协议。

既然已经有了这么多的VPN协议,那么Why WireGuard?

WireGuard的作者Jason A. DonenfeldWireGuard官网给出了很明确地理由:

  • 简单、易用、无连接、无状态:号称目前最易用和最简单的VPN解决方案

WireGuard可以像SSH一样易于配置和部署。只需交换非常简单的公钥就可以建立VPN连接,就像交换SSH密钥一样,其余所有由WireGuard透明处理。并且WireGuard建立的VPN连接是基于UDP的,无需建立和管理连接,无需关心和管理状态的。

  • 先进加密协议

WireGuard充分利用安全领域和密码学在这些年的最新成果,使用noise frameworkCurve25519ChaCha20Poly1305BLAKE2SipHash24等构建WireGuard的安全方案。

  • 最小的攻击面(最少代码实现)

WireGuard的内核模块c代码仅不足5k行,便于代码安全评审。也使得WireGuard的实现更不容易被攻击(代码量少,理论上漏洞相对于庞大的代码集合而言也会少许多)。

  • 高性能

密码学最新成果带来的高速机密原语和WireGuard的内核驻留机制,使其相较于之前的VPN方案更具性能优势。

以上这些理由,同时也是WireGuard这个协议栈的特性。

这么说依然很抽象,我们来实操一下,体验一下WireGuard的简洁、易用、安全、高效。

二. WireGuard安装和使用

WireGuard将在linux 5.6内核中提供原生支持,也就是说在那之前,我们还无法直接使用WireGuard,安装还是不可避免的。在我的实验环境中有两台Linux VPS主机,都是ubuntu 18.04,内核都是4.15.0。因此我们需要首先添加WireGuard的ppa仓库:

sudo add-apt-repository ppa:wireguard/wireguard

更新源后,即可通过下面命令安装WireGuard:

sudo apt-get update

sudo apt-get install wireguard

安装的WireGuard分为两部分:

  • WireGuard内核模块(wireguard.ko),这部分通过动态内核模块技术DKMS安装到ubuntu的内核模块文件目录下:
$ ls /lib/modules/4.15.0-29-generic/updates/dkms/
wireguard.ko

  • 用户层的命令行工具

类似于内核netfilter和命令行工具iptables之间关系,wireguard.ko对应的用户层命令行工具wireguard-tools:wg、wg-quick被安装到/usr/bin下面了:

$ ls -t /usr/bin|grep wg|head -n 2
wg
wg-quick

1. peer to peer vpn

在两个linux Vps上都安装完WireGuard后,我们就可以在两个节点(peer)建立虚拟专用网络(VPN)了。我们分为称两个linux节点为peer1和peer2:

img{512x368}

图:点对点wireguard通信图

就像上图那样,我们只分别需要在peer1和peer2建立/etc/wireguard/wg0.conf

peer1的/etc/wireguard/wg0.conf

[Interface]
PrivateKey = {peer1's privatekey}
Address = 10.0.0.1
ListenPort = 51820

[Peer]
PublicKey = {peer2's publickey}
EndPoint = {peer2's ip}:51820
AllowedIPs = 10.0.0.2/32

peer2的/etc/wireguard/wg0.conf

[Interface]
PrivateKey = {peer2's privatekey}
Address = 10.0.0.2
ListenPort = 51820

[Peer]
PublicKey = {peer1's publickey}
EndPoint = {peer1's ip}:51820
AllowedIPs = 10.0.0.1/32

我们看到每个peer上WireGuard所需的配置文件wg0.conf包含两大部分:

  • [Interface]部分

    • PrivateKey – peer自身的privatekey

    • Address – peer的wg0接口在vpn网络中绑定的路由ip范围,在上述例子中仅绑定了一个ip地址

    • ListenPort – wg网络协议栈监听UDP端口

  • [Peer]部分(描述vpn网中其他peer信息,一个wg0配置文件中显然可以配置多个Peer部分)

    • PublicKey – 该peer的publickey

    • EndPoint – 该peer的wg网路协议栈地址(ip+port)

    • AllowedIPs – 允许该peer发送过来的wireguard载荷中的源地址范围。同时本机而言,这个字段也会作为本机路由表中wg0绑定的ip范围。

每个Peer自身的privatekey和publickey可以通过WireGuard提供的命令行工具生成:

$ wg genkey | tee privatekey | wg pubkey > publickey
$ ls
privatekey  publickey

注:这两个文件可以生成在任意路径下,我们要的是两个文件中内容。

在两个peer上配置完/etc/wireguard/wg0.conf配置文件后,我们就可以使用下面命令在peer1和peer2之间建立一条双向加密VPN隧道了:

peer1:

$ sudo wg-quick up wg0
[#] ip link add wg0 type wireguard
[#] wg setconf wg0 /dev/fd/63
[#] ip -4 address add 10.0.0.1 dev wg0
[#] ip link set mtu 1420 up dev wg0
[#] ip -4 route add 10.0.0.2/32 dev wg0

peer2:

$ sudo wg-quick up wg0
[#] ip link add wg0 type wireguard
[#] wg setconf wg0 /dev/fd/63
[#] ip -4 address add 10.0.0.2 dev wg0
[#] ip link set mtu 1420 up dev wg0
[#] ip -4 route add 10.0.0.1/32 dev wg0

执行上述命令,每个peer会增加一个network interface dev: wg0,并在系统路由表中增加一条路由,以peer1为例:

$ ip a

... ...

4: wg0: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1420 qdisc noqueue state UNKNOWN group default qlen 1000
    link/none
    inet 10.0.0.1/32 scope global wg0
       valid_lft forever preferred_lft forever

$ ip route
default via 172.21.0.1 dev eth0 proto dhcp metric 100
10.0.0.2 dev wg0 scope link
... ...

现在我们来测试两个Peer之间的连通性。WireGuard的peer之间是对等的,谁发起的请求谁就是client端。我们在peer1上ping peer2,在peer2上我们用tcpdump抓wg0设备的包:

Peer1:

$ ping -c 3 10.0.0.2
PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
64 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=34.9 ms
64 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=34.7 ms
64 bytes from 10.0.0.2: icmp_seq=3 ttl=64 time=34.6 ms

--- 10.0.0.2 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2002ms
rtt min/avg/max/mdev = 34.621/34.781/34.982/0.262 ms

Peer2:

# tcpdump -i wg0
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on wg0, link-type RAW (Raw IP), capture size 262144 bytes
13:29:52.659550 IP 10.0.0.1 > instance-cspzrq3u: ICMP echo request, id 20580, seq 1, length 64
13:29:52.659603 IP instance-cspzrq3u > 10.0.0.1: ICMP echo reply, id 20580, seq 1, length 64
13:29:53.660463 IP 10.0.0.1 > instance-cspzrq3u: ICMP echo request, id 20580, seq 2, length 64
13:29:53.660495 IP instance-cspzrq3u > 10.0.0.1: ICMP echo reply, id 20580, seq 2, length 64
13:29:54.662201 IP 10.0.0.1 > instance-cspzrq3u: ICMP echo request, id 20580, seq 3, length 64
13:29:54.662234 IP instance-cspzrq3u > 10.0.0.1: ICMP echo reply, id 20580, seq 3, length 64

我们看到peer1和peer2经由WireGuard建立的vpn实现了连通:在peer2上ping peer1(10.0.0.1)亦得到相同结果。

这时如果我们如果在peer2(vpn ip: 10.0.0.2)上启动一个http server(监听0.0.0.0:9090):

//httpserver.go
package main

import "net/http"

func index(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("hello, wireguard\n"))
}

func main() {
    http.Handle("/", http.HandlerFunc(index))
    http.ListenAndServe(":9090", nil)
}

那么我们在peer1(vpn ip:10.0.0.1)去访问这个server:

$ curl http://10.0.0.2:9090
hello, wireguard

在peer2(instance-cspzrq3u)上的tcpdump显示(tcp握手+数据通信+tcp拆除):

14:15:05.233794 IP 10.0.0.1.43922 > instance-cspzrq3u.9090: Flags [S], seq 1116349511, win 27600, options [mss 1380,sackOK,TS val 3539789774 ecr 0,nop,wscale 7], length 0
14:15:05.233854 IP instance-cspzrq3u.9090 > 10.0.0.1.43922: Flags [S.], seq 3504538202, ack 1116349512, win 27360, options [mss 1380,sackOK,TS val 2842719516 ecr 3539789774,nop,wscale 7], length 0
14:15:05.268792 IP 10.0.0.1.43922 > instance-cspzrq3u.9090: Flags [.], ack 1, win 216, options [nop,nop,TS val 3539789809 ecr 2842719516], length 0
14:15:05.268882 IP 10.0.0.1.43922 > instance-cspzrq3u.9090: Flags [P.], seq 1:78, ack 1, win 216, options [nop,nop,TS val 3539789809 ecr 2842719516], length 77
14:15:05.268907 IP instance-cspzrq3u.9090 > 10.0.0.1.43922: Flags [.], ack 78, win 214, options [nop,nop,TS val 2842719551 ecr 3539789809], length 0
14:15:05.269514 IP instance-cspzrq3u.9090 > 10.0.0.1.43922: Flags [P.], seq 1:134, ack 78, win 214, options [nop,nop,TS val 2842719552 ecr 3539789809], length 133
14:15:05.304147 IP 10.0.0.1.43922 > instance-cspzrq3u.9090: Flags [.], ack 134, win 224, options [nop,nop,TS val 3539789845 ecr 2842719552], length 0
14:15:05.304194 IP 10.0.0.1.43922 > instance-cspzrq3u.9090: Flags [F.], seq 78, ack 134, win 224, options [nop,nop,TS val 3539789845 ecr 2842719552], length 0
14:15:05.304317 IP instance-cspzrq3u.9090 > 10.0.0.1.43922: Flags [F.], seq 134, ack 79, win 214, options [nop,nop,TS val 2842719586 ecr 3539789845], length 0
14:15:05.339035 IP 10.0.0.1.43922 > instance-cspzrq3u.9090: Flags [.], ack 135, win 224, options [nop,nop,TS val 3539789880 ecr 2842719586], length 0

如果要拆除这个vpn,只需在每个peer上分别执行如下命令:

$ sudo wg-quick down wg0
[#] ip link delete dev wg0

2. peer to the local network of other peer

上面两个peer虽然实现了点对点的连通,但是如果我们想从peer1访问peer2所在的局域网中的另外一台机器(这显然是vpn最常用的应用场景),如下面示意图:

img{512x368}

图:从一个peer到另外一个peer所在局域网的节点的通信图

基于目前的配置是否能实现呢?我们来试试。首先我们在peer1上要将192.168.1.0/24网段的路由指到wg0上,这样我们在peer1上ping或curl 192.168.1.123:9090,数据才能被交给wg0处理并通过vpn网络送出,修改peer1上的wg0.conf:

// peer1's /etc/wireguard/wg0.conf

... ...
[Peer]
PublicKey = {peer2's publickey}
EndPoint = peer2's ip:51820
AllowedIPs = 10.0.0.2/32,192.168.1.0/24

重启peer1上的wg0使上述配置生效。然后我们尝试在peer1上ping 192.168.1.123:

$ ping -c 3 192.168.1.123
PING 192.168.1.123 (192.168.1.123) 56(84) bytes of data.

--- 192.168.1.123 ping statistics ---
3 packets transmitted, 0 received, 100% packet loss, time 2038ms

我们在peer2上的tcpdump显示:

# tcpdump -i wg0
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on wg0, link-type RAW (Raw IP), capture size 262144 bytes
14:33:38.393520 IP 10.0.0.1 > 192.168.1.123: ICMP echo request, id 30426, seq 1, length 64
14:33:39.408083 IP 10.0.0.1 > 192.168.1.123: ICMP echo request, id 30426, seq 2, length 64
14:33:40.432079 IP 10.0.0.1 > 192.168.1.123: ICMP echo request, id 30426, seq 3, length 64

我们看到peer2收到来自10.0.0.1的到192.168.1.123的ping包都没有对应的回包,通信失败。Why?我们分析一下。

peer2在51820端口收到WireGuard包后,去除wireguard包的包裹,露出真实数据包。真实数据包的目的ip地址为192.168.1.123,该地址并非peer2自身地址(其自身局域网地址为192.168.1.10)。既然不是自身地址,就不能送到上层协议栈(tcp)处理,那么另外一条路是forward(转发)出去。但是是否允许转发么?显然从结果来看,从wg0收到的消息无权转发,于是消息丢弃,这就是没有回包和通信失败的原因。

为了支持转发(这是vpn常用场景的功能哦),我们需要为peer2的wg0.conf增加些转发配置:

// peer2's  wg0.conf

[Interface]

... ...
PostUp   = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUT  ING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUT  ING -o eth0 -j MASQUERADE

... ...

重启peer2的wg0。在peer2的内核层我们也要开启转发开关:

// /etc/sysctl.conf

net.ipv4.ip_forward=1

net.ipv6.conf.all.forwarding=1

执行下面命令临时生效:

# sysctl -p
net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1

接下来,我们再来测试一下连通性。我们在peer1上再次尝试ping 192.168.1.123

$ ping -c 3 192.168.1.123
PING 192.168.1.123 (192.168.1.123) 56(84) bytes of data.
64 bytes from 192.168.1.123: icmp_seq=1 ttl=46 time=200 ms
64 bytes from 192.168.1.123: icmp_seq=2 ttl=46 time=200 ms
64 bytes from 192.168.1.123: icmp_seq=3 ttl=46 time=200 ms

--- 192.168.1.123 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2002ms
rtt min/avg/max/mdev = 200.095/200.239/200.396/0.531 ms

这回通了!peer2上的Tcpdump输出中也看到了回包:

14:49:58.808467 IP 10.0.0.1 > 192.168.1.123: ICMP echo request, id 402, seq 1, length 64
14:49:58.974035 IP 192.168.1.123 > 10.0.0.1: ICMP echo reply, id 402, seq 1, length 64
14:49:59.809747 IP 10.0.0.1 > 192.168.1.123: ICMP echo request, id 402, seq 2, length 64
14:49:59.975240 IP 192.168.1.123 > 10.0.0.1: ICMP echo reply, id 402, seq 2, length 64
14:50:00.810802 IP 10.0.0.1 > 192.168.1.123: ICMP echo request, id 402, seq 3, length 64
14:50:00.976202 IP 192.168.1.123 > 10.0.0.1: ICMP echo reply, id 402, seq 3, length 64

我们在192.168.1.123上运行上面的那个httpserver程序,再在peer1上用curl访问这个程序:

$ curl 192.168.1.123:9090
hello, wireguard

我们看到httpserver的应答成功返回。peer2上的tcpdump也抓到了整个通信过程:

14:50:36.437259 IP 10.0.0.1.47918 > 192.168.1.123.9090: Flags [S], seq 3235649864, win 27600, options [mss 1380,sackOK,TS val 101915019 ecr 0,nop,wscale 7], length 0
14:50:36.593554 IP 192.168.1.123.9090 > 10.0.0.1.47918: Flags [S.], seq 2420552016, ack 3235649865, win 28960, options [mss 1460,sackOK,TS val 2323314775 ecr 101915019,nop,wscale 7], length 0
14:50:36.628315 IP 10.0.0.1.47918 > 192.168.1.123.9090: Flags [.], ack 1, win 216, options [nop,nop,TS val 101915210 ecr 2323314775], length 0
14:50:36.628379 IP 10.0.0.1.47918 > 192.168.1.123.9090: Flags [P.], seq 1:84, ack 1, win 216, options [nop,nop,TS val 101915210 ecr 2323314775], length 83
14:50:36.784550 IP 192.168.1.123.9090 > 10.0.0.1.47918: Flags [.], ack 84, win 227, options [nop,nop,TS val 2323314822 ecr 101915210], length 0
14:50:36.784710 IP 192.168.1.123.9090 > 10.0.0.1.47918: Flags [P.], seq 1:134, ack 84, win 227, options [nop,nop,TS val 2323314822 ecr 101915210], length 133
14:50:36.820339 IP 10.0.0.1.47918 > 192.168.1.123.9090: Flags [.], ack 134, win 224, options [nop,nop,TS val 101915401 ecr 2323314822], length 0
14:50:36.820383 IP 10.0.0.1.47918 > 192.168.1.123.9090: Flags [F.], seq 84, ack 134, win 224, options [nop,nop,TS val 101915401 ecr 2323314822], length 0
14:50:36.977226 IP 192.168.1.123.9090 > 10.0.0.1.47918: Flags [F.], seq 134, ack 85, win 227, options [nop,nop,TS val 2323314870 ecr 101915401], length 0
14:50:37.011927 IP 10.0.0.1.47918 > 192.168.1.123.9090: Flags [.], ack 135, win 224, options [nop,nop,TS val 101915594 ecr 2323314870], length 0

3. WireGuard的用户层实现

在linux上,我们务必使用WireGuard的内核模式,这显然是最高效的。在macOS、Windows上,WireGuard无法以内核模块驻留模式运行,但WireGuard项目提供了WireGuard的用户层实现。其作者Jason A. Donenfeld亲自实现了Go语言版本的wireguard-go。macOS上使用的就是wireguard的Go实现。我们可以使用brew在macOS上按照WireGuard:

$brew install wireguard-tools

配置好/etc/wireguard/wg0.conf后(和linux上的配置方式一致),同样可以通过wg-quick命令启动wireguard:

$sudo wg-quick up wg0

wg-quick实际上会通过wireguard-go来实现linux wireguard在内核中完成的功能:

$ps -ef|grep wireguard

    0 57783     1   0  3:18下午 ttys002    0:00.01 wireguard-go utun

三. WireGuard性能如何

关于WireGuard性能如何,官方给出了一个性能基准测试的对比数据(相较于其他vpn网络栈):

img{512x368}

图:WireGuard性能与其他vpn网络栈的对比(来自官方截图)

我们看到和IPSec、OpenVPN相比,无论从吞吐还是延迟,WireGuard都领先不少。

我们这里用microsoft开源的带宽测试工具ethr来直观看一下走物理网络和走WireGuard VPN的带宽差别。

在peer2上运行:

$ ethr -s

然后在peer1上分别通过物理网络和VPN网络向peer2发起请求:

  • peer1 -> peer2 (物理网络)
$ ethr -c  peer2's ip
Connecting to host [peer2 ip], port 9999
[  6] local 172.21.0.5 port 46108 connected to  peer2 ip port 9999
- - - - - - - - - - - - - - - - - - - - - - -
[ ID]   Protocol    Interval      Bits/s
[  6]     TCP      000-001 sec     1.54M
[  6]     TCP      001-002 sec     1.54M
[  6]     TCP      002-003 sec     1.54M
[  6]     TCP      003-004 sec     1.54M
[  6]     TCP      004-005 sec     1.54M

.... ...

  • peer1 -> peer2 (vpn网络)
$ ethr -c 10.0.0.2
Connecting to host [10.0.0.2], port 9999
[  6] local 10.0.0.1 port 36010 connected to 10.0.0.2 port 9999
- - - - - - - - - - - - - - - - - - - - - - -
[ ID]   Protocol    Interval      Bits/s
[  6]     TCP      000-001 sec     1.79M
[  6]     TCP      001-002 sec      640K
[  6]     TCP      002-003 sec     1.15M
[  6]     TCP      003-004 sec      512K
[  6]     TCP      004-005 sec     1.02M
[  6]     TCP      005-006 sec     1.02M
[  6]     TCP      006-007 sec     1.02M

我们看到走vpn的带宽相当于走物理网络的66%(1.02/1.54)左右。这里peer1(腾讯云)、peer2(百度云)之间走的是互联网,而在局域网测试的效果可能更好(留给大家^_^)。

四. 小结

经过上面的实验,我们看到了WireGuard的配置的确十分简单,这也是我目前使用过的配置过程最为简单的vpn。随着linux kernel 5.6内置对WireGuard的原生支持,WireGuard在vpn领域势必会有更为广泛的应用。

在容器网络方面,目前WireGuard已经给出了跨容器的网络通信方案,基于wireguard的k8s cni网络插件wormhole可以让pod之间通过wireguard实现的overlay网络通信。

国外的tailscale公司正在实现一种基于Wireguard的mesh vpn网络,该网络以WireGuard为数据平面的承载体,该公司主要实现控制平面。该公司目前聚集了一些Go核心开发人员,这里就包括著名的go核心开发团队成员、net/http包的最初作者和当前维护者的Brad Fitzpatrick。

五. 参考资料


我的网课“Kubernetes实战:高可用集群搭建、配置、运维与应用”在慕课网上线了,感谢小伙伴们学习支持!

我爱发短信:企业级短信平台定制开发专家 https://tonybai.com/
smspush : 可部署在企业内部的定制化短信平台,三网覆盖,不惧大并发接入,可定制扩展; 短信内容你来定,不再受约束, 接口丰富,支持长短信,签名可选。

著名云主机服务厂商DigitalOcean发布最新的主机计划,入门级Droplet配置升级为:1 core CPU、1G内存、25G高速SSD,价格5$/月。有使用DigitalOcean需求的朋友,可以打开这个链接地址:https://m.do.co/c/bff6eed92687 开启你的DO主机之路。

Gopher Daily(Gopher每日新闻)归档仓库 – https://github.com/bigwhite/gopherdaily

我的联系方式:

微博:https://weibo.com/bigwhite20xx
微信公众号:iamtonybai
博客:tonybai.com
github: https://github.com/bigwhite

微信赞赏:
img{512x368}

商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。

Kubernetes网络插件(CNI)基准测试的最新结果

本文翻译自Alexis Ducastel的文章《Benchmark results of Kubernetes network plugins (CNI) over 10Gbit/s network (Updated: April 2019)》

本文是我之前的基准测试的最新更新,这次测试在最新版Kubernetes 1.14上运行,其中CNI版本在2019年4月更新。

首先,非常感谢Cilium团队对我的帮助,包括协助审查测试结果以及更正我的指标监控脚本。

自2018年11月以来都有哪些新变化

如果你只是想知道自上次以来发生的变化,这里有一个简短的总结:

Flannel仍然是CNI竞赛中最快和最精简的那个选手,但它仍然不支持NetworkPolicies(网络策略),也不支持加密。

Romana不再维护,因此我们决定将其从基准测试中剔除。

WeaveNet现在同时支持Ingress和Egress的NetworkPolicies!但性能要略低于之前的版本。

如果您想获得最佳性能,Calico仍需要手动定制MTU。Calico为安装CNI提供了两个新选项,无需专用ETCD存储

  • 将状态存储在Kubernetes API中作为数据存储区(集群<50个节点)
  • 使用Typha代理将状态存储在Kubernetes API中,以减轻K8S API(集群> 50个节点)的压力

Calico宣布在Istio之上支持应用层策略(Application Layer Policy),为应用层带来安全性。

Cilium现在支持加密!Cilium使用IPSec隧道提供加密,并为WeaveNet提供了加密网络的替代方案。但是,在启用加密的情况下,WeaveNet比Cilium更快。这是由于Cilium 1.4.2仅支持CBC加密,若使用GCM将会更好,但它将是1.5版本的Cilium的一部分。

由于嵌入了ETCD operator,因此Cilium现在更容易部署。

Cilium团队还通过降低内存消耗和CPU成本,努力减少CNI占用空间。但他们仍然比其他选手更重。

基准测试的上下文

基准测试是在通过Supermicro 10Gbit交换机连接的三台Supermicro裸机服务器上进行的。服务器通过DAC SFP +无源电缆直接连接到交换机,并在激活巨型帧(MTU 9000)的同一VLAN中设置。

Kubernetes 1.14.0​在Ubuntu 18.04 LTS上运行,运行Docker 18.09.2(此linux版本中的默认docker版本)。

为了提高可重复性,我们选择始终在第一个节点上设置master,在第二个服务器上设置基准测试的服务器部分,在第三个服务器上设置客户端部分。这是通过Kubernetes deployments中的NodeSelector实现的。

以下是我们将用于描述基准测试结果和解释的表情图:

img{512x368}

为基准测试选择CNI

这个基准测试仅仅关注那些入选kubernetes正式文档:“create a single master cluster with kubeadm”中的CNI列表。在提到的9个CNI中,我们只测试其中的6个,不包括那些我们无法轻松安装和/或不通过以下文档开箱即用的工具(Romana,Contiv-VPP和JuniperContrail / TungstenFabric)

以下是我们将要比较的CNI列表:

  • Calico v3.6
  • Canal v3.6(事实上,Flannel用于网络+ Calico用于防火墙)
  • Cilium 1.4.2
  • Flannel 0.11.0
  • Kube-router 0.2.5
  • WeaveNet 2.5.1

安装

CNI越容易设置,我们对其第一印象就越好。所有参与基准测试的CNI都很容易设置(一个或两个命令行)。

如前所述,服务器和交换机都配置了Jumbo帧激活(通过将MTU设置为9000)。我们非常感谢CNI可以自动发现要使用的MTU,具体取决于适配器。事实上,Cilium和Flannel是唯一能够正确自动检测MTU的选手。大多数其他CNI在GitHub中引发了启用MTU自动检测的问题,但是现在,我们需要通过修改Calico,Canal和Kube-router的ConfigMap或WeaveNet的ENV var来手动修复它。

也许您想知道错误的MTU会产生什么影响?这里有一个图表,显示WeaveNet与默认MTU和WeaveNet与Jumbo帧之间的区别:

img{512x368}

那么,既然我们知道MTU对性能非常重要,那么这些CNI如何自动检测MTU:

img{512x368}

正如我们在上图中看到的,我们必须对Calico,Canal,Kube-router和WeaveNet应用一些MTU调整以获得最佳性能。Cilium和Flannel能够自行正确地自动检测MTU,确保开箱即用的最佳性能。

安全

在比较这些CNI的安全性时,我们谈论两件事:它们加密通信的能力,以及它们对Kubernetes网络策略的实现(根据实际测试,而不是来自他们的文档)。

只有两个CNI可以实现加密通信:Cilium和WeaveNet。通过将加密密码设置为CNI的ENV变量可以来启用WeaveNet加密。WeaveNet文档有点令人困惑,但这很容易做到。Cilium加密是通过创建Kubernetes Secrets和daemonSet修改的命令设置的(比WeaveNet复杂一点,但是Cilium有很棒的文档记录了它)。

在网络策略实现方面,通过实施Ingress和Egress规则,Calico,Canal,Cilium和WeaveNet是最好的控制面板。Kube-router实际上只实现了Ingress规则。

Flannel没有实现网络策略。

以下是结果摘要:

img{512x368}

性能

该基准测试显示每次测试的三次运行(至少)的平均带宽。我们正在测试TCP和UDP性能(使用iperf3),真实应用程序,如HTTP(使用Nginx和curl),或FTP(使用vsftpd和curl),最后是使用SCP协议进行应用程序加密的行为(使用OpenSSH服务器和客户端)。

对于所有测试,我们还在裸机节点(绿色条)上运行基准测试,以比较CNI与本机网络性能的有效性。为了与我们的基准比例保持一致,我们在图表上使用以下颜色:

  • 黄色=非常好
  • 橙色=好
  • 蓝色=一般
  • 红色=差

因为我们不关注错误配置的CNI的性能,所以我们只会显示MTU调整的CNI基准测试结果。(NOTA BENE:如果激活加密,Cilium无法正确计算MTU,因此您必须在v1.4中手动将MTU降低到8900.下一版1.5将自动适应。)

结果如下:

img{512x368}

每个CNI都在TCP基准测试中表现良好。由于加密成本,启用加密的CNI远远落后于其他CNI。

img{512x368}

同样,在UDP基准测试中,所有CNI都表现良好。加密的CNI现在彼此非常接近。Cilium落后于其竞争对手,但事实上,它仅略高于裸机结果的2,3%,这是公平的。我们应该记住的是,Cilium和Flannel都是唯一能够正确自动检测MTU的CNI,从而提供了开箱即用的结果。

img{512x368}

真实世界的应用程序怎么样?使用HTTP基准测试,我们可以看到全局性能略低于TCP测试。即使HTTP支持TCP,在TCP基准测试中,iperf3配置为避免任何“TCP慢启动”副作用,这可以有效地影响HTTP基准测试。这里的每个选手的表现都相当不错,Kube-router有明显的优势,WeaveNet在这项测试中表现非常糟糕,比裸机少了约20%。Cilium加密和WeaveNet加密现在都远远落后于裸机性能。

img{512x368}

使用FTP,另一个TCP支持的协议,结果更加复杂。虽然Flannel和Kube-router的表现非常好,但是Calico,Canal和Cilium稍稍落后,在裸机速度下约为10%。WeaveNet与裸机性能相差甚远,差距为17>%。无论如何,WeaveNet的加密版本比Cilium加密的性能高出约40%。

img{512x368}

通过SCP,我们可以清楚地看到SSH协议的加密成本。大多数CNI表现良好,但WeaveNet再次落后于其他人。当然,由于双重加密成本(SSH加密+ CNI加密)。

以下是性能摘要总结:

img{512x368}

资源消耗

现在让我们比较这些CNI在负载很重的情况下处理所带来的资源消耗如何(在TCP 10Gbit传输期间)。在性能测试中,我们将CNI与裸金属(绿色条)进行比较。对于资源消耗测试,我们还显示了没有任何CNI设置的新闲置Kubernetes(紫色条)的消耗。然后我们可以计算出CNI真正消耗的开销。

让我们从内存方面开始吧。以下是传输期间以MB为单位的平均节点RAM使用率(无缓冲区/缓存)。

img{512x368}

Flannel和Kube-router表现非常好,只有大约50MB的内存占用,其次是Calico和Canal,70MB。WeaveNet的消费量明显高于其竞争对手,资源占用约为130MB。凭借400MB的内存占用,Cilium具有最高的基准内存消耗。

现在,让我们检查CPU消耗。警告:图形单位不是百分比,而是permil。因此裸金属的38 permil实际上是3.8%。结果如下:

img{512x368}

Calico,Canal,Flannel和Kube-router都非常高效的CPU使用,与没有CNI的kubernetes相比,开销仅多出2%。远远落后于WeaveNet,开销约为5%,然后是Cilium,CPU开销超过7%。

以下是资源消耗的摘要:

img{512x368}

摘要

以下是所有结果的汇总概述:

img{512x368}

结论

最后一部分是主观的,并传达了我自己对结果的解释。请记住,此基准测试仅在一个非常小的集群(3个节点)上测试单个连接中的吞吐速度。它不反映大型集群(> 50个节点)的网络行为,也没有多少连接并发。

如果你在相应的场景中,我建议使用以下CNI:

  • 您的群集中有低资源节点(只有几GB的RAM,几个核心)并且您不需要安全功能,请使用Flannel。它是我们测试过的最精简的CNI之一。此外,它与大量架构兼容(amd64,arm,arm64等)。它是唯一一个能够正确自动检测MTU的CNI,和Cilium一起,因此您无需配置任何内容即可使其正常工作。Kube-router也很好,但标准较低,需要您手动设置MTU。
  • 出于安全原因,您需要加密网络,请使用WeaveNet。如果您使用巨型帧并通过在环境变量中提供密码来激活加密,请不要忘记设置MTU大小。但话说回过来,忘掉性能,这就是加密的代价。
  • 对于其他常见用法,我会推荐Calico。这种CNI广泛用于许多kubernetes部署工具(Kops,Kubespray,Rancher等)。就像WeaveNet一样,如果您使用的是巨型帧,请不要忘记在ConfigMap中设置MTU。事实证明,它在资源消耗,性能和安全性方面具有多用途和高效性。

最后但并非最不重要的,我建议你关注Cilium的工作。他们的团队非常活跃,他们正在努力提高他们的CNI(功能,资源节约,性能,安全性,多集群跨越……),他们的路线图听起来非常有趣。

img{512x368}


我的网课“Kubernetes实战:高可用集群搭建、配置、运维与应用”在慕课网上线了,感谢小伙伴们学习支持!

我爱发短信:企业级短信平台定制开发专家 https://tonybai.com/
smspush : 可部署在企业内部的定制化短信平台,三网覆盖,不惧大并发接入,可定制扩展; 短信内容你来定,不再受约束, 接口丰富,支持长短信,签名可选。

著名云主机服务厂商DigitalOcean发布最新的主机计划,入门级Droplet配置升级为:1 core CPU、1G内存、25G高速SSD,价格5$/月。有使用DigitalOcean需求的朋友,可以打开这个链接地址:https://m.do.co/c/bff6eed92687 开启你的DO主机之路。

我的联系方式:

微博:https://weibo.com/bigwhite20xx
微信公众号:iamtonybai
博客:tonybai.com
github: https://github.com/bigwhite

微信赞赏:
img{512x368}

商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。

如发现本站页面被黑,比如:挂载广告、挖矿等恶意代码,请朋友们及时联系我。十分感谢! Go语言第一课 Go语言精进之路1 Go语言精进之路2 商务合作请联系bigwhite.cn AT aliyun.com

欢迎使用邮件订阅我的博客

输入邮箱订阅本站,只要有新文章发布,就会第一时间发送邮件通知你哦!

这里是 Tony Bai的个人Blog,欢迎访问、订阅和留言! 订阅Feed请点击上面图片

如果您觉得这里的文章对您有帮助,请扫描上方二维码进行捐赠 ,加油后的Tony Bai将会为您呈现更多精彩的文章,谢谢!

如果您希望通过微信捐赠,请用微信客户端扫描下方赞赏码:

如果您希望通过比特币或以太币捐赠,可以扫描下方二维码:

比特币:

以太币:

如果您喜欢通过微信浏览本站内容,可以扫描下方二维码,订阅本站官方微信订阅号“iamtonybai”;点击二维码,可直达本人官方微博主页^_^:
本站Powered by Digital Ocean VPS。
选择Digital Ocean VPS主机,即可获得10美元现金充值,可 免费使用两个月哟! 著名主机提供商Linode 10$优惠码:linode10,在 这里注册即可免费获 得。阿里云推荐码: 1WFZ0V立享9折!


View Tony Bai's profile on LinkedIn
DigitalOcean Referral Badge

文章

评论

  • 正在加载...

分类

标签

归档



View My Stats