자체 호스팅 LLM 에이전트 군단 신뢰성 확보
출처: Dev.to
단일 로컬 LLM 노드는 이미 해결된 문제다. InferenceService를 작성하고 오퍼레이터가它调度한다, llama.cpp 또는 MLX가 서비스를 제공하고 OpenAI 호환 엔드포인트를 얻게 된다. 우리는 수개월간 그렇게 해왔다.
여러 대의 노드를 실행하는 것은 쉽지 않다. 내 플릿은 의도적으로 다양하다: 클러스터 내 CUDA 파드와 홈 랩 네트워크에 격리된 Apple Silicon Macs, 각각 별도의 에이전트(인ference용, 에이전틱 코딩 하니스용) 두 개를 실행한다. 0.8.4 버전을 플릿에 배포한 날, 정확히 확장되지 않음을 깨달았다.
나는 각 Mac을 직접 업데이트했다. 제어 평면은 실행 중인 에이전트 버전을 알 수 없었다. 그리고 launchd 재로드를 사용해 에이전트를 재시작했을 때, 이미 로드된 서비스에서는 무음으로 아무 일도 일어나지 않아 구버전이 계속 실행되는 것을 발견했다. 직접 프로세스 트리를 확인하면서 알게 되었다. 3대만 하면 번거롭고, 30대가 되면 불가능해진다. 전체적인 주장을 고려할 때 온프레미스 AI는 3보다 훨씬 많은 수를 실행해야 한다.
LLMKube의 마지막 작업은 더 빠른 런타임이나 더 큰 모델이 아니었다. 플랫폼을 신뢰할 수 있게 만드는 것이었다: 안전하게 자체 업데이트하고 제어 평면에 자신의 상태를 거짓말로 말할 수 없게 하는 것이다. 여기 그것이 어떻게 이루어졌는지이다.
해결책은 새로운 클러스터 범위 CRD인 AgentRelease와 에이전트 자체에 있는 self‑update 경로이다. 원하는 릴레이스를 한 번 기술하고 오퍼레이터가 배포하면 에이전트가 가져와 적용한다. 설계는 Rancher의 system‑upgrade‑controller, k0s autopilot의 per‑platform SHA‑256 스테이징, Teleport의 outbound‑only 폴 모델과 같은 기존 Kubernetes 노드용 솔루션을 직접 차용했다.
- 선언적이며 승인됨. AgentRelease는 에이전트 이름, 버전, 플랫폼별 아티팩트(URL 및 SHA‑256)를 지정한다. 인간 한 명이 approved: true 로 전환할 때까지 아무 일도 일어나지 않는다. 승인된 CR은 신뢰의 앵커이다.
- 단계적이며 건강 검증 단계다. 오퍼레이터는 한 노드씩 업데이트한다. 새로 업데이트된 노드는 다시 등록하고, 다음 노드를 다루기 전에 지정된 시간 동안 정상적으로 작동하는 상태를 유지해야 한다.
- 실패 시 중단. 타임아웃 내에 목표 버전에 도달하지 못하면 롤아웃이 즉시 중단된다. 영향 범위는 정확히 하나의 노드이며, 그 노드를 확인하러 나간다.
- 검증 가능하고 되돌릴 수 있다. 에이전트는 아티팩트를 다운로드하고 SHA‑256을 확인한 뒤 anything을 건드리기 전에는 새 바이너리를 기존 파일 옆에 스테이징한다, 원자적으로 현재 심볼릭 링크를 전환하고, 이전 심볼릭 링크를 유지해 한 명령으로 롤백한다. 잘못된 체크섬은 실행 중인 버전을 그대로 남겨 둔다.
- 외부 전용이다. 엣지 에이전트는 NAT 뒤에 있으며 Tailscale를 사용한다. 외부에서 폴을 보낼 뿐, 들어오는 연결은 없다. 노트북이 스스로 업데이트할 수 있는 형태가 멀리 떨어진 캐비닛 안 Mac도 같은 방식으로 업데이트할 수 있게 만든다.
최종 결과는 내가 만든 릴레이스를 한 줄 kubectl apply 명령과 승인만으로 끝나며, 오후 동안 SSH를 이용한 것이 아니라 된다.
실제 노드에서 전체 루프를 검증했다: 버전을 게시하고 AgentRelease를 적용한 뒤 AwaitingApproval 상태에서 대기하고, 승인하면 노드가 드레인되고, 다운로드하고, 검증하고, 플립하여 새 바이너스로 재시작하고, 보고서를 받아 롤아웃이 성공적으로 종료되는 것을 확인했다.
첫 번째는 여전히 수동 단계가 있다(구버전 에이전트는 새 버전을 가르칠 수 없으므로 자체 업데이트를 할 수 없다), 하지만 이후 릴리스부터는 자동으로 진행된다.
자동 업데이트되는 플랫폼이 건강을 거짓말로 말하는 것은 수작업보다 더 나쁜 일이다. 따라서 업데이트 경로와 함께 덜 화려한 신뢰성 작업인 “신뢰할 수 있는 플랫폼” 마일스톤도 반드시 구현되어야 했다.
Liveness, not optimism.
생존(리얼 타임), 낙관적이지 않다.
금속 노드는 엔드포인트를 등록하고 호스트가 몇 주간 오프라인 상태에서도 영원히 하나의 준비된 복제본을 계속 보고했다. 이제 에이전트는 하트비트를 보내고, 컨트롤러는 오래된 등록을 만료한다.
입장 검증
새로운 입장 검증 웹훅은 kubectl apply 시 에이전트와 작업 정의를 검사하므로, 사양이 잘못된 경우 문턱에서 바로 거부되고, 이후에 작업이 배치될 때 혼잡하게 3단계 뒤에 실패하는 대신이다.
실제 엔드투엔드 테스트
유닛 테스트와 envtest는 많은 것을 커버하지만, 전체 설치 경로를 검증한 적은 없었다: helm 차트를 설치하고 오퍼레이터가 작동하며, 에이전트가 노드를 등록하고, 스케줄러가 작업을 라우팅하고, 작업이 완료까지 실행되는 과정을 말이다. 이제 kind 기반 CI 작업은 정확히 그렇게 한다.
이 중 어느 것도 광고판에 올릴 기능이 아니다. 이는 데모와 생산 하드웨어를 향하게 할 수 있는 것 사이의 차이를 만든다.
여기서는 솔직히 공개된 개발 과정을 보여주고, 녹색 테스트 스위트만으로 신뢰하기보다는 이 작업에 더 신뢰한다.
처음 실제 노드에 적용한 라이브 self‑update는 동작하지 않았다. 에이전트 로그는 self‑update가 “관리된 설치 루트가 아닌 곳에서 실행 중”이라며 비활성화되었다는 메시지를 남겼는데, 이는 잘못된 내용이었다: 정확히 그 루트에서 실행 중이었다. 탐지 메커니즘은 실행 중인 바이너리의 해시 경로를 문자의 current/ 심볼릭 링크 경로와 비교했지만, 바이너리 경로를 해싱할 때는 심볼릭 링크를 따라 실제 버전 디렉터리로 이동했기 때문에 두 경로는 절대 일치하지 않았다.
유닛 테스트는 두 가지 이유에서 통과했다: 체크에 절대 발생하지 않는 해시되지 않은 경로를 제공했고, 결과를 한 번 저장해 영원히 캐시했기 때문에 실제로는 발견하지 못했다. 기능은 실제 설치 환경에서는 조용히 비활성화된 채, 실제 롤아웃을 dogfooding하면서야 비로소 이를 발견했다.
해결책은 작았다. 진짜로 실행해 보는 것이 필요했다.
그 후엔 엔드투엔드 테스트가 있었다. 나는 이 테스트를 작성해 유닛 테스트가 볼 수 없는 설치 경로 버그를 잡으려 했고, 첫 CI 실행에서 바로 하나의 버그가 포착되었다: 작업이 Scheduled 상태에 도달한 뒤 stalled 되었다. 이유는 에이전트가 하나만 모니터링하는 네임스페이스에 있었고, 작업은 다른 네임스페이스에 있었기 때문이다.
이는 실제 API 서버가 보여주는 버그 클래스이며, 모의에서는 나타나지 않는다.
나는 나머지 사이클이 청결하지 않았다고 가정하지 않는다. 웹훅의 TLS 인증서를 간단히 고정하는 방법이 CI 스크립트를 깨뜨렸다. 이 방법은 환경 변수를 통해 거대한 블롭을 조용히 전달했으며, macOS에서는 작동하지만 리눅스에서는 실패한다.
Glob 패턴이 한 방향은 올바르게 라우팅했지만 다른 방향에서는 매칭되지 않는 문자로 컴파일되었고, 여전히 정상적인 상태를 보고했다.
이 모든 것이 리뷰나 로컬 검토를 통과했고, 다음 단계에서 포착되었다: 전체 린트, 실제 클러스터, 적대적인 두 번째 검토.
레이어링이 핵심이다. 목표는 제로 버그가 아니었고, 도달할 수 없는 노드에 남는 버그가 없다는 것이었다.
자체 호스팅 AI의 어려운 문제는 인퍼런스이다: 양자화, GPU 메모리, 토큰당 초 수이다. 이는 어려우며 많은 시간을 소비한다. 하지만 관리 클라우드에 머무르게 하는 실제 요소는 원시적인 능력 자체가 아니다.
그것은 다른 사람이 플랫폼을 운영한다는 것이다. 업데이트가 landen(적용)되고, 죽은 노드는 풀리고, 잘못된 구성이 거부되며, 당신은 그 어떤 것도 생각하지 않는다.
주권형 AI가 취미가 아닌 실제 대안이 되려면 동일한 “생각하지 않아도 되는” 특성을 제공하면서 데이터와 모델을 소유한 하드웨어에 보관해야 한다.
수작업으로 돌봐야 하는 플랫폼은 사실상 주권이 없는 것이다. 그것은 누군가의 운영 부담을 당신에게 옮긴 것에 불과하다.
이 포스트의 작업은 격차를 줄이는 비 glamorous한 절반이다: 안전하게 자체 업데이트하고 자신의 건강을 진실되게 말하며, 작동에 방해가 되는 구성을 거부하는 플랫폼.
그것이 현지 AI 규모에 필요한 제어 평면이다. 이미 LLMKube에 구현되어 있으며 오픈 소스이며, 스스로 버그를 잡아냈다.
LLMKube는 CUDA, Apple Silicon Metal을 지원하는 Kubernetes 연산자다.