You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
vehicle-telemetry-platform/
├── simulator/ # 차량 데이터 시뮬레이터 (Python/C)
├── broker/ # Mosquitto MQTT 브로커 설정
├── kafka/ # Kafka 설정 및 토픽 초기화 스크립트
├── backend/ # Spring Boot API 서버
│ ├── src/
│ └── build.gradle
├── anomaly-detector/ # Python 이상 감지 모듈
├── monitoring/ # Grafana + Prometheus 설정
├── docs/ # 개발 일지 및 설계 문서
├── docker-compose.yml
├── .env.example
└── README.md
구현 단계 (Phase)
Phase
내용
상태
1
데이터 수집 파이프라인 (시뮬레이터 → MQTT → Kafka → InfluxDB)
완료
2
REST API 서버 (Spring Boot + JWT + Rate Limiting)
완료
3
이상 감지 (룰 기반 + Isolation Forest ML)
완료
4
보안 강화 (X.509 준비, BruteForce 차단, 감사 로그)
완료
5
모니터링 & 배포 (Grafana + Prometheus + Docker Compose)
완료
6
버그 픽스 (Actuator 인증 우회/정보 노출, 예외 처리 보강)
완료
7
Refresh Token + 로그아웃 무효화 (Redis 기반)
완료
8
데이터 파이프라인 안정성 (InfluxDB 동기 쓰기-수동 offset 연계, Kafka DLQ)
완료
9
AI 진단 (Gemini API)
완료
10
MQTT mTLS 실제 활성화 (기본 Compose는 8883/mTLS, 평문은 명시적 dev override만 허용)
완료
11
Flutter 모바일 앱 연동 — 브라우저(Flutter Web)에서 API 호출을 허용하는 CORS 지원 추가
완료
12
이상 감지 서비스(Python) 다중 인스턴스화 — 단일 인스턴스 lag 발산 문제에 Consumer Group 다중화 적용, 재검증에서 순발산 없음 확인(단, 동일 조건 A/B는 아직 — 향후 계획 참고)
완료(검증 계속 진행 중)
13
이상 감지 서비스 처리 신뢰성 — 수동 커밋(개수/시간 배치) + DLQ(vehicle-telemetry-anomaly-dlq) 도입, 발행 실패도 처리 실패로 감지
완료
14
ML 이상 감지 다중 인스턴스 대응 — 슬라이딩 윈도우(버퍼 상한) + 주기적 재학습, 파티션 ID 기준 Redis 영속화로 재시작/리밸런싱 시 학습 상태 유지
완료
15
보안/신뢰성 보강 — 검증된 ID 기반 Flux 생성+InfluxDB 2.7 계약 테스트, 신뢰 프록시, STOMP 구독 인가, 차량별 mTLS/ACL, 로컬 Kafka spool, Flyway
완료
향후 계획
항목
내용
AWS EC2 배포
Docker Compose 기반으로 실제 서버에 배포 (또는 Render 무료 티어)
다중 사용자 지원
현재 admin 단일 계정 → DB 기반 사용자 관리로 교체. 도입 시 차량 소유자 검증(IDOR 차단)도 함께 필요
DLQ 재처리 컨슈머
현재 DLQ는 유실 방지/격리까지만 — 재처리 자동화는 미구현
차량별 MQTT 인증서/ACL 세분화
완료 — backend는 telemetry-backend 구독자 인증서, 차량은 CN=vehicle_id 인증서와 vehicle/telemetry/%u 발행 ACL 사용
InfluxDB 읽기 경로 개선
REST 조회(/latest, /telemetry)가 InfluxDB 동시 쿼리 용량에 막혀 PostgreSQL 조회보다 최대 40배 느림 — 스케일링/캐싱/쿼리 최적화 필요
InfluxDB batchSize/flush 튜닝, acks=all vs acks=1 비교
부하 테스트 범위에서 비교 측정하지 못함
kafka-python 버전 고정
anomaly-detector/requirements.txt가 >=2.0.2로만 열려 있어 재빌드 시마다 버전이 달라질 수 있음(재현성)
이상 감지 다중화 동일 조건 A/B 재검증
Phase 12 재측정은 부하·관찰시간·라이브러리 버전이 before/after 사이에 달랐다(코덱스 리뷰로 발견) — 동일 이미지·동일 부하·동일 시간으로 1인스턴스 vs 3인스턴스 최소 1시간 비교 + 3인스턴스 12~24시간 soak test 필요
DLQ 격리 메시지 재처리
vehicle-telemetry-anomaly-dlq(Phase 13에서 추가)도 기존 DLQ들과 마찬가지로 격리까지만 하고 재처리 컨슈머는 없음
이상 감지 웹훅 동기 호출
notifier.send_webhook이 이상 감지마다 동기로 호출됨 — 느려지면 전체 처리량에 영향 줄 수 있어 비동기화/타임아웃 튜닝 검토
Refresh Token은 Redis GETDEL 기반 rotation과 로그아웃 폐기를 지원한다. Access Token은 10분 만료의
stateless JWT이며 별도 jti 블랙리스트는 두지 않는다. 즉시 강제 로그아웃이 요구되면 jti 블랙리스트를 추가한다.
장애 시나리오 및 동작
실제 운영에서 발생할 수 있는 장애 상황별로 시스템이 어떻게 동작하는지 정리했다.
시나리오 1 — Kafka 브로커 다운
단계
동작
장애 발생
Spring Boot TelemetryProducer의 kafkaTemplate.send() 실패
즉각 영향
차량 데이터가 즉시 InfluxDB/이상 감지로 전달되지 않음
MQTT 수신
Mosquitto는 독립적으로 계속 동작. 데이터는 Spring Boot까지 도달
복구 시
전송 전 로컬 volume에 기록한 spool을 Kafka ACK 후 삭제하며, 재연결 시 보류 파일부터 재전송
미구현 한계
spool volume 자체가 손실되면 복구 불가. 운영에서는 디스크 사용량 경보와 HA 수집 계층 필요
시나리오 2 — Python 이상 감지 서비스 다운
단계
동작
장애 발생
anomaly-detector 컨테이너 종료
즉각 영향
이상 감지 중단, Webhook 알림 중단
데이터 파이프라인
telemetry-storage-group은 별도 Consumer Group이므로 InfluxDB 저장은 영향 없이 계속됨
Kafka 메시지
anomaly-detector-group offset이 멈춘 상태로 유지 — 재시작 시 밀린 메시지부터 재처리
복구 시
docker-compose restart anomaly-detector 후 자동으로 밀린 메시지 처리 시작
Consumer Group 분리의 핵심 이점: 저장 경로와 이상 감지 경로가 독립적이므로 한 쪽 장애가 다른 쪽에 전파되지 않는다.
시나리오 3 — MQTT 브로커(Mosquitto) 재시작
단계
동작
장애 발생
Mosquitto 컨테이너 재시작
Spring Boot
MqttPahoMessageDrivenChannelAdapter의 automaticReconnect=true 설정으로 자동 재연결 시도
시뮬레이터
paho-mqtt의 재연결 로직으로 자동 재구독
재연결 소요 시간
connectionTimeout=10s, keepAliveInterval=60s 기준 수 초 내 복구
재연결 중 데이터
연결이 끊긴 사이 시뮬레이터가 발행한 메시지는 유실 (QoS 1 기준, 브로커 재시작이므로 세션 복원 불가)
시나리오 4 — InfluxDB 쓰기 실패
단계
동작
장애 발생
InfluxDB 응답 불가 또는 쓰기 타임아웃
동작
동기 WriteApiBlocking 실패를 listener가 잡아 원본을 DLQ로 발행
Kafka offset
InfluxDB 쓰기 또는 DLQ 발행 성공 뒤에만 수동 커밋. DLQ 발행도 실패하면 미커밋
로그
저장 실패와 DLQ 실패를 서로 다른 ERROR 로그로 기록
미구현 한계
DLQ 자동 재처리 consumer와 재시도 횟수 정책은 아직 없음
시나리오 5 — Redis 다운 (Rate Limiting / BruteForce)
단계
동작
장애 발생
Redis 연결 불가
Rate Limiting
redisTemplate.opsForValue().increment() 예외 발생 → 요청이 preHandle()에서 터짐
영향 범위
Rate Limiting과 BruteForce 감지가 비활성화되는 게 아니라 API 전체가 500 응답. Refresh Token(Phase 7)도 Redis에 저장되므로 재로그인(로그인 자체는 영향 없음, 재발급만 불가)도 함께 영향받음
운영 개선 방향
Redis 장애 시 Rate Limiting을 bypass하도록 try-catch 추가 고려 (가용성 vs 보안 트레이드오프)
성능 (실측)
로컬 Docker Compose 환경(2026-07~08)에서 부하 테스트로 측정. 상세 방법론·전체 표는
부하 테스트 계획 및 결과 참고.
수집 파이프라인: 시뮬레이터를 3→1,000대까지 스케일해 측정. 단일 프로세스로는 Python
GIL/스레드 오버헤드로 약 1,000-1,250 msg/s에서 먼저 벽에 부딪혀 백엔드의 진짜 한계를 잴 수
없었다 — 부하 생성기를 여러 프로세스(별도 GIL)로 병렬 실행하도록 개선해 ~2,500 msg/s를
약 24시간 지속시킨 결과, Kafka Consumer Group으로 분리해둔 두 경로 중 이상 감지(Python,
단일 인스턴스) 쪽만 lag이 1,264 → 200만 건 이상으로 폭주했고, 저장 경로(Java→InfluxDB)는
같은 시간 내내 lag 수백 단위로 버텼다. 이 시스템의 진짜 첫 확장 병목은 Kafka도 InfluxDB도
아니라 단일 인스턴스로 도는 이상 감지 서비스였다 — Consumer Group 분리 설계(ADR-002)가
장애(여기선 성능 저하) 전파를 실제로 막아준 것도 함께 확인.
이상 감지 서비스 다중화 — 12시간 soak test로 검증 완료: docker-compose.yml의
container_name 고정을 제거하고 deploy.replicas: 3으로 파티션 수(3)에 맞춰 3개 인스턴스를
띄웠다. 동일 이미지·동일 부하(~2,300-2,700 msg/s)·시작 lag 0 조건의 1시간 A/B에서 1인스턴스는
30분 만에 lag 140만대까지 치솟는 반면 3인스턴스는 낮게 유지됨을 먼저 확인했고, 이어서
3인스턴스 구성으로 12시간(43,200초) soak test를 중단 없이 완주해 lag이 최대 100(평균 27.4,
500 초과 0건)으로 장시간 안정적임을 확정했다 — 자세한 내용은
docs/architecture-decisions.md ADR-016, 원시 로그는 load-test/anomaly-detector-scale/.
12시간 soak 중 InfluxDB 저장 무음 유실 버그 발견(원인 미해결): 위 soak test 도중
원시 텔레메트리(speed 등)의 InfluxDB 기록이 시작 26초 만에 완전히 멈춰 이후 12시간 동안
단 1건도 저장되지 않은 것을 발견했다. 이 구간 내내 telemetry-storage-group의 Kafka lag은
낮게 유지돼 정상처럼 보였고, backend 로그에도 에러가 전혀 없었다(무음 실패) — lag 기반
모니터링만으로는 잡을 수 없는 유형의 저장 유실이다. 기존에 고쳤던 초 단위 타임스탬프 충돌
버그(ADR-014, WritePrecision.S)의 재발은 아님을 코드로 확인했으나 근본 원인은 아직
못 찾았다 — 별도 이슈로 남겨둠.
데이터 유실 버그 발견·수정: InfluxDB WritePrecision.S(초 단위)와 시뮬레이터의 초 단위
타임스탬프가 겹쳐, 차량당 초당 2회 조건에서 같은 초의 메시지가 서로 덮어써 50%가
조용히 유실되고 있었다(Kafka lag은 0으로 정상처럼 보임). 밀리초 정밀도로 수정해
약 100 msg/s → 약 197 msg/s(목표의 98.5%)로 회복.
REST API: k6로 VU 200까지 부하 테스트. InfluxDB 기반 조회(/telemetry/latest, /telemetry)의
p95가 3.7~3.8초까지 늘어나는 것을 확인 — 클라이언트의 동시 요청 한도를 넓혀봤다가 오히려
InfluxDB 자체가 타임아웃을 뱉는 것을 보고 진짜 병목이 InfluxDB의 동시
쿼리 처리 용량임을 역으로 검증(PostgreSQL 기반 /anomalies는 같은 부하에서 p95 95ms로 40배 빠름).
Rate Limit: 분당 60회 제한이 정확히 61번째 요청부터 429를 반환하는 것을 순차 요청으로 검증.
# 1. 환경변수 설정
cp .env.example .env
# .env 파일 편집 (GEMINI_API_KEY는 선택 — 없으면 AI 진단 기능만 동작 안 함, 나머지는 정상)# 2. 전체 스택 실행
docker-compose up -d
# 3. 시뮬레이터 실행cd simulator
python vehicle_simulator.py
Real-time vehicle telemetry collection & monitoring platform. Features IoT data streaming via MQTT/Kafka, ML/Rule-based anomaly detection, and real-time visualization. Built with Spring Boot, InfluxDB, and designed for automotive cybersecurity standards.