Skip to content

Commit f9a169f

Browse files
authored
Merge pull request #1400 from mickem/feature/modernized_docker_2
Modernize CheckDocker and add stats, restarts and disk-usage checks
2 parents fb59f8e + ada2492 commit f9a169f

28 files changed

Lines changed: 2089 additions & 151 deletions
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#### About `check_docker`
2+
3+
`check_docker` checks the state of docker containers via the local daemon
4+
socket (`/var/run/docker.sock` on Linux, the `\\.\pipe\docker_engine` named
5+
pipe on Windows; configurable via the `endpoint` setting or `host=`). It works
6+
against any daemon speaking the docker API, including podman's compatibility
7+
socket.
8+
9+
Three ways to use it:
10+
11+
* `container=<name>` (repeatable) — the "is my container running" mode: only
12+
the named containers are checked, and a name the daemon does not know gets
13+
the synthetic state `missing`, so it trips the default critical instead of
14+
silently disappearing from the listing.
15+
* No arguments — lists running containers; nothing trips the default
16+
thresholds, so this is an inventory-style check.
17+
* `all=true` — also includes stopped containers (like `docker ps -a`); any
18+
non-running container then trips the default critical
19+
(`container_state != 'running'`), so combine it with `filter=` on hosts
20+
where exited one-shot containers are expected.
21+
22+
Available keywords (for `filter=` / `warning=` / `critical=` / syntax):
23+
24+
| Keyword | Description |
25+
|-------------------|------------------------------------------------------------------------------|
26+
| `names` | Container name(s), comma separated |
27+
| `container_state` | `created`, `restarting`, `running`, `removing`, `paused`, `exited`, `dead` or `missing` |
28+
| `status` | Human readable status, e.g. `Up 3 hours (healthy)` |
29+
| `health` | Health-check state: `healthy`, `unhealthy`, `starting`, or empty without a health check |
30+
| `image` | Image the container was created from |
31+
| `image_id` | Id of that image |
32+
| `id` | Container id |
33+
| `command` | Command the container runs |
34+
| `ip` | First IP address on any attached network |
35+
| `ports` | Published/exposed ports, e.g. `0.0.0.0:8080->80/tcp` |
36+
| `labels` | Container labels as `key=value`, comma separated |
37+
| `created` | When the container was created (date) |
38+
39+
The daemon endpoint is restricted to a local named pipe / absolute socket path:
40+
a UNC path in `host=` would make a Windows host authenticate to a remote SMB
41+
server with the service account, so anything non-local is refused outright.
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#### About `check_docker_df`
2+
3+
`check_docker_df` reports docker's disk usage the way `docker system df`
4+
does — images, container writable layers, volumes and the build cache — plus
5+
what a prune would reclaim: unused images (their non-shared size), stopped
6+
containers, unreferenced volumes and idle build cache.
7+
8+
The daemon computes this by walking every image layer, container filesystem
9+
and volume, so **the request is slow on large hosts** (seconds to minutes).
10+
The check therefore defaults to six times the module's normal timeout; don't
11+
schedule it more often than you need it.
12+
13+
Available keywords (for `filter=` / `warning=` / `critical=` / syntax):
14+
15+
| Keyword | Description |
16+
|---------------------------|--------------------------------------------------------------|
17+
| `images` / `unused_images`| Number of images / images no container uses |
18+
| `images_size` | Disk used by images (unique layer sizes) |
19+
| `images_reclaimable` | Freed by pruning unused images |
20+
| `containers` | Number of containers (running and stopped) |
21+
| `containers_size` | Disk used by container writable layers |
22+
| `containers_reclaimable` | Freed by pruning stopped containers |
23+
| `volumes` / `unused_volumes` | Number of volumes / volumes no container references |
24+
| `volumes_size` | Disk used by volumes |
25+
| `volumes_reclaimable` | Freed by pruning unused volumes |
26+
| `build_cache_size` | Disk used by the build cache |
27+
| `build_cache_reclaimable` | Freed by pruning the idle build cache |
28+
| `total_size` | Everything above combined |
29+
| `total_reclaimable` | Everything a full prune would free |
30+
| `message` | The human readable summary line |
31+
32+
All `*_size` / `*_reclaimable` keywords are byte-sized: thresholds take a unit
33+
(`500M`, `10G`, `1T`); a bare number is rejected. No default thresholds — a
34+
responding daemon is OK until you add limits.
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
**Show docker disk usage (like `docker system df`):**
2+
3+
```
4+
check_docker_df
5+
OK: images 71 (8.216GB), containers 19 (388KB), volumes 6 (4.517GB), build cache 12.808GB, reclaimable 21.479GB
6+
```
7+
8+
**Alert when a prune would free a lot of space (size thresholds take a unit):**
9+
10+
```
11+
check_docker_df "warning=total_reclaimable > 10G" "critical=total_reclaimable > 50G"
12+
WARNING: images 71 (8.216GB), containers 19 (388KB), volumes 6 (4.517GB), build cache 12.808GB, reclaimable 21.479GB|'docker reclaimable'=23063090871B;10737418240;53687091200
13+
```
14+
15+
**Alert on unused images piling up:**
16+
17+
```
18+
check_docker_df "warning=unused_images > 20"
19+
OK: images 71 (8.216GB), containers 19 (388KB), volumes 6 (4.517GB), build cache 12.808GB, reclaimable 21.479GB|'docker unused images'=63;20;0
20+
```
21+
22+
**Watch a specific category, e.g. the build cache:**
23+
24+
```
25+
check_docker_df "warning=build_cache_size > 20G" "detail-syntax=build cache %(build_cache_size) (reclaimable %(build_cache_reclaimable))"
26+
OK: build cache 13752766549 (reclaimable 13752766549)|'docker build cache'=13752766549B;21474836480;0
27+
```
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#### About `check_docker_info`
2+
3+
`check_docker_info` checks the docker daemon itself: it reads `/info` from the
4+
local daemon socket and reports the server version, host name and the
5+
container/image counts. A responding daemon is **OK** unless you threshold the
6+
counts; an unreachable daemon is **UNKNOWN** with the transport error — which
7+
makes this the natural "is docker itself healthy" companion to per-container
8+
`check_docker` checks.
9+
10+
Available keywords (for `filter=` / `warning=` / `critical=` / syntax):
11+
12+
| Keyword | Description |
13+
|--------------|----------------------------------------|
14+
| `version` | Docker server version |
15+
| `name` | Daemon host name |
16+
| `os` | Operating system the daemon runs on |
17+
| `containers` | Total number of containers |
18+
| `running` | Number of running containers |
19+
| `paused` | Number of paused containers |
20+
| `stopped` | Number of stopped containers |
21+
| `images` | Number of images |
22+
23+
Count keywords used in thresholds are emitted as performance data.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
**Check that the docker daemon itself is up and responding:**
2+
3+
```
4+
check_docker_info
5+
OK: docker 29.5.3 on docker-host: 16 running, 0 paused, 4 stopped containers, 231 images
6+
```
7+
8+
**Alert when nothing is running (or too much is stopped), with perf data:**
9+
10+
```
11+
check_docker_info "warning=running < 1" "critical=stopped > 100"
12+
OK: docker 29.5.3 on docker-host: 16 running, 0 paused, 4 stopped containers, 231 images|'docker-host running'=16;1;0 'docker-host stopped'=4;0;100
13+
```
14+
15+
**A daemon that is down is clearly reported (UNKNOWN):**
16+
17+
```
18+
check_docker_info host=/var/run/missing.sock
19+
Failed to connect to docker daemon at '/var/run/missing.sock': Failed to connect to /var/run/missing.sock: No such file or directory
20+
```
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#### About `check_docker_restarts`
2+
3+
`check_docker_restarts` detects crash-looping and OOM-killed containers via
4+
the inspect endpoint (`RestartCount`, `State.OOMKilled`, `State.StartedAt`,
5+
`State.ExitCode`). It always includes stopped containers: a crash-looping
6+
container spends most of its time not-running, and a final crash leaves it
7+
exited — both must stay visible.
8+
9+
Default thresholds encode the crash-loop signature: **warning** when
10+
`restart_count > 3 and started < 15m and started >= 0` (many restarts *and* a
11+
recent start — a container that has been up for a month is fine no matter how
12+
bumpy its past), **critical** when `oom_killed = 1` (the last exit was an
13+
out-of-memory kill).
14+
15+
Available keywords (for `filter=` / `warning=` / `critical=` / syntax):
16+
17+
| Keyword | Description |
18+
|-------------------|-----------------------------------------------------------------------|
19+
| `names` | Container name(s), comma separated |
20+
| `image` | Image the container was created from |
21+
| `container_state` | `created`, `restarting`, `running`, `removing`, `paused`, `exited`, `dead` |
22+
| `restart_count` | Restarts since the container was created |
23+
| `started` | Seconds since the last start, `-1` if it never started; supports units (`started < 10m`) |
24+
| `exit_code` | Exit code of the last exit (0 while running fine) |
25+
| `oom_killed` | `1` when the last exit was an out-of-memory kill, else `0` |
26+
27+
`container=<name>` (repeatable) scopes the check to specific containers.
28+
Note that `restart_count` is cumulative for the container's lifetime; that is
29+
why the default warning also requires a recent start before it fires.
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
**Detect restart loops (default: WARNING when a container restarted more than 3 times and last started within 15 minutes; OOM kills are CRITICAL):**
2+
3+
```
4+
check_docker_restarts
5+
WARNING: crashy-app: 8 restarts, restarting
6+
```
7+
8+
A stable container is OK no matter how bumpy its distant past:
9+
10+
```
11+
check_docker_restarts container=app-backend
12+
OK: app-backend: 0 restarts, running
13+
```
14+
15+
**An out-of-memory kill is CRITICAL by default:**
16+
17+
```
18+
check_docker_restarts container=greedy-app
19+
CRITICAL: greedy-app: 2 restarts, exited
20+
```
21+
22+
**Custom rules using the keywords (e.g. any restart of a specific container within the last hour):**
23+
24+
```
25+
check_docker_restarts container=app-backend "warning=restart_count > 0 and started < 1h" "detail-syntax=%(names): %(restart_count) restarts, up %(started)s, exit=%(exit_code) oom=%(oom_killed)"
26+
OK: app-backend: 0 restarts, up 236s, exit=0 oom=0|'app-backend restarts'=0;0;0
27+
```
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
**Require that specific containers are running (a missing or stopped container is CRITICAL):**
2+
3+
```
4+
check_docker container=web-frontend
5+
OK: web-frontend=running
6+
```
7+
8+
```
9+
check_docker container=web-frontend container=backup-agent
10+
CRITICAL: backup-agent=missing
11+
```
12+
13+
**List all running containers (inventory-style):**
14+
15+
```
16+
check_docker
17+
OK: web-frontend=running, database=running
18+
```
19+
20+
**Include stopped containers (`docker ps -a`) — any non-running container trips the default critical:**
21+
22+
```
23+
check_docker all=true
24+
CRITICAL: old-job=exited
25+
```
26+
27+
**Use the container keywords in the output:**
28+
29+
```
30+
check_docker container=web-frontend "detail-syntax=%(names): %(image) %(status) ports=%(ports)" "top-syntax=${status}: ${list}"
31+
OK: web-frontend: nginx:alpine Up 2 hours ports=0.0.0.0:18080->80/tcp,:::18080->80/tcp
32+
```
33+
34+
**Alert on failing container health checks instead of state:**
35+
36+
```
37+
check_docker "filter=has_health_check = 1" "warning=health = 'starting'" "critical=health = 'unhealthy'" "detail-syntax=%(names)=%(health)"
38+
OK: web-frontend=healthy
39+
```
40+
41+
**A daemon that is down is clearly reported (UNKNOWN):**
42+
43+
```
44+
check_docker host=/var/run/missing.sock
45+
Failed to connect to docker daemon at '/var/run/missing.sock': Failed to connect to /var/run/missing.sock: No such file or directory
46+
```
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#### About `check_docker_stats`
2+
3+
`check_docker_stats` samples per-container resource usage the way
4+
`docker stats` does: CPU as a percentage of the host (delta of the container's
5+
CPU time over the host's, scaled by online CPUs) and memory usage with the
6+
page cache excluded, against the container's memory limit.
7+
8+
Sampling uses the daemon's `stream=false` stats mode, which takes two readings
9+
about a second apart so the CPU delta is meaningful — expect roughly **one
10+
second per sampled container**. On hosts with many containers, scope the check
11+
with `container=<name>` (repeatable) or accept the added latency.
12+
13+
Available keywords (for `filter=` / `warning=` / `critical=` / syntax):
14+
15+
| Keyword | Description |
16+
|----------------|--------------------------------------------------------------------|
17+
| `names` | Container name(s), comma separated |
18+
| `image` | Image the container was created from |
19+
| `cpu_pct` | CPU usage in percent of the host (like `docker stats`) |
20+
| `memory_used` | Memory used in bytes, page cache excluded; size units in thresholds (`memory_used > 200M`) |
21+
| `memory_limit` | Memory limit in bytes (the host's total memory when unlimited) |
22+
| `memory_pct` | Memory usage in percent of the limit |
23+
| `memory` | Human readable usage, e.g. `45.2MB of 256MB` (display only) |
24+
25+
Size-typed thresholds need a unit (`1b`, `64k`, `200M`, `1G`); a bare number
26+
is rejected. No default thresholds: an idle container is OK until you say
27+
otherwise.
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
**Check resource usage of a specific container:**
2+
3+
```
4+
check_docker_stats container=app-backend
5+
OK: app-backend: cpu 2%, memory 45.2MB of 256MB (17%)
6+
```
7+
8+
**Alert on memory pressure or CPU saturation (thresholded keywords become perf data):**
9+
10+
```
11+
check_docker_stats container=app-backend "warning=memory_pct > 80" "critical=memory_pct > 95"
12+
OK: app-backend: cpu 2%, memory 45.2MB of 256MB (17%)|'app-backend memory %'=17%;80;95
13+
```
14+
15+
**Absolute memory thresholds take byte units (`1b`, `64k`, `200M`, `1G`, ...):**
16+
17+
```
18+
check_docker_stats container=app-backend "critical=memory_used > 200M"
19+
OK: app-backend: cpu 2%, memory 45.2MB of 256MB (17%)|'app-backend memory'=47401984B;0;209715200
20+
```
21+
22+
**Sample every running container (about a second per container, so scope on busy hosts):**
23+
24+
```
25+
check_docker_stats "warning=cpu_pct > 80"
26+
OK: web-frontend: cpu 1%, memory 12.4MB of 15.35GB (0%), app-backend: cpu 2%, memory 45.2MB of 256MB (17%)
27+
```

0 commit comments

Comments
 (0)