Skip to content

Commit

Permalink
Docker improvements (#86)
Browse files Browse the repository at this point in the history
* Don't include node_modules in Docker context

* Add env var to provide precompiled webapp assets

* Do npm build in separate Dockerfile stage

* Use cargo chef in Dockerfile

* Use sparse registry protocol in Dockerfile

* Use docker buildx caching in CI
  • Loading branch information
divergentdave committed May 12, 2023
1 parent 18ebe35 commit e6a4542
Show file tree
Hide file tree
Showing 7 changed files with 68 additions and 29 deletions.
2 changes: 2 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
target
*/node_modules
23 changes: 17 additions & 6 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,20 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: echo "GIT_REVISION=$(git describe --always --dirty=-modified)" >> $GITHUB_ENV
- run: |-
docker build \
--build-arg GIT_REVISION=${GIT_REVISION} \
--build-arg RUST_FEATURES=${{ matrix.rust-features }} \
.
- id: git
run: echo "GIT_REVISION=$(git describe --always --dirty=-modified)" >> $GITHUB_OUTPUT
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
with:
driver: docker-container
use: true
- name: Build
uses: docker/build-push-action@v4
with:
context: .
push: false
cache-from: type=gha,scope=${{ github.ref_name }}-${{ matrix.rust-features }}
cache-to: type=gha,scope=${{ github.ref_name }}-${{ matrix.rust-features }},mode=max
build-args: |
GIT_REVISION=${{ steps.git.outputs.GIT_REVISION }}
RUST_FEATURES=${{ matrix.rust-features }}
44 changes: 29 additions & 15 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,29 +1,43 @@
FROM rust:1.69.0-alpine as builder
FROM node:alpine as assets
WORKDIR /src/app
COPY app/package.json /src/app/package.json
COPY app/package-lock.json /src/app/package-lock.json
RUN npm ci
COPY app /src/app
RUN npm ci
RUN npm run build

FROM rust:1.69.0-alpine as chef
RUN apk add libc-dev
RUN apk add --update npm
ENV CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse
RUN cargo install cargo-chef
WORKDIR /src
COPY app /src/app

FROM chef AS planner
COPY Cargo.toml /src/Cargo.toml
COPY Cargo.lock /src/Cargo.lock
COPY build.rs /src/build.rs
COPY migration /src/migration
COPY src /src/src
RUN cargo chef prepare --recipe-path recipe.json

FROM chef as builder
COPY --from=planner /src/recipe.json /src/recipe.json
RUN cargo chef cook --workspace --release --recipe-path recipe.json
COPY Cargo.toml /src/Cargo.toml
COPY Cargo.lock /src/Cargo.lock
COPY build.rs /src/build.rs
COPY migration /src/migration
COPY src /src/src
RUN cd app && npm ci
COPY --from=assets /src/app/build /src/app/build
ARG RUST_FEATURES=default
RUN --mount=type=cache,target=/usr/local/cargo/registry \
--mount=type=cache,target=/src/target \
cargo build --profile release -p migration && \
cp /src/target/release/migration /migration
RUN --mount=type=cache,target=/usr/local/cargo/registry \
--mount=type=cache,target=/src/target \
cargo build --profile release --features ${RUST_FEATURES} && \
cp /src/target/release/divviup_api_bin /divviup_api_bin
RUN ASSET_DIR=/src/app/build cargo build --workspace --release --features ${RUST_FEATURES}

FROM alpine:3.17.3
FROM alpine:3.17.3 AS final
ARG GIT_REVISION=unknown
LABEL revision ${GIT_REVISION}
EXPOSE 8080
ENV HOST=0.0.0.0
COPY --from=builder /migration /migration
COPY --from=builder /divviup_api_bin /divviup_api_bin
COPY --from=builder /src/target/release/migration /migration
COPY --from=builder /src/target/release/divviup_api_bin /divviup_api_bin
ENTRYPOINT ["/divviup_api_bin"]
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ An example `.envrc` is provided for optional but recommended use with [`direnv`]
* `OTEL_EXPORTER_PROMETHEUS_HOST` -- default `"localhost"`
* `OTEL_EXPORTER_PROMETHEUS_PORT` -- default `9464`
* `SKIP_APP_COMPILATION` -- we currently build the react app in a build script. To avoid this behavior, set this environment variable.
* `ASSET_DIR` -- set this to skip building the react app and include react app assets from a different directory

## Initial setup

Expand Down
15 changes: 13 additions & 2 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,18 @@ use std::process::Command;
use rustc_version::version;

fn main() {
if std::env::var("SKIP_APP_COMPILATION").is_err() {
let out_dir = std::env::var("OUT_DIR").unwrap();
if std::env::var("ASSET_DIR").is_ok() {
// Don't run npm, and let the ASSET_DIR value pass through to the static asset handler.
} else if std::env::var("SKIP_APP_COMPILATION").is_ok() {
// Skip asset compilation, but reuse stale assets in the OUT_DIR directory.
println!("cargo:rustc-env=ASSET_DIR={out_dir}");
} else {
// Build the react app.
let status = Command::new("npm")
.current_dir("app")
.args(["run", "build"])
.env("BUILD_PATH", std::env::var("OUT_DIR").unwrap())
.env("BUILD_PATH", &out_dir)
.status()
.unwrap();

Expand All @@ -18,8 +25,12 @@ fn main() {
println!("cargo:rerun-if-changed=app/package-lock.json");
println!("cargo:rerun-if-changed=app/public");
println!("cargo:rerun-if-env-changed=API_URL");

// Point the static asset handler at the build script output directory.
println!("cargo:rustc-env=ASSET_DIR={out_dir}");
}

println!("cargo:rerun-if-env-changed=ASSET_DIR");
println!("cargo:rerun-if-env-changed=SKIP_APP_COMPILATION");

let rustc_semver = version().expect("could not parse rustc version");
Expand Down
2 changes: 1 addition & 1 deletion src/handler/assets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const ONE_YEAR: Duration = Duration::from_secs(60 * 60 * 24 * 365);

pub fn static_assets(config: &ApiConfig) -> impl Handler {
ReactApp {
handler: static_compiled!("$OUT_DIR").with_index_file("index.html"),
handler: static_compiled!("$ASSET_DIR").with_index_file("index.html"),
api_url: config.api_url.clone(),
}
}
Expand Down
10 changes: 5 additions & 5 deletions src/handler/origin_router.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,20 @@ impl OriginRouter {
.map(|boxed_handler| &**boxed_handler)
}

/// Construct a new OriginRouter
/// Construct a new OriginRouter.
pub fn new() -> Self {
Self::default()
}

/// add a handler to this origin router at the specified exact origin, returning self
/// see also [`add_handler`]
/// Add a handler to this origin router at the specified exact origin, returning self.
/// See also [`add_handler`].
pub fn with_handler(mut self, origin: &str, handler: impl Handler) -> Self {
self.add_handler(origin, handler);
self
}

/// add a handler to this origin router at the specified exect origin.
/// see also [`with_handler`] for chainability
/// Add a handler to this origin router at the specified exact origin.
/// See also [`with_handler`] for chainability.
pub fn add_handler(&mut self, origin: &str, handler: impl Handler) {
self.map.insert(
origin.to_lowercase().trim_end_matches('/').to_owned(),
Expand Down

0 comments on commit e6a4542

Please sign in to comment.