尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

Terratest 使用 Docker 进行本地迭代测试:把基础设施代码的“单元测试“跑在容器里

发布时间:2026/9/27 8:21:13

资讯中心
01
ARTICLE

Terratest 使用 Docker 进行本地迭代测试:把基础设施代码的“单元测试“跑在容器里

Terratest 使用 Docker 进行本地迭代测试:把基础设施代码的“单元测试“跑在容器里
测试开发工具DevOps质量保障【免费下载链接】terratestTerratest is a Go library that makes it easier to write automated tests for your infrastructure code.项目地址https://gitcode.com/gh_mirrors/te/terratest点击查看免费下载Terratest 官方最佳实践文档《Iterating locally using Docker》给出了一套用 Docker 让脚本类基础设施代码Bash、Python、Go实现完全本地化测试的方法论利用 Docker 容器比真实服务器构建快 10 倍、启动快 100 倍的特性通过 Packer Docker builder、预构建测试镜像、docker-compose.yml 和 Mock 脚本四招把原本必须在 AWS 等真实环境才能验证的逻辑搬到笔记本上秒级迭代。读完本文你将掌握这套本地迭代工作流的完整搭建方式并能在仓库的 Packer Docker Example 与 TestPackerDockerExampleLocal 中直接看到可运行的落地代码。为什么基础设施代码也需要本地迭代大部分基础设施代码Terraform 资源编排、AMI 构建等的最终验证对象是真实云环境除了部署到 AWS 别无选择。但有一类代码例外——脚本Bash、Python、Go 编写的配置脚本、启动脚本、provisioning 脚本。Terratest 文档明确指出Docker 容器通常比真实服务器构建快 10 倍、启动快 100 倍因此用 Docker 跑这些脚本可以把改一行代码 → 重新部署 → 验证的漫长循环压缩到秒级。这与 test-docker-images/README.md 中Unit Tests with Terratest的理念一脉相承用 Packer 把生产环境AMI的 provisioner 脚本原样打到 Docker 镜像里在本地快速验证脚本逻辑等价于为 Packer 模板写单元测试而真正把 AMI 部署到 AWS 的验证则属于集成测试交给 terraform-packer-example 这类示例完成。仓库实践中采用的本地迭代手段可以归纳为以下四条下文逐一展开脚本若被 Packer 模板使用在模板中增加Docker builder用同一份代码产出 Docker 镜像使用仓库预构建的、已装好常用依赖curl、vim、tar、sudo 等的主流 Linux 发行版 Docker 镜像用docker-compose.yml统一声明端口、环境变量、挂载等运行参数一条命令起容器用Mock 脚本bind-mount 进容器并提前出现在PATH替换真实依赖做到 100% 本地测试。技巧一Packer 模板加 Docker builder一份脚本两种产物如果你的脚本出现在 Packer 模板的 provisioner 里最省事的做法是给同一个模板加一个 Docker builderDocker builder 与 AMI builder 执行完全相同的 provisioner 脚本但产物是 Docker 镜像而非云镜像。这样你在本地docker run得到的环境就是将来 AMI 里脚本环境的预演。以仓库的 Packer Docker Example 为例该模板用同一个脚本创建一个带 Sinatra 应用Ruby的 Ubuntu AMI同时创建装有同一应用的 Docker 镜像并配套 docker-compose.yml。关键的共享 provisioner 脚本是 configure-sinatra-app.sh它做的事情包括#!/bin/bash # 安装并配置一个基于 Ruby / Sinatra 的简单 Web 应用 set -e readonly APP_RB_SRC/tmp/packer-docker-example/app.rb readonly APP_RB_DST/home/ubuntu/app.rb echo Installing Ruby sudo apt-get update sudo apt-get install -y make zlib1g-dev build-essential ruby ruby-dev echo Installing Sinatra sudo gem install sinatra json rackup puma echo Moving $APP_RB_SRC to $APP_RB_DST mkdir -p $(dirname $APP_RB_DST) mv $APP_RB_SRC $APP_RB_DST注意脚本开头使用的sudo apt-get、sudo gem install—— 这类命令正是真实 AMI 环境中脚本的典型形态而 Docker 镜像中同样可以执行因此二者可以共用。模板的构建方式随 Packer 版本略有差异仓库 README 原文Packer 1.7.0先packer init build.pkr.hcl初始化模板依赖再packer build build.pkr.hcl构建Packer 1.7.0直接packer build build.json。若只想在本地构建 Docker 镜像而跳过 AWS可仿照 packer_docker_example_test.go 中Only: docker.ubuntu-docker的写法用-onlyubuntu-docker指定只执行 Docker builder。技巧二预构建测试镜像省去每轮下载依赖的时间即便有了 Docker builderpacker build每次仍会从零构建镜像——因为Packer 不像原生docker build那样利用镜像缓存这一点在 test-docker-images/README.md 中有明确说明导致大量时间浪费在反复下载 curl、sudo 等库上。Terratest 的解法是预构建一套规范化的 Gruntwork Terratest Docker 镜像把 AMI 环境中默认存在的常用工具预先装好公开在 Docker Hub如gruntwork/ubuntu-test。仓库的 test-docker-images 目录下目前维护了四类镜像gruntwork-amazon-linux-testAmazon Linux 系gruntwork-centos-testCentOS 系gruntwork-ubuntu-testUbuntu 系moto模拟 AWS API 的本地服务见技巧四。以 gruntwork-ubuntu-test/Dockerfile 为例基于 ubuntu:22.04预装的依赖覆盖了调试与运维场景的常用工具curl、wgetHTTP 请求与下载sudo、vim文档中重点提到的日常运维依赖dnsutils提供dig等 DNS 排查工具jqJSON 命令行解析python3/python3-pipPython 脚本运行环境rsyslog、software-properties-common、gpg-agent、ca-certificates日志与软件源管理另外还预装了 AWS CLIpip3 install awscli --upgrade和 Docker CE。Packer 模板引用这些镜像的方式摘自 test-docker-images/README.md 的示例配置{ builders: [{ name: ubuntu-ami, type: amazon-ebs // ...(其余参数略)... },{ name: ubuntu-docker, type: docker, image: gruntwork/ubuntu-test:18.04, commit: true }], provisioners: [ // ... ], post-processors: [{ type: docker-tag, repository: gruntwork/example, tag: latest, only: [ubuntu-docker] }] }要点builder 的image直接指向预构建镜像作为基础层commit: true让 Packer 把 provisioner 产生的层提交为新的 Docker 镜像最后用docker-tagpost-processor 打上仓库标签。这样packer build就省去了最耗时的系统包下载阶段。技巧三docker-compose.yml 统一管理运行参数镜像构建好之后如何以正确的端口、环境变量、文件挂载跑起来Terratest 团队的做法是写一个docker-compose.yml把这一切声明化。仓库的 docker-compose.yml 是一个完整的可复制范例# 该文件用于在本地 100% 运行 Packer 模板中的 Web 应用无需向 AWS 部署任何东西 version: 3 services: web_app: # 与 build.json或 build.pkr.hcl中 Docker 镜像名保持一致 image: gruntwork/packer-docker-example # 在 8080 端口运行示例 Web 应用端口与文案由环境变量注入 command: [ruby, /home/ubuntu/app.rb, ${SERVER_PORT}, ${SERVER_TEXT}] # bind-mount 本地 Ruby 应用文件实现测试期间的热更新 volumes: - ./app.rb:/home/ubuntu/app.rb # 把容器内端口暴露到宿主机 ports: - ${SERVER_PORT}:${SERVER_PORT}各配置项的实战含义配置项作用与取值建议image引用 Packer 构建出的镜像本例为gruntwork/packer-docker-example必须与 Packer 模板里 Docker builder/post-processor 产出的镜像名一致command覆盖镜像默认入口${SERVER_PORT}、${SERVER_TEXT}为 Compose 变量插值运行时可注入端口和响应文本volumes将宿主机./app.rbbind-mount 到容器内/home/ubuntu/app.rb本地改代码后无需重建镜像即可生效热更新ports${SERVER_PORT}:${SERVER_PORT}把容器端口映射到宿主机同端口被挂载的 app.rb 是一个极简 Sinatra 应用要求恰好两个命令行参数SERVER_PORT和SERVER_TEXT绑定0.0.0.0用 puma 启动对GET /返回server_text——这正好与 docker-compose.yml 的command一一对应也便于测试断言响应内容。在仓库 README 的说明中本地启动只需两步运行docker compose up老版本环境可用docker-compose up访问 http://localhost:8080 即可看到示例 Web 应用。技巧四Mock 脚本替换真实依赖100% 本地测试对于脚本中的真实依赖最典型的是 AWS CLITerratest 文档给出了一个非常轻量的 Mock 方案创建与真实命令同名的 mock 脚本通过 bind-mount 挂进容器并让它提前出现在PATH中从而截胡对真实依赖的调用。文档中的例子如果你的脚本调用awsCLI就创建一个名为aws的 mock 脚本把它放在PATH中优先级更高的位置。于是脚本里所有aws调用都会落到 mock 上而不会真正请求 AWS API。这样测试可以 100% 在本地完成完全摆脱 AWS 之类的外部依赖。仓库中还有一条更重但更逼真的 Mock 路线test-docker-images里的 Moto 镜像。Moto 是一个本地服务接收 AWS API 调用并返回合法响应资源仅存于内存不创建任何真实资源可以看作本地 AWS。它与业务容器组成容器集群共同运行# 启动 Moto模拟 EC2 API允许任意 IP 访问 docker run -p 5000:5000 gruntwork/moto moto_server ec2 --host 0.0.0.0由于 Moto 的 API 与官方 AWS API 完全一致任何 AWS SDK含 AWS CLI都能直接使用只需把 endpoint 指向本地aws --region us-west-2 --endpoint-urlhttp://localhost:5000 \ ec2 run-instances --image-id ami-abc12345 \ --tag-specifications ResourceTypeinstance,Tags[{KeyServerGroupName,Valuejosh}]Moto 支持所有 AWS 区域还会自动创建带默认子网的 VPC。这两条 Mock 路线轻量 mock 脚本 vs 完整 Moto 服务可按脚本对 AWS 交互的深度取舍。用 Terratest 把这套流程自动化读一个真实测试上面的构建、运行、验证步骤若全部手工执行依然繁琐。Terratest 的 docker 与 packer 模块把这些操作封装成了 Go 测试代码仓库的 TestPackerDockerExampleLocal 是完整的端到端范例其执行流程为配置 Packer 只构建 Docker 镜像Only: docker.ubuntu-docker并配置重试策略packerOptions : packer.Options{ Template: ../examples/packer-docker-example/build.pkr.hcl, // 只为本地测试构建 Docker 镜像 Only: docker.ubuntu-docker, RetryableErrors: DefaultRetryablePackerErrors, TimeBetweenRetries: DefaultTimeBetweenPackerRetries, MaxRetries: DefaultMaxPackerRetries, } packer.BuildArtifactContext(t, t.Context(), packerOptions)通过 docker.Options 声明 Compose 运行参数工作目录 环境变量随后用docker.RunDockerComposeContext执行up -d启动容器并defer注册down保证测试结束清理serverPort : 8080 expectedServerText : fmt.Sprintf(Hello, %s!, random.UniqueID()) dockerOptions : docker.Options{ WorkingDir: ../examples/packer-docker-example, EnvVars: map[string]string{ SERVER_PORT: strconv.Itoa(serverPort), SERVER_TEXT: expectedServerText, }, } defer docker.RunDockerComposeContext(t, t.Context(), dockerOptions, down) docker.RunDockerComposeContext(t, t.Context(), dockerOptions, up, -d)注意这里的环境变量与 docker-compose.yml 中的${SERVER_PORT}、${SERVER_TEXT}是同一套变量Terratest 会把EnvVars注入到docker compose进程Compose 再插值进command。用 httphelper 轮询断言容器启动需要几秒所以重试 5 次、每次间隔 2 秒maxRetries : 5 timeBetweenRetries : 2 * time.Second url : fmt.Sprintf(http://localhost:%d, serverPort) httphelper.HTTPGetWithRetryContext(t, t.Context(), url, tlsConfig, 200, expectedServerText, maxRetries, timeBetweenRetries)从源码层面看 Terratest 是如何驱动 Docker Compose 的modules/docker/docker_compose.go有几个值得注意的实现细节命令选择runDockerComposeE先执行docker compose version探测若返回码为 0 则用新式docker compose --project-name ...否则回退到旧式docker-compose兼顾两种安装形态项目名未显式设置ProjectName时默认取测试名t.Name()的小写形式并经过generateValidDockerComposeProjectName把特殊字符替换为-docker-compose 不接受小写以外的特殊字符避免并行测试的项目名冲突BuildKit 开关EnableBuildKit: true时会自动注入DOCKER_BUILDKIT1与COMPOSE_DOCKER_CLI_BUILD1环境变量以启用 BuildKit 加速构建。本地跑这个测试的完整前置条件README 原文安装 Packer 与 Docker 并加入PATH、安装 Go然后在test目录执行go test -v -run TestPackerDockerExampleLocal。落地建议把这套工作流套到你的脚本上把上面的四招组合成你自己的本地迭代工作流可参考以下路径识别可本地化的部分凡是 Bash / Python / Go 脚本provisioning、启动、健康检查脚本都值得用 Docker 本地验证只有 Terraform 资源编排这类与云资源强耦合的代码仍需真实环境。共享脚本把脚本以 provisioner 形式同时喂给 AMI builder 和 Docker builder参考 configure-sinatra-app.sh 的写法保证本地验证的内容 生产部署的内容。用预构建基础镜像优先以gruntwork/ubuntu-test等镜像作为 Docker builder 的基础层避免每轮packer build重复下载依赖。声明运行方式用 docker-compose.yml 管理端口、环境变量、bind-mount需要热更新脚本时就挂载本地文件。Mock 外部依赖简单场景用提前排入PATH的 mock 脚本需要模拟 AWS API 语义时引入 Moto 这类本地服务。交给 Terratest 固化用packer.BuildArtifactContextdocker.RunDockerComposeContexthttphelper.HTTPGetWithRetryContext把整条链路写成可重复执行的 Go 测试作为 CI 中的快速反馈环。这套方法的本质是把部署到真实环境这件事从每次迭代的必要步骤中剥离出来——只有最终验收时才上云日常开发在容器里完成秒级闭环。这也正是 Terratest 文档强调用 Docker 测试能让你迭代快得多的核心原因。赞分享测试开发工具DevOps质量保障【免费下载链接】terratestTerratest is a Go library that makes it easier to write automated tests for your infrastructure code.项目地址https://gitcode.com/gh_mirrors/te/terratest点击查看免费下载相关推荐Terratest强大的基础设施代码测试框架Terratest强大的基础设施代码测试框架 是一个开源的 Go 语言库专为编写自动化测试而设计用于验证你的 Terraform、CloudFormati测试开发工具DevOps质量保障Seafile基础设施即代码测试使用Terratest验证部署Seafile基础设施即代码测试使用Terratest验证部署 痛点与解决方案 你是否曾因手动部署Seafile服务器而遭遇配置不一致、环境依赖冲突等问题本存储数据同步基础设施即代码测试Awesome Sysadmin Terratest实战基础设施即代码测试Awesome Sysadmin Terratest实战 你是否还在为基础设施即代码Infrastructure as Code, IaC知识库运维上一篇GitHub_Trending/ssr-benchmark扩展指南添加SvelteKit流式渲染支持下一篇如何用LLDAP实现SSO单点登录终极完整配置教程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。