Zpět na domů

DNS Docker v GitLab CI: oprava upozornění MAX

Případ ladění upozornění MAX z GitLab CI: symptomy proto.payload maskovaly problém s DNS v Dockeru. Oprava přes config.toml runnera, funkční šablona skriptu s diagnostikou.

GitLab CI + MAX: DNS narušil upozornění o nasazení
Advertisement 728x90

DNS v Dockeru zablokoval notifikace MAX z GitLab CI: ladění a řešení

V GitLab CI úkol odeslat notifikaci do MAX Messenger po nasazení skončil týdnem ladění. Zprávy odcházely, ale odpověď přicházela s chybou {"code":"proto.payload","message":"Can't deserialize body"}. Log ukazoval problém s tělem požadavku, takže se soustředění přesunulo na tvorbu JSON payloadu.

I přes zjednodušení nic nefungovalo. Proměnné prostředí MAX_BOT_TOKEN a MAX_NOTIFY_CHAT_ID byly na místě, curl byl v obraze. Ale zprávy nedocházely.

Neúspěšné pokusy s payloadem

Příkaz prošel několika iteracemi:

Google AdInline article slot
  • printf do souboru: printf '{"text":"%s"}' "$MSG" > /tmp/payload.json; curl ... -d @/tmp/payload.json. Odpověď: Can't deserialize body.
  • jq v Alpine: Přechod na alpine:3.20 s apk add jq. jq -nc --arg t "$MSG" '{text:$t}' > /tmp/payload.json. Stejná odpověď.
  • jq do proměnné: PAYLOAD=$(jq ...); curl ... --data "${PAYLOAD}". Teď Empty request body, navíc apk add selhal s exit code 2 kvůli nedostupným repozitářům.
  • Hodnocení: curl ... -d '{"text":"test ok"}'. Znovu Empty request body.

Pokrok byl minimální: tělo požadavku nedocházelo stabilně.

Klíčový test: host vs kontejner

Porovnání odhalilo rozdíl:

Z hostitele:

Google AdInline article slot
ssh ... "curl -s -X POST 'https://platform-api.max.ru/messages?chat_id=...' -H 'Authorization: ...' -H 'Content-Type: application/json' -d '{"text":"test from server"}'"

HTTP 200, zpráva doručena.

Z Dockeru:

docker run --rm curlimages/curl:8.7.1 curl -s -X POST 'https://platform-api.max.ru/messages?chat_id=...' -H 'Authorization: ...' -d '{"text":"test from docker"}'

exit code 6: Could not resolve host: platform-api.max.ru.

Google AdInline article slot

Diagnóza: DNS v Docker kontejnerech nefungoval, i když hostitel resolvoval v pořádku.

Test s --dns 8.8.8.8 potvrdil: zpráva odešla okamžitě.

Proč se DNS maskoval jako JSON chyba

Očekávaná DNS chyba — curl exit code 6 bez HTTP odpovědi. Místo toho přicházel JSON od MAX s proto.payload. Důvod v GitLab runneru: používá vlastní DNS konfiguraci pro kontejnery úloh. To vedlo k nestabilnímu spojení — tělo se ořezávalo nebo ztrácelo, MAX interpretoval prázdný vstup jako chybu deserializace.

Oprava: DNS v config.toml runneru

Řešení — přidat DNS do /etc/gitlab-runner/config.toml:

[[runners]]
  ...
  [runners.docker]
    dns = ["8.8.8.8", "8.8.4.4"]

Restart: sudo gitlab-runner restart.

Alternativa na úrovni Docker daemonu v /etc/docker/daemon.json:

{
  "dns": ["8.8.8.8", "8.8.4.4"]
}

sudo systemctl restart docker.

Po opravě fungoval hardcoded payload hned při prvním spuštění.

Funkční šablona skriptu

Finální skript s diagnostikou a čištěním proměnných:

if [ -z "$MAX_BOT_TOKEN" ] || [ -z "$MAX_NOTIFY_CHAT_ID" ]; then
  echo "WARN: MAX_BOT_TOKEN nebo MAX_NOTIFY_CHAT_ID nejsou nastaveny"
  exit 0
fi

TOKEN="$(printf '%s' "$MAX_BOT_TOKEN" | tr -d '\r\n')"
CHAT_ID="$(printf '%s' "$MAX_NOTIFY_CHAT_ID" | tr -d '\r\n[:space:]')"
BODY_FILE="$(mktemp)"
RESPONSE_FILE="$(mktemp)"

printf '{"text":"%s"}' "${MSG}" > "${BODY_FILE}"

BODY_SIZE="$(wc -c < "${BODY_FILE}" | tr -d '[:space:]')"
echo "DEBUG: CHAT_ID=${CHAT_ID}"
echo "DEBUG: BODY_SIZE=${BODY_SIZE}"
echo "DEBUG: BODY_TEXT=$(cat "${BODY_FILE}")"

CURL_STATUS=0
HTTP_CODE=$(curl -sS --max-time 15 \
  -o "${RESPONSE_FILE}" \
  -w "%{http_code}" \
  -X POST "https://platform-api.max.ru/messages?chat_id=${CHAT_ID}" \
  -H "Authorization: ${TOKEN}" \
  -H "Content-Type: application/json" \
  --data @"${BODY_FILE}") || CURL_STATUS=$?

RESPONSE="$(cat "${RESPONSE_FILE}" 2>/dev/null || true)"
rm -f "${BODY_FILE}" "${RESPONSE_FILE}"

echo "DEBUG: HTTP_CODE=${HTTP_CODE} CURL_STATUS=${CURL_STATUS}"
echo "DEBUG: Response: ${RESPONSE}"

if [ "${CURL_STATUS}" -ne 0 ] || [ "${HTTP_CODE}" != "200" ]; then
  echo "ERROR: Nepodařilo se odeslat. Response: ${RESPONSE}"
  exit 1
fi

Formát notifikace a extrakce dat

Notifikace se skládá z proměnných GitLab CI:

  • CI_PROJECT_NAME — projekt
  • CI_COMMIT_BRANCH — větev
  • CI_COMMIT_SHORT_SHA — verze
  • CI_COMMIT_TITLE — zpráva commitu (escapováno sed 's/\/\\/g; s/\"\"/g')
  • CI_COMMIT_AUTHOR — autor
  • CI_PIPELINE_URL — odkaz na pipeline

Příklad:

✅ Úspěšné nasazení na DEV

📦 Projekt: u.clinic
🌍 Prostředí: DEV
🌿 Větev: dev
🔖 Verze: f7a249d0
💬 Commit: fix(ci): zlepšit zpracování notifikací MAX
👤 Autor: Anton Kurganskii
⏰ Čas (CET): 2026-04-08 21:29:07
⏱️ Doba pipeline: 9 min 52 s

🔗 Pipeline: https://git.tverdasoft.ru/...

✓ Nasazení dokončeno úspěšně

Výběr obrazu a alternativy

curlimages/curl:8.7.1 vybrán místo alpine + jq: nevyžaduje síť pro instalaci balíčků, funguje z cache s pull_policy: if-not-present. apk add v Alpine selhal kvůli nedostupným repozitářům.

Co je důležité

  • DNS selhání v Dockeru se maskuje jako payload chyby kvůli nestabilnímu spojení v GitLab runneru.
  • Nejdřív test docker run curl ... platform-api.max.ru na serveru runneru — exit code 6 ukazuje na DNS.
  • Oprava: dns = ["8.8.8.8", "8.8.4.4"] v [runners.docker] config.toml.
  • Diagnostika: logovat BODY_SIZE, HTTP_CODE, celou Response.
  • Pro commit zprávy: tr -d '\r\n' + sed escapování.

— Editorial Team

Advertisement 728x90

Číst dál