From a64615386d06129626c9b0a3e2aeda6e303f15b5 Mon Sep 17 00:00:00 2001 From: erjemin Date: Fri, 14 Feb 2025 15:54:33 +0300 Subject: [PATCH] =?UTF-8?q?add:=20=D0=97=D0=B0=D0=B2=D0=B8=D1=81=D0=B8?= =?UTF-8?q?=D0=BC=D0=BE=D1=81=D1=82=D0=B8=20=D0=B8=20=D1=85=D0=B5=D0=BB?= =?UTF-8?q?=D1=81=D1=87=D0=B5=D0=BA=D0=B8=20(6)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/docker-nginx-w-certbot.md | 115 +++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) diff --git a/docker/docker-nginx-w-certbot.md b/docker/docker-nginx-w-certbot.md index 230a0f0..48fd7cb 100644 --- a/docker/docker-nginx-w-certbot.md +++ b/docker/docker-nginx-w-certbot.md @@ -479,4 +479,119 @@ dd0b7a683dde certbot/certbot:latest "/bin/sh -c 'apk add…" 13 **Все!** Теперь у нас полностью контейнеризированное решение, без лишних зависимостей на хосте, и при переносе каталога `~/docker-data` на другой сервер (с Docker + docker-compose) всё должно точно также запуститься. +## Зависимости и "проверки здоровья" +В нашем случае контейнеры не особо зависят друг от друга, но если стремиться к идеальной контейнеризации, то можно +рассмотреть "кто на ком стоит", какие зависимости и какой порядок запуска контейнеров. + +Самый независимый контейнер — это `portainer`. Он просто запускается и работает. Начали его проксировать через nginx +или нет — ему важно. + +Контейнер `letsencrypt-certbot` зависит от контейнера `nginx`, так как он без него не пройдет валидация домена. +Решение установить `depends_on` от контейнера `nginx` не идеально, т.к. старт контейнера `nginx` не означает, что он +готов принимать запросы. Избежать проблем можно сделав **healthcheck** для Nginx. + +В docker-compose.yml добавим: +```yaml + nginx: + ... + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost"] + interval: 10s + timeout: 3s + retries: 3 + start_period: 10s + ... + ... + + certbot: + ... + depends_on: + nginx: + condition: service_healthy + ... + ... +``` + +Что тут происходит: +- `healthcheck: ...` — добавляем блок в контейнере `nginx` для проверки его здоровья. В данном случае проверяем + доступность Nginx выполняя команду `curl -f http://localhost`. Если Nginx отвечает, то контейнер считается здоровым; + - `interval: 10s` — проверка состояния каждые 10 секунд; + - `timeout: 3s` — ожиданий проверки 3 секунды (когда curl зависнет дольше 3 секунд, то это считается ошибкой); + - `retries: 3` — количество попыток проверки (если команда завершается с ошибкой (код ≠ 0) — Docker попробует ещё + раз... и так до 3-х раз); + - `start_period: 10s` — время ожидания перед началом проверок (первая проверка будет выполнена через 10 секунд после + старта контейнера); +- `depends_on: ...` — добавляем зависимость контейнера `certbot` от контейнера `nginx`. Но не просто так, а с условием + `service_healthy`. Это означает, что контейнер `certbot` не будет запущен, пока контейнер `nginx` не будет здоров. + +Теперь при запуске контейнера `certbot` он будет ждать, пока контейнер `nginx` не станет здоровым (а еще задержку +в 10 секунд будет заметно даже на глаз). + +К сожалению, если `nginx` упадет в процессе, то `certbot` не будет перезапущен. Но это уже другая история, +мир не идеален. + +### Погасить проверку здоровья после первого успешного запуска (раздел для параноиков) + +Бдительный читатель может заметить, что проверка `curl -f http://localhost` будет порождать бесполезную нагрузку на +сервере (ведь кажется, что по locahost будет отвечать `portainer`, а не `nginx`). Но это не так. Прокси на `portainer` +отвечать там не будет (ведь он настроен на домен `portainer.you.domain.name`), а будет отдавать дефолтную страницу +`nginx`. У нас она даже не настроена, и будет 301, а это вообще очень-очень мало не потребляет... + +Тем не менее есть способ погасить проверку здоровья, как только контейнер стал `healthy` (допустим, чтобы "не следить" +в логах). У нас есть мапп `/home/web/docker-data/letsencrypt/_cert:/etc/letsencrypt` внутри контейнера `nginx`. Если мы +создадим скрипт в каталоге `/home/web/docker-data/letsencrypt/_cert` он будет доступен внутри контейнера в каталоге +`/etc/letsencrypt`. Создадим скрипт `healthcheck-nginx.sh`: +```bash +nano /home/web/docker-data/letsencrypt/_cert/healthcheck-nginx.sh +``` + +И поместим в него следующее содержимое: +```bash +#!/bin/sh +HEALTH_FILE_FLAG="/tmp/nginx_healthy" + +if [ -f "HEALTH_FILE_FLAG" ]; then + exit 0 # Уже healthy, больше не проверяем +fi + +if curl -fs http://localhost > /dev/null; then + touch "HEALTH_FILE_FLAG" # Создаем файл-флаг + exit 0 # Успешная проверка +else + exit 1 # Nginx еще не поднялся, пусть проверяют еще +fi +``` + +Что будет происходить. При старте контейнера `nginx` через *healthcheck* будем запускать этот скрипт. +При первом запуске он проверит есть ли файл-флаг `/tmp/nginx_healthy` внутри контейнера. Сразу после старта контейнера +этого фал-флага нет (контейнер запускается "чистым"). Затем скрипт попытается выполнить `curl -fs http://localhost`. +Если Nginx еще не поднялся, то **curl** возвращает ошибку и контейнер остается *unhealthy*. Но если Nginx уже работает, +то **touch** создается файл-флаг `/tmp/nginx_healthy` то **exit 0** ответит, что контейнер *healthy*.⃣ При всех +следующих *healthcheck* проверка файл-флага пройдет успешно, сразу будет получен `exit 0` и проверка *healthy* будет +завершена без вызова `curl`. + +Осталось добавить этот скрипт в `docker-compose.yml`: +```yaml + nginx: + ... + healthcheck: + test: ["CMD", "sh", "/etc/letsencrypt/healthcheck-nginx.sh"] + interval: 10s + timeout: 3s + retries: 3 + start_period: 10s + ... + ... +``` + +### Зависимости и healthcheck для `portainer` (или параноики могут сломать бизнес-логику) + +А что с `nginx` и `portainer`? Они зависят друг от друга? Кажется нет. Если `nginx` упадет, то `portainer` просто +станет недоступен (ничего не будет доступно вообще). Если же `portainer` упадет, то `nginx` будет отдавать ошибку 502. +Ошибка, в данном случае, это тоже информация. Как минимум мы увидим что nginx работает, а упало приложение. + +Тем не менее можно добавить `healthcheck` в `portainer`, проверять, что он отвечает внутри себя по порту 9000, +а затем установить зависимость `nginx` от здоровья `portainer`. + +Но, кажется, это уже перебор. \ No newline at end of file