POOOLING FOREST
AI 코드 실행, 언제까지 보안 팀 눈치만 보실 건가요? (feat. Firecracker) - LLM 에이전트의 코드 실행 기능을 안전하고 빠르게 구현하는 방법, AWS가 사용하는 Firecracker
Engineering & Tech

AI 코드 실행, 언제까지 보안 팀 눈치만 보실 건가요? (feat. Firecracker)

LLM 에이전트의 코드 실행 기능을 안전하고 빠르게 구현하는 방법, AWS가 사용하는 Firecracker microVM과 오픈소스 샌드박스를 활용한 실무 해결책을 공유합니다.

김테크

8년차 개발자

안녕하세요. 풀링포레스트 풀스택 개발자 김테크입니다.

요즘 저희 팀의 가장 큰 화두는 단연 'LLM 에이전트'입니다. 단순히 텍스트만 뱉어내는 챗봇을 넘어서, 실제로 코드를 작성하고 실행해서 데이터를 분석해 주는 기능을 기획하고 있었거든요. 그런데 기획 회의 때마다 백엔드 개발자들의 표정이 썩 좋지 않았습니다. 저를 포함해서요. 이유는 딱 하나, "그 코드를 어디서 실행할 건데?" 라는 질문 때문이었습니다.

솔직히 말씀드리면, 처음에는 좀 안일하게 생각했습니다. "그냥 Docker 컨테이너 하나 띄워서 거기서 돌리고 삭제하면 되는 거 아니야?"라고 말이죠. 하지만 POC(개념 증명) 단계에서 바로 벽에 부딪혔습니다. 매번 요청이 들어올 때마다 컨테이너를 새로 띄우니 콜드 스타트(Cold Start) 때문에 3~4초씩 딜레이가 생기더군요. 사용자는 채팅창에서 깜빡이는 커서를 보며 하염없이 기다려야 했습니다. 그렇다고 미리 띄워놓자니 리소스 낭비가 심했고요.

더 큰 문제는 보안이었습니다. 인프라 보안 담당자분이 제 PR을 보시더니 심각한 표정으로 달려오셨습니다. "Docker는 완벽한 격리가 아닙니다. 커널을 공유하잖아요. 만약 악의적인 코드가 컨테이너를 탈출(Escape)하면 우리 서버 전체가 털립니다." 등골이 서늘해지더군요. 사용자(혹은 AI)가 생성한 '신뢰할 수 없는 코드'를 우리 앞마당에서 돌린다는 건 시한폭탄을 안고 있는 것과 다름없었습니다.

그때부터 멘붕에 빠져 구글링을 시작했습니다. AWS Lambda 같은 서버리스를 쓰자니 비용과 타임아웃 제약이 걸리고, 직접 VM을 띄우자니 너무 무거웠습니다. 그러다 우연히 GitHub에서 PwnFunction/sandbox (Concave AI) 라는 프로젝트를 발견했습니다. 이 녀석을 뜯어보면서 "아, 이거다!" 싶더군요.

이 프로젝트의 핵심은 Firecracker microVM을 사용한다는 점입니다. Firecracker는 AWS가 Lambda나 Fargate를 돌릴 때 사용하는 경량 가상화 기술입니다. Docker처럼 빠르면서도, 실제 VM처럼 완벽하게 격리된 환경을 제공하죠. 하지만 Firecracker를 생으로 다루는 건 정말 어렵습니다. 리눅스 커널 설정부터 네트워크 탭 인터페이스까지 건드려야 할 게 산더미거든요.

그런데 이 Concave 샌드박스는 그 복잡한 걸 아주 우아하게 포장해 뒀습니다. 제가 가장 감탄한 부분은 스냅샷 기반의 웜 풀링(Warm Pooling) 전략이었습니다.

보통 VM을 켜는 데 시간이 걸리는데, 이 친구는 미리 부팅된 VM의 메모리 상태를 스냅샷으로 찍어둡니다. 그리고 요청이 들어오면 그 스냅샷에서 '복제'해서 VM을 깨웁니다. 덕분에 부팅 시간이 200ms 미만으로 줄어듭니다. Docker 컨테이너를 띄우는 것보다 훨씬 빠르면서 보안은 VM 수준인 것이죠.

아키텍처도 꽤나 실무 친화적입니다. 제어(Control Plane)는 gRPC로 통신하고, 실행 결과(stdout/stderr)는 스트리밍으로 받아옵니다. 이게 왜 중요하냐면, LLM이 긴 코드를 실행할 때 사용자가 멍하니 기다리지 않고 중간중간 로그가 찍히는 걸 볼 수 있게 해 줄 수 있기 때문입니다.

실제 코드 사용성도 훌륭했습니다. Python SDK를 제공하는데, 마치 로컬 함수를 호출하듯 자연스럽습니다. 내부적으로 복잡한 gRPC 통신이나 VM 프로비저닝은 싹 감춰져 있죠.

# 대략적인 사용 예시 (pseudo-code)
from concave import Sandbox

async def run_risky_code():
    # 샌드박스 인스턴스 생성 (200ms 소요)
    box = await Sandbox.create()
    
    code = """
    import os
    print("Hello from isolated VM!")
    # 위험한 시스템 명령어를 시도해도 격리되어 있어 안전함
    """
    
    # 코드 실행 및 결과 스트리밍
    async for log in box.run(code):
        print(log)
        
    await box.destroy()

물론 이 프로젝트가 만능은 아닙니다. 아직 스타트업 단계의 프로젝트라 문서가 좀 부족하고, 운영 환경에 배포하려면 Terraform과 Ansible 설정을 우리 입맛에 맞게 꽤 많이 수정해야 했습니다. 저희 팀도 도입을 검토하면서 모니터링 대시보드를 커스텀하고, 리소스 제한 정책을 좀 더 빡빡하게 설정하는 작업을 거쳤습니다.

하지만 '신뢰할 수 없는 코드 실행'이라는 난제를 해결하기 위해 바닥부터 Firecracker를 공부해서 오케스트레이터를 짜는 것보다는, 이런 잘 만들어진 오픈소스를 베이스로 삼는 게 훨씬 효율적입니다.

개발자로서 우리는 늘 '기능 구현'과 '안정성' 사이에서 줄타기를 합니다. 특히 AI 기능을 제품에 녹여낼 때는 보안이 뒷전이 되기 쉬운데, 이런 도구들을 적극적으로 활용하면 보안 팀의 눈치를 보지 않고도 공격적인 기능을 시도해 볼 수 있습니다. 혹시 지금 "Code Interpreter" 기능을 만들고 계신다면, 무작정 `docker run`을 때리기 전에 이 샌드박스 프로젝트를 한번 살펴보시길 권합니다. 여러분의 주말과 서버의 안녕을 위해서 말이죠.

지금 읽으신 내용, 귀사에 적용해보고 싶으신가요?

상황과 목표를 알려주시면 가능한 옵션과 현실적인 도입 경로를 제안해드립니다.