홈으로 돌아가기

GitLab CI의 DNS Docker: MAX 알림 수정

GitLab CI의 MAX 알림 디버깅 사례: proto.payload 증상이 Docker의 DNS 문제를 숨김. runner의 config.toml을 통한 수정, 진단 포함 작동 스크립트 템플릿.

GitLab CI + MAX: DNS가 배포 알림을 망침
Advertisement 728x90

Docker DNS 문제로 GitLab CI MAX 알림 실패: 디버깅과 해결

배포 후 MAX 메신저로 알림을 보내는 GitLab CI 작업이 일주일간의 디버깅 지옥으로 변했습니다. 요청은 보내졌지만, 응답은 {"code":"proto.payload","message":"Can't deserialize body"}로 돌아왔어요. 로그가 요청 본문 문제를 가리키길래 JSON 페이로드를 다듬는 데 집중했습니다.

간단하게 해도 안 됐어요. 환경 변수 MAX_BOT_TOKENMAX_NOTIFY_CHAT_ID는 설정됐고, 이미지에 curl도 있었는데 메시지가 도착하지 않았습니다.

실패한 페이로드 시도

팀은 여러 번 시도해봤습니다:

Google AdInline article slot
  • 파일로 printf: printf '{"text":"%s"}' "$MSG" > /tmp/payload.json; curl ... -d @/tmp/payload.json. 응답: Can't deserialize body.
  • Alpine에서 jq: alpine:3.20으로 전환해 apk add jq. jq -nc --arg t "$MSG" '{text:$t}' > /tmp/payload.json. 같은 응답.
  • 변수로 jq: PAYLOAD=$(jq ...); curl ... --data "${PAYLOAD}". 이제 Empty request body, 게다가 apk add가 리포지토리 접근 불가로 종료 코드 2.
  • 하드코딩: curl ... -d '{"text":"test ok"}'. 또 Empty request body.

진척이 거의 없었어요: 요청 본문이 서버에 도달하지 못했습니다.

핵심 테스트: 호스트 vs 컨테이너

병렬 비교로 원인을 찾았습니다:

호스트에서:

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, 메시지 전달 성공.

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"}'

종료 코드 6: Could not resolve host: platform-api.max.ru.

Google AdInline article slot

진단: Docker 컨테이너 안에서 DNS 해석 실패. 호스트는 정상이었어요.

--dns 8.8.8.8 테스트로 확인: 메시지 즉시 전송.

JSON 오류로 위장된 DNS 문제

curl 종료 코드 6 같은 DNS 오류를 기대했는데, MAX가 proto.payload JSON으로 응답했어요. GitLab 러너의 작업 컨테이너 커스텀 DNS 설정 때문이었죠. 이로 연결이 불안정해져 요청 본문이 잘리거나 사라졌고, MAX가 빈 입력을 역직렬화 오류로 봤습니다.

해결: 러너 config.toml에 DNS 추가

해결책: /etc/gitlab-runner/config.toml에 DNS 서버 추가:

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

재시작: sudo gitlab-runner restart.

Docker 데몬 대안 /etc/docker/daemon.json:

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

sudo systemctl restart docker.

수정 후 하드코딩 페이로드가 첫 실행에서 작동.

작동 스크립트 템플릿

진단과 변수 정리 포함 최종 스크립트:

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

알림 형식과 데이터 추출

알림은 GitLab CI 변수에서 가져옵니다:

  • CI_PROJECT_NAME — 프로젝트
  • CI_COMMIT_BRANCH — 브랜치
  • CI_COMMIT_SHORT_SHA — 커밋 해시
  • CI_COMMIT_TITLE — 커밋 메시지 (sed 's/\/\\/g; s/\"\"/g' 이스케이프)
  • CI_COMMIT_AUTHOR — 작성자
  • CI_PIPELINE_URL — 파이프라인 링크

예시:

✅ DEV 배포 성공

📦 프로젝트: u.clinic
🌍 환경: DEV
🌿 브랜치: dev
🔖 버전: f7a249d0
💬 커밋: fix(ci): improve MAX notification handling
👤 작성자: Anton Kurganskii
⏰ 시간 (UTC): 2026-04-08 21:29:07
⏱️ 파이프라인 소요: 9분 52초

🔗 파이프라인: https://git.tverdasoft.ru/...

✓ 배포 완료

이미지 선택과 대안

curlimages/curl:8.7.1alpine + jq보다 우수: 패키지 네트워크 불필요, pull_policy: if-not-present로 캐시 실행. Alpine apk add는 리포지토리 접근 실패.

주요 교훈

  • GitLab 러너 Docker DNS 실패가 불안정 연결으로 페이로드 오류처럼 보임.
  • 먼저 테스트: 러너 호스트에서 docker run curl ... platform-api.max.ru — 종료 코드 6으로 DNS 확인.
  • 수정: config.toml [runners.docker]dns = ["8.8.8.8", "8.8.4.4"].
  • 디버그: BODY_SIZE, HTTP_CODE, 전체 Response 로그.
  • 커밋 메시지: tr -d '\r\n' + sed 이스케이프.

— Editorial Team

Advertisement 728x90

다음 읽기