训练营学员

傲来操作系统训练营报告

Docker Moby测试在ARM与RISC-V平台上的执行与性能评估

马震 发布于 # 2024 年第一期

项目背景介绍

随着计算机硬件架构的多样化发展,ARMRISC-V 作为两种重要的处理器架构,正在越来越多的领域得到应用。

ARM 架构以其高效能和低功耗著称,广泛应用于移动设备、嵌入式系统和服务器领域。在云原生领域, 的生态系统已经相当成熟和完善。

相比之下,RISC-V 作为一种开源指令集架构,因其灵活性和开放性,正在迅速崛起,并在学术研究和工业应用中逐步获得关注。然而,RISC-V 的生态系统相对 ARM 来说仍处于发展阶段,需要通过对比研究找到自身的差距。

Docker 是当前最流行的容器化平台,其核心引擎是由 Moby 项目提供的。本项目特别关注 Moby 测试在ARMRISC-V 硬件架构上的执行与性能表现。

Docker 和 Moby 的关系

Docker 最初就是作为一个开源项目开始的,后来演变成现在的 Docker 平台。

为了增强灵活性和模块化,Docker 公司在2017年将 Docker 引擎的核心组件分离出来,创建了 Moby 项目。

Moby 是 Docker 引擎的上游项目,它包含了 Docker 引擎的核心组件和一些实验性功能,而 Docker 引擎则是基于 Moby构建的一个具体实现。

Moby test 分析

Moby 项目包含了两篇测试相关的文档:

通过这两篇文档,我们知道,Moby 项目的测试主要分为以下几类:

  • 单元测试,使用 make test-unit 运行。
  • 集成测试,使用 make test-integration 运行。
  • Docker API client 测试,使用 make test-docker-py 运行。

make test 命令会自动执行以上所有测试。本文后面主要讨论 test-unit 测试。

进一步分析 Makefile,发现 Moby 的测试是运行在 Docker 容器中的:

.PHONY: test-unit
test-unit: build ## run the unit tests
	$(DOCKER_RUN_DOCKER) hack/test/unit

test-unit 目标依赖于 build 目标,而 build 目标主要是用来构建 Docker 镜像 docker-dev

build: bundles
	$(BUILD_CMD) $(BUILD_OPTS) $(shell_target) --load -t "$(DOCKER_IMAGE)" .

build 目标使用 buildx 构建 Docker 镜像,这意味着 build 目标会创建一个特定开发环境 Docker 镜像,这个镜像包括所有必要的依赖和工具,以便在一个统一的环境中进行测试。

另外,buildx 是 Docker 的多平台构建工具,提供了一些标准 docker build 所没有的高级功能和优化。

Moby test 运行方式的问题

Moby 的所有测试都跑在容器镜像 docker-dev 中,这样做的好处是让每次运行测试的环境都完全一致,包括go的版本、各种环境变量的设置等都能够统一,不依赖当前系统的设置,跑完测试,也不会影响系统的设置。

但这样的测试方式并不合适我们这次项目的目标。我们希望在 ARM 和 RISC-V 上直接使用 go test -v 的方式跑测试,以获得不同硬件架构下的性能表现。如果这些测试跑在 docker 容器中,不便于使用 perf 收集性能指标。

配置 Moby 单元测试的独立运行环境

Moby 的测试跑在容器中是为了获得环境的隔离和统一,如果我们还原出 docker-dev 镜像安装的依赖,目录结构,以及环境变量的设置,那么我们就可以脱离容器,直接在 Host 跑 Moby 的测试。

通过分析 Dockerfile,我们发现对于运行 Moby 的单元测试,配置出统一的运行环境并不复杂,主要有下面几项:

  • 指定 go 的版本为 1.21.11
  • 设置Go相关的环境变量
export GOTOOLCHAIN=local
export GO111MODULE=off
export GOPATH=...
export GOROOT=...

docker 开发的比较早,并没有使用 Go 1.11 才引入的 Modules 功能,而是使用传统的 GOPATH 模式,因此需要设置好 GOPATH 环境变量,并将 Moby 的源码放在合适的目录下。

GOPATH/
├── bin/
├── pkg/
└── src/
     └── github.com/
            └── docker/
                  └── docker/
                        └── moby source code
  • 配置系统的语言和区域设置
export LANG=en_US.UTF-8
export LC_ALL=en_US.UTF-8

使用统一的语言和区域设置,避免字符编码问题。

设置好这几项,我们就可以在 Host 上跑测试 moby 的单元测试了。

运行 Moby 单元测试

配置好单元测试的运行环境后,我们如何使用 go test -v 运行测试呢?还是先回顾一下原先是如何在 docker 容器中跑测试的。

.PHONY: test-unit
test-unit: build ## run the unit tests
	$(DOCKER_RUN_DOCKER) hack/test/unit

可以看出,运行单元测试就是在容器中执行 hack/test/unit 脚本。

接着我们分析 hack/test/unit 脚本,它主要执行了以下几步:

  1. 使用 go list $TESTDIRS 列出所有需要测试的package。
  2. 将这些 package 分成两类:基础单元测试libnetwork 相关的测试
  3. 使用 gotestsum 运行 基础单元测试
  4. 使用 gotestsum 运行 libnetwork 相关的测试

为什么又要把单元测试分成两类呢?因为 libnetwork 的测试需要调用 iptables 修改系统配置,如果多个测试并行运行可能会导致测试之间相互干扰,所以运行 libnetwork 测试需要增加 -p=1 标识,指示 go test 命令以单个包为单位串行执行测试。而基础单元测试没有这个限制,可以并行执行。

至于 gotestsum,可以很容易的替换为 go test -v,只需将 -- 后的参数传给 go test 即可。最终运行测试的命令如下:

go test -v ${BUILDFLAGS} ${TESTFLAGS} ${base_pkg_list} 
go test -v ${BUILDFLAGS} -p=1 ${TESTFLAGS} ${libnetwork_pkg_list}

如果需要收集运行过程中的性能指标,可以使用老师提供的 performance_counter_920.sh 脚本:

performance_counter_920.sh "go test -v ${BUILDFLAGS} ${TESTFLAGS} ${base_pkg_list}" "${reports}/test_base_perf"
performance_counter_920.sh "go test -v ${BUILDFLAGS} -p=1 ${TESTFLAGS} ${libnetwork_pkg_list}" "${reports}/test_libnetwork_perf"

至此,我们就完成了直接在 Host 上运行 Moby 单元测试的任务。

自动化脚本

为了能重复执行上述的环境设置和测试步骤,我们使用 bash 脚本将这些执行步骤自动化。

脚本主要执行流程如下:

  • 自动下载适合不同 CPU 架构的 go 安装包
  • go 安装在当前测试目录,测试过程使用自己下载的 go,不使用也不影响系统的 go 安装
  • 自动下载 Moby 源码仓库,需要放置在 $GOPATH/src/github.com/docker/docker 目录下
  • 配置好测试环境,包括 go 的版本、环境变量、语言和区域设置等
  • 分别运行两类单元测试,并收集性能指标
  • 对测试结果进行统计和输出

另外,为了避免网络问题影响测试,可以自定义下面的配置:

  • GO_BASE_URL 设置 go 的下载网址,例如可设置为 https://golang.google.cn/dl
  • REPO_URL 设置 Moby 源码仓库。可以提前将 Moby 仓库从 GitHub 导入到 gitee

自动测试脚本已经提交到 gitee 上,地址为 https://gitee.com/mz1999/moby-test

测试结果

由于当前只有 ARM64 的资源,因此我们只在 ARM64 上运行了 Moby 的单元测试。测试结果如下:

Number of packages in base_pkg_list: 224
Total tests: 2466
Passed tests: 2448
Failed tests: 18

Number of packages in libnetwork_pkg_list: 59
Total tests: 501
Passed tests: 347
Failed tests: 154

基础单元测试一共运行了 2466 个测试,其中 2448 个测试通过,18 个测试失败。暂未分析失败原因。

libnetwork 相关的测试一共运行了 501 个测试,其中 347 个测试通过,154 个测试失败。

由于 libnetwork 的很多测试涉及 iptables 修改系统设置,需要 sudo 权限,因此有大量失败。

go test -v 运行过程的详细输出,以及运行过程的 pert 统计指标,原始文件提供如下:

如果 risc-v 资源到位,可以使用自动化脚本很方便的运行测试,然后和 arm64 的结果进行对比。

未完继续

基础单元测试和 libnetwork 相关测试的失败案例,还未进行深入的分析。

当前只运行了 Moby 的单元测试,还没有运行集成测试和Docker API client 测试。初步判断,如果想将这两类测试也脱离 docker 环境运行,还需要做更多的工作。