
배포 비용이 저렴해질 때, 결정의 비용은 비싸진다: AI 시대의 클라우드 비용 관리를 위한 가드레일
요약
AI 기술로 구현 비용이 낮아짐에 따라 의사결정의 중요성이 커지는 환경에서, 클라우드 비용을 효율적으로 관리하기 위한 가드레일 구축 전략을 다룹니다. 단순한 비용 절감을 넘어, 머지 시점의 게이트와 일일 가시성을 확보하여 비용 폭증을 사전에 방지하는 방법을 제안합니다.
핵심 포인트
- 구현 속도가 빨라질수록 무엇을 만들지 결정하는 비용이 핵심이 됨
- 사후 비용 귀속이 어려운 환경에서는 사전 차단 게이트가 필수적임
- PR 단계에서 AI 리뷰어를 통해 비용 임계값을 검토하는 프로세스 도입
- 단순 최적화보다 일일 보고와 머지 시점의 가시성 확보가 더 중요함
AI 지원 공지: 이 기사는 Claude의 도움을 받아 작성되었습니다. 모든 기술적 내용, 디자인 결정, 코드 참조 및 스크린샷은 제가 airCloset에서 설계하고 운영하는 프로덕션 시스템을 반영하며, 문장은 출판 전 제가 직접 수정하였습니다.
안녕하세요, airCloset의 CTO인 Ryan입니다.
저희는 내부 AI 플랫폼의 클라우드 청구서를 검토하는 데 일주일을 보냈습니다. 결과부터 말씀드리면 다음과 같습니다:
- Cloud Run 인스턴스 기반 과금 84% 감소 (가장 먼저 테스트한 단일 서비스에서는 44% 감소)
- 유지하는 버전 수를 재검토한 후 컨테이너 이미지 스토리지 65% 감소
- 가장 중요했던 점은 단일 절감액이 아니었습니다. 그것은 머지(merge) 시점의 게이트와 일상적인 가시성이었습니다.
하지만 절감액 자체가 제가 쓰고 싶은 핵심 주제는 아닙니다. 구현(implementation) 비용이 저렴해지면, 무언가를 만들지 말지 결정하는 비용은 그대로 유지되기에, 갑자기 결정이 가장 비싼 부분이 됩니다.
느린 구현은 과거에 결정의 게이트였다
이 글은 정확히 AI에 관한 글은 아닙니다. 하지만 AI는 바로 이 특정한 현상을 성장시키는 요인입니다.
무언가를 만드는 데는 과거에 실제 시간이 소요되었습니다. 규모를 추정하고, 디자인을 검토하고, 엔지니어링 시간을 할당해야 했습니다. 그 과정은 지루하며, 아무도 의도하지 않았던 부작용도 있었습니다. 바로 그 과정이 해당 기능이 정말 만들 가치가 있는지 묻게 만든다는 점입니다. 느린 속도 그 자체가 게이트(gate)였습니다.
구현을 빠르게 만들면 그 게이트는 사라집니다. 아이디어가 떠오른 당일에 바로 배포할 수 있습니다. 이는 진정으로 좋은 일이며,
이 문제를 촉발한 것은 특정 변경 사항 이후 사용량 기반 지출(usage-based spend)이 예상보다 더 많이 증가했던 몇 차례의 사례였습니다. BigQuery 스캔 볼륨(scan volume), 그리고 Vertex AI / Gemini 호출 건수가 그러했습니다.
좌절스러웠던 점은 사후에 해당 지출을 특정 앱에 귀속(attribute)시킬 수 없었다는 것입니다. BigQuery는 쿼리 작업(query jobs)에 레이블(label)을 붙일 수 있게 해주고 작업 이력(job history)도 제공합니다. 하지만 저희 앱 중 여러 개가 동일한 서비스 계정(service account) 아래에서 실행되기 때문에, 어떤 주체(principal)가 쿼리를 실행했는지 아는 것만으로는 어떤 앱이 책임자인지 알 수 없었습니다. 모든 호출 경로를 통해 레이블을 전달할 수도 있겠지만, 그것이 완전히 배포될 때까지는 여전히 같은 상황에 처하게 됩니다.
사후에 귀속시킬 수 없다면, 지출이 늘어나기 전에 차단해야 합니다. 입구에 게이트(gate)를 설치하자는 아이디어는 바로 여기서 나왔습니다.
비용 절감에 앞서 가시성(Visibility)이 확보되어야 한다
결론부터 말씀드리자면, 이번 한 주 동안 가장 영향력이 컸던 부분은 개별적인 최적화가 아니라, 머지 시점(merge time)의 게이트와 일일 보고(daily reporting)였습니다. 비용 절감은 일회성입니다. 그대로 두면 똑같은 문제들이 다시 쌓이게 됩니다.
게이트: 임계값을 초과하는 변경 사항은 사람에게 전달된다
저희는 AI 리뷰어(AI reviewer)의 검토 차원(review dimension)에 비용을 추가했습니다. 만약 PR(Pull Request)이 사용량 기반 지출을 임계값 이상으로 높인다면, 해당 건은 사람에게 전달됩니다. 세 가지 임계값이 있습니다:
| 카테고리 | 임계값 |
|---|---|
| 지속적인 일일 비용 증가 | ¥2,000/일 (~$13) |
| ... |
여기서 제가 중요하게 생각하는 설계 선택은 다음과 같습니다: AI가 산술 연산(arithmetic)을 수행하지 않는다는 점입니다. AI는 변경 사항을 읽고 스캔 볼륨(scan volume), 호출 횟수(call counts), 토큰 수(token counts)와 같은 수치를 측정합니다. 이를 통화로 변환하고 임계값과 비교하는 작업은 결정론적 스크립트(deterministic script)에서 수행됩니다. 돈 계산을 모델에 맡기면 가끔 산술적 실수나 잘못된 단가, 그리고 실행할 때마다 결과가 달라지는(drift) 답변을 얻게 됩니다. 날짜마다 다른 답변을 내놓는 게이트는 게이트가 아닙니다.
단가(Unit prices)는 공표된 요금표(rate card)가 아니라, 실제 청구 데이터 내보내기(지출액 ÷ 사용량)를 통해 산출됩니다. 리스트 가격(list prices)을 하드코딩하는 것은 USD 환산, 환율, 그리고 약정 할인(committed-use discounts)이라는 세 가지 별도의 드리프트(drift) 소스를 떠안는 것을 의미합니다.
저장 비용(Storage)은 별도의 임계값(threshold)을 가집니다. 왜냐하면 누적 비용(cumulative cost)은 실행 비용(execution cost)과 다르게 작동하기 때문입니다. 작업을 중단하면 해당 작업의 일일 비용은 0이 됩니다. 하지만 이미 작성된 데이터는 누군가 삭제하기 전까지 계속 비용을 발생시킵니다. 따라서 저장 비용에 대해서는 다른 질문을 던져야 합니다. '만료 정책(expiration policy)이 있는가?', 그리고 '이것이 향후 매달 얼마를 추가하는가?'와 같은 질문입니다. 이것은 이미 발생한 일을 측정하는 것이 아니라, 호출량(call volume)에 페이로드 크기(payload size)를 곱하여 계산된 **향후 예측(forward projection)**입니다.
일일 보고: 하루에 두 번 Slack에 게시
우리는 앱별 비용을 하루에 두 번 Slack에 게시합니다. 여기서 언급할 만한 몇 가지 결정 사항이 있습니다.
임계값을 넘지 않은 날에도 게시하십시오. "오늘은 확인할 내용이 없습니다"라는 메시지도 정보입니다. 더 중요한 것은, 독자가 평온한 날과 보고 시스템이 고장 난 날을 구분할 수 있어야 한다는 점입니다. 문제가 생겼을 때만 말을 하는 모니터는 자신이 죽었다는 사실을 알려줄 수 없습니다.
어제와 비교하지 말고, 지난주 같은 요일과 비교하십시오. 배치(Batch) 볼륨은 요일마다 다르기 때문에, 전일 대비(day-over-day) 비교를 하면 매주 월요일마다 급증(spike)으로 표시되어 사람들은 일주일 안에 보고를 읽지 않게 됩니다.
"지난주 데이터 없음"을 증가로 처리하십시오. 이것이 가장 위험한 경우입니다. 막 실행되기 시작한 배치는 당신을 놀라게 만드는 바로 그 요소입니다. 퍼센트 변화(Percent-change) 지표는 기준값(baseline)이 0일 때 수학적으로 정의되지 않기 때문에 이러한 경우를 무시하는 경향이 있으므로, 우리는 이 케이스를 명시적으로 처리합니다.
최후의 보루: 사용량 기반 서비스에 상한선 설정
게이트(Gates)와 보고(reporting)에는 여전히 누수(leak)가 발생합니다. 수동 실행, 외부 트리거, 그리고 아무도 예상하지 못한 사용 패턴처럼 PR(Pull Request)을 전혀 거치지 않는 경로들이 존재합니다.
따라서 사용량에 따라 비용이 청구되는 모든 것에 대해, 실제 운영 방식에 맞는 상한선(ceiling)을 설정하십시오. BigQuery, Vertex AI, Gemini 모두 할당량(quotas)을 통해 이를 제한할 수 있습니다.
중요한 점은 플랫폼이 허용하는 수치가 아니라, 측정된 사용량으로부터 상한선(ceiling)을 도출해야 한다는 것입니다.
BigQuery를 예로 들어보겠습니다. 온디맨드(On-demand) 가격 책정 방식은 프로젝트당 하루 200 TiB의 기본 제한(cap)이 설정되어 있습니다(이는 2025년 9월부터 기본값이며, 그 이전에는 무제한이었습니다). TiB당 가격은 지역마다 다르지만, TiB당 몇 달러라고 가정하면 이 상한선은 하루에 천 달러가 넘는 금액이 됩니다. 이를 한 달 내내 실행하면 수만 달러에 달하게 됩니다.
이는 기본 설정이 다음과 같음을 의미합니다: 아무에게도 알리지 않은 채, 매달 그만큼의 금액을 지출하십시오. 이것은 상한선(ceiling)이라고 할 수 없습니다.
우리는 BigQuery 스캔 볼륨을 평소 사용량의 약 1.5배로 제한합니다. "안전을 위해 평소의 10배로 설정하자"는 생각은 기본 설정과 동일한 문제를 가집니다. 상한선은 통제 불능의 상황이 실제로 멈추는 지점에 위치할 때에만 의미가 있습니다.
한 가지 분명히 해둘 점이 있습니다: 우리가 이렇게 할 수 있는 이유는 이것이 내부 인프라(internal infrastructure)이기 때문입니다. 1.5배로 제한한다는 것은 한계치에 도달했을 때 작업이 중단되는 것을 수용한다는 의미입니다. 내부 플랫폼의 경우, 내일 다시 실행하면 됩니다. 고객 대상 경로(customer-facing path)에서 똑같이 행동한다면, 스스로 장애(outage)를 만든 셈이 됩니다. 중단되어도 괜찮은 대상에만 이런 종류의 상한선을 설정하십시오. 최종 사용자(end users)와 접점이 있는 모든 것에는 상한선을 훨씬 높게 설정하고, 대신 알림(alerting)과 오토스케일링(autoscaling)으로 처리하십시오.
그러한 제약 조건을 고려할 때, 부작용은 괜찮습니다. 정상적인 작업이 가끔 상한선에 도달할 수 있으며, 그럴 때가 바로 왜 평소 볼륨의 1.5배가 필요했는지 원인을 찾아보라는 신호가 됩니다. 이는 그 자체로 일종의 탐지(detection) 방식입니다. 알림(Alerts)은 사후에 알려주지만, 할당량(quota)은 일이 벌어지고 있는 도중에 중단시킵니다. 사용량 기반 과금(usage-based billing) 체제에서는 이 차이가 매우 중요합니다.

이 모든 것을 시스템이 아닌 습관으로 실행하려 한다면, 첫 번째 바쁜 주가 지나자마자 증발해 버릴 것입니다. 이것은 구조화되었을 때만 작동합니다. 입구에서의 게이트(Gate), 지속적인 가시성을 위한 일일 보고(Daily reporting), 그리고 강제 중단점으로서의 할당량(Quota). 이 세 가지 계층이 결합되어야만 비로소 보이지 않던 것들이 가시화됩니다.
사례 1: 기본값이 비싼 설정일 때, 속도는 부채를 쌓는다
이제 우리가 실제로 발견한 내용들을 살펴보겠습니다. 먼저 Cloud Run입니다.
Cloud Run에는 두 가지 과금 모델이 있습니다. CPU가 항상 할당되는 인스턴스 기반(Instance-based) 모델과, 요청을 처리하는 동안에만 할당되는 요청 기반(Request-based) 모델입니다. 우리의 Cloud Run 지출 중 97%가 인스턴스 기반이었습니다.
원인은 구조적인 문제였습니다. 리포지토리(Repo)에서 Cloud Run 서비스를 정의하는 곳을 살펴보니, 대부분의 서비스가 cpuIdle을 전혀 지정하지 않았습니다. 공유된 팩토리(Factory)가 없었기 때문에, 누군가 직접 설정하지 않는 한 모든 새로운 서비스는 조용히 비싼 쪽으로 설정되었습니다.
알아둘 점: 콘솔이나 gcloud를 통해 배포하면 기본값은 저렴한 방식인 요청 기반(Request-based)입니다. 하지만 IaC(Infrastructure as Code)를 통해 명시적인 리소스 제한과 cpuIdle을 false로 설정하여 동일한 서비스를 선언적으로 정의하면, 인스턴스 기반(Instance-based)으로 설정됩니다. 즉, 클릭해서 설정하면 저렴한 기본값을 얻지만, 코드로 작성하면 비싼 기본값을 얻게 됩니다. 인프라 관리 관행이 더 엄격할수록, 이러한 차이를 놓치기가 더 쉬워집니다.
이것이 바로 앞서 언급한 핵심입니다. 기본값이 비싼 쪽에 위치할 때, 속도를 높이는 것은 자동으로 부채를 쌓는 행위가 됩니다. 서비스당 발생하는 차이는 아무도 눈치채지 못할 만큼 작기 때문입니다.
이름이 반대로 읽힌다
명명 규칙에 함정이 있습니다. 설정 이름이 cpuIdle이기 때문입니다.
| 값 | 동작 | 과금 방식 |
|---|---|---|
true | 요청 중에만 CPU 할당 | 요청 기반 (더 저렴함) |
false (IaC 기본값) | CPU 항상 할당 | 인스턴스 기반 (더 비쌈) |
true를 "유휴 상태(idle) 중에도 계속 실행됨"으로 읽는다면, 당신은 정반대로 이해하고 있는 것입니다. 그리고 그 실패 모드는 매우 고약합니다. 예외도, 에러도 발생하지 않고, 단지 응답이 발생하지 않은 이후에 수행되어야 할 작업이 조용히 사라질 뿐입니다. '발사 후 망각(fire-and-forget)' 방식의 백그라운드 작업을 수행하는 서비스를 true로 전환하면, 해당 작업은 로그에 흔적도 남기지 않고 사라져 버립니다.
그래서 우리는 결정을 단 하나의 질문으로 축소했습니다: "이 서비스는 응답을 반환한 후에도 계속 작동하는가?"
제외 목록은 보기보다 좁습니다
어떤 서비스를 전환할지 선택할 때, 우리는 "아마도 항상 CPU가 필요할 것 같은" 모든 것을 포함하는 관대한 제외 목록(exclusion list)에서 시작했습니다. 하지만 대부분은 그렇지 않은 것으로 밝혀졌습니다. Cloud Run의 동작 방식에는 명확하지 않은 두 가지 측면이 있습니다.
첫째, 이 설정과 관계없이 컨테이너 시작(startup) 중에는 CPU가 할당됩니다. 웹 서버가 시작되기 전, 부팅 시 BigQuery에서 대규모 데이터 세트를 동기식(synchronously)으로 로드하는 서비스는 명백히 "지속적인 CPU가 필요한" 사례처럼 보입니다. 하지만 그 작업은 시작 단계에서 발생하므로, 설정을 전환해도 괜찮습니다.
둘째, 긴 타임아웃(timeout)은 제외 사유가 아닙니다. 3,600초 동안 동기식 작업이 수행되는 배치(batch) 작업은 그 시간 내내 요청을 처리하고 있는 상태이므로 CPU를 보유하고 있습니다. "무거운 작업이므로 지속적인 CPU가 필요하다"는 논리는 성립하지 않습니다.
실제로 제외해야 하는 것은 응답이 나간 후에도 계속 작동하는 서비스, 즉 '발사 후 망각(fire-and-forget)' 방식의 서비스들입니다. 그것이 목록의 전부입니다. 이 결론에 도달하기까지 제외 세트를 여러 번 다시 작성해야 했습니다.
이러한 설정은 직관에 의존하여 결정할 때 항상 과도하게 제외하는 경향이 있습니다. 서비스가 필요할 것 같다는 느낌이 아니라, 명시할 수 있는 조건에 기반하여 제외하십시오. 규칙을 한 줄로 압축할 수 있는지 여부는 당신이 그 규칙을 제대로 이해했는지를 판단하는 괜찮은 척도가 됩니다.
결과, 그리고 퇴보(regression) 방지
우리가 전환한 첫 번째 서비스는 단독으로 비용이 44% 감소했습니다. 이를 전체로 확대 적용하자 인스턴스 기반 과금(instance-based billing)이 전체적으로 84% 감소했습니다. '발사 후 망각' 작업을 수행하는 몇 안 되는 서비스를 제외하고 모든 서비스가 전환되었습니다.
그다음 우리는 cpuIdle 설정이 없는 서비스 정의에 대해 실패하는 CI 가드레일 (CI guard)을 추가했습니다. 제가 마음에 드는 부분은 cpuIdle: false를 명시적으로 지정하는 것 또한 위반 사항으로 간주된다는 점입니다. 만약 어떤 서비스가 진정으로 지속적인 CPU 사용을 필요로 한다면, 작성된 사유와 함께 허용 목록 (allowlist)에 포함됩니다. 핵심은 누군가가 왜 그런 설정이 필요한지 직접 글로 작성하게 만드는 것입니다.
그리고 이러한 가드레일이 있으면, 이제 우리는 의도적으로 위반 사항을 작성하고 빌드가 실제로 실패하는지 확인합니다. 작성되었다고 해서 반드시 실행되는 가드레일인 것은 아니며, 실행되지 않을 때 이를 알려주는 방법은 없습니다. 보호받고 있다는 믿음만 남게 되는데, 이는 보호받지 못하고 있다는 사실을 아는 것보다 더 나쁩니다.
사례 2: 데이터는 실제 복구 방식이 어떻게 작동하는지 알려주지 못한다
컨테이너 이미지 저장 (Container image storage) 또한 또 다른 큰 비용 항목이었습니다. 해결 방법은 간단했습니다. 이미지 버전을 10개 유지하던 것에서 2개만 유지하는 것으로 변경했습니다. 그 결과 저장 비용이 65% 감소했습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기