Docker 多阶段构建

偷懒是人类进步的阶梯

今天看到一个概念,不是新的,但之前却很奇怪地完全没有接触过。

Docker multi-stage builds

简单来说,就是 Dockerfile 支持多次 build,然后把上一次 build 产生的文件直接 COPY 到下一次的 build 过程中。

相当于第二次的 build 里只执行了一次 COPY,省去中间很多下载、安装、编译的过程。

优点

  • 把 build + run 过程分离,让 run 只负责 run;
  • 大幅度减少 run 镜像的层数,从而压缩 run 环境的分发和更新时间;

缺点

  • 对于动态语言来说(比如 SRE 工程师最爱的 Python),依赖环境较难处理,需要用额外的打包工具;
  • 打包后镜像只有 run 环境,出问题的时候不方便进行 debug;

从上面的优缺点可以看到,对静态语言,尤其是最后可以打包成二进制的文件来说,多阶段构建的好处是显而易见的,对动态语言来说,就需要各种尝试,确认打包后程序的稳定性。

例子

这里摘抄两个在网上看到的 python 的例子:

使用 wheel

FROM python:2.7-alpine as base
RUN mkdir /svc
COPY . /svc
WORKDIR /svc
RUN apk add --update \
    postgresql-dev \
    gcc \
    musl-dev \
    linux-headers
RUN pip install wheel && pip wheel . --wheel-dir=/svc/wheels

FROM python:2.7-alpine
COPY --from=base /svc /svc
WORKDIR /svc
RUN pip install --no-index --find-links=/svc/wheels -r requirements.txt

使用 pyinstaller

FROM python:3.6 as build-env
WORKDIR /app/
ADD . /app/
ADD https://github.com/pyinstaller/pyinstaller/archive/develop.zip /app/pyinstaller-develop.zip
RUN pip install -r requirements.txt && \
    pip install pyinstaller-develop.zip
RUN pyinstaller --hidden-import=tornado --onefile -d -y run.py
 
FROM frolvlad/alpine-glibc
WORKDIR /app/
COPY --from=build-env /app/dist/run .
ENTRYPOINT ["./run"]