레거시 시스템 트러블슈팅 경험 — 캐시

Lee Anne
6 min readDec 10, 2023

1년간 레거시 서비스 운영 업무를 하며 다양한 이슈들을 만나 트러블슈팅 역량을 키웠는데 그 중 정말 골치아프면서 특이했던 트러블슈팅 경험을 소개하고자 한다. 이때만 생각하면 정말… 아찔하다🥲

현재 필자는 다양한 레거시 시스템을 유지보수하고 있다. 대부분은 CMS 어드민 서버들인데 기존에는 하나의 프로덕트에 여러 프로젝트를 서로 다른 외주 개발사가 진행하다보니 프로젝트가 끝날 때마다 새로운 컨텐츠 관리 시스템이 만들어졌다. 그러다보니 하나의 프로덕트에서 관리해야만 하는 어드민 서버만 4개인데 뭔가 이슈가 생길 때마다 항상 새롭다.(각 서버마다 전부 다른 개발팀이 작업하여 소스코드나 컨벤션, 테이블 설계 등이 완전히 다르다. 그래서 운영이슈로 레포나 DB, 모니터링 지표를 확인할 적마다 너무 괴롭다. 이를 통해 팀 컨벤션이 얼마나 중요한지를 절실히 깨닫고 있다🥲) 매번 새로운 이슈를 만날 적마다 고통의 연속이긴하나 이번 블로그에서 소개할 이슈는 입체북 서비스의 컨텐츠를 관리하는 시스템에서 발생한 캐시 이슈이다.

배경

평소처럼 일과시간에 업무를 처리하고 있을 때였다. 아이들나라 PO로부터 입체북 CMS 어드민 페이지가 정상동작하지 않는다는 이슈를 보고 받았다.

project-입체북 슬랙 채널 대화

아이들나라 컨텐츠 등록 및 심사는 모두 외주사 직원들이 관리하고 있다. 거기다 어드민 서비스라 이슈 인지 시점이 사용자 서비스 이슈 인지보다 조금 늦은 편이긴 하다.(당시 이슈 재현 시점이 1주 전이라고 전달 받았다. 그래서 1주일동안 신규 컨텐츠 편성을 못했다고…🥲) 그래서 해당 슬랙이 왔을 적에 빠른 대응을 위하여 업무 우선순위를 높여 진행하였다.

보고받은 이슈 현상으로는 어드민 페이지에서 새로고침 혹은 페이지 이동 후 컨텐츠 목록 조회를 할 때마다 컨텐츠 개수가 달라진다는 것이다. 확인해보니 정말 희한하게도 새로고침 할 때마다 컨텐츠가 사라졌다 다시 생겼다 하였다.

보였다 안보였다 하는 컨텐츠 이미지 캡쳐(빨간색 표시)

원인

진짜 이 원인 찾는게 제일 어려웠는데 그 이유는 로그가 제대로 남지 않아서였다. 일반적으로 이슈가 있을 적이나 뭐가 안될 때에는 서비스 모니터링 지표를 제일 먼저 확인하는데 로그가 제대로 남지 않고 있어 트래킹이 어려웠다. 거기다 입체북 어드민 프로젝트의 아키텍처는 전부 다른 서버를 호출하여 모든 데이터를 가져와서 보여주는 일종의 Proxy 서버 아키텍처였다. 그래서 해당 이슈도 이 어드민 페이지가 컨텐츠 정보를 조회하기 위하여 호출하는 서버의 모니터링 지표와 소스코드 혹은 아키텍처 문서를 확인할 수밖에 없었다.

여기가 정말 큰 허들이었다. 그 이유는 컨텐츠 정보를 조회하기 위하여 호출하는 서버의 모니터링 지표는 존재하지 않을 뿐더러 문서화가 전혀 되어있지 않아 소스코드만을 보고 파악해야했다. 그런데 이 서버 개발자들이 이스라엘 출신이여서 그런지 몰라도 프로젝트 패키지 구조와 컨벤션이 한국의 개발자들이 알고 있는 것과는 확연히 달랐다. 거기다 우리와는 다른 기술 스택(Node.js)으로 개발하여 쉽고 빠르게 파악하기 어려웠다.

소스코드 혹은 모니터링 지표로 원인을 단번에 파악하기 쉽지 않아 해당 현상이 일어나는 원인을 하나씩 가정하고 해결하자로 전략을 바꿨다. 거기서 첫 번째로 가정한 원인은 캐시가 제대로 적용이 되지 않아서였다. 컨텐츠 DB에서 데이터 조회를 했을 적에 존재하는 것으로 보아 컨텐츠 등록에는 이슈가 없다고 판단하였다. 그렇다면 조회할 적에 이슈가 될 수 있는 포인트는 그때 당시 알고 있던 아키텍처 수준에선 캐시가 가장 유력할 것이라고 추측하였다. 따라서, 이를 시작으로 문제 해결을 시도하였다.

해결

현상과 프로젝트 구조, 인프라 설정을 기반으로 근본적인 원인은 로컬 캐시로 인하여 발생하였다는 것을 알아냈다. GCP 운영 환경에 분명 redis를 사용하고 있음에도 이런 이슈가 발생한 것이 조금 의아하였다. 아래 그림은 현재 입체북 어드민 서버에서 컨텐츠 조회 시 요청하는 서버의 GCP 클러스터 구조이다.

입체북 어드민 컨텐츠 조회 서버 인프라 구성도(GCP)

해당 인프라 설정에 의하면 분명 redis 글로벌 캐시를 활용하고 있는 것으로 보이지만 실제 컨텐츠 조회 API 요청 후 로그 모니터링을 해보면 phoenix 컨테이너의 pod별로 응답값이 다 달랐다. 어떤 pod는 캐시 적재가 되어있어 신규 컨텐츠 정보가 조회되는 반면, 다른 pod에서는 캐시 적재가 되지 않아 신규 컨텐츠 정보가 없었다. 소스코드를 보았을 적에는 살짝 당황스러운 설정들이 몇 가지 있었다.😨

// 배포 path 혹은 variable 관련 json 설정파일
{
...
"REDIS_HOST": "localhost",
"ENABLE_REDIS": "true",
}

// 쿠버네티스 배포 yaml 파일
...
- name: ENABLE_REDIS
value: "true"
- name: REDIS_HOST
value: "redis"
...

이 설정으로 보았을 적에 쿠버네티스 컨테이너별 redis 로컬 캐시가 존재하고 있고 실제로 배포 시 자동으로 flush all 실행이 되고 있는 것이다. 빠른 대응을 위해선 일단 해당 서버 어플리케이션 재배포를 하였고 덕분에 정상적으로 컨텐츠 데이터가 조회되는 것을 확인할 수 있었다.

앞으로는….

그 누구도 히스토리를 모르는 서비스를 유지보수한다는 것이 얼마나 괴롭고 힘든 일인지를 몸소 느끼고 있다.(이런 이슈 원인 파악을 위해서 항상 크롬의 개발자 도구로 HTTP 요청/응답값을 확인하는데 이젠 거의 IDE만큼 친숙하고 익숙해졌다…ㅎ) 이번 트러블슈팅 경험을 통해선 서버 아키텍처 구성 시 위와 같이 로컬 캐시 구조로 가져가면 scale-out 할 적에 이슈가 될 수 있음을 배웠다. 서비스 규모 확장을 고려한다면 이런 식으로 절대 설계하지 않을 것을 절실히 깨달았다. 또한, ReadME 파일이나 프로젝트 문서화의 중요성, 그리고 기술 스택의 일원화를 크게 배웠다. 무작정 개발자 개인이 편하다고 혹은 바쁘다고 위의 것들을 생략하게 되면 정말 나중에 합류하는 동료가 크게 힘들어할 수 있다. 심하면 자신이 작성한 코드로 인하여 동료가 퇴사를 할 수도 있다. 따라서, 협업하면서 프로덕트를 만들어가는 프로로서 정말 기본기를 항상 잊지말 것을 다짐하고 또 다짐하자💪

--

--