More examples

Usually the scripts of the containers are kept in git repositories, not in a local directory (like /var/test/ that we have used so far). Let’s try some of the simple scripts that are located at https://gitlab.com/docker-scripts

1. Get the scripts

Let’s try to build an ubuntu container. We can get the scripts for the container with the command ds pull:

root@vps ~ # ds pull ubuntu
Cloning into '/opt/docker-scripts/ubuntu'...
warning: redirecting to https://gitlab.com/docker-scripts/ubuntu.git/
remote: Enumerating objects: 45, done.
remote: Counting objects: 100% (25/25), done.
remote: Compressing objects: 100% (24/24), done.
remote: Total 45 (delta 5), reused 0 (delta 0), pack-reused 20
Unpacking objects: 100% (45/45), 17.91 KiB | 2.99 MiB/s, done.
Some docker-scripts internals

As you can see from the output, ds pull is basically a git clone of https://gitlab.com/docker-scripts/ubuntu to the directory /opt/docker-scripts/. The command ds (without arguments) shows some information about the general configuration and setup of docker-scripts:

root@vps ~ # ds

DockerScripts:2.0 https://gitlab.com/docker-scripts/ds

DSDIR='/root/.ds'

--> tree /root/.ds :
/root/.ds
└── config.sh

--> cat /root/.ds/config.sh :
REPO='https://gitlab.com/docker-scripts'
SCRIPTS='/opt/docker-scripts'
CONTAINERS='/var/ds'
NETWORK='dsnet'
#SUBNET='172.27.0.0/16'
#SHOW_DOCKER_COMMANDS=true

--> ls /opt/docker-scripts:
ds
ubuntu

--> ls /var/ds:

For help about commands try: ds -h

The environment variable DSDIR tell DS where to look for configurations. It loads $DSDIR/config.sh, which contains these settings:

  • REPO — the base URL for the script repos

  • SCRIPTS — the default directory where the scripts are saved

  • CONTAINERS — the default directory where the containers are created

These can be customized if needed, but usually the defaults are OK.

Let’s have a look at the scripts that we just downloaded:

root@vps ~ # cd /opt/docker-scripts/ubuntu/
root@vps /opt/docker-scripts/ubuntu # tree
.
├── Dockerfile
├── settings.sh
├── cmd
│   ├── config.sh
│   └── create.sh
├── inject
│   └── setup.sh
├── README.md
└── LICENSE
  • Dockerfile:

    include(jammy)
    
    RUN apt install --yes openssh-server python3

    Besides the basic image, we are also installing openssh-server and python3.

  • settings.sh:

    APP=ubuntu
    PORTS="2201:22"

    Port 2201 is forwarded to 22, in order to allow SSH access.

  • cmd/create.sh:

    rename_function cmd_create orig_cmd_create
    cmd_create() {
        orig_cmd_create \
            "$@"    # accept additional options, e.g.: --privileged
    }

    Although cmd_create is extended, it actually does not do any extra things.

  • cmd/config.sh:

    cmd_config() {
        ds inject ubuntu-fixes.sh
        ds inject setup.sh
    }

    The script ubuntu-fixes.sh is defined by the framework itself, and setup.sh is the script below.

  • inject/setup.sh:

    #!/bin/bash -x
    
    ### generate a key pair
    [[ -f /host/sshkey ]] \
        || ssh-keygen -t rsa -f /host/sshkey -q -N ''
    
    ### copy public key to authorized_keys
    mkdir -p /root/.ssh
    chmod 700 /root/.ssh
    cat /host/sshkey.pub > /root/.ssh/authorized_keys
    chmod 600 /root/.ssh/authorized_keys
    
    ### customize the configuration of sshd
    sed -i /etc/ssh/sshd_config \
        -e '/PermitRootLogin/ c PermitRootLogin without-password'
    systemctl restart sshd

    It generates an SSH key pair, if one is not already present, and corrects the SSH config.

2. Initialize a directory for the container

root@vps /opt/docker-scripts/ubuntu # cd
root@vps ~ # ds init ubuntu @server1
Container initialized on '/var/ds/server1/'.

Note that because the directory of the container (@server1) does not start with ./ or ../ or /, it is interpreted as a subdirectory of $CONTAINERS, which is set on ~/.ds/config.sh and has as default value /var/ds/.

root@vps ~ # cd /var/ds/server1/
root@vps /var/ds/server1 # ls
settings.sh

root@vps /var/ds/server1 # cat settings.sh
APP=ubuntu
IMAGE=ubuntu
CONTAINER=server1
PORTS="2201:22"

So, initialization just copies settings.sh on the directory of the container and sets some values for IMAGE and CONTAINER. The default value for IMAGE is the name of the scripts' directory, and the default value for CONTAINER is the name of the container’s directory. You may change them if you wish, but the default values are usually OK.

3. Build and test the container

To build the image and start the container do:

root@vps /var/ds/server1 # ds make

Besides starting the container, it also runs any configuration or setup scripts.

Docker uses cached layers of images and sometimes they may be stale and create some problems. To build the image from scratch (ignoring any caches) and then start the container do:

root@vps /var/ds/server1 # ds build --no-cache
root@vps /var/ds/server1 # ds make

Did you notice the file sshkey that is created in the directory? We can use it to login to the container, like this:

root@vps /var/ds/server1 # ssh -p 2201 -i sshkey root@localhost

The port 2201 on the localhost is forwarded to the port 22 of the container, so we end up inside the container. With this key we can also login to the container remotely (from outside the server).

Don’t you find it a bit strange that you did not have to open the port 2201 on the firewall, in order to access it? As a matter of fact, Docker bypasses the firewall, by default. So, any port that you forward from the host to a container is immediately open and available to the whole world, regardless of the firewall on your host. Keep this in mind and be careful.

4. Build another container

We don’t need to get the scripts with ds pull ubuntu, because we did it for the first container. However id doesn’t hurt to update them.

Let’s initialize a directory for the container:

root@vps ~ # ds init ubuntu @server2
Container initialized on '/var/ds/server2/'.
root@vps ~ # cd /var/ds/server2/
root@vps /var/ds/server2 # ls
settings.sh
root@vps /var/ds/server2 # cat settings.sh
APP=ubuntu
IMAGE=ubuntu
CONTAINER=server2
PORTS="2201:22"

When we try to build it (with ds make), we get errors like this:

Error response from daemon:
driver failed programming external connectivity on endpoint server2
(df70bc5342142c45df6ff8ad80ebf0b73e71187f1904482d510d288ad7ee3336):
Bind for 0.0.0.0:2201 failed: port is already allocated
Error: failed to start containers: server2

Indeed, we can confirm with docker ps that the port 2201 is already being used for the container server1:

root@vps ~ # docker ps --format '{{.Names}}: {{.Ports}}'
server1: 0.0.0.0:2201->22/tcp, :::2201->22/tcp

Let’s edit settings.sh, change the port to 2202 (or something else), and try again:

root@vps /var/ds/server2 # vim settings.sh
root@vps /var/ds/server2 # cat settings.sh
APP=ubuntu
IMAGE=ubuntu
CONTAINER=server2
PORTS="2202:22"
root@vps /var/ds/server2 # ds make

5. Build a debian container

The scripts for a simple debian container are almost identical to those for a simple ubuntu container, except that the Dockerfile has the line include(bullseye) instead of include(jammy)

  • Get the scripts:

    root@vps ~ # ds pull debian
  • Initialize container dir:

    root@vps ~ # ds init debian @server3
  • Change the port on settings.sh:

    root@vps ~ # cd /var/ds/server3/
    root@vps /var/ds/server3 # sed -i settings.sh \
                                   -e '/PORTS=/ c PORTS="2203:22"'
    root@vps /var/ds/server3 # cat settings.sh
    APP=debian
    IMAGE=debian
    CONTAINER=server3
    PORTS="2203:22"
  • Build the container:

    root@vps /var/ds/server3 # ds make
  • Test it:

    root@vps /var/ds/server3 # ssh -p 2203 -i sshkey root@localhost

6. Build other containers

6.1. Use a branch of the scripts

The command ds pull can also have a branch argument:

root@vps ~ # ds pull
Usage:
    pull <app> [<branch>]
        Clone or pull 'https://gitlab.com/docker-scripts/<app>'
        to '/opt/docker-scripts/<app>'. A certain branch can be specified
        as well. When a branch is given, then it is saved to
        '/opt/docker-scripts/<app>-<branch>'

Let’s use the branch buster of the scripts' repo this time.

  1. Get the scripts:

    root@vps ~ # ds pull debian buster
    Cloning into '/opt/docker-scripts/debian-buster'...
    warning: redirecting to https://gitlab.com/docker-scripts/debian.git/
  2. Initialize the container:

    root@vps ~ # ds init debian-buster @server4
    Container initialized on '/var/ds/server4/'.
  3. Customize settings.sh:

    root@vps ~ # cd /var/ds/server4/
    root@vps /var/ds/server4 # sed -i settings.sh \
                                   -e '/PORTS=/ c PORTS="2204:22"'
    root@vps /var/ds/server4 # cat settings.sh
    APP=debian-buster
    IMAGE=debian-buster
    CONTAINER=server4
    PORTS="2204:22"
  4. Build the container:

    root@vps /var/ds/server4 # ds make
  5. Test it:

    root@vps /var/ds/server4 # ssh -p 2204 -i sshkey \
            root@localhost cat /etc/os-release
    PRETTY_NAME="Debian GNU/Linux 10 (buster)"
    NAME="Debian GNU/Linux"
    VERSION_ID="10"
    VERSION="10 (buster)"
    VERSION_CODENAME=buster
    ID=debian
    HOME_URL="https://www.debian.org/"
    SUPPORT_URL="https://www.debian.org/support"
    BUG_REPORT_URL="https://bugs.debian.org/"
    Connection to localhost closed.

6.2. Get the scripts with git clone

This time let’s get the scripts with git clone instead of ds pull.

  1. Get the branch buster of the debian repo:

    root@vps ~ # git clone -b buster \
            https://gitlab.com/docker-scripts/debian \
    	/opt/docker-scripts/test/buster
    Cloning into '/opt/docker-scripts/test/buster'...
  2. Initialize the container:

    root@vps ~ # ds init test/buster @server5
    Container initialized on '/var/ds/server5/'.
  3. Customize settings.sh:

    root@vps ~ # cd /var/ds/server5/
    root@vps /var/ds/server5 # sed -i settings.sh \
                                   -e '/PORTS=/ c PORTS="2205:22"'
    root@vps /var/ds/server5 # cat settings.sh
    APP=test/buster
    IMAGE=buster
    CONTAINER=server5
    PORTS="2205:22"
  4. Build the container:

    root@vps /var/ds/server5 # ds make
  5. Test it:

    root@vps /var/ds/server5 # ssh -p 2205 -i sshkey \
            root@localhost cat /etc/os-release
    PRETTY_NAME="Debian GNU/Linux 10 (buster)"
    NAME="Debian GNU/Linux"
    VERSION_ID="10"
    VERSION="10 (buster)"
    VERSION_CODENAME=buster
    ID=debian
    HOME_URL="https://www.debian.org/"
    SUPPORT_URL="https://www.debian.org/support"
    BUG_REPORT_URL="https://bugs.debian.org/"
    Connection to localhost closed.

7. Clean up

Let’s remove all the test containers:

root@vps ~ # for dir in /var/ds/server[1-5] ; do \
             cd $dir ; ds remove ; done

root@vps ~ # rm -rf /var/ds/server[1-5]

root@vps ~ # rm -rf /opt/docker-scripts/{ubuntu,debian,debian-buster,test}