8080 포트에 프로세스가 올라오지 않은 서비스 중단
Health Check 알림으로 서비스 이상을 확인하고 서버에 직접 접속했다.
Portainer와 SSH로 상태를 확인하고 Nginx를 점검했다. 서비스가 쓰던 8080 포트에 정상 프로세스가 없었다. Docker Compose로 production 서비스를 다시 기동했다.
복구로 끝내지 않고 왜 프로세스가 사라졌는지 확인했다. 이어지는 로그 누적 문제(OPS-02)가 여기서 나왔다.
소프트웨어 엔지니어 — 400만 DAU 서비스의 프론트엔드와 그 뒤의 전송 구간
스크롤고지서가 같이 왔다
수백만 명이 매일 쓰는 서비스의 프론트엔드를 개발하다가, 트래픽 비용 고지서와 장애 알림도 같이 받게 됐다. 그때부터 화면 바깥의 구간 — CDN, 캐시, 서버, 배포, 데이터 — 을 함께 읽기 시작했다.
숫자는 전부 두 번째 측정값이다
문제는 감으로 좁히지 않는다. 로그와 이벤트로 구간을 나누고, 어디서 초와 바이트가 새는지 확인하고, 구조를 바꾼 뒤 같은 지표를 다시 잰다.
LLM도 검증이 필요한 외부 시스템이다
특별한 존재로 두지 않는다. 응답을 스키마로 검사하고, 실제 세션을 재현해 품질을 다시 측정한다.
235GB/day
CloudFront 캐시 키와 무효화 정책을 리소스 특성에 맞추고, Service Worker로 반복 요청을 네트워크에서 걷어냈다.
광고 요청부터 클릭까지 7개 이벤트를 직접 정의해 수집하고, BigQuery로 병목 구간을 특정했다. 그다음 번들과 광고 스크립트의 실행 순서를 바꿨다.
빨라진 만큼 더 보였고, 보인 만큼 더 눌렸고, 눌린 만큼 매출이 올랐다. 세 지표를 이어서 검증했다.
253M
4.27 TB
CloudFront와 Lambda@Edge 기반 이미지 전송 인프라. 위 캐시 최적화와는 다른 지표이며 합산하지 않는다.
1 ← 10+
10개 이상을 하나의 멀티테넌트 구조로 합치고, 서비스별 DNS 레코드를 하나씩 이관했다. 운영·유지보수 인력은 기존의 약 20% 수준이 됐다.
캐시 적중률을 올리면 요청이 더 일찍 멈춘다. 오리진까지 내려간 요청만 전송량으로 청구된다.
235GB/day
56%
417 GB/day와 235 GB/day는 실제 측정값이다. 두 지점 사이는 설명을 위한 선형 모델이며 실제 청구서가 아니다.
Health Check 알림으로 서비스 이상을 확인하고 서버에 직접 접속했다.
Portainer와 SSH로 상태를 확인하고 Nginx를 점검했다. 서비스가 쓰던 8080 포트에 정상 프로세스가 없었다. Docker Compose로 production 서비스를 다시 기동했다.
복구로 끝내지 않고 왜 프로세스가 사라졌는지 확인했다. 이어지는 로그 누적 문제(OPS-02)가 여기서 나왔다.
서버 상태를 확인하다 PM2 로그가 약 21GB까지 쌓여 있는 것을 발견했다.
디스크를 회수하려고 로그를 정리했다. 다만 정리는 조치가 아니라 유예다. 같은 일이 반복된다.
pm2-logrotate를 적용했다. max_size 10M, retain 7, compress, 날짜 기반 파일 관리와 rotation을 설정했다.
ping 기반 모니터링은 ICMP가 차단되면 정상 서비스를 장애로 판단한다. 반대로 서버가 ping에 응답해도 HTTP 서비스는 죽어 있을 수 있다.
ICMP 응답과 애플리케이션 상태를 구분했다. curl 등 HTTP 요청 기반 확인을 검토했다.
서버 장애, 서비스 장애, 네트워크 장애, 모니터링 시스템 자체 오류를 구분할 수 있어야 알림이 의미를 갖는다는 기준을 세웠다.
네트워크 정책이 바뀌면서 운영 서버가 쓰던 포트의 외부 연결이 차단됐다. 서비스를 바로 수정하기 어려운 상태였다.
외부 연결이 가능한 서버를 이용해 임시 TCP Relay 경로를 구성하고 서비스를 유지했다.
정상 네트워크 환경이 복구된 뒤 원래 구조로 전환했다. 임시 경로를 그대로 두지 않았다.
RDS CA 인증서 교체와 만료 과정에서 연결 문제가 발생했다. DataGrip에서는 연결되는데 CLI에서는 실패하는 등, 클라이언트마다 결과가 달랐다.
클라이언트별 신뢰 저장소와 TLS 처리 차이를 확인해 어느 쪽이 인증서를 어떻게 검증하는지 구분했다.
운영환경에서는 인증서 자체보다 클라이언트 환경 차이가 연결 실패의 원인이 되는 경우가 있다는 것을 기록해 뒀다.
운영 서버의 Docker 컨테이너가 ExitCode 1로 종료됐다.
컨테이너 상태와 시간 정보, 로그를 대조해 종료 시점을 특정했다.
근본 원인은 확인되지 않았다. 확인되지 않은 원인을 만들어서 적지 않는다.
SCG 회장으로 기술 도입과 변경을 판단하면서, 초기에는 검증된 방식을 유지하려는 성향이 강했다. 팀원에게서 새로운 시도에는 지나치게 높은 증명을 요구하면서 기존 방식의 위험은 낮게 본다는 피드백을 받았다.
맞는 지적이었다. 그래서 '안정적인가'를 묻는 대신 두 가지를 묻기로 기준을 바꿨다. 이 변경의 영향 범위는 어디까지인가, 그리고 문제가 생겼을 때 어느 정도까지 되돌릴 수 있는가.
새로운 아이디어를 낸 사람에게 모든 위험 검증 책임을 맡기지 않는다. 검증 수준을 정하는 것은 팀의 일이다.
좁은 범위에서 먼저 시험한다. 합의에 드는 비용이 변경 자체보다 크면 안 된다.
검토 범위와 참여 인원을 넓힌다. 되돌릴 수 없는 변경일수록 더 그렇다.
복구 경로를 먼저 확인하고 실행한다. DNS 순차 전환도 같은 이유로 그렇게 했다.