DockerをMultipassで構築[Mac]

Docker Desktopの代替案を探していたところMultipassがかなり良い感じだったので構築方法を記載しておきます。

## Multipassとは

Multipassは手軽・高速にUbuntu環境を作成する仮想環境で、Linux, Windows, Macで動作します。

## ホスト環境

この内容を実施するとホストのMacには以下の影響があります。

- Docker Desktopを使わない。
- Mac上で`docker`コマンドを使うとMultipass上のdockerが使用されます。
- Ubuntu構築用にCPU,メモリ,ストレージ等のリソースが必要です。
- Homebrewで`multipass`, `docker`, `docker-compose`をインストールします。
- `~/.ssh`ディレクトリの`config`ファイルに必要な記述を追記します。
- `~/.ssh`ディレクトリにsshファイルを作成します。
- Ubuntuにホームディレクトリをマウントします（任意）。
- Docker Desktopのように各種アクセス先はlocalhostではなく`任意の名前.local`でアクセスが出来るようになります。localhostと切り分けができるので個人的にはこちらの方が好みです。
- MultipassがHypreKitを利用しているので他の仮想化アプリケーションは無くても構いません。（任意で別の仮想化アプリケーションを利用する方法もあるようです）

> 参考
> https://medium.com/@gourneau_38759/docker-engine-with-multipass-on-macos-dc44ff09ffb2

## 必要なパッケージをインストール

```bash
# もし、homebrewでdocker desktop及びdockerがインストールしてある場合は以下を実行で一旦削除
# brew uninstall --force docker docker-machine docker-compose
brew install multipass docker docker-compose
```

## SSHファイル作成

`multipass_docker`という任意の名前でSSHファイルを作成します。

`your_email@example.com`の箇所は必ず書き換えてください。メールアドレスである必要はありません。

> ssh key gen
> https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent

```bash
ssh-keygen -t rsa -b 4096 -C "your_email@example.com" -f ~/.ssh/multipass_docker
# your_email@example.comは以下のようにuuidgenを使ってランダムな文字列を使っても良い
# ssh-keygen -t rsa -b 4096 -C "$(uuidgen)" -f ~/.ssh/multipass_docker
chmod 600 ~/.ssh/multipass_docker
chmod 600 ~/.ssh/multipass_docker.pub
```

## yamlファイル作成

Ubuntu構成用に以下のコマンドをターミナルで入力してファイルを作成する。

```bash
# 作成した.pubファイルを変数に読み込む
AUTHORIZED_KEYS=$(cat ~/.ssh/multipass_docker.pub )
# ヒアドキュメントでファイルを作成する
cat > ./multipass_docker.yaml << _EOF_
---
locale: en_US.UTF8
timezone: Asia/Tokyo
package_upgrade: true
users:
  - name: ubuntu
    sudo: ALL=(ALL) NOPASSWD:ALL
    ssh-authorized-keys:
      - ${AUTHORIZED_KEYS}

packages:
  - docker
  - avahi-daemon
  - apt-transport-https
  - ca-certificates
  - curl
  - gnupg
  - lsb-release

runcmd:
  - sudo curl -fsSL https://get.docker.com | sudo bash
  - sudo systemctl enable docker
  - sudo systemctl enable -s HUP ssh
  - sudo groupadd docker
  - sudo usermod -aG docker ubuntu
_EOF_
```

## 構築

以下の様式でMultipassを使ったUbuntuを構築します。

```bash
multipass launch -c CPU -m メモリ量 -d ストレージ容量 -n 任意の名前 20.04 --cloud-init ./multipass_docker.yaml
```

以下のコマンドでUbuntuを構築します。今回は`docker`という名前のマシンで作成しており、これ以降`docker.local`で直接アクセス出来るようになります。

`docker`という名前は任意ですので、他の名前でもかまいません。その際はアクセス先が`任意の名前.local`になります。

```bash
multipass launch -c 2 -m 4G -d 40G -n docker 20.04 --cloud-init ./multipass_docker.yaml
```

### 構築できたか確認

```bash
# SSH接続をする。
# 念の為、以下を実行してログイン記録を削除。
ssh-keygen -R docker.local
# sshファイルを使用してMultipassのインスタンスにログイン
ssh -i ~/.ssh/multipass_docker ubuntu@docker.local
# Multipassのインスタンスにログイン出来たら以下を実行してdockerがインストールされているか確認
docker info
# 確認出来たらインスタンスから出る
exit
# SSH接続が出来ることが確認出来たら構成用に使用したyamlファイルは不要になるので削除する。
rm ./multipass_docker.yaml

# 設定内容の確認
multipass info docker
# 一覧表
multipass ls
```

### ~/.ssh/configファイルに追記

`ssh ubuntu@docker.local`コマンドでログイン出来るように`~/.ssh/config`ファイルに以下を追記します。無い場合は新しく作成しましょう。

```config
Host docker.local
  HostName docker.local
  IdentityFile ~/.ssh/multipass_docker
  User ubuntu
  Port 22
```

## ログイン出来るか確認

### multipass shell

`multipass`コマンドで以下を実行することでログイン出来ます。

```bash
multipass shell docker
# 確認出来たらインスタンスから出る
exit
```

### sshでログイン

```bash
# 念の為、以下を実行してログイン記録を削除。
ssh-keygen -R docker.local
# ubuntuにログイン
ssh ubuntu@docker.local
# ログイン出来ることを確認出来たらインスタンスから出る。
exit
```

## .zshrcに追記

環境変数`DOCKER_HOST`にUbuntuのアクセス先を記述することで、ホスト側での`docker`コマンドはUbuntuのものを使うように設定できます。`.zshrc`の末尾にでも追記しておくことで、ログイン時に設定されるようにしておきましょう。

```bash
# multipass docker
export DOCKER_HOST="ssh://ubuntu@docker.local"
```

## [任意]ホームディレクトリをMultipass上にマウントする

作業しやすいようにホームディレクトリをUbuntu上にマウントします。

1回マウントしたらホストを再起動してもマウントし続けます。

```bash
# ホームをUbuntu上の同じ名前の階層（/Users/ユーザ名）にマウントする
multipass mount ${HOME} docker:${HOME}
# アンマウント
# multipass unmount docker:${HOME}
```

この作業は任意ですが、していない場合、docker上で`Dockerfile`や`docker-compose.yaml`と同階層のファイルをマウントしようとしてもMultipassの内部しか見えていない状態なので以下のようなエラーになります。

```error
user@mac docker % docker-compose up               
[+] Running 1/0
 ⠿ Container docker-xxxx-1  Created                                                                                                                                                                    0.0s
Attaching to docker-xxxx-1
Error response from daemon: invalid mount config for type "bind": bind source path does not exist: /Users/***
```

## テスト

### nginxを使ってみる

```bash
docker run --name nginx_test -d -p 8080:80 nginx
# 以下で削除
# docker stop $(docker ps -aqf "name=nginx_test")
# docker rm $(docker ps -aqf "name=nginx_test")
```

### アクセス

この場合URLは`http://docker.local:8080`です。

![image02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76058/a5fec5c0-e927-33d0-5adc-a47f72b4bf22.png)

### 外部からのアクセス

外部からアクセスするには`multipass ls`もしくは`multipass info docker`で`IPv4`項目を参照しましょう。

## 削除

multipassの削除は、`delete`で一次削除、`purge`で完全削除の２段階です。

`delete`でゴミ箱に入れて、`purge`でゴミ箱を空にする感じです。

```bash
# 削除は以下
multipass delete docker
multipass purge
```

## エラー対応

### Operation not permitted

ホスト側のファイルを扱おうとした際にこのようなエラーが表示されることがあります。

解決策は、`システム環境設定` の`プライバシー`タブの`フルディスクアクセス`の一覧にある`multipassd`にチェックを入れることです。

![image000.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76058/03184cf6-4bd4-101b-5488-e70dfc764e18.png)