이전 글에서 우리는 서버리스의 두 가지 프로세스 모델(실행 완료와 장기 실행 프로세스)에 대해 논의했습니다. 이 두 모델의 주요 차이점은 함수 인스턴스가 실행 후 즉시 종료되는지 여부에 있습니다. 또한 데이터 오케스트레이션과 서비스 오케스트레이션의 두 가지 시나리오를 살펴보았습니다.
이러한 시나리오를 장기 실행 프로세스를 사용하여 구현할 수 있는지 궁금할 수 있습니다. 대답은 '가능하다’이지만, 실행 완료가 가장 순수한 형태의 서버리스라는 점을 알아야 합니다. 그렇다면 그 이면의 논리는 무엇일까요?
이를 완전히 이해하려면 복잡한 인터넷 애플리케이션 아키텍처 진화에서 중요한 개념인 스케일링 을 소개해야 합니다. 이것이 이 글의 초점입니다.
로컬에서 개발한 웹 애플리케이션의 index.html 홈페이지에 200명의 사용자가 동시에 접속한다고 상상해 보세요. 로컬 웹 서버 인스턴스는 어떻게 될까요?
PC의 상태를 설명해 보겠습니다. 먼저 클라이언트와 PC 사이에 200개의 TCP/IP 연결이 설정되며, PC는 이를 간신히 처리할 수 있습니다. 그런 다음 200명의 클라이언트가 모두 동시에 HTTP ‘GET/’ 요청을 시작합니다. 웹 서버의 메인 프로세스는 'CPU 코어 수 - 1’개의 자식 프로세스를 동시에 생성하여 이러한 요청을 처리합니다. CPU 코어 수에서 1을 빼는 이유는 메인 프로세스를 위해 하나를 남겨두기 위해서입니다.
예를 들어, 4코어 CPU는 3개의 자식 프로세스를 생성하여 3개의 클라이언트 요청을 동시에 처리하고 나머지 요청은 대기열에 쌓입니다. 자식 프로세스는 ‘GET/’ 요청 처리를 시작하고, 라우팅 규칙을 매칭하며, 해당 제어 함수에 진입하여 클라이언트에 index.html을 반환합니다. 자식 프로세스가 index.html 파일을 전송하면 메인 프로세스는 이를 재활용하고 새로운 자식 프로세스를 생성하여 모든 요청이 처리될 때까지 다음 요청을 처리합니다.
이를 이해하면 다음 질문은 간단해집니다. 클라이언트 대기열의 처리 속도를 어떻게 개선할 수 있을까요?
수직적 스케일링 vs. 수평적 스케일링
명확한 해결책은 CPU 코어 수를 늘리는 것입니다. 단일 머신의 구성을 업그레이드하여(예: 4코어에서 8코어로) 7개의 동시 자식 프로세스를 얻을 수 있습니다.
CPU 코어를 직접 늘리는 것 외에도 더 많은 머신(각각 4코어)을 추가할 수 있습니다. 두 머신에 각각 500명의 클라이언트를 분산하면 동시 자식 프로세스 수를 6개로 늘릴 수 있습니다.
단일 머신 성능을 높이거나 낮추는 것을 **수직적 스케일링 ** 이라고 하며, 성능이 증가함에 따라 비용 곡선이 가파르게 상승하는 경우가 많습니다. 따라서 이 방식을 채택할 때는 신중한 고려가 필요합니다. 반면, 머신 수를 늘리거나 줄이는 것은 수평적 스케일링 으로, 비용 효율성이 더 높으며 기본 스케일링 방식입니다.
이제 약간의 복잡성을 추가해 보겠습니다. index.html은 단일 파일이지만 데이터는 어떨까요? 수직적 또는 수평적 스케일링을 할 때마다 머신을 재시작해야 합니다. 할 일 목록 예제에서는 데이터가 메모리에 저장되며 재시작할 때마다 초기화됩니다. 그렇다면 스케일링 중에 데이터를 어떻게 보존할 수 있을까요?
상태 유지 vs. 무상태
네트워크 토폴로지의 노드는 상태 저장 여부에 따라 상태 유지(stateful) 또는 ** 무상태(stateless)**로 분류할 수 있습니다. 상태 유지 노드는 상태를 보유하므로 데이터를 저장합니다. 따라서 추가적인 주의가 필요하며 안정성과 빈번한 변경에 대한 내성이 요구됩니다. 예를 들어 데이터베이스는 일반적으로 마스터-슬레이브 구조를 사용하여 마스터 노드에 문제가 발생해도 즉시 슬레이브 노드로 전환하여 지속적인 서비스 가용성을 보장합니다.
반면, 무상태 노드는 상태를 저장하지 않거나 일시적으로 신뢰할 수 없는 데이터만 보관합니다. 상태가 없기 때문에 무상태 노드는 높은 동시성을 처리하기 위해 수평적으로 스케일링할 수 있고, 트래픽이 없을 때는 0으로 스케일다운할 수 있습니다(익숙하게 들리나요?). 그러나 상태 유지 노드는 이렇게 할 수 없습니다. 피크 시간과 비피크 시간 사이에 트래픽 변동이 큰 시나리오에서는 상태 유지 노드가 피크 트래픽을 처리하도록 설계하면서도 트래픽이 적은 시간에도 운영 비용을 유지해야 합니다.
데이터베이스는 사용자의 할 일 작업을 영구적으로 저장하는 전형적인 상태 유지 노드입니다. 마찬가지로 로드 밸런서도 상태 유지입니다. 우리의 사고 실험에서 클라이언트 대기열을 유지하는 메인 프로세스와 유사합니다. 웹 애플리케이션이 처리한 결과를 클라이언트에 반환하려면 클라이언트 연결을 저장해야 합니다.
프로세스 모델로 돌아가서, 실행 완료는 본질적으로 무상태입니다. 실행 후 종료되므로 단독으로 영구 데이터 저장에 사용할 수 없습니다. 장기 실행 프로세스는 메인 프로세스가 종료되지 않아 일부 값을 저장할 수 있으므로 자연스럽게 상태 유지됩니다.
그러나 서버리스에서는 장기 실행 프로세스의 메인 프로세스에 값을 저장하더라도 클라우드 제공자가 이를 회수할 수 있습니다. 예약 인스턴스를 사용하더라도 스케일아웃된 노드의 메모리 데이터는 여전히 격리됩니다.
따라서 장기 실행 프로세스를 무상태로 만들려면 메인 프로세스에 값을 저장하지 않거나 임시 변수만 저장해야 합니다. 영구 데이터는 데이터베이스와 같은 전용 상태 유지 노드로 옮겨야 합니다.
데이터 저장소를 메인 프로세스 노드와 분리하고 메인 프로세스가 데이터를 보유하지 않도록 하면 애플리케이션이 무상태가 됩니다. 데이터는 별도의 상태 유지 데이터베이스 노드에 저장합니다. 이 예제는 이전 글에서 논의한 장기 실행 서버리스 시나리오로 변환됩니다. 메인 프로세스 시작 중에 데이터베이스에 연결하고 자식 프로세스를 통해 데이터에 접근합니다. 그러나 이 방법에는 큰 단점이 있습니다: 콜드 스타트 시간이 직접적으로 증가합니다. 더 나은 해결책이 있을까요?
데이터 지속성에 대한 대체 접근 방식을 생각해 봅시다. 왜 우리가 직접 데이터베이스에 연결해야 할까요? 데이터에 대한 CRUD(생성, 읽기, 갱신, 삭제) 작업은 기본적으로 자식 프로세스가 메인 프로세스가 설정한 TCP 연결을 재사용하고, 데이터베이스 명령문을 전송하며, 데이터를 검색하는 것입니다. 만약 HTTP 요청(POST, DELETE, PUT, GET)을 사용하여 데이터베이스에 명령을 보낼 수 있다면 어떨까요? 그러면 이전 수업의 데이터 및 서비스 오케스트레이션 개념을 활용할 수 있지 않을까요?
BaaS란 무엇인가?
실제로 이 모든 준비는 오늘의 주인공인 BaaS화(BaaSification) 로 이어집니다. 데이터 인터페이스 작업인 POST, DELETE, PUT, GET은 RESTful API의 의미론적 HTTP 메서드에 해당합니다. MySQL을 예로 들면, POST는 CREATE 명령, DELETE는 DELETE, PUT은 UPDATE, GET은 SELECT에 매핑됩니다. 이러한 의미론적 일대일 대응을 통해 MySQL 작업을 RESTful API 작업으로 자연스럽게 변환할 수 있습니다.
전통적인 데이터베이스 접근 방식은 TCP 연결 재사용과 낮은 통신 오버헤드 덕분에 동일한 작업에 대해 HTTP보다 빠릅니다. 서버리스는 데이터베이스에 직접 연결할 수 있지만, VPC 분할이 있는 클라우드 환경에서는 IP 주소를 사용하여 전통적인 데이터베이스에 연결하는 것이 종종 어렵습니다. 따라서 서버리스 데이터베이스 연결의 경우 일반적으로 클라우드 제공업체가 제공하는 BaaS 서비스에 의존하지만, 많은 BaaS 서비스가 아직 성숙하지 않았습니다.
한 걸음 더 나아가, 서버리스가 상태 유지 노드에 적합하지 않다면 모든 상태 유지 작업을 데이터 인터페이스로 외부화하는 것은 어떨까요? 그러면 서버리스 함수는 이전 수업에서 논의한 데이터 오케스트레이션 방식을 활용하고 자유롭게 스케일링할 수 있습니다.
요약
실행 완료 모델이 장기 실행 프로세스 모델보다 더 순수하다고 간주되는 이유는 후자가 오해를 불러일으킬 수 있기 때문입니다. 마치 PaaS처럼 취급하여 영구 데이터 저장을 위한 상태 유지 노드로 사용하고 싶은 유혹이 생깁니다. 그러나 서버리스에서는 장기 실행 프로세스라도 클라우드 제공자가 함수 인스턴스를 회수할 수 있습니다.
메모리에 데이터를 저장하면 재시작할 때마다 초기화되는 예제에서처럼, 데이터 오케스트레이션 사고 방식을 채택하고 백엔드 데이터베이스 작업을 데이터 인터페이스로 변환하면 서버리스의 데이터 저장을 백엔드 애플리케이션에 위임하고 이전 수업에서 설명한 데이터 오케스트레이션을 사용하여 상호작용할 수 있습니다. 그러나 백엔드 애플리케이션을 위한 데이터 인터페이스를 만드는 것을 넘어서, BaaS화를 수용하여 백엔드 엔지니어가 개발 중 서버 측 운영 문제에서 해방되도록 해야 합니다.
스케일링의 경우 수직적 스케일링과 수평적 스케일링 중에서 선택할 수 있습니다. 수직적 스케일링은 단일 머신 성능 향상에 초점을 맞추지만 비용 증가가 가파르므로 신중하게 선택해야 합니다. 수평적 스케일링은 머신 수를 늘리는 방식으로 비용 곡선이 완만하여 기본 스케일링 방식입니다.
상태 유지 노드는 데이터를 저장하고, 무상태 노드는 데이터를 보유하지 않고 처리합니다. 오직 무상태 노드만이 자유롭게 스케일링될 수 있습니다. 중요한 데이터를 저장하는 상태 유지 노드는 신중하게 다루어야 합니다. 네트워크 토폴로지 노드가 자유롭게 스케일링되길 원한다면, 데이터 작업을 전용 상태 유지 노드로 외부화해야 합니다.
서버리스 함수가 상태 유지 노드에 접근할 때는 데이터베이스 명령어에만 의존하기보다는 이러한 노드가 데이터 인터페이스를 제공하는 것이 바람직합니다. 데이터베이스 연결은 서버리스 함수에 추가 오버헤드를 도입하기 때문입니다. 또한 백엔드 엔지니어의 개발을 단순화하기 위해 상태 유지 노드의 BaaS화를 추구해야 합니다. 이후 글에서 BaaS화에 대해 더 깊이 다룰 예정입니다.
Novita AI는 AI 야망을 실현하는 올인원 클라우드 플랫폼입니다. 통합 API, 서버리스, GPU 인스턴스 — 비용 효율적인 도구를 제공합니다. 인프라를 제거하고, 무료로 시작하며, AI 비전을 현실로 만드세요.
추천 자료
