SODA Engineering Blog
🐳

Virtualization.Frameworkを使ったDocker開発環境の構築 (Lima/virtiofs/Rosetta2)

2023/12/18に公開

\スニダンを開発しているSODA inc.の Advent Calendar 2023 18日目の記事です!!!/

概要

Limaを使って、macOS上にVirtualization.Frameworkを使用したDocker実行環境を構築する方法を紹介します。
Limaは、設定ファイを元にLinuxのVirtual Machine(VM)を自動で構築してくれる便利なツールです。様々な用途に応じたVMを作成するための設定ファイルのテンプレートが用意されており、これを利用すると簡単にVMを構築することができます。LimaにはDocker実行環境を構築するためのテンプレートが用意されていますが、今回はこれをそのまま使うのではなくカスタマイズし、Docker Desktopと同様の技術構成を目指します。

注意

  • LimaのVirtualization.Frameworkサポートはexperimentalな機能なので変更が入る可能性があります[1]
  • 今回利用するvirtiofsはinotifyをサポートしないため、inotifyの仕組みを基にしたホットリロードツールが動作しない可能性があります

システム要件

  • CPU: Apple Silicon
  • macOS: 13.0以上
  • Lima: 0.14以上

アーキテクチャ

LimaでmacOSのVirtualization.Frameworkを利用してLinux VMを作成し、この上でDocker Engineを動かします。
Virtualization.Frameworkを利用することで、virtiofsを使ってホストとゲスト間でディレクトリを共有することができます。また、Rosetta 2を使ってIntel(x86_64)向けのバイナリを実行することもできます。

virtiofsを使う利点

virtiofsは仮想環境のユースケースに最適化したファイルシステムで、従来のネットワークファイルシステムのオーバーヘッドを回避できる利点があると公式サイトにあります。[2]

Virtiofs takes advantage of the virtual machine’s co-location with the hypervisor to avoid overheads associated with network file systems.

また、これはDocker Desktopの事例ですが、virtiofsを採用したことでDocker Desktopのパフォーマンスが大幅に向上したという報告があります。[3]

Big performance improvements

  • A 90% improvement in the time taken to complete a 284MB MySQL import (3m 16s to 18s)
  • An 87% improvement in the time taken to run ‘composer install’ in a large codebase (1m 27s to 11s)
  • An 80% improvement in the time taken to boot a monolithic Typescript app (1m 30s to 18s)

事前準備

Limaのインストール

Linux VMを作成するために必要です。

$ brew install lima

Docker CLIのインストール

ホストからゲストのDocker Engineに繋ぐために必要です。

$ brew install docker

Rosetta 2のインストール

ゲスト側でx86_64のバイナリを実行するために必要です。

$ softwareupdate --install-rosetta --agree-to-license

Linux VMの作成

Limaでは、YAMLの設定ファイルを指定してVMを作成します。
limactlはLimaのコマンドです。

$ limactl create VMの設定ファイル.yaml

現時点の最新(v0.19.0)のDocker用設定ファイルのテンプレートをみてみると、次のようになっています。 今回はこれをベースにしてカスタマイズをしていきます。
https://github.com/lima-vm/lima/blob/v0.19.0/examples/docker.yaml

次の項目を設定ファイルに追加します。
vmType はVMを実行する環境を指定するもので、vz を指定するとVirtualization.Frameworkを使ってVMを作成します。省略した場合は、デフォルトのQEMUでVMが作成されます。
rosetta を有効にすると、x86_64のバイナリをRosetta2で実行します。
networksvz を使う場合は vzNAT を指定する必要があります。[4]
mountTypevirtiofs を指定しています。これでvirtiofsを使ってホストとゲスト間でファイルを共有することができます。

vmType: "vz"
rosetta:
  enabled: true
  binfmt: true
networks:
  - vzNAT: true
mountType: "virtiofs"

項目を追加したバージョンの設定ファイルを公開していますので、これを利用してVMを作成することもできます。
https://gist.github.com/hiroshinakasone/d7b70739e06162f56fa36ced9fc6cdcb

limactl create を実行するとプロンプトが表示されるので、Proceed with the current configuration を選択してEnterを押すとVMの作成が始まります。
作成が正常に終わると、Run `limactl start docker-vz` to start the instance. が表示されます。

$ limactl create docker-vz.yaml
? Creating an instance "docker-vz"  [Use arrows to move, type to filter]
> Proceed with the current configuration
  Open an editor to review or modify the current configuration
  Choose another template (docker, podman, archlinux, fedora, ...)
  Exit

Linux VMの起動

VMの起動は limactl start VMの名前 で行います。
VMの名前は、limactl create 時に指定した設定ファイルと同じになります。
今回は次のコマンドで起動します。

$ limactl start docker-vz

次のメッセージが表示されていれば起動が成功です。

To run `docker` on the host (assumes docker-cli is installed), run the following commands:
------
docker context create lima-docker-vz --docker "host=unix:///Your/home_directory/.lima/docker-vz/sock/docker.sock"
docker context use lima-docker-vz
docker run hello-world
------

念のためにLimaでVMのステータスを確認する場合は次のコマンドを実行します。 STATUSRunning になっていれば起動ができています。

$ limactl list
NAME         STATUS     SSH                VMTYPE    ARCH       CPUS    MEMORY    DISK      DIR
docker-vz    Running    127.0.0.1:54758    vz        aarch64    4       4GiB      100GiB    ~/.lima/docker-vz

docker contextの設定

ホストからゲスト(VM)のDocker Engineに接続するための設定をします。
VMを起動するとホストの ${HOME}/.lima/docker/sock/docker.sock にDockerのUnixドメインソケットが作成されます。これはゲストと共有されており、ゲスト上のDocker Engineのエンドポイントになっています。
ホストからこのソケットに繋ぐことで、ホストからゲストのDocker Engineに接続することができます。ホストとゲスト上のソケットファイルのパスは設定ファイル (docker-vz.yaml) に記述されているので、分からなくなった場合はこれをみることで確認することができます。

docker context createでエンドポイントを登録し、docker context use でこれを使うように設定します。
/Your/home_director の部分は適宜ご自身の環境のパスに書き換えてください。

$ docker context create lima-docker-vz --docker "host=unix:///Your/home_directory/.lima/docker-vz/sock/docker.sock"
$ docker context use lima-docker-vz

docker context ls で現在選択中のエンドポイントを確認できます。* が付いているものが現在選択中のものです。lima-docker-vz * になっていれば問題ありません。

$ docker context ls
NAME               DESCRIPTION                               DOCKER ENDPOINT                                                ERROR
default            Current DOCKER_HOST based configuration   unix:///var/run/docker.sock
lima-docker-vz *                                             unix:///Your/home_directory/.lima/docker-vz/sock/docker.sock

動作確認

いよいよDockerコンテナを実行して動作確認をします。
今回作成したDocker Engineを動かしているVMはARM向けのVMです。 したがって、ARM向けのDockerイメージに含まれるバイナリをそのまま実行することができます。
一方で、Intel(x86_64)向けのDockerイメージに含まれるバイナリも実行することができます。Intel向けのバイナリはRosetta 2を使って実行します。Rosetta 2はすでに有効になっているので特別な操作は必要ありません。

ARM向けのバイナリを実行を確認するには次のコマンドを実行します。

$ docker run --rm --platform linux/arm64 hello-world

Intel向けのバイナリを実行を確認するには次のコマンドを実行します。

$ docker run --rm --platform linux/amd64 hello-world

正常に動作できた場合は、それぞれ次のメッセージが表示されます。

Hello from Docker!
This message shows that your installation appears to be working correctly.

おわりに

Limaを使うことでmacOS上に簡単にDockerの開発環境を作成することができます。
今回は指定しませんでしたが、VMに割り当てるCPUの数やメモリのサイズをVM作成時に指定するとができます。また、その他の各種パラメータは、今回のように設定ファイルに記載する以外にも、limactl create でVM作成時に --set オプションを付けることでコマンドラインから指定することが可能です。実際の開発環境を構築するときには公式サイトを参考に適宜パラメータを指定してください。

検証環境

CPU

$ uname -m
arm64

OS

$ sw_vers 
ProductName:            macOS
ProductVersion:         13.6.2
BuildVersion:           22G320

Lima

$ limactl -v
limactl version 0.19.0

参考にしたドキュメント

脚注
  1. https://lima-vm.io/docs/config/multi-arch/#fast-mode-2-rosetta-intel-containers-on-arm-vm-on-arm-hostfast-mode-2 ↩︎

  2. https://virtio-fs.gitlab.io/ ↩︎

  3. https://www.docker.com/blog/speed-boost-achievement-unlocked-on-docker-desktop-4-6-for-mac/ ↩︎

  4. https://lima-vm.io/docs/config/network/ ↩︎

SODA Engineering Blog
SODA Engineering Blog

Discussion