构建最小的 Docker 镜像来部署 Rust 带来了很多好处:它不仅有利于安全性(减少攻击面),而且可以缩短部署时间、降低成本(减少带宽和存储),并降低依赖冲突的风险。
我们的 "应用程序 "相当简单:我们将建立一个命令行工具,调用https://api.myip.com,并打印出结果。
进行HTTPS调用很有意思,因为它需要一个与TLS交互的库,通常是openssl。但为了建立尽可能小的Docker镜像,我们需要静态链接我们的程序,而静态链接openssl并不那么容易。这就是为什么我们要避免使用openssl,而使用rustls。
Cargo.toml:
[package] name = "myip" version = "0.1.0" edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] serde = { version = "1", features = ["derive"] } reqwest = { version = "0.11", default-features = false, features = ["json", "rustls-tls", "blocking"] }
[target.'cfg(all(target_env = "musl", target_pointer_width = "64"))'.dependencies.jemallocator] version = "0.3"
|
main.rs:
use serde::Deserialize; use std::error::Error;
// Use Jemalloc only for musl-64 bits platforms #[cfg(all(target_env = "musl", target_pointer_width = "64"))] #[global_allocator] static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
#[derive(Deserialize, Debug)] struct ApiRes { ip: String, }
fn main() -> Result<(), Box<dyn Error>> { let res = reqwest::blocking::get("https://api.myip.com")?.json::<ApiRes>()?;
println!("{}", res.ip);
Ok(()) }
|
$ cargo run Running `target/debug/myip` 127.0.0.1
|
FROM scratch
Size: 15.9 MB
为了使用FROM scratch作为基础镜像,我们必须将我们的程序静态链接到musl libc,因为glibc在scratch中是不可用的。这可以通过使用x86_64-unknown-linux-musl目标来实现。
这种方法的一个问题是,musl的内存分配器没有进行速度优化,可能会降低你的应用程序的性能,特别是在处理高吞吐量的应用程序时。
这就是为什么我们使用 jemalloc,一个为高并发应用设计的内存分配器。
Dockerfile.scratch
#################################################################################################### ## Builder #################################################################################################### FROM rust:latest AS builder
RUN rustup target add x86_64-unknown-linux-musl RUN apt update && apt install -y musl-tools musl-dev RUN update-ca-certificates
# Create appuser ENV USER=myip ENV UID=10001
RUN adduser \ --disabled-password \ --gecos "" \ --home "/nonexistent" \ --shell "/sbin/nologin" \ --no-create-home \ --uid "${UID}" \ "${USER}"
WORKDIR /myip
COPY ./ .
RUN cargo build --target x86_64-unknown-linux-musl --release
#################################################################################################### ## Final image #################################################################################################### FROM scratch
# Import from builder. COPY --from=builder /etc/passwd /etc/passwd COPY --from=builder /etc/group /etc/group
WORKDIR /myip
# Copy our build COPY --from=builder /myip/target/x86_64-unknown-linux-musl/release/myip ./
# Use an unprivileged user. USER myip:myip
CMD ["/myip/myip"]
|
运行:
$ docker build -t myip:scratch -f Dockerfile.scratch . # ... $ docker run -ti --rm myip:scratch 127.0.0.1
|
FROM alpine
Size: 21.6MB
Alpine Linux是一个以安全为导向,基于musl libc和busybox的轻量级Linux发行版。
当FROM scratch不够用,而你需要一个软件包管理器来安装chromium或ssh等依赖项时,就应该使用它。
由于它是基于musl libc的,因此具有与FROM scratch相同的限制,我们需要使用x86_64-unknown-linux-musl静态链接我们的Rust程序。
Dockerfile.alpine:
#################################################################################################### ## Builder #################################################################################################### FROM rust:latest AS builder
RUN rustup target add x86_64-unknown-linux-musl RUN apt update && apt install -y musl-tools musl-dev RUN update-ca-certificates
# Create appuser ENV USER=myip ENV UID=10001
RUN adduser \ --disabled-password \ --gecos "" \ --home "/nonexistent" \ --shell "/sbin/nologin" \ --no-create-home \ --uid "${UID}" \ "${USER}"
WORKDIR /myip
COPY ./ .
RUN cargo build --target x86_64-unknown-linux-musl --release
#################################################################################################### ## Final image #################################################################################################### FROM alpine
# Import from builder. COPY --from=builder /etc/passwd /etc/passwd COPY --from=builder /etc/group /etc/group
WORKDIR /myip
# Copy our build COPY --from=builder /myip/target/x86_64-unknown-linux-musl/release/myip ./
# Use an unprivileged user. USER myip:myip
CMD ["/myip/myip"]
|
运行:
$ docker build -t myip:alpine -f Dockerfile.alpine . # ... $ docker run -ti --rm myip:alpine 127.0.0.1
|
更多:你可以在 GitHub 上找到代码:github.com/skerkour/kerkour.com