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

Node.js の開発では Docker for Mac が便利ですが、仕組み的にディスクアクセスが遅くなるので、処理性能が半分とまではいかないものの、３割くらい遅いです。そこで 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 くらい。
![virtualbox.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12419/1e7fada2-2340-602b-5000-3e85fbd717b6.jpeg)

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

- 作成ボタンを押すと、ネットワーク vboxnet0 ができる。
- IPv4 アドレスをメモしておく。ここでは「192.168.56.1/24」になっている。
![vboxnet0.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12419/9f2eedd0-f4eb-d379-1b3e-cac46949302c.jpeg)

## (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) を作成する

```sh
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
# ↑１行目の「#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 を選択
![5-1-machine.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12419/a65e1a6b-bdf4-db73-6b65-cb42e7434c28.jpeg)

## (6) amzn2 ＞ 設定 ＞ ストレージ

- コントローラー：IDE の右側の [＋] ボタンから、seed.iso を追加する。
- 画面下部の「新しいストレージコントローラを追加します」「新規のストレージ割り当てを追加します」ではなくて、「光学ドライブの追加」を選ぶ。
![6-storage.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12419/8aeba55c-3b7d-325b-14a5-43cd0767985a.jpeg)

## (7) amzn2 ＞ 設定 ＞ ネットワーク ＞ アダプター１

- ネットワークアダプターを有効化：ON（まま）
- 割り当て：NAT
- 備考：アダプター１＝ eth0 は、VM からの上り回線用。Mac 側の上流回線が何でもOK。LAN 内から VM へのアクセスを防ぐ。
![7-eth0.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12419/c753051c-5e48-783f-b886-5d0798fc86b0.jpeg)

## (8) amzn2 ＞ 設定 ＞ ネットワーク ＞ アダプター２

- ネットワークアダプターを有効化：ON
- 割り当て：ホストオンリーアダプター
- 名前：vboxnet0
- 備考：アダプター２＝eth1 は、Mac から VM への接続用。eth0 をリセットしても、eth1 は通じる。
![8-eth1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12419/89344e6d-5740-76cb-0294-1e0862617182.jpeg)

## (9) amzn2 ＞ 起動

- VM を起動する。
- 初回は ssh が効かないので、VirtualBox の画面から ec2-user ユーザでログインする。
- cloud-init の設定をすれは、初回から SSH 接続できるだろうけど、デフォルトではできないので、妥協。
![login-screen.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12419/ebdd5e38-652f-0e92-632d-d98dae932258.jpeg)

```sh
# 以下は 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 接続でログインする

```sh
# 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 を使う

```sh
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 サーバを設定する

```sh
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 をマウントする

```sh
# 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 が動くようなので、羨ましいですね。