Zurück zur Startseite

DNS Docker in GitLab CI: Behebung für MAX-Benachrichtigungen

MAX-Benachrichtigungs-Debugging-Fall aus GitLab CI: proto.payload-Symptome maskierten DNS-Problem in Docker. Behebung über Runners config.toml, funktionsfähiges Skriptvorlage mit Diagnostik.

GitLab CI + MAX: DNS hat Deploy-Benachrichtigungen unterbrochen
Advertisement 728x90

Docker-DNS-Problem stoppte GitLab CI MAX-Benachrichtigungen: Debuggen und Beheben

Ein GitLab-CI-Job, der nach dem Deployment eine Benachrichtigung an MAX Messenger senden sollte, wurde zu einer wochenlangen Debug-Albtraum. Anfragen wurden gesendet, aber die Antworten kamen als {"code":"proto.payload","message":"Can't deserialize body"} zurück. Die Logs deuteten auf ein Problem mit dem Request-Body hin, daher konzentrierten wir uns auf die Erstellung des JSON-Payloads.

Trotz Vereinfachungen klappte nichts. Die Umgebungsvariablen MAX_BOT_TOKEN und MAX_NOTIFY_CHAT_ID waren gesetzt, und curl war im Image vorhanden. Aber die Nachrichten kamen nie an.

Fehlgeschlagene Payload-Versuche

Das Team probierte mehrere Varianten aus:

Google AdInline article slot
  • printf in Datei: printf '{"text":"%s"}' "$MSG" > /tmp/payload.json; curl ... -d @/tmp/payload.json. Antwort: Can't deserialize body.
  • jq in Alpine: Wechsel zu alpine:3.20 mit apk add jq. jq -nc --arg t "$MSG" '{text:$t}' > /tmp/payload.json. Gleiche Antwort.
  • jq in Variable: PAYLOAD=$(jq ...); curl ... --data "${PAYLOAD}". Nun Empty request body, dazu scheiterte apk add mit Exit-Code 2 wegen nicht erreichbarer Repos.
  • Hartkodiert: curl ... -d '{"text":"test ok"}'. Wieder Empty request body.

Fortschritt war minimal: Der Request-Body erreichte den Server nie zuverlässig.

Der entscheidende Test: Host vs. Container

Ein direkter Vergleich deckte den Übeltäter auf:

Vom Host:

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, Nachricht zugestellt.

Aus Docker:

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

Diagnose: DNS-Auflösung schlug im Docker-Container fehl, obwohl sie auf dem Host problemlos lief.

Test mit --dns 8.8.8.8 bestätigte es: Die Nachricht ging sofort raus.

Warum DNS als JSON-Fehler getarnt war

Wir erwarteten einen klaren DNS-Fehler wie curl Exit-Code 6 ohne HTTP-Antwort. Stattdessen gab MAX JSON mit proto.payload zurück. Das Problem lag in der individuellen DNS-Konfiguration des GitLab-Runners für Job-Container. Dadurch entstanden instabile Verbindungen – Request-Bodys wurden abgeschnitten oder ganz fallen gelassen. MAX interpretierte den leeren Input als Deserialisierungsfehler.

Lösung: DNS in der Runner-config.toml

Lösung: DNS-Server in /etc/gitlab-runner/config.toml ergänzen:

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

Neustart: sudo gitlab-runner restart.

Alternative beim Docker-Daemon in /etc/docker/daemon.json:

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

sudo systemctl restart docker.

Nach der Korrektur funktionierte der hartkodierte Payload sofort.

Funktionsfähiges Skript-Template

Finales Skript mit Diagnose und Variablenbereinigung:

if [ -z "$MAX_BOT_TOKEN" ] || [ -z "$MAX_NOTIFY_CHAT_ID" ]; then
  echo "WARN: MAX_BOT_TOKEN or MAX_NOTIFY_CHAT_ID not set"
  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: Failed to send. Response: ${RESPONSE}"
  exit 1
fi

Benachrichtigungsformat und Datenextraktion

Benachrichtigungen beziehen sich auf GitLab-CI-Variablen:

  • CI_PROJECT_NAME — Projekt
  • CI_COMMIT_BRANCH — Branch
  • CI_COMMIT_SHORT_SHA — Commit-Hash
  • CI_COMMIT_TITLE — Commit-Nachricht (escaped mit sed 's/\/\\/g; s/\"\"/g')
  • CI_COMMIT_AUTHOR — Autor
  • CI_PIPELINE_URL — Pipeline-Link

Beispiel:

✅ Erfolgreiches DEV-Deployment

📦 Projekt: u.clinic
🌍 Umgebung: DEV
🌿 Branch: dev
🔖 Version: f7a249d0
💬 Commit: fix(ci): improve MAX notification handling
👤 Autor: Anton Kurganskii
⏰ Zeit (UTC): 2026-04-08 21:29:07
⏱️ Pipeline-Dauer: 9 Min. 52 Sek.

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

✓ Deployment erfolgreich abgeschlossen

Image-Auswahl und Alternativen

curlimages/curl:8.7.1 schlägt alpine + jq: Kein Netzwerk für Pakete nötig, läuft aus Cache mit pull_policy: if-not-present. Bei Alpine scheiterte apk add wegen nicht erreichbarer Repos.

Wichtige Erkenntnisse

  • Docker-DNS-Ausfälle in GitLab-Runners täuschen Payload-Fehler durch instabile Verbindungen vor.
  • Zuerst testen: docker run curl ... platform-api.max.ru auf dem Runner-Host – Exit-Code 6 signalisiert DNS.
  • Fix: dns = ["8.8.8.8", "8.8.4.4"] in [runners.docker] der config.toml.
  • Debuggen: BODY_SIZE, HTTP_CODE, vollständige Response loggen.
  • Für Commit-Nachrichten: tr -d '\r\n' + sed-Escaping.

— Editorial Team

Advertisement 728x90

Weiterlesen