如何使用Groovy的de.gesellix docker-client构建Docker镜像?
如何使用Groovy的de.gesellix docker-client构建Docker镜像?
我来帮你排查这个问题~你遇到的{"message":"unexpected EOF"}错误,核心原因是直接传递Dockerfile的输入流不符合Docker远程API的要求——Docker构建镜像时需要的是包含Dockerfile的上下文tar归档流,而不是单独的Dockerfile文本流。咱们一步步调整代码来解决:
1. 理解问题根源
Docker的Build API接收的是整个构建上下文的压缩包(通常是tar格式),这个上下文里包含Dockerfile以及构建过程中需要的所有文件(比如拷贝到镜像里的本地文件)。你之前直接传了Dockerfile的文本流,客户端解析时自然会因为格式不对报“意外EOF”。
2. 修正代码的两种方式
方式一:用临时目录作为构建上下文(最简单)
我们可以把Dockerfile写入临时目录,让客户端自动处理上下文的打包:
#!/usr/bin/env groovy @Grapes( [ @Grab(group='de.gesellix', module='docker-client', version='2025-01-19T00-00-00'), @Grab(group='commons-io', module='commons-io', version='2.18.0'), ] ) import de.gesellix.docker.remote.api.BuildInfo import de.gesellix.docker.remote.api.BuildImageRequest import de.gesellix.docker.remote.api.core.StreamCallback import de.gesellix.docker.client.DockerClientImpl import java.nio.charset.StandardCharsets void buildTestContainer() { def dockerBuildStr = """FROM ubuntu:25.04 RUN env DEBIAN_FRONTEND=noninteractive apt update && apt install -y curl python3-pyqt6 libmpv-dev python3-pycodestyle xvfb RUN curl -LsSf https://astral.sh/uv/install.sh | env XDG_BIN_HOME=/bin sh """.stripIndent() // 创建临时目录并写入Dockerfile def tempDir = File.createTempDir() def dockerFile = new File(tempDir, "Dockerfile") dockerFile.text = dockerBuildStr def dockerClient = new DockerClientImpl() println(dockerClient.images()) println(dockerClient.ping().content) // 构建请求配置,可添加标签等参数 def buildRequest = new BuildImageRequest() buildRequest.setTags(["my-test-image:latest"]) // 使用临时目录作为上下文执行构建 dockerClient.build(new StreamCallback<BuildInfo>() { @Override void onNext(BuildInfo buildInfo) { println(buildInfo) } }, java.time.Duration.ofDays(1), buildRequest, tempDir ) // 清理临时目录(可选) tempDir.deleteDir() } buildTestContainer()
方式二:手动打包Dockerfile为tar流(适合无文件系统场景)
如果不能创建临时目录,我们可以手动把Dockerfile打包成tar流传给客户端:
#!/usr/bin/env groovy @Grapes( [ @Grab(group='de.gesellix', module='docker-client', version='2025-01-19T00-00-00'), @Grab(group='commons-io', module='commons-io', version='2.18.0'), @Grab(group='org.apache.commons', module='commons-compress', version='1.26.1'), ] ) import de.gesellix.docker.remote.api.BuildInfo import de.gesellix.docker.remote.api.BuildImageRequest import de.gesellix.docker.remote.api.core.StreamCallback import de.gesellix.docker.client.DockerClientImpl import org.apache.commons.compress.archivers.tar.TarArchiveEntry import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream import java.nio.charset.StandardCharsets void buildTestContainer() { def dockerBuildStr = """FROM ubuntu:25.04 RUN env DEBIAN_FRONTEND=noninteractive apt update && apt install -y curl python3-pyqt6 libmpv-dev python3-pycodestyle xvfb RUN curl -LsSf https://astral.sh/uv/install.sh | env XDG_BIN_HOME=/bin sh """.stripIndent() // 手动将Dockerfile打包为tar流 def baos = new ByteArrayOutputStream() def tarOut = new TarArchiveOutputStream(baos) tarOut.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX) def entry = new TarArchiveEntry("Dockerfile") entry.setSize(dockerBuildStr.getBytes(StandardCharsets.UTF_8).length) tarOut.putArchiveEntry(entry) tarOut.write(dockerBuildStr.getBytes(StandardCharsets.UTF_8)) tarOut.closeArchiveEntry() tarOut.close() def contextStream = new ByteArrayInputStream(baos.toByteArray()) def dockerClient = new DockerClientImpl() println(dockerClient.images()) println(dockerClient.ping().content) def buildRequest = new BuildImageRequest() buildRequest.setTags(["my-test-image:latest"]) dockerClient.build(new StreamCallback<BuildInfo>() { @Override void onNext(BuildInfo buildInfo) { println(buildInfo) } }, java.time.Duration.ofDays(1), buildRequest, contextStream ) } buildTestContainer()
3. 额外注意点
- 确保Dockerfile的语法正确:你的Dockerfile里的命令已经用
stripIndent()处理了缩进,没问题,但要注意&&连接命令的格式(你已经写对了)。 - 可以在
BuildImageRequest里添加更多参数,比如setBuildArgs()传递构建参数,setPull(true)强制拉取基础镜像等。
备注:内容来源于stack exchange,提问作者djsumdog
相关产品推荐
相关产品推荐

