将CI/CD服务前移到k8s后,当drone的插件drone-docker构建镜像时,总卡在更新软件包的步骤。是网络连接的问题,为了解决这个问题,耗费了我整个周末。

# apk update
fetch http://dl-cdn.alpinelinux.org/alpine/v3.11/main/x86_64/APKINDEX.tar.gz

Drone-Runner in K8S

在k8s环境中,drone-runner通过调用k8s的api,创建相应的pod来运行pipeline。此时,一个pipeline对应一个pod。Pod中的各个容器对应pipeline中的各个step

Drone-Docker

在docker环境中,drone-docker一般以docker outside of docker(dood)的方式运行。但在k8s环境中,drone-docker以docker in docker(dind)的方式运行,可以理解成“套娃”。

由于我的集群的cri是containerd,更准确的说,这里是以docker in containerd的方式运行的:

  • 执行drone-pipeline的pod运行在k8s的containerd上
  • 构建镜像的docker容器通过pod内容器的docker服务运行
k8s (containerd)
└── pod (pipeline)
    ├── container (step clone)
    └── container (step drone-docker)
        └── docker (dind)
            └── container (docker build)

这种复杂的结构,导致了奇怪的网络现象,结合以上树形图进行说明:

  • 容器(step drone-docker)的网络正常
  • 容器(docker build)可以下载基于http协议的小体积文件
  • 容器(docker build)不能下载基于http协议的大文件以及所有基于https的文件

Maximum Transmission Unit(MTU)

起初,我以为区别在于http和https协议,可能是tls握手出现了问题。打算通过http协议,下载安装一些包,进行网络测试,却发现大体积的文件也无法下载。即使能够下载,下载的速度也特别慢。

看来并不是协议的问题,是响应体大小的问题,更准确的说是传输速度的问题。其实,我有过容器内下载速度慢的经验:在京东云运行docker容器时因为网速慢调整过mtu。于是便在mtu上下功夫了。

容器(step drone-docker),主机网卡eth0@if112mtu是1450,docker网桥docker0mtu是1500

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1
    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@if112: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1450 qdisc noqueue state UP
    link/ether 76:93:e3:c6:53:fb brd ff:ff:ff:ff:ff:ff
    inet 10.42.0.108/24 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::7493:e3ff:fec6:53fb/64 scope link tentative dadfailed
       valid_lft forever preferred_lft forever
4: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN
    link/ether 02:42:2a:76:ce:8a brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:2aff:fe76:ce8a/64 scope link
       valid_lft forever preferred_lft forever

容器(docker build),主机网卡eth0@if8mtu是1500

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1
    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
7: eth0@if8: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever

果然,子容器内的mtu跟容器宿主机的mtu是不一致的,且子容器的mtu比容器宿主机的mtu大。这就导致了数据传输时需要分片,进而导致网络速度下降。将子容器的mtu设置成与容器宿主机一样的值就行了。

在这里,我在drone pipeline内设置相应的mtu值即可。注意:这里的mtu键是plugins/docker插件的一个参数。对于正常的容器而言,应设置与之对应的参数。

kind: pipeline
type: kubernetes
name: default

steps:
  - name: php-fpm
    image: plugins/docker
    settings:
      mtu: 1450

关于MTU的理解

将子容器往容器宿主机数据传输过程比作用货车运箱子:子容器mtu值是箱子的大小,容器宿主机的mtu值是货车车厢的容量大小。

当箱子比车厢大时,需要将箱子切割并分给其他货车运输(数据包的分片),因此箱子的运输效率会降低。

参考文章