확장성
고친 사람 github-actions[bot]
확장성은 일이 늘어날 때 자원을 보태서 그만큼 더 받아내는 능력입니다. 서버를 두 대에서 네 대로 늘렸을 때 받아내는 요청도 두 배 가까이 느는지를 봅니다. 한국어에서는 기능을 새로 덧붙이기 쉬운 정도도 확장성이라고 부릅니다. 이 항목은 앞의 뜻을 다룹니다.
쉽고 빠른 이해
확장성은 늘어난 일을 자원을 더 붙여서 받아낼 수 있는 정도입니다. 손님이 두 배로 늘었을 때 서버도 두 배로 늘려 버틸 수 있으면 확장성이 있는 시스템입니다.
이게 없으면 서버를 더 사도 서비스가 더 버티지 못합니다. 늘어나는 일을 자원으로 따라잡을 길이 막힙니다.
어떻게 도나:
- 일을 여러 조각으로 나눌 수 있게 만듭니다
- 조각을 받아 처리할 자원을 더 붙입니다. 한 대를 키우거나 대수를 늘립니다
- 모든 일이 같이 거치는 한 곳이 남아 있으면 그곳부터 찾아 나눕니다
대가도 있습니다. 여러 대로 나뉘어 도는 시스템은 한 대로 도는 시스템보다 만들고 운영하기가 어렵습니다. 나눠 맡은 서버끼리 서로 맞추는 일이 새로 생깁니다.
그래서 부하가 늘 것이 보일 때 따집니다. 한 대로 버틸 만하면 미룹니다.
상세
식당 주방 둘을 떠올려 봅시다. 손님이 두 배로 늘자 두 주방 모두 요리사를 한 명씩 더 들였습니다. 한 주방은 화구가 넉넉해서 음식이 두 배로 나옵니다. 다른 주방은 화구가 하나뿐이라 요리사가 둘이 되어도 나오는 음식이 그대로입니다.
확장성(scalability)은 들어오는 일이 늘 때 자원을 보태서 그만큼 더 받아낼 수 있는 정도입니다. 웹 서버 한 대가 받아내던 요청을 두 대가 두 배 가까이 받아낸다면, 그 서비스는 서버 대수를 늘리는 방향으로 확장성이 있습니다.
이렇게 시스템에 들어오는 일의 양을 부하라고 부릅니다. 초당 들어오는 요청 수, 동시에 접속한 사용자 수, 쌓인 데이터의 양이 모두 부하입니다.
자원은 그 일을 처리하는 데 쓰는 것입니다. CPU(Central Processing Unit, 중앙 처리 장치) 코어, 메모리, 서버 대수가 자원입니다.
확장성이 없는 시스템은 부하가 늘어도 손쓸 방법이 없습니다. 서버를 더 사도 처리하는 양이 안 늡니다. 응답이 늦어지다가 요청이 실패하기 시작합니다.
성능과는 묻는 것이 다릅니다. 성능은 지금 이 부하를 얼마나 짧은 시간에 처리하느냐를 묻습니다. 확장성은 부하가 늘어날 때 자원을 보태서 그 성능을 지킬 수 있느냐를 묻습니다. 한 대에서 아주 짧게 답하는 서비스라도 일을 두 대로 나눌 수 없으면 확장성은 없습니다.
이 절은 늘린 만큼 늘어나는 이상적인 경우를 먼저 봅니다. 그다음 그것을 막는 두 원인을 작은 계산과 그림으로 보입니다. 이어서 자원을 보태는 두 방법, 확장성을 재는 법, 언제 따지고 언제 미루는지를 봅니다.
늘린 만큼 늘어나는가
처리량은 정해진 시간 동안 끝낸 일의 양입니다. 초당 처리한 요청 수가 흔히 쓰는 처리량입니다. 확장성은 자원을 늘릴 때 이 처리량이 어떻게 따라오는지로 따집니다.
자원을 두 배로 늘렸을 때 처리량도 두 배가 되면 이것을 선형 확장이라고 부릅니다. 자원과 처리량이 곧은 직선을 그리며 함께 늘어난다는 뜻입니다. 확장성을 따질 때 이 직선이 기준선이 됩니다.
대부분의 시스템은 이 직선 아래에 있습니다. 자원을 늘릴수록 한 단위가 보태는 몫이 줄어듭니다. 어떤 시스템은 어느 지점을 넘으면 자원을 늘릴수록 처리량이 오히려 줄어듭니다.
| 자원을 늘려 갈 때 | 처리량 | 까닭 |
|---|---|---|
| 직선을 따라간다 | 늘린 만큼 는다 | 서버끼리 기다릴 일도, 주고받을 말도 없다 |
| 갈수록 덜 는다 | 늘지만 늘린 만큼은 못 는다 | 나눌 수 없는 부분이 남아 있다 |
| 꺾여 내려간다 | 오히려 준다 | 서버끼리 맞추는 비용이 보탠 몫보다 커졌다 |
표의 둘째 줄과 셋째 줄이 확장을 막는 두 원인입니다. 아래 두 소절이 하나씩 봅니다.
나눌 수 없는 부분이 만드는 천장
일에는 여러 자원에 나눠 맡길 수 있는 부분과 그럴 수 없는 부분이 섞여 있습니다. 모든 요청이 거쳐 가는 데이터베이스 한 대나, 한 번에 한 작업만 들어갈 수 있는 잠금 구간이 나눌 수 없는 부분입니다.
나눌 수 없는 부분은 자원을 아무리 늘려도 줄지 않습니다. 그래서 늘릴 수 있는 폭에 천장이 생깁니다. 이 천장을 계산하는 식이 암달의 법칙입니다.
일을 자원 한 개가 할 때 걸리는 시간을 1 이라 합시다. 그중 나눌 수 없는 몫이 s 입니다. 자원 n 개가 나눠 하면 걸리는 시간은 s 에 나머지 1 - s 를 n 으로 나눈 것을 더한 만큼입니다. 1 을 이 시간으로 나누면 몇 배 빨리 끝나는지가 나옵니다.
같은 일이 두 배 빨리 끝나면 같은 시간에 두 배의 일을 끝냅니다. 그래서 이 배수가 곧 처리량이 느는 배수입니다.
아래 코드는 나눌 수 없는 몫이 열에 하나일 때를 셉니다. speedup(n) 은 자원을 n 개로 늘렸을 때 몇 배 빨리 끝나는지를 돌려줍니다.
s = 0.1 # 나눌 수 없는 몫
def speedup(n):
return 1 / (s + (1 - s) / n)
speedup(2) # 1.82
speedup(10) # 5.26
speedup(1000) # 9.91
자원을 열 배로 늘려도 다섯 배 남짓 빨라질 뿐입니다. 천 배로 늘려도 열 배를 못 넘습니다. 나눌 수 없는 열에 하나만큼의 시간은 자원을 몇 개 붙여도 끝까지 남기 때문입니다.
이렇게 전체를 묶어 두는 한 곳을 병목이라고 부릅니다. 앞의 주방에서는 화구 하나가 병목이었습니다. 확장성을 끌어올리려면 자원을 늘리기 전에 병목부터 찾습니다. 병목이 아닌 곳에 자원을 보태면 들인 자원만 늘고 처리량은 그대로입니다.
서버끼리 맞추는 비용
자원을 늘리면 서버끼리 주고받을 말도 늘어납니다. 같은 데이터를 서버 여러 대가 들고 있으면 한 대가 값을 바꿀 때 나머지 서버에도 알려야 합니다. 이렇게 서로 맞추는 일을 조율이라고 부릅니다.
조율 비용은 대수보다 가파르게 커집니다. 모든 서버가 서로 말을 주고받아야 하는 일이면 둘씩 짝지은 쌍마다 연결이 하나씩 필요합니다. 대수가 n 이면 쌍은 n(n-1)/2 개입니다.
flowchart TD
subgraph 두대["두 대 · 쌍 하나"]
A1["서버 1"] --- A2["서버 2"]
end
subgraph 네대["네 대 · 쌍 여섯"]
B1["서버 1"] --- B2["서버 2"]
B1 --- B3["서버 3"]
B1 --- B4["서버 4"]
B2 --- B3
B2 --- B4
B3 --- B4
end
두대 -->|"대수를 두 배로"| 네대
그림에서 대수는 두 배가 됐지만 선은 하나에서 여섯으로 늘었습니다. 서버 한 대가 말을 나눌 상대도 한 대에서 세 대로 늘었습니다. 대수를 늘릴수록 서버 한 대가 조율에 쓰는 시간이 커집니다.
그러다 조율에 드는 시간이 보탠 자원의 몫보다 커지는 지점이 옵니다. 그 뒤로는 자원을 늘릴수록 처리량이 줄어듭니다. 앞 표의 셋째 줄이 이 경우입니다.
그래서 확장성을 노리는 설계는 서버끼리 주고받을 말을 줄이는 쪽으로 갑니다. 한 방법은 요청 사이에 기억할 것을 서버에 안 두는 무상태 서버입니다. 서버가 들고 있는 것이 없으니 다른 서버에 알릴 것도 없습니다.
다른 방법은 데이터를 겹치지 않게 갈라 맡기는 샤딩입니다. 서버마다 맡은 데이터가 겹치지 않으니 한 서버가 값을 바꿔도 다른 서버에 알릴 필요가 없습니다.
자원을 보태는 두 방법
자원을 보태는 길은 둘입니다. 기계 한 대를 더 큰 기계로 바꾸는 것을 수직 확장이라고 부릅니다. 같은 기계를 옆에 더 세우는 것을 수평 확장이라고 부릅니다.
수직 확장은 애플리케이션을 고치지 않아도 됩니다. 대신 그때 살 수 있는 가장 큰 기계에서 끝납니다.
수평 확장은 대수를 붙이는 동안 끝이 열려 있습니다. 대신 일을 여러 대에 나눠 맡길 수 있게 애플리케이션을 먼저 고쳐야 합니다. 들어온 요청을 여러 대에 나눠 주는 일은 로드 밸런서가 맡습니다.
두 방법은 함께 따집니다. 한 대를 키워 버틸 수 있는 동안은 수직 확장이 손이 덜 갑니다. 그 끝이 보이면 수평 확장으로 넘어갑니다.
확장성을 재는 법
확장성은 숫자 하나로 적히지 않습니다. 부하나 자원을 한 단계씩 늘려 가며 처리량과 응답 시간이 어떻게 변하는지를 곡선으로 봅니다. 응답 시간은 요청 하나가 들어가서 답이 나올 때까지 걸린 시간입니다.
자원을 고정하고 부하만 늘리면 그 구성이 어디까지 버티는지가 보입니다. 부하와 자원을 같은 비율로 함께 늘리면 처리량도 늘린 만큼 늘어나는지가 보입니다. 이렇게 일부러 부하를 걸어 보는 시험을 부하 테스트라고 부릅니다.
처리량이 더 안 오르거나 응답 시간이 치솟는 지점이 그 구성의 한계입니다. 그 지점에서 무엇이 먼저 가득 찼는지를 찾으면 병목이 나옵니다.
늘리기와 줄이기
부하는 한 방향으로만 움직이지 않습니다. 낮에는 많고 밤에는 적은 서비스가 흔합니다. 부하가 줄 때 자원을 덜어 내고 늘 때 다시 보태는 능력은 탄력성이라고 따로 부릅니다.
확장성은 늘릴 수 있느냐를 묻습니다. 탄력성은 부하를 따라 얼마나 빨리, 사람 손 없이 늘고 줄 수 있느냐를 묻습니다. 부하에 맞춰 서버 대수를 자동으로 바꾸는 오토스케일링이 탄력성을 얻는 수단입니다.
같은 이름의 다른 뜻
한국어 문서에서 확장성은 다른 뜻으로도 쓰입니다. 기존 코드를 크게 고치지 않고 새 기능을 덧붙일 수 있는 정도입니다. 영어로는 extensibility 라고 부릅니다. 이 사전에서는 기능 확장성으로 따로 둡니다.
두 뜻은 묻는 것이 다릅니다. 이 항목의 확장성은 같은 일이 많아질 때 버티는지를 묻습니다. 기능 확장성은 새로운 일을 끼워 넣기 쉬운지를 묻습니다.
소프트웨어 아키텍처에서는 시스템이 갖춰야 할 성질을 품질 속성이라고 부릅니다. 이 성질들은 목록으로 늘어놓습니다. 두 확장성은 이 목록에서도 다른 칸에 들어갑니다. 문서에서 이 낱말을 만나면 부하 이야기인지 기능 이야기인지부터 가려 읽습니다.
언제 따지고 언제 미루나
부하가 늘 것이 보이는 서비스는 설계할 때부터 확장성을 따집니다. 나중에 나눌 수 없는 부분을 걷어 내려면 데이터를 나누는 방식과 코드를 크게 고쳐야 하기 때문입니다. 앞으로 부하가 얼마나 늘지 헤아려 자원을 미리 마련하는 일을 용량 계획이라고 부릅니다.
부하가 작고 한동안 늘 계획이 없으면 한 대로 도는 구성이 단순합니다. 확장성을 얻으려고 일을 나누면 조율과 운영의 짐이 함께 옵니다. 여러 대로 나뉜 시스템이 서로 맞추는 문제는 분산 시스템에서 본격적으로 다룹니다.
관련 항목
확장성을 얻으려고 자원을 보태는 방법
수직 확장 · 수평 확장 · 오토스케일링 · 탄력성 · 용량 계획
확장성을 얻으려고 일을 나누는 기법
부하 분산 · 로드 밸런서 · 무상태 · 샤딩 · 파티셔닝 · 파티션 · 복제 · 캐싱 · 일관성 해싱
확장성을 가로막는 병목과 법칙
병목 · 암달의 법칙 · 구스타프손의 법칙 · 범용 확장성 법칙 · 락 경합 · 거짓 공유
확장성을 재는 지표와 시험
처리량 · 응답 시간 · 지연 · 꼬리 지연 · 선형 확장 · 부하 테스트
확장성과 나란히 따지는 품질 속성
품질 속성 · 성능 · 가용성 · 바꾸기 쉬움 · 기능 확장성
확장성을 위해 나누면 새로 생기는 조율 문제
분산 시스템 · 일관성 · 합의 · 분산 트랜잭션 · CAP 정리
확장성을 다루는 상위 분야
소프트웨어 아키텍처 · 시스템 설계 · 병렬성 · 동시성
다른 이름: scalability