Rust:用多阶段构建方式优化Docker镜像大小

使用 Docker 部署应用程序时,则需要遵循一些最佳实践。

每条指令Dockerfile大致翻译为一个层:

RUN apt-get update 
RUN apt-get install -y python3 python3-pip curl git
RUN curl -sSL https://install.python-poetry.org | python3 -
RUN curl https://pyenv.run | bash

每条RUN指令都会创建一个新层。

优化目标是降低层数:

RUN apt-get update \
    && apt-get install -y python3 python3-pip curl git \
    && curl -sSL https://install.python-poetry.org | python3 - \
    && curl https://pyenv.run | bash

这样,层数就减少到一层。

Docker在 Docker 17.06 CE 中引入了多阶段构建。此功能可以帮助在容器化应用程序时优化 Docker 映像。

多阶段构建可以减少构建后文件大小。

如何实现多阶段构建?
将构建过程分为两个阶段:

  • 在第一阶段,构建应用程序并获取二进制文件。除了二进制文件外,不需要生成依赖关系和任何其他文件。
  • 在第二阶段,将构建最终镜像,二进制文件从第一阶段复制,是唯一包含的文件。

这就是多阶段编译的工作原理。每个 FROM 指令都可以使用不同的基础镜像。

FROM rust:latest AS builder
WORKDIR /app

COPY Cargo.toml .
RUN mkdir src && echo "fn main() {}" > src/main.rs
RUN cargo build --release

COPY src src
RUN touch src/main.rs
RUN cargo build --release

RUN strip target/release/hello_rocket

FROM alpine:latest as release
WORKDIR /app
COPY --from=builder /app/target/release/hello_rocket .

ENV ROCKET_ADDRESS=0.0.0.0
ENV ROCKET_PORT=8000
EXPOSE 8000

CMD ["./hello_rocket"]

在第一阶段:

  • 以 rust:latest 镜像为基础,将阶段命名为 builder
  • 工作目录设置为 /app
  • 复制清单(Cargo.toml)
  • 创建临时 src 目录和 main.rs 文件
  • 启动构建进程,生成依赖项缓存
  • 复制应用程序代码
  • 将 main.rs 文件的访问和修改时间戳更新为当前时间
  • 构建应用程序
  • 删除二进制文件中不必要的信息,从而减小文件大小,并增加逆向工程的难度

在第二阶段:

  • 以 alpine:latest 镜像为基础,将阶段命名为 release
  • 工作目录设置为 /app
  • 从第一阶段复制二进制文件
  • 设置 ROCKET_ADDRESS 环境变量
  • 设置 ROCKET_PORT 环境变量
  • 暴露主机中的 8000 端口
  • 指定容器启动时要运行的命令

上述多阶段构建可将镜像文件从2GB减小到11.2MB。这就是通过多阶段构建来优化 Rust 容器化应用程序的方法,并且此功能可以与任何编译的编程语言一起使用。