/websocket-xorg-docker

Base VNC + WebSocket server docker image that aims to simplify the initial setup of a containerized Graphical environment

Primary LanguageDockerfileMIT LicenseMIT

Running Xorg Virtual Framebuffer, VNC Server, Websocket server and Web/browser-based VNC client from Docker

Information

Summary

Base VNC + WebSocket server docker image that aims to simplify the initial setup of a containerized Graphical environment

Components

  • x11vnc : VNC server
  • websockify : WebSocket server
  • novnc : Web/Browser-based VNC client
  • Xorg : Display Server
    • xauth : Xorg Authorization CLI utility
    • Xvfb : Xorg Virtual Framebuffer; Used to create a virtual framebuffer in the memory to allow you to draw/render graphical applications in a virtual environment within the background as a process

Operational Flow

  • Image Build
    • Setup
      • Installs dependencies
      • Creates a brand new Xauthority cookie file in the HOME directory (default = root, /root)
      • Adds a randomly-generated number using the MAGIC-COOKIE-1 algorithm used by xauth into the xauth database
    • Entry Point
      • Set the DISPLAY environment variable that provides the virtual monitor display to be used by the DISPLAY server
      • Startup Virtual Framebuffer environment pointing to the 'DISPLAY' monitor number environment variable
      • Startup VNC server pointing to the Virtual Framebuffer environment variable and options
      • Startup Websocket server mapping the Websocket server's listening port to the VNC server's host address and port number

Setup

Dependencies

  • docker
  • docker-compose
  • git

Pre-Requisites

  • Select your base image distribution of choice
    • alpine
    • debian
    • nix
  • Select your base image
    • x11vnc (Recommended)
    • tigervncserver (aka xtigervnc)
  • (Optional) Change permissions of script (if any)
    chmod -R u+x scripts/[base-image-distribution]/

Build

  • Build (Base) Docker image

    docker build \
        -t thanatisia/websocket-x:latest \
        --build-arg VNC_SERVER_PASS=[VNC-server-password] \
        --build-arg "FRAMEBUFFER_SCREEN_SPECS=-screen 0 1920x1080x16" \
        -f docker/Dockerfiles/[distribution-name]/base/[vnc-server].Dockerfile \
        .
  • (Optional) Build multi-stage examples

    • Notes
      • This is just an example, substitute this with your multi-stage Dockerfiles
      • In this example, I will be using the 'docker/Dockerfiles/[distribution-name]/stage-2/examples/bspwm.Dockerfile' dockerfile
    docker build \
        -t thanatisia/websocket-x:latest \
        -f docker/Dockerfiles/[distribution-name]/stage-2/examples/bspwm.Dockerfile \
        .

Startup

  • Startup Docker container
    • Explanation
      • -e SHELL=${SHELL} : Default shell environment variable
      • -p 5900:5900 : Websocket server port
      • -p 6080:6080 : VNC server port
      • -v "/dev/shm:/dev/shm" : Mount the shm device driver
      • -v "/run/dbus:/run/dbus" : Mount the host system's device buses
      • --device /dev/snd:/dev/snd : Passthrough the host system soundcard
    docker run -itd \
        --name=browser-x \
        --restart=unless-stopped \
        -e SHELL=${SHELL} \
        -p 5900:5900 \
        -p 6080:6080 \
        -v "/dev/shm:/dev/shm" \
        -v "/run/dbus:/run/dbus" \
        --device /dev/snd:/dev/snd \
        thanatisia/websocket-x:latest

Teardown

  • Stop container

    docker stop browser-x
  • Remove container

    docker rm browser-x

Run all

  • Using make script

    ./make.sh
  • Command Line

    docker build -t thanatisia/websocket-x:latest --build-arg VNC_SERVER_PASS=[VNC-server-password] -f Dockerfile . && \
        docker stop browser-x && docker rm browser-x; \
        docker run -itd \
            --name=browser-x \
            -p 6080:6080 \
            -p 5900:5900 \
            thanatisia/websocket-x:latest

Container use

  • Enter container TTY
    docker exec -it browser-x /bin/bash

Implementation

  • To use as a base image
    • Build the image
    • Include the following
      • in your Dockerfile
        FROM thanatisia/websocket-x:latest AS new-image
        
      • in your docker-compose
        image: thanatisia/websocket-x:latest
    • Multi-stage Build Example
      • Dockerfiles
        • Embedding as a reference (in stage-2.Dockerfile)
          FROM thanatisia/websocket-x:latest AS test-container
          
          ## Copy files
          COPY ./test.sh /tmp/test.sh
          
          ## Run on startup
          RUN /bin/bash -c "/tmp/test.sh"
          
        • Building using 'docker build'
          # Build Stage 1 image
          docker build \
              --tag thanatisia/websocket-x:latest \
              --build-arg VNC_SERVER_PASS=[VNC-server-password] \
              --build-arg "FRAMEBUFFER_SCREEN_SPECS=-screen 0 1920x1080x16" \
              -f docker/Dockerfiles/[distribution-name]/base/[vnc-server].Dockerfile \
              [context]
          
          # Build Stage 2 image
          docker build --tag thanatisia/websocket-x:latest -f stage-2.Dockerfile [context]
          
          ...
          
          # Build Stage N image
          docker build --tag thanatisia/websocket-x:latest -f stage-N.Dockerfile [context]
        • Starting up container
          • Explanation
            • -e SHELL=${SHELL} : Default shell environment variable
            • -p 5900:5900 : Websocket server port
            • -p 6080:6080 : VNC server port
            • -v "/dev/shm:/dev/shm" : Mount the shm device driver
            • -v "/run/dbus:/run/dbus" : Mount the host system's device buses
            • --device /dev/snd:/dev/snd : Passthrough the host system soundcard
          docker run -itd \
              --name=[container-name] \
              --restart=unless-stopped \
              -e SHELL=${SHELL} \
              -p 5900:5900 \
              -p 6080:6080 \
              -v "/dev/shm:/dev/shm" \
              -v "/run/dbus:/run/dbus" \
              --device /dev/snd:/dev/snd \
              thanatisia/websocket-x:latest
      • docker-compose
        • Explanation
          • The service application 'websocket-x-stage-1' is Stage 1 of the Multi-stage Build
            • which will build the image 'thanatisia/websocket-x:latest' using the specified dockerfile (in this case - docker/Dockerfiles/[distribution-name]/base/[vnc-server].Dockerfile)
            • The focus of the Dockerfile recipe is on installing dependencies and establishing the ENTRY POINT
          • The service application 'websocket-x-stage-2' is Stage 2 of the Multi-stage Build, as well as any other additional stages you require when importing this framework as an image recipe
            • For example
              • websocket-x-stage-2 could be for installing additional dependencies to be ran and started after the container is built and started up
          • The service application 'browser-x' is your main application after every previous build stages have been completed
            • In this service, you do not need to use the 'build' key-value and instead, call for the image directly
          • Important Options
            • tty: true : The 'tty' key-value in docker-compose is equivalent to the '-t' option/flag in 'docker run', which basically tells docker to keep the TTY/terminal enabled even after the command has ended
        # Docker compose recipe for running both x11vnc and tigervncserver
        version: "3.7"
        services:
            websocket-x-stage-1:
              image: thanatisia/websocket-x:latest
              build:
                context: .
                args:
                  - "VNC_SERVER_SPECS=[additional-vnc-server-specifications]"
                  - VNC_SERVER_PASS=[your-vnc-server-password]
                  - "FRAMEBUFFER_SCREEN_SPECS=-screen 0 1920x1080x16"
                dockerfile: docker/Dockerfiles/debian/base/[vnc-server].Dockerfile
        
            websocket-x-stage-2:
              image: thanatisia/websocket-x:latest
              build:
                context: .
                dockerfile: docker/Dockerfiles/debian/stage-2.Dockerfile
        
            #### ...
        
            websocket-x-stage-N:
              image: thanatisia/websocket-x:latest
              build:
                context: .
                dockerfile: docker/Dockerfiles/debian/stage-N.Dockerfile
        
            browser-x:
              image: thanatisia/websocket-x:latest
              container_name:  browser-x
              restart: unless-stopped
              environment:
                ## Environment Variables
                - SHELL=${SHELL}
              tty: true
              ports:
                ## Port Forward/Translate/Map host system port to container port
                ## [ip-address]:[host-system-port]:[container-port]
                - 5900:5900 # VNC Server listening port
                - 6080:6080 # Websocket server listening port
              volumes:
                ## Mount volumes from host system to container
                ## [host-system-volume]:[container-volume]
                - /dev/shm:/dev/shm # SHM device
                - /run/dbus:/run/dbus # Device Buses
              devices:
                - /dev/snd:/dev/snd # Passthrough soundcard for audio output

Documentations

Build-time Arguments (Local Variables)

  • To invoke and specify: docker build --build-arg [ARGUMENT_VARIABLE]=[ARGUMENT_VALUE]

Arguments

  • FRAMEBUFFER_OPTS="[DISPLAY-monitor-number] -screen [monitor-number] [width]x[height]x[color-depth/bitrate]" : Set the X Virtual Framebuffer (Xvfb) startup options
    • Positionals
      • DISPLAY : Specify the $DISPLAY monitor number environment variable (i.e. :0, :1)
    • Options
      • screen
        • monitor-number : Specify the virtual monitor number to display the window; i.e. :0 = 0, :1 = 1
      • resolution
        • width : Specify the width (horizontal length) of the Virtual Framebuffer's canvas window (i.e. 1920)
        • height : Specify the height (vertical height) of the Virtual Framebuffer's canvas window (i.e. 1080)
        • color-depth/bitrate : Specify the color density of the Virtual Framebuffer's canvas window (i.e. 16-bit, 32-bit)
  • VNC_SERVER_PASS : Set the VNC server's password to use (Optional; Set '-nopw' in VNC_SERVER_OPTS to not use passwords)
  • VNC_SERVER_OPTS="-display :0 -rfbport 5900 -usepw -passwd ${VNC_SERVER_PASS} -xkb -forever -shared" : Set the VNC server's options to startup with
    • Notes
      • '-bg' is not used to ensure that the ENTRY POINT has a foreground application to keep the container running
      • In the case where you would like to use '-bg',
        • you can add 'bash' to the last application within the ENTRY POINT block

Run-time Arguments (Environment Variables)

  • To invoke and specify: [ENVIRONMENT_VARIABLE]=[VALUE] docker build

Arguments

  • System
    • DISPLAY=:0 : DISPLAY virtual monitor number used for Graphical application rendering by the display server (Xorg/Wayland)
  • VNC Server
    • VNC_SERVER_HOST=127.0.0.1 : Set the VNC server's Hostname/IP address
    • VNC_SERVER_PORT=5900 : Set the VNC server's listening port number
    • VNC_SERVER_PASS=[your-vnc-server-password] : Set the VNC server's password
  • Websocket server
    • WEBSOCKET_CLIENT_PATH=/usr/share/novnc : Set the Web/Browser-based VNC client you wish to access in the websocket server
    • WEBSOCKET_SERVER_PORT=6080 : Set the WebSocket server's listening port number; This is the port number you access to view the Web/Browser-based VNC/SPICE client

Dockerfiles

  • x11vnc.Dockerfile : Recommended

    • Base Image: docker
    • Display Server: Xorg
    • VNC server: x11vnc
    • Websocket server: websockify
  • xtigervnc.Dockerfile

    • Base Image: docker
    • Display Server: Xorg
    • VNC server: Xtigervnc (Package: tigervnc-standalone-server; aka tigervncserver)
    • Websocket server: websockify

Networking

  • Ports to expose
    • 5900 : Default VNC server listening port
    • 6080 : Default WebSocket server listening port

Makefile

Environment Variables

Images

  • IMAGE_NAME : Specify the name of the image to build
  • IMAGE_TAG : Specify the tag/version of the image
  • BUILD_ARGS : Set the build arguments to parse into the build process
    • Defaults
      • --build-arg "VNC_SERVER_PASS=[your-vnc-server-password]"
      • --build-arg "FRAMEBUFFER_SCREEN_SPECS=-screen 0 1920x1080x16"
  • STAGE_1_DOCKERFILE : Specify the Stage 1 (Base) Dockerfile to build the image with
  • CONTEXT : Specify the context (working directory)
    • Defaults
      • . (Current Working Directory)

Containers

  • CONTAINER_IMAGE_NAME : Specify the name of the image to startup the container with
  • CONTAINER_IMAGE_TAG : Specify the tag/version of the image to startup the container with
  • CONTAINER_NAME : Specify the name of the container to startup
  • CONTAINER_OPTS : Specify the options to startup the container with
    • Defaults
      • --restart=unless-stopped
  • CONTAINER_PORT_FORWARDING : Specify the ports to forward/translate/map from host system to the container; Format each port entry with '-p [ip-address]:[host-system-port]:[container-port]' and separate each entry with a space delimiter
    • Defaults
      • -p 5901:5900
      • -p 6080:6080
  • CONTAINER_MOUNT_VOLUMES : Specify the volumes to mount from host system to the container; Format each mount point entry with '-v [host-system-volume]:[container-volume]:[permission]' and separate each entry with a space delimiter
    • Defaults
      • -v "/dev/shm:/dev/shm"
      • -v "/run/dbus:/run/dbus"
  • CONTAINER_PASSTHROUGH_DEVICE : Specify the devices to passthrough from the host system to the container; Format each device entry with '--device [host-system-device]:[container-mount-point]' and separate each entry with a space delimiter
    • Defaults
      • --device "/dev/snd:/dev/snd"

Targets

  • build-stage-1: Build image from Dockerfile
  • run: Startup a container from an image
  • start: Start the container if stopped and exists
  • stop: Stop the container if running
  • restart: Restart the container if running
  • remove: Remove the container if exists
  • logs: Display logs of the container
  • gif: Make/generate the demo animation gif for the software documentations using VHS (by charmbracelet)

Usage Snippets

  • Full run

    • Explanation
      • Parameters
        • -k : Continue and keep going even if errors are encountered
      • Targets
        • stop : Stop the container
        • remove : Remove the container
        • build-stage-1 : Build the stage 1 image
        • run : Startup a container using the image
        • logs : Display logs after running
    make -k stop remove build-stage-1 run logs
  • Parse customized optional values

    ENVIRONMENT_VARIABLE="new_value" ... make -k [targets]
  • Restart container manually

    make restart
  • Build a new image

    make -k stop remove build-stage-1
  • Build a new image and run

    make stop remove build-stage-1 run
  • Generate a demo animation gif for software documentations using the docker image of the 'vhs' CLI utility by charmbracelet

    make gif

Customization

  • To change the base image support
    • Notes
      • The following is assuming that
        1. You are using the repository in its current structure, you can change it according to your needs by following the structure/format
        2. You are using the same naming (i.e. container name or image name), take note of your custom image/container names if you are changing it
    • Open the Makefile
      $EDITOR Makefile
    • Edit the following
      • IMAGE_TAG : Set this to the base image you wish to use
      • CONTAINER_IMAGE_TAG : Same as 'IMAGE_TAG'
      • STAGE_1_DOCKERFILE : Specifically change the base image directory name in 'docker/Dockerfiles/[base-image-directory]'

Wiki

Repository structure

Folders

  • root/ : Project root directory
    • docker/ : Contains all docker contents
      • Dockerfiles/ : Contains all Dockerfile recipes
        • debian/ : Contains debian-based base images for the websocket + VNC server project; You can use these as references and build your own
        • stage-2/ : Contains all Stage-2 (aka Post-Installation) Dockerfiles that you use to build on top of the base images (in a multi-staged build); You can use these as references and build your own
          • examples/ : This is the example directory containing example Dockerfiles for the multi-stage builds
    • scripts/ : Place all your launcher/runner helper scripts that will be executed in the containers here
      • alpine/ : Place all alpine-based helper scripts here
      • debian/ : Place all debian-based helper scripts here
      • nix/ : Place all nix-based helper scripts here
      • '*' : All other remainder scripts

Files

  • docker/Dockerfiles/[distribution-name]/base/

    • x11vnc.Dockerfile : Stage-1 dockerfile that builds up a (Websocket Server + VNC Server + Xorg(11) headless backend via Xvfb) stack using (websockify + x11vnc + Xvfb)
  • docker/Dockerfiles/[distribution-name]/stage-2/examples/

    • bspwm.Dockerfile : Stage-2 example dockerfile that installs and starts up BSPWM and SXHKD

Resources

References

Remarks