Lambdaカクテル

集団への盲従を激しく嫌う

FreeBSDのKVMことbhyveでdockerを動かした話

リモートホスト(FreeBSD 11.1)にbhyveを使った仮想マシンとしてUbuntuをホストさせ,そこでdockerdをセットアップし,そこにIPv6 TCP通信を介してローカルマシン(FreeBSD 11.1)から接続した記録です.

動機

FreeBSD上でDockerを動かす試みはあるものの,未だに試験的存在の域を離れず,満足に動作するとは言い難い状況にある. とはいえコンテナエコシステムはdockerが圧倒的優位で,(Docker以外はコンテナじゃないよね〜という驕りは許せないが,)イメージを指定したら適当に拾ってコンテナが立つ便利さを知ってしまうとjailでは不満が残る. そこでdocker-machineの要領でlinuxマシンを仮想的に動作させ,その上でdockerdを起動することにした. FreeBSDで利用できる仮想化環境としてbhyveがあるため,これの上でUbuntu Serverをセットアップした. 開発環境であるローカルマシンの負荷を上げて開発に支障をきたさぬよう,bhyveはリモートホストで動作させることにした.

登場人物

  • bhyve
  • Docker
    • 言わずと知れたコンテナツール.覇権になった.
  • vm-bhyve
    • 面倒なbhyveの操作を簡単にしてくれるshラッパー.
  • IPv6
    • みんなの夢

結論

ネットワークを介したファイルシステムのマウントの煩雑さを除けば,十分実用できそうである.

bhyveをインストールする

bhyveは単体では使いにくいので,ラッパーであるところのvm-bhyveを使います.

仮想マシンを動かしたいホストで以下のようにします.ちなみにOSはFreeBSD 11.1です. だいたい公式ページのQuick startの通りにしていれば動きます. 必要に応じてsudoとか使うとよさそう.

# vm-bhyve入れる.
pkg install vm-bhyve

# bhyveとvm-bhyveが使う領域を確保する.ISOとかイメージファイルとかが入るので大きめ推奨.
zfs create POOL_NAME/vm # プール名はよしなに環境に合わせて設定する.vm部分も変更できる.

# 起動時にvm-bhyveが有効になるようにする
echo 'vm_enable="YES"' >> /etc/rc.conf
# vm-bhyveが使う領域を指定する.ふつうのディレクトリを指定することもできる.(割愛)
echo 'vm_dir="zfs:POOL_NAME/vm"' >> /etc/rc.conf

# 上掲の領域に構成ディレクトリを掘ったり,vmmカーネルモジュールを有効化したりする.
vm init

# 仮想マシンのスペックを決めるテンプレートファイルをサンプルからコピってくる.
cp /usr/local/share/examples/vm-bhyve/* /mountpoint/for/pool/vm/.templates/

# 仮想マシンがぶら下がるための仮想スイッチを'public'という名前で作成する.
vm switch create public

# igb0を仮想スイッチに接続する.
vm switch add public igb0 # LAN側NICを指定した.NIC名はよしなに環境に合わせる.

ここまでbhyveのインストール.ここから仮想マシンのインストールである.ここまでの操作は2つ目の仮想マシンを立てる際は不要.

# インストールに使うISOイメージを拾ってこさせる.今回はUbuntu serverを使う.
vm iso http://cdimage.ubuntu.com/releases/18.04/release/ubuntu-18.04-server-amd64.iso

# 「ubuntu-dev」として仮想マシンを作成する.
# -tで上掲のテンプレートを指定する.ubuntuテンプレートもサンプルにあるので,これを使った.
# -sで仮想マシンのために作成するディスクイメージのサイズを指定する.ひとまず50GiBにする.
vm create -t ubuntu -s 50G ubuntu-dev

# 仮想マシンが起動するとシャットダウンするまでホストに戻れないので,tmuxなどを起動しておく.
tmux

# installコマンドでISOを起動し,インストール作業を行う.
# -fオプションで仮想マシンをフォアグラウンドで起動する.
# フォアグラウンドで起動しなくても,vm consoleコマンドで仮想マシンにアタッチできる.
vm -f install ubuntu-dev ubuntu-18.04-server-amd64.iso

ほぼ公式の手順を踏んでUbuntuをインストールしようとしてきたが,ここまで来て起動しない.

ちょっと調べたところ,bhyveでlinuxを起動するにはbhyve用のGRUBを入れないといけないようだ. https://blog.bixr.com/2016/07/1178/

pkg install grub2-bhyve

するとうまくインストーラが起動した.

# インストーラを再び起動する
vm -f install ubuntu-dev ubuntu-18.04-server-amd64.iso

今回はリモートホストでbhyveを利用しているので,仮想ホストを使うたびにリモートに接続するのはおっくうだ. インストールの過程で,sshdを起動するように設定しておいたほうがよい.ここは単にインストーラ中に操作するだけなので割愛する. また,鍵の生成やsshdの設定も忘れずに行う. ちなみに,自分の環境ではリモートホストがルータとしても動作しているので,ローカルマシンからそのまま仮想マシンに接続できるようになる.

dockerのセットアップ

インストールした仮想マシンを起動しておく.

vm start ubuntu-dev

ここからは仮想マシンでの操作である.

ssh ADDRESS_OF_ubuntu-dev

通常のDockerのインストールと特に変わった点はない.自分の環境固有かもしれないが,うまくレポジトリを 見付けられずにdocker-ceを発見できないことがあった.適当にネットサーフィンして解決したが,bhyveとは特に関係ないので割愛する.

さてDockerがセットアップされたら,DockerデーモンがTCPポートをリッスンするように設定する. dockerdがTCPをリッスンするには,dockerdの起動時にdockerd -H tcp://0.0.0.0:2375のように指定してやればよい. dockerdは自分の環境ではsystemdによって起動していたので,/etc/systemd/system/multi-user.target.wants/docker.serviceを編集する.

vi /etc/systemd/system/multi-user.target.wants/docker.service

ExecStartの箇所にdockerdの起動オプションが記述されているので,ここに前述のオプションを書き足す. 自分の環境ではIPv6を使用したいので,IPv6でURIを記述した.アドレスはユニークローカルを指定している.

ExecStart=/usr/bin/dockerd -H fd:// -H tcp://[fdda:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx]:2375

dockerdを再起動する.

systemctl restart dockerd

ネットワーク越しに接続するために,2375番ポートを開放する.今回はローカルなLAN内で行ったので開放のみを行っているが, インターネットに露出しうる環境ではポートがインターネットに露出しないように気をつける必要がある.場合によってはIPアドレスによる制限などをかける必要がある. とはいえ今回はユニークローカルアドレスのみをリッスンさせている.

ufw allow 2375

ローカルマシンでのdockerのセットアップ

ローカルマシンもリモートホスト同様FreeBSD11.1である.dockerはクライアントとサーバデーモンがREST APIを通じて分離しているので, クライアントだけをインストールしてリモートのdockerdと交信することができる.リモートのdockerdを使う場合であってもローカルのDockerfiledocker-compose.ymlを利用できる.コンテナはリモートに作成される.

dockerクライアントにdockerdのエンドポイントを指示するには,DOCKER_HOST環境変数を設定するか,直接docker -H tcp://...のように-Hオプションを使う.

pkg install docker docker-compose
export DOCKER_HOST=tcp://[fdda:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx]:2375

ただし,ボリュームマウントでローカルのファイルシステムを読み書きすることはできない.例えば,-v /hoge:/fugaのように指定しても,ローカルの/hogeがバインドされるわけではなく,仮想マシンから見た/hogeをコンテナの/fugaにバインドしようとする.

ひとまずこれでbhyve上のdockerdを使ってhello-worldができるようになった.

docker run hello-world
Hello from Docker! ...