MENU

LimaでDocker Desktopとサヨナラする

  • URLをコピーしました!

こんにちは、みーまです。

そろそろDocker Desktopの無料猶予期間(2022/01/31)が迫ってきましたので、Docker Desktopとお別れすることにしました。

私が次にお付き合いするのはLimaにしました。

会社ではCloud9くらいなら費用出してくれるのでそっちでも良いのですが、やっぱりPC内でやりたいよね、ということで。

LimaはMacOS向けなので、Windows向けの方はWSLなどで検討されると良いと思います。

目次

Limaとは?

LimaはLinux VM環境が手軽に実現できるアプリケーションです。

Docker DesktopはGUI機能以外にも、LinuxカーネルではないOSに対してLinux VM環境を提供していたため、WindowsやMacOSには必須でした。

Docker Desktopを辞めるということは、Linuxカーネルを持たないOSは何らかの手段でLinuxカーネルが動く環境(Linux VM)を用意しなければいけないのですが、これをLimaにやらせようという魂胆です。

従来のDocker Desktopとの違い

従来のDocker Desktopとの違いは以下の通りです。

ビルド速度が遅い、VMのスペック変更がCLIしかないなど、制約事項はあるものの、それ以外は概ね使い勝手も変わらないことが分かっていただけるでしょう。

従来Lima
Docker CLIの利用
GUI×(メモリやCPUなどはLimaの設定から)
ビルド済イメージの保持※1
ビルド速度▲(ダウンロードなど、気持ち遅いかも)
VSCode※2

※1 ただしLima環境を消す(limactl rmする)と無くなってしまうので注意、保持したい場合は別途待避するスクリプト実装など必要
※2 本来の望ましい方法ではないようだが設定変更で問題無く動作(参考

Limaの導入

インストール

LimaはHomebrewでインストールできるので各自インストールしてください。

インストールするとlimalimactlが使えるようになります。

定義ファイル

Limaは起動させるVMの定義ファイルとしてymlを読み込みます。

本日(2022/01/05)現在、Lima公式のDocker向けyamlサンプルがどうも動かないので、今回は手元で検証したyamlを載せておきます。

Arch/CPU/Memory/Diskはカスタマイズできますが、CPUアーキテクチャのArchは記載のままのほうが良いです。(ARMコンテナが必要な人だけaarch64にするのが良いでしょう)

# ===================================================================== #
# BASIC CONFIGURATION
# ===================================================================== #

# Arch: "default", "x86_64", "aarch64".
# "default" corresponds to the host architecture.
arch: "x86_64"

# An image must support systemd and cloud-init.
# Ubuntu and Fedora are known to work.
# Default: none (must be specified)
images:
  # Try to use a local image first.
  - location: "~/Downloads/impish-server-cloudimg-amd64.img"
    arch: "x86_64"
  - location: "~/Downloads/impish-server-cloudimg-arm64.img"
    arch: "aarch64"

  # Download the file from the internet when the local file is missing.
  # Hint: run `limactl prune` to invalidate the "current" cache
  - location: "https://cloud-images.ubuntu.com/impish/current/impish-server-cloudimg-amd64.img"
    arch: "x86_64"
  - location: "https://cloud-images.ubuntu.com/impish/current/impish-server-cloudimg-arm64.img"
    arch: "aarch64"

# CPUs: if you see performance issues, try limiting cpus to 1.
# Default: 4
cpus: 4

# Memory size
# Default: "4GiB"
memory: "4GiB"

# Disk size
# Default: "100GiB"
disk: "10GiB"

# Expose host directories to the guest, the mount point might be accessible from all UIDs in the guest
# Default: none
mounts:
  - location: "~"
    # CAUTION: `writable` SHOULD be false for the home directory.
    # Setting `writable` to true is possible, but untested and dangerous.
    writable: false
  - location: "/tmp/lima"
    writable: true

ssh:
  # A localhost port of the host. Forwarded to port 22 of the guest.
  # Default: 0 (automatically assigned to a free port)
  localPort: 0
  # Load ~/.ssh/*.pub in addition to $LIMA_HOME/_config/user.pub .
  # This option is useful when you want to use other SSH-based
  # applications such as rsync with the Lima instance.
  # If you have an insecure key under ~/.ssh, do not use this option.
  # Default: true
  loadDotSSHPubKeys: true
  # Forward ssh agent into the instance.
  # Default: false
  forwardAgent: false

# ===================================================================== #
# ADVANCED CONFIGURATION
# ===================================================================== #

containerd:
  # Enable system-wide (aka rootful)  containerd and its dependencies (BuildKit, Stargz Snapshotter)
  # Default: false
  system: false
  # Enable user-scoped (aka rootless) containerd and its dependencies
  # Default: true
  user: false
#  # Override containerd archive
#  # Default: hard-coded URL with hard-coded digest (see the output of `limactl info | jq .defaultTemplate.containerd.archives`)
#  archives:
#    - location: "~/Downloads/nerdctl-full-X.Y.Z-linux-amd64.tar.gz"
#      arch: "x86_64"
#      digest: "sha256:..."

# Provisioning scripts need to be idempotent because they might be called
# multiple times, e.g. when the host VM is being restarted.
provision:
  - mode: system
    script: |
      #!/bin/sh
      sed -i 's/host.lima.internal.*/host.lima.internal host.docker.internal/' /etc/hosts
  - 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
  - mode: system
    script: |
      #!/bin/bash
      set -eux -o pipefail
      mkdir -p /etc/systemd/system/docker.service.d/
      cat <<EOF > /etc/systemd/system/docker.service.d/override.conf
      [Service]
      ExecStart=
      ExecStart=/usr/bin/dockerd -H tcp://127.0.0.1:2375
      EOF
      systemctl daemon-reload
      systemctl restart docker

probes:
#  # Only `readiness` probes are supported right now.
  - mode: readiness
    description: docker to be installed
    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
    hint: See "/var/log/cloud-init-output.log". in the guest

# ===================================================================== #
# FURTHER ADVANCED CONFIGURATION
# ===================================================================== #

firmware:
  # Use legacy BIOS instead of UEFI.
  # Default: false
  legacyBIOS: false

video:
  # QEMU display, e.g., "none", "cocoa", "sdl", "gtk".
  # As of QEMU v5.2, enabling this is known to have negative impact
  # on performance on macOS hosts: https://gitlab.com/qemu-project/qemu/-/issues/334
  # Default: "none"
  display: "none"

# The instance can get routable IP addresses from the vmnet framework using
# https://github.com/lima-vm/vde_vmnet.
networks:
  # Lima can manage daemons for networks defined in $LIMA_HOME/_config/networks.yaml
  # automatically. Both vde_switch and vde_vmnet binaries must be installed into
  # secure locations only alterable by the "root" user.
  # - lima: shared
  #   # MAC address of the instance; lima will pick one based on the instance name,
  #   # so DHCP assigned ip addresses should remain constant over instance restarts.
  #   macAddress: ""
  #   # Interface name, defaults to "lima0", "lima1", etc.
  #   interface: ""
  #
  # Lima can also connect to "unmanaged" vde networks addressed by "vnl". This
  # means that the daemons will not be controlled by Lima, but must be started
  # before the instance.  The interface type (host, shared, or bridged) is
  # configured in vde_vmnet and not in lima.
  # vnl (virtual network locator) points to the vde_switch socket directory,
  # optionally with vde:// prefix
  # - vnl: "vde:///var/run/vde.ctl"
  #   # VDE Switch port number (not TCP/UDP port number). Set to 65535 for PTP mode.
  #   # Default: 0
  #   switchPort: 0
  #   # MAC address of the instance; lima will pick one based on the instance name,
  #   # so DHCP assigned ip addresses should remain constant over instance restarts.
  #   macAddress: ""
  #   # Interface name, defaults to "lima0", "lima1", etc.
  #   interface: ""

# Port forwarding rules. Forwarding between ports 22 and ssh.localPort cannot be overridden.
# Rules are checked sequentially until the first one matches.
# portForwards:
#   - guestPort: 443
#     hostIP: "0.0.0.0" # overrides the default value "127.0.0.1"; allows privileged port forwarding
#   # default: hostPort: 443 (same as guestPort)
#   # default: guestIP: "127.0.0.1" (also matches bind addresses "0.0.0.0", "::", and "::1")
#   # default: proto: "tcp" (only valid value right now)
#
#   - guestPortRange: [4000, 4999]
#     hostIP:  "0.0.0.0" # overrides the default value "127.0.0.1"
#   # default: hostPortRange: [4000, 4999] (must specify same number of ports as guestPortRange)
#
#   - guestPort: 80
#     hostPort: 8080 # overrides the default value 80
#
#   - guestIP: "127.0.0.2" # overrides the default value "127.0.0.1"
#     hostIP: "127.0.0.2" # overrides the default value "127.0.0.1"
#   # default: guestPortRange: [1, 65535]
#   # default: hostPortRange: [1, 65535]
#
#   - guestPort: 8888
#     ignore: true (don't forward this port)
#
#   - guestSocket: "/run/user/{{.UID}}/my.sock"
#     hostSocket: mysocket
#   # "guestSocket" can include these template variables: {{.Home}}, {{.UID}}, and {{.User}}.
#   # "hostSocket" can include {{.Home}}, {{.Dir}}, {{.Name}}, {{.UID}}, and {{.User}}.
#   # Put sockets into "{{.Dir}}/sock" to avoid collision with Lima internal sockets!
#   # Sockets can also be forwarded to ports and vice versa, but not to/from a range of ports.
#   # Forwarding requires the lima user to have rw access to the "guestsocket",
#   # and the local user rwx access to the directory of the "hostsocket".
#
#   # Lima internally appends this fallback rule at the end:
#   - guestIP: "127.0.0.1"
#     guestPortRange: [1, 65535]
#     hostIP: "127.0.0.1"
#     hostPortRange: [1, 65535]
#   # Any port still not matched by a rule will not be forwarded (ignored)

# Message. Information to be shown to the user, given as a Go template for the instance.
# The same template variables as for listing instances can be used, for example {{.Dir}}.
# You can view the complete list of variables using `limactl list --list-fields` command.
# It also includes {{.HostOS}} and {{.HostArch}} vars, for the runtime GOOS and GOARCH.
message: |
  To run `docker` on the host (assumes docker-cli is installed):
  $ export export DOCKER_HOST='tcp://127.0.0.1:2375'
  $ docker ...


# Extra environment variables that will be loaded into the VM at start up.
# These variables are consumed by internal init scripts, and also added
# to /etc/environment.
# If you set any of "ftp_proxy", "http_proxy", "https_proxy", or "no_proxy", then
# Lima will automatically set an uppercase variant to the same value as well.
env:
  DOCKER_HOST: 'tcp://127.0.0.1:2375'

# Lima will override the proxy environment variables with values from the current process
# environment (the environment in effect when you run `limactl start`). It will automatically
# replace the strings "localhost" and "127.0.0.1" with the host gateway address from inside
# the VM, so it stays routable. Use of the process environment can be disabled by setting
# propagateProxyEnv to false.
# Default: true
propagateProxyEnv: true

# The host agent implements a DNS server that looks up host names on the host
# using the local system resolver. This means changing VPN and network settings
# are reflected automatically into the guest, including conditional forward,
# and mDNS lookup:
# Default: true
useHostResolver: false

# If useHostResolver is false, then the following rules apply for configuring dns:
# Explicitly set DNS addresses for qemu user-mode networking. By default qemu picks *one*
# nameserver from the host config and forwards all queries to this server. On macOS
# Lima adds the nameservers configured for the "en0" interface to the list. In case this
# still doesn't work (e.g. VPN setups), the servers can be specified here explicitly.
# If nameservers are specified here, then the "en0" configuration will be ignored.
dns:
- 1.1.1.1
- 1.0.0.1

# ===================================================================== #
# GLOBAL DEFAULTS AND OVERRIDES
# ===================================================================== #

# The builtin defaults can be changed globally by creating a $LIMA_HOME/_config/default.yaml
# file. It will be used by ALL instances under the same $LIMA_HOME, and it
# will be applied on each `limactl start`, so can affect instance restarts.

# A similar mechanism is $LIMA_HOME/_config/override.yaml, which will take
# precedence even over the settings in an instances lima.yaml file.
# It too applies to ALL instances under the same $LIMA_HOME, and is applied
# on each restart. It can be used to globally override settings, e.g. make
# the mount of the home directory writable.

# On each instance start the config settings are determined: If a value is
# not set in `lima.yaml`, then the `default.yaml` is used. If that file
# doesn't exist, or the value is not defined in the file, then the buildin
# default is used. If `override.yaml` exists and defines the value, then
# it overrides whatever has been choosen so far.

# For slices (e.g. `mounts`, `provision`) and maps (`env`) the entries are
# combined instead of replacing each other. Slices are produced from override
# settings, followed by lima.yaml, followed by defaults.yaml (but NOT from
# builtin defaults). Maps are produced starting with defaults.yaml values,
# overwriting with lima.yaml ones, overwriting with override.yaml.

# Exceptions:
# - `dns` will use the list from the highest priority file; they are not
#   combined. If override.yaml defines a list of `dns` entries, then the
#   settings in default.yaml and lima.yaml are ignored.
#
# - `mounts` will update the `writable` setting when 2 entries have the
#   same `location` value. For this reason they are processed in the opposite
#   order: starting with default, followed by lima, and then override.
#
# -`networks` will replace lower priority entries with the same `interface`
#   name with higher priority definitions. This does not apply if the
#  `interface` field is empty. `networks` are therefore also processed
#  in lowest to highest priority order.

# ===================================================================== #
# END OF TEMPLATE
# ===================================================================== #

VMの起動

定義ファイルをdocker.yamlとして保存したら、保存場所のディレクトリでVMを起動します。
起動処理の間、別のターミナルで起動ログを管理しましょう。

起動にかなり時間がかかる(7〜8分)のと、途中で起動状況の監視タイムアウトが発生する(FATA[0602] did not receive an event with the "running" status)ためです。
ログに[ OK ] Reached target Cloud-init target.が出れば起動完了です。

# Limaを起動
limactl start ./docker.yaml

# 別のターミナルでログを監視
# "[ OK ] Reached target Cloud-init target."が出るまで待機
tail -f ~/.lima/docker/serial.log

ホスト(クライアント側)の設定

VMで起動しているDockerサーバーをデフォルトとしたいので、環境変数で上書きします。

DOCKER_HOST環境変数を常時設定しておけるよう、~/.bashrcまたは~/.zshrcに記載します。

使っているシェルの環境で異なりますので、シェル種別の確認コマンドとシェル別の環境変数追加コマンドを用意しています。

「TCPは非推奨である」という話が他のサイトで見受けられますが、開発端末上の話の場合、Limaを起動させる場所やファイアウォールを適切に設定するよう注意していれば、大きなリスクはないと考えます(不特定多数が利用するネットワーク上での利用は十分な注意が必要です)。
Limaのシェル上でもsudoなし、rootless-kitなしで使えるようになるので、こちらにも利点があると考えています。
socket通信も可能なので、気になる方は調べて変更してみてください。

# シェル種別を判断
echo $SHELL

# 以下を~/.zshrcまたは~/.bashrcに追記する
# zsh
echo "export DOCKER_HOST='tcp://127.0.0.1:2375'" >> ~/.zshrc

# bash
echo "export DOCKER_HOST='tcp://127.0.0.1:2375'" >> ~/.bashrc

あとはターミナルをリロードし直すと、LimaのVMをDockerサーバーとして利用可能になります。

docker versionが問題無く動作するはずです。

(おまけ)VSCode設定

このままではVSCode上でコンテナを管理していた人は正しく動作しません。
VSCodeの設定を変更するとLima上のDockerを管理することが可能です。

設定画面を開き、フォームからdocker.hostと入力後、表示された設定項目のフォームにtcp://127.0.0.1:2375と入力することで使えるようになります。

さいごに

ここまでお読みいただきありがとうございました。

Limaの環境を捨ててしまうとデータが消えるのは注意したいところですが、Docker Desktopなしで使えるというのは便利です。

私はECSで使うイメージをビルドしているのですが、ECRに最後pushしてしまえば消えても困りませんね。Dockerfileは残りますし。

この記事は以上です。

目次