<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Soul Of Free Loop &#187; UML</title>
	<atom:link href="https://zohead.com/archives/tag/uml/feed" rel="self" type="application/rss+xml" />
	<link>https://zohead.com</link>
	<description>Uranus Zhou&#039;s Blog</description>
	<lastBuildDate>Sat, 19 Jul 2025 15:42:46 +0000</lastBuildDate>
	<language>zh-CN</language>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.8</generator>
	<item>
		<title>在Docker容器中使用FUSE文件系统</title>
		<link>https://zohead.com/archives/docker-fuse/</link>
		<comments>https://zohead.com/archives/docker-fuse/#comments</comments>
		<pubDate>Tue, 19 Dec 2017 16:32:18 +0000</pubDate>
		<dc:creator><![CDATA[Uranus Zhou]]></dc:creator>
				<category><![CDATA[Docker]]></category>
		<category><![CDATA[User-mode Linux]]></category>
		<category><![CDATA[FUSE]]></category>
		<category><![CDATA[httpfs2]]></category>
		<category><![CDATA[NFS]]></category>
		<category><![CDATA[Slirp]]></category>
		<category><![CDATA[UML]]></category>
		<category><![CDATA[VDE]]></category>
		<category><![CDATA[容器]]></category>
		<category><![CDATA[文件系统]]></category>

		<guid isPermaLink="false">https://zohead.com/?p=1518</guid>
		<description><![CDATA[容器使用 FUSE 的问题 我们一般使用的 Docker 容器都是非特权容器，也就是说容器内的 root 用户并不拥有真正的 root 权限，这就导致很多属于系统管理员的操作都被禁用了。 最近有个在 IBM Bluemix 容器内部挂载 FUSE 文件系统的需求，例如我使用 davfs2 挂载 WebDAV 服务器不出意外地会报错： mount.davfs 命令报错表示无法打开 fuse 设备，而 fuse 设备实际上是存在的（说明 fuse 模块也已经加载了）： 从容器内部可以查看到 cgroup 实际允许访问的设备，并没有包含 fuse 设备： 手工允许 fuse 设备自然也是不可行的：  [&#8230;]]]></description>
				<content:encoded><![CDATA[<h2 id="docker-fuse-problem">容器使用 FUSE 的问题</h2>
<p>我们一般使用的 Docker 容器都是非特权容器，也就是说容器内的 root 用户并不拥有真正的 root 权限，这就导致很多属于系统管理员的操作都被禁用了。</p>
<p>最近有个在 IBM Bluemix 容器内部挂载 FUSE 文件系统的需求，例如我使用 davfs2 挂载 WebDAV 服务器不出意外地会报错：</p>
<pre class="brush: bash; title: ; notranslate">
root@instance-007a20ff:~# mount.davfs https://dav.jianguoyun.com/dav/ /mnt/
Please enter the username to authenticate with server
https://dav.jianguoyun.com/dav/ or hit enter for none.
  Username: xxx@xx.com
Please enter the password to authenticate user fcoe@qq.com with server
https://dav.jianguoyun.com/dav/ or hit enter for none.
  Password: 
mount.davfs: can't open fuse device
mount.davfs: trying coda kernel file system
mount.davfs: no free coda device to mount
</pre>
<p>mount.davfs 命令报错表示无法打开 fuse 设备，而 fuse 设备实际上是存在的（说明 fuse 模块也已经加载了）：</p>
<pre class="brush: bash; title: ; notranslate">
root@instance-007a20ff:~# cat /sys/devices/virtual/misc/fuse/dev
10:229
</pre>
<p>从容器内部可以查看到 cgroup 实际允许访问的设备，并没有包含 fuse 设备：</p>
<pre class="brush: bash; title: ; notranslate">
root@instance-007a20ff:~# cat /sys/fs/cgroup/devices/devices.list
c 1:5 rwm
c 1:3 rwm
c 1:9 rwm
c 1:8 rwm
c 5:0 rwm
c 5:1 rwm
c *:* m
b *:* m
c 1:7 rwm
c 136:* rwm
c 5:2 rwm
c 10:200 rwm
</pre>
<p>手工允许 fuse 设备自然也是不可行的：</p>
<pre class="brush: bash; title: ; notranslate">
root@instance-007a20ff:~# echo &quot;c 10:229 rwm&quot; &gt; /sys/fs/cgroup/devices/devices.allow
-bash: /sys/fs/cgroup/devices/devices.allow: Permission denied
</pre>
<p>另外由于 Bluemix 提供的是非特权容器，即使允许访问 /dev/fuse 设备也会因为没有 mount 权限而挂载失败。</p>
<h2 id="uml-modify">UML 系统修改</h2>
<p>虽然 Docker 容器内部不能直接挂载使用 FUSE 文件系统，但我想到如果用 User-mode Linux（以下简称 UML） 来实现在应用层再运行一个 Linux kernel，就可以在 UML guest 系统中挂载 FUSE 文件系统了，而且 UML 系统中也可以通过 hostfs 直接访问容器本身的文件系统。</p>
<p>有关 UML 的介绍和编译使用可以参考我之前写的 <a href="https://zohead.com/archives/openvz-uml-bbr/">小内存OpenVZ VPS使用UML开启BBR</a> 文章。</p>
<p>首先我们需要修改 UML kernel 配置：</p>
<ul>
<li>增加对 FUSE 的支持，这个是必须的了，否则无法在 UML guest 系统中使用 FUSE 文件系统；</li>
<li>增加了对 xfs、btrfs、ext4、squashfs、iso9660 等常见文件系统的支持（方便在 UML 系统中挂载各种文件系统镜像）；</li>
<li>另外为了支持将 UML guest 的文件系统导出，启用了 sunrpc 和 NFS 服务器支持；</li>
<li>UML 网络配置中增加了对 Slirp 和 VDE 网卡的支持。</li>
</ul>
<p>UML kernel 目前支持常见的几种网络模式：</p>
<ul>
<li>TUN/TAP <br />
最简单和常用的模式，不过 host kernel 需要支持 tun 或者 tap 设备，这个在 Docker 容器中一般都不可用的；</li>
<li>SLIP <br />
SLIP 串行线路 IP 支持，现在一般很少用到了，同样 host kernel 需要支持 slip 设备；</li>
<li>Slirp <br />
通过用户层的 slirp 程序实现 SLIP 连接，好处是不依赖任何内核层的设备，而且 slirp 可以支持非 root 用户使用，不过 Slirp 只支持模拟 IP 协议的数据包，详细可以参考 <a href="http://slirp.sourceforge.net/" target="_blank">Slirp</a> 开源项目的官方网站。</li>
<li>VDE <br />
VDE（Virtual Distributed Ethernet）可以在不同的计算机间实现软件定义的以太网络，同样支持在用户层以非 root 用户身份来运行，目前 Linux 上的 QEMU 和 KVM 虚拟机都支持 VDE 虚拟网络。</li>
</ul>
<p>鉴于我们需要在 Docker 容器中运行 UML 系统，目前只能使用 Slirp 和 VDE 模式的网卡，另外单独的 slirp 程序使用起来相对 VDE 也更简单（支持 VDE 的 UML kernel 可以直接调用 slirp 程序，不像 VDE 还需要预先使用 <code>vde_switch</code> 等命令配置软件交换机），因此这里的 UML 系统就使用 Slirp 网卡了。</p>
<p>当然原来基于 busybox 的 UML 系统用户层也做了些修改：</p>
<ul>
<li>增加 libfuse 支持挂载 FUSE 文件系统；</li>
<li>增加 rpcbind、nfs-common、nfs-kernel-server 等软件包，支持在 UML 系统中运行 NFS 服务器，导出 UML guest 的文件系统；</li>
<li>增加 dropbear，支持 SSH 和 SFTP 服务器和客户端了；</li>
<li>增加 httpfs2 FUSE 文件系统支持，来自 GitHub 上的 <a href="https://github.com/Tomas-M/httpfs2-enhanced" target="_blank">httpfs2-enhanced</a> 项目，方便挂载访问 HTTP 和 HTTPS 远程文件；</li>
<li>增加 archivemount FUSE 文件系统支持，方便直接挂载 zip、rar、tar.gz 等各种格式的压缩包以支持直接访问压缩包中的文件；</li>
<li>增加 <a href="http://curlftpfs.sourceforge.net" target="_blank">CurlFtpFs</a> FUSE 文件系统支持，支持挂载 FTP 远程文件；</li>
<li>增加 <a href="http://savannah.nongnu.org/projects/davfs2" target="_blank">davfs2</a> FUSE 文件系统支持，支持挂载远程 WebDAV 目录；</li>
<li>增加 <a href="https://github.com/libfuse/sshfs" target="_blank">sshfs</a> FUSE 文件系统支持，支持通过 SFTP 方式挂载远程主机目录。</li>
</ul>
<blockquote>
<p><strong>提示</strong></p>
<ul>
<li>httpfs2-enhanced 最好使用支持 SSL 和多线程的版本，可以加快响应速度并能挂载 HTTPS 的远程文件；</li>
<li>我在测试中发现 httpfs2-enhanced 工具在国内的网络环境下挂载某些 HTTP 远程文件存在一些问题，因此做了简单的修改，并 fork 到我自己的 GitHub 仓库里了。有需要的朋友可以参考我修改过的 <a href="https://github.com/zohead/httpfs2-enhanced" target="_blank">httpfs2-enhanced</a>，检出其中的 <a href="https://github.com/zohead/httpfs2-enhanced/tree/http-fix" target="_blank">http-fix</a> 分支即可，我也已经针对原项目提交了 Pull request。</li>
</ul>
</blockquote>
<p>为了方便使用，我给原来的 <code>uml-linux.sh</code> 执行脚本增加了新的 <code>uml.conf</code> 配置文件：</p>
<pre class="brush: bash; title: ; notranslate">
UML_ID=&quot;umlvm&quot;
UML_MEM=&quot;64M&quot;

UML_NETMODE=&quot;slirp&quot;
#HOST_ADDR=&quot;192.168.0.3&quot;
#UML_ADDR=&quot;192.168.0.4&quot;
#UML_DNS=&quot;&quot;

TAP_DEV=&quot;tap0&quot;
ROUTER_DEV=&quot;eth0&quot;
VDE_SWITCH=&quot;&quot;

REV_TCP_PORTS=&quot;&quot;
REV_UDP_PORTS=&quot;&quot;
#FWD_TCP_PORTS=&quot;111 892 2049 32803 662&quot;
#FWD_UDP_PORTS=&quot;111 892 2049 947 32769 662 660&quot;
</pre>
<p>简单说明如下：</p>
<ul>
<li><code>UML_MEM</code> 指定为 UML 系统分配多少内存，默认 64 MB；</li>
<li><code>UML_NETMODE</code> 比较重要，指定 UML 系统的网卡模式，目前支持 <code>tuntap</code>、<code>slirp</code>、<code>vde</code> 这三个选项；</li>
<li>如果是 <code>tuntap</code> 网卡模式，<code>TAP_DEV</code> 指定 host 主机上的 TUN 网卡设备名称，可以使用 <code>HOST_ADDR</code> 配置 host 主机 TUN 网卡的 IP 地址，<code>UML_ADDR</code> 配置 UML guest 主机的 IP 地址（最好和 <code>HOST_ADDR</code> 在同一网段），如果需要端口转发，那么还需要修改 <code>ROUTER_DEV</code> 指定 host 主机用于转发的物理网卡设备名称；</li>
<li>如果是 <code>slirp</code> 网卡模式，那么会直接使用 Slirp 默认固定的专用地址：<code>10.0.2.2</code> 为 host 主机地址，<code>10.0.2.15</code> 为 UML guest 主机的 IP 地址，并自动将 UML guest 系统内的 DNS 服务器地址设置为 <code>10.0.2.3</code> 通过 host 主机进行域名解析；</li>
<li>如果是 <code>vde</code> 网卡模式，那么必须修改 <code>VDE_SWITCH</code> 指定 VDE 软件交换机的路径，VDE 软件交换机需要通过 <code>vde_switch</code> 等命令预先配置，详细使用说明可以参考 <a href="http://wiki.virtualsquare.org/wiki/index.php/VDE_Basic_Networking" target="_blank">Virtualsquare VDE Wiki</a> 页面；</li>
<li><code>FWD_TCP_PORTS</code> 和 <code>FWD_UDP_PORTS</code> 指定进行转发的 TCP 和 UDP 端口列表（多个转发端口以空格隔开），转发端口支持 <code>10080-80</code> 这种形式（表示将 host 主机的 10080 端口转到 UML guest 的 80 端口），上面 <code>uml.conf</code> 中的注释列出来的是 UML 系统中对外的 NFS 服务器所需要的端口（根据实际情况也可以只允许 111、892、2049 这几个端口）。</li>
</ul>
<p>为了能够根据 <code>uml.conf</code> 文件配置 Slirp 的端口转发功能，我还为 slirp 程序增加了一个 <code>slirp.sh</code> wrapper 脚本：</p>
<pre class="brush: bash; title: ; notranslate">
#!/bin/sh
DIR=&quot;$( cd &quot;$( dirname &quot;$0&quot; )&quot; &amp;&amp; pwd )&quot;

[ -f $DIR/uml.conf ] &amp;&amp; . $DIR/uml.conf

CMD=`which slirp-fullbolt 2&gt;/dev/null || which slirp`

for i in $FWD_TCP_PORTS; do
	if [ &quot;x$i&quot; = &quot;x*&quot; ]; then
		continue
	fi
	CMD=&quot;$CMD \&quot;redir ${i%-*} ${i##*-}\&quot;&quot;
done
for i in $FWD_UDP_PORTS; do
	if [ &quot;x$i&quot; = &quot;x*&quot; ]; then
		continue
	fi
	CMD=&quot;$CMD \&quot;redir udp ${i%-*} ${i##*-}\&quot;&quot;
done

eval &quot;exec $CMD&quot;
</pre>
<p>由于 UML kernel 调用 slirp 程序时不支持附加参数，这里才通过 <code>slirp.sh</code> 脚本来实现，功能也非常简单，通过 slirp 程序的 redir 选项配置端口转发。</p>
<blockquote>
<p><strong>提示</strong></p>
<p>为了让 UML guest 系统的 Slirp 网络真正达到接近 host 主机的网络性能，host 主机上编译 slirp 程序时必须打开 <code>FULL_BOLT</code> 开关，并为 slirp 源代码打上 <a href="https://www.mail-archive.com/user-mode-linux-user@lists.sourceforge.net/msg07014.html" target="_blank">real full bolt</a> 的 patch，否则 UML guest 系统内通过 Slirp 访问外网的速度会很慢。</p>
<p>值得庆幸的是 Debian、Ubuntu 系统自带的 slirp 软件包一般都打上了这个 patch，而且提供了 <code>slirp-fullbolt</code> 和 <code>slirp</code> 这两个程序分别对应 <code>FULL_BOLT</code> 开启和关闭的 Slirp，我的 <code>slirp.sh</code> 脚本也对此做了判断，优先使用速度更快的 <code>slirp-fullbolt</code> 程序。</p>
</blockquote>
<p>至于新的启动 UML 系统的脚本 <code>uml-linux.sh</code> 稍微有点长，这里就不贴出来了，和原来相比的改动就是增加对 Slirp 和 VDE 网络的支持。</p>
<p>另外新的 <code>uml-linux.sh</code> 脚本改为默认前台方式启动 UML 系统；如果需要以后台方式启动 UML 系统，则可以用 <code>uml-linux.sh -D</code> 的方式来运行。</p>
<h2 id="uml-mount-fuse">使用 UML 挂载 FUSE 文件系统</h2>
<p>修改之后支持 FUSE 和 NFS 服务器的 UML 系统可以从这里下载（百度网盘备用）：</p>
<p><a href="https://zohead.com/downloads/uml-fuse-nfsd-x64.tar.xz">https://zohead.com/downloads/uml-fuse-nfsd-x64.tar.xz</a> <br />
<a href="https://pan.baidu.com/s/1bp6l7B5" target="_blank">https://pan.baidu.com/s/1bp6l7B5</a></p>
<p>解压缩下载下来的 <code>uml-fuse-nfsd-x64.tar.xz</code> 文件，运行其中的 <code>uml-linux</code> 脚本就可以启动 UML 系统了，此 UML 系统的 root 用户默认密码为 <code>uml</code>。</p>
<p>下面我以挂载 HTTP 远程 iso 文件中的安装包为例，介绍如何在 UML 系统中使用 FUSE。</p>
<p>首先使用 httpfs2 挂载阿里云上的 CentOS 6.9 iso 文件（这里为 httpfs2 指定 <code>-c /dev/null</code> 参数是为了去掉 httpfs2 的网络访问输出日志），挂载成功之后可以查看挂载点中的文件：</p>
<pre class="brush: bash; title: ; notranslate">
~ # httpfs2 -c /dev/null http://mirrors.aliyun.com/centos/6/isos/x86_64/CentOS-6.9-x86_64-minimal.iso /tmp
file name:      CentOS-6.9-x86_64-minimal.iso
host name:      mirrors.aliyun.com
port number:    80
protocol:       http
request path:   /centos/6/isos/x86_64/CentOS-6.9-x86_64-minimal.iso
auth data:      (null)
httpfs2: main: connecting to mirrors.aliyun.com port 80.
No SSL session data.
httpfs2: main: closing socket.
httpfs2: main: connecting to mirrors.aliyun.com port 80.
httpfs2: main: keeping socket open.
file size:      427819008
httpfs2: main: closing socket.
~ # ls -l /tmp
total 0
-r--r--r--    1 root     root     427819008 Mar 29  2017 CentOS-6.9-x86_64-minimal.iso
</pre>
<p>我们可以发现 httpfs2 的挂载点中实际上就只有一个远程文件名，我们可以直接挂载这个 iso 文件：</p>
<pre class="brush: bash; title: ; notranslate">
~ # mount -t iso9660 -o ro,loop /tmp/CentOS-6.9-x86_64-minimal.iso /media
~ # ls /media
CentOS_BuildTag                GPL                            RPM-GPG-KEY-CentOS-6           RPM-GPG-KEY-CentOS-Testing-6   isolinux
EFI                            Packages                       RPM-GPG-KEY-CentOS-Debug-6     TRANS.TBL                      repodata
EULA                           RELEASE-NOTES-en-US.html       RPM-GPG-KEY-CentOS-Security-6  images
</pre>
<p>到这里就可以直接查看远程 iso 文件中的软件包了，你可以很方便地将远程 iso 中的软件包拷贝到 host 主机的文件系统中。</p>
<p>如果你想直接拷贝软件包中的特定文件，也可以通过 archivemount 来实现哦：</p>
<pre class="brush: bash; title: ; notranslate">
~ # archivemount /media/Packages/sed-4.2.1-10.el6.x86_64.rpm /mnt
fuse: missing mountpoint parameter
~ # ls /mnt
bin  usr
</pre>
<p>这样直接复制软件包中的文件就实在太方便了（请忽略 archivemount 时的 fuse 警告，实际不影响使用）。</p>
<p>当然如果你想挂载 FTP 远程文件就可以通过 curlftpfs 命令来实现，也可以使用 mount.davfs 命令挂载 WebDAV 服务器上的文件（例如直接访问坚果云中的文件）。</p>
<h2 id="export-uml-fuse">导出 UML FUSE 文件系统</h2>
<p>有些情况下我们需要将 UML 系统中的 FUSE 挂载点导出给 host 主机或者网络中的其它主机使用，这时可以通过 hostfs、SFTP、NFS 等方式实现，分别简单说明一下。</p>
<h3 id="hostfs-export-fuse">hostfs 导出 FUSE</h3>
<p>这是最简单的方式，由于 UML 直接使用 hostfs 访问 host 主机的文件系统，因此 UML 系统内可以直接将 FUSE 中的文件拷贝到 hostfs 文件系统，只是这种方式 host 主机并不能直接访问 UML guest 主机的 FUSE 文件系统。</p>
<h3 id="sftp-export-fuse">SFTP 导出 FUSE</h3>
<p>这种方式也很简单，由于 UML 系统启动时自动运行了 dropbear 服务，我们可以先修改 <code>uml.conf</code> 配置文件设置 SSH 端口转发，将 host 主机的 2222 端口通过 Slirp 转发到 UML guest 系统内的 22 端口（如果 host 主机本身并没有运行 SSH 服务器，那甚至可以配置为 <code>FWD_TCP_PORTS="22"</code> 直接转发 22 端口）：</p>
<pre class="brush: bash; title: ; notranslate">
FWD_TCP_PORTS=&quot;2222-22&quot;
</pre>
<p>此时 host 主机就可以使用 scp 命令直接从 UML 的 FUSE 文件系统中拷贝文件了：</p>
<pre class="brush: bash; title: ; notranslate">
root@instance-007a20ff:~# scp -P 2222 root@192.168.1.52:/mnt/bin/sed /home
</pre>
<blockquote>
<p><strong>注意</strong></p>
<p>上面命令中的 <code>192.168.1.52</code> 应根据实际情况替换为 host 主机上实际访问网络的网卡 IP 地址，不能使用 localhost 或者 127.0.0.1，因为 slirp 默认会自动选择访问网络的网卡，并不会进行本地转发。</p>
</blockquote>
<h3 id="nfs-export-fuse">NFS 导出 FUSE</h3>
<p>首先需要修改 <code>uml.conf</code> 配置文件中的 <code>FWD_TCP_PORTS</code> 和 <code>FWD_UDP_PORTS</code> 值，将默认的 NFS 服务需要的 TCP 和 UDP 端口注释去掉，表示将这些端口转发到 UML 系统内。</p>
<blockquote>
<p><strong>提示</strong></p>
<p>如果你需要在 host 主机上直接 NFS 挂载 UML 系统里的 FUSE 文件系统，由于 sunrpc 的 111 端口无法修改而且需要转发到 UML 系统内，这种情况下 host 主机的 portmap 或者 rpcbind 服务需要保持关闭状态，防止 111 端口被占用。</p>
</blockquote>
<p>使用 <code>uml-linux.sh</code> 脚本启动 UML 系统，启动完成之后通过集成的 FUSE 相关工具挂载需要访问的 FUSE 文件系统。</p>
<p>假设需要导出 UML 系统中的 <code>/mnt</code> FUSE 文件系统，UML 中默认的 NFS 导出目录配置文件 <code>/etc/exports</code> 如下：</p>
<pre class="brush: plain; title: ; notranslate">
/mnt 0.0.0.0/0.0.0.0(async,insecure,no_subtree_check,rw,no_root_squash,fsid=0)
</pre>
<p>上面的配置文件表示将 /mnt 目录通过 NFS 导出，默认允许所有主机访问，你可以根据需要修改导出目录路径和允许访问的主机地址。</p>
<p>接着在 UML 系统中通过集成的服务脚本启动 rpcbind 和 nfs 服务：</p>
<pre class="brush: bash; title: ; notranslate">
~ # /etc/init.d/rpcbind start
Starting up rpcbind...
~ # /etc/init.d/nfs start
Starting nfs service:
fs.nfs.nlm_tcpport = 32803
fs.nfs.nlm_udpport = 32769
Exporting directories for NFS...
Starting NFS daemon: 
rpc.nfsd: address family inet6 not supported by protocol TCP
NFSD: the nfsdcld client tracking upcall will be removed in 3.10. Please transition to using nfsdcltrack.
NFSD: starting 90-second grace period (net 000000006035a880)
Starting NFS mountd:
</pre>
<p>如果一切正常的话，此时就可以在外部通过 NFS 挂载 UML 系统导出的 FUSE 文件系统进行访问了：</p>
<pre class="brush: bash; title: ; notranslate">
root@instance-007a20ff:~# mount -t nfs -o nolock,proto=tcp,mountproto=tcp 192.168.1.52:/mnt /media
</pre>
<blockquote>
<p><strong>提示</strong></p>
<p><code>nolock</code> 参数是为了防止如果挂载的主机上没有运行 portmap 或者 rpcbind 服务导致挂载失败的问题（如果直接在 host 主机上挂载，host 的对应服务需要关闭）； <br />
  <code>proto=tcp</code> 和 <code>mountproto=tcp</code> 参数指定 NFS 数据和挂载请求都使用 TCP 协议，防止使用的随机 UDP 端口无法被 slirp 转发的问题。</p>
</blockquote>
<p>这里需要说明的是如果直接在 Docker 容器（UML 的 host 主机）上挂载 NFS，虽然绝大多数 Docker 容器平台都默认支持 NFS 文件系统，但在非特权容器内部由于没有权限 mount 还是会失败的。</p>
<h2 id="summary">总结</h2>
<p>本文介绍的在容器中使用 UML 挂载 FUSE 文件系统并通过 NFS 导出 UML 文件系统的方法是一个比较小众的需求，不过也算达到我的目的了。</p>
<p>对于非特权 Docker 容器来说，虽然还不能直接挂载 UML guest 的文件系统，但初步看起来还是可以通过 <a href="https://github.com/lkl/linux" target="_blank">LKL</a>（Linux Kernel Library）在应用层访问 UML 网络并实现 NFS 挂载的，只是 LKL 库的 NFS 挂载目录并不能直接给 Docker 容器中的普通应用程序使用。</p>
<p>最后祝大家在即将到来的 2018 年能继续玩地开心 ^_^。</p>
]]></content:encoded>
			<wfw:commentRss>https://zohead.com/archives/docker-fuse/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>小内存OpenVZ VPS使用UML开启BBR</title>
		<link>https://zohead.com/archives/openvz-uml-bbr/</link>
		<comments>https://zohead.com/archives/openvz-uml-bbr/#comments</comments>
		<pubDate>Mon, 06 Mar 2017 18:39:55 +0000</pubDate>
		<dc:creator><![CDATA[Uranus Zhou]]></dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[User-mode Linux]]></category>
		<category><![CDATA[技术]]></category>
		<category><![CDATA[网络]]></category>
		<category><![CDATA[BBR]]></category>
		<category><![CDATA[busybox]]></category>
		<category><![CDATA[kernel]]></category>
		<category><![CDATA[OpenVZ]]></category>
		<category><![CDATA[Shadowsocks]]></category>
		<category><![CDATA[ShadowsocksR]]></category>
		<category><![CDATA[TAP]]></category>
		<category><![CDATA[UML]]></category>
		<category><![CDATA[VPS]]></category>
		<category><![CDATA[翻墙]]></category>

		<guid isPermaLink="false">https://zohead.com/?p=1365</guid>
		<description><![CDATA[前言 Bottleneck Bandwidth and RTT（BBR）是 Google 最近提交到 Linux 4.9 版本内核的新 TCP 拥塞控制算法，BBR 相对于 Linux 中已经用了好多年的默认 CUBIC 算法的主要改进是在有一定丢包率的网络链路上充分利用带宽，另外也能有效降低延迟。这对于很多使用美帝 VPS + Shadowsocks 爬墙方案的人来说应该还是比较有用的，目前从一些更新了内核开启 BBR 的 KVM VPS 用户的反馈来看效果也还是不错的。 我目前使用的爬墙服务器是 AlphaRacks 的低配 OpenVZ VPS，内存只有 96 MB，到国内的线路质量倒还 [&#8230;]]]></description>
				<content:encoded><![CDATA[<h2 id="preface">前言</h2>
<p><a href="https://github.com/google/bbr">Bottleneck Bandwidth and RTT（BBR）</a>是 Google 最近提交到 Linux 4.9 版本内核的新 TCP 拥塞控制算法，BBR 相对于 Linux 中已经用了好多年的默认 CUBIC 算法的主要改进是在有一定丢包率的网络链路上充分利用带宽，另外也能有效降低延迟。这对于很多使用美帝 VPS + Shadowsocks 爬墙方案的人来说应该还是比较有用的，目前从一些更新了内核开启 BBR 的 KVM VPS 用户的反馈来看效果也还是不错的。</p>
<p>我目前使用的爬墙服务器是 AlphaRacks 的低配 OpenVZ VPS，内存只有 96 MB，到国内的线路质量倒还凑合，OpenVZ 虽然没办法直接升级 kernel ，但看到 AlphaRacks 控制面板里能开启 TUN/TAP，那可以考虑捣鼓使用 User-mode Linux（UML）来开启 BBR 了。</p>
<p>User-mode Linux 和 QEMU 这种虚拟机稍微有点类似，但 UML 是直接将 Linux 内核编译成可执行的 ELF 应用层程序，这样可以直接在普通 Linux 环境中运行及调试其它版本 Linux 内核的功能，有关 UML 的详细信息请参考其<a href="http://user-mode-linux.sourceforge.net/">官方网站</a>。</p>
<blockquote>
<p><strong>提示</strong></p>
<p>不想自己编译 UML 内核和构建 UML rootfs 的朋友可以直接拉到「<a href="#use-uml-ss-system">使用 UML Shadowsocks 系统</a>」这部分哦。</p>
</blockquote>
<h2 id="compile-uml-kernel">编译 User-mode Linux 内核</h2>
<h3 id="compile-prepare">准备编译环境</h3>
<p>我是直接在 Windows 10 RS2 64 位系统上编译 UML 内核的，使用 Win10 自带的 Bash on Ubuntu 14.04 环境编译还是非常方便的，而且编译速度相比使用虚拟机也是快了很多。</p>
<p>首先安装编译需要的软件包：</p>
<blockquote>
<p><strong>备注</strong></p>
<p>由于我使用的 OpenVZ VPS 系统是 32 位的，所以这里安装 <code>libc-dev</code> 和 <code>libvdeplug-dev</code> 开发包时都指定了使用 32 位版本；如果你需要编译 64 位版本的 UML 内核，那把下面的 <code>-i386</code> 去掉即可。</p>
</blockquote>
<pre class="brush: bash; title: ; notranslate">
zzm@ZZM-P238:~$ sudo apt-get install build-essential libncurses5-dev libc6-dev-i386 libvdeplug-dev-i386
</pre>
<p>这里安装 <code>libvdeplug-dev</code> 开发包是为了支持 UML 的 <a href="http://wiki.virtualsquare.org/wiki/index.php/VDE_Basic_Networking#Step_3:_run_a_User-Mode_Linux">Virtual Distributed Ethernet（VDE）</a>模式网卡设备，VDE 主要是为了让 UML 的网络功能在类似 Docker 这种连 TUN/TAP 都不支持的系统上也能工作起来。</p>
<p>接着可以下载最新 Linux 4.10.1 版本内核源代码，并解压缩准备配置内核选项，4.10.1 版本源代码解压之后不到 800 MB。</p>
<blockquote>
<p><strong>提示</strong></p>
<p>如果像我一样直接在 Win10 Bash 环境中编译，那么最好将内核源代码解压到 Win10 Bash 的 VolFs 文件系统中（也就是 Ubuntu rootfs 目录，一般直接 cd 到 <code>HOME</code> 主目录即可）。</p>
</blockquote>
<h3 id="uml-kernel-config">UML 内核配置</h3>
<p>如果使用默认的 UML 内核配置是比较简单的，直接下面两步即可，第二步内核配置界面里可以手工开启 BBR 功能：</p>
<pre class="brush: bash; title: ; notranslate">
zzm@ZZM-P238:~/linux-4.10.1$ cp arch/um/configs/i386_defconfig .config
zzm@ZZM-P238:~/linux-4.10.1$ make menuconfig ARCH=um
</pre>
<p>不过为了能编译出体积最小又能满足 BBR 爬墙需要的 kernel，我还是使用自己修改的最小配置，首先将下面的内容保存为 <code>mini.config</code> 文件（如果需要编译 64 位 kernel，那直接把开头的 <code>CONFIG_64BIT=n</code> 这一行去掉即可）：</p>
<pre class="brush: bash; title: ; notranslate">
zzm@ZZM-P238:~/linux-4.10.1$ cat mini.config
CONFIG_64BIT=n
CONFIG_BINFMT_ELF=y
CONFIG_BINFMT_SCRIPT=y
CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=n
CONFIG_HOSTFS=y
CONFIG_MCONSOLE=y
CONFIG_MAGIC_SYSRQ=y
CONFIG_KERNEL_STACK_ORDER=1
CONFIG_SWAP=n
CONFIG_SYSVIPC=y
CONFIG_EXPERT=n
CONFIG_EMBEDDED=n
CONFIG_SLUB_DEBUG=n
CONFIG_BLOCK=y
CONFIG_LBDAF=y
CONFIG_DEVTMPFS=y
CONFIG_DEVTMPFS_MOUNT=y
CONFIG_BLK_DEV_BSG=n
CONFIG_BLK_DEV=y
CONFIG_BLK_DEV_UBD=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_IOSCHED_DEADLINE=y
CONFIG_IOSCHED_CFQ=n
CONFIG_DEFAULT_IOSCHED=&quot;deadline&quot;
CONFIG_NETDEVICES=n
CONFIG_STDERR_CONSOLE=y
CONFIG_SSL=y
CONFIG_NULL_CHAN=y
CONFIG_PORT_CHAN=y
CONFIG_PTY_CHAN=y
CONFIG_TTY_CHAN=y
CONFIG_XTERM_CHAN=y
CONFIG_UNIX98_PTYS=y
CONFIG_EXT2_FS=y
CONFIG_PROC_FS=y
CONFIG_PROC_SYSCTL=y
CONFIG_TMPFS=y
CONFIG_SYSFS=y
CONFIG_SCHED_DEBUG=n
CONFIG_NET=y
CONFIG_UNIX=y
CONFIG_INET=y
CONFIG_TCP_CONG_ADVANCED=y
CONFIG_TCP_CONG_CUBIC=y
CONFIG_TCP_CONG_BBR=y
CONFIG_DEFAULT_BBR=y
CONFIG_DEFAULT_TCP_CONG=&quot;bbr&quot;
CONFIG_IPV6=n
CONFIG_SYN_COOKIES=y
CONFIG_NET_SCHED=y
CONFIG_NET_SCH_QFQ=y
CONFIG_NET_SCH_CODEL=y
CONFIG_NET_SCH_FQ_CODEL=y
CONFIG_NET_SCH_FQ=y
CONFIG_NET_SCH_FIFO=y
CONFIG_UML_NET=y
CONFIG_UML_NET_TUNTAP=y
CONFIG_UML_NET_VDE=y
CONFIG_DEBUG_KERNEL=n
CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE=0x1
</pre>
<p>简单说明一下上面的内核配置选项：</p>
<ul>
<li>启用了 UML 的 hostfs 及 mconsole 等很实用的功能；</li>
<li>支持 UML 的 ubd 块设备，也开启了 loop 设备支持，去掉了 ramdisk 等没什么用的其它设备支持；</li>
<li>支持 proc、sysfs、tmpfs 等虚拟文件系统，真实的文件系统只保留了 ext2 支持，需要 ext3 或者 ext4 支持的朋友请自行修改；</li>
<li>开启 BBR 并设置为默认拥塞控制算法，另外需要为 BBR 启用 Fair Queue 包调度器；</li>
<li>去掉了 IPv6 支持，去掉了所有的网卡设备驱动；</li>
<li>去掉了几乎所有的内核调试信息。</li>
</ul>
<p>然后使用 <code>mini.config</code> 生成新的内核配置文件：</p>
<pre class="brush: bash; title: ; notranslate">
zzm@ZZM-P238:~/linux-4.10.1$ make ARCH=um allnoconfig KCONFIG_ALLCONFIG=mini.config
</pre>
<h3 id="compile-kernel">编译内核</h3>
<p>内核选项配置完毕之后，编译就非常简单了：</p>
<pre class="brush: bash; title: ; notranslate">
zzm@ZZM-P238:~/linux-4.10.1$ make ARCH=um vmlinux -j4
</pre>
<p>这里我开启了 4 个并发编译（make 的 <code>-j4</code> 参数）支持，UML 的 vmlinux 可执行程序在我的笔记本上不到 4 分钟就编译完成了，最终编译出来的 vmlinux 文件大小大概为 3 - 4 MB。</p>
<p>你可以试着运行编译出来的 vmlinux 可执行程序查看编译时使用的内核配置：</p>
<pre class="brush: bash; title: ; notranslate">
[root@localhost ~]# ./vmlinux --showconfig
</pre>
<h2 id="prepare-uml-rootfs">准备 UML rootfs</h2>
<h3 id="prepare-rootfs-ss-libev">准备 rootfs 和 shadowsocks-libev</h3>
<p>既然 UML 内核我都是按最小化编译的，那运行的 rootfs 自然不能用像 Ubuntu、CentOS 那种「庞大」的系统了，这里我直接使用最小化的 busybox rootfs + Shadowsocks 服务端程序和运行库的方式，除了 busybox 基础环境和 Shadowsocks 服务端程序基本没有添加任何其它东西。</p>
<p>最终我将 UML vmlinux 内核文件和 rootfs 目录一起压缩打包起来大小也不过才 3 MB，这样能非常方便地上传到 VPS 上和分享给其他人。当然缺点就是如果需要安装其它的软件那只能手工拷贝到 rootfs 目录中了。</p>
<p>具体 busybox rootfs 的制作方法这里我就不做详细说明了，感兴趣的朋友可以参考相关的文档了解一下，我分别制作了 32 位和 64 位的 busybox rootfs 目录。</p>
<p>另外为了减少 UML 系统的程序依赖和内存占用，Shadowsocks 服务端我选择的是 <a href="https://github.com/shadowsocks/shadowsocks-libev">shadowsocks-libev</a>，这里我也分别编译了最新 3.0.3 版本 32 位和 64 位的服务端程序。</p>
<p>32 位 shadowsocks-libev 我是在 32 位 Ubuntu 14.04 系统下编译的，依赖的动态库也是使用的 Ubuntu 14.04 自带的库文件；64 位 shadowsocks-libev 则是在 64 位 CentOS 6 系统下编译的，所以 64 位运行库版本和 32 位的不同。你问我为什么不用同一个系统？因为我刚好有这两个系统的 VPS 能用来编译，实在是懒的换了 ^_^。</p>
<h3 id="test-uml-rootfs">测试 UML rootfs</h3>
<p>由于 OpenVZ 系统内核不支持 loop 设备，不能挂载文件系统映像进行修改，这里我直接使用 UML 的 hostfs 将 OpenVZ 主机系统的一个目录设为 UML 系统的 rootfs。</p>
<p>现在 UML hostfs 并没有某些网友说的存在只读的限制，而且相比使用 loop 设备或者 ubd 文件系统这种方式，hostfs 并不需要预先产生一个很大的文件，主机也能直接修改 UML rootfs 目录内容，UML 内核也根本不需要支持 ext3、ext4 这种真实的文件系统，综合比较下来实在是没有什么理由不用 hostfs。</p>
<p>首先安装 UML 工具 <code>uml-utilities</code> 软件包，后面的 <code>tunctl</code> 和 <code>uml_mconsole</code> 等命令会用到：</p>
<pre class="brush: bash; title: ; notranslate">
root@zohead:~# apt-get install uml-utilities
</pre>
<p>接着可以配置 OpenVZ 系统上的 TUN/TAP 虚拟网卡和开启端口转发：</p>
<pre class="brush: bash; title: ; notranslate">
root@zohead:~# tunctl -u root
Set 'tap0' persistent and owned by uid 0
root@zohead:~# ifconfig tap0 192.168.0.3 up
root@zohead:~# echo 1 &gt; /proc/sys/net/ipv4/ip_forward
root@zohead:~# iptables -t nat -I POSTROUTING -o venet0 -j MASQUERADE
root@zohead:~# iptables -t nat -A PREROUTING -i venet0 -p tcp --dport 9443 -j DNAT --to-destination 192.168.0.4
root@zohead:~# iptables -t nat -A PREROUTING -i venet0 -p udp --dport 9443 -j DNAT --to-destination 192.168.0.4
</pre>
<blockquote>
<p><strong>备注</strong></p>
<p>上面的命令中假设 TUN/TAP 虚拟网卡 IP 地址为 <code>192.168.0.3</code>（可以自行修改），UML 系统网卡 IP 地址为 <code>192.168.0.4</code>（需要和 TUN/TAP 虚拟网卡地址在同一网段），只将 OpenVZ 主机系统上 <code>9443</code> 端口的 TCP 和 UDP 数据转发到 UML 系统网卡上。</p>
</blockquote>
<p>最后是启动 UML 系统的例子：</p>
<pre class="brush: bash; title: ; notranslate">
root@zohead:~# ./vmlinux-i686 umid=shadowsocks root=/dev/root rootfstype=hostfs rootflags=/root/umlfs-i686 rw mem=48M init=/sbin/init eth0=tuntap,tap0
</pre>
<p>简单说明一下 UML vmlinux 程序的参数：</p>
<ul>
<li><code>umid</code> 参数指定 UML 系统 ID，建议每个 UML 系统都使用的固定的 ID，不然每次运行都会在 <code>~/.uml</code> 目录下产生新的临时目录；</li>
<li><code>rootflags</code> 参数指定 UML rootfs 路径，这里 <strong>必须</strong> 使用绝对路径；</li>
<li><code>mem</code> 参数指定 UML 系统的内存大小，经过我的测试，只运行 shadowsocks-libev 服务端的话就算分配 32 MB 内存看起来也没什么压力；</li>
<li><code>init</code> 参数指定 UML 系统的 init 进程，不建议修改；</li>
<li><code>eth0=tuntap,tap0</code> 则是指定 UML 的 tuntap 模式网卡，使用 OpenVZ 主机系统的 <code>tap0</code> 虚拟网卡设备。</li>
</ul>
<h2 id="use-uml-ss-system">使用 UML Shadowsocks 系统</h2>
<h3 id="uml-linux-sh">uml-linux.sh 脚本</h3>
<p>为了使用起来方便，我把启动 UML 系统和设置端口转发的操作写成了一个 <code>uml-linux.sh</code> 脚本，这样 OpenVZ 系统启动之后可以根据配置自动运行并配置端口转发：</p>
<pre class="brush: bash; title: ; notranslate">
#!/bin/sh
UML_ID=&quot;shadowsocks&quot;
TAP_DEV=&quot;tap0&quot;
ROUTER_DEV=&quot;venet0&quot;
TAP_ADDR=&quot;192.168.0.3&quot;
UML_ADDR=&quot;192.168.0.4&quot;
REV_TCP_PORTS=&quot;&quot;
REV_UDP_PORTS=&quot;&quot;
FWD_TCP_PORTS=&quot;9443&quot;
FWD_UDP_PORTS=&quot;9443&quot;

DIR=&quot;$( cd &quot;$( dirname &quot;$0&quot; )&quot; &amp;&amp; pwd )&quot;

[ ! -d /sys/class/net/$TAP_DEV ] &amp;&amp; tunctl -t $TAP_DEV
ifconfig $TAP_DEV $TAP_ADDR up

echo 1 &gt; /proc/sys/net/ipv4/ip_forward

iptables -t nat -I POSTROUTING -o $ROUTER_DEV -j MASQUERADE

for i in $REV_TCP_PORTS; do
	if [ &quot;x$i&quot; = &quot;x*&quot; ]; then
		P_TMP=&quot;&quot;
	else
		P_TMP=&quot;--dport $i &quot;
	fi
	iptables -t nat -A PREROUTING -p tcp $P_TMP-j RETURN
done
for i in $REV_UDP_PORTS; do
	if [ &quot;x$i&quot; = &quot;x*&quot; ]; then
		P_TMP=&quot;&quot;
	else
		P_TMP=&quot;--dport $i &quot;
	fi
	iptables -t nat -A PREROUTING -p udp $P_TMP-j RETURN
done

for i in $FWD_TCP_PORTS; do
	if [ &quot;x$i&quot; = &quot;x*&quot; ]; then
		P_TMP=&quot;&quot;
	else
		P_TMP=&quot;--dport $i &quot;
	fi
	iptables -t nat -A PREROUTING -i $ROUTER_DEV -p tcp $P_TMP-j DNAT --to-destination $UML_ADDR
done
for i in $FWD_UDP_PORTS; do
	if [ &quot;x$i&quot; = &quot;x*&quot; ]; then
		P_TMP=&quot;&quot;
	else
		P_TMP=&quot;--dport $i &quot;
	fi
	iptables -t nat -A PREROUTING -i $ROUTER_DEV -p udp $P_TMP-j DNAT --to-destination $UML_ADDR
done

nohup $DIR/vmlinux-i686 umid=$UML_ID root=/dev/root rootfstype=hostfs rootflags=$DIR/umlfs-i686 rw mem=48M init=/sbin/init con=pts eth0=tuntap,$TAP_DEV,,$UML_ADDR &amp;
</pre>
<p>一般只要修改脚本最开始的几行配置即可（最后一行 vmlinux 程序和 UML rootfs 路径默认使用脚本所在路径，如果不同请自行修改）：</p>
<ul>
<li><code>TAP_ADDR</code> 为 TUN/TAP 虚拟网卡地址，<code>UML_ADDR</code> 为 UML 系统内网卡地址；</li>
<li><code>REV_TCP_PORTS</code> 和 <code>REV_UDP_PORTS</code> 分别指定 OpenVZ 主机系统需要保留哪些 TCP 和 UDP 端口不转发到 UML 系统（多个以空格隔开），一般如果 UML 系统使用固定的一些端口，那么默认留空即可；</li>
<li><code>FWD_TCP_PORTS</code> 和 <code>FWD_UDP_PORTS</code> 分别指定哪些 TCP 和 UDP 端口需要转发到 UML 系统（默认只写了一个 <code>9443</code>，实际如果需要多个则以空格隔开），如果需要指定端口范围也可以写成 <code>15000:20000</code>，如果需要所有端口都转发到 UML 系统那可以直接写成 <code>*</code>（此时 <code>REV_TCP_PORTS</code> 一般最少要留一个端口用于 SSH 远程登录）；</li>
<li>需要分配给 UML 系统的内存大小请自行修改 vmlinux 的 <code>mem</code> 参数。</li>
</ul>
<h3 id="uml-ss-config">UML Shadowsocks 系统配置</h3>
<p>另外 UML 系统内部的网卡地址配置及启动 shadowsocks-libev 服务端的操作都在 <code>/etc/rc.local</code> 脚本中：</p>
<pre class="brush: bash; title: ; notranslate">
#!/bin/sh
if [ -d /sys/class/net/eth0 ]; then
	ifconfig eth0 192.168.0.4 up
	route add default gw 192.168.0.3 dev eth0
	sleep 2
fi

SS_DNS=&quot;&quot;

[ -f /etc/default/pdnsd ] &amp;&amp; . /etc/default/pdnsd

# start local DNS server if needed
if [ &quot;x$START_DAEMON&quot; = &quot;xyes&quot; -o &quot;x$START_DAEMON&quot; = &quot;xYES&quot; ]; then
	pdnsd -d
	[ $? -eq 0 ] &amp;&amp; SS_DNS=&quot;-d 127.0.0.1 &quot;
fi

for i in `ls /etc/shadowsocks-libev/*.json 2&gt;/dev/null`; do
	SS_NAME=`basename $i .json 2&gt;/dev/null`
	start-stop-daemon -n shadowsocks-$SS_NAME -x ss-server -b -S -- -c $i -u $SS_DNS--fast-open
done

exit 0
</pre>
<p>简单说明一下：</p>
<ul>
<li>这里的 <code>192.168.0.4</code> 网卡 IP 地址就对应上面的 <code>UML_ADDR</code>，<code>192.168.0.3</code> 网关地址就是 <code>TAP_ADDR</code>，因此如果上面脚本中的地址修改了，那么 UML rootfs 的 <code>/etc/rc.local</code> 中的地址也需要修改；</li>
<li>你可以将 shadowsocks-libev 配置文件放到 <code>/etc/shadowsocks-libev</code> 目录中，此脚本会自动遍历并一一启动 <code>ss-server</code> 服务器，同时默认也开启了 TCP Fast Open 和 UDP 转发；</li>
<li>另外 rootfs 里集成了 <code>pdnsd</code> DNS 服务器，如果需要实现 DNS 缓存加速或者 UML 系统的 UDP DNS 解析存在问题，都可以启用 <code>pdnsd</code> 本地 DNS 服务器。</li>
</ul>
<p>配置修改完成之后，运行 <code>uml-linux.sh</code> 脚本就会在后台启动 UML 系统，并根据需要自动启动 shadowsocks-libev 服务器。</p>
<h3 id="control-uml-system">控制 UML 系统</h3>
<p>如果你需要登录到 UML 系统，可以先用 <code>uml_mconsole</code> 命令得到 UML 系统分配到的虚拟终端（第二个参数就是 UML 系统 ID）：</p>
<pre class="brush: bash; title: ; notranslate">
root@zohead:~# uml_mconsole shadowsocks config con0
OK pts:/dev/pts/2
</pre>
<p>然后使用 <code>screen</code> 命令就可以连接到该虚拟终端上（默认什么都不显示，需要按回车键终端才会出来）：</p>
<pre class="brush: bash; title: ; notranslate">
root@zohead:~# screen /dev/pts/2
</pre>
<p>如果想在 OpenVZ 主机系统里直接关闭整个 UML 系统，那也非常简单：</p>
<pre class="brush: bash; title: ; notranslate">
root@zohead:~# uml_mconsole shadowsocks halt
</pre>
<h3 id="uml-ss-download">UML Shadowsocks 系统下载</h3>
<p>为了方便大家使用，我把 32 位和 64 位的 UML 4.10.1 版本 vmlinux 内核程序及配置和 Shadowsocks 服务端 rootfs 目录、<code>uml-linux.sh</code> 脚本一起打包分享给大家。</p>
<p>32 位的 UML Shadowsocks 系统（百度网盘备用）：</p>
<p><a href="https://zohead.com/downloads/uml-shadowsocks-i686.tar.xz">https://zohead.com/downloads/uml-shadowsocks-i686.tar.xz</a> <br />
<a href="https://pan.baidu.com/s/1kVdoWcB" target="_blank">https://pan.baidu.com/s/1kVdoWcB</a></p>
<p>64 位的 UML Shadowsocks 系统：</p>
<p><a href="https://zohead.com/downloads/uml-shadowsocks-x64.tar.xz">https://zohead.com/downloads/uml-shadowsocks-x64.tar.xz</a> <br />
<a href="https://pan.baidu.com/s/1dFDta9N" target="_blank">https://pan.baidu.com/s/1dFDta9N</a></p>
<p>将上面的文件下载到 VPS 某个目录并解压缩，修改 <code>uml-linux.sh</code> 文件中的转发端口，将 <a href="https://wiki.archlinux.org/index.php/Shadowsocks" target="_blank">Shadowsocks 服务端配置文件</a>放到 rootfs 的 <code>/etc/shadowsocks-libev</code> 目录，之后以 root 身份运行 <code>uml-linux.sh</code> 脚本即可。</p>
<blockquote>
<p><strong>提示</strong></p>
<p>如果需要让 ShadowsocksR 服务器使用 TCP 等方式进行域名解析，可以修改 <code>/etc/default/pdnsd</code> 文件将其中的 <code>START_DAEMON</code> 改为 <code>yes</code> 来启用 <code>pdnsd</code> 本地 DNS 服务器。</p>
</blockquote>
<h3 id="uml-ssr-download">UML ShadowsocksR 系统</h3>
<p>考虑到某些网友的需要，我还生成了 32 和 64 位的 UML ShadowsocksR 系统，都是使用的最新支持混淆的 Python 版本 ShadowsocksR 服务器，有需要的朋友可以下载测试（百度网盘为备用下载地址）。</p>
<p>32 位的 UML ShadowsocksR 系统：</p>
<p><a href="https://zohead.com/downloads/uml-shadowsocksr-i686.tar.xz">https://zohead.com/downloads/uml-shadowsocksr-i686.tar.xz</a> <br />
<a href="https://pan.baidu.com/s/1eSDrWye">https://pan.baidu.com/s/1eSDrWye</a></p>
<p>64 位的 UML ShadowsocksR 系统：</p>
<p><a href="https://zohead.com/downloads/uml-shadowsocksr-x64.tar.xz">https://zohead.com/downloads/uml-shadowsocksr-x64.tar.xz</a> <br />
<a href="https://pan.baidu.com/s/1o7F3qpc">https://pan.baidu.com/s/1o7F3qpc</a></p>
<p>ShadowsocksR 系统的 rootfs 只包含了基础 Python 环境和 ShadowsocksR 服务器程序，同样下载并解压缩，由于包含了 Python 环境，该 rootfs 解压缩之后接近 30 MB。</p>
<p>使用前可根据需要修改 <code>uml-linux.sh</code> 文件中的转发端口，将 <a href="https://github.com/breakwa11/shadowsocks-rss/wiki/config.json" target="_blank">ShadowsocksR 服务器 JSON 格式配置文件</a>放到 rootfs 的 <code>/etc/shadowsocks</code> 目录，之后以 root 身份运行 <code>uml-linux.sh</code> 脚本即可（启用 <code>pdnsd</code> 本地 DNS 服务器的方法和上面的相同）。</p>
<h2 id="uml-bbr-compare">UML 系统 BBR 效果</h2>
<p>最后对比一下我的 AlphaRacks OpenVZ VPS 上启用 BBR 前后的对比速度，这里我就直接观看同一个 1080p 的 YouTube 视频来对比统计信息了。</p>
<p>在我的江苏移动宽带环境下，启用 BBR 之前 YouTube 视频连接速度一般只能跑到 1Mbps 左右：</p>
<p><img src="http://res.cloudinary.com/digwht2y0/image/upload/v1737442863/openvz-ss-speed.png" alt="OpenVZ 未启用 BBR 的 YouTube 速度" title="OpenVZ 未启用 BBR 的 YouTube 速度"></p>
<p>而在使用 UML Shadowsocks 系统开启 BBR 之后能跑到 2Mbps，不过有的时候速度还是不太稳定：</p>
<p><img src="http://res.cloudinary.com/digwht2y0/image/upload/v1737442867/openvz-ss-speed-bbr.png" alt="OpenVZ 启用 UML BBR 的 YouTube 速度" title="OpenVZ 启用 UML BBR 的 YouTube 速度"></p>
<p>UML 目前也是主要用于调试验证 Linux 内核功能的，总之 OpenVZ 使用 UML 开启 BBR 的实际使用效果还是需要长期观察测试的，本文也主要关注 UML 系统下使用 Shadowsocks 的问题，并没有考虑通用的情况，如果文章中有任何问题还请提出指正，祝大家玩的开心。</p>
]]></content:encoded>
			<wfw:commentRss>https://zohead.com/archives/openvz-uml-bbr/feed/</wfw:commentRss>
		<slash:comments>40</slash:comments>
		</item>
	</channel>
</rss>
