12.2.2 샌드박스(Sandbox) 실행 환경 구축: 도커(Docker) 기반 격리 전략
NL2SQL 오라클이 정답과 오답을 판별해 낼 때, 만약 그 쿼리가 가동되는 채점장(데이터베이스)이 초당 수천 건의 트랜잭션으로 값이 변동하는 라이브 프로덕션(운영) DB이거나, 여러 개발자의 테스트 코드가 동시에 상태를 훼손할 수 있는 단순한 로컬 공유 DB라면, 그 오라클이 내놓은 채점 결과(Accuracy)의 통계적 신뢰성은 0으로 수렴한다.
대수학적으로 엄밀한 평가를 수행하기 위해서는, 반드시 매 테스트 쿼리가 주입될 때마다 **‘1비트의 어긋남도 없이 완벽하게 동일한 초기 상태(Deterministic Initial State)’**를 보장하고, 모델이 미쳐 날뛰어 악의적인 파괴 쿼리(DROP DATABASE)를 던지더라도 본진(Host)에 타격을 주지 않으며, 검증이 끝나면 흔적도 없이 폐기(Teardown)하여 재생성할 수 있는 고립된 ‘격리 샌드박스(Isolated Sandbox)’ 환경이 절대적으로 요구된다. 이를 가장 우아하고 저비용으로 달성하는 서빙 아키텍처의 산업 표준이 바로 도커(Docker) 컨테이너 기반의 가상화 전략이다.
1. 인메모리(In-Memory) 컨테이너 기반의 초고속 휘발성 런타임
수천 개의 벤치마크 쿼리 세트를 검증하기 위해 매번 수 기가바이트(GB)에 달하는 더미(Dummy) 테이블과 테스트용 데이터를 하드디스크(SSD) 볼륨에 쓰고 지우는(I/O) 행위는 오라클 인프라의 채점 속도를 극단적으로 저하시키는 오버헤드의 주범이다.
이를 타파하기 위한 가장 강력한 인프라 공학적 해법은, 도커의 tmpfs 마운트 플래그를 활용하여 데이터베이스의 메인 볼륨 스토리지를 호스트 컴퓨터의 RAM(메모리) 위로 강제 할당해 버리는 것이다.
# Docker 인메모리 Sandbox 실행 예시
docker run -d --name oracle-sandbox-pg \
--tmpfs /var/lib/postgresql/data:rw,noexec,nosuid,size=2048m \
-e POSTGRES_PASSWORD=oracle \
oracle-test-db-image:latest
이처럼 물리적 디스크를 배제하고 온전히 메모리 상에서 가동되는 PostgreSQL이나 MySQL (혹은 SQLite 인메모리 모드) 컨테이너를 가동하게 되면, 복잡한 다중 조인(Join) 쿼리의 실행 병목(Bottleneck)이 하드웨어 한계까지 돌파되어 수십 배 향상된다. 무엇보다 가장 위대한 이점은, 테스트 파이프라인 컨테이너가 폭파(Kill)됨과 전원이 꺼짐과 동시에 내부의 오염된 모든 상태 데이터가 물리적으로 완전 증발(Volatilization)하여, 다음 평가 사이클로의 ’오염 전이(Contamination Propagation)’를 수학적으로 원천 절단한다는 사실이다.
2. 다중 쿼리 동시성을 위한 무조건적 롤백(Unconditional Rollback) 트랜잭션 사슬
도커 컨테이너를 통해 물리적인 호스트망을 격리시켰다고 안심해서는 안 된다. 시간을 아끼기 위해 수십 개의 GPU 스레드(Thread)가 단일 샌드박스 컨테이너 안으로 AI의 예측 쿼리를 동시에 폭격(Concurrency)할 때, 그 내부 테이블의 레코드가 꼬이는 상태 오염을 방지해야 한다.
따라서 오라클 시스템 내부의 ‘쿼리 실행기(Query Executor)’ 레이어는, 샌드박스에 TCP 접속하여 쿼리를 쏘아 보낼 때 다음과 같은 무자비한 ’어플리케이션단 트랜잭션 수칙’을 강제 래핑(Wrapping)하여 준수해야만 한다.
def execute_oracle_sandbox_query(query: str, db_conn) -> List[Dict]:
"""오라클 샌드박스 내에서 안전하게 쿼리를 실행하는 래퍼 함수"""
try:
# 1. 묵시적 Auto-commit 차단 및 독립 트랜잭션 개시
db_conn.autocommit = False
with db_conn.cursor() as cursor:
# 2. 타임아웃(Timeout) 방벽 설정 (무한 루프 쿼리 폭주 방지)
cursor.execute("SET statement_timeout = '5000ms'")
# 3. AI 모델의 쿼리 실행 및 결과 인출(Fetch)
cursor.execute(query)
result_tensor = cursor.fetchall()
return result_tensor
finally:
# 4. [CRITICAL] 쿼리의 성공/에러 여부, 종류(SELECT/UPDATE) 막론하고
# 결과를 메모리로 뽑아낸 직후 무조건 Rollback을 강제 타격하여 상태 원복
db_conn.rollback()
위의 아키텍처 방어망 코드처럼, AI가 만약 프롬프트 탈옥(Jailbreak)에 걸려 비즈니스 테이블 내 데이터를 변질시키는 INSERT, UPDATE를 던졌거나 무한 재귀 조인을 걸었다 할지라도, 시스템은 5초 만에 프로세스를 끊어버리고, 인터프리터의 finally 구문을 통해 트랜잭션의 마무리에 무조건 rollback()을 강제 하달한다. 이로써 샌드박스의 무결성은 밀리초(ms) 단위의 생명 주기로 계속해서 원복(Restore)의 숨을 쉰다.
결국 도커(Docker)가 제공하는 거시적인 ‘물리적 격리’ 위에 파이썬 프레임워크가 강제하는 ’논리적 트랜잭션 롤백’의 칼날이 정밀하게 겹쳐질 때, 비로소 우리의 시스템은 LLM의 그 어떤 환각적 반란 쿼리 백만 개를 주입받아도 꼿꼿하게 동일한 결과를 출력해 내는 가장 차갑고 절대적인 **‘결정론적 채점관(Deterministic Judge)’**의 공학적 완성체를 획득하게 되는 것이다.