V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Distributions
Ubuntu
Fedora
CentOS
中文资源站
网易开源镜像站
guoguobaba
V2EX  ›  Linux

docker image 分层的问题

  •  
  •   guoguobaba · 342 天前 · 1622 次点击
    这是一个创建于 342 天前的主题,其中的信息可能已经有所发展或是发生改变。

    不知道有人碰到过没有

    就是 gitlab ci 的时候,会 build docker image ,再 push 到 registry 上,我用的是公有云 的容器镜像服务,所以希望每次 push 都能做到尽可能小的增量

    project 是 java springboot 的工程,每次都会生成一个 jar 包,这个 jar 包大概 200m ,如果 用类似 COPY xxxx.jar /app

    这样的 Dockerfile ,每次 jar 这一层就要上传 200m+

    所以采用了 jar 包分层的技术,用 $ java -Djarmode=layertools -jar target/xxx.jar extract 得到四个目录,

    Step 7/11 : COPY dependencies/ /app
     ---> 9831e5b6f9ae
    Step 8/11 : COPY spring-boot-loader/ /app
     ---> 82f47422bade
    Step 9/11 : COPY snapshot-dependencies/ /app
     ---> 24658da80ea8
    Step 10/11 : COPY application/ /app
     ---> 6d47ede4decb
    

    这样每次 ci 的时候,前三个目录基本没有变化,这样生成 docker image 的 layer 是不变的 ,每次 push 只会 push step 10 之后的内容

    这种方法在本地没有问题 mvn clean package... docker build -t ..., docker push ...显示只需要 push 最后一个 layer

    e44c9b0a313a: Pushed
    5732f7831cbe: Layer already exists
    48d1ef8e0017: Layer already exists
    025a15bbce04: Layer already exists
    8460ea9541f4: Layer already exists
    de24004afe49: Layer already exists
    a52fcbff5465: Layer already exists
    767f936afb51: Layer already exists
    

    然后在 gitlab ci 里,显示每次还是要 push 多个 layer

    看了一下日志,每次 gitlab ci 里 build 的时候,

    Status: Downloaded newer image for ccr.ccs.tencentyun.com/.../xx:latest
     ---> 08909e73839f
    Step 2/11 : ENV AppName xxx-system
     ---> Running in b66bf82ba02d
    

    Step1 的 hash 值是一样的,但是 Step 2 的 hash 值就发生变化了,理论上相同的操作应该产 生相同的 hash 值

    10 条回复    2023-09-28 17:49:54 +08:00
    sadfQED2
        1
    sadfQED2  
       342 天前 via Android
    分阶段编译?
    Lax
        2
    Lax  
       342 天前
    "理论上相同的操作应该产 生相同的 hash 值" --- 这句话,是那个文档里说的?
    ziwen1943
        3
    ziwen1943  
       342 天前   ❤️ 1
    分层逻辑失败变成全量 docker 构建的原因主要有两个,第一是编译用基础镜像每次都会重新下载或者变动,那么第一层就变动了,后续也要重新来,第二种就是删除了历史的 image ,导致分层的逻辑没办法命中历史的缓存层。
    结合你上面的情况,我认为是 gitlab-ci 每次编译的时候都启用了一个新环境(可能是 docker 模式的 gitlab-runner )导致编译都是全新编译,但是上传的时候缓存层在 hub 里面存在,然后出现全新打包,缓存上传的局面了。
    ( ps 如果是 shell 模式的 gitlab-runner ,你需要看一下.gitlab-ci.yaml 文件里面是不是加了 docker rm 的命令删除旧的镜像,因为我就是这么干的,为了节约磁盘空间,为了全新打包避免遗漏)
    liantian
        4
    liantian  
       342 天前 via iPhone   ❤️ 1
    别用 docker build…换 buildah 就是啦…

    buildah 一次 build 是一层,多余文件清理掉就好。
    iBugOne
        5
    iBugOne  
       342 天前 via Android   ❤️ 1
    docker build --cache-from 拉下来的镜像

    docker 默认不会复用拉下来的镜像里的层,所以每次都等于全新 build ,需要用 cache-from 钦点 docker 去尽量复用已有的层
    newaccount
        6
    newaccount  
       342 天前
    baseimage 不要使用 latest ,固定版本试一下呢?
    xiaobai1213
        7
    xiaobai1213  
       342 天前
    看一下 gitlab-ci 使用的 runner 模式,有的 runner 模式是不会存储之前的工作空间的,这样就会无法命中缓存层,导致每一次都是全量构建

    "理论上相同的操作应该产 生相同的 hash 值" 这个是在官方文档里面看到的吗? 之前有试验过 同样命令同一份代码同一个 maven 打出来的两个 jar 最后算出来的 sha1 都是不一样的......
    Masoud2023
        8
    Masoud2023  
       342 天前
    你 Gitlab 里的 docker 根本没配置缓存吧?

    不过看这帖子也学习到了,原来 jar 包还能这么拆开。。
    guoguobaba
        9
    guoguobaba  
    OP
       342 天前
    @iBugOne best solution
    每次 gitlab runner 起的是一个新环境,所以 ci 脚本增加下面的就完美解决了
    ```
    - docker pull $IMAGE || echo "no image"
    - docker build --cache-from $IMAGE -t $IMAGE .
    ```

    看了一下 job 的运行时间,从 109s 下降到 29s

    特别是我可以开启我之前购买的屮蛋的天翼云主机做 runner 服务器了,他只有 1m 的带宽,用完成 jar 包的 image 上传要 25 分钟以上,现在每次 ci ,只需要 2-3 分钟了。
    guoguobaba
        10
    guoguobaba  
    OP
       266 天前
    汇报一下,这个问题主要是 gitlab ci 是 docker:dind 对应的/var/lib/docker 目录都从
    内存中生成导致每次都需要重新下载 image
    所以设置一个 pvc ,缓存/var/lib/cache 就可以了
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1928 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 16:25 · PVG 00:25 · LAX 09:25 · JFK 12:25
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.