如何通过Bazel Genrule结合现有Dockerfile构建Docker镜像?
问题:通过Bazel Genrule结合现有Dockerfile构建镜像的错误排查与可行示例
背景
我们有一个包含多语言及多工件的单体仓库,计划迁移至Bazel。为实现平滑过渡,现阶段暂不将所有Dockerfile转换为docker-rules,打算通过genrule调用现有Dockerfile构建镜像(已知这并非Bazel最佳实践)。
测试用Dockerfile内容:
FROM alpine:3.8 ENTRYPOINT ["echo"] CMD ["Hello Bazel!"]
遇到的两个问题
1. 手动执行tar+docker build命令报错
执行命令:
tar -czh . | docker build -t hello-bazel -
报错信息:
[+] Building 0.1s (2/2) FINISHED => [internal] load remote build context 0.0s => ERROR copy /context / 0.1s ------ > copy /context /: ------ failed to solve with frontend dockerfile.v0: failed to read dockerfile: Error processing tar file(gzip: invalid header):
2. Bazel Genrule构建失败
编写的genrule代码:
genrule( name = "gc-hello-bazel", srcs = ["Dockerfile"], outs = ["imagesha.txt"], cmd = "docker build -t hello-bazel -f $(location Dockerfile) . > $@", tools = ["Dockerfile"], )
报错信息:
failed to solve with frontend dockerfile.v0: failed to read dockerfile: open Dockerfile: no such file or directory
当前目录结构:
-WORKSPACE -<some-root-dirctories> -<a-root-directory> -<subdir> -<subsubdir1> -my_docker -Dockerfile -BUILD.bazel
错误原因与解决方案
针对问题1:tar命令参数错误
tar -czh .中-h参数会跟随符号链接,若当前目录存在循环链接或特殊文件,会导致生成的tar包损坏。正确做法是仅打包Dockerfile本身,避免冗余上下文:
tar -czf - Dockerfile | docker build -t hello-bazel -
针对问题2:Genrule沙盒路径问题
Bazel的genrule在沙盒环境中执行,工作目录并非源码目录:
$(location Dockerfile)指向沙盒内Dockerfile的路径,但你用.作为构建上下文时,docker会在沙盒根目录查找该路径,导致找不到文件- Genrule必须生成指定的
outs文件,否则会被判定为构建失败
可行Genrule示例1:临时目录方式
genrule( name = "build_hello_bazel", srcs = ["Dockerfile"], outs = ["hello_bazel_image_id.txt"], cmd = """ # 在输出目录下创建临时上下文目录 mkdir -p $(@D)/docker_context # 将沙盒中的Dockerfile复制到临时目录 cp $(location Dockerfile) $(@D)/docker_context/ # 基于临时目录执行构建 docker build -t hello-bazel $(@D)/docker_context # 将镜像ID输出到指定文件,满足Genrule的输出要求 docker inspect -f '{{.Id}}' hello-bazel > $@ """, )
可行Genrule示例2:管道传递方式(更简洁)
genrule( name = "build_hello_bazel", srcs = ["Dockerfile"], outs = ["hello_bazel_image_id.txt"], cmd = """ # 直接将Dockerfile内容通过管道传给docker build cat $(location Dockerfile) | docker build -t hello-bazel -f - - # 输出镜像ID到文件 docker inspect -f '{{.Id}}' hello-bazel > $@ """, )
额外注意事项
- 确保Bazel运行环境可调用
docker命令,必要时需配置沙盒允许访问宿主机Docker daemon tools字段无需重复声明Dockerfile,已在srcs中包含- 必须生成
outs文件,这里用docker inspect输出镜像ID来满足要求
执行构建
运行以下命令触发构建:
bazel build //a-root-directory/subdir/my_docker:build_hello_bazel
构建成功后,bazel-bin/a-root-directory/subdir/my_docker/hello_bazel_image_id.txt会包含镜像ID,本地Docker环境也会生成hello-bazel镜像。
内容的提问来源于stack exchange,提问作者Alonr
相关产品推荐
相关产品推荐

