12.2 클라우드 컴퓨팅 — 책임을 어디까지 넘기는가
"클라우드는 서버가 없다"는 말은 서버가 사라졌다는 뜻이 아니라, 서버의 존재를 계량된 API 뒤로 감추고 그 운영 책임의 일부를 벤더에게 넘겼다는 뜻이다. 이 문서는 IaaS·PaaS·SaaS라는 책임의 경계, 탄력성(elasticity)과 오토스케일링의 비용 모델, 관리형 서비스의 책임 공유(shared responsibility), 서버리스(FaaS)의 실행 모델과 과금 단위를 다루고, 벤더 가격표 대신 실행 가능한 비용 모델 스크립트로 손익분기점을 계산해 본다.
학습 목표
- IaaS·PaaS·SaaS의 구조를 벤더와 고객이 각각 무엇을 관리하는지로 구분하고 아키텍처 선택의 근거로 삼는다.
- 탄력성과 오토스케일링의 비용 모델을 유휴 자원 비용과 반응 지연의 트레이드오프로 설명한다.
- 관리형 서비스의 책임 공유 모델을 근거로 장애·보안 사고의 책임 경계를 판단한다.
- 서버리스(FaaS)의 실행 모델(콜드 스타트, 상태 비저장, 과금 단위)을 설명하고 손익분기 계산으로 배치 결정을 검증한다.
배경: 왜 이것이 존재하는가
온프레미스 용량 계획은 미래를 추측하는 일이다. 다음 분기의 트래픽을 예측해 서버를 발주하면, 조달·설치에 몇 주에서 몇 달이 걸리고 그동안 트래픽 예측이 틀릴 위험을 그대로 안는다. 넉넉히 사면 평소에는 그 여유 용량이 놀고, 부족하게 사면 성수기에 요청을 거절해야 한다. 이 문제의 근원은 **자본 지출(capex)**이다 — 서버를 사는 순간 그 비용은 고정되고, 이후 실제 사용량과 무관하게 회수해야 한다.
클라우드 컴퓨팅의 핵심 아이디어는 이 고정 비용을 사용한 만큼 내는 **운영 지출(opex)**로 바꾸는 것이다. NIST SP 800-145(2011)가 정리한 다섯 가지 필수 특성 — 요청 즉시 스스로 프로비저닝(on-demand self-service), 표준화된 네트워크 접근, 여러 사용자가 물리 자원을 공유하는 자원 풀링(resource pooling), 수요에 맞춰 빠르게 늘고 줄이는 탄력성(rapid elasticity), 사용량을 계측해 과금하는 계량된 서비스(measured service) — 는 전부 "물리 자원의 소유"에서 "계량된 접근권의 구매"로 옮기기 위한 조건들이다. 그리고 이 접근권을 어느 높이에서 파는지에 따라 IaaS·PaaS·SaaS로 나뉜다.
이 전환이 감추는 것이 하나 있다. 비용이 사라지는 것이 아니라 누가 그 비용과 책임을 지는지가 바뀔 뿐이라는 점이다. 12.1이 다룬 격리 계층이 "이 워크로드가 어디까지 남을 신뢰하는가"를 정한다면, 이 문서가 다루는 서비스 모델은 "이 워크로드의 운영 책임을 어디까지 넘기는가"를 정한다. 두 축은 서로 다른 질문이며, 함께 판단해야 한다.
핵심 개념
IaaS·PaaS·SaaS — 책임의 경계가 스택을 오르내린다
세 모델은 같은 스택에서 벤더가 관리하는 층의 높이만 다르다.
IaaS PaaS SaaS
데이터·설정 고객 관리 고객 관리 고객 관리
애플리케이션 고객 관리 고객 관리 벤더 관리
런타임·미들웨어 고객 관리 벤더 관리 벤더 관리
게스트 OS 고객 관리 벤더 관리 벤더 관리
가상화(12.1) 벤더 관리 벤더 관리 벤더 관리
물리 인프라 벤더 관리 벤더 관리 벤더 관리- IaaS(Infrastructure as Service): 벤더는 물리 인프라와 12.1의 가상화 계층까지 관리하고, 고객은 게스트 OS 이후 전부(패치, 런타임, 애플리케이션, 데이터)를 관리한다. 온프레미스와 가장 가까운 통제권을 유지하지만, OS 패치·런타임 업그레이드 같은 운영 부담도 그대로 남는다.
- PaaS(Platform as a Service): 벤더가 런타임·미들웨어까지 관리한다. 고객은 애플리케이션 코드와 데이터만 다룬다. OS 패치는 사라지지만, 언어·런타임 버전이나 실행 모델(예: 백그라운드 프로세스 제약)이 플랫폼이 정한 틀에 갇힌다.
- SaaS(Software as a Service): 벤더가 애플리케이션까지 관리하고, 고객은 데이터와 설정만 다룬다. 운영 부담이 가장 적지만 커스터마이징의 여지도 가장 적다.
세 모델 중 "무엇이 최선인가"는 질문이 성립하지 않는다. 진짜 질문은 **"어떤 운영 부담을 우리가 직접 지고 싶은가, 그리고 그 부담을 넘기는 대가로 어떤 제약을 받아들일 수 있는가"**다. 규제 때문에 런타임 패치 주기를 직접 통제해야 하는 조직은 PaaS의 자동 업그레이드가 오히려 위험 요소이고, 빠르게 기능을 출시해야 하는 소규모 팀에게는 OS 패치를 직접 관리할 이유가 없다.
탄력성과 오토스케일링의 비용 모델
**탄력성(elasticity)**은 확장성(scalability, 용량을 늘릴 수 있음)과 다르다. 탄력성은 수요에 맞춰 늘고 줄기를 자동으로, 빠르게 한다는 뜻을 더한다. 이 차이가 비용 모델을 만든다.
고정 용량(온프레미스, 또는 클라우드의 예약 인스턴스)은 피크에 맞춰 사이즈를 정해야 한다 — 그렇지 않으면 성수기에 요청을 거절한다. 평균 사용률이 피크의 일부에 불과한 워크로드(주간 트래픽 패턴, 계절성 있는 서비스)라면, 피크 기준으로 산 용량의 나머지는 대부분 시간 동안 유휴 상태로 비용만 나간다. 이 유휴 비용이 탄력성이 없을 때 지불하는 대가다.
오토스케일링은 이 유휴 비용을 줄이지만 없애지는 못한다. 새 인스턴스를 추가하는 데는 프로비저닝·부팅·워밍업 시간이 걸리므로(반응 지연), 스케일 트리거를 사용률 100%에 걸면 새 용량이 준비되기 전에 이미 요청이 밀린다. 그래서 실제 오토스케일링은 목표 사용률을 100%보다 낮게(예: 60~70%) 잡아 반응 지연을 흡수할 여유(buffer)를 남긴다 — 이 여유 역시 유휴 비용이며, 다만 "항상 피크만큼"에서 "피크 대비 반응 지연 구간만큼"으로 줄어든 유휴 비용이다.
이 모델에서 중요한 함의가 나온다. 탄력성이 절약하는 비용은 워크로드의 모양(피크 대 평균의 격차)에 비례한다. 하루 종일 거의 일정한 부하를 받는 서비스는 피크와 평균이 같으므로 오토스케일링을 켜도 절약할 유휴 용량 자체가 없고, 반응 지연이라는 위험만 추가로 진다. 반대로 특정 시간대에만 몰리는 스파이크형 워크로드는 절약이 크다. "오토스케일링을 켜면 무조건 비용이 준다"는 문장은 이 조건을 생략한 것이다.
이 스펙트럼의 극단이 scale-to-zero다. 요청이 없을 때 용량을 0까지 줄이면 유휴 비용도 0이 된다 — 다음 절의 서버리스가 정확히 이 지점에 선다.
관리형 서비스와 책임 공유 모델
관리형 서비스(managed service)는 앞의 IaaS/PaaS/SaaS 구분을 특정 소프트웨어(데이터베이스, 메시지 큐, 로드 밸런서)에 적용한 것이다. 벤더가 그 소프트웨어의 설치·패치·이중화·백업 메커니즘을 대신 운영해 준다. 이때 반드시 짚어야 할 것이 **책임 공유 모델(shared responsibility model)**이다.
일반적인 구조는 "클라우드 자체의 보안·운영"(벤더 책임 — 물리 시설, 하이퍼바이저 패치, 관리형 소프트웨어의 가용성 유지)과 "클라우드 안에서의 보안·운영"(고객 책임 — 접근 권한 설정, 데이터 분류, 애플리케이션 로직, 제공된 백업 기능을 실제로 켜고 검증하는 것)으로 나뉜다. 이 경계는 IaaS에서 SaaS로 갈수록 고객 책임 쪽이 줄어들지만, 데이터 분류와 접근 제어 설정만큼은 어떤 서비스 모델에서도 항상 고객 책임으로 남는다. 관리형 서비스가 암호화·백업·복제 기능을 "제공"하는 것과 그 기능을 고객이 "올바르게 설정"하는 것은 다른 일이고, 후자의 실패는 관리형이라는 이유로 면제되지 않는다.
이 경계는 이미 다룬 개념들의 구체적인 적용이다. 관리형 분산 데이터 저장소가 광고하는 "강한 일관성"이 실제로 무엇을 보장하는지는 10.2의 일관성 모델 스펙트럼으로 검증해야 하고, 관리형 데이터베이스가 제공하는 복제·백업의 구체적 계약은 챕터 11이 다룬다. 관리형 로드 밸런서·CDN이 요청 경로에 추가하는 구간과 그 구간의 실패 모드는 9.5가 다룬 것과 동일하다 — "관리형"이라는 말이 그 아래 메커니즘을 없애지 않는다. IaC와 CI/CD로 이 인프라를 코드로 관리하는 실천은 15.3 DevOps에 위임한다.
서버리스(FaaS)의 실행 모델
서버리스(더 정확히는 FaaS, Function as a Service)는 배포 단위를 상태 없는 핸들러 함수로 좁히고, 그 함수를 실행할 환경의 프로비저닝·스케일링을 전부 벤더에게 넘긴 모델이다. 실행 생명 주기를 알아야 콜드 스타트와 상태 제약을 이해할 수 있다.
콜드 스타트 웜 호출
┌───────────────┐┌──────────┐┌────────┐ ┌────────┐
│ 실행 환경 준비 ││ 런타임 초기화││ 모듈 최상위 코드 │ │ 핸들러 호출 │
│(경량 VM/샌드박스)││ (언어 프로세스 시작) ││ (import, 전역 초기화) │ │ (반복됨) │
└───────────────┘└──────────┘└────────┘ └────────┘
└──────────── 1회, 환경마다 ────────────┘콜드 스타트는 (1) 실행 환경 자체를 준비하고, (2) 언어 런타임을 초기화하고, (3) 코드 최상위의 import·전역 초기화를 실행하는 세 단계를 거친다. 이 셋은 환경 하나당 한 번만 일어나고, 이후 같은(웜) 환경으로 들어오는 호출은 핸들러 본문만 반복 실행한다. 그래서 DB 커넥션 풀 생성이나 무거운 라이브러리 로딩을 핸들러 밖(모듈 최상위)에 두면 웜 호출들이 그 비용을 나눠 갖고, 핸들러 안에 두면 매 호출마다 반복된다 — 같은 코드를 어디에 두느냐가 콜드/웜 상태에 따라 완전히 다른 비용 프로파일을 만든다.
언어 런타임 층에서도 같은 구조가 한 겹 더 반복된다. 5.3 인터프리터와 JIT가 다루듯 JIT의 이득은 워밍업 비용을 회수할 만큼 같은 코드가 충분히 반복 실행되는 것을 전제하는데, 실행 환경이 짧게 살고 자주 새로 뜨는 FaaS에서는 그 전제가 약해진다. "이 언어는 JIT이 있으니 오래 돌수록 빨라진다"는 기대가 서버리스에서 잘 맞지 않는 이유이며, 확인해야 할 값은 실행 환경의 수명 분포와 환경당 처리한 요청 수다 — 이것이 콜드 스타트 비용을 나누는 분모다.
상태 비저장(stateless) 제약은 임의로 정한 규칙이 아니라 이 실행 모델의 직접적 귀결이다. 어떤 호출이 새 환경에 배정될지, 기존 웜 환경이 언제 회수될지는 벤더의 스케줄링에 달려 있고, 동시에 들어온 호출은 보통 각자 별도의 환경에 배정된다(하나의 프로세스가 여러 요청을 동시에 처리하도록 보장하지 않는다). 로컬 디스크나 프로세스 메모리에 상태를 두면 다음 호출이 다른 환경에 떨어지는 순간 그 상태는 없다 — 상태는 반드시 외부화(챕터 11의 관리형 데이터베이스 같은)해야 한다.
과금 단위는 이 모델을 그대로 반영한다. 호출 횟수와 (할당 메모리 × 실행 시간)의 곱인 GB-초가 두 축이다 — 예약 인스턴스가 요청이 있든 없든 정액을 청구하는 것과 반대로, 요청이 없으면 비용도 0에 수렴한다(scale-to-zero). 콜드 스타트를 피하려고 일부 벤더가 제공하는 "프로비저닝된 동시성"(사전에 웜 환경 N개를 예약해 두는 기능)은 그 웜 환경을 유휴 상태에서도 계속 비용으로 청구한다 — 결국 앞서 본 유휴 비용 대 반응 지연의 트레이드오프를 값을 치르고 되사는 것이다.
Mini 실험: 서버리스와 예약 인스턴스의 손익분기점
벤더 가격표의 실제 숫자는 시점·리전·계정마다 다르고 이 저장소에서 재현할 수 없다. 대신 과금 축의 구조(호출 수, 실행 시간, 메모리)를 고정하고 단가를 파라미터로 둔 모델을 TypeScript(Node.js 24)로 실행해 손익분기 호출 수를 계산한다. 아래 단가는 구조를 보이기 위한 예시값이며 실제 벤더 가격이 아니다 — 자신의 판단에는 현재 벤더 가격표의 단가로 교체해 다시 계산해야 한다.
// serverless-vs-reserved.ts — 요청량에 따른 손익분기점을 계산한다
// 실행: node serverless-vs-reserved.ts (Node.js 24, 별도 빌드 없이 타입 스트리핑으로 실행된다)
//
// 주의: pricePerInvocation·pricePerGbSecond·instanceMonthlyPrice는
// 과금 축의 구조를 보이기 위한 예시 파라미터이며 특정 벤더의 실제 가격표가 아니다.
interface ServerlessParams {
invocations: number; // 월간 호출 수
avgDurationMs: number; // 요청당 평균 실행 시간(ms)
memoryMb: number; // 할당 메모리(MB)
pricePerInvocation: number; // 호출당 단가(예시)
pricePerGbSecond: number; // GB-초당 단가(예시)
freeInvocations: number; // 무료 티어 호출 수(예시)
freeGbSeconds: number; // 무료 티어 GB-초(예시)
}
function serverlessMonthlyCost(p: ServerlessParams): number {
const gbSeconds = (p.memoryMb / 1024) * (p.avgDurationMs / 1000) * p.invocations;
const billableInvocations = Math.max(0, p.invocations - p.freeInvocations);
const billableGbSeconds = Math.max(0, gbSeconds - p.freeGbSeconds);
return billableInvocations * p.pricePerInvocation + billableGbSeconds * p.pricePerGbSecond;
}
function reservedMonthlyCost(instanceMonthlyPrice: number, instanceCount: number): number {
return instanceMonthlyPrice * instanceCount;
}
// 서버리스 비용은 invocations에 대해 단조 증가하므로 이분 탐색으로 교차점을 구한다.
function breakEvenInvocations(
reservedCost: number,
makeParams: (invocations: number) => ServerlessParams,
hiBound = 100_000_000,
): number | null {
let lo = 0;
let hi = hiBound;
if (serverlessMonthlyCost(makeParams(hi)) < reservedCost) return null; // 탐색 범위 안에서는 서버리스가 항상 저렴하다
for (let i = 0; i < 50; i++) {
const mid = Math.floor((lo + hi) / 2);
if (serverlessMonthlyCost(makeParams(mid)) < reservedCost) lo = mid;
else hi = mid;
}
return hi;
}
const baseParams = (invocations: number): ServerlessParams => ({
invocations,
avgDurationMs: 200,
memoryMb: 512,
pricePerInvocation: 0.0000002,
pricePerGbSecond: 0.0000167,
freeInvocations: 1_000_000,
freeGbSeconds: 400_000,
});
const instanceMonthlyPrice = 50; // 예시: 상시 가동 예약 인스턴스 1대의 월 고정 비용
const reserved = reservedMonthlyCost(instanceMonthlyPrice, 1);
const breakEven = breakEvenInvocations(reserved, baseParams);
console.log(`월 고정(예약 인스턴스) 비용: $${reserved.toFixed(2)}`);
if (breakEven !== null) {
const atBreakEven = serverlessMonthlyCost(baseParams(breakEven));
console.log(`손익분기 호출 수(월간): ${breakEven.toLocaleString()}회`);
console.log(`손익분기 지점에서 서버리스 비용: $${atBreakEven.toFixed(2)}`);
}
console.log("\n호출 수별 서버리스 비용 vs 예약 비용");
for (const inv of [10_000, 100_000, 1_000_000, 5_000_000, 10_000_000, 50_000_000]) {
const cost = serverlessMonthlyCost(baseParams(inv));
const cheaper = cost < reserved ? "서버리스가 저렴" : "예약이 저렴";
console.log(
` invocations=${inv.toString().padStart(9)} → serverless=$${cost.toFixed(2).padStart(9)} reserved=$${reserved.toFixed(2)} (${cheaper})`,
);
}Node.js v24.14.0에서 실행한 결과다(이 저장소의 실측).
월 고정(예약 인스턴스) 비용: $50.00
손익분기 호출 수(월간): 30,417,113회
손익분기 지점에서 서버리스 비용: $50.00
호출 수별 서버리스 비용 vs 예약 비용
invocations= 10000 → serverless=$ 0.00 reserved=$50.00 (서버리스가 저렴)
invocations= 100000 → serverless=$ 0.00 reserved=$50.00 (서버리스가 저렴)
invocations= 1000000 → serverless=$ 0.00 reserved=$50.00 (서버리스가 저렴)
invocations= 5000000 → serverless=$ 2.47 reserved=$50.00 (서버리스가 저렴)
invocations= 10000000 → serverless=$ 11.82 reserved=$50.00 (서버리스가 저렴)
invocations= 50000000 → serverless=$ 86.62 reserved=$50.00 (예약이 저렴)이 모델이 보여주는 것은 숫자 자체가 아니라 구조다. 월 3천만 호출 근처의 어느 지점을 지나면 서버리스가 예약 인스턴스보다 비싸진다 — 손익분기점이 존재하고, 그 위치는 호출 수·실행 시간·메모리라는 세 파라미터에 따라 움직인다. 이 모델이 의도적으로 생략한 것도 분명히 해 둘 필요가 있다. 요청 동시성 상한, 네트워크 송신 비용, 다년 약정 할인, 프로비저닝된 동시성의 추가 비용은 포함하지 않았다 — 실제 배치 결정에는 이 항목들을 추가하고, baseParams의 예시 단가를 현재 벤더 가격표의 단가로, avgDurationMs·memoryMb를 자신의 워크로드에서 실측한 값으로 바꿔 다시 계산해야 한다. "서버리스가 항상 싸다"거나 "월 N회를 넘으면 무조건 예약이 낫다" 같은 일반 규칙이 아니라, 이 스크립트에 자신의 워크로드 숫자를 넣어 보는 것이 확인 방법이다.
실무 관점
"서버리스는 안 쓰면 공짜다"의 함정
호출이 없을 때 비용이 0에 수렴하는 것은 맞지만, 꾸준히 높은 트래픽을 받는 워크로드에서는 위 실험이 보여준 손익분기점을 넘어 예약형보다 비싸질 수 있다. 트래픽 패턴이 스파이크형(대부분 유휴, 가끔 폭증)인지 꾸준한지가 어느 모델이 유리한지를 가르는 첫 질문이다 — 이는 앞서 오토스케일링에서 본 "탄력성의 가치는 워크로드 모양에 비례한다"는 원리와 정확히 같다.
핸들러가 반환한 뒤의 코드는 실행을 보장받지 못할 수 있다
일부 FaaS 런타임은 응답을 반환한 뒤 그 실행 환경을 동결하거나 곧바로 다른 요청에 재사용한다. 핸들러가 응답을 먼저 보내고 백그라운드에서 로그 전송이나 후처리를 계속하려는 코드는, 그 비동기 작업이 실제로 끝나기 전에 환경이 동결되어 조용히 유실될 수 있다. 응답 이전에 필요한 작업을 완료하거나, 완료를 보장해야 하는 후처리는 별도의 큐·워커로 넘기는 것이 안전하다.
"관리형이니 우리 책임은 끝났다"는 착각
책임은 사라지지 않고 재배치될 뿐이다. 관리형 서비스가 복제·백업 메커니즘을 제공해도 그 설정(복제본 수, 백업 주기, 접근 권한)이 틀리면 장애는 고객 책임으로 남는다. 벤더의 SLA는 벤더가 관리하는 층에만 적용되고, 그 위에서 고객이 설정한 것에는 적용되지 않는다. 그래서 벤더 SLA의 숫자를 자사 서비스의 신뢰성 목표로 그대로 옮겨 적을 수 없다 — 사용자가 실제로 겪는 가용성은 여러 의존 서비스의 SLA와 자사 코드·설정이 함께 만든 결과다. 자사 서비스가 사용자에게 약속할 수준은 14.3 신뢰성 공학이 다루는 SLI·SLO·에러 버짓으로 따로 정의하고 직접 측정해야 한다.
배치 결정은 12.1의 격리 축과 이 문서의 책임 축을 함께 본다
VM·컨테이너·서버리스 중 무엇을 쓸지는 이 챕터의 두 축을 모두 가로지르는 결정이다. 신뢰할 수 없는 코드를 실행해야 하는가(12.1의 격리 축), 운영 부담을 어디까지 넘기고 싶은가(이 문서의 책임 축), 워크로드가 스파이크형인가 꾸준한가(탄력성 축)를 함께 답해야 하나의 배치 결정이 나온다. 어느 한 축만 보고 "컨테이너가 가벼우니까", "서버리스가 최신이니까" 고르는 것은 나머지 두 축의 비용을 나중에 발견하게 만든다.
더 깊이: 오토스케일링의 플래핑과 히스테리시스
스케일링 지표(예: CPU 사용률)가 임계값 근처에서 잠깐씩 흔들리면, 오토스케일러가 인스턴스를 추가했다가 곧바로 줄이기를 반복하는 **플래핑(flapping)**이 생길 수 있다. 매번의 스케일아웃·인은 그 자체로 비용(새 인스턴스 부팅, 연결 재분배)을 지불하므로, 지표가 잠깐 넘었다고 즉시 반응하면 이 비용만 반복해서 낸다.
일반적인 대응은 **쿨다운(cooldown)**과 **히스테리시스(hysteresis)**다. 스케일링 후 일정 시간 동안 추가 스케일링 판단을 유보하고(쿨다운), 스케일아웃 임계값과 스케일인 임계값을 다르게 잡아(예: 70% 넘으면 추가, 30% 아래로 떨어져야 축소) 지표가 중간 구간에서 흔들려도 반응하지 않게 한다. 스케일인을 스케일아웃보다 보수적으로(더 오래, 더 낮은 임계값에서) 설정하는 것도 흔한 관행인데, 너무 이르게 용량을 줄였다가 반응 지연 때문에 요청을 놓치는 비용이 용량을 조금 더 오래 유지하는 유휴 비용보다 크다고 보기 때문이다. 지표가 임계 근처에서 흔들릴 때 시스템이 과도하게 반응하는 현상 자체는 8.2의 thrashing에서 본 "회수가 곧 재사용을 부르는" 진동 패턴과 형태가 닮았다 — 메커니즘은 다르지만, 경계 근처의 지표만 보고 즉시 반응하면 시스템이 진동한다는 구조적 교훈은 같다.
정리
- 클라우드 컴퓨팅은 자본 지출(서버 소유)을 운영 지출(계량된 접근권)로 바꾸며, IaaS·PaaS·SaaS는 벤더가 관리하는 스택의 높이로 구분된다. 어느 모델이 맞는지는 어떤 운영 부담을 직접 지고 싶은지에 달려 있다.
- 탄력성은 확장성과 다르고, 오토스케일링이 절약하는 비용은 워크로드의 피크 대 평균 격차에 비례한다. 반응 지연을 흡수하려면 목표 사용률을 100% 아래로 두어야 하고, 이 여유가 남은 유휴 비용이다.
- 관리형 서비스는 벤더와 고객의 책임을 나누지만 없애지 않는다. 데이터 분류와 접근 제어 설정은 어떤 서비스 모델에서도 항상 고객 책임으로 남는다.
- 서버리스는 콜드 스타트(환경 준비·런타임 초기화·모듈 최상위 코드)와 웜 호출(핸들러만 반복)을 구분해야 하고, 상태 비저장 제약은 이 실행 모델의 결과다. 과금은 호출 수와 GB-초로 구성되며, 예약형과의 손익분기점은 워크로드에 따라 실제로 존재한다 — 벤더 규칙이 아니라 자신의 숫자로 계산해야 한다.
- VM·컨테이너·서버리스 배치는 12.1의 격리 축, 이 문서의 책임 축, 워크로드의 탄력성 축을 함께 봐야 하는 결정이다.
확인 문제
1. 한 팀이 "관리형 데이터베이스로 옮겼으니 가용성과 복구는 이제 벤더 책임"이라고 발표했다. 이 주장의 문제를 책임 공유 모델로 지적하라.
정답과 해설
관리형 서비스는 물리 인프라·소프트웨어 설치·기본 이중화 메커니즘을 벤더가 운영해 준다는 뜻이지, 고객이 그 메커니즘을 올바르게 설정했는지까지 보장한다는 뜻이 아니다. 복제본 수, 백업 주기와 보존 기간, 접근 권한, 장애 조치 정책 같은 설정은 여전히 고객 책임이며 관리형 서비스가 이를 대신 판단해 주지 않는다. 실제 가용성은 벤더가 관리하는 층(설치된 소프트웨어의 무결성, 물리 인프라)과 고객이 설정한 층(복제·백업 정책)이 함께 결정하므로, "이제 벤더 책임"이라는 발표는 후자를 점검하지 않은 채 책임을 넘겼다고 가정한 것이다. 구체적인 복제 보장 수준은 10.2와 챕터 11의 관리형 데이터베이스 논의로 검증해야 한다.
2. 하루 24시간 거의 균일한 부하를 받는 내부 배치 서비스에 오토스케일링을 도입해 "비용을 절감하겠다"는 제안이 나왔다. 이 제안의 전제를 탄력성의 비용 모델로 평가하라.
정답과 해설
오토스케일링이 절약하는 비용은 피크 대비 평균 사용률의 격차에서 나온다. 부하가 거의 균일하면 피크와 평균이 같아 애초에 절약할 유휴 용량이 없다. 오히려 오토스케일러의 반응 지연(새 인스턴스 프로비저닝 시간)이라는 위험만 추가되고, 목표 사용률을 100% 아래로 두어야 하는 만큼(반응 지연 흡수용 여유) 실질적인 절감은 기대와 다를 수 있다. 이 워크로드에 필요한 것은 오토스케일링이 아니라 고정 용량의 정확한 사이징이며, 절감 여지를 찾으려면 워크로드 자체가 스파이크형인지부터 다시 확인해야 한다.
3. 본문의 손익분기점 스크립트를 실행 시간이 짧고 호출당 메모리도 적은 API 응답형 워크로드(예: avgDurationMs=50, memoryMb=128)에 대해 다시 계산한다면, 손익분기 호출 수는 본문 예시(avgDurationMs=200, memoryMb=512)보다 커질까 작아질까? GB-초 계산식으로 근거를 제시하라.
정답과 해설
GB-초는 (memoryMb/1024) × (avgDurationMs/1000) × invocations로 계산되므로, 메모리와 실행 시간이 모두 줄면 같은 호출 수에서 발생하는 GB-초가 크게 줄어든다(이 예시는 대략 (128/512)×(50/200) = 1/16배). 같은 호출 수에서 서버리스 비용이 훨씬 낮아지므로, 예약 비용과 만나는 손익분기 호출 수는 본문 예시보다 커진다 — 짧고 가벼운 함수는 훨씬 더 많은 호출까지 서버리스가 유리한 상태를 유지한다. 실행 시간과 메모리가 과금의 두 축이라는 구조를 알면, 함수를 가볍게 만드는 최적화가 성능뿐 아니라 손익분기점 자체를 이동시킨다는 것을 알 수 있다.
4. FaaS로 배포한 함수가 매 호출마다 핸들러 안에서 데이터베이스 커넥션 풀을 새로 생성하도록 작성되어 있다. 이를 모듈 최상위(핸들러 밖)로 옮기면 무엇이 달라지는가?
정답과 해설
핸들러 안에 있으면 콜드 호출이든 웜 호출이든 매번 커넥션 풀 생성 비용(초기화 시간, 대상 DB에 대한 새 연결 수립)을 반복해서 낸다. 모듈 최상위로 옮기면 이 초기화는 실행 환경이 새로 만들어질 때(콜드 스타트) 한 번만 일어나고, 이후 같은 환경으로 들어오는 웜 호출들은 이미 만들어진 풀을 재사용한다. 콜드 스타트 자체의 비용(그 1회의 초기화 시간)은 사라지지 않지만, 전체 호출 중 웜 호출의 비율이 높을수록 평균 지연이 크게 줄어든다. 다만 이 이득은 실행 환경이 재사용된다는 벤더의 스케줄링에 의존하므로, 그 재사용이 얼마나 자주 일어나는지(웜 풀 크기, 유휴 타임아웃)를 실측해야 실제 개선폭을 확인할 수 있다.
참고 자료
- Peter Mell, Timothy Grance, "The NIST Definition of Cloud Computing," NIST Special Publication 800-145 (2011) — 클라우드의 다섯 가지 필수 특성과 IaaS/PaaS/SaaS 세 서비스 모델의 표준 정의.
- Michael Armbrust 외, "Above the Clouds: A Berkeley View of Cloud Computing" (UC Berkeley EECS Technical Report, 2009) — 탄력성이 비용을 낮추는 조건과 자본 지출·운영 지출 전환의 경제적 논거를 다룬 초기 보고서.
- Amazon Web Services, Shared Responsibility Model — 관리형 서비스에서 벤더와 고객의 책임 경계를 구조적으로 설명하는 문서(가격이 아니라 구조를 확인하는 용도로 인용).
- Node.js Documentation, TypeScript — 본문 스크립트가 별도 빌드 없이 실행되는 Node.js의 타입 스트리핑 지원 문서.