在FreeBSD上使用Docker

(本文不定期增补)

需求

FreeBSD是个好东西,因为有ZFS。Docker也是个好东西,因为很爽。

然而之前Docker只支持Linux,所以在其它平台上都需要搞个Linux虚拟机才能用,麻烦得很。

当然现在也是一样,只是官方出了一个docker-machine可以更方便地管理虚拟机里的docker。基本用法可以参考这篇《Docker on FreeBSD

不过为了折腾这个东西,我把家里的服务器搞崩溃好多次,最后甚至不得不重装一遍……前前后后折腾了快一个月。

现在把这个血泪教训总结成本文。

准备环境

docker-machine依赖虚拟机环境,这里以最常用的VirtualBox为例。

我的服务器上本来就跑着VBox,所以当时就直接拿来用了,结果可耻滴失败了。

首先是需要系统版本:FreeBSD 11以上才提供了docker-machine,所以必须先把我的10升级一下。升级方法以前说过(见《FreeBSD升级失败的处理》),这里从略。

升级完成之后还是不能用,创建新machine的时候各种报错,比如:

Error creating machine: Error in driver during machine creation: Unable to start the VM: /usr/local/bin/VBoxManage startvm dockerhost --type headless failed:
VBoxManage: error: The VMMR0.r0 module version does not match VBoxVMM.dll/so/dylib. If you just upgraded VirtualBox, please terminate all VMs and make sure th
at neither VBoxNetDHCP nor VBoxNetNAT is running. Then try again. If this error persists, try re-installing VirtualBox. (VERR_VMM_R0_VERSION_MISMATCH)
VBoxManage: error: Details: code NS_ERROR_FAILURE (0x80004005), component ConsoleWrap, interface IConsole

Details: 00:00:00.488143 Power up failed (vrc=VERR_VMM_R0_VERSION_MISMATCH, rc=NS_ERROR_FAILURE (0X80004005))

看上去是某些驱动或服务没有正常工作。于是参考VirtualBox文档,把vboxdrv和vboxnet都启用,然后重启……恭喜,系统崩溃了……

查了很久也没查出原因,我甚至另外搞了一台电脑安装了FreeBSD11重试,还是一样的问题。

只要下面任意一个命令运行就崩溃:

service vboxnet onestart
kldload vboxdrv.ko

搜了很久也没找到原因,搞了好几天,直到今天搜到有人说了一句:可能跟系统不完全兼容。我灵光一闪——对了,我的VBox是通过pkgng安装的,有可能不兼容。于是果断换成多年不用的ports:

portsnap fetch
portsnap extra  # 或update,如果已经安装了ports的话
cd /usr/ports/emulators/virtualbox-ose
make config
make
make install clean

然而仍然没有那么顺利,因为旧版的还在……

pkg delete virtualbox-ose

删除后再编译,仍然会有一些冲突的依赖包,全部删除掉改用ports的版本。VBox这东西依赖还是相当多的,断断续续编译了两天才算完成。最后还编译安装了一把virtualbox-ose-kmod

为了保险起见,docker-machinedocker-compose我也是用ports的版本。

需要注意的是,如果你想用非root用户操作的话,需要先允许这个用户操作虚拟机:

pw groupmod vboxusers -m yourname

用户需要重新登录一下,如果用了screen或tmux之类的,也需要新开会话(不是新开窗口)才会生效。

另外,为了充分利用ZFS,创建一个ZFS供machine使用:

zfs create -o mountpoint=/home/yourname/.docker tank/docker

这样创建的虚拟机就会在tank/docker这个ZFS里了。

docker-machine

重点来了,为了使用docker,先得创建一个machine,这就需要使用前面安装的docker-machine了:

docker-machine create --driver virtualbox \
    --virtualbox-memory 2048 \
    --virtualbox-cpu-count 1 \
    --virtualbox-disk-size 204800 \
    --virtualbox-share-folder "/home/user/project:/var/project" \
    dockerhost

上面的命令创建了一个叫做dockerhost的虚拟机,内存2G,CPU一颗,硬盘200G,并且自动运行起来。当然如果使用default作为虚拟机名则后面的一些操作可以省略虚拟机名字。

然后就可以对这个machine进行一系列操作了。

docker-machine ls
docker-machine stop dockerhost  # 停止虚拟机
docker-machine start dockerhost  # 启动虚拟机
docker-machine rm dockerhost  # 删除虚拟机

成功创建或启动一个machine之后,就可以操作docker了,不过操作之前需要设置一下环境:

eval `docker-machine env dockerhost`

主要是就是设置几个环境变量给docker使用。

如果使用root用户,需要注意的是:

root用户的默认shell是csh,并不支持这个命令,必须使用bash。

其它用户如果也是用csh,也要注意这一点。

docker

现在终于可以开始使用docker了:

docker images
docker ps
docker run helloworld

大功告成!

文件路径映射

但是还没完,docker-machine里的文件路径是映射过的,所以现在试试这个是不会成功的:

docker run -it --rm -v /home/yourname:/var/workdir alpine /bin/sh
# cd /var/workdir
# touch test
# exit
> cd /home/yourname
> ls test  # 并没有这个文件

因为-v映射的路径不正确,必须使用在machine里映射过的路径。默认的映射路径是:

share => /home

所以上面那个测试可以改为:

docker run -it --rm -v /share/yourname:/var/workdir alpine /bin/sh

这样再做上面的测试就可以成功了。

当然那个默认映射可以自己在VirtualBox里修改,或者在创建docker-machine的时候指定。

连接虚拟机

前面已经说了docker-machine创建的docker machine是一个虚拟机,那么如何直接访问虚拟机呢?打开一个GUI的VirtualBox当然是一个方法,但是太笨重了。

更好的办法当然是用SSH连过去,毕竟里面跑的本来就是一个Linux(用的发行版是Tiny Core Linux)。连接方法如下:

先查看虚拟机的相关信息:

docker-machine inspect dockerhost

dockerhost就是前面创建虚拟机名字。结果中会有如下的信息:

{
    "ConfigVersion": 3,
    "Driver": {
        "IPAddress": "192.168.xx.xx",
        "MachineName": "dockerhost",
        "SSHUser": "docker",
        "SSHPort": xxxx,
        "SSHKeyPath": "/path_to/id_rsa",
        ...
    }
    ...
}

通过下面的方法就可以直接连接虚拟机了:

ssh -i /path_to/id_rsa docker@192.168.xx.xx

连上以后直接sudo即可用root用户运行命令,不需要密码。

通过LAN访问

默认的docker machine只创建了两个网络连接:一个NAT和一个HOST。其中NAT中用给container们提供上网功能,HOST用来供docker命令连接操作machine中的docker服务。

如果运行container的时候使用-p绑定端口(默认地址为0.0.0.0),结果也只能是把端口绑定到HOST网络中,也就是说,只能用本地主机上访问那个192.168.x.x的地址,并不能通过LAN来访问。

放狗搜过,2015年就有人提议docker-machine使用Bridge网络,以同时提供HOST和LAN的访问支持,但是至今没有,可能是因为Bridge涉及虚拟机地址们分配问题,各种LAN环境并不好搞,所以官方一直没有提供。

推送到[go4pro.org]