多阶段构建中onbuild命令的使用和避坑
命令简介
语法:
- ONBUILD {其它普通命令},例如 onbuild copy . .
效果:
- 在父镜像中作为占位,构建父镜像时不会执行
- 当依赖此父镜像的子镜像构建时,按顺序执行onbuild后的命令
特点和限制:
- onbuild后的命令不能为onbuild、from,即不能嵌套
- 只能在直接子镜像生效,不能传播到孙子镜像
使用场景
通常同类项目的构建部署方式是一致的,每个项目中的Dockerfile也完全相同。这时可以考虑将具体命令集中到一个父镜像,各项目引用父镜像即可。这样可以极大提高可维护性,发生变化时只需要重新构建父镜像,而不需要修改各项目的dockerfile。
举例
-
常规方式
项目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命令重构。
-
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 buildparent-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 ……
浙公网安备 33010602011771号