Files
container.training/slides/exercises/container-from-scratch-details.md
2025-09-08 15:31:47 +02:00

3.3 KiB

Exercise — Build a container from scratch

Our goal will be to execute a container running a simple web server.

(Example: NGINX, or https://github.com/jpetazzo/color.)

We want the web server to be isolated:

  • it shouldn't be able to access the outside world,

  • but we should be able to connect to it from our machine.

Make sure to automate / script things as much as possible!


Steps

  1. Prepare the filesystem

  2. Run it with chroot

  3. Isolation with namespaces

  4. Network configuration

  5. Cgroups

  6. Non-root


Prepare the filesystem

  • Obtain a root filesystem with one of the following methods:

    • download an Alpine mini root fs

    • export an Alpine or NGINX container image with Docker

    • download and convert a container image with Skopeo

    • make it from scratch with busybox + a static jpetazzo/color

    • ...anything you want! (Nix, anyone?)

  • Enter the root filesystem with chroot


Help, network does not work!

  • Check that you have external connectivity from the chroot:

    ping 1.1.1.1
    

    (that should work; if it doesn't, we have a serious problem!)

  • Check that DNS resolution works:

    ping enix.io
    
  • If you're having a DNS resolution error, configure DNS in the container:

    echo nameserver 1.1.1.1 > /etc/resolv.conf
    

Running a web server

Here are a few possibilities...

  • Install the NGINX package and run it with nginx

    (note: by default it will start in the background)

  • Run NGINX in the foreground with nginx -g "daemon off;"

  • Install the package Caddy and run caddy file-server -ab

    (it will remain in the foreground and show logs; RECOMMENDED)

  • Download and/or build https://github.com/jpetazzo/color

    (if you're familiar with the Go ecosystem!)


Run with chroot

  • Start the web server from within the chroot

  • Confirm that you can connect to it from outside

  • Write a script to start our "proto-container"


Isolation with namespaces

  • Now, enter the root filesystem with unshare

    (enable all the namespaces you want; maybe not user yet, though!)

  • Start the web server

    (you might need to configure at least the loopback network interface!)

  • Confirm that we cannot connect from outside

  • Update the our start script to use unshare

  • Automate network configuration

    (pay attention to the fact that network tools may not exist in the container)


Network configuration

  • While our "container" is running, create a veth pair

  • Move one veth to the container

  • Assign addresses to both veth

  • Confirm that we can connect to the web server from outside

    (using the address assigned to the container's veth)

  • Update our start script to automate the setup of the veth pair

  • Bonus points: update the script to that it can start multiple containers


Cgroups

  • Create a cgroup for our container

  • Move the container to the cgroup

  • Set a very low CPU limit and confirm that it slows down the server

    (but doesn't affect the rest of the system)

  • Update the script to automate this


Non-root

  • Switch to a non-privileged user when starting the container

  • Adjust the web server configuration so that it starts

    (non-privileged users cannot bind to ports below 1024)