V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
wellhome
V2EX  ›  程序员

用 doker 发布程序的正确打开方式

  •  1
     
  •   wellhome · 2021-02-28 13:02:43 +08:00 via iPhone · 4347 次点击
    这是一个创建于 1146 天前的主题,其中的信息可能已经有所发展或是发生改变。
    下面两种,哪个正确。

    1. 每次发布用 DockerFile 打包一份新 image
    然后把 image tag latest 发布

    2. 在上次打包的 image 基础上 运行 docker commit,产生一个最新 image 。

    目前我用方法 1 。每个 image 都是独立的 commit,之间是没父子关系。
    我的疑惑是,docker commit 这个机制是不是可以淘汰了。
    30 条回复    2021-03-02 03:12:11 +08:00
    jdhao
        1
    jdhao  
       2021-02-28 13:08:15 +08:00 via Android   ❤️ 2
    当然是 dockerfile 的方式,便于知道里面到底装了啥,做了什么操作,docker commit 到最后变成黑箱,你都不知道里面有啥。
    去如果只追求能跑就行,用哪种方式都随便
    mimzy
        2
    mimzy  
       2021-02-28 13:09:20 +08:00   ❤️ 1
    Docker 的机制是可以复用之前的层,加快 build 速度减少体积占用,每次都 docker commit 相当于都没法复用了,所以 1 正确。
    boris93
        3
    boris93  
       2021-02-28 13:42:35 +08:00 via iPhone
    1 正确

    你要想让镜像之间有父子关系的话,后面构建就 FROM 上一版本镜像,虽然不知道有什么意义
    muzuiget
        4
    muzuiget  
       2021-02-28 13:44:32 +08:00
    我一向用 1,docker 本来就是可以自动复用之前的层的,但是把改动都写到 dockerfile 了清晰好多。
    mimzy
        5
    mimzy  
       2021-02-28 13:52:59 +08:00
    @mimzy #2 我这里之前理解得不对,commit 也可以复用,问题在于这个复用会在上一层原封不动的基础上修改,镜像会越来越臃肿…
    dangyuluo
        6
    dangyuluo  
       2021-02-28 14:38:04 +08:00
    第一步:正确拼写 docker
    dangyuluo
        7
    dangyuluo  
       2021-02-28 14:39:17 +08:00   ❤️ 1
    在正确掌握拼写的基础上,我们公司使用的是第一种策略
    jim9606
        8
    jim9606  
       2021-02-28 15:05:43 +08:00
    2 只适合增量的情况(同版本增加插件和可选依赖),如果是替换(更新依赖、更新程序本体)应该用 1,因为多层 commit 会保留中间层中被删除的部分,空占体积。

    虽然直接发布 dockerfile 才是最高效的方式,不过考虑到网络情况很多人不喜欢用。

    2 还应该善用多阶段构建来节省最终镜像体积,不要把工具链和中间产物留在镜像里。
    fiveelementgid
        9
    fiveelementgid  
       2021-02-28 15:44:30 +08:00 via Android
    Docker 写 Dockerfile 叠千层饼比较好(云 docker 玩家路过
    xuanbg
        10
    xuanbg  
       2021-02-28 15:52:05 +08:00
    1
    wellhome
        11
    wellhome  
    OP
       2021-02-28 16:01:43 +08:00 via iPhone
    感谢诸位老哥的回答。个人感觉 docker 本身的 commit 的使用场景不多。快速打个系统的补丁。如果是程序发布了话,还是需要每次都是一个干净的 image 。
    wellhome
        12
    wellhome  
    OP
       2021-02-28 16:02:15 +08:00 via iPhone
    @dangyuluo 批评的对
    konakona
        13
    konakona  
       2021-02-28 17:05:33 +08:00
    肯定不會是 2,你會後悔的。
    正確答案是 1 。
    JamesMackerel
        14
    JamesMackerel  
       2021-02-28 17:14:11 +08:00   ❤️ 2
    @wellhome

    我觉得楼主的疑问在于,既然我每次发布的时候都不从上一个版本的 docker image commit,那这个命令到底有什么用?

    其实在看这个命令有什么用的时候,不应该只从自己面前遇到的这个场景来看。docker commit 的这个设计的目的是增加存储层的复用程度。如果是 docker image 的终端用户(意思是你作出的镜像不会再被别人 FROM 或者 commit 新的东西)可能并没有什么感受,但想象以下的场景:

    A 制作了一个最简单最干净的 Ubuntu 镜像
    - B 基于上述镜像做了一个 Python runtime 镜像,只要基于该镜像起一个容器就能直接跑 python 脚本
    - - C 基于 B 制作了一个 uwsgi 镜像,只要把 wsgi 脚本放进去就能起一个服务
    - - D 基于 B 制作了一个安装了一些数据分析软件包的镜像,使用者只要起一个容器就能直接在 Ipython 中进行简单的数据分析操作

    这样其实 BCD 都复用了 A,CD 复用了 B 。试想如果没有这个 commit 的功能,这五个镜像就是五份完全独立的文件,由于 BCD 的改动并不多,其实相当于用 4*A 的大小,但是实际上只做了一点(真正)微小的工作。而基于 commit 机制的这些镜像在都保存在 docker registry 时,应该只占用比 A 的大小稍大一点的空间。

    这种机制对于社区来说是非常有益的,因为它提供给了镜像制作者一种非常廉价的复用他人已有工作的方式,同时节省了许多存储空间。
    Lemeng
        15
    Lemeng  
       2021-02-28 17:16:53 +08:00
    我说呢,docker
    fannas
        16
    fannas  
       2021-02-28 17:25:38 +08:00 via iPhone   ❤️ 1
    个人经验:
    正确姿势是第一个,第二层是运行时,第三层 dependency,第四层 config,第五层代码。
    jvm 例子:Base+jdk+所有 dependency+logconfig+class code
    python 例子:Base + python+dependency (不要远程安装)+ config + code
    fannas
        17
    fannas  
       2021-02-28 17:26:21 +08:00 via iPhone
    哦,java 最简单的方式是 jib
    wellhome
        18
    wellhome  
    OP
       2021-02-28 21:06:55 +08:00 via iPhone
    @JamesMackerel 受教了。👍
    wellhome
        19
    wellhome  
    OP
       2021-02-28 21:08:32 +08:00 via iPhone
    @fannas 👍。config 为什么不在 code 后面
    fannas
        20
    fannas  
       2021-02-28 21:11:53 +08:00 via iPhone
    @wellhome 按照修改频率升序排列,只是个人经验哈。因为这样可以更高效的运用已有的层,在建立 image 的时候更好地利用缓存,publish 的时候节省更多带宽与空间。
    wellhome
        21
    wellhome  
    OP
       2021-02-28 22:21:13 +08:00 via iPhone
    @fannas 1 2 3 层 每次在发布 code 的时候 ,都用 DockerFile 从头打包?
    jinliming2
        22
    jinliming2  
       2021-02-28 23:30:37 +08:00
    @fannas 配置 config 不应该设置为运行时从环境变量里读吗?或者设置为 mount point 在运行时指定导入进去?
    chenqh
        23
    chenqh  
       2021-02-28 23:35:22 +08:00
    有一个问题,docker 怎么用起来比较好,单机 docker registry 有点浪费呀
    jinliming2
        24
    jinliming2  
       2021-02-28 23:40:55 +08:00
    @wellhome Dockerfile 只有第一次会从头打包,后续发布更新都不会从头打包,只会从变化的位置开始打包,之前没有变化的 Dockerfile 语句都会复用。比如:

    FROM xxxxxx # 这一句每次都一样,直接复用
    RUN apt-get update && apt install -y xxx # 这一句也是每次都一样,直接复用
    COPY . . # 这一句开始,因为拷入的代码文件变了,所以从这一行开始后面的全部重新构建
    RUN build ... # 从上一句开始从新构建

    所以一般来说,安装依赖之类的语句尽可能提前,这样后续打包的时候就不会重新构建了。

    实际上你可以简单理解成你的 Dockerfile 里每一条语句都是一个 commit (不正确,部分语句是会合并的),相互之间就是父子关系。具体表现在你 docker pull / push 的时候显示的那个进度条的个数。你每次构建的时候,如果这条语句前面的环境都是一样的话,并且当前这一层没有任何变化的话,那么这一条语句就不会重新执行,而是直接复用之前的构建结果。这样一来你会发现,在重新 docker push 的时候,会提示你部分层已经在服务器上存在了,只会 push 变动的部分。
    wellhome
        25
    wellhome  
    OP
       2021-03-01 08:16:09 +08:00
    @jinliming2 "RUN apt-get update && apt install -y xxx # 这一句也是每次都一样,直接复用", 复用的前提是你本地已经有 cache, 我们的的 ci/cd 的 agent 是随机分配的。如果进行发布的 agent 上以前没有打过包,也就是说没有 cache, 那么应该还是从头开始一层一层打, 没有复用。
    SmiteChow
        26
    SmiteChow  
       2021-03-01 10:23:28 +08:00
    docker commit 用于复用他人的镜像场景。当你不知道或不关心镜像 Dockerfile 怎么写的,但你又想复用镜像的时候,commit 能快速生成包含你修改的新镜像。
    dier
        27
    dier  
       2021-03-01 13:45:59 +08:00
    基于你们项目运行的环境打一个基础镜像,例如(系统--软件--环境变量),然后每次发新代码时,在 Dockerfile 中 FROM 这个基础镜像来构建一个最新代码的镜像,发布的时候就发布最新的镜像。有基础镜像就可以提交镜像构建的速度,不需要次都去安装各种软件,特别是每次的 agent 不在同一台机器上没法利用之前的 cache 的环境。
    ku360517703
        28
    ku360517703  
       2021-03-01 17:40:30 +08:00
    方案 1 和打包镜像然后-v 挂载 jar 包进去运行,应该选哪种呢?
    fannas
        29
    fannas  
       2021-03-02 03:05:30 +08:00 via iPhone
    @wellhome 取决于实际情况。对于基础开发,例如 jvm python 我通常情况下使用 jre base 和 python base,在这个上加 dependency config 和 code 。没必要自己建立。如果有特殊需要就需要从头搞了。只是个人情况哈。
    fannas
        30
    fannas  
       2021-03-02 03:12:11 +08:00 via iPhone
    @jinliming2 config 可能用的不准确。在 jvm 软件开发的时候,resource 里面的文件通常是静态的,不进行修改的。如果这是一个离线文档 parser 或者是个生产上的预测模型,可能存在该文件夹大小几十兆甚至更大。
    这种情况下先将其放入 image,最大化的减小构建 image 的大小,有利于快速分发部署。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1030 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 31ms · UTC 19:13 · PVG 03:13 · LAX 12:13 · JFK 15:13
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.