Skip to content

Fix: SSL 만료로 보이던 문제 원인 분석 및 구조 개선 #175

Merged
lunarbae628 merged 4 commits into
devfrom
staging
Feb 3, 2026
Merged

Fix: SSL 만료로 보이던 문제 원인 분석 및 구조 개선 #175
lunarbae628 merged 4 commits into
devfrom
staging

Conversation

@lunarbae628
Copy link
Copy Markdown
Collaborator

@lunarbae628 lunarbae628 commented Jan 31, 2026

🛰️ Issue Number

🪐 작업 내용

기존에 certbot_renew 컨테이너에서 인증서 갱신 후 nginx한테 HUP 시그널 보내려고 Docker API를 wget으로 호출하고 있었는데, certbot 이미지에 들어있는 wget이 BusyBox 버전이라 --method=POST, --unix-socket 옵션을 지원 안 해서 nginx reload가 계속 실패하고 있었습니다.
그래서 certbot 입장에서는 인증서 갱신이 잘 됐다고 나오는데, nginx는 갱신된 인증서를 reload 못해서 실제로는 SSL 만료처럼 보여 서비스가 빠그라져있었스빈다.

변경 내용

  1. nginx reload 방식 수정
    wget 대신 curl 쓰도록 바꿔서 nginx reload가 제대로 되게 했습니다. 아마
  2. stg 인증서 발급 구조 정리
    curl 적용하면서 보니까 stg는 8080 포트만 열려있어서 certbot webroot 방식(80포트 필요)으로 인증서 발급/갱신이 안 된다고 하더라구요 그때는 어케했는지 모르겠지만 그래서 dns-01 방식도 고려해봤는데 저희 도메인이 내도메인.한국이라는 밤티 도메인이라 설정 복잡해지는 것 대비 이득이 별로 없어서 그냥 구조를 좀 바꿨습니다:

stg에서 인증서 발급/갱신 관련 컨테이너 제거
dev 환경에서만 인증서 발급/갱신 관리
stg nginx는 그 인증서 디렉토리 마운트해서 쓰기 👍

  1. 모니터링 추가
    여러분들은 이걸 포트폴리오에 넣는지는 모르겠지만 저는 넣습니다. 그래서 인사담당관이 서비스 링크까지 들어왔는데 안되면 나가리잖아요? 그래서
    실제 서빙되는 인증서 기준으로 만료 임박상황 및 서버 접속 불가 상황 생기면 Slack 알림 가게 해봤습니다.
    인증서 체크(cron 하루 1회), 서버 접속 체크(cron 6시간 마다)
0 */6 * * * /srv/check_server_alive.sh >/dev/null 2>&1
0 3 * * * /srv/check_cert_expiry.sh >/dev/null 2>&1

이제 인증서 갱신 실패하거나 nginx reload 안 되거나 서버 장애 나면 미리 알 수 있습니다.

요약

인증서 갱신은 되는데 적용이 안 되던 문제 해결
인증서 관리는 dev에서만 하고 stg는 그냥 갖다 쓰게 구조 정리
SSL 만료/서버 장애 사전 감지 체계 추가

📚 Reference

check_cert_expiry.sh(인증서 만료 체크 스크립트)

#!/usr/bin/env bash
set -euo pipefail

ENV_FILE="/srv/docsa-alert.env"
THRESHOLD_DAYS=14

DOMAINS=(
  "api.docsa.o-r.kr:443"
  "stg.api.docsa.o-r.kr:8443"
)

[[ -f "$ENV_FILE" ]] && source "$ENV_FILE"

notify_slack () {
  local msg="$1"
  [[ -z "${SLACK_WEBHOOK_URL:-}" ]] && return 0
  curl -sS -X POST -H 'Content-type: application/json' \
    --data "{\"text\":${msg@Q}}" \
    "$SLACK_WEBHOOK_URL" >/dev/null || true
}

failed=0
now_epoch=$(date -u +%s)

for target in "${DOMAINS[@]}"; do
  host="${target%%:*}"
  port="${target##*:}"

  exp_date=$(echo | openssl s_client -connect "$host:$port" -servername "$host" 2>/dev/null \
    | openssl x509 -noout -enddate \
    | cut -d= -f2 || true)

  [[ -z "$exp_date" ]] && {
    notify_slack "$host:$port 인증서 만료일 조회 실패"
    failed=1
    continue
  }

  exp_epoch=$(date -u -d "$exp_date" +%s)
  remain_days=$(( (exp_epoch - now_epoch) / 86400 ))

  if (( remain_days <= THRESHOLD_DAYS )); then
    notify_slack "⚠️ $host:$port 인증서 만료 임박 D-${remain_days} (만료: $exp_date UTC)"
    failed=1
  fi
done

exit "$failed"

check_server_alive.sh(서버 상태 검사 스크립트)

#!/usr/bin/env bash
set -euo pipefail

ENV_FILE="/srv/docsa-alert.env"
TIMEOUT=5

TARGETS=(
  "api.docsa.o-r.kr:443"
  "stg.api.docsa.o-r.kr:8443"
)

[[ -f "$ENV_FILE" ]] && source "$ENV_FILE"

notify_slack () {
  local msg="$1"
  [[ -z "${SLACK_WEBHOOK_URL:-}" ]] && return 0
  curl -sS -X POST -H 'Content-type: application/json' \
    --data "{\"text\":${msg@Q}}" \
    "$SLACK_WEBHOOK_URL" >/dev/null 2>&1 || true
}

for target in "${TARGETS[@]}"; do
  host="${target%%:*}"
  port="${target##*:}"

  if ! timeout "${TIMEOUT}" bash -c "</dev/tcp/${host}/${port}" 2>/dev/null; then
    notify_slack "🚨 아아 공습경보 서버 불량: ${host}:${port}"
  fi
done

✅ Check List

  • 코드가 정상적으로 컴파일되나요?
  • 테스트 코드를 통과했나요?
  • merge할 브랜치의 위치를 확인했나요?
  • Label을 지정했나요?

@lunarbae628 lunarbae628 self-assigned this Jan 31, 2026
Copy link
Copy Markdown
Collaborator

@ky1nonly ky1nonly left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

수고하셨습니다👍👍

Copy link
Copy Markdown
Collaborator

@2ternal 2ternal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

신경 못쓰고 있었는데...
부지런하시네요 수고하셨습니다!!

@lunarbae628 lunarbae628 merged commit 60e6974 into dev Feb 3, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants