[[PageOutline]] = Docker Using Docker for development Docker is a set of platform as a service products that use OS-level virtualization to deliver software in packages called containers. The software that hosts the containers is called Docker Engine. Containers are isolated from one another and bundle their own software, libraries and configuration files. Docker can be hosted on a variety of Operating Systems such as Windows, Linux, and MacOS and is extremely useful to configure OS specific container environments for building embedded Linux firmware. The various board support packages (BSP) that Gateworks provides for building firmware for the various Gateworks Single Board Computers are typically built on Ubuntu Linux development systems. Due to your personal preference as well as Ubuntu's release schedule you may end up with a development host that does not run Ubuntu or the specific version of Ubuntu that is recommended to build a particular BSP. You can use Docker to create a container with a specific version of Ubuntu to overcome this. This wiki page does not cover much of Docker, it is to be used as an example. see also: * [https://petri.com/docker-for-windows-create-a-linux-container-on-windows-10/ Set up Linux Containers on Windows 10] * ​https://hub.docker.com/editions/community/docker-ce-server-ubuntu [=#install] == Installing Docker on Ubuntu Install (from official Docker repo) {{{#!bash # download deps sudo apt-get install apt-transport-https ca-certificates curl software-properties-common # install Docker's GPG key curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - # install Docker repo sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu  $(lsb_release -cs)  stable" #install sudo apt-get update sudo apt-get install docker-ce docker-ce-cli containerd.io }}} - Note the docker group is created but no users are added to it so you need to use sudo to run docker commands or add your user to the docker group (see https://docs.docker.com/install/linux/linux-postinstall/) It is recommended that you add your user to the docker group so that you do not have to run commands as root. The commands referenced on this page assume you have done that and if you have not, you need to run any docker commands via {{{sudo}}}. Testing: * test by downloading hello-world image and running it {{{#!bash docker run hello-world }}} References: - https://hub.docker.com/editions/community/docker-ce-server-ubuntu [=#images] == Images Docker Images represent root filesystems that you can run as a container. These images are built based on a {{{Dockerfile}}} containing a few simple instructions. Create Image: 1. Create a Dockerfile for the image (each Dockerfile should be in a directory representing the image) {{{#!bash # create a directory for your images mkdir docker-images && cd docker-images # create a directory for your image mkdir ubuntu-16.04 && cd ubuntu-16.04 cat << EOF > Dockerfile # Docwnload base image ubuntu 16.04 FROM ubuntu:16.04 # Update software repo RUN apt-get update # Install apps we want RUN  apt-get install -y build-essential git python \ libssl-dev ncurses-dev device-tree-compiler bc gawk zlib1g-dev file \ wget unzip sudo \ EOF }}} 1. Build the image: {{{#!bash # build docker image docker build --tag ubuntu-16.04 . }}} 1. List images {{{#!bash # list images docker images }}} Other image commands: {{{#!bash docker commit # save container as an image docker save #save an image to a tar archive docker buid --tag sampleuser/ubuntu . # build docer image from Dockerfile in current dir docker load # loads a image from file }}} Note that images are stored system-wide in a 'registry'. These images can be pushed to shared registries to share them. References: - see https://www.howtoforge.com/tutorial/how-to-create-docker-images-with-dockerfile/ [=#tags] == Tags An image name is made up of slash-separated name components, optionally prefixed by a registry hostname:port. The hostname must comply with standard DNS rules but may not contain underscores. If registry hostname:port is not present the command uses Docker's public registry located at registry-1.docker.io by default. Name components can contain lowercase letters, digits and separators (period, one or two underscores, or one or more dashes). A tag name must be valid ASCII and may contain lower/upper letters,digits, underscores, periods and dashes. [=#registry] A registry is a storage and content deliver system holding named images available in different tagged versions. Users interact with a registry by using docker push and pull commands The registry is a stateless, open-source, highly scalable server side application that stores and lets you distribute Docker images. The [https://hub.docker.com/ Docker Hub] provides a free-to-use hosted registry with additional features commands: * start local registry {{{#!bash docker run -d -p 5000:5000 --name registry registry:2 }}} * pull an image from the hub {{{#!bash docker pull ubuntu }}} * tag that image so it points to your local registry {{{#!bash docker image tag ubuntu localhost:5000/myimage }}} * push it [=#containers] == Containers A container is a running instance of a system sharing the host machines kernel but using an image root filesystem. When the container is stopped all changes to the filesystem are lost unless you mount volumes within that image to access filesystems on the host. Running container - Run container with an interactive shell {{{#!bash # run container interactively docker run -it ubuntu-16.04 }}} - run container with a bind-mount from host {{{/usr/src/}}} to containers {{{/usr/src}}} {{{#!bash docker run -it --mount type=bind,source=/usr/src,target=/usr/src ubuntu-16.04 }}} - run container in privileged mode (necessary to perform some root-specific functions like 'mount' within the container) {{{#!bash docker run --privileged -it --mount type=bind,source=/usr/src,target=/usr/src ubuntu-16.04 }}} - Notes: - use '--rm' to remove the container after exiting   - use '-it' to allocate pseudo-TTY   - specify /bin/bash to launch specific shell   - use '-w' to set working directory   - use '--env foo=bar' or '--env-file ./env.list' to set env vars Other container commands: {{{#!bash docker ps # list all running containers docker images # list all images on local machine docker history # list history of an image docker port # display exposed port of a running container docker run -it # runs an image, creating a container and changing the terminal to it docker run -p $HOSTPORT:$CONTAINERPORT -d # run image in deatched mode wiht port forwarding ctrl+p, ctrl+q detach from container docker attach docker start docker stop docker inspect # show details docker rm -f # delete container docker rmi # delete image docker tag # apply a new tag to an image docker exec shell command # execute a command within a running container }}} [=#volumes] == Volumes Volumes provide a way to share filesystems or more importantly bring non-volatile storage to docker containers. You can specify volume mounts within image files or on the command-line when running a container. The modern command-line option for this is the {{{--mount}}} parameter which requires a type, source, and target. The type can be {{{type=bind}}} to use an OS bind mount or {{{type=volume}}} to utilize a docker IO driver. Examples: - run container with a bind-mount from host {{{/usr/src/}}} to containers {{{/usr/src}}} {{{#!bash docker run -it --mount type=bind,source=/usr/src,target=/usr/src ubuntu-16.04 }}} - create a volume and mount it in a container: {{{#!bash docker volume create ubuntu-16.04 }}} - run container with a volume mounted from host to {{{/usr/src}}} {{{#!bash docker run -it --mount type=volume,source=ubuntu-16.04,target=/root/workspace ubuntu-16.04 }}} Notes:  - use '--tmpfs' (ie --tmpfs /run:rw,noexec,nosuid,size=65536k) to mount a tmpfs == Examples === Ubuntu 20.04 Focal container This example shows how to create an Ubuntu 20.04 Focal container from an Ubuntu host OS. The steps will vary a bit on other host Operating Systems. 1. Create a docker 'image' based on Ubuntu 20.04: - On a Linux host, docker images are created from a DockerFile that provides details and commands used to build the image {{{#!bash mkdir ubuntu-focal cat << EOF ubuntu-focal/DockerFile # from base image ubuntu 20.04 FROM ubuntu:20.04 # Disable Prompt During Packages Installation ARG DEBIAN_FRONTEND=noninteractive # update list of available packages RUN apt-get update # add a non-root user ARG USER=build ARG UID=1000 ARG GID=1000 RUN groupadd -g $GID $USER && useradd -g $GID -m -s /bin/bash -u $UID $USER # and give that user permission to use sudo RUN apt-get install sudo RUN echo "$USER ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers # and set the prompt as a reminder RUN echo 'export PS1="\u@focal:\w\$ "' >> /home/$USER/.bashrc # switch to this user USER $USER WORKDIR /home/$USER }}} - use the 'docker build' command to build the OS image: {{{#!bash docker build --tag ubuntu-22.04 --build-arg USER=$USER --build-arg UID=$(id -u) --build-arg GID=$(id -g) ubuntu-jammy/ }}} * the '--build-arg' options above are optional but show how you can use your host Linux OS user/group to replace the default ones specified in the DockerFile. This can be very useful if you mount a filesystem from your host OS into your container and want to share user permissions * building the 'image' is a one-time operation. You only need to repeat it if you change the DockerFile specifying the image * if wanted you can add additional 'RUN apt-get install -y ' steps before the 'USER $USER' command (which switches the user from root to a non-root user, otherwise you would need to use sudo) to install additional packages so that you don't have to do that every time you create a new container from the image 1. Create and start docker container {{{#!bash docker run -it --privileged \ --mount type=bind,source=/usr/src,target=/usr/src \ --mount type=bind,source=/tftpboot,target=/tftpboot \ --name jammy ubuntu-jammy }}} * the '--mount' options are optional but show how you can mount directories from your host OS to the container's OS. In this case we have mounted /tftpboot so we can copy files from the container to the hosts's tftpboot directory where a TFTP server may be running. We also mount /usr/src from the host OS to allow our host OS to get to source files we use * Note that once you 'run' a container it remains present even if you have detached from it meaning you can later re-attach to it * The '--it' option is a shortcut for '--interactive' and '--tty' which allocates a pseudo-TTY terminal and keeps STDIN open even if not attached. These options allow the bash process to start in the container, attaches the host OS console to the processes standard input/output/error, and allocates a text-only console. If you at some point exit your docker container you can attach to it again later with the 'docker attach ' command after using 'docker container ls' to show available containers.