照片来自 Shubham Dhage 和 Unsplash,拍摄照片。
在过去五年里,我深入研究了在大型Kubernetes集群中的微服务。我的典型工作流程是,在笔记本电脑上本地开发解决方案,严格测试这些解决方案,然后将这些修改提交到主分支。
Docker一直以来都是我首选的开发环境。然而,Docker最近的许可证变更让我不得不寻找替代方案。此外,我还想找一个兼容ARM和x64图像的Kubernetes环境。
我的要求就是这么简单:
docker-compose
功能。在本文中,我详细介绍了对不同容器和Kubernetes方案的研究,特别是在那些被视为微服务开发中Docker替代方案的方案。虽然不够全面,本文仍提供了相当详尽的概览。主要目的是以下几点:
这里值得一提的是,这里讨论的很多解决方案可以结合起来达到特定的效果,即使一开始可能无法覆盖所有的功能需求。例如,使用 Minikube 和 Podman 这样的组合可能会很有帮助,不过这并不是本文的重点。
我想先花点时间来探讨一些容器化的概念,这些概念将有助于我们理解在构建支持容器的开发环境时使用的软件栈。
我想首先花一些时间探讨几个容器化的概念。这些概念将帮助我们更好地理解在设置支持容器的开发环境中所使用的软件栈。
虚拟化软件,也称为“虚拟器”或“hypervisor”,是一种软件或平台层,允许多个操作系统在同一物理机上同时运行。它创建虚拟机(VM),每个虚拟机都像一个独立的物理设备,能够独立运行自己的操作系统和应用程序,
容器运行时是一种负责运行和管理容器的软件,这些容器是轻量级且隔离的单元,旨在确保应用程序及其依赖项在不同的环境中一致运行。运行时处理任务,如拉取镜像、创建、启动并监控容器,确保它们与其它进程隔离,并向它们提供必要的资源。常见的容器运行时有 Docker、containerd
和 runc
。
_> 根据以上所述,容器运行时可以运行的架构取决于底层hypervisor支持的架构。
这意味着,你的 Docker(或 Podman 或任何其他工具)环境是否支持 x64 和 ARM 两个架构,取决于 hypervisor 是否支持这些架构类型。
这是一个过于简化的图表,展示了在macOS环境下典型Docker Stack中的主机(Host)、来宾(Guest)和容器运行环境。
当在Linux上运行时,Docker利用了诸如命名空间和cgroups
等原生操作系统功能以促进容器化。因此,在Linux上则不需要虚拟机。然而,当涉及到macOS和Windows时,情况不同,虚拟化就变得必要了。通常,这些平台上会涉及以下元素:
根据你所选的工具,可能需要将容器运行时的套接字直接连接到主机上。Docker 会将 docker.sock
套接字连接到你的系统,从而使主机上的 CLI 能与虚拟机内的 Docker 通信。
说到Docker时,我们通常会提到以下工具:
containerd
进行底层容器管理。docker
和 docker-compose
,并设置自动补全:brew install docker-compose
。注意,这只会安装命令行工具,不会安装 Docker 引擎或 Docker 桌面应用,即不会安装整个 Docker 堆栈。docker
命令行工具、Docker 容器引擎和 Docker 桌面应用本身。containerd
— 是一个核心容器运行时,负责在其主机系统上管理容器的整个生命周期。它支持诸如拉取、推送和管理镜像等功能。最初,containerd
是 Docker 的一个核心组件,并由 Docker, Inc. 开发。然而,在后续的发展中,它从 Docker 中被解耦,成为一个独立的项目。因此,Docker 仍然依赖于 containerd
作为其基础运行时层。Rosetta 是一个翻译层,使为基于 Intel 的 Mac 电脑开发的应用程序能够在 Apple 芯片 Mac 电脑上运行。Apple 虚拟化框架提供一组 API,允许开发人员在 Apple 芯片 Mac 和基于 Intel 的 Mac 电脑上创建和管理虚拟机 (VM)。虚拟化框架和 Rosetta 一起工作,提供一种强大的方式在 Apple 芯片 Mac 上运行基于 Intel 的应用程序。在容器化上下文中,Rosetta 和 AVF 一起工作以允许运行 x64 容器。并非所有容器化解决方案都支持 Apple 的解决方案。实际上,大多数解决方案都使用 QEmu 而不是 Apple 的框架进行虚拟化,后者直到最近才得到支持。
QEMU 是一个通用且开源的机器模拟器和虚拟化器。它可以在不同的硬件平台上运行操作系统和应用程序。QEMU 常被用于容器化工具,以创建轻量级虚拟机以运行容器。
另一种工具用于容器化的方式是使用QEMU创建瞬时容器。瞬时容器是指可以迅速创建和销毁的容器。例如,QEMU可以基于不同操作系统和架构创建瞬时容器。
在容器技术背景下,QEMU被许多工具用来创建能够运行Arm和x64镜像的虚拟机。
_等会儿,这个不是自带的吗?_
首先,需要明确的是,严格来说,并没有“多架构镜像”。但在 Docker 生态系统中,镜像通常表现出这种行为。当你使用 Docker 构建并推送一个镜像时,默认使用你机器的架构。因此,如果系统支持的架构与镜像的架构不同,尝试运行时可能会遇到错误,比如“exec 格式错误”。
Docker 的多架构镜像是指多个针对相同应用但特定于不同架构的独立镜像。这些镜像通过单一清单文件统一在一起,该清单文件将各镜像的元数据与其对应的架构关联起来。Docker 对支持多架构的镜像的概念有一个很好的解释,更多详情请参阅以下链接:https://www.docker.com/blog/multi-arch-build-and-images-the-simple-way/
最近的 Docker 版本集成了对 Apple 虚拟化框架和 Rosetta 模拟器的支持。这一集成使得 Docker 可以同时支持 Arm 和 Intel 架构的镜像,这意味着 Docker 现在可以处理这两种架构的镜像。但 Intel 架构的镜像则是通过 QEmu 模拟器来进行支持的。如预期的那样,使用模拟器处理 Intel 镜像会带来一些性能上的开销。
当你在终端执行 docker pull
命令时,Docker 会试图获取一个与你的机器架构相匹配的镜像文件。如果没有找到匹配的镜像文件,Docker 会选择一个 AMD64 架构的镜像文件,并依赖 Rosetta 来运行。
值得注意的是,许多在 Docker Hub 上的项目提供了“多架构镜像”。比如,NGINX 的 Docker 页面则说明了支持的架构。
在 macOS 上的 Docker Desktop 中,这种灵活性也延伸到了 Kubernetes。通过在 Docker Desktop 中启用 Kubernetes,您实际上告诉 Kubernetes 在 Docker 运行时环境中运行。这意味着 Kubernetes 将使用同样的容器运行时和虚拟机,就像 Docker 一样。由于容器运行时也借助了 Apple 的 Rosetta,Kubernetes 也因此能通过模拟运行 x64 容器,利用了 Docker 运行时的功能支持。
现在我们介绍了一些基础概念,让我们来探索一些基于这些技术的工具。虽然这并不是一个详尽的列表,但它提供了一些替代Docker开发微服务的选择或工具,无论你是为了运行单个容器还是整个微服务堆栈。
Minikube也可以独立运行。它支持不同类型的驱动。如果我们不考虑Docker,剩下的可行选择是QEMU和Hyperkit。
安装完 brew install minikube
后
Hyperkit 在我的机器上似乎不受支持。
➜ ~ minikube start --driver=hyperkit W0910 16:57:02.238664 4768 main.go:291] 无法解析当前 Docker CLI 上的上下文 "default": 上下文 "default": 未找到上下文: open /Users/csotiriou/.docker/contexts/meta/37a8eec1ce19687d132fe29051dca629d164e2c4958ba141d5f4133a33f0688f/meta.json: 没有那个文件或目录 😄 哎呀,minikube v1.31.2 在 Darwin 13.5.1 (arm64) 上启动 ✨ 根据用户配置,使用 hyperkit 驱动 ❌ 哎不行,由于不支持 'hyperkit' 驱动(darwin/arm64),退出
当使用 QEmu 和严格使用 AMD64 的容器时,我遇到以下错误:
执行 /usr/sbin/nginx: 执行格式错误:无法执行 流已到 EOF,关闭了 default/my-deployment-698dfbdfb7-wkcm2 (my-container)
这意味着目前我们使用 QEmu 的 Minikube 安装还不能模拟 i386 容器。不过,这种方法还不能解决不安装就能直接运行 docker
和 docker-compose
的问题,但我还是想试一试。
Minikube 支持一个 Podman 驱动,这看起来很有前景,因为 Podman 可以模拟和使用 x64 镜像——不过在写这篇文章的时候,它仍处于实验性阶段,我还没有机会试用它。
我必须承认,multipass / microk8s 的组合确实是一个非常吸引人的技术栈组合。添加新的虚拟机简直小菜一碟,而只需要运行一个命令就能设置一个新的 Kubernetes 集群。
Microk8s 使用 Multipass 作为底层。Multipass 安装并运行一个标准的 Ubuntu 虚拟机。然后在该虚拟机中安装 Microk8s 并完成必要的配置以连接到主机。
Multipass 和 MicroK8s 这个组合在容器编排领域提供了一个很棒的技术栈。启动新的虚拟机特别简单快捷,初始化新的 Kubernetes 集群只需要一条命令即可。
实际上,MicroK8s 使用 Multipass。Multipass 负责启动和运行一个标准的 Ubuntu 机器。在虚拟机内部,它会安装 MicroK8s 并进行必要的配置,以连接 guest 机器与 host 机器。
虽然工具集非常强大且无可否认,但也存在一个缺点:我难以在 MicroK8s 中执行 x64 镜像,尽管 Multipass 高度依赖 QEmu。此外,就像普通的 Minikube 一样,它在主机上无缝集成 Docker 实例方面也做得不够好。鉴于这些挑战,我决定寻找其他解决方案。
Podman(https://podman.io/?ref=oramind.com)是一款与 Docker 兼容的容器管理和编排工具。当 Docker 之前宣布其许可变更时,Podman 成为我考虑的第一个可能的替代方案。然而,刚开始使用时,我遇到了不少挑战,主要与文件夹挂载和网络配置相关。
快进到现在,Podman 已经有了显著的进步。最近几个月我一直在用它,对其功能感到非常满意。此外,还有一个 Podman Desktop,进一步简化了 Pod 和单个容器的编排与部署过程。
Podman 在底层使用了 QEmu。Quarkus 团队制作了一个关于如何在 Apple Silicon 设备上操作 Podman 的详尽教程。Podman 架构对 QEmu 的依赖显而易见。这很有益,因为 QEmu 在模拟不同架构方面很有优势,使 Podman 能够轻松支持 x64 镜像。虽然 QEmu 的 x64 模拟速度可能不如 Apple 的 Rosetta 2 快,后者是通过 Virtualization Framework 实现的,但对于大多数使用场景来说,QEmu 的表现已经足够好。
QEmu 对 x64 的模拟速度没有苹果通过 Virtualization Framework 提供的 Rosetta 2 快,但对我们来说已经足够了。因为 Podman 支持运行 ARM 和 x64 容器,我们可以安装 KIND — Kubernetes 在 Docker 中,并利用它的高级模拟能力来完成我们想要的任务。
对我来说,Podman 和 KIND 配合得非常好,完全满足了我的需求。
注意:我非常依赖Docker Compose。虽然有传言说Podman可能与docker-compose不兼容,但我的使用经历表明两者可以很好地协同工作。不过,重要的是要理解,虽然Podman常被宣传为可以完全替代Docker,但两者之间存在一些细微区别。如需深入了解这两者的差异,可以参考这篇文章:这篇文章。
利马(Lima)是一个很好的虚拟机编排器。
Lima 代表 Li nux Ma chines,它是一款非常多功能的工具,用于在 macOS 和 Linux 上设置 Linux 机器。它经常与 nerdctl 配合使用,后者是一个与 Docker 兼容的 containerd
命令行接口(CLI)。值得注意的是,Docker 也是基于 containerd
构建的。它的多功能性得益于支持的虚拟化和容器引擎的多样性。
对我而言,特别感兴趣的是这里描述的“Fast Mode 2, Rosetta”虚拟化技术,它允许我利用Rosetta 2模拟来设置了一台安装了k3s和containerd
的机器。以下是我的设置文件(yaml格式)。
vmType: "vz" rosetta: # 启用 Linux 上的 Rosetta。 # 试试 `softwareupdate --install-rosetta`,如果 Lima 卡在 `Installing rosetta...` 这个步骤。 enabled: true # 将 rosetta 注册到系统中 binfmt: true images: # 尽量使用 release-yyyyMMdd 形式的镜像。请注意,这种形式的镜像会在几个月后被移除。 - location: "https://cloud-images.ubuntu.com/releases/22.04/release-20230729/ubuntu-22.04-server-cloudimg-amd64.img" arch: "x86_64" digest: "sha256:d5b419272e01cd69bfc15cbbbc5700d2196242478a54b9f19746da3a1269b7c8" - location: "https://cloud-images.ubuntu.com/releases/22.04/release-20230729/ubuntu-22.04-server-cloudimg-arm64.img" arch: "aarch64" digest: "sha256:5ecab49ff44f8e44954752bc9ef4157584b7bdc9e24f06031e777f60860a9d17" # 如果 release-yyyyMMdd 形式的镜像不可用,则回退到最新的 release 版本的镜像。 # 提示:运行 `limactl prune` 来刷新缓存或清除缓存 - 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" mounts: [] containerd: system: false user: false provision: - mode: system script: | #!/bin/sh if [ ! -d /var/lib/rancher/k3s ]; then curl -sfL https://get.k3s.io | sh -s - --disable=traefik fi probes: - script: | #!/bin/bash set -eux -o pipefail if ! timeout 30s bash -c "until test -f /etc/rancher/k3s/k3s.yaml; do sleep 3; done"; then echo >&2 "k3s 还未启动或运行" exit 1 fi hint: | 尚未创建 k3s 配置文件。 运行 "limactl shell k3s sudo journalctl -u k3s" 查看日志。 如果日志为空,请检查 "/var/log/cloud-init-output.log" 的日志。 copyToHost: - guest: "/etc/rancher/k3s/k3s.yaml" host: "{{.Dir}}/copied-from-guest/kubeconfig.yaml" message: | 要在主机上运行 `kubectl`(假设 kubectl 已安装),请执行以下命令: ------ export KUBECONFIG="{{.Dir}}/copied-from-guest/kubeconfig.yaml" kubectl ... ------
通过这种方式,我就有了一个能够无缝地运行ARM和x64容器的Kubernetes集群。然而,Lima和nerdctl
是一个相对底层的工具集。虽然这些工具工作得很好,但我使用的一些工具,包括Quarkus(以及它的Dev Services),并不能自动利用这些工具。
使用 Lima,我成功地设置了 Kubernetes,使其能够顺畅运行 ARM 和 x64 容器。然而,Lima 和 nerdctl
处在一个相对较低的层次。虽然它们表现出色,但,我使用的某些工具,如 Quarkus(特别是它的 Dev Services),并不能直接使用,因为这些工具不知道如何访问 Docker Socket 和其他用于构建完整 Docker 环境的必要组件。因此,如果坚持使用 Lima,就需要进行更多的手动配置来完成设置。
这就是科利马出场的时候了。
Colima 实质上是在 Lima 的基础上构建的一个精简层,起到了增强封装的作用。尽管它保持了 Lima 的多功能性,Colima 进一步简化了设置 Linux 容器的过程,并强调了与 macOS 系统的更深层次整合。
比如,我用来开始新Colima安装的命令是:
在终端中输入以下命令来启动 Kubernetes 集群:
运行 colima start --kubernetes --arch aarch64 --vm-type=vz --vz-rosetta
命令。其中 --kubernetes
表示启动 Kubernetes 集群,--arch aarch64
指定架构为 aarch64,--vm-type=vz
指定虚拟机类型为 vz,--vz-rosetta
表示启用 vz rosetta。
执行此命令将会安装如下内容:
containerd
的虚拟机。该虚拟机基于非常精简的 Alpine 镜像,并使用 Lima。docker
运行时的 Kubernetes (k3s) 系统~/.colima/default/docker.sock
,挂载了一个套接字,以便与其它 Docker 运行时一起使用如果你想通过 Docker CLI 使用这个特定主机,可以使用 docker context use <<colima context 名称>>
在不同的 Docker 兼容引擎之间切换。
如果你也像我一样使用 Quarkus 的开发服务,你可以在运行 Quarkus 开发服务时,设置 DOCKER_HOST
环境变量。
比如说
export DOCKER_HOST="unix://${HOME}/.colima/default/docker.sock" mvn compile quarkus dev # 导出DOCKER_HOST环境变量并运行mvn命令编译项目并启动quarkus开发服务器.
你可以把下面这行加进你的 .bashrc 或 .zshrc 文件里,以便每次都能自动使用 Colima 的 Docker Socket。
export DOCKER_HOST="unix://${HOME}/.colima/default/docker.sock" # 导出 DOCKER_HOST 环境变量为 'unix://${HOME}/.colima/default/docker.sock'
Rancher Desktop 是基于 Lima 的。在第一次启动时,会在 ~/.rd/bin
目录下创建存放 docker 可执行文件别名的地方,这些别名指向应用程序中的二进制文件:
Rancher Desktop.app/Contents/Resources/resources/darwin/bin
提供了一套命令行工具,包括 rdctl
,这款工具用于与 Rancher 进行交互,以及常用的工具,例如 docker
、docker-compose
、nerdctl
和 docker-buildx
。
有趣的是,即使你决定在首次启动后移动“Rancher Desktop.app”,该应用程序也会通过自动更新路径,确保一切都能正常使用,从而保持一切正常运作。
当你启动 Rancher Desktop 时,将会执行以下堆栈(stack)。
~/.kube/config
文件,添加了 rancher-desktop
上下文,以便你可以与嵌入的 k3d 进行交互--docker
选项运行,从而使用 Docker 守护进程来运行 Kubernetes。containerd
,则不会安装 Docker 守护进程,k3s 将仅使用 containerd 运行时启动。Rancher 还提供了支持 Apple 的虚拟化框架,该框架使用 Rozetta 使 x64 模拟更快。QEmu 也表现不错。如果同时启用这两个选项,可以使用这两种选项下的 x64 镜像。
基于 Lima 构建,Rancher Desktop 拥有多个固有的特点和优势:
Rancher Desktop,完全基于开源组件如Moby、containerd和K3s构建,为想要创建用于开发目的的本地Kubernetes环境的任何人提供了一个出色的解决方案。我在生产环境中依赖K3s,因为它非常可靠。Rancher Desktop的独特之处在于它平滑地集成了K3s。它结合了全面的Kubernetes系统的强大功能与低资源消耗,同时确保最佳性能。额外的优势是,K3s的高人气保证了社区支持更加直接,使其区别于其他解决方案。
虽然 K3s 可以与各种产品一起安装,然而 Rancher Desktop 提供了一个一键安装包,确保你从一开始就能用上所需的一切。
使用 Rancher Desktop 与 Quarkus 的开发服务时,需要将挂载的套接字暴露在 DOCKER_HOST
变量中,就像其他在此讨论的选项一样。
export DOCKER_HOST=unix:///Users/<<username>>/.rd/docker.sock
使用 docker context list
找到实际的套接字挂载位置,然后,相应地调整你的命令。
我发现Rancher Desktop简单地解决了我所有的问题,而且使用起来也很简单。这目前就是我的首选。
OrbStack 是容器管理领域的新秀,与其他同类产品明显不同。它的功能正在扩展,目前可以与 Rancher Desktop 相媲美。尤其是在底层功能和像 limactl
这样的多功能性方面,其功能目前尚不及 Rancher Desktop 丰富。为了更好地展示其能力,我将它与 Rancher Desktop 进行比较——不仅因为其强大的工具,还在微服务开发(如 Docker 和 Kubernetes)方面与 Rancher Desktop 类似。值得一提的是,OrbStack 最近增加了在虚拟机中运行 Kubernetes 的新功能。
Orbstack的一个显著特点是它的资源占用非常低。在我的实验中,使用它来创建Kubernetes部署,它表现出了高效和可靠。该平台在同一虚拟机中管理和运行Docker和Kubernetes容器。
ORBStack 与其竞争对手的一个区别是它与 macOS 的紧密集成,非常轻量。对我来说,它功能上也不输 Rancher Desktop,但在性能和效率上也更加出色。
在我的机器上,启动带有Kubernetes的Orbstack只需要2秒钟。CPU和内存的使用都非常低。Orbstack初始资源分配较为保守,然后根据工作负载动态增加内存。使用Lima时,我也发现了类似的现象,但却有一些细微的差异值得注意。
在最基本的 Kubernetes 配置中,没有运行任何应用程序的情况下,Orbstack 消耗的资源一直更少,相比其他同类工具。
我的另一个观察是关于用户界面性能与Rancher Desktop的比较。Rancher Desktop似乎是用Electron或类似的框架开发的。这一应用在我的机器上大约消耗了800兆字节的内存,相比之下,Orbstack只用了大约100兆字节的内存。因此,内存消耗的差距更大,使得在Orbstack中菜单导航感觉迅速且顺畅,而在Rancher Desktop中则感觉有点慢。
Orbstack 也会像 Rancher Desktop 那样挂载 Docker socket,你可以通过 docker context list
找到它挂载的位置,以便与你的其他开发堆栈配合使用。
在撰写本文时,我发现了Orbstack与Rancher Desktop相比可能存在以下三个潜在缺点:
定价模型:Orbstack 将转向成为一款商业用途的付费产品。编辑:定价详情如下:https://orbstack.dev/pricing。这种定价模式可能看起来像是一个门槛,但重要的是要权衡产品的价值。鉴于 Orbstack 在 macOS 集成方面的出色表现及其相应的开发努力,其定价是合理的。
默认情况下没有可用的 Linux 机器:另一个区别在于,在仅运行 Docker 和 Kubernetes 时,Orbstack 并不提供一个底层的 Linux 机器。虽然可以通过 Orb 安装并运行一个 Linux 机器(这仍然保持了最小的资源消耗),但它作为一个独立的 VM 运行,与主 Docker 和 Kubernetes 环境分离。因此,除非在该虚拟机中安装 Docker CLI,否则无法直接访问 Docker 命令行工具。虽然这并未给我带来问题,但对于潜在用户来说,可能需要仔细考虑这一点。
总之,虽然Orbstack有多个优势,但这些潜在的限制同样需要考虑,以便做出明智的决定。对于大多数希望简单无缝地设置K8s和Docker的macOS用户来说,OrbStack的定价模式将立即显示出其价值。如果您需要更多自定义选项,可以看看我们之前提到的其他选择。
这篇文章远远超出了我最初的意图。尽管它很长,我仅仅触及了所讨论工具的表面,真是令人惊讶。每种方法都有其独特的优缺点,这些都取决于个人的具体需求。
说起来,真是看到自苹果自研芯片首次发布以来所取得的重大进步确实令人欣慰。如今,我们现在有许多强大的开发选项。令人鼓舞的是,我们已经达到了一个阶段,微服务开发的生态系统已经变得成熟和高效。
Apple 的虚拟化技术
原文发布于https://oramind.com ,2023年9月19日发布.