<aside> 📌
B:SCENE의 인프라는 Docker Compose로 오케스트레이션되는 컨테이너 기반 마이크로 생태계로 설계되어 있으며, 스트리밍 안정성을 핵심 목표로 삼습니다.
내부 서비스(Redis, Elasticsearch, MediaMTX API 등)는 Docker 네트워크 안에서만 노출되며 호스트에서 직접 접근할 수 없습니다. Let's Encrypt에서 발급된 SSL/TLS 인증서는 6시간마다 재적용됩니다. MediaMTX는 인증을 Spring의 내부 엔드포인트에 위임합니다.
B:SCENE 인프라의 모든 결정은 단일 서버라는 제약 안에서 실시간 오디오 스트리밍을 안정적으로 돌린다는 하나의 제약에서 출발합니다. 아래는 그 제약이 각 기술 선택으로 이어진 과정입니다.
B:SCENE 인프라는 bscene이라는 커스텀 브리지 네트워크 위에서 동작하며, 모든 서비스의 타임존은 Asia/Seoul(KST)로 설정되어 있습니다. 하나의 서버에서 8개의 상시 컨테이너가 동작하므로, 각 서비스는 자신이 쓸 수 있는 자원과 외부에 드러낼 포트를 명시적으로 제한받습니다.
| 정책 | 설정 | 이유 |
|---|---|---|
| 로그 로테이션 | x-logging YAML 앵커로 json-file 드라이버, max-size 10m × max-file 3 (컨테이너당 최대 30MB) |
Docker 기본값은 로그 무제한 증가라 단일 서버에서는 디스크 고갈로 이어집니다. 앵커로 선언해 서비스가 늘어나도 정책이 한 곳에서 관리됩니다. |
| 재시작 | 모든 서비스 restart: always |
서버 재부팅·프로세스 비정상 종료 시 수동 개입 없이 복구되도록 합니다. |
| 타임존 | TZ=Asia/Seoul |
로그 타임스탬프, 녹화 파일명, S3 키 접두사가 모두 KST로 일치해야 장애 추적 시 시간을 환산할 필요가 없습니다. |
| 포트 노출 | 외부 공개는 nginx(80/443), mediamtx(8189/udp), node-exporter(9100), spring 관리 포트(8081)만. 나머지는 expose |
ports와 expose를 엄격히 구분해, 데이터·제어계를 물리적으로 외부에서 닿지 못하게 합니다. |
| 리소스 배분 | mixer 1.0 CPU/512M, ES 1G, redis 0.5/256M, mediamtx 0.5/256M | 한 컨테이너의 폭주가 같은 호스트의 API 응답을 죽이는 것을 막기 위해, 예측 불가능한 소비를 하는 서비스에 상한을 걸었습니다. |
| 볼륨 이름 | 마운트 경로 | 용도 |
|---|---|---|
| recordings | /recordings | MediaMTX와 Spring이 공유하는 fmp4 세그먼트 처리용 볼륨입니다. |
| es-data | /usr/share/elasticsearch/data | Elasticsearch 인덱스 영속화용 볼륨입니다. |
| redis-data | /data | Redis AOF 지속성용 볼륨입니다. |
| promtail-positions | /positions | promtail이 어디까지 읽었는지 offset을 기록해, 재시작 후 로그 중복·유실을 막는 볼륨입니다. |
외부에서 설정 파일을 넣어주는 바인드 마운트(nginx/conf.d, mediamtx/mediamtx.yml, promtail/promtail-config.yml, ./certs)는 모두 :ro(읽기 전용)로 잡아, 컨테이너가 호스트의 설정을 덮어쓰지 못하게 합니다.
GitHub Actions 워크플로와 Flyway 기반 스키마 진화를 결합하여 자동화된 배포를 구현합니다. CI 워크플로는 pull request와 main 브랜치 push 시점에 트리거됩니다.
Flyway가 MySQL 대상 스키마 변경을 관리하며 V1부터 V53까지의 마이그레이션이 존재합니다. 운영 환경에서는 Hibernate의 ddl-auto를 validate로 설정하여 Flyway가 스키마의 유일한 권한 소스가 되도록 강제하고, 개발 환경에서는 빠른 반복을 위해 update 모드를 사용합니다.
테스트 스위트는 MySQL 호환 모드(MODE=MySQL)의 H2 인메모리 데이터베이스를 사용합니다. 테스트 실행 시 Flyway는 비활성화되고(mode: never), 대신 Hibernate가 ddl-auto: create-only로 스키마를 관리합니다. 커스텀 Elasticsearch 9.4.3 이미지에는 한국어 처리를 위한 analysis-nori 플러그인이 번들로 포함되어 있습니다.
ML 서버는 밴드 간 콘텐츠 기반 유사도를 계산하는 Python 기반 서브시스템입니다. 밴드 설명을 NLP로 가공하여 Spring Boot 추천 시스템이 사용하는 유사도 행렬을 생성합니다.
| 단계 | 동작 | 설명 |
|---|---|---|
| 1 | CREATE TABLE ... LIKE | 운영 테이블과 동일한 스키마로 band_similarity_staging 테이블을 생성합니다(106~113번째 줄). |
| 2 | INSERT | 계산된 유사도 쌍을 staging 테이블에 대량 삽입합니다(116~133번째 줄). |
| 3 | RENAME TABLE | band_similarity → band_similarity_old, band_similarity_staging → band_similarity로 원자적으로 스왑합니다(136~149번째 줄). |
| 4 | DROP TABLE | 이전 데이터를 삭제합니다(192~200번째 줄). |
모델은 jhgan/ko-sroberta-multitask(Dockerfile 16번째 줄)이며, 한국어 멀티태스크 학습에 최적화되어 짧은 설명 텍스트에서도 견고한 의미 표현을 제공합니다. Docker 빌드 시점에 다운로드되어 /hf-cache에 캐시되고, 런타임에는 HF_HUB_OFFLINE=1로 오프라인 모드로 동작합니다(25~27번째 줄).
| 설정 키 | 설명 |
|---|---|
| DB_HOST / DB_USER / DB_PASSWORD / DB_NAME | MySQL 접속 자격 증명입니다. |
| BAND_TABLE | 소스 테이블이며 값은 band입니다. |
| BAND_SIMILARITY_TABLE | 대상 라이브 테이블이며 값은 band_similarity입니다. |
| SIMILARITY_THRESHOLD | 최소 코사인 유사도 값이며 기본값은 0.55입니다. |
| TOP_K | 밴드당 최대 유사 밴드 개수이며 기본값은 10입니다. |
Spring 애플리케이션은 BandSimilarityRepository를 통해 데이터를 소비합니다(BandRecommendationServiceV2Impl.java 125번째 줄).