Build our Containers
Introduction
The ideal output from any CI/CD pipeline is one or more Docker containers. This allows us to separate deployment from build in a clean way. Once we have our containers we are free to choose how we deploy them whether that is Kubernetes in the cloud or on or with any other deployment service that support containers.
Earthly
Earthly uses Dockerfile syntax for creating builds. So we can leverage our existing Dockerfile knowledge.
Create an Earthfile with the below contents.
# https://earthly.dev/ # Fast, repeatable CI/CD with an instantly familiar syntax – like Dockerfile and Makefile had a baby. # Set the version VERSION 0.6 # We use our devcontainer as it has all the tools we need FROM purtontech/rust-on-nails-devcontainer:1.1.2 ARG APP_NAME=app ARG APP_FOLDER=app ARG IMAGE_PREFIX=rustonnails ARG APP_EXE_NAME=web-server # Version of software ARG DBMATE_VERSION=1.15.0 # Folders ARG AXUM_FOLDER=crates/web-server ARG DB_FOLDER=crates/db ARG GRPC_API_FOLDER=crates/grpc-api ARG PIPELINE_FOLDER=crates/asset-pipeline # This file builds the following containers ARG APP_IMAGE_NAME=$IMAGE_PREFIX/$APP_NAME:latest ARG MIGRATIONS_IMAGE_NAME=$IMAGE_PREFIX/$APP_NAME-migrations:latest WORKDIR /build USER root # Set up for docker in docker https://github.com/earthly/earthly/issues/1225 DO github.com/earthly/lib+INSTALL_DIND USER vscode all: BUILD +migration-container BUILD +app-container npm-deps: COPY $PIPELINE_FOLDER/package.json $PIPELINE_FOLDER/package.json COPY $PIPELINE_FOLDER/package-lock.json $PIPELINE_FOLDER/package-lock.json RUN cd $PIPELINE_FOLDER && npm install SAVE ARTIFACT $PIPELINE_FOLDER/node_modules npm-build: FROM +npm-deps COPY $PIPELINE_FOLDER $PIPELINE_FOLDER COPY --if-exists $GRPC_API_FOLDER $GRPC_API_FOLDER COPY +npm-deps/node_modules $PIPELINE_FOLDER/node_modules RUN cd $PIPELINE_FOLDER && npm run release SAVE ARTIFACT $PIPELINE_FOLDER/dist prepare-cache: # Copy in all our crates COPY --dir crates crates COPY Cargo.lock Cargo.toml . RUN cargo chef prepare --recipe-path recipe.json --bin $AXUM_FOLDER SAVE ARTIFACT recipe.json build-cache: COPY +prepare-cache/recipe.json ./ RUN cargo chef cook --release --target x86_64-unknown-linux-musl SAVE ARTIFACT target SAVE ARTIFACT $CARGO_HOME cargo_home SAVE IMAGE --cache-hint build: # Copy in all our crates COPY --dir crates crates COPY --dir Cargo.lock Cargo.toml . COPY +build-cache/cargo_home $CARGO_HOME COPY +build-cache/target target COPY --dir +npm-build/dist $PIPELINE_FOLDER/ # We need to run inside docker as we need postgres running for cornucopia ARG DATABASE_URL=postgresql://postgres:testpassword@localhost:5432/postgres?sslmode=disable USER root WITH DOCKER \ --pull postgres:alpine RUN docker run -d --rm --network=host -e POSTGRES_PASSWORD=testpassword postgres:alpine \ && while ! pg_isready --host=localhost --port=5432 --username=postgres; do sleep 1; done ;\ dbmate --migrations-dir $DB_FOLDER/migrations up \ && cargo build --release --target x86_64-unknown-linux-musl END SAVE ARTIFACT target/x86_64-unknown-linux-musl/release/$APP_EXE_NAME # This is our migrations sidecar migration-container: FROM alpine RUN apk add --no-cache \ curl \ postgresql-client \ tzdata RUN curl -OL https://github.com/amacneil/dbmate/releases/download/v$DBMATE_VERSION/dbmate-linux-amd64 \ && mv ./dbmate-linux-amd64 /usr/bin/dbmate \ && chmod +x /usr/bin/dbmate COPY --dir $DB_FOLDER . CMD dbmate up SAVE IMAGE --push $MIGRATIONS_IMAGE_NAME # Our axum server app-container: FROM scratch COPY +build/$APP_EXE_NAME web-server # Place assets in a build folder as that's where statics is expecting them. COPY --dir +npm-build/dist /build/$PIPELINE_FOLDER/ COPY --dir $PIPELINE_FOLDER/images /build/$PIPELINE_FOLDER/images ENTRYPOINT ["./web-server"] SAVE IMAGE --push $APP_IMAGE_NAME
Running the Build
From the command line run
earthly -P +all
Validating the build
Assuming the build completes successfully we will have built two docker images
docker images | grep rustonnails
Your should see
rustonnails/app latest db9b3436b423 6 minutes ago 10.1MB rustonnails/app-migrations latest 64064cf0fde4 11 minutes ago 24.1MB
One image is our Axum server, the other is an image that runs our database migrations.