bengineer.dev home

Crash course on Docker and Kubernetes

From chroot to orchestration

Dive into this guide that will take you from the basics of Docker to mastering Kubernetes. By the end of this journey, you'll have a solid understanding of how to create, manage, and orchestrate containers for your applications.

Getting Started with Docker

Pulling Docker Images

To start your container journey, the first step is pulling Docker images using the docker pull command:

docker pull <name>:<version>

Deep Images with Nested Virtualisation

Have you heard of nested virtualisation? It makes deep images seem like a great idea. Dive in and explore the possibilities.

Running an Ubuntu Container

To run an Ubuntu container interactively, use the following command:

docker run -it --name docker-host --rm --privileged ubuntu:bionic

Creating Containers without Docker

Chroot: Changing the Root Directory

chroot is an operation that changes the apparent root directory for the current running process and its children. You can set a new root to the current directory using:

chroot . bash

Additional Steps to Consider

Remember that mkdir blah/lib{,64} can create lib and lib64 directories. One issue to be aware of is that you can still see processes across running images using ps aux.

Connecting to a Running Container

To connect to a running container, use:

docker exec -it <name> bash

Setting Up Chroot Environment

Installing debootstrap

To create a chroot environment easily, you can use debootstrap:

debootstrap --variant=minbase bionic /better-root

Hiding external resources from Chroot

To hide external resources from from chroot, you can use unshare

unshare --mount --uts --ipc --net --pid --fork --map-root-user chroot /better-root bash

Setting Up a Raw Ubuntu Environment

Here's how you can get started with a raw Ubuntu environment:

apt-get update \
&& apt-get install -y debootstrap cgroup-tools htop \
&& debootstrap --variant=minbase bionic /better-root \
&& cgcreate -g gpu,memory,blkio,devices,freezer:/sandbox \
&& unshare --mount --uts --ipc --net --pid --fork --map-root-user chroot /better-root bash

Control Groups for Resource Limitation

Control groups (cgroup-tools) allow you to limit resources like memory and CPU per chroot:

Running Processes in the Background

Running processes indefinitely can be achieved using commands like hack yes or yes > /dev/null to run processes in the background.

Using Docker for Similar Tasks

Connecting to an Image and Chrooting

To connect to an existing Docker image and chroot into it, follow these steps:

  1. Connect to any image:
docker run --rm -dit --name my-alpine alpine:3.10 sh
  1. Dump container state:
docker export -o dockercontainer.tar my-alpine
  1. Unzip into a folder to explore:
mkdir container-root
tar xf dockercontainer.tar container-root
  1. Connect to it as a chroot (use ash for Alpine):
unshare --mount --uts --ipc --net --pid --fork --map-root-user chroot /better-root ash

Running Images with Docker

Running Docker images with various useful flags:

docker run -it alpine:3.10
docker image prune # Clean up dangling images

Useful Docker Run Flags

| Flag | Description | |-----------------------|----------------------------------------------| | --interactive, -i | Keep STDIN open even if not attached | | --tty, -t | Allocate a pseudo-TTY | | --detach, -d | Run in the background | | --name | Set the name of the image | | -rm | Automatically remove the container on exit |

Other Docker commands and tips include:

Building Docker Containers

Example Dockerfile

Creating a Dockerfile to build a Node.js application:

FROM node:20-alpine

CMD ["node", "-e", "console.log('omg hello!')"]

To build an image:

docker build .
docker build --tag my-node-app .

The --init flag is a hack to allow stopping containers without setting up sigterm (do it in the Dockerfile).

Node.js Application Example

A Node.js application that listens on port 3000:

const http = require('http');

process.on('SIGTERM', () => process.exit());

http
    .createServer((req, res) => {
        console.log('request received');
        res.end('omg hello!', 'utf-8');
    })
    .listen(3000);

console.log('started!');

Dockerfile for the Node.js application:

FROM node:20-alpine

COPY index.js index.js

CMD ["node", "index.js"]

To run the application in a Docker container:

docker run --init --rm --publish 3000:3000 my-node-app

Managing Containers: Bind Mounts and Volumes

Bind Mounts

Bind mounts allow you to share files between your local file system and a container. Simply specify the directory to mount when running a container. For example:

docker run -v /local/path:/container/path my-image

Volumes

Volumes are managed by Docker and are suitable for production use. To use volumes, specify the --mount flag:

docker run --mount type=volume,src=volume-name,dst=/container/path my-image

Building Development Environments

Docker makes it easy to create development environments using .devcontainer/Dockerfile and devcontainer.json configurations. Customize your development environment with necessary tools and settings.

Networking with Docker

Docker provides multiple networking options. You

can create your Docker network using docker network create and connect containers to it. Alternatively, use Docker Compose to manage multiple containers' networking.

Docker Compose: Orchestrating Containers

Docker Compose allows you to define and run multi-container applications. Use a docker-compose.yml file to specify services, volumes, and networks. For instance, a basic setup might look like this:

version: '3.8'

services:
  web:
    build: .
    ports:
      - "3000:3000"
    volumes:
      - .:/home/node/code
      - /home/node/code/node_modules
    links:
      - db
    environment:
      - MONGO_CONNECTION_STRING=mongodb://db:27017
  db:
    image: mongo:3

You can scale containers using docker-compose up --scale and set up load balancing with Nginx.

Kubernetes: Container Orchestration

Kubernetes is a powerful container orchestration tool that provides:

To start using Kubernetes:

  1. Install kubectl and kompose (for converting Docker Compose to Kubernetes).

  2. Create your Kubernetes configuration files or convert your Docker Compose file using kompose.

  3. Apply your configuration to Kubernetes using kubectl apply.

  4. Manage your Kubernetes clusters with kubectl.

Non-Docker Containers: OCI Containers, Buildah, and Podman

Check out other containerization technologies like OCI containers, Buildah, and Podman as alternatives to Docker.

Key Takeaways

For more details and code examples, check out the course I followed to get here complete guide repository and the documentation for it.