팔란티어 아키텍처의 근간에는 '대체가 아닌 통합'이라는 철학이 있다. 이는 기업이 이미 막대한 투자를 한 데이터 레이크, ERP, CRM과 같은 기존 IT 환경을 교체하는 대신, 이들을 하나로 묶고 그 가치를 증대시키는 플랫폼 역할을 하는 것이다.
이러한 철학은 아키텍처의 명확한 관심사 분리로 이어진다. 배포, 오케스트레이션, 데이터 처리와 같은 하위 계층은 의도적으로 쿠버네티스(Kubernetes), 스파크(Spark), 플링크(Flink) 등 보편적인 오픈소스 표준 위에 구축된다. 이를 통해 고객의 기존 기술 스택 및 엔지니어링 역량과 마찰 없이 통합된다. 반면, 온톨로지, AI 플랫폼(AIP)과 같은 상위 계층에는 팔란티어의 독자적인 지적 재산이 집중된다. 이 구조는 고객이 새로운 데이터베이스나 컴퓨팅 엔진이 아닌, 기존 자산과 상호 작용하는 새로운 패러다임을 구매하게 만들어 비지니스 가치를 제안한다.
PDF, 문서, 이미지와 같은 비정형 데이터는 먼저 '미디어 셋(media sets)'이라는 파일 모음으로 수집된다. 데이터를 파싱하는 과정은 블랙박스가 아니다. 개발자는 파이썬(Python)이나 자바(Java) 변환과 저수준 파일 시스템 API를 사용하여 직접 파이프라인을 구축한다. 이는 결정론적이고, 테스트 가능하며, 버전 관리가 가능한 파이프라인을 통해 신뢰성과 거버넌스를 확보하는 엔지니어링 중심의 접근 방식이다.
더 나아가 AIP는 AI 기반 파싱 기능을 제공한다. 이는 사전 훈련되거나 맞춤화된 AI 모델(예: NLP 모델)을 파이프라인 내에 통합하여 개체명 인식, 요약과 같은 정교한 작업을 수행하는 방식이다. 이 구조는 엔지니어가 견고한 데이터 파이프라인을 구축하고, AI 엔지니어가 그 안에 두뇌 역할을 하는 모델을 배포하는 효율적인 이중 계층 시스템을 만든다.
이러한 데이터 변환 및 통합 로직은 독점 엔진에 종속되지 않는다. 모든 데이터는 아파치 파케이(Apache Parquet), 아브로(Avro)와 같은 표준 형식으로 저장되며, 대규모 배치 처리를 위한 아파치 스파크, 실시간 스트림 처리를 위한 아파치 플링크와 같은 오픈소스 런타임을 사용한다.
- 객체(Object)는 클래스(Class)에 해당한다. 온톨로지의 '항공기' 객체 유형은 OOP의 Aircraft 클래스와 같다.
- 속성(Property)은 속성(Attribute)에 해당한다. '항공기' 객체의 '꼬리 번호' 속성은 Aircraft 클래스의 tailNumber 속성과 같다.
- 연결(Link)은 객체 간의 관계(Association)에 해당한다. '조종사'가 '항공기'에 탑승한다는 연결은 Pilot 객체와 Aircraft 객체 간의 관계를 정의한다.
클라이언트 앱 프로젝트 생성 UI 메뉴 |
- 객체 저장소: 객체 데이터 자체는 카산드라(Cassandra)와 같은 수평적으로 확장 가능한 Key-Value 저장소에 저장될 수 있다. 각 객체는 고유 ID를 키(Key)로, 모든 속성을 담은 JSON 문서를 값(Value)으로 저장하는 방식이다.
- 검색 및 그래프 인덱스: 빠른 검색, 집계, 그래프 탐색 기능을 위해서는 엘라스틱서치(Elasticsearch)나 루씬(Lucene) 기반의 검색 인덱스가 필수적이다. Key-Value 저장소에 저장된 객체 데이터는 검색 엔진으로 인덱싱되어 풍부한 쿼리 기능을 제공한다.
우리의 목표는 '지연' 상태인 모든 화물을 식별하고, 운영자가 상태를 '조사 중'으로 변경할 수 있는 간단한 스크립트를 작성하는 것이다.
Foundry 플랫폼은 개발자가 온톨로지와 직접 상호작용할 수 있는 언어별 SDK를 제공한다.37 Python OSDK는 객체, 속성, 액션을 마치 일반적인 Python 클래스와 메서드처럼 다룰 수 있게 해준다.
import os
from <YOUR_PACKAGE_NAME> import FoundryClient
from <YOUR_PACKAGE_NAME>.core.api import UserTokenAuth
# 1. Foundry 클라이언트 초기화
try:
auth = UserTokenAuth(
hostname=os.environ,
token=os.environ
)
client = FoundryClient(auth=auth, hostname=os.environ)
except KeyError:
print("오류: FOUNDRY_HOSTNAME 및 FOUNDRY_TOKEN 환경 변수를 설정.")
exit()
# 2. 온톨로지 객체 쿼리: 지연된 화물 검색
# OSDK는 온톨로지의 각 객체 유형(예: Shipment)에 대한 접근자를 제공
# 'where' 절을 사용하여 특정 속성 값(status == "Delayed")을 기준으로 객체를 필터링
print("\n'지연' 상태인 화물을 검색...")
delayed_shipments = client.ontology.objects.Shipment.where(status="Delayed").all()
if not delayed_shipments:
print("지연된 화물이 없음.")
else:
print(f"총 {len(delayed_shipments)}개의 지연된 화물을 찾음")
# 3. 객체 속성 접근 및 연결된 객체 탐색
# 검색된 각 화물 객체에 대해 속성(shipmentId, destination 등)에 직접 접근
# 온톨로지 연결을 통해 'assignedVehicle'과 같은 연결된 객체를 로드
for shipment in delayed_shipments:
print(f"\n- 화물 ID: {shipment.shipmentId}")
print(f" 목적지: {shipment.destination}")
print(f" 현재 상태: {shipment.status}")
# 연결된 운송 차량 정보 로드
try:
vehicle = shipment.assignedVehicle.get()
print(f" 할당된 차량 ID: {vehicle.vehicleId}")
print(f" 차량 현재 위치: {vehicle.currentLocation}")
except Exception as e:
print(f" 할당된 차량 정보를 가져오는 데 실패: {e}")
# 4. 온톨로지 액션(Action) 실행
# 온톨로지의 동역학적 계층은 'update_shipment_status'와 같은 액션을 정의
target_shipment = delayed_shipments
print(f"\n화물 {target_shipment.shipmentId}의 상태를 '조사 중'으로 업데이트...")
try:
# 'update_shipment_status'는 온톨로지에 미리 정의된 액션의 API 이름이라고 가정
target_shipment.update_shipment_status(new_status="Investigation")
print("상태 업데이트 액션이 성공적으로 실행.")
# 변경 사항 확인
updated_shipment = client.ontology.objects.Shipment.get(target_shipment.shipmentId)
print(f"화물 {updated_shipment.shipmentId}의 새로운 상태: {updated_shipment.status}")
except Exception as e:
print(f"액션 실행 중 오류가 발생: {e}")
위 코드는 Palantir 플랫폼의 핵심 철학을 보여준다. 개발자는 기본 데이터베이스의 스키마나 조인(join) 로직에 대해 알 필요가 없다. 대신, 비즈니스 용어(Shipment, Vehicle, status)로 정의된 잘 구조화된 온톨로지 API와 상호작용한다.
client.ontology.objects.Shipment.where(...)와 같은 코드는 SQL 쿼리보다 훨씬 직관적이며, 애플리케이션 로직과 기본 데이터 저장소를 분리(decouple)시킨다. 액션(Action)을 호출하는 것은 단순한 UPDATE 문이 아니라, 플랫폼의 거버넌스와 보안 모델을 통과하는 감사 가능한 트랜잭션을 실행하는 것이다. 이러한 접근 방식은 개발 속도를 크게 향상시키고 유지보수 비용을 절감하며, 복잡한 기업 환경에서도 일관성 있고 안전한 데이터 조작을 보장한다.
- Platform overview • Architecture • Palantir
- Ontology SDK • Overview • Palantir
- palantir/palantir-python-sdk: Palantir Python SDK
- Getting started • API Reference • Palantir
- Overview • Ontology • Palantir
- Get Ontology Full Metadata • API Reference • Palantir
- Object and link types • Object types • Overview • Palantir
- Search Objects • API Reference • Palantir
- Execute Sql Query • API Reference • Palantir
- Why create an Ontology? • Palantir
- Ontologies • Overview • Palantir
- Ontologies • Migrating between ontologies • Palantir
- Getting started • Cross-Organization collaboration • Palantir
- Run Palantir Foundry and Artificial Intelligence Platform on OCI
이 글은 카산드라의 쿼리 언어(CQL)와 전통적인 SQL의 관계를 설명한다. 이어서, 개발자들이 특정 시나리오에서 관계형 데이터베이스 대신 카산드라를 선택하는 핵심적인 아키텍처적 장점인 수평적 확장성, 고가용성, 쓰기 성능을 설명한다.
- 카산드라 깃허브: apache/cassandra: Apache Cassandra
- 엘라스틱서치 깃허브: elastic/elasticsearch: Free and Open Source, Distributed, RESTful Search Engine
카산드라가 "그냥 SQL이 지원되는 데이터베이스"라는 인식은 반은 맞고 반은 틀린 이야기이다.
카산드라는 CQL (Cassandra Query Language)을 사용한다. SELECT, INSERT, UPDATE, CREATE TABLE 등 그 문법이 SQL과 매우 유사하여, 기존 관계형 데이터베이스(RDBMS)에 익숙한 개발자가 쉽게 배울 수 있다는 장점이 있다. 이는 의도적으로 설계된 부분이다.
겉모습은 비슷하지만, 내부 동작 원리와 데이터 모델링 철학은 완전히 다르다. 카산드라는 관계형 데이터베이스가 아니다. 가장 큰 차이점은 다음과 같다.
- 카산드라는 여러 테이블을 연결하는 JOIN 연산을 지원하지 않는다. 이는 분산 환경에서 JOIN이 유발하는 막대한 성능 저하를 원천적으로 차단하기 위함이다.
- 외래 키(Foreign Key)와 같은 관계 무결성을 데이터베이스 차원에서 보장하지 않는다.
- RDBMS가 데이터의 관계를 정규화하여 모델링하는 반면, 카산드라는 애플리케이션이 사용할 쿼리(조회 방식)를 먼저 설계하고 그에 맞춰 테이블을 비정규화(denormalization)하여 구성한다.
3. 개발자가 카산드라를 선택하는 이유
개발자들은 RDBMS가 해결하기 어려운 특정 문제들을 풀기 위해 카산드라를 선택한다.
가. 분산 아키텍처와 수평적 확장성 (Horizontal Scalability)
이것이 카산드라를 사용하는 가장 큰 이유이다. 카산드라는 여러 서버(노드)를 묶어 하나의 거대한 데이터베이스처럼 사용하는 'Shared-Nothing' 분산 아키텍처를 기반으로 한다.
- 수평적 확장: 데이터가 늘어나거나 트래픽이 증가할 때, 고가의 단일 서버 성능을 높이는 '수직적 확장'이 아니라, 저렴한 일반 서버를 클러스터에 계속 추가하는 '수평적 확장'이 가능하다. 이론적으로 서버를 추가하는 만큼 성능과 용량이 선형적으로 증가한다.
- 대규모 데이터 처리: 수십억, 수백억 개의 행을 가진 페타바이트(PB)급 데이터를 처리하는 데 최적화되어 있다.
카산드라 클러스터에는 마스터(Master) 노드처럼 특별한 역할을 하는 노드가 없어 단일 장애점(SPOF, Single Point of Failure)이 존재하지 않는다.
- 데이터 복제: 데이터는 클러스터 내 여러 노드에 자동으로 복제되어 저장된다.
- 무중단 서비스: 특정 노드에 장애가 발생하더라도, 복제된 데이터를 가진 다른 노드가 즉시 요청을 처리하여 서비스 중단 없이 운영이 가능하다. 이는 24시간 365일 무중단이 필수적인 서비스에 결정적인 장점이다.
카산드라는 LSM-Tree (Log-Structured Merge-Tree) 라는 자료 구조를 사용하여 데이터를 저장한다. 이는 디스크에 데이터를 순차적으로 추가(Append-Only)하는 방식으로, 기존 RDBMS의 B-Tree 방식보다 쓰기 작업에 훨씬 유리하다.
- 활용 사례: 사물 인터넷(IoT) 센서 데이터, 서비스 로그, 메시징 데이터처럼 끊임없이 대량으로 쏟아지는 데이터를 지연 없이 저장해야 하는 시스템에 최적이다.
앞서 구현한 온톨로지 모델은 카산드라의 장점을 활용한 전형적인 데이터 아키텍처로 적용 가능하다.
가. 아키텍처 설계 복기
이 시스템은 두 가지 데이터베이스를 조합하여 각자의 장점을 극대화한다.
- 객체 저장소 (카산드라): 모든 객체의 원본 데이터를 저장하는 'Source of Truth' 역할을 한다.
- 검색 인덱스 (엘라스틱서치): 카산드라에 저장된 데이터를 복사하여, 복잡한 검색 및 집계 기능을 제공한다.
온톨로지 모델의 객체 저장소로 카산드라를 선택한 이유는 다음과 같다.
- Key-Value 최적화: 온톨로지 객체는 고유 ID를 Key로, 속성 데이터를 Value로 갖는다. 이는 object_id를 파티션 키로 사용하는 카산드라의 데이터 모델과 완벽하게 부합하며, ID 기반 조회 시 최고의 성능을 보장한다.
- 확장성: 수억, 수십억 개의 온톨로지 객체가 생성되더라도, 단순히 카산드라 클러스터에 노드를 추가하는 것만으로 시스템을 수평적으로 확장할 수 있다.
- 가용성: 온톨로지 데이터는 기업의 핵심 자산이다. 카산드라의 무중단 특성은 일부 노드에 장애가 발생해도 객체 데이터의 유실 없이 안정적으로 서비스를 유지할 수 있도록 보장한다.
다음은 카산드라와 엘라스틱서치를 사용하여 온톨로지 모델을 구현한 파이썬 코드이다. 이 코드는 위에서 설명한 아키텍처 원리를 추측해 구현해 본 것이다.
import uuid
import json
import time
from cassandra.cluster import Cluster
from cassandra.query import SimpleStatement
from elasticsearch import Elasticsearch
# 1. 데이터베이스 연결 설정
def setup_cassandra(contact_points=['127.0.0.1'], port=9042):
cluster = Cluster(contact_points, port=port)
session = cluster.connect()
# 키스페이스 생성
session.execute("""
CREATE KEYSPACE IF NOT EXISTS ontology
WITH replication = { 'class': 'SimpleStrategy', 'replication_factor': '1' }
""")
session.set_keyspace('ontology')
# 객체 저장을 위한 테이블 생성 (JSON 문자열로 속성 저장)
session.execute("""
CREATE TABLE IF NOT EXISTS objects (
object_id TEXT PRIMARY KEY,
properties TEXT
)
""")
print("카산드라 설정 완료: 'ontology' 키스페이스 및 'objects' 테이블 준비됨.")
return cluster, session
def setup_elasticsearch(hosts=[{'host': 'localhost', 'port': 9200, 'scheme': 'http'}]):
es_client = Elasticsearch(hosts=hosts)
if not es_client.ping():
raise ValueError("엘라스틱서치 연결에 실패.")
print("엘라스틱서치 연결 성공.")
return es_client
# 2. 온톨로지 클래스 구현
class Ontology:
def __init__(self, cassandra_session, es_client, es_index_name='ontology_index'):
self.session = cassandra_session
self.es = es_client
self.es_index = es_index_name
if not self.es.indices.exists(index=self.es_index):
self.es.indices.create(index=self.es_index)
print(f"엘라스틱서치 인덱스 '{self.es_index}' 생성됨.")
def add_object(self, object_type, properties):
object_id = f"{object_type}-{uuid.uuid4().hex[:8]}"
full_properties = {"object_type": object_type, properties}
query = SimpleStatement("INSERT INTO objects (object_id, properties) VALUES (%s, %s)")
self.session.execute(query, (object_id, json.dumps(full_properties)))
self.es.index(index=self.es_index, id=object_id, document=full_properties)
print(f"객체 추가됨: {object_id} (카산드라 저장, ES 인덱싱 완료)")
return object_id
def get_object(self, object_id):
query = "SELECT properties FROM objects WHERE object_id = %s"
row = self.session.execute(query, (object_id,)).one()
if row:
return json.loads(row.properties)
return None
def search(self, property_key, property_value):
query = { "match": { f"{property_key}.keyword" if isinstance(property_value, str) else property_key: property_value } }
response = self.es.search(index=self.es_index, query=query)
hits = response['hits']['hits']
print(f"\nES 검색 실행 ('{property_key}:{property_value}')... {len(hits)}개 결과 찾음.")
return [hit['_source'] for hit in hits]
def add_link(self, source_object_id, link_type, target_object_id):
source_object = self.get_object(source_object_id)
if not source_object: return
if "_links" not in source_object: source_object["_links"] = {}
if link_type not in source_object["_links"]: source_object["_links"][link_type] = []
source_object["_links"][link_type].append(target_object_id)
query = SimpleStatement("INSERT INTO objects (object_id, properties) VALUES (%s, %s)")
self.session.execute(query, (source_object_id, json.dumps(source_object)))
self.es.index(index=self.es_index, id=source_object_id, document=source_object)
print(f"링크 추가 및 업데이트됨: {source_object_id} --({link_type})--> {target_object_id}")
5. 결론
카산드라는 SQL과 유사한 CQL을 제공하여 개발자 친화적이지만, 그 본질은 대규모 분산 환경을 위해 태어난 NoSQL 데이터베이스이다. JOIN이나 참조 무결성을 포기하는 대신 수평적 확장성, 고가용성, 뛰어난 쓰기 성능이라는 명확한 장점을 얻었다.
참고로, 오픈소스인 칸산드라 개발자는 아비나쉬 라크쉬만 (Avinash Lakshman), 프라샨트 말릭 (Prashant Malik)이다. 페이스북의 '받은 편지함 검색(Inbox Search)' 기능의 대규모 데이터 처리 요구사항을 충족시키기 위해 내부적으로 개발되었다. 아마존(Amazon)의 DynamoDB와 구글(Google)의 Bigtable 아키텍처에서 영감을 받아 설계되었다. 2008년 오픈소스로 공개된 후, 아파치 재단(ASF)의 최상위 프로젝트로 승격되어 전 세계적으로 널리 사용되는 분산 NoSQL 데이터베이스로 성장했다.
팔란티어는 단일 개발자가 아닌, 각기 다른 전문성을 가진 공동 창업자들의 협업을 통해 탄생했다.
피터 틸 (Peter Thiel)은 페이팔(PayPal)의 사기 방지 시스템 경험을 바탕으로 초기 비전을 제시한 핵심 창업자이다. 스티븐 코언 (Stephen Cohen)은 팔란티어 플랫폼의 초기 프로토타입을 직접 코딩한 핵심 엔지니어이다. 스탠퍼드 대학교 컴퓨터 과학 석사 출신으로, 그의 객체지향 프로그래밍(OOP) 배경이 온톨로지(Ontology) 아키텍처의 근간이 되었다. 네이선 게팅스 (Nathan Gettings)는 초기 엔지니어링 및 알고리즘 개발에 기여한 컴퓨터 과학자이다.
플랫폼의 핵심인 온톨로지 개념은 창업자 그룹, 특히 스티븐 코언의 깊은 객체지향 프로그래밍(OOP) 이해도에서 비롯되었다. 이는 클래스(Class), 객체(Object), 관계(Link)로 현실을 모델링하는 OOP의 핵심 사상과 정확히 일치한다.
온톨로지 구현 사례에서 보았듯이, 카산드라는 대규모 객체의 원본 데이터를 안정적으로 저장하고, 엘라스틱서치와 같은 검색 엔진과 결합하여 복잡한 조회 요구사항을 충족시키는 'Polyglot Persistence' 아키텍처의 핵심 구성 요소로 사용될 수 있다.
댓글 없음:
댓글 쓰기