Docker for Mac が遅いので代わりに VirtualBox 上で Docker を使う


Node.js の開発では Docker for Mac が便利ですが、仕組み的にディスクアクセスが遅くなるので、処理性能が半分とまではいかないものの、3割くらい遅いです。そこで Docker for Mac のお手軽環境を諦めて、代わりに VirtualBox 上で Docker を使うと、ほとんど性能劣化なく動くようです。

VirtualBox 上で動かす VM は何でも良いのですが、プロダクション環境は AWS が多いので、今回は VM にも Amazon Linux 2 を使うことにします。各ファイルも VirtualBox 上の VM 側に置いて、Mac から NFS でアクセスします。

(1) VirtualBox をインストールする。

https://www.virtualbox.org/wiki/Downloads の「OS X hosts」から、VirtualBox をダウンロード・インストールする。120MB くらい。

(2) VirtualBox > ファイル > ホストネットワークマネージャー > 作成

  • 作成ボタンを押すと、ネットワーク vboxnet0 ができる。
  • IPv4 アドレスをメモしておく。ここでは「192.168.56.1/24」になっている。

(3) Amazon Linux 2 の VM イメージをダウンロードする。

https://cdn.amazonlinux.com/os-images/latest/virtualbox/ から
最新の VM イメージ amzn2-virtualbox-2.0.20200304.0-x86_64.xfs.gpt.vdi をダウンロードする。1.5GB。
(Amazon Linux 2 LTS 2.0.20200304.0 x86_64 VirtualBox image)

(4) cloud-init 用の ISO イメージ (cidata) を作成する

mkdir -p ~/Desktop/amzn2
cd ~/Desktop/amzn2
mkdir seedconfig

cat <<__EOT__>seedconfig/meta-data
local-hostname: amzn2
__EOT__

cat <<__EOT__>seedconfig/user-data
#cloud-config
# ↑1行目の「#cloud-config」を抜かないこと。

# デフォルトで ec2-user ユーザが作成される。
users:
  - default

# ec2-user ユーザのパスワードを指定する。: の後にスペースを入れると、スペース入りのパスワードになるので注意。
chpasswd:
  list:
    - "ec2-user:amazon"

# あとで eth1 を作るために、vboxnet0 のネットワーク内の IP アドレスを指定する。
write_files:
  - path: /etc/sysconfig/network-scripts/ifcfg-eth1
    content: |
      BOOTPROTO=static
      DEVICE=eth1
      IPADDR=192.168.56.78
      ONBOOT=yes
      TYPE=Ethernet
      PREFIX=24
      PEERDNS=no
      IPV6INIT=no
      DEFROUTE=no
      EC2SYNC=no
      USERCTL=no
      PERSISTENT_DHCLIENT=no
__EOT__

mkdir -p ~/VirtualBox\ VMs

# ISO イメージを作成する
hdiutil makehybrid -o ~/VirtualBox\ VMs/seed.iso -hfs -joliet -iso -default-volume-name cidata seedconfig/

# ダウンロードした VM イメージも、コピーしてから使う
cp -p ~/Downloads/amzn2-virtualbox-2.0.20200304.0-x86_64.xfs.gpt.vdi ~/VirtualBox\ VMs/amzn2.xfs.dvi

(5) VirtualBox > 仮想マシン > 新規

  • 名前:amzn2
  • マシンフォルダー:/Users/xxxx/VirtualBox VMs(デフォルト)
  • タイプ:Linux
  • バージョン:Linux 2.6 / 3.x / 4.x (64-bit)
  • メモリーサイズ:2048MB
  • ハードディスク:すでにある仮想ハードディスクファイルを使用する → amzn2.xfs.dvi を選択

(6) amzn2 > 設定 > ストレージ

  • コントローラー:IDE の右側の [+] ボタンから、seed.iso を追加する。
  • 画面下部の「新しいストレージコントローラを追加します」「新規のストレージ割り当てを追加します」ではなくて、「光学ドライブの追加」を選ぶ。

(7) amzn2 > 設定 > ネットワーク > アダプター1

  • ネットワークアダプターを有効化:ON(まま)
  • 割り当て:NAT
  • 備考:アダプター1= eth0 は、VM からの上り回線用。Mac 側の上流回線が何でもOK。LAN 内から VM へのアクセスを防ぐ。

(8) amzn2 > 設定 > ネットワーク > アダプター2

  • ネットワークアダプターを有効化:ON
  • 割り当て:ホストオンリーアダプター
  • 名前:vboxnet0
  • 備考:アダプター2=eth1 は、Mac から VM への接続用。eth0 をリセットしても、eth1 は通じる。

(9) amzn2 > 起動

  • VM を起動する。
  • 初回は ssh が効かないので、VirtualBox の画面から ec2-user ユーザでログインする。
  • cloud-init の設定をすれは、初回から SSH 接続できるだろうけど、デフォルトではできないので、妥協。
# 以下は VM 内で実行する
sudo su -

# ホストオンリーアダプターを有効化する
ifup eth1

# 自分のアクセス用のユーザ foobar を作成する。
# 自分の uid と docker の gid は固定しておくと、あとで面倒がない。
# ここでは 1234 としているが、1001 を指定した方が便利かも?
adduser -M -u 1234 foobar
groupadd -g 987 docker
usermod -aG docker foobar

# ssh 公開鍵 authorized_keys を設定せずに、パスワードで SSH 接続する場合
passwd foobar
sed -i 's/^PasswordAuthentication no/PasswordAuthentication yes/' /etc/ssh/sshd_config
systemctl reload sshd

(10) Mac から SSH 接続でログインする

# Mac 側の Terminal.app で実行する
echo "192.168.56.78 amzn2" | sudo tee -a /etc/hosts
ssh foobar@amzn2

# 以下は VM 内で実行する
sudo su -

#(オプション)ダウンロードした .rpm パッケージを残したい場合
sed -i 's/^keepcache=0/keepcache=1/' /etc/yum.conf

#(オプション)yum や apk のキャッシュ用に Squid を入れる場合
yum install -y squid
systemctl enable squid 
systemctl start squid 
tail -f /var/log/squid/access.log /var/log/squid/cache.log 
echo 'proxy=http://127.0.0.1:3128' >> /etc/yum.conf

#(オプション)他にも必要なパッケージがあればインストールする
yum update -y
yum install -y git

(11) Docker を使う

ssh foobar@amzn2
# 以下は VM 内で実行する

# Docker をインストールする
sudo yum install -y docker
sudo systemctl enable docker 
sudo systemctl start docker

# foobar ユーザ権限でも Docker を使えるのを確認する
docker run hello-world

(12) NFS サーバを設定する

ssh foobar@amzn2

# 以下は VM 内で実行する
sudo su -

# NFS をインストールする
yum install -y nfs-utils
systemctl start nfs-server.service
systemctl enable nfs-server.service

# ホストオンリーアダプター(自分の Mac)からのみ NFS 接続を許可する
echo "/home/foobar 192.168.56.0/24(rw,sync,no_subtree_check,insecure,all_squash,anonuid=1234,anongid=1234)" > /etc/exports
exportfs -ar # 更新
exportfs -v # 確認

(12) Mac から NFS をマウントする

# Mac 側の Terminal.app で実行する
sudo mkdir /Volumes/foobar
sudo mount -t nfs amzn2:/home/foobar /Volumes/foobar

# Mac から NFS 経由でアクセスできるようになった
ls -l /Volumes/foobar
df -m /Volumes/foobar
open /Volumes/foobar

# マウント解除する場合(Finder.app からもアンマウントできる)
sudo umount /Volumes/foobar

あるいは、NFS でなくて Samba でも、VM 内のファイルを Mac から操作できますが、NFS の方が良さそう。
VM を動かすホストを別マシンに分けても(有線 LAN なら)NFS 経由で問題ない速度でアクセスできます。

(備考)Samba と NFS の比較:

Samba
⭕ Finder からマウント操作できるので分かりやすい
⭕ 設定でアクセスするユーザアカウントを指定可能。
❌ パーミションの互換性や、シンボリックリンクで面倒な事態が発生

NFS
⭕ 1ユーザの環境なら、パーミションも問題なし。
⭕ UNIX 同士なので、シンボリックリンクも問題なし。
❌ マウントするときに mount コマンドを叩く必要がある(切断は Finder から操作できる)


PS.
Docker for Mac ができる前の時代に戻った感覚ですが、ちょっとこれでしばらく運用してみようかと。
Node.js については Docker for Mac もまだ現実的な速度なんだけど、PHP (WordPress) はファイルアクセスが多いためか、数倍は遅くて、使い物になりませんでした。
『Alpine のメモリが遅い』という話もみかけましたが、Node.js については、試した限りでは、メモリは問題なくて、ディスクアクセスの影響が大きいように感じています。
Windows なら WSL 2 でそろそろ直接 Docker が動くようなので、羨ましいですね。