chef可以让依赖的下载与编译与源码解耦。只要源码的依赖不变,就不会重新构建依赖层。
下面的例子是一个workspace的Dockerfile,所有crate都写在crates
文件夹里面。构建的时候可以传入不同的APP_NAME
,这样就可以重复利用该Dockerfile,来构建多个app了。
由于chef prepare
时的状态只和源码有关,因此每次源码改动后就会重新计算依赖树,但代价很小;依赖信息被记录为json,然后该json被转移到没有源码的新镜像里面,这样的话即使源码改变了,只要依赖不变,那么chef cook
时的状态就不变。也就是说,只要依赖不变,一直到chef cook
后都是会被直接跳过的。这样就不需要重复下载和编译依赖了。
也要注意Debian版本要对应上。这里统一使用了最新的bookworm版本,否则一些东西(包括chef)可能编译不通过。编译用rust:1.75.0-bookworm
,运行用debian:bookworm-slim
。Debian版本对不上的话复杂的代码可能会出现环境动态库版本过低的问题。
# cargo with source replacement
# Comment the `RUN` if you are not in China.
FROM rust:1.75.0-bookworm as source-replaced-cargo
RUN mkdir -p /usr/local/cargo/registry \
&& echo '[source.crates-io]' > /usr/local/cargo/config.toml \
&& echo 'replace-with = "rsproxy"' >> /usr/local/cargo/config.toml \
&& echo '[source.rsproxy]' >> /usr/local/cargo/config.toml \
&& echo 'registry = "https://rsproxy.cn/crates.io-index"' >> /usr/local/cargo/config.toml
# cargo-chef
FROM source-replaced-cargo as chef
RUN cargo install cargo-chef
# Computes the recipe file
FROM chef as planner
WORKDIR /app
COPY . .
RUN cargo chef prepare --recipe-path recipe.json
# Build app
FROM chef as builder
WORKDIR /app
COPY --from=planner /app/recipe.json recipe.json
# Build dependencies - this is the caching Docker layer.
# No source code here, so as long as the dependency remains unchanged, it will not be rebuilt.
RUN cargo chef cook --release --recipe-path recipe.json
ARG APP_NAME
COPY . .
# Build
RUN cd ./crates/${APP_NAME} \
&& cargo build --release
# Run app
FROM debian:bookworm-slim
ARG APP_NAME
COPY --from=builder /app/target/release/${APP_NAME} /usr/local/bin/${APP_NAME}
ENV APP_NAME_ENV=${APP_NAME}
CMD ["sh", "-c", "/usr/local/bin/$APP_NAME_ENV"]
附上项目用的Makefile:
# image name & version
WRAPPER_NAME = ipfs_storage_cruster/ipfs_node_wrapper
MANAGER_NAME = ipfs_storage_cruster/ipfs_storage_cruster_manager
VERSION = latest
.PHONY: build-wrapper build-manager build-all
# build docker image of ipfs_node_wrapper_app
build-wrapper:
docker build --build-arg APP_NAME=ipfs_node_wrapper_app -t $(WRAPPER_NAME):$(VERSION) .
# build docker image of ipfs_storage_cruster_app
build-manager:
docker build --build-arg APP_NAME=ipfs_storage_cruster_app -t $(MANAGER_NAME):$(VERSION) .
build-all: build-wrapper build-manager