HashiQube - DevOps Lab
Youtube Channel Medium Posts Riaan Nolan Linkedin Riaan Nolan Hashicorp Ambassador

.

Docker

Docker Logo

Containerize applications for faster deployment and scaling

🚀 About

In this HashiQube DevOps lab, you'll get hands-on experience with Docker. You'll learn how to:

  • Build a custom Docker image from a Dockerfile
  • Run Docker containers
  • Configure a Docker daemon with authentication
  • Set up a Docker registry

This lab follows the official Docker installation guide for Ubuntu-based systems.

📋 Provision

Open in GitHub Codespaces

bash docker/docker.sh
vagrant up --provision-with basetools,docsify,docker
docker compose exec hashiqube /bin/bash
bash hashiqube/basetools.sh
bash docker/docker.sh
bash docsify/docsify.sh

📦 What You'll Build

When you run the provisioner, it will:

  1. Install Docker on your HashiQube environment
  2. Build an Apache 2.4 container from a Dockerfile
  3. Run the container and expose it to your host machine
  4. Make the container accessible at http://localhost:8889

🛠️ Dockerfile Walkthrough

Below is the Dockerfile used to build the Apache web server container:

FROM ubuntu:18.04

# Install dependencies
RUN apt-get update && \
 apt-get -y install apache2

# Install apache and write hello world message
RUN echo 'Hello World!' > /var/www/html/index.html

# Configure apache
RUN echo '. /etc/apache2/envvars' > /root/run_apache.sh && \
 echo 'mkdir -p /var/run/apache2' >> /root/run_apache.sh && \
 echo 'mkdir -p /var/lock/apache2' >> /root/run_apache.sh && \
 echo '/usr/sbin/apache2 -D FOREGROUND' >> /root/run_apache.sh && \
 chmod 755 /root/run_apache.sh

EXPOSE 80

CMD /root/run_apache.sh

This Dockerfile:

  • Uses Ubuntu 18.04 as the base image
  • Installs Apache web server
  • Creates a simple "Hello World" web page
  • Sets up a script to run Apache in the foreground
  • Exposes port 80 for web traffic
  • Specifies the command to run when the container starts

📊 Monitoring Docker

HashiQube includes Prometheus and Grafana for monitoring Docker containers.

For detailed information, see the Monitoring Docker guide.

🧩 Key Docker Concepts

Images vs Containers

  • Images: Read-only templates used to create containers
  • Containers: Runnable instances of images

Basic Docker Commands

# List running containers
docker ps

# List all containers (including stopped ones)
docker ps -a

# List available images
docker images

# Pull an image from Docker Hub
docker pull ubuntu:latest

# Run a container
docker run -d -p 8080:80 --name my-container nginx

# Stop a container
docker stop my-container

# Remove a container
docker rm my-container

# Remove an image
docker rmi nginx

🔌 Docker Networking

Docker creates isolated networks for containers by default. You can:

  • Create custom networks
  • Connect containers to multiple networks
  • Configure network settings
# Create a custom network
docker network create my-network

# Run a container connected to the custom network
docker run -d --network=my-network --name my-db postgres

# Connect an existing container to a network
docker network connect my-network my-container

📦 Docker Volumes

Volumes provide persistent storage for containers:

# Create a named volume
docker volume create my-data

# Run a container with a volume
docker run -d -v my-data:/data --name my-container ubuntu

# Mount a host directory as a volume
docker run -d -v /host/path:/container/path --name my-container ubuntu

🔍 Docker Provisioner

The script below automates the setup of Docker in your HashiQube environment:

#/bin/bash
# https://docs.docker.com/install/linux/docker-ce/ubuntu/
echo -e '\e[38;5;198m'"++++ "
echo -e '\e[38;5;198m'"Installing Docker Dependencies"
echo -e '\e[38;5;198m'"++++ "
sudo DEBIAN_FRONTEND=noninteractive apt-get update -qq < /dev/null > /dev/null
sudo DEBIAN_FRONTEND=noninteractive apt-get install -qq --assume-yes apt-transport-https ca-certificates curl gnupg-agent software-properties-common < /dev/null > /dev/null
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

arch=$(lscpu | grep "Architecture" | awk '{print $NF}')
if [[ $arch == x86_64* ]]; then
  ARCH="amd64"
elif  [[ $arch == aarch64 ]]; then
  ARCH="arm64"
fi
echo -e '\e[38;5;198m'"CPU is $ARCH"

echo -e '\e[38;5;198m'"++++ "
echo -e '\e[38;5;198m'"Installing Docker"
echo -e '\e[38;5;198m'"++++ "
sudo add-apt-repository -y "deb [arch=$ARCH] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
sudo apt-get update -qq < /dev/null > /dev/null
# BUG: error reopening /dev/null https://bugs.launchpad.net/ubuntu/+source/docker.io/+bug/1950071 so we pin docker-ce=5:20.10.16~3-0~ubuntu-focal and containerd.io=1.5.11-1
# BUG: https://github.com/containerd/containerd/issues/6203
# FIXED: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: error during container init: error reopening /dev/null inside container: open /dev/null: operation not permitted: unknown
sudo DEBIAN_FRONTEND=noninteractive apt-get install -qq --allow-downgrades --assume-yes docker-ce docker-ce-cli containerd.io docker-compose-plugin < /dev/null > /dev/null

echo -e '\e[38;5;198m'"++++ "
echo -e '\e[38;5;198m'"Set Docker Configs"
echo -e '\e[38;5;198m'"++++ "
sudo usermod -aG docker vagrant
sudo mkdir -p /etc/docker
# https://docs.docker.com/config/daemon/prometheus/
cat <<EOF | sudo tee /etc/docker/daemon.json
{
  "metrics-addr": "0.0.0.0:9323",
  "experimental": true,
  "storage-driver": "overlay2",
  "insecure-registries": ["10.9.99.10:5001", "10.9.99.10:5002", "localhost:5001", "localhost:5002"]
}
EOF

echo -e '\e[38;5;198m'"++++ "
echo -e '\e[38;5;198m'"Restart Docker Daemon"
echo -e '\e[38;5;198m'"++++ "
sudo service docker restart
cd /vagrant/docker

echo -e '\e[38;5;198m'"++++ "
echo -e '\e[38;5;198m'"Docker System prune"
echo -e '\e[38;5;198m'"++++ "
docker stop registry
docker rm registry
docker stop apache2
docker rm apache2
yes | sudo docker system prune -a
yes | sudo docker system prune --volumes

# echo -e '\e[38;5;198m'"++++ "
# echo -e '\e[38;5;198m'"Creating Private Docker Registry"
# echo -e '\e[38;5;198m'"++++ "
# # https://docs.docker.com/registry/deploying/#customize-the-published-port
# docker run -d --restart=always \
#   --name registry \
#   -v "$(pwd)"/auth:/auth \
#   -e "REGISTRY_AUTH=htpasswd" \
#   -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
#   -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
#   -e REGISTRY_HTTP_ADDR=0.0.0.0:5002 \
#   --memory 256M -p 5002:5002 registry:2

# cat <<EOF | sudo tee /etc/docker/auth.json
# {
#   "username": "admin",
#   "password": "password",
#   "email": "admin@localhost"
# }
# EOF

# echo -e '\e[38;5;198m'"++++ "
# echo -e '\e[38;5;198m'"++++ Docker Login to Registry"
# echo -e '\e[38;5;198m'"++++ "
# sleep 10;
# sudo --preserve-env=PATH -u vagrant docker login -u="admin" -p="password" http://10.9.99.10:5002

# echo -e '\e[38;5;198m'"++++ "
# echo -e '\e[38;5;198m'"++++ Docker build -t apache2 ."
# echo -e '\e[38;5;198m'"++++ "
# docker build -t apache2 .

# echo -e '\e[38;5;198m'"++++ "
# echo -e '\e[38;5;198m'"++++ Docker images --filter reference=apache2"
# echo -e '\e[38;5;198m'"++++ "
# docker images --filter reference=apache2

# echo -e '\e[38;5;198m'"++++ "
# echo -e '\e[38;5;198m'"++++ Docker run -t -d -i -p 8889:80 --name apache2 --rm apache2"
# echo -e '\e[38;5;198m'"++++ "
# docker run -t -d -i -p 8889:80 --name apache2 --memory 16M --rm apache2

echo -e '\e[38;5;198m'"++++ "
echo -e '\e[38;5;198m'"++++ Docker ps"
echo -e '\e[38;5;198m'"++++ "
docker ps

echo -e '\e[38;5;198m'"++++ "
echo -e '\e[38;5;198m'"++++ Docker stats"
echo -e '\e[38;5;198m'"++++ "
docker stats --no-stream -a

echo -e '\e[38;5;198m'"++++ "
echo -e '\e[38;5;198m'"++++ Docker Daemon installed"
echo -e '\e[38;5;198m'"++++ "
# echo -e '\e[38;5;198m'"++++ open http://localhost:8889 in your browser"
# echo -e '\e[38;5;198m'"++++ you can also run below to get apache2 version from the docker container"
# echo -e '\e[38;5;198m'"++++ vagrant ssh -c \"docker ps; docker exec -it apache2 /bin/bash -c 'apache2 -t -v; ps aux'\""

🔗 Additional Resources