DEVなメモ

個人開発のメモ的なブログ

M1 MacBook Pro 2021 に Lima + Docker 環境をインストールする

背景

まずは個人開発をすすめるべく、開発環境を整えていきます。 今回は以下の状況や条件を踏まえて Lima + Docker の環境を作っていきます。

  • Apple Silicon の M1 Macbook Pro を購入した
  • Docker Desktop が有料化される(個人的には対象外ですが)
  • Docker Desktop for Mac でのファイルシステムマウントが激遅なのを避けたい
  • WSL (Windows Subsystem for Linux) のような Linux 仮想環境をつくりたい

環境

環境構築手順

Lima のインストール

brew でお手軽にインストールできます。

brew upgrade
brew update
brew install lima

Lima での ゲスト VM 作成

Lima の公式リポジトリ にはいろんな VM のサンプルが掲載されています。

その中から、docker-compose もインストールされるように 作られた VM の YAML ファイル を利用します。

cd ~/tmp
mkdir lima && cd lima
curl -o default.yaml https://raw.githubusercontent.com/chibiegg/lima-config/master/docker.yaml
cat default.yaml

# Example to use Docker instead of containerd & nerdctl
# $ limactl start ./docker.yaml
# $ limactl shell docker docker run -it -v $HOME:$HOME --rm alpine

# Hint: To allow `docker` CLI on the host to connect to the Docker daemon running inside the guest,
# add `NoHostAuthenticationForLocalhost yes` in ~/.ssh/config , and then run the following commands:
# $ export DOCKER_HOST=ssh://localhost:60006
# $ docker ...

# If ssh:// ... does not work, try the following commands:
# $ ssh -f -N -p 60006 -i ~/.lima/_config/user -o NoHostAuthenticationForLocalhost=yes -L $HOME/docker.sock:/run/user/$(id -u)/docker.sock 127.0.0.1
# $ export DOCKER_HOST=unix://$HOME/docker.sock
# $ docker ...

images:
  # Hint: run `limactl prune` to invalidate the "current" cache
  - location: "https://cloud-images.ubuntu.com/hirsute/current/hirsute-server-cloudimg-amd64.img"
    arch: "x86_64"
  - location: "https://cloud-images.ubuntu.com/hirsute/current/hirsute-server-cloudimg-arm64.img"
    arch: "aarch64"
mounts:
  - location: "~"
    writable: false
  - location: "/tmp/lima"
    writable: true
ssh:
  localPort: 60006
  # Load ~/.ssh/*.pub in addition to $LIMA_HOME/_config/user.pub , for allowing DOCKER_HOST=ssh:// .
  # This option is enabled by default.
  # If you have an insecure key under ~/.ssh, do not use this option.
  loadDotSSHPubKeys: true
# containerd is managed by Docker, not by Lima, so the values are set to false here.
containerd:
  system: false
  user: false
provision:
  - mode: system
    script: |
      #!/bin/bash
      set -eux -o pipefail
      command -v docker >/dev/null 2>&1 && exit 0
      export DEBIAN_FRONTEND=noninteractive
      curl -fsSL https://get.docker.com | sh
      # NOTE: you may remove the lines below, if you prefer to use rootful docker, not rootless
      systemctl disable --now docker
      apt-get install -y uidmap
  - mode: user
    script: |
      #!/bin/bash
      set -eux -o pipefail
      dockerd-rootless-setuptool.sh install
      docker context use rootless
      sudo apt install -y docker-compose qemu-system-x86 binfmt-support qemu-user qemu-user-static
probes:
  - script: |
      #!/bin/bash
      set -eux -o pipefail
      if ! timeout 30s bash -c "until command -v docker >/dev/null 2>&1; do sleep 3; done"; then
        echo >&2 "docker is not installed yet"
        exit 1
      fi
      if ! timeout 30s bash -c "until pgrep rootlesskit; do sleep 3; done"; then
        echo >&2 "rootlesskit (used by rootless docker) is not running"
        exit 1
      fi
    hint: See "/var/log/cloud-init-output.log". in the guest

ゲスト VM の起動

limactl start default.yaml
? Creating an instance "default" Proceed with the default configuration
INFO[0023] Attempting to download the image from "https://cloud-images.ubuntu.com/hirsute/current/hirsute-server-cloudimg-arm64.img"  digest=
543.69 MiB / 543.69 MiB [-----------------------------------] 100.00% 9.21 MiB/s
INFO[0084] Downloaded image from "https://cloud-images.ubuntu.com/hirsute/current/hirsute-server-cloudimg-arm64.img"
INFO[0088] [hostagent] Starting QEMU (hint: to watch the boot progress, see "/Users/xxxxxx/.lima/default/serial.log")
INFO[0088] SSH Local Port: 60006
INFO[0088] [hostagent] Waiting for the essential requirement 1 of 5: "ssh"
INFO[0142] [hostagent] The essential requirement 1 of 5 is satisfied
INFO[0142] [hostagent] Waiting for the essential requirement 2 of 5: "user session is ready for ssh"
INFO[0142] [hostagent] The essential requirement 2 of 5 is satisfied
INFO[0142] [hostagent] Waiting for the essential requirement 3 of 5: "sshfs binary to be installed"
INFO[0143] [hostagent] The essential requirement 3 of 5 is satisfied
INFO[0143] [hostagent] Waiting for the essential requirement 4 of 5: "/etc/fuse.conf to contain \"user_allow_other\""
INFO[0146] [hostagent] The essential requirement 4 of 5 is satisfied
INFO[0146] [hostagent] Waiting for the essential requirement 5 of 5: "the guest agent to be running"
INFO[0146] [hostagent] The essential requirement 5 of 5 is satisfied
INFO[0146] [hostagent] Mounting "/Users/xxxxxx"
INFO[0146] [hostagent] Mounting "/tmp/lima"
INFO[0146] [hostagent] Waiting for the optional requirement 1 of 1: "user probe 1/1"
INFO[0146] [hostagent] Forwarding "/run/lima-guestagent.sock" (guest) to "/Users/xxxxxx/.lima/default/ga.sock" (host)
INFO[0146] [hostagent] Not forwarding TCP 127.0.0.53:53
INFO[0146] [hostagent] Not forwarding TCP 0.0.0.0:22
INFO[0146] [hostagent] Not forwarding TCP [::]:22
INFO[0173] [hostagent] The optional requirement 1 of 1 is satisfied
INFO[0173] [hostagent] Waiting for the final requirement 1 of 1: "boot scripts must have finished"
INFO[0214] [hostagent] Waiting for the final requirement 1 of 1: "boot scripts must have finished"
INFO[0214] [hostagent] The final requirement 1 of 1 is satisfied
INFO[0214] READY. Run `lima` to open the shell.

ホスト(macOS )に docker と docker-compose コマンドをインストールする

ホスト側である macOS から Lima のゲスト VM 上にあるコンテナを操作するため、docker と docker-compose コマンドをインストールします。

こちらも brew でかんたんにインストールできます。

brew install docker docker-compose

コマンドをインストールしただけではゲスト VM 上のコンテナにアクセスできませんので、SSH 経由でコマンドを実行できるようにします。

echo 'export DOCKER_HOST=ssh://limadocker:60006' >> ~/.zshrc

cat << EOS >> ~/.ssh/config
Host localhost
  HostName localhost
  User xxxxxx
  NoHostAuthenticationForLocalhost yes
  Port 60006
EOS

source ~/.zshrc

うまくいくかと思ったら、SSH がうまく通っていないようでした。

docker version
Client: Docker Engine - Community
 Version:           20.10.11
 API version:       1.41
 Go version:        go1.17.2
 Git commit:        dea9396e18
 Built:             Wed Nov 17 23:49:46 2021
 OS/Arch:           darwin/arm64
 Context:           default
 Experimental:      true
error during connect: Get "http://docker.example.com/v1.24/version": command [ssh -p 60006 -- localhost docker system dial-stdio] has exited with exit status 255, please make sure the URL is valid, and Docker 18.09 or later is installed on the remote host: stderr=xxxxxx@127.0.0.1: Permission denied (publickey).

調べてみると、SSH 設定を表示してくれるコマンドがある ようです。

limactl show-ssh --format=(cmd|args|options|config) INSTANCE

インスタンス名は default 、出力フォーマットは ~/.ssh/config 用に config とします。

limactl show-ssh default --format=config
Host lima-default
  IdentityFile "/Users/xxxxxx/.lima/_config/user"
  StrictHostKeyChecking no
  UserKnownHostsFile /dev/null
  NoHostAuthenticationForLocalhost yes
  GSSAPIAuthentication no
  PreferredAuthentications publickey
  Compression no
  BatchMode yes
  IdentitiesOnly yes
  Ciphers "^aes128-gcm@openssh.com,aes256-gcm@openssh.com"
  User xxxxxx
  ControlMaster auto
  ControlPath "/Users/xxxxxx/.lima/default/ssh.sock"
  ControlPersist 5m
  Hostname 127.0.0.1
  Port 60006

表示された設定のうち、Host を lima-default から docker コマンド実行エラー時に表示されていたホスト名 localhost へ変えて ~/.ssh/config に記載しておきます。

改めて docker コマンドを実行してみるとエラーが解消されました!

docker version
Client: Docker Engine - Community
 Version:           20.10.11
 API version:       1.41
 Go version:        go1.17.2
 Git commit:        dea9396e18
 Built:             Wed Nov 17 23:49:46 2021
 OS/Arch:           darwin/arm64
 Context:           default
 Experimental:      true

Server: Docker Engine - Community
 Engine:
  Version:          20.10.11
  API version:      1.41 (minimum version 1.12)
  Go version:       go1.16.9
  Git commit:       847da18
  Built:            Thu Nov 18 00:35:20 2021
  OS/Arch:          linux/arm64
  Experimental:     false
 containerd:
  Version:          1.4.12
  GitCommit:        7b11cfaabd73bb80907dd23182b9347b4245eb5d
 runc:
  Version:          1.0.2
  GitCommit:        v1.0.2-0-g52b36a2
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

ゲスト VM 上でのコンテナの起動と動作確認

ゲスト VM 上でコンテナを起動します。今回は nginx が実行されるコンテナを起動して確認します。

docker run -it -p 8080:80 nginx:latest
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
/docker-entrypoint.sh: Configuration complete; ready for start up
2021/12/06 08:50:34 [notice] 1#1: using the "epoll" event method
2021/12/06 08:50:34 [notice] 1#1: nginx/1.21.4
2021/12/06 08:50:34 [notice] 1#1: built by gcc 10.2.1 20210110 (Debian 10.2.1-6)
2021/12/06 08:50:34 [notice] 1#1: OS: Linux 5.11.0-41-generic
2021/12/06 08:50:34 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
2021/12/06 08:50:34 [notice] 1#1: start worker processes
2021/12/06 08:50:34 [notice] 1#1: start worker process 32
2021/12/06 08:50:34 [notice] 1#1: start worker process 33
2021/12/06 08:50:34 [notice] 1#1: start worker process 34
2021/12/06 08:50:34 [notice] 1#1: start worker process 35

起動後にホスト側から http://localhost:8080 にアクセスできれば成功です!

nginx 起動画面
nginx 起動画面

参考記事

qiita.com

qiita.com

zenn.dev

korosuke613.hatenablog.com