Skip to main content

Docker Intro

1. Install

  1. Get Docker’s official GPG key:

    wget https://download.docker.com/linux/ubuntu/gpg \
    -O /etc/apt/keyrings/docker.asc
    ls -l /etc/apt/keyrings/docker.asc
  2. Add the docker sources to the apt list:

    cat <<EOF > /etc/apt/sources.list.d/docker.sources
    Types: deb
    URIs: https://download.docker.com/linux/debian
    Suites: bookworm
    Components: stable
    Signed-By: /etc/apt/keyrings/docker.asc
    EOF

    cat /etc/apt/sources.list.d/docker.sources
  3. Install:

    apt update
    apt install --yes \
    docker-ce \
    docker-ce-cli \
    containerd.io \
    docker-buildx-plugin \
    docker-compose-plugin
  4. Check:

    docker --version
    docker compose version
    systemctl status docker

    docker run hello-world

2. Testing

  1. Run a command:

    docker run \
    --name test1 \
    debian:12 \
    echo 'Hello World!'
    docker image ls
    docker ps
    docker ps -a
  2. Run a loop:

    docker run \
    --name test2 \
    debian:12 \
    /bin/sh -c "while true; do date; sleep 1; done"
    docket ps
    docker ps -a

    Note: Press Ctrl+c to stop it.

  3. Starting, stopping, checking the logs:

    docker start test1
    docker ps
    docker logs test1
    docker start test1
    docker ps
    docker logs test1

    docker start test2
    docker logs test2 -f
    docker ps
    docker stop test2
    docker ps
    docker ps -a
  4. Run a shell in a container:

    docker run --name test3 -i -t ubuntu bash
    cat /etc/os-release
    ps aux
    exit
    docker ps
    docker ps -a
    docker image ls
  5. Start a shell in a container:

    docker start test2
    docker ps
    docker exec -it test2 bash
    cat /etc/os-release
    ps aux
    apt update
    apt list procps
    apt install procps
    ps aux
    top
    free -h
    df -h .
    exit
  6. Some other commands:

    docker ps -a
    docker ps -a --format "{{.ID}}: {{.Names}}"

    docker image ls
    docker image history hello-world

    docker inspect test1 | less
    docker inspect test2 | less

    docker --help | less
    docker run --help | less
  7. Clean up:

    docker ps -a
    docker rm test1 test2 test3
    docker ps -a

    docker image ls
    docker rmi ubuntu debian:12 hello-world

3. Run a web server

  1. Get the image:

    docker search welcome-to-docker --limit=5
    docker pull docker/welcome-to-docker
    docker image ls
    docker image history docker/welcome-to-docker
  2. Start the container:

    docker run \
    --name nginx \
    -p 8081:80 \
    docker/welcome-to-docker

    Stop it with Ctrl+c. Then run it again with the option -d:

    docker rm nginx
    docker run -d \
    --name nginx \
    -p 8081:80 \
    docker/welcome-to-docker
    docker ps
    docker logs nginx -f
  3. Open in browser http://nginx.user1.fs.al:8081/

    Refresh the page and check the logs again.

    Stop the logs with Ctrl+c.

  4. Look inside:

    docker exec -it nginx /bin/sh
    cat /etc/os-release
    ps aux
    top
    ls /etc/nginx/
    ls /etc/nginx/conf.d/
    less /etc/nginx/conf.d/default.conf
    exit
  5. Clean up:

    docker stop nginx
    docker rm nginx
    docker image ls
    docker rmi docker/welcome-to-docker
caution

Did you notice that we could access the port 8081 although we didn't open it in the firewall? If we forward a port to a container, docker will make sure to open that port on the firewall. So, we should be careful when we forward a port to a container, because it will be available to the whole world.

firewall-cmd --list-all
firewall-cmd --get-active-zones
firewall-cmd --zone=docker --list-all
firewall-cmd --zone=docker --list-interfaces

There is no easy way to fix this issue. One workaround is to use the firewall of the VPS provider, in order to make sure that only the ports that we want are allowed. Docker cannot mess with that firewall.

Another way is to restrict port forwarding only to the localhost. For example, if we use -p 127.0.0.1:8081:80 instead of -p 8081:80, then the port 8081 can be accessed only from the localhost.

4. An openssh container

  1. Let's use the directory /var/docker/sshd for building and managing this container.

    mkdir -p /var/docker/sshd
    cd /var/docker/sshd/
  2. We can use a Dockerfile in order to build the image:

    cat <<'_EOF_' > Dockerfile
    # syntax=docker/dockerfile:1.4

    FROM debian:12

    RUN apt update && apt upgrade --yes
    RUN apt install --yes procps

    RUN <<EOF
    # install openssh-server
    apt install --yes openssh-server
    mkdir -p /var/run/sshd
    sed -i /etc/ssh/sshd_config \
    -e 's/^#PermitRootLogin .*/PermitRootLogin prohibit-password/'
    EOF

    WORKDIR /host
    CMD ["/usr/sbin/sshd", "-D"]

    _EOF_

    We are starting from the base image of debian:12 and are creating additional layers on it, by installing other things.

  3. Build the image:

    docker build -t sshd-image .
    docker image ls
  4. Create the container and start it:

    cat <<'_EOF_' > create.sh
    #!/bin/bash -x
    docker create \
    --name=sshd \
    --mount type=bind,source=$(pwd),destination=/host \
    --publish 2201:22 \
    --restart=unless-stopped \
    --hostname openssh \
    sshd-image
    _EOF_
    chmod +x create.sh

    docker ps -a
    docker start sshd
    docker ps
  5. Look inside the container:

    docker exec -it sshd bash
    ps aux
    top
    pwd
    ls -al
    exit
  6. Let's create the script setup-key.sh that generates an ssh key pair and makes sure that the public key is saved to ~/.ssh/authorized_keys inside the container:

    cat <<'_EOF_' > setup-key.sh
    #!/bin/bash -x

    # generate a key pair
    [[ -f /host/sshkey ]] \
    || ssh-keygen -t ecdsa -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
    _EOF_
    chmod +x setup-key.sh
  7. Let's copy this script to the /tmp dir inside the container, and then execute it:

    docker cp setup-key.sh sshd:/tmp/
    docker exec sshd ls -l /tmp/
    docker exec sshd /tmp/setup-key.sh

    It will generate an ssh key pair, if it does not exist:

    ls -al
    docker exec sshd rm /tmp/setup-key.sh
    info

    The option --mount type=bind,source=$(pwd),destination=/host that we used on the command docker create makes sure that the current directory is mounted to the director /host inside the container.

    Besides, the instruction WORKDIR /host in the Dockerfile sets the default working directory for the commands that are run by docker exec.

    These explain why the keys are generated on the directory /host inside the container, and why we can access them outside the container as well (on the system that is hosting the container).

  8. Let's also create a script that modifies ~/.bashrc:

    cat <<'_EOF_' > setup-bashrc.sh
    #!/bin/bash -x

    cat << 'EOF' | tee -a /root/.bashrc

    # make ls colorized
    export LS_OPTIONS='--color=auto'
    export SHELL='/bin/bash'
    alias ls='ls $LS_OPTIONS'
    alias ll='ls $LS_OPTIONS -l'
    alias l='ls $LS_OPTIONS -lA'

    # set a better prompt
    PS1='\n\[\033[01;33m\]\u@\[\033[01;32m\]\h\[\033[00m\]:\[\033[01;34m\]\w\[\e[32m\]\n==> \$ \[\033[00m\]'
    EOF

    _EOF_
    chmod +x setup-bashrc.sh
    nano setup-bashrc.sh
  9. Because the current directory is mounted to /host inside the container, and because /host is the default working directory for the container, we can execute the script like this:

    docker exec sshd pwd
    docker exec sshd ls -al
    docker exec sshd ./setup-bashrc.sh
    docker exec -it sshd bash
    ls
    exit
  10. Let's try to use the private key sshkey to ssh to the container, on the port 2201:

    ssh -p 2201 -i sshkey root@localhost
    whoami
    pwd

    We can try from outside the VPS, like this:

    ssh -p 2201 -i sshkey root@sshd.user1.fs.al

    Note: We have to copy/transfer the sshkey first.

    This verifies again that once we forward a port to a container, docker will make the forwarded port available to the whole world, despite the firewall that is installed on the VPS.

5. More examples

info

For more examples and a deeper understanding see: https://docs.docker.com/get-started/