You can use high-level Docker, Podman, nerdctl, or even containerd's ctr to run containers with comfort. However, if you want to get the real feel of what containers are, you should start at least one of them performing the main steps manually.
In this challenge, you will need to create, start, access, and then destroy an Nginx container using the runc
CLI.
The following steps are required to achieve this:
- Prepare a container root filesystem.
- Prepare a container configuration file.
- Create a container using the
runc
CLI. - Configure the container networking.
- Start the container using the
runc
CLI. - Make sure it's accessible from the host.
- Terminate the container and clean up the resources.
A lot of things to learn, so let's get started!
First things first - create a bundle directory that will hold the future container root filesystem and the configuration file.
Hint 1 π‘
Despite the fancy name, a bundle directory is just a regular folder.
Theoretically, it can be located anywhere on the filesystem,
but to make the solution checker happy, you should create it in your $HOME
directory.
Now, inside the bundle directory, create a configuration file of the future container.
Hint 2 π‘
The format of the configuration file is defined by the OCI Runtime Specification.
It's a JSON document that describes the container's runtime environment,
including the process to run inside the container, its arguments, environment variables, namespaces, and so on.
Even though it's relatively straightforward, it may get pretty lengthy,
so writing it from scratch is not the best idea.
Luckily, the runc spec
command can generate a template configuration file for you.
Similarly, inside the bundle directory, create a rootfs
subfolder with the files of the nginx:latest
image.
Hint 3 π‘
There are many ways to extract the files of an image into a folder.
For instance, you can use the docker export
command or
mount the image as a local directory using ctr
.
Hint 4 π‘
Still having trouble?
runc spec --help
has a decent example of how to create a bundle,
including the extraction of the image files into the rootfs
folder.
Your ultimate goal is to start an nginx
process inside the container.
To tell runc
what process to start, you need to specify its name and arguments in the configuration file.
Adjust the process.args
field of the config.json
file to make it point to the nginx
binary.
Hint 5 π‘
Not sure what to put in the process.args
field?
You can get some inspiration from the nginx
image's ENTRYPOINT
and CMD
directives.
docker inspect nginx:latest
will help you with that π
With the bundle ready, it's time to create a container using the runc
CLI!
Hint 6 π‘
runc create --help
is your friend π
Hint 7 π‘
Getting the cannot allocate tty
error?
Since nginx
is a typical server container, you can safely skip the terminal allocation.
Make sure to adjust the config.json
file accordingly.
Can you tell if the container is running yet? Find the PID of the process that you've just started. Is it Nginx?
Hint 8 π‘
Linux containers are "just" regular processes.
Can you spot the runc init
process in the output of the ps
command?
Hint 9 π‘
Much like docker ps
, you can use runc list
to get the list of running containers.
The runc create
command prepared a bunch of Linux namespaces and used the "stub" runc init
process to hold them until the Nginx process is started.
By default, a new container is fully isolated from the outside world,
and the only network interface that exists in its network namespace is the loopback (lo
).
To be able to access the container from the host,
configure the virtual Ethernet pair 192.168.0.1 <-> 192.168.0.2
and move its upper end into the container's network namespace.
Hint 10 π‘
Feeling lost? No worries, container networking is a large topic. Try solving Reproduce a Docker Bridge Network Using Basic Linux Commands challenge first.
Hint 11 π‘
There is a good chance that the ip netns
command won't see the network namespaces created by runc
.
Here is a trick to make it work:
mkdir -p /run/netns
ln -sT /proc/$RUNC_INIT_PID/ns/net /run/netns/$CONTAINER_ID
Finally, start the container!
Hint 12 π‘
Try running runc start
and follow the dynamic hints from the solution checker.
Hint 13 π‘
An attempt to start the container failed,
and now you're getting the cannot start a container that has stopped
error?
Remove and re-create the container.
But please use the same ID to make the solution checker happy.
Cleanup of the resources is an important part of the container lifecycle. Now, when the container served its purpose, it's time to terminate it.
Hint 14 π‘
What command is used in Linux to send a signal to a process?
runc
has an eponymous subcommand to signal containerized processes.
Has the bundle directory been removed as well? Make sure the disk space is not wasted on the unused container filesystems.
Level up your Server Side game β Join 9,000 engineers who receive insightful learning materials straight to their inbox