Ubuntu 서버에서 Mattermost의 채널마다 전담 Hermes 에이전트를 붙이는 전 과정을, 처음부터 차근차근 따라 할 수 있도록 정리한 가이드입니다. M1 Mac에서 검증·구축한 내용(Hermes × Mattermost — 채널별 다중 에이전트)을 Ubuntu 서버 환경으로 이식한 글입니다.

전제 환경 (기존 Ubuntu base 글 기준)

  • 서버: Ubuntu, amd64, root 계정
  • Mattermost: 이미 Docker로 구동 중, 도커 네트워크 이름 mattermost
  • Hermes 데이터: /root/.hermes → 컨테이너 /opt/data
  • Hermes compose: /root/hermes/docker-compose.yml, 컨테이너명 hermes
  • Hermes: mattermost 네트워크에 합류 → Mattermost 접근 주소 http://mattermost:8065/

검증 완료 (2026-07-14, 서버 your-server): 이 가이드대로 default(@agents-bot 보존) + sales/finance/dev/qna 4개 채널 에이전트를 실제 구축·기동, 봇 응답까지 확인함. 아래 명령·주의사항은 그 실전에서 정리된 것.


전체 그림

Ubuntu 서버
└─ Docker
   ├─ 네트워크: mattermost
   ├─ 컨테이너: docker-mattermost-1, docker-postgres-1, nginx_mattermost
   └─ 컨테이너: hermes   (s6-svscan PID1 — 프로필별 게이트웨이 감독)
        ├─ 프로필 default  (@agents-bot) ← base 글에서 만든 기존 봇 (보존)
        ├─ 프로필 sales    (@sales-bot)   → 영업 채널
        ├─ 프로필 finance  (@finance-bot) → 재무 채널
        ├─ 프로필 dev      (@dev-bot)     → 개발 채널
        └─ 프로필 qna      (@qna-bot)     → 고객응대 채널

핵심 개념: 컨테이너를 여러 개 띄우는 게 아니라, 하나의 hermes 컨테이너 안에서 "프로필"을 여러 개 만들고, 각 프로필의 게이트웨이를 s6가 감독합니다. 프로필마다 자체 .env(봇 토큰)·성격·메모리를 격리 보관합니다.


Phase 0. 전제 확인 — base 설치가 끝났는가

이 가이드는 단일 봇(default 프로필)이 이미 응답하는 상태에서 시작합니다. 아직이라면 먼저 Hermes Agent — Ubuntu 서버 Docker 설치 + Mattermost 연동를 완료하세요. 완료 기준:

# hermes 컨테이너 실행 중
docker ps --filter name=hermes          # Up ... 이면 OK

# Mattermost 연결됨
docker exec hermes cat /opt/data/gateway_state.json \
  | python3 -c 'import json,sys;print(json.load(sys.stdin)["platforms"]["mattermost"]["state"])'
# => connected 이면 OK

그리고 편의 alias(호스트에서 hermes 한 단어로 실행):

grep -q "alias hermes=" ~/.bashrc || \
  echo "alias hermes='docker exec -it hermes /opt/hermes/.venv/bin/hermes'" >> ~/.bashrc
source ~/.bashrc

⚠️ default 프로필은 절대 삭제·변경하지 마세요. 새 채널은 항상 프로필 "추가"로만 진행합니다.


Phase 1. 왜 프로필별 에이전트인가 (개념)

  • 문맥 분리: 채널마다 전담 에이전트 → 영업/개발/재무 문맥이 안 섞임 → 답 품질↑ (64k 컨텍스트 한계 우회).
  • 무료/작은 모델에 유리: 각자 다룰 범위가 좁아 RAG 파이프라인 없이도 잘 동작.
  • 역할·권한 분리: 프로필마다 성격(SOUL.md)·모델·봇을 다르게.
  • 한 컨테이너로 관리: 컨테이너 N개보다 리소스·운영이 단순. 재시작 시 전부 자동 복구.

Hermes 이미지는 s6-overlay(컨테이너 init/프로세스 감독자) 위에 지어져, 한 컨테이너 안 다중 게이트웨이를 원래 지원합니다. 프로필별 게이트웨이가 런타임에 s6 서비스 gateway-<name>으로 등록됩니다.


Phase 2. Ubuntu 환경의 차이 (M1 Mac 대비)

항목 M1 Mac Ubuntu 서버 (이 글)
데이터 경로 ~/.hermes /root/.hermes
프로필 경로(호스트) ~/.hermes/profiles/<name>/ /root/.hermes/profiles/<name>/
셸/alias 파일 ~/.zshrc ~/.bashrc
Mattermost 주소 host.docker.internal:8065 http://mattermost:8065/ (도커 네트워크 공유)
아키텍처 arm64 amd64 (--platform 불필요)

프로필을 --clone으로 만들면 default의 .env를 복제하므로 MATTERMOST_URL=http://mattermost:8065/가 자동으로 상속됩니다. 새 프로필에선 토큰과 채널 ID만 바꾸면 됩니다.


Phase 3. 채널마다 전용 봇 계정 만들기 (Mattermost)

채널 1개 : 봇 1개 : 프로필 1개 원칙. 같은 토큰을 재사용하면 한 메시지에 봇이 중복 응답하므로, 채널마다 별도 봇을 만듭니다.

각 채널(영업/재무/개발/고객응대)에 대해 반복:

  1. 관리자 로그인 → System Console → Integrations → Bot AccountsEnable Bot Account Creation = true

  2. 메인 화면 → 좌상단 메뉴 → Integrations → Bot Accounts → Add Bot Account

  3. 폼 입력:

    항목 값 예시 비고
    Username sales-bot / finance-bot / dev-bot / qna-bot 봇 핸들
    Display Name 영업봇 선택
    Role Member 대화 에이전트엔 충분
    post:all ✅ Enabled DM + 채널 응답 가능
  4. Create Bot Account → 다음 화면의 Token: 즉시 복사 (이 화면에서만 보임).

결과적으로 봇 4개와 토큰 4개를 확보하게 됩니다.


Phase 4. 각 채널의 ID 확보

MATTERMOST_HOME_CHANNEL에는 채널 ID(26자)를 넣습니다. handle(URL 슬러그)·표시이름 아님.

방법 A — 브라우저: 채널명 클릭 → View Info → Channel ID 복사.

방법 B — 터미널 (mmctl, 컨테이너 내부 무인증):

docker exec docker-mattermost-1 mmctl --local team list
docker exec docker-mattermost-1 mmctl --local channel list <팀이름> --json \
  | python3 -c 'import json,sys;[print(c["display_name"], c["id"]) for c in json.load(sys.stdin)]'

확보한 값을 정리해둡니다 (예):

sales   채널ID=b9ee…   봇토큰=…
finance 채널ID=q6re…   봇토큰=…
dev     채널ID=b15a…   봇토큰=…
qna     채널ID=f5xr…   봇토큰=…

Phase 5. 프로필 생성 + 설정

⚠️ Ubuntu 서버 필수 주의 — root 소유권 함정. Ubuntu에서 docker exec hermes ...root로 실행됩니다. 그대로 profile create하면 프로필 파일이 root 소유로 생기고, 실제 게이트웨이는 s6가 hermes 사용자로 띄우므로 hermes가 로그·상태 파일을 못 써서 게이트웨이가 뜨자마자 조용히 죽습니다(gateway list에 not running, 로그 파일도 안 생김). Mac Docker Desktop에선 안 드러나는 리눅스 서버 특유의 문제.

해법 2가지 (택1):

  • (권장) 처음부터 hermes 사용자로 실행: 모든 docker exec hermes …docker exec -u hermes hermes … 로.
  • (사후 교정) 이미 root로 만들었으면 chown: docker exec hermes chown -R hermes:hermes /opt/data/profiles /opt/data/logs

5-1. 프로필 생성 (default 복제)

# 권장: -u hermes 로 실행 (root 소유권 문제 원천 차단)
docker exec -u hermes hermes /opt/hermes/.venv/bin/hermes profile create sales \
  --clone --description "영업 채널 담당 에이전트. 영업/CRM 문의 응대."
  • --clone = default의 config.yaml·.env·SOUL.md·skills 복제 → MATTERMOST_URL 등 공통값 자동 상속.
  • 생성 위치: /root/.hermes/profiles/sales/ (호스트에서 직접 편집 가능).

5-2. 전용 봇 토큰 + 채널 ID 주입

호스트에서 그 프로필의 .env를 직접 편집:

nano /root/.hermes/profiles/sales/.env

아래 두 줄만 이 프로필 전용 값으로 교체 (나머지 URL/ALLOWED_USERS는 그대로 둠):

MATTERMOST_TOKEN=<sales 봇 토큰>
MATTERMOST_HOME_CHANNEL=<sales 채널 ID>

MATTERMOST_ALLOWED_USERS는 default와 동일하게 두면 같은 사용자 화이트리스트를 씁니다. 채널별로 다르게 하려면 여기서 조정.

5-3. finance / dev / qna 반복

같은 방식으로 나머지 3개 프로필도 생성·설정 (권장: -u hermes):

docker exec -u hermes hermes /opt/hermes/.venv/bin/hermes profile create finance --clone --description "재무 채널 담당."
docker exec -u hermes hermes /opt/hermes/.venv/bin/hermes profile create dev     --clone --description "개발 채널 담당."
docker exec -u hermes hermes /opt/hermes/.venv/bin/hermes profile create qna     --clone --description "고객응대 채널 담당."
# 각각 /root/.hermes/profiles/<name>/.env 에서 TOKEN + HOME_CHANNEL 교체

5-4. (root로 만들었다면) 소유권 교정 — 기동 전 필수

만약 5-1~5-3에서 -u hermes 없이 root로 프로필을 만들었다면, 기동 전에 반드시 소유권을 hermes로 되돌립니다. 안 하면 게이트웨이가 조용히 죽습니다(위 경고 참고):

docker exec hermes chown -R hermes:hermes /opt/data/profiles /opt/data/logs

실제 Ubuntu 서버(your-server) 구축 시 이 단계가 없어서 4개 게이트웨이가 전부 not running이었고, chown 후 정상 기동됨. root로 만들었으면 이 줄을 꼭 실행.


Phase 6. 게이트웨이 기동

프로필마다 게이트웨이를 s6 감독 서비스로 띄웁니다. docker exec -d(detached) 로 실행하세요 — 프롬프트가 바로 돌아옵니다.

docker exec -d hermes /opt/hermes/.venv/bin/hermes -p sales   gateway run --replace
docker exec -d hermes /opt/hermes/.venv/bin/hermes -p finance gateway run --replace
docker exec -d hermes /opt/hermes/.venv/bin/hermes -p dev     gateway run --replace
docker exec -d hermes /opt/hermes/.venv/bin/hermes -p qna     gateway run --replace
  • -d 없이 실행하면 → gateway is now running under s6 supervision 메시지 출력 후 프롬프트가 안 돌아오고 매달릴 수 있습니다(터미널이 앞단 프로세스에 붙어 대기). 이때 게이트웨이는 이미 s6로 떠 있으므로, Ctrl+C로 빠져나와도 게이트웨이는 안 죽습니다(s6가 감독·유지). 새 창에서 gateway list로 확인 가능.
  • -d를 붙이면 이 매달림이 없어 깔끔합니다. 게이트웨이는 어차피 s6로 넘어가 동일하게 동작합니다.

기동 확인:

docker exec hermes /opt/hermes/.venv/bin/hermes gateway list

기대 결과 (5개 모두 running):

Gateways:
  ✓ default (current)  — PID …
  ✓ sales              — PID …
  ✓ finance            — PID …
  ✓ dev                — PID …
  ✓ qna                — PID …

각 프로필이 올바른 봇으로 붙었는지:

for N in sales finance dev qna; do
  echo -n "$N → "; docker exec hermes sh -c \
    "grep -E 'authenticated as' /opt/data/profiles/$N/logs/gateway.log | tail -1"
done

Phase 7. 봇을 채널에 초대 + 검증

게이트웨이가 붙어도, 봇이 채널 멤버가 아니면 메시지를 못 받습니다.

각 봇을 담당 채널에 초대:

  • Mattermost에서 해당 채널 → 채널명 클릭 → Add Members → 봇 검색(@sales-bot 등) 추가.

초대 후 검증 (채널에서 봇 멘션 → 실시간 로그 관찰):

docker exec hermes sh -c 'tail -f /opt/data/profiles/sales/logs/gateway.log' \
  | grep --line-buffered -iE "inbound message|response ready|Sending response"

채널에서 @sales-bot 안녕? 보내면 아래 흐름이 찍히면 성공:

inbound message: … chat=<채널ID> msg='안녕?'
response ready: … response=… chars
[Mattermost] Sending response … to <채널ID>

Channel directory built: 0 target(s) 로그는 능동 추적 대상 수일 뿐, 멘션 반응과 무관합니다. 봇이 채널 멤버이면 응답합니다.


Phase 8. 재시작 생존성 (자동 복구)

프로필 게이트웨이를 running 상태로 두면, 컨테이너 재시작·크래시 후 부팅 리컨실러(cont-init.d/02-reconcile-profiles)가 자동으로 다시 띄웁니다 (각 프로필의 gateway_state.json을 읽어 판단).

확인:

cd /root/hermes && docker compose restart hermes
# 잠시 후
docker exec hermes /opt/hermes/.venv/bin/hermes gateway list   # 5개 다시 running 이면 OK

즉 별도 systemd 설정 없이도, 한 번 켜둔 채널 에이전트들은 서버/컨테이너 재시작 후 전부 자동 복구됩니다.


Phase 9. 운영 노트

9-1. 메모리

  • Hermes 컨테이너: 게이트웨이 1개 ~270MB → 5개 ~720MB (1개당 약 +110MB, 클라우드 모델 기준).
  • compose의 mem_limit: 4g가 상한. 채널을 많이 늘리면 상향 고려. 서버 여유 메모리 확인:
    free -h
    docker stats --no-stream --format 'table {{.Name}}\t{{.MemUsage}}'
    

9-2. 모델

  • 프로필별로 다른/더 빠른 모델 지정 가능:
    docker exec hermes /opt/hermes/.venv/bin/hermes -p sales config set model.default <모델>
    
  • 무료 모델은 응답이 느릴 수 있음(수십 초). 트래픽 몰리는 채널은 모델 교체 검토.

9-3. 성격 부여

  • 각 프로필의 SOUL.md를 채널 성격에 맞게 수정:
    nano /root/.hermes/profiles/sales/.env    # 참고: 성격은 SOUL.md
    nano /root/.hermes/profiles/sales/SOUL.md  # 영업봇답게
    # 반영하려면 해당 프로필 게이트웨이 재시작 (아래)
    

9-4. 프로필 게이트웨이 재시작 / 관리

# 프로필 게이트웨이만 재시작: 해당 python PID kill → s6 자동 부활
docker exec hermes sh -c 'kill $(pgrep -f "hermes -p sales gateway run")'

# 상태 / 목록
docker exec hermes /opt/hermes/.venv/bin/hermes gateway list
docker exec hermes /opt/hermes/.venv/bin/hermes profile list

# 프로필 삭제 (s6 슬롯 자동 정리) — default엔 절대 쓰지 말 것
docker exec hermes /opt/hermes/.venv/bin/hermes profile delete <name> -y

9-5. 로그 위치

  • 프로필별: /root/.hermes/profiles/<name>/logs/gateway.log
  • default: /root/.hermes/logs/
  • s6 캡처: /root/.hermes/logs/gateways/<name>/current

요약 체크리스트

  • Phase 0: base 설치 완료 확인 (gateway_state.json = connected), hermes alias(~/.bashrc)
  • Phase 3: 채널마다 봇 계정 생성 (sales-bot 등, post:all), 토큰 4개 복사
  • Phase 4: 채널 ID 4개 확보 (View Info 또는 mmctl)
  • Phase 5: -u hermes profile create <name> --clone × 4, 각 .env에 TOKEN + HOME_CHANNEL 주입
  • Phase 5-4: (root로 만들었다면) chown -R hermes:hermes /opt/data/profiles /opt/data/logs
  • Phase 6: docker exec -d -p <name> gateway run --replace × 4 → gateway list 5개 running
  • Phase 7: 각 봇을 담당 채널에 Add Members → 멘션 테스트로 응답 확인
  • Phase 8: docker compose restart hermes 후 5개 자동 복구 확인
  • Phase 9: (선택) 모델 교체, SOUL.md 성격, 메모리 점검

자주 겪는 문제

증상 원인 해결
봇이 채널에서 무응답 봇이 채널 멤버 아님 Add Members로 초대
한 메시지에 여러 봇 응답 여러 프로필이 같은 토큰 프로필마다 별도 봇/토큰
gateway run이 멈춤(hang) -d 없이 앞단 실행 docker exec -d … 로 기동 (Ctrl+C해도 s6가 유지)
프로필 게이트웨이가 뜨자마자 not running, 로그 파일도 없음 root로 만든 프로필 파일을 hermes가 못 씀 docker exec hermes chown -R hermes:hermes /opt/data/profiles /opt/data/logs 후 재기동. 이후엔 -u hermes로 생성
Refusing to run … as root --no-supervise를 root로 실행 --no-supervise 빼고 감독 경로로 실행(s6가 hermes로 띄움)
프로필이 mattermost 못 찾음 URL이 host.docker.internal .envhttp://mattermost:8065/
재시작 후 프로필 게이트웨이 안 뜸 기동 전 gateway_state=stopped 한 번 gateway run으로 running 만든 뒤 재시작
전 채널 봇이 동시에 Provider authentication failed root로 CLI를 돌려 auth.json이 root 소유가 됨 → 게이트웨이가 못 읽고 빈 스토어로 기동 chown -R hermes:hermes /opt/data 후 재기동. 상세: ../03-troubleshooting/2026-07-21-01-…
전 채널 봇이 동시에 model provider failed after retries 사용 중인 무료 모델이 provider에서 사라짐(404) 호출 가능한 모델로 config set model.default (프로필마다 실행) 후 재기동

참고