← 개발 로그 목록

website: 헬스체크 대기 중에도 로그가 보이게 개선

/ 5분 분량 / 개발 로그

멋사 경희대 사이트 프로젝트에서 CD 파이프라인의 헬스체크 로직이 대기 중엔 아무 로그도 남기지 않던 문제를 고치고, 실패 시 컨테이너 로그까지 CD 로그에 바로 남기도록 개선한 PR입니다.

요약

이 PR은 .github/workflows/cd.yml의 헬스체크 루프를 수정해서, 헬스체크가 진행되는 동안 매 시도마다 HTTP 상태 코드를 로그에 찍고, 타임아웃으로 실패했을 때 해당 서비스 컨테이너의 최근 로그(50줄)까지 CD 로그에 남기도록 만들었습니다. 2026년 7월 12일에 생성되어 1분도 안 되어 바로 병합됐고, 변경 규모도 8줄 추가 / 3줄 삭제로 작지만 디버깅 경험을 크게 개선하는 수정입니다.

배경 및 목적

PR 설명에 따르면, 작성자가 stage 배포(workflow_dispatch)를 돌리던 중 헬스체크 단계에서 멈춰있는 걸 지켜보게 됐습니다. 문제는 기존 헬스체크가 curl -sf를 쓰고 있었다는 것인데, 이 옵션은 실패해도 아무 출력도 하지 않는 완전 침묵 모드입니다. 그래서 헬스체크가 대기 중인 동안에는 CD 로그에 아무것도 찍히지 않았고, 실제로 무슨 일이 벌어지고 있는지 CD 로그만 봐서는 전혀 알 수 없는 상태였습니다.

결국 원인을 알아내려면 SSH로 서버에 직접 들어가서 docker compose logs를 확인해야 했는데, 이번에 그렇게 해서 찾아낸 원인이 JWT_SECRET 키 길이 부족으로 인한 크래시 루프였습니다. 문제 자체는 해결했지만, "매번 이렇게 SSH로 들어가야 하나"라는 불편함이 남았고, 그걸 이번 PR로 개선한 셈입니다.

구현 내용

변경된 파일은 .github/workflows/cd.yml 하나이고, 헬스체크 루프 부분만 수정했습니다.

기존 코드는 이런 식이었습니다:

until curl -sf http://localhost:${{ steps.config.outputs.port }}/actuator/health; do
  sleep 3
  ELAPSED=$((ELAPSED + 3))
  if [ $ELAPSED -ge 120 ]; then
    echo "❌ 헬스체크 실패"
    exit 1
  fi
done

curl -sf는 요청이 실패하든 헬스체크가 아직 준비 안 됐든 조용히 다음 루프로 넘어가기 때문에, 3초마다 도는 루프 안에서 실제로 무슨 상태인지 전혀 알 수 없는 구조였습니다.

이걸 다음과 같이 바꿨습니다:

until STATUS={{ steps.config.outputs.port }}/actuator/health) && [ "$STATUS" == "200" ]; do
  echo "[{STATUS:-연결 실패}"
  sleep 3
  ELAPSED=$((ELAPSED + 3))
  if [ $ELAPSED -ge 120 ]; then
    echo "❌ 헬스체크 실패 — 마지막 응답: HTTP ${STATUS:-연결 실패}"
    echo "--- ${{ steps.config.outputs.service }} 최근 로그 ---"
    docker compose -f docker-compose.yml logs --tail=50 ${{ steps.config.outputs.service }}
    exit 1
  fi
done

핵심 변화는 두 가지입니다.

  1. 상태 코드를 직접 뽑아서 로그로 출력: curl -sf 대신 curl -s -o /dev/null -w '%{http_code}'로 HTTP 상태 코드만 문자열로 받아오고, 이 값이 200인지 비교하는 방식으로 루프 조건을 바꿨습니다. 그리고 루프를 도는 매 시도마다 [{STATUS:-연결 실패} 형태로 진행 상황을 찍어서, CD 로그만 봐도 헬스체크가 어느 상태에서 멈춰있는지 실시간으로 보이게 했습니다. 연결 자체가 안 될 때는 STATUS 변수가 비어있을 수 있어서 ${STATUS:-연결 실패}로 처리해 빈 값이 그냥 노출되지 않도록 했습니다.

  2. 실패 시 컨테이너 로그 tail을 CD 로그에 바로 첨부: 120초 타임아웃에 도달해 실패로 처리될 때, 마지막 응답 상태를 출력한 뒤 docker compose logs --tail=50 ${{ steps.config.outputs.service }}로 해당 서비스의 최근 로그 50줄을 그대로 CD 로그에 남기도록 했습니다. 이러면 SSH로 서버에 들어가지 않아도 GitHub Actions 로그 화면에서 바로 크래시 원인을 확인할 수 있습니다.

배운 점 및 개선점

이번 PR은 코드 자체는 짧지만, "침묵하는 도구"가 디버깅을 얼마나 어렵게 만드는지 직접 겪고 나서 나온 수정이라는 점이 눈에 띕니다. curl -sf처럼 조용히 실패하는 옵션은 정상 동작할 때는 로그가 깔끔해서 편하지만, 문제가 생겼을 때는 오히려 아무 단서도 안 남는다는 트레이드오프가 있습니다. 이번에 JWT_SECRET 문제를 SSH로 직접 들어가서 찾아낸 경험이 있었기 때문에, "이 정보를 CD 로그에 미리 남겨뒀으면 SSH 없이 바로 알 수 있었겠다"는 생각으로 바로 개선까지 이어진 흐름이 자연스럽습니다.

앞으로는 헬스체크뿐 아니라 CD 파이프라인의 다른 단계들에서도 비슷하게 "실패했을 때 SSH 없이 원인을 알 수 있는가"를 기준으로 점검해볼 만합니다. 예를 들어 배포 자체가 실패하거나 nginx 설정 문제가 생겼을 때도 관련 로그를 CD 로그에 자동으로 남기는 방향으로 확장할 수 있을 것 같습니다.