多阶段构建中onbuild命令的使用和避坑

命令简介

语法:

  • ONBUILD {其它普通命令},例如 onbuild copy . .

效果:

  • 在父镜像中作为占位,构建父镜像时不会执行
  • 当依赖此父镜像的子镜像构建时,按顺序执行onbuild后的命令

特点和限制:

  • onbuild后的命令不能为onbuild、from,即不能嵌套
  • 只能在直接子镜像生效,不能传播到孙子镜像

使用场景

通常同类项目的构建部署方式是一致的,每个项目中的Dockerfile也完全相同。这时可以考虑将具体命令集中到一个父镜像,各项目引用父镜像即可。这样可以极大提高可维护性,发生变化时只需要重新构建父镜像,而不需要修改各项目的dockerfile。

举例

  1. 常规方式

    项目1

    # project1
    FROM node:lts-alpine as build-stage
    WORKDIR /app
    COPY package.json /app/
    RUN npm i --registry=https://registry.npm.taobao.org
    COPY . /app/
    RUN npm run build
    
    FROM nginx:stable-alpine as production-stage
    copy --from=build-stage /app/dist /usr/share/nginx/html
    EXPOSE 80
    EXPOSE 443
    CMD ["nginx", "-g", "daemon off;"]
    

    项目2

    # projec2
    FROM node:lts-alpine as build-stage
    WORKDIR /app
    COPY package.json /app/
    RUN npm i --registry=https://registry.npm.taobao.org
    COPY . /app/
    RUN npm run build
    
    FROM nginx:stable-alpine as production-stage
    copy --from=build-stage /app/dist /usr/share/nginx/html
    EXPOSE 80
    EXPOSE 443
    CMD ["nginx", "-g", "daemon off;"]
    

    可以看到两个项目中的Dockerfile完全一样。这时如果需要修改构建流程,只能一个个项目修改,十分不利于维护。于是可以考虑用ONBUILD命令重构。

  2. ONBUILD方式
    在典型项目中,经常会用到多阶段构建。后一阶段会用到前一阶段的内容,而onbuild命令是在子镜像环境下执行。为了避免执行copy --from时找不到指定的阶段,必须在子镜像中包含相同阶段,因此需要按阶段拆分镜像。如下拆分为两个阶段。

    parent-build

    FROM node:lts-alpine as build-stage
    WORKDIR /app
    ONBUILD COPY package.json /app/
    ONBUILD RUN npm i --registry=https://registry.npm.taobao.org
    ONBUILD COPY . /app/
    
    ONBUILD ARG DEPLOY_ENV=Production
    ONBUILD npm run build
    

    parent-run

    FROM nginx:stable-alpine
    ONBUILD COPY --from=build-stage /app/dist /usr/share/nginx/html
    EXPOSE 80
    EXPOSE 443
    CMD ["nginx", "-g", "daemon off;"]
    

    项目1

    from parent-build:latest as build-stage
    from parent-run:latest
    

    项目2

    from parent-build:latest as build-stage
    from parent-run:latest
    

    项目n

    ……
    

    由上可以看到,每个项目的dockerfile简化为只包含两条from指令。这样极大提高可维护性,除非颠覆式改变,此后都不需要做任何调整。

坑与避坑

按上述操作,在创建子镜像时会报错,找不到/app/dist,好像先执行了from parent-run。其实新版docker默认启用了BuildKit,会并行执行from。其对onbuild copy --from支持存在问题(issue),这时我们需要取消使用BuildKit。

  • windows
set DOCKER_BUILDKIT=0
docker build ……
  • linux/unix
DOCKER_BUILDKIT=0 docker build ……

参考资料

https://www.hxstrive.com/article/658.htm

posted @ 2022-01-10 14:44  超软毛毛虫  阅读(439)  评论(0)    收藏  举报