The Docker Container Lifecycle | Docker made easy #3
Tags: docker, container, devops, cloudnative
Published: 2021-09-05 17:07:35

Link to video: https://www.youtube.com/watch?v=ifvrfriMDaA 

---

> Learn how a Container works based on its Lifecycle state and the Docker cli commands to manage them effectively

---

Have you ever felt overwhelmed by the multitude of `docker` cli commands?

Have you ever been confused about what's actually happening with your Docker Containers?

If you have, don't worry... you're not alone. 🤗

How a Container behaves depends on which state it is in.

Luckily, once you understand how a Container works based on its Lifecycle state, you will be WAY more confident with Docker.

It will also be much easier for you to switch to more advanced Container management solutions like Docker Compose, Docker Swarm, Kubernetes, or OpenShift, etc.

So, let's learn about the Container Lifecycle, once and for all!

## Introduction

In this blog, we will:

- learn about the different states a container can be in
- learn how to manage them using the `docker` cli
- clear confusions regarding the differences between
    - `docker pause` and `docker stop`
    - `docker stop` and `docker kill`
- end with a detailed view of the container lifecycle

We'll also discuss POSIX signals briefly.

If you haven't read the first two blogs on the 'Docker made easy' series, I highly recommend you do so.

- [#1 - what is docker](https://medium.com/techmormo/what-is-docker-docker-made-easy-part-1-8cd5a0a26907)
- [#2 - how docker images work](https://medium.com/techmormo/how-do-docker-images-work-docker-made-easy-part-2-91d5c1a8d8a6)

If you can't access Medium, try these:

- [#1 - what is docker](https://techmormo.com/posts/docker-made-easy-1-what-is-docker/)
- [#2 - how docker images work](https://techmormo.com/posts/docker-made-easy-2-how-docker-images-work/)


Alright, let's begin by looking at...

## The Container Lifecycle

The following diagram shows the states of a simplified Container Lifecycle, which determines how a Container behaves.

![simple container lifecycle](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w9xow4a3vys1um5wtghh.png)

simplified container lifecycle state diagram

The text on the arrows corresponds to the `docker` commands which allow that state transition.

As shown above, a container can be in the following states:

1. Created
2. Running
3. Paused
4. Stopped
5. Deleted

Before we dive into each state, we need to know a tiny bit about POSIX signals.

### Quick Overview of POSIX signals

Simply put, signals as a standard way that an Operating System (OS) tells a child process how to behave.

There are several signals, each having different purposes. But for us, we'll just focus on the following 2:

- `SIGTERM`: this signal is sent to a process to request its termination. It can be caught and interpreted or ignored by the process. This allows the process to perform graceful termination releasing resources and saving state if appropriate.
- `SIGKILL`: this signal is sent to a process to cause it to terminate immediately. Unlike  `SIGTERM`, this signal cannot be caught or ignored, and the receiving process cannot perform any clean-up upon receiving this signal (excluding few exceptions).

`SIGTERM` is usually preferred over `SIGKILL` since it provides a chance for graceful/safe termination of a process.

If interested, learn more about signals [here](https://en.wikipedia.org/wiki/Signal_(IPC)).

---

Now let's discuss each state of the container lifecycle one by one...

### Created state

This is the first state of the container lifecycle which comes after acquiring or building an Image. 

Read the last blog to learn more about how Docker Images work.

Using the `docker create` command, a thin writeable layer is created over the specified Image and prepared to run the main process command (`CMD` and/or `ENTRYPOINT`). 

NOTE: *On this state, the container is created but not started.*

Here's an example of creating a container using the `nginx:alpine` Image and naming it `app1`.

```docker
docker create --name app1 nginx:alpine
```

The id of the new container is printed if successfully created.

### Running state

As the name suggests, this is the state where the container is actively running. Meaning, the main process has started execution.

For a container that is created (using `docker create`) or stopped, it can be started using `docker start`.

So, to run app1 we can use -

```docker
docker start app1
```

If you don't want to explicitly `create` and then `start` a container, you can do both steps at once using the `docker run`. 

For example -

```docker
docker run -d --name app2 nginx:alpine
```

The command above creates and starts a container named `app2` in the background (as specified by `-d`).

### Paused state

A running container can be paused using the `docker pause` command. This has the effect of suspending (or freezing) all processes in the specified container.

*When paused, the state of the container stays intact - both the disk (file-system) portion and the memory (RAM) portion.*

NOTE: A container that is paused is unaware that it has been paused.

So, if we wanted to pause app1, we'd simply do -

```docker
docker pause app1
```

Similarly, to get the paused container back to running, we'd use `unpause` -

```docker
docker unpause app1
```

Implementation detail (beyond scope): to implement `pause`, the `freezer cgroup` is used instead of POSIX signals.

### Stopped state

A container that is stopped doesn't have its main processes actively running (I know, Duh 🤷‍♀️). 

When stopped, the disk portion of the state is persisted i.e. saved.

*But unlike when paused, the memory portion of the state is cleared when a container is stopped. This is the main difference between the paused and stopped states.*

A container can be stopped in 4 primary ways:

1. Using the `docker stop` command
2. Using the `docker kill` command
3. When the main container process has exited/completed
4. When 'Out Of Memory Exception' (OOME) is encountered

Let's discuss each individually -

**1. Using the `docker stop` command**

This command is as simple as -

```docker
docker stop app1
```

When executed, the main container process receives a `SIGTERM` signal (by default) and after a grace period (default 10s as of writing), it receives a `SIGKILL` signal.

**2. Using the `docker kill` command**

When you run -

```docker
docker kill app2
```

the `SIGKILL` signal is directly sent to the main container process (default behavior).

*This means the difference between `docker stop` and `docker kill` is that - `stop` can allow safe termination (within the grace period) while `kill` terminates immediately.*

For this reason, `stop` is preferred over `kill`. Review **'Quick Overview of POSIX signals'** above if you're confused.

It's important to note that, `docker kill` can also be used to issue any signal to the container process using the `—signal`/`-s` argument, not just `SIGKILL`.

Here's an example of issuing the `SIGINT` signal -

```docker
docker kill -s SIGINT app1
```

**3. When the main process has exited/completed**

A container can stop automatically if its main process has exited/completed. 

This can happen if 

- the main process runs into an exception/interrupt
- or if the task completes after a certain point in time (instead of running in an infinite loop like a server)

Here's a very simple example -

```docker
docker run alpine echo "hi"
```

This command runs the `echo "hi"` command inside an alpine container, prints "hi" to the console, and exits immediately after the `echo` command is done. 

**4. When 'Out Of Memory Exception' (OOME) is encountered**

If your containers attempt to use more memory than the system has available, you may experience an Out Of Memory Exception (OOME). As a result, some containers or even the Docker daemon might be killed by the kernel OOM killer.

Therefore, you should ensure that your application runs on hosts with adequate memory in production.

The simplest way of managing memory is by limiting the maximum amount of memory a container can use using the `-m` option -

```docker
docker run -m 50m redis:alpine
```

This runs a container from the `redis:alpine` Image with a memory limit of 50MB.

For learning more about managing memory and mitigating risks associated with OOME, refer to the [official documentation](https://docs.docker.com/config/containers/resource_constraints/#understand-the-risks-of-running-out-of-memory).

### Deleted state

A container that is in the created state or stopped can be removed with `docker rm`. This will result in the removal of all data associated with the container like the processes, file system, volume & network mappings, etc.

If app1 is stopped, for example, you can remove it like -

```docker
docker rm app1
```

If however the container is running or paused and you attempt to remove it with `docker rm`, you will get an error response from the daemon saying the container needs to be stopped.

If you are sure that your running container doesn't have any associated data which needs proper cleanup, then you can perform force removal using `-f` -

```docker
docker rm -f app1
```

But it is recommended not to do this, especially if the data integrity of the container is important - like the case for databases.

## Advanced Container Lifecycle

Here is a detailed diagram of the Container Lifecycle we just discussed including events in rectangles, courtesy of [Docker Saigon](http://docker-saigon.github.io/post/Docker-Internals/).

![advanced container lifecycle](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2tya7uvwg5aw4d9wu2tu.png)

This should be easier to understand if you have read the preceding discussion.

Or do you think I should have started with this instead of the simplified one? Interested to hear what you think.

## Conclusion

Today we learnt about the different states a Container can be in - the Container Lifecycle, as well as how to use the `docker` cli to maneuver through those states.

Once we know the basics of Containers, we can do really powerful stuff with more advanced tools like Docker Compose, Docker Swarm, Kubernetes, or OpenShift, etc.

Containers are awesome, and we're just getting started! 

On the next docker blog, we'll discuss how Volumes work on Docker.

Till then, be bold and keep learning.

But most importantly...

Tech Care! 👋