한동안 정말 조용하게 무난하게 시간을 보냈습니다.
그 동안 전화 오면 장애가 발생한 것일까 싶어서 심쿵하고, 단순한 슬랙 채팅 노티도 장애 노티일까 싶어서 열어보는 순간까지 조마조마 했고, 장애 현황 파악 및 긴급 조치를 위해서 주말에도 항상 노트북을 휴대하고 다녔었습니다.
Database에서 누락된 index 생성하고, AWS RDS에서 지원하는 slow query log 확인하고, API 서버 DB connection pool 정리하고, … 외주 업체가 무책임하게 심어놓은 지뢰 찾아서 하나씩 해결하다보니 어느덧 솥뚜껑보고 놀라는 일은 없어졌습니다.
그러던 중 방심한 것은 아닌데 무지에 의해서 어제와 오늘 연달아서 장애가 발생했습니다. 오랜만에 발생하는 장애였고 장애 발생 유형이 기존에 경험했던 것과 완전히 달라서 멘붕과 함께 에너지를 엄청나게 소모하는 바람에 진이 빠져서 그로기 상태를 겪었습니다.
서비스 초기에 서버 구성을 API와 DB로 단순하게 분리했었는데, 운영하다보니 분리해야 하는 서버 구성요소들이 하나둘씩 보이기 시작했습니다. 사업적으로 중요한 서버가 별로 중요하지도 않은 기능으로 인해서 전체 서비스 장애로 이어지는 현상을 몇번 겪었고, 안정적인 서비스 운영을 위해 서버 구성 변경과 기능 패치가 필요했습니다. 비록, 아직 발생하지 않았지만 언제 터질지 모를 시한폭탄이 돌고 있다는 생각이 드니 서버 구성 변경을 무한정 늦출 수가 없어서 서버 패치 일정을 잡았습니다
D-day였던 그제 밤 11시에 각자 집에서 슬랙으로 이야기 하면서 서버 분리 작업과 서비스 패치 작업을 순차적으로 진행하고, 하나씩 기본 기능 테스트해가면서 나름 순탄한 패치를 끝냈고 그래도 혹시나 하는 마음에 매장 영업 시간에 맞춰서 일부는 재택 근무로, 일부는 사무실에서 대기를 했습니다.
오전 10시에 매장 영업을 시작하고 별문제가 없는 듯 하다가 11시경부터 여기저기서 장애 현상이 보고되기 시작했습니다. 전체 장애는 아닌데 일부 매장에서 포인트 적립과 사용이 안된다는 클레임이 접수되기 시작했고 원인 분석을 위해서 로그 파일을 열어 본 순간 모두가 멘붕이 되었습니다. 막내 개발자가 로그 파일 크기를 줄인다고 API 호출 기록만 남기고 나머지 로그가 모두 출력되지 않도록 작업을 해 놓은 것이었습니다.
어찌 어찌 어렵게 장애 원인은 찾았는데 해결책을 못찾았고 장애 시간이 길어지니 rollback하자는 의견이 나오기 시작했는데, 서버 구성을 바꾼터라 기존 서버 구성으로 완전 복구는 몇시간이 더 소요될 것이고 바뀐 서버 구성에 예전 서비스 모듈을 올리면 어떤 오동작을 하게 될지 몰라서 선뜻 판단을 못내리고 있었습니다. 모두를 제 판단만 기다리는데 아무런 정보가 없는 상태에서 현재 문제를 해결할지 rollback 해야 할지 결정하는 것은 의사결정이라기 보다는 찍기에 가까운 무모함 그 자체였습니다.
문제의 원인은 특정 API 호출이 DB에 write하는 동작에서 master DB로 access하지 않고 replica DB로 write operation을 시도하니 Exception이 발생하는 것이었는데, 소스 코드 상으로는 master DB를 access하도록 코딩되어 있었기 때문에 해결책을 도저히 찾을 수 없었습니다.
이 상황에서 더 이상 나빠질 것도 없겠다 싶어서 일부 멤버는 서비스를 rollback하는 작업을 하고 나머지 멤버는 기술 동냥을 위해서 짐을 싸서 초고수분께 찾아갔습니다. 다행히도 이동하는 와중에 rollback 이후에 서비스가 정상 동작해서 큰 산을 넘었다고 생각했습니다. 장애가 발생한 문제는 생각보다 복잡해서 당장 문제를 해결할 수 있는 골드키는 얻지 못했지만 수정해야 하는 방향에 대해서 조언을 얻어서 무엇인가 해볼 수 있는 것이 생기니 일단 심리적 안정감을 가질 수 있었습니다.
오랜만에 생각지도 못했던 유형의 장애를 만나니, 서비스 무중단 패치와 staged roll-out을 하는 방법이 필요하다 싶어서 아침에 출근해서 이런 저런 이야기를 나누던 중에 여러가지 아이디어가 나와서 하나씩 적용해서 하다보면 밤새지 않아도 패치할 수 있고 장애가 발생해도 바로 복구할 수 있는 역량이 생길 것 같습니다.
여기까지는 괜찮았는데 막내 개발자가 테스트 한다고 로컬 버전을 업로드한 서버가 staging 서버가 아니고 상용 서버였다는 사실을 이실직고 하면서 평화로웠던 아침이 전쟁터로 변했습니다. 매장에서 적립/사용되는 포인트가 상용 서버가 아닌 테스트 서버에 쌓이는 상황에서 불가피하게 서비스를 중단시킬 수 밖에 없었고 어제 장애 여파도 있어서 대외적으로 대응해야 하는 입장에서 이래저래 참 난처할 수 밖에 없었습니다.
일단 급한 불을 끄고 테스트 서버에 쌓인 거래 내역을 상용 서버로 옮기는 작업까지 마무리하면서 오랜만에 쫄깃한 장애 대응을 마무리 했습니다.
Lesson Learned
- 단위테스트를 잘해도 빼먹은 테스트가 있으면 거기서 꼭 펑크가 난다. 빼먹은 테스트가 있는지 꼭 확인하자.
- 부하 테스트를 안하면 비주기 문제가 상용 서비스에서 발생한다. 부하 테스트 하는 방법을 꼭 찾아 놓자.
- 서비스 운영 경험이 없는 개발자에게는 사소한 경험이라도 알려줘야 로그에 대한 개념을 잡는다.
- 아무리 작은 수정사항이라도 double check하는 방안을 찾아야 한다.
- 비록, git을 쓰고 있지만 최종 산출물에 대한 버전 관리가 안되면 소스 빌드부터 다시 해야 하기 때문에 장애 대응이 늦을 수 밖에 없다. 서버 로컬 storage에 날짜별로 산출물을 남겨 놔야 빠른 rollback이 가능하다.
- 무중단 패치와 Staged roll-out은 아무리 바쁘더라도 먼저 도입했어야 했다. (비록 초반에는 몰라서 못했지만 장애를 겪기 전에 심각성을 예상했다면 충분히 방안을 찾을 수 있었을 것이라는 생각)
- 궁하니 통하고, 아무도 알려주지 않는 서비스 운영의 노하우는 몸으로 배울 수 밖에 없다.