赞
踩
公众号关注 「奇妙的 Linux 世界」
设为「星标」,每天带你玩转 Linux !

首先要明确的是, 作为了一个每天在 Linux Server 上 rm -rf 的人来说, 如果想在 Mac 上使用 Docker, 最舒服的也是兼容所有 docker cli 命令行操作即可; 至于图形化的界面完全不需要, 我们并不指望图形化界面能比敲命令快到哪里去, 也不指望图形化界面变为主力; 所以本篇文章的核心目标:
在 Mac 上使用完整的 docker cli 命令, 包括对基本的 -v 挂载支持
可以支持 x86 的模拟, 可以为 x86 build 或者运行相关镜像
在尽可能的情况下可以进行 CPU 架构切换, arm64 与 x86 最好都可以支持
首先是我们最熟悉的 Docker Desktop, 安装包奇大无比, UI 卡成翔, 启动速度更不用提而且还时不时的卡死, 所以 Docker Desktop 是完全不考虑的; 那么剩下几种方案类型如下:
VM 虚拟机方案
Colima 方案
Lima 方案
先说结论: Lima YES! VM 虚拟机方案要花钱且难受, Colima 暂且不稳定. Lima 方案直接看第五节.
目前在 M1 上, 唯一可用或者说堪用的虚拟机当属 Parallels Desktop, 至于其他的 VBox、VMware 目前还不成熟; 如果纯 qemu 有点过于硬核(愿意自己封装脚本的当我没说); 对于 Parallels Desktop 来说, 我们需要购买开发版本的 License, 因为我们需要借助 prlctl 来实现一些自动化 , 一年好几百… 经过测试这种方案也有一定可行性:
1、首先通过 PD 创建 Ubuntu 之类的虚拟机
2、在虚拟机里安装好 Docker
3、通过 cli 程序启动虚拟机, 并且将 ~ rw 挂载到虚拟机里
基于这个方案我个人尝试过, 曾经写过一个 PD 的小工具来辅助完成挂载动作. 但是这种工具有一些明显的缺点:
目前不支持 x86 的模拟, 可通过 binfmt 缓解, 但是不完善
虚拟机要花钱且需要虚拟机 cli 支持完善
Colima 号称是专门为了解决 Mac 平台容器化工具链的, 但是实际测试发现目前 Colima 还不算稳定, 有时可能会有一些小问题; 当然 Colima 最大的问题是: 可自定义化程度不高, 底层基于 Lima. Colima 具体的使用方式啥的这里暂不详细描述, 目前还不稳定不太推荐.
Lima 目前是基于 QEMU 的自动化 VM 方案, 当前由于其出色设计, 借助 Cloud Init 可以在很多阶段帮助我们完成 hook; 所以不论是装个 Docker 还是 k8s, 亦或是弄个其他的东西都很方便; 而且很多方案比如 docker 官方都有相关样例, 我们可以直接照抄外加做点自定义.
Lima 在 Mac 下安装相对简单, 以下命令将安装 master 分支版本.
brew install lima --HEAD
在正常情况下, 安装 Lima 会附带安装 QEMU, 如果本机已经安装 QEMU, 可能需要执行以下命令将 QEMU 升级到 7.0:
brew upgrade qemu
为了使用 docker, 还需要通过 brew 安装一下 docker cli:
brew install docker
默认情况下 Lima 安装完成后会生成一个 lima 的快捷命令, 目前不太推荐使用, 原因是看起来方便一点但是没法控制太多参数, 所以仍然建议使用标准的 limactl 命令进行操作. limactl 使用方式如下:
- Lima: Linux virtual machines
-
- Usage:
- limactl [command]
-
- Examples:
- Start the default instance:
- $ limactl start
-
- Open a shell:
- $ lima
-
- Run a container:
- $ lima nerdctl run -d --name nginx -p 8080:80 nginx:alpine
-
- Stop the default instance:
- $ limactl stop
-
- See also example YAMLs: /opt/homebrew/share/doc/lima/examples
-
- Available Commands:
- completion Generate the autocompletion script for the specified shell
- copy Copy files between host and guest
- delete Delete an instance of Lima.
- edit Edit an instance of Lima
- factory-reset Factory reset an instance of Lima
- help Help about any command
- info Show diagnostic information
- list List instances of Lima.
- prune Prune garbage objects
- shell Execute shell in Lima
- show-ssh Show the ssh command line
- start Start an instance of Lima
- stop Stop an instance
- sudoers Generate /etc/sudoers.d/lima file for enabling vmnet.framework support
- validate Validate YAML files
-
- Flags:
- --debug debug mode
- -h, --help help for limactl
- -v, --version version for limactl
-
- Use "limactl [command] --help" for more information about a command.Copy

Lima 通过读取一个 yaml 配置描述文件来决定如何创建一个虚拟机, 该文件基本结构如下:
- # 定义每个平台架构需要使用的启动镜像
- images:
- - location: "https://cloud-images.ubuntu.com/releases/22.04/release/ubuntu-22.04-server-cloudimg-amd64.img"
- arch: "x86_64"
- - location: "https://cloud-images.ubuntu.com/releases/22.04/release/ubuntu-22.04-server-cloudimg-arm64.img"
- arch: "aarch64"
-
- # 定义虚拟机需要使用哪个架构启动(对应上面的镜像)
- arch: "x86_64"
-
- # CPU 数量
- cpus: 4
-
- # 内存大小
- memory: "16G"
-
- # 磁盘大小
- disk: "100G"
-
- # 虚拟机与 macOS 宿主机挂载时使用的挂载技术
- # 目前推荐 9p, 可换成 sshfs, 但是 sshfs 会有权限问题
- mountType: 9p
-
- # 定义虚拟机和 macOS 宿主机有哪些目录可以共享
- mounts:
- - location: "~"
- # 定义虚拟机对这个目录是否可写
- writable: true
- 9p:
- # 对于可写的共享目录, cache 推荐类型为 mmap, 不写好像默认 fscache
- cache: "mmap"
- - location: "/tmp/lima"
- writable: true
- 9p:
- cache: "mmap"
- # containerd is managed by Docker, not by Lima, so the values are set to false here.
- containerd:
- system: false
- user: false
-
- # cloud-init hook 定义
- provision:
- # 定义以什么权限在虚拟机内执行脚本
- - mode: system
- # This script defines the host.docker.internal hostname when hostResolver is disabled.
- # It is also needed for lima 0.8.2 and earlier, which does not support hostResolver.hosts.
- # Names defined in /etc/hosts inside the VM are not resolved inside containers when
- # using the hostResolver; use hostResolver.hosts instead (requires lima 0.8.3 or later).
- 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
- if command -v docker >/dev/null 2>&1; then
- docker run --platform=linux/amd64 --privileged --rm tonistiigi/binfmt --install all
- exit 0
- else
- export DEBIAN_FRONTEND=noninteractive
- curl -fsSL https://get.docker.com | sh
- docker run --platform=linux/amd64 --privileged --rm tonistiigi/binfmt --install all
- # 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 dbus-user-session
- fi
- - mode: user
- script: |
- #!/bin/bash
- set -eux -o pipefail
- systemctl --user start dbus
- dockerd-rootless-setuptool.sh install
- docker context use rootless
- 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
- hostResolver:
- # hostResolver.hosts requires lima 0.8.3 or later. Names defined here will also
- # resolve inside containers, and not just inside the VM itself.
- hosts:
- host.docker.internal: host.lima.internal
- portForwards:
- - guestSocket: "/run/user/{{.UID}}/docker.sock"
- hostSocket: "{{.Dir}}/sock/docker.sock"
- # 自己定义的启动后消息输出
- message: |
- To run `docker` on the host (assumes docker-cli is installed), run the following commands:
- ------
- docker context create amd64 --docker "host=unix://{{.Dir}}/sock/docker.sock"
- docker context use amd64
- ------
- Copy

limactl 命令提供了一个 start 子命令用于启动一个虚拟机, 子命令接受一个参数, 这个参数形式不同会产生不同的行为:
如果参数为一个文件路径, 则假定文件为一个 lima 虚拟机的 yaml 配置, 读取并启动
如果参数是单纯字符串, 首先尝试从已存在的虚拟机中查找名字相同的, 找到则立即启动
如果参数是单纯字符串, 且未找到已存在同名的虚拟机, 则尝试通过内置模版来创建一个新的虚拟机
以上面我自己定义的 docker 配置文件为例, 我们直接启动这个配置既可以创建一个 docker 虚拟机:
limactl start ./docker-amd64.yaml
启动后会提示是否编辑然后再启动, 这是为了使用同一个配置来启动多个 vm 使用的, 所以不编辑直接启动即可:
稍等片刻后虚拟机将启动成功:
启动完成后, 执行最下面打印出的两条命令, 即可在宿主机上完整的使用 docker. 其本质上利用 docker context 功能, 然后通过将虚拟机中的 sock 文件挂载到宿主机, 并配置 docker context 来实现无缝使用 docker 命令.
某些情况下, 我们需要定制一些 VM 里的配置, 在定制时主要需要调整配置文件的 provision 部分; 在该部分中, 如果 mode 被定义为 system 则会以 root 用户执行相关命令, 否则以普通用户来执行命令. 需要注意的是, 我们定义的脚本需要具有幂等性, 因为脚本在每次都会执行一次, 所以一般对于可能造成数据擦除动作的命令都要写好判断逻辑, 避免重复执行.
关于文件挂载, 这里推荐使用 9p 类型, 未来 lima 将完全切换到该挂载方式; 同时经过测试目前仅有 9p 挂载模式下, 本地目录 rw 映射到虚拟机时不会出现权限问题, sshfs 方式挂载如果遇到 chown 之类的命令会造成权限错误, 可能导致容器启动失败(例如 mysql).
在测试虚拟机配置过程中, 可以直接使用 limactl delete -f xxxx 来强制删除目标虚拟机, 然后重新启动即可; 虚拟机名称默认与 yaml 文件名相同, 可使用 limactl ls 命令查看.
在上面我的 docker 配置样例中, 每次虚拟机启动完成后会自动安装 binfmt:
docker run --platform=linux/amd64 --privileged --rm tonistiigi/binfmt --install all
这样能保证无论 Lima 虚拟机原始架构是什么, 都能运行其他平台的 docker 镜像; 典型的例如某些 openjdk8 镜像只有 amd64 的版本, 但是在 lima 虚拟机为 aarch64 的情况下仍然可以使用.
除了这种 “速度较快” 的跨架构运行方式, lima 还支持直接在 VM 中定义架构, 这样在 qemu 启动时则会直接从 VM 系统层模拟目标架构; 这种方式的好处是对目标架构兼容性很好, 但是运行速度会更慢. 调整 VM 架构只需要修改 arch 配置即可(注意, 目标架构的镜像一定要配置好):
- # 定义每个平台架构需要使用的启动镜像
- images:
- - location: "https://cloud-images.ubuntu.com/releases/22.04/release/ubuntu-22.04-server-cloudimg-amd64.img"
- arch: "x86_64"
- - location: "https://cloud-images.ubuntu.com/releases/22.04/release/ubuntu-22.04-server-cloudimg-arm64.img"
- arch: "aarch64"
-
- # 定义本虚拟机需要使用哪个架构启动(对应会使用上面目标架构的镜像)
- arch: "aarch64"Copy
目前整体来看, Docker Desktop 在 mac 上基本上是很难用的, Colima 现在还不太成熟, 适合轻度使用 docker 的用户; 而重度使用 docker 并且有定制化需求的用户还是推荐 Lima 虚拟机; 同时 Lima 也支持很多操作系统, 官方有大量的样例模版(包括 k8s、k3s、podman 等), 非常适合重度容器使用者.
本文转载自:「 bleem 的博客 」,原文:https://url.hi-linux.com/DZxIp/ ,版权归原作者所有。欢迎投稿,投稿邮箱: editor@hi-linux.com。

最近,我们建立了一个技术交流微信群。目前群里已加入了不少行业内的大神,有兴趣的同学可以加入和我们一起交流技术,在 「奇妙的 Linux 世界」 公众号直接回复 「加群」 邀请你入群。

你可能还喜欢
点击下方图片即可阅读

更多有趣的互联网新鲜事,关注「奇妙的互联网」视频号全了解!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。