🦊

MacでDocker Desktopを入れずにDocker CLI+Multipassで代替してみる

2021/09/18に公開
2

※このやり方はIntel Macでのみ動作します。
M1 MacはMultipass(virtualbox)が動作しないので使えません。

概要

Docker Desktopが従業員250人以上だと有料化しました.
(※Docker CLIやDocker Engineは引き続き無料で使うことができます)

MacやWindowsだとDocker Desktopを入れないとDockerを使えないかと思いきやDocker CLIだけ個別に入手することができるようです.

この記事では仮想マシンにDockerを入れ,そこからホストのMac上のDocker CLIから操作する方法を紹介します.

Docker CLI単独のバイナリはこちらから入手できます
(業務で使う場合はライセンス等きっちり調べてから使ってください)

https://download.docker.com/

この辺の使います

内容を理解するにはこの辺に関する知識があると良いです

  • Docker CLI
  • Docker Context
  • Multipass
  • cloud-init
  • VirtualBox

環境構築

multipassとVirtualBoxのインストール

brew install --cask virtualbox virtualbox-extension-pack
brew install multipass

# multipassのバックエンドをhyperkit->virtualboxに変更
sudo multipass set local.driver=virtualbox

multipassのインスタンス初期設定

cloud-initという仕組みを使ってインスタンス作成時に自動で下記設定をします.

  • Dockerインストール + SSH経由でDocker使えるようにする設定
        - docker.ioのインストール
  • multipass (VirtualBox)でローカルディレクトリをマウントできるようにする
    • multipass-sshfsをインストール

cloud-initで初期設定するためのcloud-configの記述例

(ここではcloud-config.yamlというファイル名で)

#cloud-config

groups:
  - docker

system_info:
  default_user:
    groups: ["docker"]

package_update: true
packages:
  - docker
  - docker.io

snap:
  commands:
    - 'snap install multipass-sshfs'

注意事項

↑のYAMLファイル先頭の#cloud-configの記述は必須です.これがないとcloud-initが認識してくれません.

参考

他の設定はこちらを参考にしてください。
(aptのプロキシ設定や/etc/environmentにプロキシ設定を書き込む方法など)
https://cloudinit.readthedocs.io/en/latest/topics/modules.html

インスタンス起動

この設定でインスタンスを作成します

  • インスタンス名: Docker
  • CPU: 4コア
  • メモリ: 4GB
  • ストレージ: 40GB
multipass launch --name docker --mem 4G --disk 40G --cpus 4 --cloud-init cloud-config.yaml 

注意事項

この時タイムアウトと出る可能性がありますがインスタンスは作成されています.
インスタンスがまだバックグラウンドでDockerをインストールしている最中の可能性があるのでこのコマンドでインスタンスにDockerが入っているのが確認できるまで放置しましょう.

multipass exec docker  -- docker --version

Multipassインスタンスへ接続するSSHポート設定

VirtualBoxバックエンドなMultipassのインスタンスへSSH接続するにはVirtualBox VMのポートフォワーディング設定が必要です.

今回はlocalhost:10022がインスタンスの22番ポートに飛ぶ設定で説明します.

# ポートフォワーディング追加 (VM起動中の場合)
sudo VBoxManage controlvm "docker" natpf1 "docker-ssh,tcp,,10022,,22"

# ポートフォワーディング追加 (VM停止中の場合)
sudo VBoxManage modifyvm "docker" --natpf1 "docker-ssh,tcp,,10022,,22"

注意事項

↑コマンドのdocker-ssh はVirtualBox上のポートフォワーディング設定名です. 設定名はssh以外の名前にしてください.

sshはmultipass本体がインスタンスを制御するために使用している設定で,インスタンス起動時に毎回設定を書き換えてランダムポートが割り当てているようです.

補足1

設定したポートフォワーディング削除はこちらのコマンドで.

# ポートフォワーディング削除 (VM起動中の場合)
sudo VBoxManage controlvm "docker" natpf1 delete "docker-ssh"

# ポートフォワーディング削除 (VM停止中の場合)
sudo VBoxManage modifyvm "docker" --natpf1  delete "docker-ssh"

補足2

コマンドじゃなくGUIの方が好きな人はこちらで(VMがルート権限で動いてるので表示させるにはsudoが必要です).

sudo VirtualBox

muitipassインスタンスへアクセスするためのホストのSSH設定

multipassのインスタンスへアクセスするための秘密鍵をホストへコピーします.
(ここではファイル名は適当にmultipass.pemとします)

sudo install -m 600 -o $USER -g $(id -g) /var/root/Library/Application\ Support/multipassd/ssh-keys/id_rsa ~/.ssh/multipass.pem

~/.ssh/configにSSHアクセス情報を設定します.

Host multipass-docker
    Hostname localhost
    user ubuntu
    port 10022
    IdentityFile ~/.ssh/multipass.pem

念のためSSHフィンガープリントを削除します.
(手動で~/.ssh/known_hostsの中身を消してもいいですがコピペできるようにコマンドで)

ssh-keygen -R "[localhost]:10022"

フィンガープリント登録とSSH疎通確認
(yesとCtlr+Cを入力するのがめんどくさいのでコマンド1つでできるようにしてます)

echo exit | ssh -oStrictHostKeyChecking=no multipass-docker

Docker CLI単品ダウンロード

Docker CLI単品をダウンロードしてきて~/bin/docker にコピーします.
ついでにパスも通します.

mkdir ~/.src && cd ~/.src
wget https://download.docker.com/mac/static/stable/x86_64/docker-20.10.8.tgz
tar xzvf docker*
sudo xattr -rc docker
mkdir ~/bin
cp docker/docker $HOME/bin/docker
echo "export PATH=$PATH:$HOME/bin" >> ~/.zshrc

土下座事項 (202109182230追記)
sudo xattr -rc docker 入れ忘れてました。追記。

参考

Docker公式ドキュメントにちらっとだけMac版Docker CLIの情報が記載してます.
https://docs.docker.com/engine/install/binaries/#install-client-binaries-on-macos

Docker CLIの接続先をmultipassのdockerにする

docker context create multipass --docker "host=ssh://ubuntu@multipass-docker"
docker context use multipass

土下座事項 (202109182230追記)
docker context use multipass コマンド入れ忘れてました。追記。

Dockerを動かす

Dockerが使えているかどうかの確認

# 地獄の世界へようこそ
docker run --rm -it hello-world

ローカルディレクトリをDockerコンテナへマウントする方法

ローカルディレクトリをDockerコンテナへマウントするためには,ローカルディレクトリ→Multipassインスタンス→Dockerコンテナの順でマウントする必要があります.

また,VirtulBoxバックエンドでマウントするためにはインスタンスにmultipass-sshfsがインストールされている必要があります.
(※cloud-config.yamlでインスタンス作成時に自動インストールするように設定済みです)

例 (ローカルのホームディレクトリを同じパスでそっくりそのままマウントする)

multipass mount $HOME docker:$HOME
docker run --rm -it -v $HOME:$HOME bash -c "echo $HOME; ls $HOME"

余談1

↑のマウントしておけばVSCodeのRemote-ContainersのOpen Folder in Container使えるようになります.

余談2

ディレクトリのマウントはSSHFSで行っています.
Macbook Pro 2020で計測したらスループットは80~100MB/sでした.

multipassインスタンスのメモリサイズ変更方法

multipassコマンドにはメモリサイズを変更する機能が実装されていません.
そのため,メモリサイズを変更するには直接VirtualBox VMの設定を変更する必要があります.

# メモリを4GBに変更する (インスタンス停止中のみ変更可)
multipass stop docker
sudo VBoxManage modifyvm "docker" --memory 4000
multipass start docker

補足
hyperkitバックエンドだとmultipassd-vm-instances.jsonを編集すればメモリサイズを変更できますが,VirtualBoxバックエンドだとmultipassd-vm-instances.jsonの編集は無意味です.

参考 (hyperkitバックエンドでのメモリサイズ変更方法)
https://github.com/canonical/multipass/issues/1158#issuecomment-548073024

現状困っているところ

  • VirtualBoxバックエンドだとM1 Macで動かない
    • hyperkitバックエンドでもほぼ同じことできるけどだと多分IP固定が出来ない.
  • Podmanとかlimaでよくない?
    • はい…
    • Lima詳しくなかったので後から調べてみましたがよざげな記事を発見
          - Docker CLI + Lima + Docker Engine on Limaの構成がほぼこの記事と同じアプローチです。 おすすめ。
          - https://qiita.com/yoichiwo7/items/44aff38674134ad87da3
  • ローカルディレクトリのマウントで手動操作が入ってくるのでめんどくさい。
    • どうしようねこれ…
    • 追記: 普通にローカルのホームディレクトリマウントしておけば普段の作業で困ることはなかった。
    multipass mount $HOME docker:$HOME
    

Discussion

ギンギツネさんギンギツネさん

cloud-config.yamlでプロキシ設定例

  • /etc/environmentにプロキシの環境変数をセット
  • aptにプロキシ設定をセット (もしかしたら/etc/environmentの設定するだけで十分かもしれないです)
#cloud-config
write_files:
  - path: /etc/environment
    content: |
      http_proxy=http://192.168.2.1:3128
      https_proxy=http://192.168.2.1:3128
      HTTP_PROXY=http://192.168.2.1:3128
      HTTPS_PROXY=http://192.168.2.1:3128
      no_proxy=localhost,127.0.0.1
    append: true
apt:
  proxy: http://192.168.2.1:3128
  http_proxy: http://192.168.2.1:3128
  ftp_proxy: http://192.168.2.1:3128
  https_proxy: http://192.168.2.1:3128

groups:
  - docker

system_info:
  default_user:
    groups: ["docker"]

package_update: true
package_upgrade: true
packages:
  - docker
  - docker.io

snap:
  commands:
    - 'snap install multipass-sshfs'

引数--cloud-init の値を-にすると標準入力でcloud-configを渡すことができます。

こうすればホストのプロキシ環境変数も引き継げるんじゃないかな思います。

multipass launch --name docker --mem 4G --disk 40G --cpus 4 --cloud-init - <<EOF
#cloud-config
write_files:
  - path: /etc/environment
    content: |
      http_proxy=$http_proxy
      https_proxy=$https_proxy
      HTTP_PROXY=$HTTP_PROXY
      HTTPS_PROXY=$HTTPS_PROXY
      no_proxy=$no_proxy
    append: true
apt:
  http_proxy: $http_proxy
  ftp_proxy: $ftp_proxy
  https_proxy:$https_proxy

groups:
  - docker

system_info:
  default_user:
    groups: ["docker"]

package_update: true
package_upgrade: true
packages:
  - docker
  - docker.io

snap:
  commands:
    - 'snap install multipass-sshfs'
EOF
ギンギツネさんギンギツネさん

multipassのhyperkitバックエンドの場合は/var/db/dhcpd_leasesを編集して割り振られるIPアドレスを
調整できるようなのでやろうと思えば固定IP化できるようです。
これができたらApple SiloconのMACでもmultipass使ってdocker使うことができるかもしれないです。
(持ってないので検証できないのと、x86なインスタンスが実行できるのか怪しいですが)

※割り振られるIPの範囲は192.168.64.0/24

参考
https://multipass.run/docs/troubleshooting-networking-on-macos

/var/db/dhcpd_leasesの中身の例

{
	name=primary
	ip_address=192.168.64.4
	hw_address=1,ca:3b:cc:e:10:69
	identifier=1,ca:3b:cc:e:10:69
	lease=0x6146506c
}
{
	name=docker
	ip_address=192.168.64.3
	hw_address=1,aa:33:ff:6d:fc:7a
	identifier=1,aa:33:ff:6d:fc:7a
	lease=0x61464ee9
}
{
	ip_address=192.168.64.2
	hw_address=1,e2:2f:64:91:dc:e8
	identifier=1,e2:2f:64:91:dc:e8
	lease=0x6146661a
}