对静态链接的 Rust 二进制文件使用多阶段 Docker 构建
我正在用Rust开发一个静态网站。上次做类似的事情时,我用Docker来自动化部署。虽然每次都要迁移这些庞大的构建镜像,占用了大量带宽,让我很沮丧,但 Docker 的便利性实在难以抗拒,而且我也不经常重新构建镜像,所以就一直用着它。
采用这种新方法,我最终得到的整个应用程序的生产环境 Docker 镜像大小为 6.85MB。我可以接受这个大小。
我使用Askama进行模板生成,它会将类型检查后的模板编译成二进制文件。我的图像资源都是SVG 格式,本质上是XML,所以我可以将include_str!()它们以及诸如 `<div>`manifest.json和robots.txt`<span>` 之类的元素和所有 CSS 都直接编译到二进制文件中&'static str。因此,我不需要完整的 Rust 构建环境,甚至不需要任何资源文件就能运行编译后的输出。
这次我做了功课,找到了@alexbrand的这篇博文,其中演示了这项技术。与其直接将所有构建依赖项打包在一起,不如使用多阶段构建,先生成编译后的输出,然后再将其复制到一个最小化的容器中进行分发。以下是我针对此项目所做的调整:
# Build Stage
FROM rust:1.40.0 AS builder
WORKDIR /usr/src/
RUN rustup target add x86_64-unknown-linux-musl
RUN USER=root cargo new deciduously-com
WORKDIR /usr/src/deciduously-com
COPY Cargo.toml Cargo.lock ./
RUN cargo build --release
COPY src ./src
COPY templates ./templates
RUN cargo install --target x86_64-unknown-linux-musl --path .
# Bundle Stage
FROM scratch
COPY --from=builder /usr/local/cargo/bin/deciduously-com .
USER 1000
CMD ["./deciduously-com", "-a", "0.0.0.0", "-p", "8080"]
就是这样!顶部标有“base image”的部分builder使用了rust:1.40.0基础镜像,其中包含了用 Rust 构建二进制文件所需的一切。它的目标平台是 [此处应填写目标平台名称] x86_64-unknown-linux-musl。musl库是为静态链接而非动态链接而设计的替代方案。Rust 对 musl的支持非常出色(显然如此)。这意味着生成的二进制文件是完全独立的——它没有任何环境依赖。libc
第二部分定义了实际的发行版,它直接从 `docker-compose up` 开始scratch,甚至没有alpine使用我通常会用到的其他最小 Docker 基础镜像。你可以使用 ` COPY --from=builderdocker-compose up` 来引用之前的 Docker 阶段。这个 Docker 镜像里什么都没有。这意味着我的镜像实际上只包含我的二进制文件,没有 Linux 用户空间!所有这一切都只需一次 `docker-compose up` 调用即可完成docker build。
中间部分cargo new会创建一个虚拟应用程序,利用 Docker 缓存来管理依赖项。这意味着在开发过程中,后续运行 `docker build` 时docker build无需每次都重新构建 Rust 应用程序中的每个依赖项,而只会重新构建已更改的部分,就像在本地构建一样。太棒了!
我正在使用 DigitalOcean 的一键 Docker应用进行部署,这是一个预装了 Docker 并预设了一些UFW设置的 Ubuntu LTS 镜像。以下是我的整个部署过程:
$ docker build -t deciduously-com .
$ docker tag SOMETAG83979287 deciduously0/deciduously-com:latest
$ docker push deciduously0/deciduously-com:latest
$ ssh root@SOME.IP.ADDR
root@SOME.IP.ADDR# docker pull deciduously0/deciduously-com:latest
root@SOME.IP.ADDR# docker run -dit -p 80:8080 deciduously0/deciduously-com:latest
root@SOME.IP.ADDR# exit
$
远程服务器下载了我那只有 6.85MB 的镜像并启动了它。我立刻就连接上了。这个小镜像占用的磁盘空间、内存和 CPU 资源都非常少,所以我可以最大限度地利用我每月 5 美元的 DigitalOcean 最低档服务器资源。回想起之前用 Clojure 做类似事情的惨痛经历,真是令人不寒而栗……
添加一些脚本,这样你就不必记住那些命令了,我的整个构建和部署过程就简化为几次击键。
我为什么要用别的呢?
各位注意了,我已经放弃Stencil,转而使用Askama / Hyper。一天之内,我就用大约一半的代码量和极小的软件包体积重新实现了之前的所有工作。没错,我正在酝酿一篇更长的文章(以及一个 GitHub 模板)来详细介绍这件事,而且,我一点也不后悔。KISS原则嘛……
照片由 Richard Sagredo 拍摄,来自 Unsplash
文章来源:https://dev.to/decidously/use-multi-stage-docker-builds-for-statically-linked-rust-binaries-3jgd