From 948e8868bf33d3d43fe05b858a46c10ff92e5228 Mon Sep 17 00:00:00 2001 From: Vladimir Jovin Date: Mon, 11 Nov 2024 00:19:14 +0100 Subject: [PATCH] Refactor bake config file, much more readable, structured and extendible, modify Makefile, add initial Dockerfile.deploy --- docker/Dockerfile.deploy | 71 ++++++++++++++++++++++++++++++++++++++++ docker/Makefile | 64 ++++++++++++++++++++++++++---------- docker/docker-bake.hcl | 68 +++++++++++++++++++------------------- 3 files changed, 151 insertions(+), 52 deletions(-) create mode 100644 docker/Dockerfile.deploy diff --git a/docker/Dockerfile.deploy b/docker/Dockerfile.deploy new file mode 100644 index 00000000..3226f5df --- /dev/null +++ b/docker/Dockerfile.deploy @@ -0,0 +1,71 @@ +ARG BASE_IMAGE=osrf/ros:humble-simulation +FROM $BASE_IMAGE + +USER root + +# Essentials +RUN apt-get update && apt-get install --no-install-recommends -y -o Dpkg::Options::="--force-overwrite" \ + ros-humble-navigation2 \ + ros-humble-nav2-bringup \ + ros-humble-rviz2 \ + ros-humble-teleop-twist-keyboard \ + ros-humble-dynamixel-sdk \ + ros-humble-can-msgs \ + ros-humble-ruckig \ + ros-humble-laser-filters \ + ros-humble-domain-bridge \ + ros-humble-rmw-cyclonedds-cpp \ + ros-humble-ros2-control \ + ros-humble-ros2-controllers \ + ros-humble-rqt-common-plugins \ + ros-humble-webots-ros2 \ + ros-humble-dynamixel-workbench-toolbox \ + ros-humble-behaviortree-cpp \ + libopencv-dev \ + # TODO: Question which of these are necessary + python3-pip \ + python3-pil \ + alsa \ + libxshmfence1 \ + libgtk-3-dev \ + git \ + git-lfs \ + curl \ + wget \ + vim \ + rsync \ + dialog \ + fuse + +RUN python3 -m pip install scipy transforms3d + +#HOTFIX: https://github.com/ros-controls/ros2_controllers/issues/482 +RUN wget -O /tmp/diff_drive_controller.deb http://snapshots.ros.org/humble/2022-11-23/ubuntu/pool/main/r/ros-humble-diff-drive-controller/ros-humble-diff-drive-controller_2.12.0-1jammy.20221108.202153_amd64.deb && \ + apt install -y --allow-downgrades /tmp/diff_drive_controller.deb && \ + rm -f /tmp/diff_drive_controller.deb + +# User config +COPY ./config/bashrc /tmp/bashrc + +RUN mkdir -p /memristor && \ + cat /tmp/bashrc >> /memristor/.bashrc && \ + rm -f /tmp/bashrc && \ + mkdir -p /memristor/ros2_ws/src/mep3 + +# Set the working directory +WORKDIR /root/ros2_ws + +RUN git clone https://github.com/memristor/mep3 src/mep3 + +RUN touch src/mep3/mep3_simulation/COLCON_IGNORE + +RUN apt-get update && \ + apt-get install -y python3-vcstool && \ + rosdep update && \ + rosdep install --from-paths src --ignore-src -r -y + +# Build the packages +RUN . /opt/ros/humble/setup.sh && \ + colcon build --symlink-install --cmake-args -DCMAKE_BUILD_TYPE=RelWithDebInfo + +ENTRYPOINT ["/bin/bash", "-c", "source install/local_setup.bash && exec bash"] diff --git a/docker/Makefile b/docker/Makefile index 94d91fe1..8349d4f6 100644 --- a/docker/Makefile +++ b/docker/Makefile @@ -1,8 +1,13 @@ +SHELL := $(shell which bash) +.SHELLFLAGS := -eu -o pipefail -c + + MAKEFLAGS+=--silent UID:=$(shell id -u) DOCKER_DIR:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) PROJECT_DIR:=$(shell dirname ${DOCKER_DIR}) NVIDIA_GPU:=$(shell (docker info | grep Runtimes | grep nvidia 1> /dev/null && command -v nvidia-smi 1>/dev/null 2>/dev/null && nvidia-smi | grep Processes 1>/dev/null 2>/dev/null) && echo '--runtime nvidia --gpus all' || echo '') +BUILDX_INSTALLED := $(shell docker buildx 1>/dev/null 2>&1 && echo true) FLAVOR=base IMAGE=ghcr.io/memristor/mep3 @@ -21,20 +26,43 @@ vnc: $(eval IMAGE=ghcr.io/memristor/mep3-vnc) true -# Docker since version 23.0 has been including buildx by default, current live version is 27.2.1 as of 2024-09-16 -build: - docker buildx bake ${FLAVOR} +deploy: + $(eval FLAVOR=deploy) + $(eval IMAGE=ghcr.io/memristor/mep3-deploy) + true +multiple: + $(eval FLAVOR=multiple) + true -# Should be removed in the future, buildx is the new default as per comment above -build_legacy: - echo ${NO_CACHE_ARG} - DOCKER_BUILDKIT=1 docker build ${DOCKER_DIR} -f ${DOCKER_DIR}/Dockerfile.base -t mep3 ${DOCKER_ARGS} --build-arg UID=${UID} - [ ${FLAVOR} != 'base' ] && \ - DOCKER_BUILDKIT=1 docker build ${DOCKER_DIR} -f ${DOCKER_DIR}/Dockerfile.${FLAVOR} -t ${IMAGE} ${DOCKER_ARGS} || \ - true +## Test if the provided command exists +exists/cmd/%: + @hash $(*) > /dev/null 2>&1 || (echo "ERROR: '$(*)' must be installed"; exit 1) + +## Test if the provided environment variable exists +exists/env/%: + @if [ -z '$($(*))' ]; then echo "ERROR: environment variable '$*' not set" && exit 1; fi + + +# Docker since version 23.0 has been including buildx by default, current live version is 27.2.1 as of 2024-09-16 +build: | exists/cmd/docker colors + if [ -n "${BUILDX_INSTALLED}" ]; then \ + [ ${FLAVOR} == 'multiple' ] && docker buildx bake && exit 0; \ + [ ${FLAVOR} == 'base' ] && docker buildx bake mep3 && exit 0; \ + docker buildx bake mep3-${FLAVOR} && exit 0; \ + else \ + printf '%b\n' "${RED}Docker buildx is not present, it is highly recommended to install newer version of docker\n${NC}" || \ + [ ${FLAVOR} = 'multiple' ] && \ + DOCKER_BUILDKIT=1 docker build ${DOCKER_DIR} -f ${DOCKER_DIR}/Dockerfile.base -t mep3 ${DOCKER_ARGS} --build-arg UID=${UID} && \ + DOCKER_BUILDKIT=1 docker build ${DOCKER_DIR} -f ${DOCKER_DIR}/Dockerfile.vnc -t mep3-vnc ${DOCKER_ARGS} && \ + DOCKER_BUILDKIT=1 docker build ${DOCKER_DIR} -f ${DOCKER_DIR}/Dockerfile.deploy -t mep3-deploy ${DOCKER_ARGS} && exit 0; \ + [ ${FLAVOR} = 'base' ] && \ + DOCKER_BUILDKIT=1 docker build ${DOCKER_DIR} -f ${DOCKER_DIR}/Dockerfile.${FLAVOR} -t mep3 ${DOCKER_ARGS} --build-arg UID=${UID} && exit 0; \ + [ ${FLAVOR} != 'base' ] && \ + DOCKER_BUILDKIT=1 docker build ${DOCKER_DIR} -f ${DOCKER_DIR}/Dockerfile.${FLAVOR} -t mep3-${FLAVOR} ${DOCKER_ARGS} && exit 0; \ + fi -run: test-nvidia +run: | exists/cmd/docker test-nvidia docker run \ --net=host \ --ipc=host \ @@ -51,7 +79,7 @@ run: test-nvidia -v ${PROJECT_DIR}:/memristor/ros2_ws/src/mep3:rw \ -d -it ${IMAGE} 1>/dev/null -test-nvidia: colors +test-nvidia: | exists/cmd/docker lspci | grep -qi nvidia && base64 --decode massage | unxz || true docker run --rm \ -e NVIDIA_DRIVER_CAPABILITIES=all ${NVIDIA_GPU} \ @@ -60,26 +88,26 @@ test-nvidia: colors printf '%b\n' "${RED}Detected NVIDIA GPU in system, but missing packets, look up NVIDIA GPU section in README!\n${NC}" || \ true -start-code-server: +start-code-server: | exists/cmd/docker docker exec -d -it mep3-${FLAVOR} bash -c 'pgrep code-server || code-server /memristor/ros2_ws/src/mep3' && \ xdg-open 'localhost:31415?folder=/memristor/ros2_ws/src/mep3' -stop-code-server: +stop-code-server: | exists/cmd/docker docker exec -it mep3-${FLAVOR} pkill -f code-server -exec: +exec: | exists/cmd/docker docker exec -it mep3-${FLAVOR} bash -destroy: +destroy: | exists/cmd/docker docker container kill mep3-${FLAVOR} 1>/dev/null || true docker container rm -f mep3-${FLAVOR} 1>/dev/null || true -setup-default: colors +setup-default: | exists/cmd/docker colors docker exec -it mep3-${FLAVOR} sh -c '/usr/bin/setup.sh --default' printf '%b\n%b\n' "${GREEN}Default setup complete!${NC}" \ "Run ${BOLD}make exec${NC} or ${BOLD}docker exec -it mep3-${FLAVOR}${NC} to access the container" -setup-interactive: colors +setup-interactive: | exists/cmd/docker colors docker exec -it mep3-${FLAVOR} sh -c '/usr/bin/setup.sh --interactive' printf '%b\n%b\n' "${GREEN}Interactive setup complete!${NC}" \ "Run ${BOLD}make exec${NC} or ${BOLD}docker exec -it mep3-${FLAVOR}${NC} to access the container" diff --git a/docker/docker-bake.hcl b/docker/docker-bake.hcl index 3cfb7546..15f8fee9 100644 --- a/docker/docker-bake.hcl +++ b/docker/docker-bake.hcl @@ -1,45 +1,45 @@ -group "default" { - targets = ["base", "vnc"] -} - variable "COMMIT_SHA" {} variable "GITHUB_REPO" {} -target "base" { - context = "." - dockerfile = "Dockerfile.base" - tags = [ - // If GITHUB_REPO is not set, then we are building locally - equal("", GITHUB_REPO) ? "mep3:latest" : "", - equal("", GITHUB_REPO) && notequal("", COMMIT_SHA) ? "mep3:${COMMIT_SHA}" : "", - notequal("", GITHUB_REPO) ? "ghcr.io/${GITHUB_REPO}/mep3:latest" : "", - notequal("", GITHUB_REPO) && notequal("", COMMIT_SHA) ? "ghcr.io/${GITHUB_REPO}:${COMMIT_SHA}" : "" - ] - // Enable layer caching - cache-from = ["type=gha,scope=mep3"] - cache-to = ["type=gha,mode=max,scope=mep3"] - // for reference, can be passed the same way as --build-arg, this can be completely omitted, and in this configuration will default to values specified in Dockerfile - args = { - DEBIAN_FRONTEND = null, - UID = null, +variable "TARGET_IMAGE_NAME_MAPPING" { + default = { + "base" = "mep3" + "vnc" = "mep3-vnc" + "deploy" = "mep3-deploy" } } -target "vnc" { - context = "." - dockerfile = "Dockerfile.vnc" - contexts = { - mep3 = "target:base" +variable CONTEXTS_MAPPING { + default = { + "vnc" = { + "mep3" = "target:mep3" + } } - tags = [ +} + +function "eval_tags" { + params = [image_name, commit_sha, github_repo] + result = [ // If GITHUB_REPO is not set, then we are building locally - equal("", GITHUB_REPO) ? "mep3-vnc:latest" : "", - equal("", GITHUB_REPO) && notequal("", COMMIT_SHA) ? "mep3-vnc:${COMMIT_SHA}" : "", - notequal("", GITHUB_REPO) ? "ghcr.io/${GITHUB_REPO}/mep3-vnc:latest" : "", - notequal("", GITHUB_REPO) && notequal("", COMMIT_SHA) ? "ghcr.io/${GITHUB_REPO}-vnc:${COMMIT_SHA}" : "" + equal("", github_repo) ? image_name : "", + equal("", github_repo) && notequal("", commit_sha) ? "${image_name}:${commit_sha}" : "", + // otherwise, we are building on GitHub Actions + notequal("", github_repo) ? "ghcr.io/${github_repo}/${image_name}:latest" : "", + notequal("", github_repo) && notequal("", commit_sha) ? "ghcr.io/${github_repo}:${commit_sha}" : "" ] - // Enable layer caching - cache-from = ["type=gha,scope=mep3-vnc"] - cache-to = ["type=gha,mode=max,scope=mep3-vnc"] +} + +target "default" { + name = lookup(TARGET_IMAGE_NAME_MAPPING, tgt, "") + matrix = { + tgt = keys(TARGET_IMAGE_NAME_MAPPING) + } + + tags = eval_tags(lookup(TARGET_IMAGE_NAME_MAPPING, tgt, ""), COMMIT_SHA, GITHUB_REPO) + dockerfile = "Dockerfile.${tgt}" + cache-to = [format("%s%s", "type=gha,mode=max,scope=", lookup(TARGET_IMAGE_NAME_MAPPING, tgt, ""))] + cache-from = [format("%s%s", "type=gha,scope=", lookup(TARGET_IMAGE_NAME_MAPPING, tgt, ""))] + + contexts = lookup(CONTEXTS_MAPPING, tgt, {}) }