From da6a41b0e900bf6ad4a0fd4aa2c8849dff54ed6a Mon Sep 17 00:00:00 2001 From: devkelley <105753233+devkelley@users.noreply.github.com> Date: Mon, 18 Mar 2024 11:30:02 -0700 Subject: [PATCH] Add multiplatform dockerfile (#54) * Add multi arch dockerfile * Add target to final image * Escape period and remove sdv/target copy --- Cargo.lock | 1 + Dockerfile.freyja_apps.arm64 | 13 ++- Dockerfile.freyja_apps.multi | 134 +++++++++++++++++++++++ container/cargo/config.toml | 7 ++ freyja_apps/e2e/Cargo.toml | 4 +- freyja_apps/ibeji_integration/Cargo.toml | 4 +- freyja_apps/in_memory/Cargo.toml | 5 +- 7 files changed, 156 insertions(+), 12 deletions(-) create mode 100644 Dockerfile.freyja_apps.multi create mode 100644 container/cargo/config.toml diff --git a/Cargo.lock b/Cargo.lock index b2b206b..6a6852d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -603,6 +603,7 @@ dependencies = [ "in-memory-mock-data-adapter", "in-memory-mock-digital-twin-adapter", "in-memory-mock-mapping-adapter", + "paho-mqtt", "tokio", ] diff --git a/Dockerfile.freyja_apps.arm64 b/Dockerfile.freyja_apps.arm64 index 1f4655a..8f49804 100644 --- a/Dockerfile.freyja_apps.arm64 +++ b/Dockerfile.freyja_apps.arm64 @@ -29,12 +29,13 @@ RUN /sdv/container/scripts/argument_sanitizer.sh \ --regex "^[a-zA-Z_0-9-]+$" || \ ( echo "Argument sanitizer failed for ARG 'APP_NAME'"; exit 1 ) -# Check that CONFIG_PATH argument is valid if it is not empty. -RUN [ -z "${CONFIG_PATH}" ] || \ - /sdv/container/scripts/argument_sanitizer.sh \ - --arg-value "${CONFIG_PATH}" \ - --regex "^[.\/a-zA-Z_0-9-]+$" || \ - ( echo "Argument sanitizer failed for ARG 'CONFIG_PATH'"; exit 1 ) +# Check that CONFIG_PATH argument is valid if it's non-empty. +RUN if [ -n "${CONFIG_PATH}" ]; then \ + /sdv/container/scripts/argument_sanitizer.sh \ + --arg-value "${CONFIG_PATH}" \ + --regex "^[\.\/a-zA-Z_0-9-]+$" || \ + ( echo "Argument sanitizer failed for ARG 'CONFIG_PATH'"; exit 1 ); \ + fi # Add Build dependencies. RUN apt update && apt upgrade -y && apt install -y \ diff --git a/Dockerfile.freyja_apps.multi b/Dockerfile.freyja_apps.multi new file mode 100644 index 0000000..8474e52 --- /dev/null +++ b/Dockerfile.freyja_apps.multi @@ -0,0 +1,134 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. +# SPDX-License-Identifier: MIT + +# Comments are provided throughout this file to help you get started. +# If you need more help, visit the Dockerfile reference guide at +# https://docs.docker.com/engine/reference/builder/ + +################################################################################ +# Create a stage for building the application. + +ARG RUST_VERSION=1.72.1 +ARG APP_NAME=freyja-in-memory-app +ARG UID=10001 + +# CONFIG_PATH must be relative to the project root +ARG CONFIG_PATH="" + +FROM --platform=$BUILDPLATFORM docker.io/library/rust:${RUST_VERSION} AS build + +# Target architecture to cross-compile +ARG TARGETARCH + +ARG APP_NAME +ARG CONFIG_PATH +WORKDIR /sdv + +COPY ./ . + +COPY ./container/cargo/config.toml ./.cargo/config.toml + +# Check that APP_NAME argument is valid. +RUN /sdv/container/scripts/argument_sanitizer.sh \ + --arg-value "${APP_NAME}" \ + --regex "^[a-zA-Z_0-9-]+$" || \ + ( echo "Argument sanitizer failed for ARG 'APP_NAME'"; exit 1 ) + +# Check that TARGETARCH argument is valid. +RUN /sdv/container/scripts/argument_sanitizer.sh \ + --arg-value "${TARGETARCH}" \ + --regex "^[a-zA-Z_0-9-]+$" || \ + ( echo "Argument sanitizer failed for ARG 'TARGETARCH'"; exit 1 ) + +# Check that CONFIG_PATH argument is valid if it's non-empty. +RUN if [ -n "${CONFIG_PATH}" ]; then \ + /sdv/container/scripts/argument_sanitizer.sh \ + --arg-value "${CONFIG_PATH}" \ + --regex "^[\.\/a-zA-Z_0-9-]+$" || \ + ( echo "Argument sanitizer failed for ARG 'CONFIG_PATH'"; exit 1 ); \ + fi + +# Add Build dependencies. +RUN apt update && apt upgrade -y && apt install -y \ + cmake \ + libssl-dev \ + pkg-config \ + protobuf-compiler + +# Based on the target architecture, add the appropriate build target and build service. +RUN if [ "$TARGETARCH" = "amd64" ]; then \ + CARGOARCH="x86_64-unknown-linux-gnu"; \ + elif [ "$TARGETARCH" = "arm64" ]; then \ + apt install -y gcc-aarch64-linux-gnu; \ + CARGOARCH="aarch64-unknown-linux-gnu"; \ + else \ + echo "Unsupported cross-compile architecture"; \ + exit 1; \ + fi; \ + rustup target add ${CARGOARCH}; \ + cargo build --release --target=${CARGOARCH} -p "${APP_NAME}"; \ + cp /sdv/target/${CARGOARCH}/release/"${APP_NAME}" /sdv/service + +# Create the config override directory and if CONFIG_PATH is provided, copy files from it +RUN mkdir -p /sdv/config \ + && if [ -n "${CONFIG_PATH}" ]; then \ + cp -r "${CONFIG_PATH}"/* /sdv/config; \ + fi + +################################################################################ +# Create a new stage for running the application that contains the minimal +# runtime dependencies for the application. This often uses a different base +# image from the build stage where the necessary files are copied from the build +# stage. +# +# The example below uses the debian bullseye image as the foundation for running the app. +# By specifying the "bullseye-slim" tag, it will also use whatever happens to be the +# most recent version of that tag when you build your Dockerfile. If +# reproducability is important, consider using a digest +# (e.g., debian@sha256:ac707220fbd7b67fc19b112cee8170b41a9e97f703f588b2cdbbcdcecdd8af57). +FROM --platform=$TARGETPLATFORM docker.io/library/debian:bullseye-slim AS final +ARG UID +ARG CONFIG_PATH + +# Copy container scripts. +COPY ./container/scripts/*.sh /sdv/scripts/ + +# Check that UID argument is valid. +RUN /sdv/scripts/argument_sanitizer.sh \ + --arg-value "${UID}" \ + --regex "^[0-9]+$" || \ + ( echo "Argument sanitizer failed for ARG 'UID'"; exit 1 ) + +# Create a non-privileged user that the app will run under. +# See https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#user +RUN adduser \ + --disabled-password \ + --gecos "" \ + --home "/nonexistent" \ + --shell "/sbin/nologin" \ + --no-create-home \ + --uid "${UID}" \ + appuser + +# Create and add user ownership to config directory. +RUN mkdir -p /sdv/.freyja/config +RUN chown appuser /sdv/.freyja/config + +# Create mnt directory to copy override configs into. +RUN mkdir -p /mnt/config + +USER appuser + +WORKDIR /sdv + +# Copy the executable from the "build" stage. +COPY --from=build /sdv/service /sdv/ + +# Copy from config override directory into where Freyja expects it +COPY --from=build /sdv/config /sdv/.freyja/config + +ENV FREYJA_HOME=/sdv/.freyja + +# What the container should run when it is started. +CMD ["/sdv/scripts/container_startup.sh"] diff --git a/container/cargo/config.toml b/container/cargo/config.toml new file mode 100644 index 0000000..46486d4 --- /dev/null +++ b/container/cargo/config.toml @@ -0,0 +1,7 @@ +[target.aarch64-unknown-linux-gnu] +linker = "aarch64-linux-gnu-gcc" +rustflags = [ "-C", "target-feature=+crt-static", "-C", "link-arg=-lgcc" ] + +[target.x86_64-unknown-linux-gnu] +linker = "x86_64-linux-gnu-gcc" +rustflags = [ "-C", "target-feature=+crt-static", "-C", "link-arg=-lgcc" ] diff --git a/freyja_apps/e2e/Cargo.toml b/freyja_apps/e2e/Cargo.toml index 969117a..e62c81b 100644 --- a/freyja_apps/e2e/Cargo.toml +++ b/freyja_apps/e2e/Cargo.toml @@ -25,6 +25,6 @@ sample-grpc-data-adapter = { workspace = true } managed-subscribe-data-adapter = { workspace = true } mqtt-data-adapter = { workspace = true } -# If built for aarch64, enable the 'vendored-ssl' feature. -[target.'cfg(target_arch = "aarch64")'.dependencies] +# If built for certain architectures, enable the 'vendored-ssl' feature. +[target.'cfg(any(target_arch = "aarch64", target_arch = "x86_64"))'.dependencies] paho-mqtt = { workspace = true, features = ["vendored-ssl"] } diff --git a/freyja_apps/ibeji_integration/Cargo.toml b/freyja_apps/ibeji_integration/Cargo.toml index c628ee3..ab8d80a 100644 --- a/freyja_apps/ibeji_integration/Cargo.toml +++ b/freyja_apps/ibeji_integration/Cargo.toml @@ -25,6 +25,6 @@ sample-grpc-data-adapter = { workspace = true } managed-subscribe-data-adapter = { workspace = true } mqtt-data-adapter = { workspace = true } -# If built for aarch64, enable the 'vendored-ssl' feature. -[target.'cfg(target_arch = "aarch64")'.dependencies] +# If built for certain architectures, enable the 'vendored-ssl' feature. +[target.'cfg(any(target_arch = "aarch64", target_arch = "x86_64"))'.dependencies] paho-mqtt = { workspace = true, features = ["vendored-ssl"] } diff --git a/freyja_apps/in_memory/Cargo.toml b/freyja_apps/in_memory/Cargo.toml index e092e47..d9a0d38 100644 --- a/freyja_apps/in_memory/Cargo.toml +++ b/freyja_apps/in_memory/Cargo.toml @@ -22,5 +22,6 @@ in-memory-mock-digital-twin-adapter = { workspace = true } in-memory-mock-mapping-adapter = { workspace = true } in-memory-mock-data-adapter = { workspace = true } -[features] -containerize = [] +# If built for certain architectures, enable the 'vendored-ssl' feature. +[target.'cfg(any(target_arch = "aarch64", target_arch = "x86_64"))'.dependencies] +paho-mqtt = { workspace = true, features = ["vendored-ssl"] }