WSL2 Ubuntu 21.04原生Docker无法运行的问题

最近为了用上更新一点的软件,把运行在WSL2里的Ubuntu 20.04 LTS版本升级了一下,升级到了Ubuntu 21.04,升级之后呢,大部分功能都正常(当然本身我用的功能也不会很多),但是确实也遇到了个小问题:Docker Daemon无法启动了。这个确实很影响工作,因为很多时候写完代码会本地打个镜像运行一下,简单测试一下代码是否有问题。但是升级之后,突然发现Docker用不了了。

具体点呢,是会报一个iptables相关的错误:

......
WARN[2021-10-23T11:30:05.864210900+08:00] Your kernel does not support cgroup blkio throttle.write_iops_device
INFO[2021-10-23T11:30:05.864538700+08:00] Loading containers: start.
INFO[2021-10-23T11:30:06.135353300+08:00] stopping event stream following graceful shutdown  error="context canceled" module=libcontainerd namespace=moby
INFO[2021-10-23T11:30:06.135542600+08:00] stopping healthcheck following graceful shutdown  module=libcontainerd
INFO[2021-10-23T11:30:06.136083800+08:00] stopping event stream following graceful shutdown  error="context canceled" module=libcontainerd namespace=plugins.moby
failed to start daemon: Error initializing network controller: error obtaining controller instance: unable to add return rule in DOCKER-ISOLATION-STAGE-1 chain:  (iptables failed: iptables --wait -A DOCKER-ISOLATION-STAGE-1 -j RETURN: iptables v1.8.7 (nf_tables):  RULE_APPEND failed (No such file or directory): rule in chain DOCKER-ISOLATION-STAGE-1
 (exit status 4))

提示iptables RULE_APPEND也不知道是咋回事,不过想着估计和Docker的网络有点关系,还好一般情况下编译和打镜像也不需要网络隔离,所以前几天就临时在启动Docker Daemon时加上--iptables=false参数,不加载iptables规则,当然docker buillddocker run的时候也得加上--network=host使用不隔离的Host网络,勉强扛了几天。

今天有时间就查了查资料,一开始查到的大部分都是让用Docker Desktop for Windows并且开启WSL2后端,然后在WSL2里直接用Windows的docker.exe命令。这个方案我没试过,不过说实话即使能用,也觉得有点太别扭了。想想还是放弃了。于是继续找资料。终于找到一篇文章,文章里贴了个Ubuntu的bug report,大致意思是说:

Ubuntu从20.10开始,将默认的防火墙切换到了nftables实现,这个实现需要5.8版本及以上的内核,而微软在WSL2中提供的5.4版本的内核没有nftables,所以导致iptables功能出错了。

解决方法也简单,直接把iptables实现切换回iptables-legacy就好了:

~]# sudo update-alternatives --config iptables
There are 2 choices for the alternative iptables (providing /usr/sbin/iptables).

  Selection    Path                       Priority   Status
------------------------------------------------------------
* 0            /usr/sbin/iptables-nft      20        auto mode
  1            /usr/sbin/iptables-legacy   10        manual mode
  2            /usr/sbin/iptables-nft      20        manual mode

Press <enter> to keep the current choice[*], or type selection number: 1
update-alternatives: using /usr/sbin/iptables-legacy to provide /usr/sbin/iptables (iptables) in manual mode

切换回去之后,果然就好了,又可以开心的用原生docker了。

最后我又看了眼现在跑的内核:

~]# uname -r
5.10.60.1-microsoft-standard-WSL2

因为升级了Windows11,现在内核已经到了5.10了。按理不应该有问题才对。除非微软编译内核的时候没开相关的选项?

~]# zcat /proc/config.gz |grep NF_TABLES
CONFIG_NF_TABLES=y
CONFIG_NF_TABLES_INET=y
# CONFIG_NF_TABLES_NETDEV is not set
CONFIG_NF_TABLES_IPV4=y
# CONFIG_NF_TABLES_ARP is not set
CONFIG_NF_TABLES_IPV6=y
# CONFIG_NF_TABLES_BRIDGE is not set

确实有些选项没打开,而且WSL的github issue里似乎也有类似的讨论:#6655#6044#4165。不过微软似乎也没想着解决,不过问题不大啦,也没到要自己编译内核的地步。能用就行~