如何在Docker容器中利用Go 1.10构建缓存加速Go项目构建?
哈哈,这个坑我刚用Go 1.10的时候也踩过!你没法直接go build vendor/...的原因很简单——Go的vendor目录是作为项目的依赖目录存在的,不是独立的包路径源,直接构建它会因为包路径不匹配而报错。咱们来调整一下Dockerfile,既能利用Docker缓存,又能让Go的build cache发挥作用:
先理清楚核心逻辑
Go 1.10的build cache会自动缓存已编译的包,但前提是依赖的文件(比如vendor目录)没有变化。咱们要做的就是把vendor目录作为独立的Docker缓存层,先复制到镜像里,再复制项目代码,这样只有当vendor变更时,这一层才会重新构建。
正确的Dockerfile写法(基础版)
这个版本利用Docker分层缓存特性,先固定vendor目录,再复制项目代码,Go会自动复用build cache里的vendor编译结果:
FROM golang:1.10-alpine # 切换到GOPATH下的项目工作目录(官方golang镜像默认GOPATH是/go) WORKDIR /go/src/myproject # 第一步:先复制vendor目录,这一层只会在vendor变更时重新构建 COPY ./vendor ./vendor # 第二步:复制项目的所有源代码(此时vendor已经存在,不会被覆盖) COPY . . # 构建你的可执行文件,Go会自动使用vendor依赖,且复用之前编译vendor的缓存 RUN go build -v -o myapp .
进阶版:预编译vendor依赖(进一步加速)
如果想提前把vendor里的依赖都编译好,避免后续build时重复处理,可以用go list遍历vendor里的所有包,提前预编译它们:
FROM golang:1.10-alpine # 可选:如果你的vendor里有带C代码的包,需要安装gcc等编译工具 RUN apk add --no-cache gcc musl-dev WORKDIR /go/src/myproject # 复制vendor目录 COPY ./vendor ./vendor # 预编译vendor下的所有依赖包,排除测试目录避免不必要的编译 RUN go list -f '{{.Dir}}' ./vendor/... | grep -v test | xargs go build -v # 复制项目源代码 COPY . . # 构建主项目,此时vendor依赖已经编译完成,直接复用缓存 RUN go build -v -o myapp .
为什么之前的写法会报错?
你之前的go build myproject/vendor/...是错误的:在GOPATH模式下,Go的包路径是相对于GOPATH/src的,vendor里的包实际导入路径是github.com/xxx/yyy这类格式,而不是myproject/vendor/github.com/xxx/yyy。直接构建myproject/vendor/...会让Go找不到匹配的包路径,自然就报“cannot find package”了。
简单来说,vendor目录是给主项目“调用”的,不是给你直接“构建”的——只要主项目的路径正确,Go会自动去vendor里找依赖,不需要单独构建vendor目录。
内容的提问来源于stack exchange,提问作者ahmet alp balkan

