From ad58e6c29bf065d9ef44c206fb9a57fba6c9e08d Mon Sep 17 00:00:00 2001 From: devkelley <105753233+devkelley@users.noreply.github.com> Date: Mon, 11 Mar 2024 05:58:17 -0700 Subject: [PATCH] Add multi and arm dockerfiles (#265) * Add multi and arm dockerfiles * Update multi dockerfile to remove duplicate code * Changes for blueprint build * Addressing comments, documentation changes * Fix whitespace * Fix newlines --------- Co-authored-by: Lauren Datz <105828115+ladatz@users.noreply.github.com> --- .dockerignore | 5 - ...ering => Dockerfile.intent_brokering.amd64 | 0 ...very => Dockerfile.service_discovery.amd64 | 14 +- Dockerfile.service_discovery.arm64 | 62 ++++++++ Dockerfile.service_discovery.multi | 81 ++++++++++ container/README.md | 138 ++++++++++++++++++ container/scripts/argument_sanitizer.sh | 61 ++++++++ 7 files changed, 352 insertions(+), 9 deletions(-) rename Dockerfile.intent_brokering => Dockerfile.intent_brokering.amd64 (100%) rename Dockerfile.service_discovery => Dockerfile.service_discovery.amd64 (87%) create mode 100644 Dockerfile.service_discovery.arm64 create mode 100644 Dockerfile.service_discovery.multi create mode 100644 container/README.md create mode 100755 container/scripts/argument_sanitizer.sh diff --git a/.dockerignore b/.dockerignore index 61378992..92e19357 100644 --- a/.dockerignore +++ b/.dockerignore @@ -11,8 +11,3 @@ target/ devops/ docs/ tools/ - -Cargo.lock - -**/Dockerfile -**/Dockerfile.** diff --git a/Dockerfile.intent_brokering b/Dockerfile.intent_brokering.amd64 similarity index 100% rename from Dockerfile.intent_brokering rename to Dockerfile.intent_brokering.amd64 diff --git a/Dockerfile.service_discovery b/Dockerfile.service_discovery.amd64 similarity index 87% rename from Dockerfile.service_discovery rename to Dockerfile.service_discovery.amd64 index 0310a22b..53f50a35 100644 --- a/Dockerfile.service_discovery +++ b/Dockerfile.service_discovery.amd64 @@ -17,6 +17,16 @@ ARG CHARIOTT_UID=10001 RUN apt update && apt upgrade -y RUN apt install -y cmake protobuf-compiler +WORKDIR /sdv + +COPY ./ . + +# Check that CHARIOTT_UID argument is valid. +RUN /sdv/container/scripts/argument_sanitizer.sh \ + --arg-value "${CHARIOTT_UID}" \ + --regex "^[0-9]+$" || \ + ( echo "Argument sanitizer failed for ARG 'CHARIOTT_UID'"; exit 1 ) + # unprivileged identity to run Chariott Service Discovery as RUN adduser \ --disabled-password \ @@ -27,10 +37,6 @@ RUN adduser \ --uid "${CHARIOTT_UID}" \ chariott -WORKDIR /sdv - -COPY ./ . - RUN rustup target add x86_64-unknown-linux-musl RUN cargo build --release --target=x86_64-unknown-linux-musl -p service_discovery diff --git a/Dockerfile.service_discovery.arm64 b/Dockerfile.service_discovery.arm64 new file mode 100644 index 00000000..f293cd0c --- /dev/null +++ b/Dockerfile.service_discovery.arm64 @@ -0,0 +1,62 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. +# SPDX-License-Identifier: MIT + +ARG RUST_VERSION=1.70 + +FROM docker.io/library/rust:${RUST_VERSION} AS builder + +# Dockerfile for building Eclipse Chariott Service Discovery container +# +# This Dockerfile utilizes a two step build process. It builds Chariott +# Service Discovery with statically linked dependencies (using musl) +# for an arm64 architecture. + +# Chariott user id +ARG CHARIOTT_UID=10001 + +RUN apt update && apt upgrade -y +RUN apt install -y cmake protobuf-compiler gcc-aarch64-linux-gnu + +WORKDIR /sdv + +COPY ./ . + +# Check that UID argument is valid. +RUN /sdv/container/scripts/argument_sanitizer.sh \ + --arg-value "${UID}" \ + --regex "^[0-9]+$" || \ + ( echo "Argument sanitizer failed for ARG 'UID'"; exit 1 ) + +# unprivileged identity to run Chariott Service Discovery as +RUN adduser \ + --disabled-password \ + --gecos "" \ + --home "/nonexistent" \ + --shell "/sbin/nologin" \ + --no-create-home \ + --uid "${CHARIOTT_UID}" \ + chariott + +RUN rustup target add aarch64-unknown-linux-musl + +RUN cargo build --release --target=aarch64-unknown-linux-musl -p service_discovery + +#################################################################################################### +## Final image +#################################################################################################### +FROM arm64v8/alpine:latest + +# Import Chariott user and group from builder. +COPY --from=builder /etc/passwd /etc/passwd +COPY --from=builder /etc/group /etc/group + +WORKDIR /sdv + +# Copy our build +COPY --from=builder /sdv/target/aarch64-unknown-linux-musl/release/service_discovery /sdv/service_discovery + +# Use the unprivileged chariott user during execution. +USER chariott:chariott + +CMD ["./service_discovery"] diff --git a/Dockerfile.service_discovery.multi b/Dockerfile.service_discovery.multi new file mode 100644 index 00000000..ef3b8cc0 --- /dev/null +++ b/Dockerfile.service_discovery.multi @@ -0,0 +1,81 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. +# SPDX-License-Identifier: MIT + +ARG RUST_VERSION=1.70 + +FROM --platform=$BUILDPLATFORM docker.io/library/rust:${RUST_VERSION} AS builder + +# Dockerfile for building Eclipse Chariott Service Discovery container +# +# This Dockerfile utilizes a two step build process. It builds Chariott +# Service Discovery with statically linked dependencies (using musl) +# for a x86_64 and aarch64 architecture, based on the TARGETARCH. + +# Target architecture to cross-compile +ARG TARGETARCH + +# Chariott user id +ARG CHARIOTT_UID=10001 + +RUN apt update && apt upgrade -y +RUN apt install -y cmake protobuf-compiler + +WORKDIR /sdv + +COPY ./ . + +# Check that CHARIOTT_UID argument is valid. +RUN /sdv/container/scripts/argument_sanitizer.sh \ + --arg-value "${CHARIOTT_UID}" \ + --regex "^[0-9]+$" || \ + ( echo "Argument sanitizer failed for ARG 'CHARIOTT_UID'"; exit 1 ) + +# unprivileged identity to run Chariott Service Discovery as +RUN adduser \ + --disabled-password \ + --gecos "" \ + --home "/nonexistent" \ + --shell "/sbin/nologin" \ + --no-create-home \ + --uid "${CHARIOTT_UID}" \ + chariott + +# 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 ) + +# Based on the target architecture, add the appropriate build target and build service. +RUN if [ "$TARGETARCH" = "amd64" ]; then \ + CARGOARCH="x86_64-unknown-linux-musl"; \ + elif [ "$TARGETARCH" = "arm64" ]; then \ + apt install -y gcc-aarch64-linux-gnu; \ + CARGOARCH="aarch64-unknown-linux-musl"; \ + else \ + echo "Unsupported cross-compile architecture"; \ + exit 1; \ + fi; \ + rustup target add ${CARGOARCH}; \ + cargo build --release --target=${CARGOARCH} -p service_discovery; \ + mkdir -p /sdv/release && cp /sdv/target/${CARGOARCH}/release/service_discovery /sdv/release/service_discovery + +#################################################################################################### +## Final image +#################################################################################################### +FROM --platform=$TARGETPLATFORM alpine:latest + +# Import Chariott user and group from builder. +COPY --from=builder /etc/passwd /etc/passwd +COPY --from=builder /etc/group /etc/group + +WORKDIR /sdv + +# Copy our build +COPY --from=builder /sdv/release/service_discovery /sdv/service_discovery + +# Use the unprivileged chariott user during execution. +USER chariott:chariott + +CMD ["./service_discovery"] diff --git a/container/README.md b/container/README.md new file mode 100644 index 00000000..7fa34fc2 --- /dev/null +++ b/container/README.md @@ -0,0 +1,138 @@ +## Containers + +This repository provides several Dockerfiles to enable building of OCI container images. This +document has instructions for building and running the provided Dockerfiles in +[Docker](#docker-containers) and [Podman](#podman-containers). Refer to the +[Dockerfiles](#dockerfiles) section to select the appropriate Dockerfile. + +### Dockerfiles + +#### Service Discovery + +- [Dockerfile.service_discovery.amd64](../Dockerfile.service_discovery.amd64) - Dockerfile used to build the `Service Discovery Service` for the +x86-64 architecture. +- [Dockerfile.service_discovery.arm64](../Dockerfile.service_discovery.arm64) - Dockerfile used to build the `Service Discovery Service` for the +aarch64 architecture. +- [Dockerfile.service_discovery.multi](../Dockerfile.service_discovery.multi) - Dockerfile used to build the `Service Discovery Service` for multiple architectures based on the TARGETARCH argument. + +#### Intent Brokering + +- [Dockerfile.intent_brokering.amd64](../Dockerfile.intent_brokering.amd64) - Dockerfile used to build the `Intent Brokering Service` for the +x86-64 architecture. +- [Dockerfile.intent_brokering.arm64](../Dockerfile.intent_brokering.arm64) - Dockerfile used to build the `Intent Brokering Service` for the +aarch64 architecture. + +### Docker Containers + +#### Prequisites + +[Install Docker](https://docs.docker.com/engine/install/) + +#### Running in Docker + +To run the service in a Docker container: + +1. Run the following command in the project root directory to build the docker container from the +Dockerfile: + + ```shell + docker build -t -f . + ``` + + For example, to build an image for the `service_discovery` component: + + ```shell + docker build -t service_discovery -f Dockerfile.service_discovery.amd64 . + ``` + + Or to build a multi-platform image for the `service_discovery` component and push it to a + container registry: + You must first create a new builder using the docker-container driver, which gives you access + to more complex features like multi-platform build. See more information here: + [multi-platform builds.](https://docs.docker.com/build/building/multi-platform/#cross-compilation) + + ```shell + docker buildx create --name multibuilder --driver docker-container --use + docker buildx build --platform=linux/amd64,linux/arm64 -f Dockerfile.service_discovery.multi -t /service_discovery_multi --push . + ``` + +1. Once the container has been built, start the container in interactive mode with the following +command in the project root directory: + + ```shell + docker run --name --network=host -it --rm + ``` + + For example, to run the `service_discovery` image built in step 1: + + ```shell + docker run --name service_discovery --network=host -it --rm service_discovery + ``` + + >Note: A custom network is recommended when using a container for anything but testing. + +1. To detach from the container, enter: + + Ctrl + p, Ctrl + q + +1. To stop the container, enter: + + ```shell + docker stop + ``` + + For example, to stop the `service_discovery` container started in step 2: + + ```shell + docker stop service_discovery + ``` + +### Podman Containers + +#### Prequisites + +[Install Podman](https://podman.io/docs/installation) + +#### Running in Podman + +To run the service in a Podman container: + +1. Run the following command in the project root directory to build the podman container from the +Dockerfile: + + ```shell + podman build -t -f . + ``` + + For example, to build an image for the `service_discovery` component: + + ```shell + podman build -t service_discovery -f Dockerfile.amd64 . + ``` + +1. Once the container has been built, start the container with the following command in the project +root directory: + + ```shell + podman run --network=host + ``` + + For example, to run the `service_discovery` image built in step 1: + + ```shell + podman run --network=host service_discovery + ``` + + >Note: A custom network is recommended when using a container for anything but testing. + +1. To stop the container, run: + + ```shell + podman ps -f ancestor= --format="{{.Names}}" | xargs podman stop + ``` + + For example, to stop the `service_discovery` container started in step 2: + + ```shell + podman ps -f ancestor=localhost/service_discovery:latest --format="{{.Names}}" | xargs podman stop + ``` diff --git a/container/scripts/argument_sanitizer.sh b/container/scripts/argument_sanitizer.sh new file mode 100755 index 00000000..d6ed4dad --- /dev/null +++ b/container/scripts/argument_sanitizer.sh @@ -0,0 +1,61 @@ +#!/bin/bash + +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. +# SPDX-License-Identifier: MIT + +# Exits immediately on failure. +set -eu + +# Function to display usage information +usage() { + echo "Usage: $0 [-a|--arg-value] [-r|--regex] " + echo "Example:" + echo " $0 -a \"\${APP_NAME}\" -r \"^[a-zA-Z_0-9-]+$\"" +} + +# Parse command line arguments +while [[ $# -gt 0 ]] +do + key="$1" + + case $key in + -a|--arg-value) + arg_value="$2" + shift # past argument + shift # past value + ;; + -r|--regex) + regex="$2" + shift # past argument + shift # past value + ;; + -h|--help) + usage + exit 0 + ;; + *) + echo "Unknown argument: $key" + usage + exit 1 + esac +done + +# Check if all required arguments have been set +if [[ -z "${arg_value}" || -z "${regex}" ]]; then + echo "Error: Missing required arguments:" + [[ -z "${arg_value}" ]] && echo " -a|--arg-value" + [[ -z "${regex}" ]] && echo " -r|--regex" + echo -e "\n" + usage + exit 1 +fi + +sanitized=$(echo "${arg_value}" | tr -dc "${regex}"); +[ "$sanitized" = "${arg_value}" ] || { + echo "ARG is invalid. ARG='${arg_value}' sanitized='${sanitized}'"; + exit 1 +} + +echo -e "\nARG with value '${arg_value}' is sanitized" +exit 0