Cloud Native 5 Minutes at a Time: Creating, Observing, and Deleting Containers

Eric Gregory - January 24, 2022
image

One of the biggest challenges for implementing cloud native technologies is learning the fundamentals — especially when you need to fit your learning in a busy schedule.

In this series, we’ll break down core cloud native concepts, challenges, and best practices into short, manageable exercises and explainers, so you can learn five minutes at a time. These lessons assume a basic familiarity with the Linux command line and a Unix-like operating system — beyond that, you don’t need any special preparation to get started.

Table of Contents

  1. What is a Container?
  2. Creating, Observing, and Deleting Containers←You are here
  3. Build Image from Dockerfile
  4. Using an Image Registry
  5. Volumes and Persistent Storage
  6. Container Networking and Opening Container Ports
  7. Running a Containerized App
  8. Multi-Container Apps on User-Defined Networks

In the last lesson, we learned how containers work and installed a container engine. Now that we have Docker installed, let’s create a container. In your terminal, enter:

docker container run alpine echo Hello World

This command will call up Docker Engine and ask it to create a new container with container run.

We’re telling it to use a container image (more about those in our next lesson) called alpine which gives our container the binaries and libraries of a very lightweight Linux distribution as its foundation.

The next parameters specify a process to run within our new container: the system command echo which outputs strings. “Hello World” is an argument passed to echo — the string we would like to output.

When we run this command, Docker should download the alpine image and then run the process, producing terminal output like this:

Unable to find image 'alpine:latest' locally
latest: Pulling from library/alpine
59bf1c3509f3: Already exists 
Digest: sha256:21a3deaa0d32a8057914f36584b5288d2e5ecc984380bc0118285c70fa8c9300
Status: Downloaded newer image for alpine:latest
Hello World

Those using Docker Desktop may open the application and find the new container represented (with a randomly generated name) in the graphical user interface.

Now let’s get a little more advanced and create a container with a continuously running process. We’ll use the same basic command structure as before, but this time we’ll run the ping command inside our container:

docker container run alpine ping localhost

We’ll notice a few differences this time. First, we already had the alpine image on our machine, so the container started immediately. Second, the command doesn’t finish the way echo did. The ping process is ongoing, continuously checking network connectivity. We can stop the process by pressing CTRL+C.

Observing containers

After stopping the process, let’s take a look at the containers on our system using the terminal. We can already see this in Docker Desktop if we’re on Mac or Windows, but it’s useful to be able to bring up this information quickly in the terminal.

docker container ls -a

Since we used the -a flag, this will list all of our containers, whether they are running or not — we should see both our echo and our ping containers, along with some useful information such as their names and container IDs. Make a note of the ping container’s ID. Mine, for example, is 5480aa85d1c5.

Now let’s restart our ping container. Using the start command will run the process separately from our current shell, so we can enter other commands while the container process runs. You’ll replace 5480aa85d1c5 below with the ID of your container.

docker container start 5480aa85d1c5

The terminal will return the container ID to let us know that it’s running. We can also use the ls command without the -a flag:

docker container ls

This shows us only running containers. Our ping container should be listed here, but not the echo container.

Before we finish up, let’s inspect our container a little more closely in order to better understand what’s happening under the hood. If we use the top command with a running container ID, we’ll get information about the processes running within the container from the perspective of the host system:

docker container top 5480aa85d1c5

This gives us output looking something like this:

UID     PID     PPID     C    STIME    TTY   TIME       CMD
root    3509    3481     0    16:17    ?     00:00:00   ping localhost

The second column, PID, indicates the process identifier number for our containerized ping. This is the number the host machine’s operating system kernel is using to keep track of processes running on the machine, and in my case we can see that it is one of thousands. So we see that the ping process is using the host OS kernel, and we see how the kernel perceives the process: number 3509 in a long list.

But we said before that one way containers isolate themselves is through kernel namespaces: the way processes signify themselves relative to the kernel within the container. Metaphorically, we can think of this as the way the process sees itself versus the way the wider world sees it. Let’s take a look at the container from its own perspective:

docker container exec 5480aa85d1c5 ps

This gives us information on the processes running within the container from its perspective. In the container’s kernel namespaces, the ping process is PID 1. We’ll also see a listing for the ps command we just ran within the container. And that’s it!

The ping running as PID 1 within the container is the very same process as the one signified by PID 3509 on the wider system, but because the container has its own isolated kernel namespaces, its system doesn’t see the wider world outside.

Removing containers

Now let’s clean up. We can stop our running container (again, replacing the numeric ID with your own) using:

docker container stop 5480aa85d1c5

Now we can clean up using:

docker container rm 5480aa85d1c5

It’s an important piece of system hygiene to remove unused containers. Make sure you remember to remove the echo container as well — you can use the ls command with the -a flag to retrieve its id.

In the next lesson, we’ll take a closer look at container images.

{
  "$experimentIndex": 0,
  "$variantIndexes": [
    0
  ],
  "$activeVariants": [
    "OriginalVariant"
  ],
  "$classes": [
    "exp-alternate-ad-placement-0"
  ],
  "name": "alternate-ad-placement",
  "experimentID": "ca62VGC4QDaNqECV8gH-kg",
  "variants": [
    "OriginalVariant",
    "AltVariant"
  ]
}