Denozordec 2671e90d20
Docker / build (push) Successful in 31s
Init Commit
2026-03-23 17:31:26 +07:00
2026-03-23 17:31:26 +07:00
2026-03-23 17:31:26 +07:00
2026-03-23 17:31:26 +07:00
2026-03-23 17:31:26 +07:00
2026-03-23 17:31:26 +07:00
2026-03-23 17:31:26 +07:00
2026-03-23 17:31:26 +07:00
2026-03-23 17:31:26 +07:00

Cloudflare DNS pool balancer

Лёгкий контейнер на Alpine: периодически проверяет доступность бэкендов (ping или HTTP), синхронизирует записи A / AAAA для одного DNS-имени (пул) в Cloudflare через API Token, пишет цветные логи и опционально отдаёт HTML-страницу статуса с ограничением по IP.


Оглавление


Как это работает

  1. Для каждой цели из CHECK_TARGETS определяется IP (прямой адрес, резолв имени или хоста из URL).
  2. Выполняется проверка: ICMP ping к IP или curl по URL (см. CHECK_MODE).
  3. IP считается желательным в пуле, только если все цели, которые резолвятся в этот IP, прошли проверку.
  4. Читаются текущие записи A/AAAA с именем POOL_DOMAIN в Cloudflare.
  5. Лишние записи удаляются, недостающие создаются (TTL и proxied из переменных окружения).

Два разных смысла curl:

  • Проверка бэкендов (CHECK_MODE=curl) — запросы к URL из целей / шаблону CURL_URL_TEMPLATE.
  • Порт STATUS_HTTP_PORT — только встроенная страница статуса внутри контейнера, не health-check.

Требования

  • Docker Engine на Linux (или эквивалент с Linux-контейнерами).
  • У контейнера должен быть доступ в интернет (Cloudflare API, ping/curl к бэкендам).
  • Для ping из пользователя nobody в образе выставлен cap_net_raw на /usr/bin/ping. Если ping не работает, запускайте с --cap-add=NET_RAWdocker-compose.yml это уже указано).

Сборка образа (Linux / bash)

Из корня репозитория:

cd /path/to/cloudflare_balancer
docker build -t cloudflare-balancer:latest .

Проверка локально (без реальных секретов контейнер сразу завершится с ошибкой — это нормально):

docker build -t cloudflare-balancer:latest . && echo "сборка OK"

Быстрый старт

Минимально нужны: POOL_DOMAIN, CHECK_TARGETS, CLOUDFLARE_API_TOKEN.

Рекомендуется передавать секреты через --env-file:

cp .env.example .env
# отредактируйте .env

docker run --rm \
  --cap-add=NET_RAW \
  --env-file .env \
  cloudflare-balancer:latest

Переменные окружения

Переменная Обязательно По умолчанию Описание
POOL_DOMAIN да DNS-имя пула (все A/AAAA с этим именем управляются скриптом).
CHECK_TARGETS да Список целей через запятую: IP, hostname или http(s)://....
CLOUDFLARE_API_TOKEN да* Bearer-токен Cloudflare.
CLOUDFLARE_API_KEY нет Алиас для токена (если не задан CLOUDFLARE_API_TOKEN).
CLOUDFLARE_ZONE_ID нет ID зоны; если пусто — поиск по имени зоны.
CLOUDFLARE_ZONE_NAME нет Имя зоны (apex), например example.com; если пусто — эвристика: две последние метки POOL_DOMAIN.
CHECK_INTERVAL_SEC нет 30 Пауза между циклами, сек.
CHECK_MODE нет ping ping или curl.
PING_COUNT нет 2 Число ICMP-запросов.
PING_TIMEOUT_SEC нет 2 Таймаут ping (см. iputils-ping).
CURL_URL_TEMPLATE нет http://%s/ Для не-URL целей: один плейсхолдер %s заменяется на цель.
CURL_MAX_TIME_SEC нет 5 --max-time для curl.
CURL_CONNECT_TIMEOUT_SEC нет 3 --connect-timeout.
CURL_OK_MIN нет 200 Минимальный допустимый HTTP-код.
CURL_OK_MAX нет 399 Максимальный допустимый HTTP-код.
DEFAULT_TTL нет 300 TTL при создании записи (для прокси-записей Cloudflare сам использует авто-TTL).
DEFAULT_PROXIED нет false true / false — оранжевое облако.
ENABLE_STATUS_HTTP нет 0 1 — включить HTTP-страницу статуса.
STATUS_HTTP_PORT нет 8080 Порт внутри контейнера для страницы статуса.
STATUS_HTTP_ALLOW_IPS нет (пусто) Whitelist: IP и IPv4 CIDR через запятую. Пусто = слушать только 127.0.0.1.

* Обязателен токен или алиас.

Важно: эвристика зоны по двум последним меткам не подходит для всех публичных суффиксов (например некоторые зоны второго уровня). В сомнениях задайте CLOUDFLARE_ZONE_ID или CLOUDFLARE_ZONE_NAME.


Cloudflare: API Token

  1. Cloudflare Dashboard → My ProfileAPI TokensCreate Token.
  2. Шаблон Edit zone DNS или кастомный минимум:
    • ZoneDNSEdit
    • ZoneZoneRead
    • Ограничить конкретной зоной (рекомендуется).
  3. Скопируйте токен в .env как CLOUDFLARE_API_TOKEN.

Официальная документация API: Cloudflare API.


Примеры запуска

Только ping, без UI

docker run --rm --cap-add=NET_RAW \
  -e POOL_DOMAIN=app.example.com \
  -e CHECK_TARGETS='10.0.0.1,10.0.0.2' \
  -e CLOUDFLARE_API_TOKEN="$CLOUDFLARE_API_TOKEN" \
  -e CHECK_INTERVAL_SEC=20 \
  cloudflare-balancer:latest

Режим curl и шаблон URL

docker run --rm --cap-add=NET_RAW \
  -e POOL_DOMAIN=app.example.com \
  -e CHECK_TARGETS='backend1.internal,backend2.internal' \
  -e CLOUDFLARE_API_TOKEN="$CLOUDFLARE_API_TOKEN" \
  -e CHECK_MODE=curl \
  -e CURL_URL_TEMPLATE='http://%s:8080/health' \
  cloudflare-balancer:latest

Полный URL как цель

docker run --rm --cap-add=NET_RAW \
  -e POOL_DOMAIN=app.example.com \
  -e CHECK_TARGETS='https://node1.example.com/health' \
  -e CLOUDFLARE_API_TOKEN="$CLOUDFLARE_API_TOKEN" \
  -e CHECK_MODE=curl \
  cloudflare-balancer:latest

Веб-статус + проброс порта + whitelist

При доступе с хоста Docker клиентский IP в контейнере часто совпадает с адресом шлюза bridge (часто 172.17.0.1 или подсеть 172.17.0.0/16). Добавьте её или конкретный IP в whitelist.

docker run --rm --cap-add=NET_RAW \
  -p 8080:8080 \
  -e POOL_DOMAIN=app.example.com \
  -e CHECK_TARGETS='10.0.0.1' \
  -e CLOUDFLARE_API_TOKEN="$CLOUDFLARE_API_TOKEN" \
  -e ENABLE_STATUS_HTTP=1 \
  -e STATUS_HTTP_PORT=8080 \
  -e STATUS_HTTP_ALLOW_IPS='127.0.0.1,172.17.0.0/16,10.0.0.0/8' \
  cloudflare-balancer:latest

Откройте в браузере: http://127.0.0.1:8080/ (если whitelist это разрешает).


Docker Compose

cp .env.example .env
# заполните CLOUDFLARE_API_TOKEN и остальное

docker compose build
docker compose up -d
docker compose logs -f

Файл docker-compose.yml подключает .env и добавляет NET_RAW для ping.


Веб-интерфейс статуса и whitelist

  • STATUS_HTTP_PORT — порт HTTP-сервера статуса, не имеет отношения к CHECK_MODE=curl.
  • Если STATUS_HTTP_ALLOW_IPS пустойsocat слушает 127.0.0.1 (доступ с хоста через -p без NAT к loopback контейнера обычно не попадёт на 127.0.0.1 внутри контейнера). Для просмотра со стороны хоста задайте whitelist и 0.0.0.0-bind (это делается автоматически при непустом whitelist).
  • В whitelist поддерживаются точные IPv4 и IPv4 CIDR; для IPv6 — в основном точное совпадение (CIDR для v6 в обработчике минимальный).

Логи

docker logs -f <container_id>

В логах используются ANSI-цвета (удобно в терминале). Блоки: заголовок цикла, строки OK/FAIL по целям, действия DELETE/POST в API.


Ограничения

  • Одна строка в CHECK_TARGETS с запятой внутри URL может сломать разбор — избегайте запятых в целях или используйте только один хост на цель.
  • Несколько имён, резолвящихся в один IP, считаются одним бэкендом: IP остаётся в пуле только если все такие проверки успешны.
  • Записи A/AAAA для POOL_DOMAIN, которые не соответствуют IP из текущего резолва целей, не удаляются (в лог и HTML выводится предупреждение «вне списка»).

Устранение неполадок

Симптом Что проверить
Ошибка авторизации Cloudflare Токен, срок, зона в области действия токена.
«Зона не найдена» Задайте CLOUDFLARE_ZONE_ID или корректный CLOUDFLARE_ZONE_NAME.
Все ping FAIL NET_RAW / cap_add; маршрутизация сети контейнера до бэкендов; ICMP может быть запрещён файрволом.
curl всегда FAIL URL, TLS, коды ответа (CURL_OK_MIN / CURL_OK_MAX), таймауты.
UI отдаёт 403 Whitelist: добавьте IP шлюза Docker или вашу подсеть.
После старта пул «пустой» При всех FAIL записи удаляются — проверьте доступность целей и корректность IP.

Сборка в CI (Gitea Actions)

  1. На сервере Gitea включите Actions и подключите runner (документация).
  2. Workflow: .gitea/workflows/docker.yml.
  3. По умолчанию выполняется docker build.
  4. Опциональная публикация в Container Registry Gitea: задайте секреты репозитория:
    • REGISTRY_URL — хост (можно с https://, скрипт обрежет протокол для docker login).
    • REGISTRY_USER — логин пользователя Gitea.
    • PACKAGE_TOKEN — токен Gitea с правом публиковать пакеты (используется как пароль в docker login; рекомендуемый способ).
    • REGISTRY_PASSWORD — опционально, только если не задан PACKAGE_TOKEN (устаревший/альтернативный секрет).

Имя образа в registry: <REGISTRY>/<owner_lowercase>/cloudflare-balancer:<tag>.

В Gitea создайте Personal Access Token (или токен с нужным scope) с доступом к пакетам / записи в Container Registry и сохраните его в секрете PACKAGE_TOKEN.


Безопасность

  • Не коммитьте .env и реальные токены.
  • Минимизируйте права API Token одной зоной.
  • Не публикуйте порт статуса в интернет без whitelist и без понимания сетевой модели Docker.

Windows и WSL2

Основные команды из этой инструкции рассчитаны на bash под Linux. На Windows удобнее запускать Docker внутри WSL2 и выполнять те же docker build / docker run из Ubuntu (или другого дистрибутива в WSL).


Лицензия

Проект в репозитории пользователя — при необходимости добавьте файл LICENSE отдельно.

S
Description
No description provided
Readme
69 KiB
Languages
Shell 94.5%
Dockerfile 5.5%