# Staging 502 원인 분석 및 재발방지 조치

## 개요

- 대상: `https://sso.hmac.kr`
- 관련 Actions run: `https://gitea.hmac.kr/baron/baron-sso/actions/runs/962`
- 관련 이슈: `https://gitea.hmac.kr/baron/baron-sso/issues/1025`
- 확인일: 2026-06-08

Gitea Actions run 962는 성공으로 종료됐지만, `https://sso.hmac.kr`는 `502 Bad Gateway`를 반환했습니다. 장애는 애플리케이션 코드 레벨의 단일 오류가 아니라 스테이징 Docker Compose의 restart policy 누락으로 인해 호스트 또는 Docker 데몬 재시작 후 장기 실행 컨테이너가 자동 복구되지 않은 문제였습니다.

## 근본 원인

`docker/staging_pull_compose.template.yaml`에서 일부 장기 실행 서비스에 `restart: unless-stopped` 또는 `restart: always`가 없었습니다.

호스트 상태 확인 결과, 재시작 정책이 있던 `baron_gateway`, Postgres, Redis, ClickHouse, Oathkeeper는 자동 복구됐지만, 정책이 없던 다음 컨테이너는 종료 상태로 남았습니다.

- `baron_backend`
- `baron_userfront`
- `baron_adminfront`
- `baron_devfront`
- `baron_orgfront`
- `ory_kratos`
- `ory_hydra`
- `ory_keto`
- `ory_postgres`
- `ory_clickhouse`

결과적으로 외부 reverse proxy와 `baron_gateway`는 살아 있었지만, gateway의 upstream인 UserFront/Backend/AdminFront/DevFront/OrgFront가 없어 외부 도메인이 502를 반환했습니다. `/auth`, `/oidc` 계열 요청은 Oathkeeper가 살아 있어 JSON 404까지 도달했습니다.

## 조치

- `docker/staging_pull_compose.template.yaml`의 장기 실행 서비스에 `restart: unless-stopped`를 추가했습니다.
- 마이그레이션/유틸 컨테이너에는 restart policy를 추가하지 않았습니다.
  - `kratos-migrate`
  - `hydra-migrate`
  - `keto-migrate`
  - `ory_stack_check`
  - `init-rp`
  - `oathkeeper_logs_init`
  - `infra_check`
- `.gitea/workflows/staging_code_pull.yml`의 배포 후 검증 범위를 확장했습니다.
  - `baron_backend` health 확인
  - `baron_userfront` 확인
  - `baron_gateway` 확인
- 이 검증은 배포 직후 smoke test이며, 배포 이후 장애를 감지하는 운영 알람 대책은 아닙니다.

## 재발방지 테스트

추가된 정책 테스트:

```sh
sh test/staging_pull_restart_policy_test.sh
```

기존 배포 정책 테스트 강화:

```sh
sh test/staging_frontend_deploy_policy_test.sh
```

두 테스트는 다음을 보장합니다.

- 스테이징 장기 실행 서비스에는 restart policy가 있어야 합니다.
- 마이그레이션/유틸 컨테이너는 restart 대상에 포함하지 않습니다.
- 배포 성공 판정은 내부 프론트엔드뿐 아니라 backend, userfront, gateway까지 확인해야 합니다.

## 남은 과제

배포 이후 발생하는 장애를 감지하고 알림을 보내려면 별도 상시 모니터링 구성이 필요합니다. 배포 워크플로 내부 health check는 post-deploy smoke test일 뿐이므로, 머신 재시작 이후 장애 감지나 알림의 대체 수단으로 사용하지 않습니다.

## 검증 결과

복구 후 다음 외부 URL이 정상 응답을 반환했습니다.

- `https://sso.hmac.kr/` -> 200
- `https://sso.hmac.kr/ko/signin` -> 200
- `https://sadmin.hmac.kr/` -> 200
- `https://sdev.hmac.kr/` -> 200
- `https://sorg.hmac.kr/` -> 200
