Nanobot 설치기 (2편): Config의 늪과 PM2 무중단 배포
Nanobot 설치기 시리즈
- ← 1편: 메인 서버를 지켜줄 AI 비서 찾기
- 2편: Config의 늪과 PM2 무중단 배포 (현재 글)
설치 명령어 몇 줄 치면 뚝딱 끝날 줄 알았는데, config 파일 하나 때문에 주말 반나절을 통째로 날렸다.
그래도 결국 PM2에 태워 무중단으로 띄워놓고 나니 밀려오는 그 성취감이란… 바닥부터 구른 Nanobot 설치 2편 기록이다.
오랜만의 venv, 그리고 낯선 에러
1편에서 pip install -r requirements.txt까지는 어떻게든 끝냈다. 의존성 패키지 몇 개가 낡아서 deprecated 경고를 사정없이 뿜어냈지만, 일단 실행 파일은 만들어졌으니 쿨하게 넘어가기로 했다.
이제 대망의 핵심 설정 파일 config.yaml을 건드릴 차례.
Zhipu AI(GLM) 콘솔에서 발급받은 API 키와 텔레그램 BotFather한테 받아온 봇 토큰을 각각의 자리에 정성껏 붙여넣었다. README 예제 파일 보면서 복붙했으니 문제 생길 구석이 없다고 확신했다.
python main.py
터미널에 초록색 로그가 쫙 올라오면서 봇이 실행됐다. 속으로 ‘나이스!’를 외치며 폰으로 텔레그램을 열고 봇에게 설레는 마음으로 “안녕” 한 글자를 보냈다.
그런데 돌아온 건 다정한 인사가 아니라, 터미널을 새빨갛게 물들인 에러 폭탄이었다.
Error: Anthropic API Key missing
범인은 ‘Default Provider’
“아니 대체 왜 Anthropic이 튀어나오는데?? 난 GLM 쓴다고 등록해 뒀잖아!”
처음엔 오픈소스 코드 자체에 버그가 있는 줄 알고 리포지토리 Issue 탭부터 뒤졌다. 분명 config.yaml에 GLM API 키를 똑똑히 넣었는데, 대체 왜 뜬금없이 Claude 키를 달라고 생떼를 쓰는 걸까?
머리를 식히고 로그를 한 줄씩 천천히 뜯어보다가 비로소 범인을 찾아냈다.
# config.yaml (문제의 부분)
agents:
defaults:
workspace: "~/.nanobot/workspace"
model: "anthropic/claude-opus-4.5" # <-- 바로 여기가 함정이었다
아래쪽 provider 섹션에 아무리 GLM 설정을 정성스럽게 적어놔도, 정작 최상단 defaults 섹션의 글로벌 기본 model 값이 anthropic/claude-opus-4.5로 굳건하게 박혀있었던 것이다. README 템플릿을 무지성으로 복붙하다 보니 이 한 줄을 미처 확인하지 못했다.
# 수정 후
agents:
defaults:
workspace: "~/.nanobot/workspace"
model: "zhipu/glm-4" # GLM-4.7로 깔끔하게 변경
이렇게 고치고 다시 스크립트를 올리니, 그제야 봇이 얌전하게 GLM API를 호출하기 시작했다. 허탈해서 헛웃음이 다 나왔다.
GLM-4 vs 5 (feat. 그래도 4가 안정적이다)
모델은 일단 GLM-4로 잡았다. 때마침 Zhipu AI의 Coding Plan Pro 요금제에서도 신형 GLM-5가 풀렸다는 소식을 듣고 혹해서 zhipu/glm-5.0으로 슬쩍 바꿔봤다.
그런데 막상 붙여보니 봇 응답 상태가 영 메롱이었다. API 호출은 되는데 답변을 하다 말고 중간에 뚝 끊기거나 헛소리를 내뱉는 현상이 잦았다. 아직 Nanobot 프레임워크와 GLM-5 사이의 프롬프트 템플릿 호환성이 덜 다듬어진 느낌이었다. 결국 미련을 버리고 검증된 GLM-4.7로 조용히 롤백했다. 최신 모델이라고 무조건 좋은 게 아니라는 뼈아픈 교훈을 얻었다(얼리어답터의 씁쓸한 눈물).
설정을 전부 매끄럽게 정리하고 다시 텔레그램으로 말을 걸었다.
나: “지금 시간 알려줘” Nanobot: “현재 시각 오후 3시 42분.”
캬, 드디어 성공이다! 봇이 사람 말을 알아듣고 정상적인 답변을 내뱉는다.
신나서 텔레그램으로 이것저것 더 시켜봤다. 간단한 계산, 날씨 확인, 서버 파일 목록 조회 같은 일상 명령어들. 대부분 빠릿하게 잘 처리했는데, 파일 목록 조회에서 살짝 막혔다. 정해진 workspace 디렉터리 바깥으로는 절대 접근을 안 해준다. 에러 메시지를 보는데 짜증 나기는커녕, 1편에서 원했던 ‘권한 제어’가 실제로 칼같이 작동하고 있다는 뜻이라 오히려 안도감이 밀려왔다.
PM2로 ‘죽지 않는 봇’ 만들기
동작 테스트까지 끝났으니, 이제 터미널 창을 닫아도 봇이 24시간 내내 죽지 않도록 PM2에 영구 상주시켜야 한다.
지난번에 맥미니에 PM2 세팅하면서 바닥부터 구른 짬바가 여기서 빛을 발했다. Python 가상환경(venv) 바이너리 경로만 정확하게 물려주면 끝이다.
pm2 start main.py --name "nanobot" \
--interpreter ./venv/bin/python \
-- --gateway-port 8000
--interpreter 옵션으로 우리가 만든 venv 안의 파이썬 인터프리터를 지정하고, -- 뒤에는 main.py에 전달할 게이트웨이 포트 인자를 붙여줬다.
pm2 save
pm2 startup
pm2 list를 찍어보니 nanobot 프로세스가 영롱한 초록색 online 상태로 예쁘게 떠 있다. 이제 집이 정전되거나 macOS가 새벽에 혼자 업데이트 후 재부팅돼도 알아서 벌떡 살아난다.
터미널을 끄고, SSH 연결을 끊고, 맥북에어를 덮었다. 그리고 한 시간쯤 지난 뒤 침대에 누워 폰으로 텔레그램을 열었다.
나: “아직 살아있어?” Nanobot: “응, 계속 대기 중!”
완벽하다. 마음의 평화가 찾아왔다.
앞으로의 계획: 본격적인 보안 검증
일단 설치와 구동, 무중단 배포까지는 완벽하게 성공했다. 하지만 애초에 OpenClaw를 메인 서버에서 쫓아냈던 이유가 보안 문제였던 만큼, Nanobot도 당장 중요한 실무 코드를 덥석 맡길 생각은 없다.
당분간은 방구석 화이트해커 모드로 이것저것 찔러보면서 자체 검증을 거칠 생각이다:
- 파일 시스템 탈출 테스트:
workspace디렉터리 밖으로 빠져나갈 수 있는지,/etc나/var같은 민감한 시스템 파일을 훔쳐볼 수 있는지 집요하게 공격해 보기 - 명령어 실행 차단:
rm -rf나sudo같은 시스템 폭파용 위험 명령어가 실제로 필터링에 걸려 튕겨 나가는지 확인 - 외부 접근 통제: 텔레그램 봇은 외부 통신망과 연결되는 만큼, 내 계정 외에 허가받지 않은 침입자의 명령을 칼같이 씹는지 체크
이 모든 검증을 통과하고 “진짜 안전하다”는 확신이 설 때, 비로소 내 피 같은 메인 프로젝트 코드베이스를 봇에게 넘겨줄 계획이다. 코드 리뷰 자동화, 실시간 크래시 분석, 정기 백업 스크립트 관리까지… 1편에서 꿈꿨던 나만의 맥미니 AI 비서 완성형이 머지않았다.
지금은 일단 “말 잘 듣고 안전한 비서 수습사원 한 명 채용했다” 정도로 만족하련다. 본격적인 현장 투입은 수습 기간이 끝난 뒤다!
(설치기 완결. 험난한 실전 보안 검증기는 테스트가 끝나는 대로 정리해 볼 예정이다)