요약(Summary)
main 병합 직후 Nginx 502 발생(S1), API 전체 영향, 2025-08-22 21:10 ~ 21:27 (KST).
원인은 application.yml 설정 변경으로 앱 기동 실패 → 업스트림 미응답.
환경(Context)
- STACK: Spring Boot 3.5.x, Java 21, MySQL 8.x (AWS RDS), Nginx(RevProxy), Docker, GitHub Actions
- 배포 흐름: GitHub → Actions 빌드/푸시 → EC2 Docker run → Nginx 리버스 프록시
- 프로필: application-{dev,prod}.yml (운영은 prod)
- RDS 연결 정책: EC2 보안그룹 ↔ RDS 3306 인바운드 허용
- 아키텍처(간단 도식):
flowchart LR
Client --> Nginx --> App(Spring Boot) --> RDS[(MySQL)]
증상 & 로그(Symptoms & Logs)
Nginx 에러 로그
2025/08/22 21:11:02 [error] 1234#1234: *15 upstream prematurely closed connection while reading response header from upstream,
upstream: http://127.0.0.1:8080/actuator/health, host: "api-bytecookie.click"
컨테이너 로그
org.yaml.snakeyaml.error.YAMLException: java.lang.IllegalArgumentException: Failed to bind properties under 'server'...
Caused by: ParserException: while parsing a block mapping; expected <block end>, but found '?'
프로세스 상태
docker ps # 앱 컨테이너 Exited(1)
타임라인(Timeline, 예시)
21:10 — 모니터링에서 /actuator/health 실패 감지(5xx 스파이크)
21:12 — docker logs로 yml 바인딩 오류 확인
21:14 — 문제 PR 식별(application.yml 다발 수정 포함)
21:16 — PR Revert 결정(가역·신속 복구 우선)
21:20 — Revert PR 머지 → GHA 자동 재배포 시작
21:25 — 앱 기동 정상, Nginx 200 응답 확인
21:27 — 사건 종료, RCA/액션 항목 수립 착수
근본 원인(Root Cause Analysis)
표면 원인:
- application.yml의 포트/CORS/DB/프로필 키 변경 중 문법/바인딩 불일치로 Spring Boot 기동 실패.
- SnakeYAML 파서 단계 혹은 바인딩 단계에서 예외 발생. MediumSnyk
근본 원인: 설정 변경 검증 부재
- yml 변경에 대한 정적/동적 검증이 파이프라인에 없었음
- 리뷰/헬스체크 게이트가 yml/diff에 특화돼 있지 않음
카테고리: 구성관리/프로필/리뷰 프로세스 미흡
재현하기(Reproduction)
전제: 로컬 Docker, JDK 21, prod 프로필용 env 파일 존재
# 1) 문제 커밋 체크아웃
git checkout <problem-commit>
# 2) 로컬 빌드 & 실행(문제 yml로)
./gradlew bootJar
docker run --rm --name app -p 8080:8080 \
-e SPRING_PROFILES_ACTIVE=prod \
-v $PWD/src/main/resources/application-prod.yml:/app/config/application-prod.yml \
ghcr.io/your/app:<problem-tag>
# 3) 증상 확인
curl -i http://localhost:8080/actuator/health # 기대: 실패/연결 종료
로컬/H2만 통과하는 허점:
운영 프로필(prod) yml이 실제 RDS/프록시 전제와 달라 로컬 성공, 운영 실패 가능.
해결(Resolution) & 롤백 전략(Rollback)
- 실행
- GitHub에서 PR Revert(웹 UI Revert) 머지 → GHA 자동 재배포
- 배포 완료 후 헬스체크 통과 및 에러율 정상화 확인
- 문제 PR 재현/세분화 후, 스테이징에서 Forward Fix로 재반영 준비
- 참고: GitHub PR Revert/git revert -m 1 <merge-commit>는 머지 커밋 되돌림의 표준 경로. GitHub DocsAtlassian
- 선택 근거(왜 Revert?)
- 장애 시 MTTR 최단화 + 가역적/감사 가능한 복구
- 설정 다발 변경 상황에서 부분 핫픽스 리스크(누락/중복) 회피
Revert vs merge -s ours: ours는 충돌 해결용 전략으로 “내 브랜치 내용 채택”에 가깝고, 원복/추적성 측면에선 Revert가 적합.
왜 이게 통했나(Why it worked)
- Revert는 직전 정상 상태로의 원자적 되돌림 → Nginx 업스트림(App) 응답이 즉시 복귀.
- 문제는 네트워크가 아니라 애플리케이션 기동 실패였고, Revert로 올바른 yml 구성이 복원되며 파서/바인딩 예외가 제거됨.
검증(Verification) & 스모크 테스트
# Nginx -> App 경로
curl -i https://api-bytecookie.click/actuator/health
# 컨테이너 상태
docker ps
docker logs -n 200 app
# 리스닝 포트
ss -lntp | grep -E '80|8080'
# RDS 연결성(EC2에서)
telnet <rds-endpoint> 3306
- Swagger /swagger-ui/index.html 응답 확인
- 에러율/지표(5xx, 응답시간 P95) 정상화 대시보드 스크린샷 확보 → (내부 링크 예정)
재발 방지(Prevention) 체크리스트
정책
- 운영 프로필(prod)은 spring.jpa.hibernate.ddl-auto: validate 고정(DDL은 마이그레이션 도구로만).
- 운영 yml 변경은 머지 전 스테이징 기동 + 헬스 스크린샷 첨부 의무화.
도구/자동화
- YAML 린트 + 스키마 바인딩 테스트:
- yamllint/action-yamllint로 정적 검사
- ConfigProperties 바인딩 테스트(Spring Boot test) 추가 — 잘못된 키/타입 시 실패.
- 스모크 부트: CI에서 SPRING_PROFILES_ACTIVE=prod로 부트 드라이런(앱 15초 부팅/종료)
- Secrets/Env diff 가드: GHA에서 필수 ENV 존재/형식 검증
- SnakeYAML 최신 유지 + CVE 워치(Dependabot/Snyk) Java Code Geeks
리뷰 포인트
- application-*.yml, Dockerfile, GHA 워크플로우 diff는 2인 승인 + 헬스 성공 증빙
- Nginx 업스트림 포트/호스트와 Spring server.port 일치 재확인
모니터링/알림
- /actuator/health 실패율, 5xx, 컨테이너 재시작 횟수 알림
- 배포 후 5분 집중 모니터링 윈도우 운영
부록(Playbooks / Snippets)
Nginx 업스트림 포트 확인
sudo cat /etc/nginx/conf.d/app.conf # upstream 포트 확인
sudo nginx -t && sudo systemctl reload nginx
docker logs에서 yml 파싱 에러 확인
docker logs app | grep -E "YAMLException|ParserException|Failed to bind"
PR Revert → 재배포
# 머지 커밋 기준 되돌림
git revert -m 1 <merge-commit>
git push origin main # GHA 트리거
#여기서부턴 선택, 직전 정상 이미지 롤백
docker pull ghcr.io/org/app:<last_green>
docker run -d --name app -p 8080:8080 --env-file .env ghcr.io/org/app:<last_green>
관련 파일(Related Files)
- src/main/resources/application-prod.yml (주석으로 변경 의도 명시)
- /etc/nginx/conf.d/app.conf
- .github/workflows/deploy.yml
- Dockerfile
- com.example.config.* (ConfigProperties 바인딩 클래스)
메타데이터(Metadata)
- RACI
- R(실행): 백엔드 온콜
- A(책임): SRE/플랫폼
- C(협의): 프론트, 기획
- I(통지): PO, 경영지원
- 링크: (내부 링크 예정) — 사건 티켓, Revert PR, 배포 로그, 대시보드
- TAGS: #AWS #Nginx #SpringBoot #YAML #SnakeYAML #Docker #GitHubActions #RDS #CI_CD
'Dev Log (개발 문제 & 해결 기록) > Error & Fix (에러 해결 기록)' 카테고리의 다른 글
[오류로그 #2][S2#1] 8080 포트 충돌로 앱 기동 실패 — 중복기동 점검 체크리스트 (0) | 2025.09.05 |
---|