diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..df3bf78 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,18 @@ +.git +.gitattributes +.gitignore +.dockerignore + +debug/ +target/ + +# Allow configuration files +!target/debug/*.yaml + +.devcontainer/ +.github/ +devops/ +docs/ +tools/ + +Cargo.lock diff --git a/.gitignore b/.gitignore index ce69d9b..4aee225 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,6 @@ Cargo.lock # Stops pushes of local vscode files. /.vscode/* + +# Do not include .env files for Docker. +/*.env diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..94a5893 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,77 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. +# SPDX-License-Identifier: MIT + +# syntax=docker/dockerfile:1 + +# 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 +FROM rust:${RUST_VERSION}-slim-bullseye AS build +ARG APP_NAME=pub-sub-service +WORKDIR /app + +COPY ./ . + +# Add Build dependencies. +RUN apt update && apt upgrade -y && apt install -y \ + cmake \ + libssl-dev \ + pkg-config \ + protobuf-compiler + +# Check that APP_NAME argument is valid. +RUN sanitized=$(echo "${APP_NAME}" | tr -dc '^[a-zA-Z_0-9-]+$'); \ +[ "$sanitized" = "${APP_NAME}" ] || { \ + echo "ARG 'APP_NAME' is invalid. APP_NAME='${APP_NAME}' sanitized='${sanitized}'"; \ + exit 1; \ +} + +# Build the application with the 'containerize' feature. +RUN cargo build --features containerize --release -p "${APP_NAME}" + +# Copy the built application to working directory. +RUN cp ./target/release/"${APP_NAME}" /app/service + +################################################################################ +# 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 debian:bullseye-slim AS final + +# Create a non-privileged user that the app will run under. +# See https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#user +ARG UID=10001 +RUN adduser \ + --disabled-password \ + --gecos "" \ + --home "/nonexistent" \ + --shell "/sbin/nologin" \ + --no-create-home \ + --uid "${UID}" \ + appuser +USER appuser + +WORKDIR /sdv + +# Copy the executable from the "build" stage. +COPY --from=build /app/service /sdv/ +COPY --from=build /app/target/debug/*.yaml /sdv/target/debug/ + +# Expose the port that the application listens on. +EXPOSE 50051 + +# What the container should run when it is started. +CMD ["/sdv/service"] diff --git a/README.md b/README.md index f98858a..7147639 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,8 @@
Getting Started • Configuration Setup • - Running the Service + Running the Service • + Running in a Container
@@ -264,6 +265,101 @@ These two methods are used by a publisher to dynamically manage a topic. Please see more full featured examples in [Running the Simple Samples](./samples/README.md#running-the-simple-samples). +## Running in a Container + +### Docker + +#### Prequisites + +[Install Docker](https://docs.docker.com/engine/install/) + +#### Running in Docker + +To run the service in a Docker container: + +1. Copy the [docker.env](./container/template/docker.env) template into the project root directory. +The file sets two environment variables, 'HOST_GATEWAY' and 'LOCALHOST_ALIAS', where 'HOST_GATEWAY' +is the DNS name used by the container to represent the localhost address and 'LOCALHOST_ALIAS' is +the localhost address used in the service's configuration settings. This file should already be set +up with out any modification needed. From the project root directory, the file can be copied with: + + ```shell + cp ./container/template/docker.env . + ``` + +1. Run the following command in the project root directory to build the docker container from the +Dockerfile: + + ```shell + docker build -t pub_sub_service -f Dockerfile . + ``` + +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 pub_sub_service -p 50051:50051 --env-file=docker.env --add-host=host.docker.internal:host-gateway -it --rm pub_sub_service + ``` + +1. To detach from the container, enter: + + Ctrl + p, Ctrl + q + +1. To stop the container, enter: + + ```shell + docker stop pub_sub_service + ``` + +### Podman + +#### Prequisites + +[Install Podman](https://podman.io/docs/installation) + +#### Running in Podman + +To run the service in a Podman container: + +1. Copy the [podman.env](./container/template/podman.env) template into the project root directory. +The file sets two environment variables, 'HOST_GATEWAY' and 'LOCALHOST_ALIAS', where 'HOST_GATEWAY' +is the DNS name used by the container to represent the localhost address and 'LOCALHOST_ALIAS' is +the localhost address used in the service's configuration settings. This file should already be set +up with out any modification needed. From the project root directory, the file can be copied with: + + ```shell + cp ./container/template/podman.env . + ``` + +1. Run the following command in the project root directory to build the podman container from the +Dockerfile: + + ```shell + podman build -t pub_sub_service:latest -f Dockerfile . + ``` + +1. Once the container has been built, start the container with the following command in the project +root directory: + + ```shell + podman run -p 50051:50051 --env-file=podman.env --network=slirp4netns:allow_host_loopback=true localhost/pub_sub_service + ``` + +1. To stop the container, run: + + ```shell + podman ps -f ancestor=localhost/pub_sub_service:latest --format="{{.Names}}" | xargs podman stop + ``` + +#### Notes + +1. By default, podman does not recognize docker images for dockerfile. To fix this, one can add the +`docker.io` registry to `/etc/containers/registries.conf` by changing the following field: + + ```conf + unqualified-search-registries = ["docker.io"] + ``` + ## Trademarks This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft diff --git a/container/template/docker.env b/container/template/docker.env new file mode 100644 index 0000000..56bafc6 --- /dev/null +++ b/container/template/docker.env @@ -0,0 +1,9 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. +# SPDX-License-Identifier: MIT + +# DNS name used by the container to communicate with host. +HOST_GATEWAY=host.docker.internal + +# Alias for localhost to be replaced by HOST_GATEWAY if run in a container. +LOCALHOST_ALIAS=0.0.0.0 diff --git a/container/template/podman.env b/container/template/podman.env new file mode 100644 index 0000000..1abc4a3 --- /dev/null +++ b/container/template/podman.env @@ -0,0 +1,9 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. +# SPDX-License-Identifier: MIT + +# DNS name used by the container to communicate with host. +HOST_GATEWAY=host.containers.internal + +# Alias for localhost to be replaced by HOST_GATEWAY if run in a container. +LOCALHOST_ALIAS=0.0.0.0 diff --git a/pub-sub-service/Cargo.toml b/pub-sub-service/Cargo.toml index b43ec78..d3bd5ba 100644 --- a/pub-sub-service/Cargo.toml +++ b/pub-sub-service/Cargo.toml @@ -29,4 +29,5 @@ yaml-rust = { workspace = true, optional = true } [features] default = ["yaml"] -yaml = ["yaml-rust"] \ No newline at end of file +yaml = ["yaml-rust"] +containerize = [] diff --git a/pub-sub-service/src/connectors/chariott_connector.rs b/pub-sub-service/src/connectors/chariott_connector.rs index b2eb0ff..015ae08 100644 --- a/pub-sub-service/src/connectors/chariott_connector.rs +++ b/pub-sub-service/src/connectors/chariott_connector.rs @@ -13,6 +13,8 @@ use proto::{ service_registry::v1::{RegisterRequest, ServiceMetadata}, }; +use crate::load_config::get_uri; + type ChariottClient = ServiceRegistryClient