사내 AI 코딩 에이전트 관리 플랫폼 — 기기 정책 가드와 사용량 수집 서버
Platform Part 리더 · 설계·운영·머지 승인 직접, 구현은 AI 코딩 에이전트
2026-08-01 — present
전사 AI 코딩 에이전트의 기기 정책 가드·사용량 수집 서버 설계·운영·머지 승인을 맡고 AI 에이전트로 구현: 가드의 정책 배포로 사용량 보고 스위치를 92명에게 켜 보고 중인 기기 87대 모두에서 집행을 확인했고, 수집 서버의 2일 18시간·17시간 40분 무응답 장애 뒤 메모리 상한을 "앱 < 컨테이너 < 호스트" 순서로 다시 잡고 파싱을 스트리밍으로 바꿔 최대 파일 파싱 피크를 640MiB 한도의 67%로 낮췄습니다.
성과 지표
- 보고 스위치 집행 기기
- 87대 전부
- 활성 명단 92명 배정 · 보고 중인 온라인 기기 기준
- 수집 서버 장애 대응
- 예산·한도 명시
- 2일 18시간 31분 무응답 후 적용 · 재파싱 루프가 방아쇠였을 가능성은 남음
- 최대 파일 파싱 피크
- 이전: 770,900kB에서 OOM kill이후: 427.8MiB
- 640MiB 한도의 67% · 스트리밍 파싱
- 알람 확인 경로 보완
- 알람 5종 멘션
- 3일간 47회 발화·사람 메시지 0건을 확인한 뒤 적용 · 공유 라우팅 무변경
문제 해결 과정
AI 서비스 사용이 늘면서 회사 자료가 외부에 공개·공유되는 요청을 실행 시점에 막고 기록해야 했고, macOS·Windows와 섞여 있는 구독 플랜 모두에서 동작해야 했습니다.
- 증상: 외부 공개·공유 요청을 막고 기록할 수단 없음
- 조치: 기기 데몬: 정책 위반 요청 차단 · 안내 · 감사 기록
- 조치: 가드 서버: 기기 등록 · 하트비트 · 서명 검증된 규칙만 적용
- 결과: 보고 스위치를 보고 중인 기기 87대 모두 집행(92명 배정)
기기 상주 데몬이 정책에 어긋나는 외부 공개·공유 요청을 차단하고, 사용자에게 안내 창을 띄우며 시도를 감사 기록으로 남기게 했습니다. 이어 가드 서버를 붙여 회사 계정 OAuth(PKCE)로 기기를 등록하고 하트비트와 차단 이벤트를 모았습니다. 서버는 규칙의 위치와 버전만 내려주고 데몬은 서명을 검증한 규칙만 적용하며, 등록이나 전송이 실패해도 로컬 차단은 멈추지 않습니다. 이 정책 배포 경로로 사용량 보고 스위치를 카나리 두 대에서 확인한 뒤 전역으로 켰습니다.
활성 명단 92명에게 보고 스위치를 배정했고, 보고 중인 온라인 기기 87대가 모두 집행했습니다. macOS·Windows 보호 상태 보고를 확인하고 설치 안내·운영 가이드·복구 절차를 인계했으며, 전사 순차 설치는 다른 팀이 맡습니다.
사내 NAS에서 AWS로 옮긴 수집 서버가 2일 18시간 31분 동안 완전히 무응답이었고 그동안 수집은 0건이었습니다. 인스턴스는 running이었고 오류 로그도, OOM kill도 없었습니다.
- 증상: 2일 18시간 31분 무응답 · 수집 0건
- 계측: sar: 메모리 62.90% → 81.66% · 페이지 스캔 153.77 → 332.58/s
- 기각: 디스크 · 하이퍼바이저 경합 · CPU 크레딧
- 원인: ClickHouse 기본 예산 RAM 90% · 공유 호스트 · swap 없음
- 조치: 예산 명시 · 큰 두 컨테이너에 mem_limit
- 결과: 한 축 차단 · 실제 방아쇠는 재파싱 루프였을 가능성
끊기기 직전 sar 표본에서 메모리 사용률 62.90%→81.66%, 페이지 스캔 153.77→332.58/s로 메모리 압박을 확인하고, 디스크·하이퍼바이저 경합·CPU 크레딧(장애 38시간 뒤 소진된 결과)을 근거와 함께 배제했습니다. 인스턴스 설정에서 ClickHouse가 기본값으로 RAM의 90%를 자기 예산으로 잡는 것을 찾았는데, 이 호스트는 RAM을 다른 컨테이너와 나눠 쓰고 swap이 없어 OOM kill 대신 스래싱에 갇힐 수 있는 구조였습니다. ClickHouse 예산을 호스트에 맞게 명시하고 큰 두 컨테이너에 mem_limit을 걸었습니다.
공유 호스트에서 기본 예산이 메모리를 독점하는 축을 막았습니다. 같은 증상의 다음 장애를 분석하며 이 장애의 실제 방아쇠도 재파싱 루프였을 가능성이 높다고 판단했고, 인스턴스 상향은 관측 전에는 잘못된 설정을 비용으로 덮는 일이라 보류했습니다.
메모리 한도를 건 뒤 수집 앱이 512m 한도에서 OOM 재시작을 19회 반복했고, 한도를 768m로 올린 뒤에는 호스트가 17시간 40분 동안 응답하지 않았습니다.
- 증상: OOM 재시작 19회 · 768m 상향 뒤 호스트 17시간 40분 정지
- 기각: 힙 상한 명시안 · 유휴 상태 241MiB 기준
- 계측: 미처리 청크가 있는 파일 통째 재파싱 · 매 사이클 254.3MB
- 원인: 파일 상한 미발화 · 워커 힙 2,048MB > 호스트 · 768m 한도가 호스트 스래싱
- 조치: 64MB·256MB·640m으로 하향 · 스트리밍 파싱
- 결과: 최대 파일 피크 427.8MiB(640MiB의 67%)
처음엔 재시작을 Node 힙 설정 문제로 보고 힙 상한을 명시하려 했지만, 넣기 전에 실측해 보니 Node 22는 cgroup 한도의 약 51%로 힙을 스스로 잡고 있어 그 안을 버렸습니다. 한도의 근거로 쓴 241MiB도 유휴 상태 값이었습니다. 재시작과 17시간 40분 정지는 같은 원인, 미처리 청크가 하나라도 있는 파일을 통째로 다시 읽는 무한 재파싱이었고, 512m에서는 이 루프에 컨테이너만 죽고 호스트가 버텼습니다. 176MB 파일에 미처리 청크 9개가 생긴 뒤로 이 파일을 포함해 매 사이클 45청크 254.3MB를 문자열 배열로 다시 올렸습니다. 512MB 파일 상한은 176MB 파일에 걸리지 않았고, 워커 힙 2,048MB는 호스트 RAM보다 커 의미가 없었으며, 유일하게 발화한 768m 컨테이너 한도는 죽을 때마다 페이지 캐시를 밀어내 호스트를 스래싱으로 끌고 갔습니다. 512m에서 768m으로 올린 제 변경이 컨테이너 재시작을 호스트 정지로 키운 것입니다. 파일 상한 64MB·워커 힙 256MB·컨테이너 640m으로 낮춰 루프를 끊고, 파싱을 청크 단위 스트리밍으로 바꾼 뒤 파일 상한을 256MB로 다시 올렸습니다.
최대 파일의 파싱 피크가 427.8MiB로 640MiB 한도의 67% 안에 머뭅니다. 미처리 청크 하나가 파일 전체를 다시 읽는 O(n²) 구조는 후속 과제로 분리했습니다.
17시간 40분 장애 동안 알람은 정상적으로 발화해 채널에 도달했지만 아무도 보지 않았고, 직전 2일 18시간 장애에서도 같은 일이 있었습니다.
- 증상: 최근 3일 알람 47회 발화 · 사람 메시지 0건
- 계측: 최근 3일 104건 중 봇 98건 · 멘션 없음
- 원인: 멘션 없는 채널 메시지는 모바일 푸시 없음
- 조치: 공유 라우팅·연락처는 그대로 두고 직접 관리하는 알람에서 해결
- 결과: 알람 5종 요약에 멘션 추가
알람 채널의 최근 3일 메시지 104건 중 98건이 봇이고 사람이 쓴 메시지는 0건이었으며, 47회 발화한 알람에 멘션이 하나도 없어 모바일 푸시가 가지 않았습니다. 라우팅과 연락처는 전사 공유 설정이라 건드리지 않고, 직접 관리하는 알람 규칙 5종의 요약 문구 앞에 멘션을 붙였습니다.
공유 설정을 바꾸지 않고 알람 5종의 요약 문구에 멘션을 추가했습니다.
운영 한계
프로젝트 설명
직원들이 AI 코딩 에이전트와 AI 서비스를 쓰기 시작하면서 두 가지가 필요했습니다. 회사 자료가 외부에 공개·공유되는 요청을 기기에서 막고 기록하는 가드, 그리고 개인 구독으로는 회사가 공식 대시보드를 볼 수 없는 Claude·Codex 사용량·스킬 지표를 모으는 수집 서버입니다. 가드는 macOS·Windows 상주 데몬과 가드 서버·콘솔로, 수집은 기기 사용 기록 동기화와 ClickHouse 집계로 만들었고, 사내 NAS에서 AWS로 옮긴 뒤 생긴 장애를 원인별로 정리했습니다. 구현은 Claude Code·Codex 같은 AI 코딩 에이전트로 진행했고, 설계와 운영 판단, 실기기 QA, 머지 승인은 직접 맡았습니다. 내부 서비스명·호스트·티켓 번호는 일반 명칭으로 바꿨고, 보안 정책의 대상·규칙·차단 방식은 공개 범위에서 뺐습니다.
주요 내용
- 가드의 정책 배포로 사용량 보고 스위치를 카나리 두 대 확인 뒤 전역 적용 — 활성 명단 92명 배정, 보고 중인 기기 87대 모두 집행
- 가드 서버는 규칙의 위치와 버전만 내려주고 데몬은 서명을 검증한 규칙만 적용, 등록·전송이 실패해도 로컬 차단 유지
- 2일 18시간 무응답: OOM kill 없는 스래싱 — 공유 호스트에서 RAM의 90%를 잡는 ClickHouse 기본 예산을 찾아 예산·컨테이너 한도를 명시, 실제 방아쇠는 다음 장애 분석에서 재파싱 루프였을 가능성이 높다고 판단
- 17시간 40분 정지: 파일 상한은 176MB 파일에 걸리지 않고 워커 힙 2,048MB는 호스트보다 컸으며 768m 한도는 죽을 때마다 호스트를 스래싱 — 64MB·256MB·640m으로 낮추고 스트리밍 파싱으로 최대 파일 피크 427.8MiB(한도의 67%)
기술 선택 근거
- ▶동기화한 기록이 기준: 기기의 사용 기록을 서버에 동기화해 두어 서버 장애 뒤에는 자연스럽게 백필되고, 재파싱으로 지표를 소급 계산할 수 있게 했습니다.
- ▶상한의 순서: 앱 자신의 힙 상한 < 컨테이너 한도 < 호스트 RAM. 뒤집히면 앱의 자기 제한이 한 번도 동작하지 못하고, 한도가 호스트를 흔들면 컨테이너 대신 호스트가 멈춥니다.
- ▶로컬 차단은 서버와 독립: 등록·하트비트 전송이 실패해도 기기의 차단은 멈추지 않고, 규칙의 서명 검증은 데몬이 합니다.
- ▶역할별 열람 분리: 설치 현황을 챙기는 IT 역할은 차단 이벤트를 볼 수 없게 해 기록의 열람 범위를 좁혔습니다.
깨달은 점
- •상한은 "얼마나 쓸 수 있나"와 함께 "죽을 때 호스트가 얼마를 잃나"로도 본다. 한도를 올린 내 변경이 컨테이너 재시작을 호스트 정지로 키웠다.
- •기본값에는 그 기본값을 정한 머신의 가정이 들어 있다. 서버를 옮기면 상한을 다시 계산한다.
- •한도의 근거가 되는 실측은 부하 상태에서 잰다. 유휴 상태 값으로 고른 한도는 금방 깨진다.