Testcontainers Java 集成 Ollama:在 JUnit 测试中启动本地 LLM 容器与模型镜像固化实战
2026/9/16 14:36:07 网站建设 项目流程

Testcontainers Java 集成 Ollama:在 JUnit 测试中启动本地 LLM 容器与模型镜像固化实战

【免费下载链接】testcontainers-javaTestcontainers is a Java library that supports JUnit tests, providing lightweight, throwaway instances of common databases, Selenium web browsers, or anything else that can run in a Docker container.项目地址: https://gitcode.com/GitHub_Trending/te/testcontainers-java

Ollama 是广受欢迎的本地大语言模型(LLM)运行环境,Testcontainers Java 提供了专门的testcontainers-ollama模块,让你可以在 JUnit 测试中启动一个真实可用的 Ollama 容器实例,通过其 HTTP API(端口 11434)拉取模型、调用推理接口。本文基于 docs/modules/ollama.md 展开,并结合仓库源码与测试用例,完整讲解该模块的依赖引入、容器启动、模型拉取、镜像固化(commit)与镜像名替换,帮助你构建可复现、可并行的 LLM 集成测试。

模块概览:OllamaContainer 做了什么

Testcontainers 的 Ollama 模块核心实现位于 OllamaContainer.java,它继承自GenericContainer<OllamaContainer>,为 Ollama 做了三件关键事情:

  1. 固定暴露 11434 端口:Ollama 的服务端口为11434,构造时通过withExposedPorts(OLLAMA_PORT)暴露该端口,便于测试代码访问 REST API;
  2. 自动检测 NVIDIA GPU 运行时:构造时读取 Dockerinforuntimes信息,若存在nvidia运行时,则自动为容器附加 GPUDeviceRequestcapabilities=[["gpu"]]count=-1),从而在支持 GPU 的环境下自动启用硬件加速;
  3. 提供便捷 APIgetPort()返回映射后的宿主机端口,getEndpoint()返回完整的http://host:port端点地址,commitToImage(imageName)用于把当前容器(含已拉取的模型)固化成新镜像。

从源码注释可知,该模块支持ollama/ollama官方镜像,依赖DockerImageName做镜像兼容性校验(assertCompatibleWith)。

添加模块依赖

在项目的pom.xmlbuild.gradle中加入testcontainers-ollama依赖即可({{latest_version}}替换为实际发布的版本号):

=== "Gradle"groovy testImplementation "org.testcontainers:testcontainers-ollama:{{latest_version}}"

=== "Maven"xml <dependency> <groupId>org.testcontainers</groupId> <artifactId>testcontainers-ollama</artifactId> <version>{{latest_version}}</version> <scope>test</scope> </dependency>

在当前仓库中,该模块位于 modules/ollama,由 settings.gradle 中遍历modules目录的构建逻辑统一纳入多模块构建。实际使用时通常还需要配合 JUnit 4(testcontainers)或 JUnit 5(testcontainers-junit-jupiter)等核心依赖一起使用。

启动一个 Ollama 容器

在任意 Java 应用中,通过OllamaContainer即可启动一个 Ollama 实例,测试代码参考 OllamaContainerTest.java:

try ( // container { OllamaContainer ollama = new OllamaContainer("ollama/ollama:0.1.26") // } ) { ollama.start(); String version = given().baseUri(ollama.getEndpoint()).get("/api/version").jsonPath().get("version"); assertThat(version).isEqualTo("0.1.26"); }

要点说明:

  • 构造器接收字符串镜像名,内部会解析为DockerImageName并校验是否兼容ollama/ollama镜像;
  • try-with-resources让容器在测试结束后自动停止并清理,这正是 Testcontainers "轻量、一次性实例" 的典型用法;
  • 启动后可通过getEndpoint()拿到的地址直接调用 Ollama 的 REST API(如/api/version/api/tags),上述测试即断言了返回的版本号与镜像标签一致,验证了容器内 Ollama 服务真实可用。

关于启动就绪等待,Testcontainers 默认会等待容器第一个映射端口开始监听(详见 startup_and_waits.md),Ollama 容器暴露的正是 11434 端口,因此默认等待策略即可覆盖大部分场景。

在容器内拉取模型

Testcontainers 允许在运行中的容器内执行命令(类似docker exec),完整说明见 commands.md。基于此,拉取模型非常简单,直接调用execInContainer执行 Ollama 的 CLI 即可:

// pullModel { ollama.execInContainer("ollama", "pull", "all-minilm"); // }

上述代码来自测试方法downloadModelAndCommitToImage,它在ollama/ollama:0.1.26容器内执行了ollama pull all-minilm命令。随后测试通过 HTTP API 验证模型确实可用:

String modelName = given() .baseUri(ollama.getEndpoint()) .get("/api/tags") .jsonPath() .getString("models[0].name"); assertThat(modelName).contains("all-minilm");

即通过GET /api/tags确认容器中已经存在all-minilm模型。由此可以看出,"启动容器 + execInContainer 拉模型" 是使用该模块的完整最小工作流:容器提供 Ollama 服务,命令在容器内执行,之后便可通过 HTTP API 进行推理调用。

将含模型的容器固化为新镜像

每次测试都重新拉取模型会比较耗时。Ollama 模块提供了commitToImage(String imageName)方法,把当前容器(包含已下载模型的文件系统变更)提交成一个新的 Docker 镜像,后续测试可直接基于该镜像秒级启动,无需再次下载。

// commitToImage { ollama.commitToImage(newImageName); // }

从 OllamaContainer.java 的源码可以看到该方法的幂等设计:

  • 若新镜像名与当前容器镜像名不同,则先通过listImagesCmd().withReferenceFilter(imageName)查询该镜像是否已存在;
  • 仅当镜像尚不存在时才执行docker commit,并将org.testcontainers.sessionId作为标签写入,避免重复提交、节省时间;
  • 提交时仓库(repository)与标签(tag)分别取自DockerImageNamegetUnversionedPart()getVersionPart(),因此传入形如tc-ollama-allminilm-xxxx:latest的完整镜像名即可。

测试中使用的镜像名是"tc-ollama-allminilm-" + Base58.randomString(4).toLowerCase(),通过随机后缀避免测试间镜像名冲突。

结合镜像名替换复用新镜像

固化出的新镜像可以配合 Testcontainers 的镜像名替换(Image Name Substitution)机制使用,让新镜像"伪装"成ollama/ollama,从而继续使用OllamaContainer的全部能力,详见 image_name_substitution.md 中的 手动替换(Manual substitution) 一节:

// substitute { OllamaContainer ollama = new OllamaContainer( DockerImageName.parse(newImageName) .asCompatibleSubstituteFor("ollama/ollama") ) // }

这段代码将新镜像通过asCompatibleSubstituteFor("ollama/ollama")声明为ollama/ollama的兼容替代品,绕过OllamaContainer构造器中的镜像名兼容性校验。测试随后再次调用GET /api/tags,断言all-minilm模型依然存在,从而证明固化镜像中的模型数据被完整保留。

这一组合的完整流程是:

  1. ollama/ollama:0.1.26启动容器;
  2. execInContainer拉取所需模型;
  3. commitToImage固化含模型的镜像;
  4. 后续测试用asCompatibleSubstituteFor直接使用固化镜像,跳过模型下载,显著提升测试速度与稳定性。

小结与延伸阅读

Testcontainers 的 Ollama 模块把"本地 LLM 环境"变成可编程、可回收的测试基础设施:OllamaContainer封装了端口暴露、GPU 检测与镜像兼容校验,配合execInContainer拉取模型、commitToImage固化模型镜像,以及asCompatibleSubstituteFor实现镜像名替换,即可搭建出稳定高效的 LLM 集成测试链路。

进一步了解可参考:

  • 模块完整实现:modules/ollama/src/main/java/org/testcontainers/ollama/OllamaContainer.java
  • 模块测试用例:modules/ollama/src/test/java/org/testcontainers/ollama/OllamaContainerTest.java
  • 容器内执行命令:docs/features/commands.md
  • 镜像名替换机制:docs/features/image_name_substitution.md
  • 容器启动等待策略:docs/features/startup_and_waits.md

【免费下载链接】testcontainers-javaTestcontainers is a Java library that supports JUnit tests, providing lightweight, throwaway instances of common databases, Selenium web browsers, or anything else that can run in a Docker container.项目地址: https://gitcode.com/GitHub_Trending/te/testcontainers-java

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询