How to run and manage containers using ctr
Basic container management with ctr
One of the main differences between ctr
and docker
UX is that the former requires doing more things explicitly and doesn’t allow you to take (many) shortcuts. For instance, with docker
, you can run a container without explicitly pulling its image first. With ctr
though, you’ll have to pull the image (specifying its full name, including the registry and the tag parts) and only then invoke the ctr run
command.
Compare the de-facto standard docker run nginx
with the following ctr
equivalent:
ctr image pull docker.io/library/hello-world:latest
ctr run docker.io/library/hello-world:latest hello1
Notice that unlike user-friendly docker run
that generates a unique ID for every container, with ctr
, you must supply a container ID yourself (hello1
in the above example).
The ctr run
command resembles the docker run
command but it doesn’t support all the flags you may be used to. For instance, you won’t be able to publish container ports or do something like --restart=always
. But it also can do things that docker run
can’t,
List containers
Back to basic container operations, you can list existing containers with:
ctr container ls
Inspect container
You can also inspect a container with ctr container info <container-id>
:
Remove container
Finally, you can remove a container with ctr container remove
ctr container remove hello1
Containers vs tasks
Interesting that the ctr run
command is actually a shortcut! It’s a combination of ctr container create
and ctr task start
. Let’s explore this behavior:
# Don't forget to pull the image first!
ctr container create docker.io/library/nginx:alpine nginx1
If you list the containers ctr container ls
, the output will be similar to the following:
CONTAINER IMAGE RUNTIME
nginx1 docker.io/library/nginx:alpine io.containerd.runc.v2
However, checking the running processes with pgrep nginx
will return nothing:
pgrep nginx
<empty>
As you can see, the container is created but no process is running inside it yet.
To make the Nginx container actually run, you’ll need to start a task:
ctr task start --detach nginx1
If you list the tasks now:
ctr task ls
…the output should be similar to the following:
TASK PID STATUS
nginx1 39928 RUNNING
I like this separation of container
and task
subcommands because it reflects the often forgotten nature of OCI containers. Despite the common belief, containers aren’t processes - containers are isolated and restricted execution environments for processes. So, in containerd, a container seems to be a configuration entity that describes the execution environment, while tasks represent the actual processes running inside of containers.
Note that at least with ctr
it doesn’t seem to be possible to have multiple tasks running for the same container simultaneously. You can always stop the running task and then start another one for the same container, but you can’t have two tasks running at the same time. In particular, it means that the task management commands we’ll see below accept container IDs as arguments, not task IDs.
Attaching to background tasks
The nginx1
task from the previous section runs in the background because the ctr task start
command was used with the --detach
flag. To see the stdout and stderr of a running task, you can attach to it with ctr task attach <container-id>
. Let’s try attaching to the nginx1
task:
ctr task attach nginx1
The output should be similar to the following:
...
2023/05/06 18:48:22 [notice] 1#1: using the "epoll" event method
2023/05/06 18:48:22 [notice] 1#1: nginx/1.23.4
2023/05/06 18:48:22 [notice] 1#1: built by gcc 10.2.1 20210110 (Debian 10.2.1-6)
2023/05/06 18:48:22 [notice] 1#1: OS: Linux 5.10.175
2023/05/06 18:48:22 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1024:1024
2023/05/06 18:48:22 [notice] 1#1: start worker processes
2023/05/06 18:48:22 [notice] 1#1: start worker process 29
2023/05/06 18:48:22 [notice] 1#1: start worker process 30
But be careful, the ctr task attach
command will also reconnect the stdin stream and start forwarding signals from the controlling terminal to the task processes, so hitting Ctrl+C
might kill the task.
Unfortunately, ctr
doesn’t support the Ctrl+P+Q
shortcut to detach from a task - it’s solely docker
’s feature. There is also no ctr task logs
, so you can’t see the stdout/stderr of a task without attaching to it. Neither you can easily see the logs of a stopped task. It’s a lower-level tool, remember?
Executing commands in containers
Much like in Docker, you can execute a command in a running container. Let’s revive the nginx1
task and execute a command inside the Nginx container:
ctr task start --detach nginx1
Here’s how you can get an interactive shell inside the nginx1 container using ctr task exec
:
ctr task exec -t --exec-id shell1 nginx1 sh
When you’re done exploring the inside of the container, you can exit the shell ending the shell1
exec session:
You can also execute a single command inside the container without getting an interactive shell. For instance, here’s how you can curl the Nginx container from the host:
ctr task exec --exec-id curl1 nginx1 curl 127.0.0.1:80
The output will be the standard Nginx welcome page:
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
...
Sending signals to tasks
It’s also possible to send signals to tasks, or rather to the processes running inside the tasks. For instance, here is how you can send a SIGTERM
signal to the Nginx process, effectively terminating it:
ctr task kill -s 9 nginx1
Interesting that when all container processes are terminated, the task may still exist:
ctr task ls
TASK PID STATUS
nginx1 2756 STOPPED
However, the ctr task ps nginx1
command will show that there are no processes running inside the task.
If your only goal for sending a signal is to terminate the task before removal, there might be a faster way to remove a running task - using the ctr task rm
with the --force
. In any case, let’s clean things up and remove the stopped nginx1
task:
ctr task rm nginx1
Source:
https://labs.iximiuz.com/courses/containerd-cli/ctr/container-management