[POSTMORTEM] 260819 - Oracle Cloud Free Tier 축소로 인한 k-devcon 운영 서버 다운 및 홈서버 이관
# 260819 - Oracle Cloud Free Tier 축소로 인한 k-devcon 운영 서버 다운 및 홈서버 이관
| 항목 | 내용 |
| -------------- | -------------------------------------------------------------------- |
| Incident Level | SEV-1 (운영 전면 중단, 데이터 손실 없음) |
| 발생일 | 2026-08-19 08:08 KST (Oracle Free Tier 축소로 구 운영 서버가 다운됨) |
| 인지일 | 2026-08-19 22:20 KST (강성욱님 확인) |
| 담당 | 박종훈 |
| 영향 서비스 | k-devcon.com |
| 영향 범위 | 운영 서비스 전면 중단 약 39시간 17분 (08-19 08:08 ~ 08-20 23:25) |
| 상태 | 새로운 서버로 컷오버 완료 |
What Happened
Oracle Cloud Free Tier 지원이 갑작스럽게 축소되면서 k-devcon 운영 서버가 다운되었다.
데이터를 살리려면 일단 서버를 띄워야 했고 이를 위해 결제 수단을 빠르게 등록하였지만, 플랜을 변경하는 데에는 1-2일 정도 필요한 것으로 파악되었다.
일단 서버가 다시 뜨면 데이터를 복구한 후 홈서버로 이관하기로 결정했다.
여기서 까다로웠던 점은 홈서버가 비어 있지 않았다는 것이다. 같은 호스트에서 dev 환경이 라이브로 돌고 있었다. 운영 파일들은 ~/devcon-prd/로 옮겨두었지만 그 복사본을 그대로 기동하면 compose 프로젝트명·container_name·호스트 포트(5173/8080)·이미지 태그(devcon-server:latest)·og-html 마운트 경로가 전부 겹쳐 충돌이 발생한다. 특히 이미지 태그 충돌은 dev 빌드가 운영 이미지를 덮어써 dev 코드가 운영으로 승격되는 경로였다.
그래서 실제 과제는 "서버를 복구한다"가 아니라 "살아 있는 dev를 죽이지 않고 그 옆에 prd를 새로 세운다" 였고 네 갈래로 나뉘었다.
| 갈래 | 내용 |
| -------- | ---------------------------------------------------------------------------------------------- |
| DB | 홈서버 MySQL에 devcon_prd 신설 → 백업 SQL import → 기존의 devcon 은 devcon_dev 로 리네임 |
| 앱/배포 | 한 호스트에서 dev·prd 공존 (디렉터리·컨테이너·포트·이미지·DB·CI 전면 분리) |
| 프론트 | nginx 단일화, 503 유지보수 페이지 게시 → 컷오버 |
| 스토리지 | Oracle Object Storage → Garage(S3 호환, podman) 자가호스팅 이관 |
Impact
서비스 중단 — k-devcon.com 이 구 서버 다운 시점인 2026-08-19 08:08 KST부터 2026-08-20 23:25 컷오버까지 약 39시간 17분 동안 정상 서비스되지 않았다. 이 중 다운을 인지하기까지 14시간 12분이 걸렸고 유지보수 안내 페이지가 올라간 것은 다운 19시간 57분 뒤인 08-20 04:05이다.
데이터 — 손실 없음. 데이터 테이블 전량 이관했고 원본 SQL의 튜플을 독립적으로 세어 행 수를 대조했다. FK 12개 참조 관계 전부 고아 행 0건, 인코딩 정상.
Resolution
배포
dev/prd 공존 구성 — 두 스택을 디렉터리 단위로 대칭 분리했다.
~/devcon-dev/ 프로젝트 devcon-dev devcon-dev-{web,server-blue,server-green} 5173/8080/8081
~/devcon-prd/ 프로젝트 devcon-prd devcon-prd-{web,server-blue,server-green} 5174/8082/8083
~/deploy/ 공용 blue/green 라이브러리 + 환경별 설정
~/nginx/ 단일 k-devcon-nginx + certbot + otel-collector
각 환경 디렉터리에 src/, .env{,.properties}, db.env, .active_slot, og-html, logs가 대칭으로 모이도록 하였다. 앱 포트는 전부 127.0.0.1 바인딩.
nginx는 하나만 두고 두 세트의 vhost를 프론트하도록 했다. 컷오버는 실전 conf와 유지보수 conf를 같은 디렉터리에 나란히 두고 파일명 접미사만 스왑한 뒤 reload하는 방식으로 처리했다. 롤백도 같은 명령 역순이라 로그인이 실제로 되는지 사람이 확인한 뒤에 플립할 수 있었다.
CI 워크플로
기존에는 dev 까지만 github action 으로 배포되도록 설정해뒀다.
이번 수정으로 prd 서버도 같은 서버가 되었기 때문에, prd 환경도 github action 으로 배포가 되도록 수정하였고,
dev 환경에만 실험적으로 적용 됐었던 blue-green 배포 방식을 prd 환경에도 적용하여 무중단으로 배포될 수 있도록 개선하였다.
(단, prd 배포는 dev 환경에서 테스트를 거친 뒤에 나가야 하므로, 워크플로우를 수동으로 실행해야만 배포되도록 하였다.)
데이터 조치
DB 이관
prd 환경을 위해 prd DB 와 전용 계정을 만들었다.
구 prd DB에서 sql 덤프를 뜨고 새 prd DB로 이관하였다.
테이블의 컬럼 구성이 dev DB와 정확히 일치하고 flyway 버전도 양쪽 같은 것을 확인한 뒤 새 prd DB에 적용했다.
devcon → devcon_dev 리네임
하나의 서버에서 dev 환경과 prd 환경의 데이터베이스가 같이 돌아가게 되었다. 혼란을 방지하기 위해 기존에 dev 환경에서 사용하던 devcon DB 를 devcon_dev 로 변경하였다.
스토리지 이관
Oracle 종속성에서 완전히 벗어나고 싶었기 때문에 객체 스토리지도 자체 구축하기로 결정하였다.
Oracle 객체 스토리지의 API 는 S3 와 호환되는데 같은 방식으로 셀프 호스팅이 가능한 대체재가 없을까 찾아보니 Garage 라는 서비스가 있어 이를 쓰기로 했다.
우선 버킷 전량을 로컬 디스크로 백업했다.
| 검증 | 결과 |
| ----------------------------------- | ------------------------------------------------- |
| 파일 개수 | 2,177 |
| 용량 | 2.396 GiB |
| rclone check --checksum --one-way | 0 differences found / 2177 matching files |
| sha256 매니페스트 | 2,177줄 (~/oracle-backup-manifest.sha256) |
버킷 안에 실제로 참조되고 있는 객체는 1,689개였고 prd와 dev가 참조하는 키가 한 개도 겹치지 않아 환경별 버킷 분리가 그대로 됐다. 아무도 참조하지 않는 고아 객체 488개는 지우지 않고 별도 보관 버킷으로 넘겼다.
단순히 스토리지만 변경하면 끝나는 것이 아니라 각 서비스에서 바라보는 base url 도 수정해줘야 했고 DB 에 있는 스토리지 연결들도 다 수정해야 했다. 스크립트를 작성하여 일괄 처리하였다.
5 Whys (Root cause)
Q1. 왜 k-devcon.com 운영 서비스가 중단됐나?
→ 서비스를 구성하던 서버·DB·스토리지가 한꺼번에 내려갔고 그것을 대체할 환경이 그 시점에 구성되지 않았다. 프리티어 지원이 축소된다는 것도 인지하지 못하였다.
Q2. 왜 대체 환경이 없었나?
→ 우리 서비스는 아직 그렇게 크지 않기 때문에 단일 서버로 운영되고 있다.
Q3. 왜 프리티어 지원이 축소된다는 것을 인지하지 못하였나?
→ 오라클 클라우드와 연동된 이메일이 개인적으로 잘 사용하지 않는 계정이어서 안내 메일이 왔지만 인지하지 못하였다.
Q4. 왜 서버가 다운되었을 때 알림이 오지 않았나?
→ 기존에 grafana 를 통한 모니터링을 구축하였지만 최근 signoz 로 교체한 상태였다. 이 과정에서 설정해둔 서버 다운 alert 가 기대와 다르게 동작하였다.
Q5. 왜 alert 가 고장난 것을 그동안 몰랐나?
→ 교체한 뒤에 alert 가 실제로 울리는지 확인해본 적이 없다. alert 는 안 울리는 것과 정상인 것이 겉으로 구분되지 않기 때문에 일부러 확인하지 않으면 고장난 상태를 알 방법이 없었다.
What Went Well?
- 임시로 503 +
Retry-After를 붙여 안내 페이지로 응답하도록 처리한 것. 200이었으면 검색엔진이 안내문을 본문으로 색인해 기존 URL 순위를 잃는다. robots.txt도 Disallow: /로 함께 뒀다.
- dev 환경을 prd 환경의 리허설로 쓴 것. 사전에 구축되어 있던 dev 환경에서 마이그레이션을 리허설했고 데이터 유실 없이 마이그레이션을 마쳤다.
- 외부(Oracle Cloud) 의존성이 완전 제거된 것.
What Didn't Go So Well?
- 프리티어 축소 안내를 놓쳤다. 잘 쓰지 않는 이메일 계정으로 연동해두고 그대로 방치한 것이 원인이다.
- 서버가 내려갔는데 알림이 오지 않았다. grafana에서 signoz로 모니터링을 옮기는 과정에서 다운 alert가 기대대로 동작하지 않았고 결국 직접 확인할 때까지 몰랐다.
Action Item
- prd 서버/DB/스토리지 전환 완료
- Signoz alert 재설정 완료 - 서버가 다운되었을 때 정상적으로 alert 가 울리도록 수정.
- LongTerm - MySQL/Garage 정기 백업 구성 필요
Timeline
| 날짜 | 시간 | 상황 | 대응 |
| ----- | ----- | ------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------- |
| 08-19 | 08:08 | Oracle Free Tier 지원 축소, 구 운영 서버 다운. | — |
| 08-19 | 22:20 | 강성욱님이 상황 인지 후 공유 | |
| 08-19 | 22:26 | 프리티어 지원 축소로 인해 서버가 다운되었다는 것을 확인 | 무료 플랜에서 유료 플랜으로 업그레이드 신청, 하지만 1-2일 정도 소요된다는 것 확인 |
| 08-19 | 22:38 | 복구 착수. DNS가 홈서버를 가리키도록 수정. PRD 환경용 DB 생성 및 Object Storage 구성 진행 | devcon_prd 논리 DB + 전용 계정 생성 |
| 08-20 | 04:05 | 유지보수 안내 페이지 게시 | 200이 아닌 503 + Retry-After: 3600 — 검색 색인 방지 |
| 08-20 | 14:26 | 유료 티어로 업그레이드 완료 확인 | 서버 다시 띄운 후 데이터 백업 진행 |
| 08-20 | 21:47 | dev/prd 공존 구성 착수. (기존 구성으로는 프로젝트명·컨테이너명·포트·이미지 태그 전부 충돌) | 계획 수립 |
| 08-20 | 22:00 | 데이터 이관 완료 | 행 수 독립 대조, FK 고아 0, 인코딩 검증 |
| 08-20 | 23:25 | prd 스택 기동 성공 | |