V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
caicaiwoshishui
V2EX  ›  Go 编程语言

docker build go 项目每次都很慢,有什么好的办法吗?

  •  
  •   caicaiwoshishui · 2020-12-01 17:03:59 +08:00 · 5982 次点击
    这是一个创建于 1480 天前的主题,其中的信息可能已经有所发展或是发生改变。
    • dockerfile 部分内容
    COPY . .
    
    ADD go.mod .
    ADD go.sum .
    
    RUN export GO111MODULE=on && \
        export GOPROXY=https://goproxy.cn && \
        go mod download
    
    RUN go build -o main main.go
    

    接触 golang 没多久,是不是姿势不对呀? mod 没有更新的情况,为啥每次都 go mod download,它不会自己缓存吗?

    求大佬解答

    第 1 条附言  ·  2020-12-02 09:39:35 +08:00
    感谢大家,特别是三楼的兄弟,编译速度减少至 3 分钟了。
    26 条回复    2020-12-01 22:12:15 +08:00
    junnplus
        1
    junnplus  
       2020-12-01 17:11:04 +08:00   ❤️ 1
    你在镜像里面构建 go,怎么会缓存- -
    可以先构建完代码,把二进制 copy 进容器里面
    kkbblzq
        2
    kkbblzq  
       2020-12-01 17:16:12 +08:00   ❤️ 1
    你可以选择用 vendor
    zealic
        3
    zealic  
       2020-12-01 17:24:13 +08:00   ❤️ 2
    没有合理的利用缓存,改成下面这样就快了

    ```dockerfile
    ADD go.mod .
    ADD go.sum .

    RUN export GO111MODULE=on && \
    export GOPROXY=https://goproxy.cn && \
    go mod download

    COPY . .

    RUN go build -o main main.go
    ```
    julyclyde
        4
    julyclyde  
       2020-12-01 17:24:22 +08:00   ❤️ 1
    这不是穿上裤子放屁么
    直接光屁股编译还能用上本地缓存
    Mitt
        5
    Mitt  
       2020-12-01 17:27:03 +08:00 via iPhone   ❤️ 1
    是因为 COPY . . 在 go mod download 前面,会导致下面 go mod download 也重建了,放到缓存之后就可以了
    Vegetable
        6
    Vegetable  
       2020-12-01 17:32:42 +08:00   ❤️ 1
    因为缓存不在你当前文件夹,你镜像里并没有缓存。楼上说的用 vender 可以,但是改变比较大。
    也可以将镜像的缓存目录挂载到本地。
    也可以不要每次都用 makefile 建立新的镜像,开发的时候用一个镜像将代码挂在进去。
    方法很多,你这个是最离谱的。
    hteen
        7
    hteen  
       2020-12-01 17:46:32 +08:00   ❤️ 1
    docker 在 build 的时候 Dockerfile 中每个 RUN 最终都会是独立容器运行, 你这样等于没用, 反而增加构建之后 image 体积
    crclz
        8
    crclz  
       2020-12-01 17:51:25 +08:00   ❤️ 2
    c# go maven gradle 的统一解决思路:
    1. 仅拷贝依赖清单
    2. 下载依赖
    3. 拷贝所有文件
    4. 编译

    如果第一步的依赖清单不改变,那么第二步开始的时候,docker 检测到校验和是相同的,所以 docker 会直接使用缓存。

    #3 楼的貌似是对的(我没用过 docker go )
    xuanbg
        9
    xuanbg  
       2020-12-01 17:51:30 +08:00
    先编译好 copy/add 进去不好么
    caicaiwoshishui
        10
    caicaiwoshishui  
    OP
       2020-12-01 17:58:15 +08:00
    @zealic 谢谢大佬, 确实快了很多,是不是 copy . . 放在前面导致 2 次 go mod download 呀
    caicaiwoshishui
        11
    caicaiwoshishui  
    OP
       2020-12-01 17:58:55 +08:00
    @Mitt 谢谢大佬,copy . . 放在前面会导致 2 次 go mod download 吗?
    boris93
        12
    boris93  
       2020-12-01 18:00:50 +08:00 via Android   ❤️ 1
    镜像里面放编译产物
    不要在镜像里面编译
    TypeErrorNone
        13
    TypeErrorNone  
       2020-12-01 18:13:46 +08:00
    在本地编译出二进制文件,再 copy 进镜像
    FROM alpine

    COPY main /

    CMD ["/main"]

    3 秒结束
    caicaiwoshishui
        14
    caicaiwoshishui  
    OP
       2020-12-01 18:14:38 +08:00
    @boris93 如果不在镜像里面编译,是在本地编译好?还是在宿主机编译呢?这个有速度区别嘛
    caicaiwoshishui
        15
    caicaiwoshishui  
    OP
       2020-12-01 18:16:26 +08:00
    @TypeErrorNone 本地编译也要花时间的吧。。 另外如果本地 macos 环境编译,线上 centos 环境,不会存在环境不兼容都问题吗?
    boris93
        16
    boris93  
       2020-12-01 18:37:20 +08:00 via Android
    @caicaiwoshishui #14 都差不多,区别在于,放在外面编译的话,可以直接用已经下载好的 go modules 啥的,要在镜像里面的话,因为里面没有缓存,每次都得重新下载
    ArJun
        17
    ArJun  
       2020-12-01 18:44:58 +08:00
    这样的编译方式第一次见··
    f6x
        18
    f6x  
       2020-12-01 18:52:49 +08:00
    如果是人手打包的话, 当然是本地编译后 COPY 进 alpine 最快

    如果是 ci 的话,当然是 Dockerfile 里编译更佳. 宁可多一层 image 也不要多加一个脚本.
    julyclyde
        19
    julyclyde  
       2020-12-01 19:04:27 +08:00
    @caicaiwoshishui mac 和 centos 不兼容。你也可以看看 multi stage docker
    junnplus
        20
    junnplus  
       2020-12-01 20:29:40 +08:00
    @julyclyde @caicaiwoshishui 本地交叉编译。。。
    Hanggi
        21
    Hanggi  
       2020-12-01 20:34:50 +08:00
    Go 语言构建 docker 只需要 4 行代码,build, from, copy, cmd 结束了。
    guyeu
        22
    guyeu  
       2020-12-01 20:43:24 +08:00
    如果对测试特性没有什么心理障碍的话,可以试试这个

    https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/experimental.md#run---mounttypecache

    可以缓存编译的中间产物
    falcon05
        23
    falcon05  
       2020-12-01 21:14:23 +08:00 via iPhone
    如果只是为了 go 跨平台编译用 docker,根本不需要,go 本身就支持交叉编译。
    tikazyq
        24
    tikazyq  
       2020-12-01 22:10:24 +08:00
    理论上你不清理 docker 的临时镜像重新 build 的时候就会复用安装依赖时的镜像,先将依赖配置拷贝进容器,下载安装完依赖后再拷贝源码,这样就会复用之前的镜像(除非你是更改了依赖配置)。这样 docker 构建就快很多了。

    话说 go 编译速度真的不算慢的,要不你去试试 java (doge)
    shmilyoo
        25
    shmilyoo  
       2020-12-01 22:11:38 +08:00
    相反,我把本地交叉编译改成 在 paas 里面编译了,本地 mac,paas:go-alpine 。 并不觉得慢,换个 goproxy 试试?
    tikazyq
        26
    tikazyq  
       2020-12-01 22:12:15 +08:00
    docker 的多阶段构建可以帮助你去掉没用的源码,最后只保留编译好的二进制文件。

    可以参考: https://github.com/crawlab-team/crawlab/blob/master/Dockerfile
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2834 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 41ms · UTC 06:43 · PVG 14:43 · LAX 22:43 · JFK 01:43
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.