diff --git a/.github/workflows/_build-image.yaml b/.github/workflows/_build-image.yaml index 80c2ebd..c299723 100644 --- a/.github/workflows/_build-image.yaml +++ b/.github/workflows/_build-image.yaml @@ -11,9 +11,6 @@ on: description: Image name on the LCAS registry required: true type: string - ros_distro: - required: true - type: string dockerfile: required: true type: string @@ -55,16 +52,12 @@ jobs: with: flavor: latest=false labels: | - org.opencontainers.image.description=AOC Docker Image (${{ inputs.push_image }}, ${{ inputs.ros_distro }}) + org.opencontainers.image.description=AOC Docker Image (${{ inputs.push_image }}) org.opencontainers.image.authors=L-CAS Team images: lcas.lincoln.ac.uk/${{ inputs.push_image }} tags: | - type=raw,value=${{ inputs.ros_distro }}-staging - type=raw,enable=${{ github.event_name != 'pull_request' }},value=${{ inputs.ros_distro }}-latest - type=ref,enable=${{ github.event_name != 'pull_request' }},event=branch,prefix=${{ inputs.ros_distro }}- - type=semver,pattern={{version}},prefix=${{ inputs.ros_distro }}- - type=semver,pattern={{major}}.{{minor}},prefix=${{ inputs.ros_distro }}- - type=semver,pattern={{major}},prefix=${{ inputs.ros_distro }}- + type=raw,value=staging + type=raw,enable=${{ github.event_name != 'pull_request' }},value=latest - uses: docker/setup-buildx-action@v3 @@ -76,10 +69,9 @@ jobs: file: ./${{ inputs.dockerfile }} platforms: ${{ inputs.architectures }} push: true - cache-from: type=registry,ref=lcas.lincoln.ac.uk/cache/${{ inputs.push_image }}:${{ inputs.ros_distro }} - cache-to: type=registry,ref=lcas.lincoln.ac.uk/cache/${{ inputs.push_image }}:${{ inputs.ros_distro }},mode=max + cache-from: type=registry,ref=lcas.lincoln.ac.uk/cache/${{ inputs.push_image }}:latest + cache-to: type=registry,ref=lcas.lincoln.ac.uk/cache/${{ inputs.push_image }}:latest,mode=max tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} build-args: | BASE_IMAGE=${{ inputs.base_image }} - ROS_DISTRO=${{ inputs.ros_distro }} diff --git a/.github/workflows/_build-ros-image.yaml b/.github/workflows/_build-ros-image.yaml new file mode 100644 index 0000000..e7dd931 --- /dev/null +++ b/.github/workflows/_build-ros-image.yaml @@ -0,0 +1,82 @@ +name: Build a single ROS Docker image (reusable) + +on: + workflow_call: + inputs: + base_image: + description: The FROM base image + required: true + type: string + push_image: + description: Image name on the LCAS registry + required: true + type: string + ros_distro: + required: true + type: string + dockerfile: + required: true + type: string + architectures: + required: true + type: string + outputs: + digest: + description: Image digest from the build + value: ${{ jobs.build.outputs.digest }} + secrets: + LCAS_REGISTRY_PUSHER: + required: true + LCAS_REGISTRY_TOKEN: + required: true + +jobs: + build: + runs-on: [lcas, qemu] + outputs: + digest: ${{ steps.build.outputs.digest }} + steps: + - uses: actions/setup-node@v4 + - uses: actions/checkout@v3 + - run: echo "BRANCH=${GITHUB_REF##*/}" >> $GITHUB_ENV + + - uses: docker/login-action@v3 + with: + registry: lcas.lincoln.ac.uk + username: ${{ secrets.LCAS_REGISTRY_PUSHER }} + password: ${{ secrets.LCAS_REGISTRY_TOKEN }} + + - name: Docker meta + id: meta + uses: docker/metadata-action@v5 + with: + flavor: latest=false + labels: | + org.opencontainers.image.description=AOC Docker Image (${{ inputs.push_image }}, ${{ inputs.ros_distro }}) + org.opencontainers.image.authors=L-CAS Team + images: lcas.lincoln.ac.uk/${{ inputs.push_image }} + tags: | + type=raw,value=${{ inputs.ros_distro }}-staging + type=raw,enable=${{ github.event_name != 'pull_request' }},value=${{ inputs.ros_distro }}-latest + type=ref,enable=${{ github.event_name != 'pull_request' }},event=branch,prefix=${{ inputs.ros_distro }}- + type=semver,pattern={{version}},prefix=${{ inputs.ros_distro }}- + type=semver,pattern={{major}}.{{minor}},prefix=${{ inputs.ros_distro }}- + type=semver,pattern={{major}},prefix=${{ inputs.ros_distro }}- + + - uses: docker/setup-buildx-action@v3 + + - name: Build and push + id: build + uses: docker/build-push-action@v6 + with: + context: ./docker + file: ./${{ inputs.dockerfile }} + platforms: ${{ inputs.architectures }} + push: true + cache-from: type=registry,ref=lcas.lincoln.ac.uk/cache/${{ inputs.push_image }}:${{ inputs.ros_distro }} + cache-to: type=registry,ref=lcas.lincoln.ac.uk/cache/${{ inputs.push_image }}:${{ inputs.ros_distro }},mode=max + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + build-args: | + BASE_IMAGE=${{ inputs.base_image }} + ROS_DISTRO=${{ inputs.ros_distro }} diff --git a/.github/workflows/docker-build-and-push.yaml b/.github/workflows/docker-build-and-push.yaml index 407675f..ae19422 100644 --- a/.github/workflows/docker-build-and-push.yaml +++ b/.github/workflows/docker-build-and-push.yaml @@ -9,11 +9,18 @@ on: workflow_dispatch: jobs: - # ── Level 0: no dependencies ────────────────────────────────────────────── + vnc: + uses: ./.github/workflows/_build-image.yaml + with: + base_image: debian:trixie-slim + push_image: vnc + dockerfile: vnc.dockerfile + architectures: linux/amd64,linux/arm64 + secrets: inherit ros-humble: - uses: ./.github/workflows/_build-image.yaml + uses: ./.github/workflows/_build-ros-image.yaml with: base_image: ros:humble push_image: ros @@ -23,7 +30,7 @@ jobs: secrets: inherit ros-jazzy: - uses: ./.github/workflows/_build-image.yaml + uses: ./.github/workflows/_build-ros-image.yaml with: base_image: ros:jazzy push_image: ros @@ -33,7 +40,7 @@ jobs: secrets: inherit ros-cuda-humble: - uses: ./.github/workflows/_build-image.yaml + uses: ./.github/workflows/_build-ros-image.yaml with: base_image: nvidia/cuda:11.8.0-runtime-ubuntu22.04 push_image: ros_cuda @@ -43,7 +50,7 @@ jobs: secrets: inherit ros-cuda-jazzy: - uses: ./.github/workflows/_build-image.yaml + uses: ./.github/workflows/_build-ros-image.yaml with: base_image: nvidia/cuda:12.9.1-runtime-ubuntu24.04 push_image: ros_cuda @@ -53,10 +60,19 @@ jobs: secrets: inherit # ── Level 1: base_image is the exact digest from level 0 ───────────────── + vnc-devtools: + needs: vnc + uses: ./.github/workflows/_build-image.yaml + with: + base_image: lcas.lincoln.ac.uk/vnc@${{ needs.vnc.outputs.digest }} + push_image: vnc_devtools + dockerfile: vnc_devtools.dockerfile + architectures: linux/amd64,linux/arm64 + secrets: inherit ros-cuda-desktop-humble: needs: ros-cuda-humble - uses: ./.github/workflows/_build-image.yaml + uses: ./.github/workflows/_build-ros-image.yaml with: # Digest is immutable — no staging tag, no race condition, no cleanup base_image: lcas.lincoln.ac.uk/ros_cuda@${{ needs.ros-cuda-humble.outputs.digest }} @@ -68,11 +84,11 @@ jobs: ros-cuda-desktop-jazzy: needs: ros-cuda-jazzy - uses: ./.github/workflows/_build-image.yaml + uses: ./.github/workflows/_build-ros-image.yaml with: base_image: lcas.lincoln.ac.uk/ros_cuda@${{ needs.ros-cuda-jazzy.outputs.digest }} push_image: ros_cuda_desktop ros_distro: jazzy dockerfile: cuda_desktop.dockerfile architectures: linux/amd64 - secrets: inherit \ No newline at end of file + secrets: inherit diff --git a/README.md b/README.md index 1e97aee..ddcb95d 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,67 @@ -# aoc_container_base +# LCAS/aoc_container_base -A repository of verstile ROS-enabled Docker containers, orginally developed as apart of the [Agri-OpenCore (AOC) project](https://agri-opencore.org). +A repository of verstile Docker containers, orginally developed as apart of the [Agri-OpenCore (AOC) project](https://agri-opencore.org) stack. -| Container Name | Tags | Purpose | -| ------------------------------------- | --------------------- | --------------------------------------------------------------------------------------------------------------------------------- | -| `lcas.lincoln.ac.uk/ros` | { `humble`, `jazzy` } | Base ROS Container, the minimal environment you need for ROS | -| `lcas.lincoln.ac.uk/ros_cuda` | { `humble`, `jazzy` } | ROS + Nvidia. When you need to use a GPU in your ROS environment for either better quality simulation or AI workloads. | -| `lcas.lincoln.ac.uk/ros_cuda_desktop` | { `humble`, `jazzy` } | ROS + Nvidia + Packages. Installs the `ros-{distro}-desktop` varient so there is the full ROS stack available. | \ No newline at end of file +This repository provides us with the following containers: + +| Container Name | Varients | Purpose | File | +| --- | --- | --- | --- | +| `lcas.lincoln.ac.uk/ros` | `humble` `jazzy` | Base ROS Container, the minimal environment you need for ROS | [base.dockerfile](base.dockerfile) | +| `lcas.lincoln.ac.uk/ros_cuda` | `humble` `jazzy` | ROS + Nvidia. When you need to use a GPU in your ROS environment for either better quality simulation or AI workloads. | [cuda.dockerfile](cuda.dockerfile) | +| `lcas.lincoln.ac.uk/ros_cuda_desktop` | `humble` `jazzy` | ROS + Nvidia + Packages. Installs the `ros-{distro}-desktop` varient so there is the full ROS stack available. | [cuda_desktop.dockerfile](cuda_desktop.dockerfile) | +| `lcas.lincoln.ac.uk/vnc` | `latest` | Standalone VNC container that can take X11 visualisations and show them in a browser. | [vnc.dockerfile](vnc.dockerfile) | +| `lcas.lincoln.ac.uk/vnc_devtools` | `latest` | Adds development tools such as a terminal for the standard VNC environment. | [vnc_devtools.dockerfile](vnc_devtools.dockerfile) | + +These containers are built from three standard container images, `ros`, `nvidia/cuda` and `debian`. Each container is either built from one of these pre-existing images or one derrived from it in this pattern. + +```mermaid +flowchart TB + library/ros["library/ros"] --> lcas/ros["lcas/ros"] + nvidia/cuda["nvidia/cuda"] --> lcas/ros_cuda["lcas/ros_cuda"] + lcas/ros_cuda --> lcas/ros_cuda_desktop["lcas/ros_cuda_desktop"] + debian["debian"] --> vnc["vnc"] + vnc --> vnc_devtools["vnc_devtools"] + + style library/ros stroke-width:1px,stroke-dasharray: 1,fill:#ffffff + style lcas/ros fill:#BBDEFB + style nvidia/cuda stroke-width:1px,stroke-dasharray: 1,fill:#ffffff + style lcas/ros_cuda fill:#BBDEFB + style lcas/ros_cuda_desktop fill:#BBDEFB + style debian stroke-width:1px,stroke-dasharray: 1,fill:#ffffff + style vnc fill:#BBDEFB + style vnc_devtools fill:#BBDEFB +``` + +## How can I use this? + +This works best if you follow the [`ros2_workspace_template`](https://github.com/lcas/ros2_pkg_template), use this as a template to build your own repositories, that contain the packages you want to ship. + +You can work either inside the devcontainer or by running the container yourself, and then when you are ready start by enabling the deployment workflows and adding automated testing. Then once you're happy move this onto a real robot platform - and keep iterating till it works! + +```mermaid +%%{init: {"theme":"neutral","flowchart":{"curve":"linear"}}}%% +flowchart LR + Dev["Develop in the Devcontainer"] + Workflows["Activate Workflows to Build and Push Images"] + AutoTest["Add Automated Testing Steps Where Possible"] + Deploy["Add/Update Your Container on to the Platform Deployment Repository"] + RealTest["Test the Packages on a Real Robot"] + Outcome{"Did it work well?"} + Fix(("Make changes that will fix the issues")) + Complete(["✅ Complete"]) + + %% main flow + Dev --> Workflows --> AutoTest --> Deploy --> RealTest --> Outcome + Outcome -- "Yes" --> Complete + Outcome -- "No" --> Fix --> AutoTest + + %% iterative dev loop (renders as a real loop in GitHub) + Dev -. "Iterate" .-> Iter(("🔄")) + Iter -.-> Dev + + %% keep it subtle (GitHub-friendly) + classDef loop fill:transparent,stroke:#999,stroke-width:1px; + class Iter loop; +``` + +# diff --git a/base.dockerfile b/base.dockerfile index 1e9ead5..9e0e146 100644 --- a/base.dockerfile +++ b/base.dockerfile @@ -11,20 +11,20 @@ ENV ROS_DISTRO=${ROS_DISTRO} ENV DEBIAN_FRONTEND=noninteractive RUN apt-get update \ - && apt-get upgrade -y && \ - apt-get install -y --no-install-recommends \ - build-essential \ - ca-certificates \ - gnupg \ - cmake \ - git \ - curl \ - wget \ - unzip \ - ros-${ROS_DISTRO}-ros-base \ - ros-${ROS_DISTRO}-rmw-cyclonedds-cpp \ - python3-colcon-common-extensions && \ - rm -rf /var/lib/apt/lists/* + && apt-get upgrade -y \ + && apt-get install -y --no-install-recommends \ + build-essential \ + ca-certificates \ + gnupg \ + cmake \ + git \ + curl \ + wget \ + unzip \ + ros-${ROS_DISTRO}-ros-base \ + ros-${ROS_DISTRO}-rmw-cyclonedds-cpp \ + python3-colcon-common-extensions \ + && rm -rf /var/lib/apt/lists/* ENV LANG=en_US.UTF-8 @@ -39,8 +39,7 @@ RUN groupadd --gid $USER_GID $USERNAME \ && useradd -s /bin/bash --uid $USER_UID --gid $USER_GID -m $USERNAME \ && apt-get update \ && apt-get install -y --no-install-recommends sudo \ - && echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME\ - && chmod 0440 /etc/sudoers.d/$USERNAME \ + && echo $USERNAME ALL=\(root\) NOPASSWD:ALL >/etc/sudoers.d/$USERNAME && chmod 0440 /etc/sudoers.d/$USERNAME \ && rm -rf /var/lib/apt/lists/* # Cyclone DDS Config @@ -60,4 +59,3 @@ ENV CYCLONEDDS_URI=file:///etc/cyclonedds.xml USER ${USERNAME} CMD ["bash", "-l"] - diff --git a/compose.gpu.yml b/compose.gpu.yml new file mode 100644 index 0000000..25db4fe --- /dev/null +++ b/compose.gpu.yml @@ -0,0 +1,24 @@ +# GPU override — merge on top of compose.yml when an NVIDIA GPU is available. +# Requires the NVIDIA Container Toolkit to be installed and configured. +# +# Usage: docker compose -f compose.yml -f compose.gpu.yml up + +services: + + vnc: + deploy: + resources: + reservations: + devices: + - driver: nvidia + count: all + capabilities: [gpu, utility, video, graphics] + + ros: + deploy: + resources: + reservations: + devices: + - driver: nvidia + count: all + capabilities: [gpu, compute, utility, video, graphics] diff --git a/compose.yml b/compose.yml new file mode 100644 index 0000000..d456bb9 --- /dev/null +++ b/compose.yml @@ -0,0 +1,54 @@ +# Docker Compose illustrating a VNC desktop container alongside a ROS container. +# The ROS container renders GUI applications (e.g. RViz) through the VNC desktop +# by sharing the X11 socket. Both services are on the same bridge network so +# CycloneDDS peer discovery works out of the box. +# +# Usage: +# No GPU: docker compose up +# With GPU: docker compose -f compose.yml -f compose.gpu.yml up +# +# Access the desktop at: http://localhost:5801/vnc.html?autoconnect=true + +services: + + # ── VNC Desktop ────────────────────────────────────────────────────────────── + vnc: + build: + context: ./docker # contains vnc-entrypoint.sh + dockerfile: ../vnc.dockerfile + target: xfce + ports: + - "5801" # noVNC web interface + volumes: + - x11:/tmp/.X11-unix # share X server socket with other containers + networks: + - ros_net + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:5801/vnc.html"] + interval: 10s + timeout: 5s + retries: 5 + + # ── ROS Container ──────────────────────────────────────────────────────────── + ros: + build: + context: ./docker # contains cyclonedds.xml + dockerfile: ../cuda.dockerfile + depends_on: + vnc: + condition: service_healthy # wait for VNC desktop to be ready before starting ROS + environment: + DISPLAY: ":1" # connect to the VNC X server + volumes: + - x11:/tmp/.X11-unix # X11 socket shared from VNC container + networks: + - ros_net + command: ["sleep", "infinity"] + +# ── Shared resources ─────────────────────────────────────────────────────────── +volumes: + x11: # ephemeral volume for the X11 unix socket + +networks: + ros_net: + driver: bridge # CycloneDDS multicast works within a bridge network diff --git a/cuda.dockerfile b/cuda.dockerfile index 0a4e2ec..6f963a8 100644 --- a/cuda.dockerfile +++ b/cuda.dockerfile @@ -12,35 +12,35 @@ ENV ROS_DISTRO=${ROS_DISTRO} ENV DEBIAN_FRONTEND=noninteractive RUN apt-get update \ - && apt-get upgrade -y \ - && apt-get install -y --no-install-recommends \ - locales \ - curl \ - wget \ - ca-certificates \ - gnupg2 \ - lsb-release \ - git \ - nano \ - python3-setuptools \ - software-properties-common \ - tzdata \ - && locale-gen en_US.UTF-8 \ - && update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8 \ - && rm -rf /var/lib/apt/lists/* + && apt-get upgrade -y \ + && apt-get install -y --no-install-recommends \ + locales \ + curl \ + wget \ + ca-certificates \ + gnupg2 \ + lsb-release \ + git \ + nano \ + python3-setuptools \ + software-properties-common \ + tzdata \ + && locale-gen en_US.UTF-8 \ + && update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8 \ + && rm -rf /var/lib/apt/lists/* ENV LANG=en_US.UTF-8 # Prepare ROS2 RUN add-apt-repository universe \ - && curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg \ - && echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(. /etc/os-release && echo $UBUNTU_CODENAME) main" | tee /etc/apt/sources.list.d/ros2.list > /dev/null + && curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg \ + && echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(. /etc/os-release && echo $UBUNTU_CODENAME) main" | tee /etc/apt/sources.list.d/ros2.list >/dev/null RUN apt-get update && apt-get install -y --no-install-recommends \ - ros-${ROS_DISTRO}-ros-base \ - python3-rosdep \ - ros-${ROS_DISTRO}-rmw-cyclonedds-cpp \ - && rm -rf /var/lib/apt/lists/* + ros-${ROS_DISTRO}-ros-base \ + python3-rosdep \ + ros-${ROS_DISTRO}-rmw-cyclonedds-cpp \ + && rm -rf /var/lib/apt/lists/* # Cyclone DDS Config COPY cyclonedds.xml /etc/cyclonedds.xml @@ -48,9 +48,9 @@ COPY cyclonedds.xml /etc/cyclonedds.xml RUN . /opt/ros/${ROS_DISTRO}/setup.sh && rosdep init && rosdep update # Setup VirtualGL -RUN wget -q -O- https://packagecloud.io/dcommander/virtualgl/gpgkey | gpg --dearmor >/etc/apt/trusted.gpg.d/VirtualGL.gpg && \ - echo "deb [signed-by=/etc/apt/trusted.gpg.d/VirtualGL.gpg] https://packagecloud.io/dcommander/virtualgl/any/ any main" >> /etc/apt/sources.list.d/virtualgl.list && \ - apt update && apt install -y virtualgl libgl1 && rm -rf /var/lib/apt/lists/* +RUN wget -q -O- https://packagecloud.io/dcommander/virtualgl/gpgkey | gpg --dearmor >/etc/apt/trusted.gpg.d/VirtualGL.gpg \ + && echo "deb [signed-by=/etc/apt/trusted.gpg.d/VirtualGL.gpg] https://packagecloud.io/dcommander/virtualgl/any/ any main" >>/etc/apt/sources.list.d/virtualgl.list \ + && apt-get update && apt-get install -y virtualgl libgl1 && rm -rf /var/lib/apt/lists/* # Create a non-root user ARG USERNAME=ros @@ -58,13 +58,12 @@ ARG USER_UID=1001 ARG USER_GID=$USER_UID RUN groupadd --gid $USER_GID $USERNAME \ - && useradd -s /bin/bash --uid $USER_UID --gid $USER_GID -m $USERNAME \ - # Add sudo support for the non-root user\ - && apt-get update \ - && apt-get install -y --no-install-recommends sudo \ - && echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME\ - && chmod 0440 /etc/sudoers.d/$USERNAME \ - && rm -rf /var/lib/apt/lists/* + && useradd -s /bin/bash --uid $USER_UID --gid $USER_GID -m $USERNAME \ + # Add sudo support for the non-root user\ + && apt-get update \ + && apt-get install -y --no-install-recommends sudo \ + && echo $USERNAME ALL=\(root\) NOPASSWD:ALL >/etc/sudoers.d/$USERNAME && chmod 0440 /etc/sudoers.d/$USERNAME \ + && rm -rf /var/lib/apt/lists/* # Configure bash profile RUN echo "if [ -f /etc/bash.bashrc ]; then source /etc/bash.bashrc; fi" >> /root/.bashrc && \ @@ -74,7 +73,7 @@ RUN echo "if [ -f /etc/bash.bashrc ]; then source /etc/bash.bashrc; fi" >> /root echo "source /opt/ros/${ROS_DISTRO}/setup.bash" >> /etc/bash.bashrc && \ echo "alias t='tmux'" >> /etc/bash.bashrc && \ echo "alias cls='clear'" >> /etc/bash.bashrc - + ENV RMW_IMPLEMENTATION=rmw_cyclonedds_cpp ENV CYCLONEDDS_URI=file:///etc/cyclonedds.xml ENV TVNC_VGL=1 diff --git a/cuda_desktop.dockerfile b/cuda_desktop.dockerfile index 30e7907..12dd340 100644 --- a/cuda_desktop.dockerfile +++ b/cuda_desktop.dockerfile @@ -1,6 +1,5 @@ ARG BASE_IMAGE=lcas.lincoln.ac.uk/ros_cuda:humble-main -########################################### FROM ${BASE_IMAGE} AS base RUN sudo apt-get update && sudo apt-get install -y --no-install-recommends \ diff --git a/docker/vnc-entrypoint.sh b/docker/vnc-entrypoint.sh new file mode 100644 index 0000000..4c6cebb --- /dev/null +++ b/docker/vnc-entrypoint.sh @@ -0,0 +1,66 @@ +#!/bin/bash + +echo +echo "****************************************************************************************************************************************" +echo "AOC VNC container starting..." +echo "****************************************************************************************************************************************" + +sudo rm -rf /tmp/.X1-lock /tmp/.X11-unix/X1 > /dev/null 2>&1 +screen -dmS turbovnc bash -c '/opt/TurboVNC/bin/vncserver :1 -depth 24 -noxstartup -securitytypes TLSNone,X509None,None 2>&1 | tee /tmp/vnc.log; read -p "Press any key to continue..."' + +echo "waiting for display to be up" +while ! xdpyinfo -display :1 2> /dev/null > /dev/null; do + sleep .1 +done +echo "display is up" + +echo "starting xfce4" +screen -dmS xfce4 bash -c 'DISPLAY=:1 /usr/bin/xfce4-session 2>&1 | tee /tmp/xfce4.log' +while [ -z "$DBUS_SESSION_BUS_ADDRESS" ]; do + if pgrep xfce4-session > /dev/null; then + XFCE_PID=$(pgrep xfce4-session) + export DBUS_SESSION_BUS_ADDRESS=$(grep -z DBUS_SESSION_BUS_ADDRESS /proc/$XFCE_PID/environ|cut -d= -f2-|tr -d '\0') + fi + sleep .1 +done +echo "xfce4 up" + +echo "starting novnc ${NOVNC_VERSION}" +screen -dmS novnc bash -c '/usr/local/novnc/noVNC-${NOVNC_VERSION}/utils/novnc_proxy --vnc localhost:5901 --listen 5801 2>&1 | tee /tmp/novnc.log' + +DISPLAY=:1 xhost +local: 2>/dev/null +echo "xhost +local: applied to :1" + +# set the wallpaper +( + export DISPLAY=:1 + CURRENT_USER="$(id -un)" + VNC_WALLPAPER=${VNC_WALLPAPER:-lcas} + WALLPAPER="/usr/share/backgrounds/xfce/${VNC_WALLPAPER}.jpg" + base="/backdrop/screen0/monitorVNC-0/workspace0" + + xfconf-query -c xfce4-desktop -p "${base}/image-show" -n -t bool -s true + xfconf-query -c xfce4-desktop -p "${base}/image-style" -n -t int -s 5 + xfconf-query -c xfce4-desktop -p "${base}/image-path" -n -t string -s "$WALLPAPER" + xfconf-query -c xfce4-desktop -p "${base}/last-image" -n -t string -s "$WALLPAPER" + xfconf-query -c xfce4-desktop -p "${base}/last-single-image" -n -t string -s "$WALLPAPER" + + # xfdesktop --reload +) & + +echo +echo "****************************************************************************************************************************************" +echo "VNC Desktop ready. Open your browser at http://localhost:5801/vnc.html?autoconnect=true or another hostname and port you may have forwarded." +echo "****************************************************************************************************************************************" +echo + +echo >&2 + +# This script can either be a wrapper around arbitrary command lines, +# or it will simply exec bash if no arguments were given +if [[ $# -eq 0 ]]; then + exec "/bin/bash" +else + exec "$@" +fi + diff --git a/docker/wallpapers/aoc.jpg b/docker/wallpapers/aoc.jpg new file mode 100644 index 0000000..49abcc1 Binary files /dev/null and b/docker/wallpapers/aoc.jpg differ diff --git a/docker/wallpapers/gir.jpg b/docker/wallpapers/gir.jpg new file mode 100644 index 0000000..1481b25 Binary files /dev/null and b/docker/wallpapers/gir.jpg differ diff --git a/docker/wallpapers/lcas.jpg b/docker/wallpapers/lcas.jpg new file mode 100644 index 0000000..5d4022d Binary files /dev/null and b/docker/wallpapers/lcas.jpg differ diff --git a/docker/wallpapers/uol.jpg b/docker/wallpapers/uol.jpg new file mode 100644 index 0000000..e3fb402 Binary files /dev/null and b/docker/wallpapers/uol.jpg differ diff --git a/vnc.dockerfile b/vnc.dockerfile new file mode 100644 index 0000000..3fe89d6 --- /dev/null +++ b/vnc.dockerfile @@ -0,0 +1,128 @@ +ARG BASE_IMAGE=debian:trixie-slim +FROM ${BASE_IMAGE} AS base + +ARG BASE_IMAGE +ARG username=lcas +ENV BASE_IMAGE=${BASE_IMAGE} + +ENV DEBIAN_FRONTEND=noninteractive \ + DISPLAY=:1 \ + NOVNC_PORT=5801 \ + RESOLUTION=1366x768x24 + +# Install timezone +RUN ln -fs /usr/share/zoneinfo/UTC /etc/localtime \ + && export DEBIAN_FRONTEND=noninteractive \ + && apt-get update \ + && apt-get install -y --no-install-recommends tzdata \ + && dpkg-reconfigure --frontend noninteractive tzdata \ + && rm -rf /var/lib/apt/lists/* + +# Install packages +RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates \ + curl \ + gnupg2 \ + lsb-release \ + sudo \ + wget \ + libglvnd0 \ + libgl1 \ + libglx0 \ + libegl1 \ + libxext6 \ + libx11-6 \ + x11-utils \ + less \ + screen \ + unzip \ + x11-apps \ + && rm -rf /var/lib/apt/lists/* + +# Create non-root user +RUN useradd -m -s /bin/bash -G video,sudo ${username} \ + && echo "${username} ALL=(ALL) NOPASSWD:ALL" >/etc/sudoers.d/${username} \ + && chmod 0440 /etc/sudoers.d/${username} + +# Fix /tmp/.X11-unix permissions +RUN mkdir -p /tmp/.X11-unix \ + && chmod 1777 /tmp/.X11-unix + +# Install VirtualGL +RUN wget -q -O- https://packagecloud.io/dcommander/virtualgl/gpgkey | gpg --dearmor >/etc/apt/trusted.gpg.d/VirtualGL.gpg \ + && echo "deb [signed-by=/etc/apt/trusted.gpg.d/VirtualGL.gpg] https://packagecloud.io/dcommander/virtualgl/any/ any main" >>/etc/apt/sources.list.d/virtualgl.list \ + && apt-get update && apt-get install -y virtualgl libgl1 && rm -rf /var/lib/apt/lists/* + +COPY </etc/apt/trusted.gpg.d/TurboVNC.gpg \ + && echo "deb [signed-by=/etc/apt/trusted.gpg.d/TurboVNC.gpg] https://packagecloud.io/dcommander/turbovnc/any/ any main" >>/etc/apt/sources.list.d/TurboVNC.list \ + && apt-get update && apt-get install -y turbovnc && rm -rf /var/lib/apt/lists/* + +# Install noVNC +ENV NOVNC_VERSION=1.4.0 +ENV WEBSOCKETIFY_VERSION=0.10.0 +RUN mkdir -p /usr/local/novnc \ + && curl -sSL https://github.com/novnc/noVNC/archive/v${NOVNC_VERSION}.zip -o /tmp/novnc-install.zip \ + && unzip /tmp/novnc-install.zip -d /usr/local/novnc \ + && cp /usr/local/novnc/noVNC-${NOVNC_VERSION}/vnc.html /usr/local/novnc/noVNC-${NOVNC_VERSION}/index.html \ + && curl -sSL https://github.com/novnc/websockify/archive/v${WEBSOCKETIFY_VERSION}.zip -o /tmp/websockify-install.zip \ + && unzip /tmp/websockify-install.zip -d /usr/local/novnc \ + && ln -s /usr/local/novnc/websockify-${WEBSOCKETIFY_VERSION} /usr/local/novnc/noVNC-${NOVNC_VERSION}/utils/websockify \ + && rm -f /tmp/websockify-install.zip /tmp/novnc-install.zip \ + && sed -i -E 's/^python /python3 /' /usr/local/novnc/websockify-${WEBSOCKETIFY_VERSION}/run + +FROM base AS xfce + +# Install XFCE4 +RUN apt-get update \ + && apt-get -y install \ + xfce4-session \ + xfce4-panel \ + xfdesktop4 \ + && rm -rf /var/lib/apt/lists/* +RUN apt-get purge -y xfce4-screensaver + +COPY vnc-entrypoint.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh +ENTRYPOINT ["/entrypoint.sh"] + +# Copy in wallpaper +COPY ./wallpapers/*.jpg /usr/share/backgrounds/xfce/ + +# Allow other containers to share windows into this display +RUN echo 'xhost +local: 2>/dev/null' >> /etc/bash.bashrc && \ + echo "if [ -f /etc/bash.bashrc ]; then source /etc/bash.bashrc; fi" >> /root/.bashrc && \ + echo "alias cls='clear'" >> /etc/bash.bashrc && \ + echo '[[ $- == *i* ]] && [[ $$ -ne 1 ]] && echo -e "$(printf "%80s" | tr " " "=") \nYou are inside the VNC container,\n - You do not have access to ROS in this terminal\n - You may docker exec into other containers if that is configured.\n$(printf "%80s" | tr " " "=")\n"' >> /etc/bash.bashrc + +EXPOSE 5801 + +USER ${username} +ENV HOME=/home/${username} +WORKDIR ${HOME} +RUN mkdir -p ${HOME}/.local/bin + +ENV DISPLAY=:1 +ENV TVNC_VGL=1 +ENV VGL_ISACTIVE=1 +ENV VGL_FPS=25 +ENV VGL_COMPRESS=0 +ENV VGL_DISPLAY=egl +ENV VGL_WM=1 +ENV VGL_PROBEGLX=0 +ENV LD_PRELOAD=/usr/lib/libdlfaker.so:/usr/lib/libvglfaker.so +ENV SHELL=/bin/bash + +CMD ["sleep", "infinity"] \ No newline at end of file diff --git a/vnc_devtools.dockerfile b/vnc_devtools.dockerfile new file mode 100644 index 0000000..347a993 --- /dev/null +++ b/vnc_devtools.dockerfile @@ -0,0 +1,20 @@ +ARG BASE_IMAGE +FROM ${BASE_IMAGE} AS base +ARG username=lcas + +ARG DOCKER_GID=984 + +# Install Docker (for docker exec etc) +USER root +RUN apt-get update \ + && apt-get install -y ca-certificates curl \ + && install -m 0755 -d /etc/apt/keyrings \ + && curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc \ + && echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian $(. /etc/os-release && echo $VERSION_CODENAME) stable" >/etc/apt/sources.list.d/docker.list \ + && apt-get update \ + && apt-get install -y docker-ce-cli konsole \ + && rm -rf /var/lib/apt/lists/* \ + && groupadd -g $DOCKER_GID docker \ + && usermod -aG docker ${username} + +USER ${username}