Mini Project 6 08

CloudWatch Container Insights로 배포 상태를 관측 가능하게 만든 과정

서비스가 접속되고 파이프라인도 성공했는데, CloudWatch까지 붙여야 무엇이 더 달라지는지 분명하지 않다.

근거 · 교안 CloudWatch/모니터링 파트

CloudWatch Container Insights로 배포 상태를 관측 가능하게 만든 과정 대표 이미지

1장: ‘배포 성공’이 정말 충분할까?

솔라의 손가락이 가볍게 트랙패드를 두드렸다. 화면에는 방금 전 마무리된 배포 파이프라인의 마지막 단계가 선명한 초록색으로 빛나고 있었다. ‘Succeeded’. 그 단어 하나로 지난 며칠간의 노력이 보상받는 기분이었다.

“언니, 이것 봐. 드디어 배포 끝났어!”

솔라는 의자를 뒤로 돌려 옆에서 조용히 책을 읽고 있던 루나를 불렀다. 루나가 솔라의 노트북 화면으로 시선을 옮겼다. 파이프라인의 각 단계—소스, 빌드, 배포—가 모두 말끔한 초록색 체크 표시를 달고 있었다. 솔라는 재빨리 새 탭을 열어 서비스 주소를 입력했다. 잠시 후, 익숙한 애플리케이션의 메인 화면이 문제없이 나타났다.

“봐, 서비스도 잘 뜨지? 이제 진짜 끝이다.”

뿌듯함이 섞인 솔라의 목소리에 루나는 작게 고개를 끄덕였다. 하지만 루나의 시선은 잠시 서비스 화면에 머물렀다가, 이내 다시 초록색으로 가득한 파이프라인 대시보드로 돌아갔다.

“성공했네. 축하해.” 루나가 말했다. “그래서, 이제 정말 다 끝난 걸까?”

“응? 그럼. 파이프라인도 성공했고, 서비스 접속도 되는데. 뭐가 더 남았어?”

솔라의 되물음에는 ‘당연히 끝난 것 아니냐’는 확신이 묻어났다. 배포의 최종 목표는 코드를 서버에 올려 서비스가 동작하게 하는 것. 그 목표는 명백히 달성되었다. 화면에 떠 있는 초록색 체크 표시와 정상적으로 로딩된 웹페이지가 그 증거였다.

루나는 잠시 생각에 잠기는 듯하더니, 솔라에게 시선을 맞추며 입을 열었다.

“만약 지금 사용자가 접속했는데, 화면이 뜨기까지 30초가 걸린다면 어떨까? 저 초록색 체크 표시가 그걸 우리에게 알려줄까?”

“30초?”

솔라는 잠시 말을 잃었다. 상상만으로도 끔찍했다. 하지만 이내 반박했다.

“그럴 리가. 내 컴퓨터에선 바로 떴는걸. 그리고 배포 과정에서 오류가 있었다면 파이프라인이 실패했겠지.”

“물론 지금은 괜찮겠지.” 루나는 솔라의 말을 부드럽게 받았다. “하지만 사용자가 갑자기 100배 늘어난다면? 특정 기능을 실행할 때만 메모리 사용량이 폭증한다면? 그런 상황이 벌어졌을 때, 우리는 그걸 어떻게 알 수 있을까? 저 파이프라인은 이미 ‘성공’이라는 초록불을 켠 채 침묵할 텐데.”

솔라의 표정이 미묘하게 굳어졌다. 분명 일리 있는 말이었다. 배포 파이프라인이 확인하는 것은 ‘배포 행위’ 그 자체의 성공 여부다. 코드가 빌드되고, 컨테이너 이미지가 만들어져서, 지정된 서버에 오류 없이 올라갔는가. 딱 거기까지였다. 그 이후에 벌어지는 일, 즉 사용자가 서비스를 이용하면서 겪게 될 수많은 변수와 그로 인한 시스템의 내부적인 ‘상태’ 변화는 저 초록색 체크 표시의 책임 범위 밖이었다.

마치 갓 구워낸 빵을 오븐에서 꺼낸 순간과 같았다. ‘굽기’라는 과정은 성공적으로 끝났다. 하지만 이 빵이 손님 입맛에 맞을지, 너무 타거나 덜 익은 부분은 없는지, 시간이 지나면서 눅눅해지지는 않을지는 오븐 타이머가 알려주지 않는다. 직접 맛을 보고, 손님들의 반응을 살피고, 남은 빵의 상태를 계속 확인해야만 알 수 있는 것이다.

“아…”

솔라의 입에서 나지막한 탄식이 흘러나왔다. 머릿속에서 두 개의 그림이 선명하게 갈라졌다. 하나는 방금 전까지 자신이 전부라고 믿었던, 정적인 ‘배포 성공’의 스냅샷. 다른 하나는 루나의 말과 함께 펼쳐진, 살아 움직이는 유기체처럼 끊임없이 변화하는 ‘서비스 상태’의 파노라마였다.

“그렇구나. 배포 성공은 끝이 아니었어. 그냥 가게 문을 연 것뿐이네.”

솔라는 스스로 중얼거리며 고개를 끄덕였다. 가게 문을 열었다고 해서 장사가 저절로 잘 되는 것은 아니다. 어떤 손님이 오는지, 어떤 물건을 찾는지, 계산대 앞에 줄이 너무 길어지지는 않는지 계속 지켜봐야 한다. 문제가 생기면 즉시 알아채고 해결해야 한다.

화려했던 초록색 체크 표시들이 이제는 다르게 보였다. 저것은 완결을 의미하는 마침표가 아니라, 이제부터 진짜 운영이 시작된다는 것을 알리는 출발 신호탄이었다. 이제 솔라의 머릿속은 새로운 질문으로 가득 찼다.

‘알겠어. 배포 후에도 서비스의 상태를 계속 지켜봐야 한다는 건. 그런데 그 눈에 보이지도 않는 ‘상태’라는 걸, 대체 무엇으로 어떻게 들여다봐야 하는 거지?’

2장: ‘보이지 않는 상태’를 어떻게 ‘보이게’ 만들까?

솔라의 손가락이 망설이며 노트북 화면 위를 맴돌았다. 초록색 체크 표시의 흥분은 가라앉고, 대신 막막함이 밀려왔다. 루나의 말대로 ‘서비스의 상태’를 보기 위해 AWS 콘솔에 들어왔지만, 눈앞에 펼쳐진 것은 수백 개의 서비스 목록뿐이었다. 어디서부터 시작해야 할지 감조차 잡히지 않았다.

검색창에 ‘monitor’라고 입력하자 CloudWatch라는 이름이 나타났다. 솔라는 일단 클릭했다. 하지만 페이지가 열리자 더 깊은 혼란에 빠졌다. 수많은 메뉴와 그래프, 그리고 정체를 알 수 없는 용어들이 가득했다. ‘로그 그룹’, ‘지표’, ‘경보’… 이것들이 어제 배포한 서비스와 연결되어 있다는 것은 어렴풋이 짐작했지만, 이 데이터의 산더미에서 어떻게 의미 있는 ‘상태’를 읽어내야 하는지 알 수 없었다. 마치 글자만 빼곡한, 색인 없는 사전을 받은 기분이었다.

“언니, CloudWatch라는 걸 찾긴 했는데… 이게 다야?”

솔라의 목소리에는 실망감이 묻어났다.

“그냥 데이터만 모아놓은 창고 같아. 여기서 뭘 어떻게 봐야 서비스가 괜찮은지 알 수 있는 거야? ‘상태’라는 게 너무 추상적으로 느껴져.”

루나는 솔라의 화면을 들여다보았다. 복잡한 CloudWatch 초기 화면을 본 루나는 고개를 끄덕이며 솔라의 의자를 자기 쪽으로 조금 당겼다.

“데이터 창고라는 표현이 아주 틀리진 않네. 모든 정보를 한곳에 모으는 것부터 시작하니까. 하지만 여긴 그냥 쌓아두기만 하는 곳은 아니야. 그 정보들을 연결해서 보여주는 특별한 지도가 있어.”

루나는 솔라의 키보드로 손을 뻗어 왼쪽 메뉴 검색창에 ‘Container Insights’라고 입력했다. 엔터를 치자, 방금 전의 복잡한 화면은 사라지고 컨테이너 환경에 특화된 대시보드가 나타났다. 여러 개의 그래프가 CPU, 메모리, 네트워크 사용량을 시간의 흐름에 따라 보여주고 있었다.

“와, 이건 아까보다 훨씬 보기 편하다.”

솔라의 눈이 조금 밝아졌다. 그래프들은 마치 심장 박동이나 혈압 기록지처럼 서비스의 활력 징후를 보여주는 듯했다. 그때 루나의 손가락이 한 그래프를 가리켰다. ‘CPU utilization’ 그래프의 특정 구간이 눈에 띄게 솟아 있었다.

“여기 좀 봐. 오후 2시 32분쯤에 CPU 사용량이 갑자기 90% 이상으로 치솟았네.”

“정말이네. 몇 분 동안 계속 높았구나. 왜 그랬을까? 사용자가 갑자기 몰렸나?”

솔라는 그래프의 뾰족한 부분을 확대하며 혼잣말을 했다. 배포는 성공했고 서비스는 정상적으로 보였지만, 내부에서는 이런 격렬한 움직임이 있었던 것이다.

“그럴 수도 있지.” 루나가 말했다. “하지만 추측만 할 순 없잖아. 저 그래프는 우리에게 ‘무슨 일’이 있었다는 징후를 보여주고 있어. 이제 우리는 저 시간에 도대체 ‘왜’ 그런 일이 벌어졌는지, 그 원인을 찾아야 해.”

루나는 새 탭을 열어 다시 CloudWatch로 들어갔다. 이번에는 ‘Logs Insights’ 메뉴로 이동했다. 솔라가 아까 막막하게 쳐다보던, 텍스트로 가득 찬 바로 그 로그 데이터 창고였다.

“이 그래프가 알려준 시간, 오후 2시 32분을 기억해.”

루나는 간단한 쿼리를 입력했다. 특정 EKS 클러스터와 네임스페이스를 지정하고, 오후 2시 32분을 전후한 5분간의 로그를 필터링하는 내용이었다. 잠시 후, 화면에 수백 줄의 로그가 쏟아져 나왔다. 대부분은 평범한 정보성 메시지였지만, 스크롤을 내리자 붉은색 ‘ERROR’ 딱지가 붙은 로그들이 눈에 띄기 시작했다.

솔라는 자신도 모르게 화면에 가까이 다가갔다.

ERROR: NullPointerException at com.example.service.DataProcessor.process(DataProcessor.java:128)

똑같은 오류 메시지가 정확히 CPU가 급증했던 그 시간에 집중적으로 기록되어 있었다. 특정 데이터를 처리하는 로직에서 비어있는 값을 처리하지 못해 반복적으로 예외를 던지고 있었던 것이다.

“아…!”

솔라의 입에서 짧은 탄성이 터져 나왔다. 모든 조각이 맞춰지는 순간이었다.

CPU 사용량 급증이라는 징후(Symptom). 애플리케이션 로그에 찍힌 NullPointerException이라는 원인(Cause).

두 개는 별개의 정보가 아니었다. Container Insights의 그래프는 서비스의 겉으로 드러난 비명이었고, Logs Insights의 기록은 그 비명의 이유를 담은 진술서였다. CloudWatch는 단순히 데이터를 모으는 창고가 아니라, 징후와 원인을 연결해 문제의 전말을 파악하게 해주는 탐정의 작업대였던 것이다.

솔라는 다시 Container Insights 대시보드를 쳐다봤다. 이제 춤추는 선으로만 보였던 그래프들이 다르게 읽혔다. 저것은 단순한 숫자의 나열이 아니었다. 서비스의 보이지 않는 상태를 우리에게 필사적으로 알려주는 신호였다.

“그렇구나. ‘상태를 본다’는 건, 그냥 데이터를 쳐다보는 게 아니었어. 징후를 발견하고, 그 징후를 단서 삼아 원인을 파고드는 거였네.”

스스로 내린 결론에 솔라는 고개를 끄덕였다. 이제 ‘관측 가능성’이라는 말이 더 이상 추상적으로 들리지 않았다. 하지만 동시에 새로운 질문이 고개를 들었다.

‘좋아, 이렇게 문제의 원인까지 찾을 수 있다는 건 알겠어. 그런데 이건 결국 일이 터진 뒤에나 할 수 있는 거잖아? 이 연결을 미리 활용해서 운영에 더 적극적으로 써먹을 방법은 없을까?’

3장: 관측 가능성으로 얻는 운영 통찰과 선제적 대응

솔라의 시선은 Container Insights 대시보드의 CPU 사용량 그래프 위를 천천히 따라가고 있었다. 어제 루나와 함께 파헤쳤던 오후 2시 32분의 뾰족한 스파이크는 이제 과거의 사건 기록처럼 선명했다. 그 옆으로, 현재 시점까지 이어지는 그래프 선은 아무 일 없다는 듯 평온한 수치를 유지하고 있었다. 마치 폭풍이 지나간 뒤의 고요한 바다 같았다.

문제의 원인을 찾아낸 것은 분명 큰 수확이었다. 하지만 솔라는 어딘가 개운치 않은 기분을 떨칠 수 없었다. 범인을 잡았지만, 피해는 이미 발생한 뒤였다. 이 대시보드는 훌륭한 사후 분석 도구였지만, 이걸로 미래의 사고를 막을 수 있을까? 솔라는 화면 위에서 춤추는 그래프들을 보며 중얼거렸다. “결국 이건 일이 터진 뒤에나 볼 수 있는 거잖아. 그냥 예쁜 상황 보고서 같은데, 이걸로 내 운영이 뭐가 달라지는 거지?”

루나는 솔라의 혼잣말을 들었지만 바로 대답하는 대신, 솔라의 노트북 화면을 가리켰다. 그녀의 손가락 끝이 닿은 곳은 CPU 사용량 그래프 우측 상단에 있는 작은 버튼, ‘경보 생성(Create alarm)’이었다.

“그 상황 보고서를, 미래를 내다보는 예보관으로 만들 수 있어.”

루나의 말에 솔라는 버튼을 클릭했다. 화면이 바뀌며 경보를 설정하는 페이지가 나타났다. ‘지표(Metric)’ 섹션에는 방금 전까지 보고 있던 CPU 사용량 데이터가 이미 선택되어 있었다. 그 아래에는 ‘조건(Conditions)’을 설정하는 항목이 보였다.

“이게… 뭐 하는 건데?” 솔라가 물었다.

“어제 우리가 발견한 ‘징후’를 시스템에게 알려주는 거야. ‘CPU 사용량이 특정 수준을 넘어서면, 그건 심상치 않은 징후이니 나에게 알려줘’라고 규칙을 만드는 거지.”

루나의 설명에 솔라는 ‘조건’ 항목을 유심히 들여다보았다. 임계값(Threshold value)을 입력하는 칸이 있었다. 솔라는 어제 확인했던 스파이크의 최고점, 90%를 떠올렸다.

“그럼 여기에 90이라고 쓰면, CPU가 90%를 넘을 때마다 나한테 알려주는 거야?”

“맞아. 하지만 90%는 이미 너무 위험한 수준일 수 있어. 문제가 터지기 직전이 아니라, 문제의 징후가 보이기 시작할 때 알아채는 게 중요하니까.”

루나는 임계값 입력 칸에 ‘80’이라고 입력했다. 그리고 그 아래 ‘연속적인 기간(consecutive period)’을 ‘5분 중 1분(1 out of 5 minutes)’에서 ‘5분 중 5분(5 out of 5 minutes)’으로 변경했다.

“일시적으로 CPU가 튀는 건 괜찮아. 하지만 5분 이상 80%를 계속 넘는다면, 그건 분명 정상적인 상황이 아니라는 신호지. 우리는 그 신호를 포착해서, 어제처럼 사용자가 불편을 겪거나 서비스 전체가 마비되기 전에 움직일 시간을 버는 거야.”

‘시간을 번다.’ 솔라의 머릿속에서 그 말이 맴돌았다. 이전까지 솔라에게 모니터링은 문제가 발생했음을 확인하는 ‘사후 통보’에 가까웠다. 하지만 지금 루나가 설정하는 이 ‘경보’는 달랐다. 이것은 단순한 통보가 아니었다. 문제의 전조를 감지하고, 최악의 상황이 오기 전에 대응할 수 있는 결정적인 기회를 제공하는 ‘조기 경보’였다.

솔라는 다음 단계로 넘어가 알림을 받을 대상을 설정했다. 이메일 주소를 입력하고 경보의 이름을 eks-prod-cpu-high-alarm으로 정했다. 모든 설정을 마치고 ‘경보 생성’ 버튼을 누르자, 경보 목록에 방금 만든 항목이 추가되었다. 상태는 ‘데이터 불충분(Insufficient data)’에서 잠시 후 초록색 ‘정상(OK)’으로 바뀌었다.

이 초록색 ‘정상’ 표시는 배포 파이프라인의 ‘Succeeded’와는 전혀 다른 무게감으로 다가왔다. 파이프라인의 성공은 단 한 번의 이벤트로 끝나는 과거의 기록이었다. 하지만 이 경보의 ‘정상’ 표시는 서비스가 지금 이 순간에도 건강하게 숨 쉬고 있다는 살아있는 증거였다. 그리고 만약 상태가 붉은색 ‘경보(Alarm)’로 바뀐다면, 그것은 더 이상 수동적으로 분석해야 할 과거 데이터가 아니라, 즉시 행동에 나서야 하는 현재의 신호가 될 터였다.

“알겠다…” 솔라가 나지막이 말했다. “대시보드로 얻은 통찰을 그냥 머릿속에만 두는 게 아니었어. 이런 경보 규칙으로 만들어서 시스템에 심어두는 거구나. 그럼 시스템이 나 대신 24시간 내내 서비스의 상태를 지켜봐 주는 감시자가 되는 거네.”

솔라는 이제 대시보드를 이전과 전혀 다른 눈으로 바라보았다. 이것은 더 이상 지나간 사건의 기록 보관소가 아니었다. CPU 사용량, 메모리 점유율, 네트워크 트래픽… 이 모든 지표는 미래에 발생할 수 있는 문제들의 잠재적 징후였다. 그리고 이제 솔라는 그 징후들을 어떻게 ‘선제적 대응’이라는 행동으로 연결할 수 있는지 알게 되었다. 그것이 바로 이 ‘경보’라는 다리였다.

스스로 깨달음을 얻은 솔라는 망설임 없이 마우스를 움직였다. 다시 Container Insights 대시보드로 돌아가 이번에는 ‘메모리 사용률(Memory utilization)’ 그래프 옆의 ‘경보 생성’ 버튼을 눌렀다.

“언니, CPU 스파이크처럼 갑자기 메모리가 부족해지는 경우도 위험하잖아. 이것도 비슷한 규칙으로 만들어서, 문제가 커지기 전에 미리 알 수 있게 해둬야겠어.”

루나는 아무 말 없이 미소 지으며 고개를 끄덕였다. 솔라는 이제 더 이상 배포 성공의 초록불에 안주하지 않았다. 운영이라는 긴 항해에서, 자신의 배가 보이지 않는 암초에 부딪히기 전에 미리 경로를 바꾸게 해줄 등대를 스스로 세우고 있었다. 그것이야말로 ‘관측 가능성’이 주는 진정한 힘이었다.