Skip to content

Commit

Permalink
Update 2024 (#1)
Browse files Browse the repository at this point in the history
* Fix repo url

* Trigger site build

* Modify tree

* Fix navigation

* Add image building part

* Add images

* Fix typo

* Add installation guide

* Fix command line

* Fix post-installation steps

* Add health checks section

* modified persisted data section

* corrected typos

* Update sections order

* modified networking section

* merge mess

* merge fix

* Restore env variables section

* fix errors in docker image

* add ci tutorial

* Fix baltig remote url

* Fix typo

* Add section on gitlab runner

* Fix syntax error

* Update image

* modified networking and data challenges

* Update home page

* Add lab challenge

* Fix typo

* corrected typo in volumes lab challenge

* Update intro, container, gui (#2)

* Enable code copy

* Fix gitlab cli installation

* Update gitlab runner section

* Fix multi-stage Dockerfile

* Fix multi-stage Dockerfile

* fixed typo in networking.md (#3)

* Dagger.io example (#4)

* Add compose section

* Add "#include <stdio.h>" to hello.c example (#5)

---------

Co-authored-by: Stefano Nicotri <[email protected]>
Co-authored-by: dciangot <[email protected]>
Co-authored-by: ciangottini <[email protected]>
Co-authored-by: Stefano Nicotri <[email protected]>
Co-authored-by: dciangot <[email protected]>
Co-authored-by: Lisa <[email protected]>
  • Loading branch information
7 people authored Sep 4, 2024
1 parent b626eac commit 4da3f8b
Show file tree
Hide file tree
Showing 54 changed files with 2,447 additions and 593 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# docker-tutorial

[https://maricaantonacci.github.io/docker-tutorial/](https://maricaantonacci.github.io/docker-tutorial/)
[https://infn-bari-school.github.io/docker-tutorial/](https://infn-bari-school.github.io/docker-tutorial/)

1 change: 1 addition & 0 deletions docs/compose/howto.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Todo
1 change: 1 addition & 0 deletions docs/compose/intro.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Todo
16 changes: 11 additions & 5 deletions docs/container/env_vars.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,16 @@ When we launch our Docker container, **we can pass environment variables as key-
For instance, let's execute the following command:

```
$ docker run --rm --env VARIABLE1=foobar alpine env
docker run --rm --env VARIABLE1=foobar alpine env
```

!!! tip
Docker Alpine is the “Dockerized” version of Alpine Linux, a Linux distribution known for being exceptionally lightweight and secure. For these reasons and others, Docker Alpine is a popular choice for developers looking for a base image on which to create their own containerized apps.

The environment variables we set will be printed to the console:

```
...
VARIABLE1=foobar
```

Expand All @@ -37,6 +41,7 @@ docker run --rm --env VARIABLE2 alpine env
And we can see Docker still picked up the value, this time from the surrounding environment:

```
...
VARIABLE2=foobar2
```

Expand All @@ -50,20 +55,21 @@ Let's define a few variables in a file we'll call _my-env.txt_:


```
$ echo VARIABLE1=foobar1 > my-env.txt
$ echo VARIABLE2=foobar2 >> my-env.txt
$ echo VARIABLE3=foobar3 >> my-env.txt
echo VARIABLE1=foobar1 > my-env.txt
echo VARIABLE2=foobar2 >> my-env.txt
echo VARIABLE3=foobar3 >> my-env.txt
```

Now, let's inject this file into our Docker container:

```
$ docker run --env-file my-env.txt alpine:3 env
docker run --env-file my-env.txt alpine env
```

Finally, let's take a look at the output:

```
...
VARIABLE1=foobar1
VARIABLE2=foobar2
VARIABLE3=foobar3
Expand Down
237 changes: 119 additions & 118 deletions docs/container/example.md

Large diffs are not rendered by default.

197 changes: 197 additions & 0 deletions docs/container/health_checks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
Health checks (available since Docker 1.12) allow a container to expose its workload’s availability. This stands apart from whether the container is running. If your database goes down, your API server won’t be able to handle requests, even though its Docker container is still running.

When a health check command is specified, it tells Docker how to test the container to see if it's working. With no health check specified, Docker has no way of knowing whether or not the services running within your container are actually up or not.

Health checks can be configured in different ways:

- directly in the docker image (Dockerfile)
- when you launch your standalone container with `docker run` (we will cover this case below) or in your docker compose file

In all cases, the health check is configured as a command that the docker daemon will execute every 30 seconds (default interval that can be overriden). Docker uses the command’s exit code to determine your container’s healthiness:

* 0 – The container is healthy and working normally.
* 1 – The container is unhealthy; the workload may not be functioning.
* 2 – This status code is reserved by Docker and should not be used.

Without health checks, a simple `docker ps` would report the container as available. Adding a health check extends the `docker ps` output to include the container’s true state.

## Health Check Configuration

There are a few options that we can use to customize our health check instruction:

- interval - DURATION (default: 30s)
- timeout - DURATION (default: 30s)
- start-period - DURATION (default: 0s)
- retries - DURATION (default: 3)

where

- **interval** (option: `--health-interval`, default: 30s) - specifies the time between the health check for the application container. it waits for the specified time from one check to another.

- **timeout** (option: `--health-timeout`, default: 30s) - specifies the time that the health check waits for a response to consider the status of the container. For example, if we define 30 seconds and our server doesn’t respond within 30 seconds, then it’s considered as failed.

- **start-period** (option: `--health-start-period`, default: 0s) - specifies the number of seconds the container needs to start; health check will wait for that time to start.

- **retries** (option: `--health-retries`, default: 3) - specifies the number of consecutive health check failures required to declare the container status as unhealthy. Health check will only try up to the specified retry number. If the server fails consecutively up to the specified times, it is then considered unhealthy.

## Example

We will now add a health check to our `nginx` container: the check will be implemented using [`curl`](https://curl.se/docs/manpage.html) command `curl --fail http://localhost`:

```
docker run --name nginx -d --health-cmd='curl --fail http://localhost:80 || exit 1' nginx
```

The `curl` command makes a request to `localhost:80` and if the request returns the http code 200, it will return exit code 0; otherwise, it will return exit code 1.

Look at the container status using `docker ps`. The container can have three states:

* starting – Initial status when the container is still starting
* healthy – If the command succeeds then the container is healthy
* unhealthy – If a single run of the command takes longer than the specified timeout then it is considered unhealthy. If a health check fails, retries will be run for the requested number of times and the container status will be declared unhealthy if the check still fails.

```
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
75beb1db3d27 nginx "/docker-entrypoint.…" 9 seconds ago Up 3 seconds (health: starting) 80/tcp nginx
```

Initially, it will take some time to start the health check and update, whether the application is healthy or not.
After the start period, if the application is healthy you will get:

```
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
75beb1db3d27 nginx "/docker-entrypoint.…" 46 seconds ago Up 44 seconds (healthy) 80/tcp nginx
```

You can have a look at the container log and see the requests made to check the health status of the application:

```
docker logs nginx
```
```
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
/docker-entrypoint.sh: Sourcing /docker-entrypoint.d/15-local-resolvers.envsh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
/docker-entrypoint.sh: Configuration complete; ready for start up
2023/09/06 06:44:20 [notice] 1#1: using the "epoll" event method
2023/09/06 06:44:20 [notice] 1#1: nginx/1.25.2
2023/09/06 06:44:20 [notice] 1#1: built by gcc 12.2.0 (Debian 12.2.0-14)
2023/09/06 06:44:20 [notice] 1#1: OS: Linux 5.15.0-82-generic
2023/09/06 06:44:20 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
2023/09/06 06:44:20 [notice] 1#1: start worker processes
2023/09/06 06:44:20 [notice] 1#1: start worker process 28
2023/09/06 06:44:20 [notice] 1#1: start worker process 29
127.0.0.1 - - [06/Sep/2023:06:44:49 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.88.1" "-"
127.0.0.1 - - [06/Sep/2023:06:45:20 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.88.1" "-"
127.0.0.1 - - [06/Sep/2023:06:45:51 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.88.1" "-"
127.0.0.1 - - [06/Sep/2023:06:46:22 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.88.1" "-"
127.0.0.1 - - [06/Sep/2023:06:46:53 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.88.1" "-"
127.0.0.1 - - [06/Sep/2023:06:47:25 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.88.1" "-"
127.0.0.1 - - [06/Sep/2023:06:47:55 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.88.1" "-"
```

As you can see, the check runs every 30s (default) but you can change the interval with the option `--health-interval`.

Now let's remove the `index.html` file that nginx serves on http://localhost in order to simulate an application failure:

```
docker exec nginx sh -c 'mv /usr/share/nginx/html/index.html /usr/share/nginx/html/index.html.1'
```

Look again at the log:

```
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
/docker-entrypoint.sh: Sourcing /docker-entrypoint.d/15-local-resolvers.envsh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
/docker-entrypoint.sh: Configuration complete; ready for start up
2023/09/06 06:44:20 [notice] 1#1: using the "epoll" event method
2023/09/06 06:44:20 [notice] 1#1: nginx/1.25.2
2023/09/06 06:44:20 [notice] 1#1: built by gcc 12.2.0 (Debian 12.2.0-14)
2023/09/06 06:44:20 [notice] 1#1: OS: Linux 5.15.0-82-generic
2023/09/06 06:44:20 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
2023/09/06 06:44:20 [notice] 1#1: start worker processes
2023/09/06 06:44:20 [notice] 1#1: start worker process 28
2023/09/06 06:44:20 [notice] 1#1: start worker process 29
127.0.0.1 - - [06/Sep/2023:06:44:49 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.88.1" "-"
127.0.0.1 - - [06/Sep/2023:06:45:20 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.88.1" "-"
127.0.0.1 - - [06/Sep/2023:06:45:51 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.88.1" "-"
127.0.0.1 - - [06/Sep/2023:06:46:22 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.88.1" "-"
127.0.0.1 - - [06/Sep/2023:06:46:53 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.88.1" "-"
127.0.0.1 - - [06/Sep/2023:06:47:25 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.88.1" "-"
127.0.0.1 - - [06/Sep/2023:06:47:55 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.88.1" "-"
127.0.0.1 - - [06/Sep/2023:06:48:26 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.88.1" "-"
127.0.0.1 - - [06/Sep/2023:06:48:57 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.88.1" "-"
2023/09/06 06:49:27 [error] 29#29: *10 directory index of "/usr/share/nginx/html/" is forbidden, client: 127.0.0.1, server: localhost, request: "GET / HTTP/1.1", host: "localhost"
127.0.0.1 - - [06/Sep/2023:06:49:27 +0000] "GET / HTTP/1.1" 403 153 "-" "curl/7.88.1" "-"
2023/09/06 06:49:58 [error] 29#29: *11 directory index of "/usr/share/nginx/html/" is forbidden, client: 127.0.0.1, server: localhost, request: "GET / HTTP/1.1", host: "localhost"
127.0.0.1 - - [06/Sep/2023:06:49:58 +0000] "GET / HTTP/1.1" 403 153 "-" "curl/7.88.1" "-"
2023/09/06 06:50:29 [error] 29#29: *12 directory index of "/usr/share/nginx/html/" is forbidden, client: 127.0.0.1, server: localhost, request: "GET / HTTP/1.1", host: "localhost"
127.0.0.1 - - [06/Sep/2023:06:50:29 +0000] "GET / HTTP/1.1" 403 153 "-" "curl/7.88.1" "-"
```

The `curl` command now returns the http error code 403 and therefore the check returns an exit code 1. Check the status of the container with `docker ps`:

```
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
75beb1db3d27 nginx "/docker-entrypoint.…" 6 minutes ago Up 6 minutes (unhealthy) 80/tcp nginx
```

The container is flagged as unhealthy after three failures of the check (you can change the number of retries with the option `--health-retries`).

!!! question "Exercise"
Restore the `index.html` file and verify the container status.

## Example: create a MariaDB container configuring a custom health check to check whether the server is available.

1. Set the following environment variables with appropriate values for your MySQL configuration:
```
export MYSQL_USER=... # MySQL user (replace with your desired value)
export MYSQL_PASSWORD=... # MySQL user password (replace with your desired value)
export MYSQL_ROOT_PASSWORD=... # MySQL root user password (replace with your desired value)
export MYSQL_DATABASE=... # MySQL database name (replace with your desired value)
```

2. Run the container with the custom health check:
```
docker run -d --name db \
-e MYSQL_DATABASE=${MYSQL_DATABASE} \
-e MYSQL_USER=${MYSQL_USER} \
-e MYSQL_PASSWORD=${MYSQL_PASSWORD} \
-e MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} \
--health-cmd='mysqladmin -p${MYSQL_ROOT_PASSWORD} ping -h localhost' \
--health-interval=20s \
--health-retries=3 \
mariadb:latest
```

In this command:

* `--name db`: Specifies the name of the container as "db."
* `-e MYSQL_DATABASE=${MYSQL_DATABASE}`: Sets the MySQL database name as an environment variable.
* `-e MYSQL_USER=${MYSQL_USER}`: Sets the MySQL user as an environment variable.
* `-e MYSQL_PASSWORD=${MYSQL_PASSWORD}`: Sets the MySQL user's password as an environment variable.
* `-e MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}`: Sets the MySQL root user's password as an environment variable.
* `--health-cmd`: Specifies the custom health check command, which uses the provided MySQL root password to check if the MariaDB server is responsive.
* `--health-interval=20s`: Sets the health check interval to 20 seconds.
* `--health-retries=3`: Specifies the number of retries before marking the container as "unhealthy."


## Lab challenge

**Goal**: create a PostgreSQL container and setup a health check to monitor its status

!!! info "Hints"
* Search for PostgreSQL official docker image on [docker hub](https://hub.docker.com/)
* Read the documentation and understand how to start your server
* Specify a simple health check using the tool "[pg_isready](https://www.postgresql.org/docs/current/app-pg-isready.html)" to check if the server is alive
Loading

0 comments on commit 4da3f8b

Please sign in to comment.