Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,15 @@ npm --prefix frontend run dev
- API 문서: `http://localhost:8080/swagger-ui/index.html`
- OpenAPI JSON: `http://localhost:8080/v3/api-docs`

Backend와 Embedding Provider의 운영 메트릭을 함께 수집하려면 선택형 monitoring profile을 실행합니다.

```bash
docker compose --profile monitoring up -d prometheus
```

- Prometheus Target: `http://localhost:9090/targets`
- 설정과 기존 Prometheus 연결: [monitoring/prometheus/README.md](monitoring/prometheus/README.md)

### 6. 핵심 흐름 확인

1. 회원가입 또는 로그인
Expand Down
4 changes: 4 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,13 @@ services:
- "--storage.tsdb.retention.time=${PROMETHEUS_RETENTION:-7d}"
ports:
- "127.0.0.1:${PROMETHEUS_PORT:-9090}:9090"
# Host에서 bootRun으로 실행한 Backend Management 포트를 Linux에서도 같은 이름으로 찾는다.
extra_hosts:
- "host.docker.internal:host-gateway"
volumes:
- ./monitoring/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
- ./monitoring/prometheus/rules:/etc/prometheus/rules:ro
- ./monitoring/prometheus/targets:/etc/prometheus/targets:ro
- prometheus-data:/prometheus
depends_on:
embedding-server:
Expand Down
61 changes: 61 additions & 0 deletions docs/design/gimin-#330-prometheus-backend-availability.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Prometheus Backend 수집과 가용성 경보 설계

- 관련 이슈: #330
- 선행 조건: #328의 Backend Management endpoint
- 대상: 번들 Prometheus와 기존 Prometheus 사용 환경

## 문제

Backend가 `/actuator/prometheus`를 제공해도 Prometheus scrape job이 없으면 시계열은 저장되지 않는다.
현재 번들 Prometheus는 Docker network 안의 Embedding Provider만 수집하며 Spring Backend는 Host에서
`bootRun`으로 실행한다. 컨테이너에서 Host Management 포트로 접근하는 경로와 Backend의 기본
가용성 경보가 필요하다.

## 수집 경계

번들 Prometheus는 `file_sd_configs`로
`monitoring/prometheus/targets/docgrid-backend.yml`을 읽는다. 기본 target은
`host.docker.internal:8081`이며 `environment=local`, `cluster=docgrid-local` label을 붙인다.

Compose의 `extra_hosts`는 Linux에서 `host.docker.internal`을 Host gateway로 연결한다. Docker
Desktop도 같은 이름을 지원하므로 운영체제에 따라 Prometheus 본체 설정을 나누지 않는다. 다른
Host·Port나 production label은 target 파일에서만 변경한다.

기존 Prometheus를 사용하는 환경은 같은 `job=docgrid-backend`와
`metrics_path=/actuator/prometheus` 계약을 사용한다. Management 포트의 네트워크 접근 제한은 배포
환경의 방화벽, Security Group, 컨테이너 network가 담당한다.

## 경보

| 경보 | 조건 | `for` | 목적 |
|---|---|---:|---|
| `DocGridBackendDown` | `up == 0` | 1분 | 일시적인 scrape 실패와 실제 중단 구분 |
| `DocGridDatabasePoolSaturated` | HikariCP active/max > 90% | 2분 | 지속적인 DB pool 고갈 조기 감지 |
| `DocGridBackendHighServerErrorRatio` | 5분간 20건 이상, 5xx > 5% | 3분 | 저트래픽 단일 오류의 오탐 방지 |

모든 경보에는 `service=docgrid-backend`와 severity를 붙인다. Backend target의 `cluster`와
`environment` label은 경보 시계열에 유지돼 이후 Alertmanager routing에 사용할 수 있다.

HTTP 오류율은 `sum by (cluster, environment)`로 인스턴스 전체를 집계한다. 분자와 분모 모두
`uri!="/mcp"`를 사용한다. MCP Streamable HTTP 요청은 일반 REST와 응답 특성이 달라 별도 오류
예산 없이 일반 API 경보에 합치지 않는다.

## 검증 전략

`promtool check config`와 `check rules`로 syntax와 rule loading을 확인한다. `promtool test rules`는
다음 네 경계를 고정한다.

1. Backend down이 1분 전에는 firing하지 않는다.
2. HikariCP 포화가 2분 유지돼야 firing한다.
3. 5xx 비율이 높아도 최소 요청량을 만족한 cluster만 경보한다.
4. `/mcp` 5xx는 일반 Backend 오류율 경보를 만들지 않는다.

실제 E2E에서는 Host Backend와 격리 Prometheus를 기동하고 Embedding Provider와 Backend가 모두
`UP`인지 확인한다. Backend를 중단해 target `DOWN`, 경보 `pending`, `firing`을 순서대로 확인한 뒤
재기동해 target `UP`과 경보 `inactive` 복구까지 검증한다.

## 운영 경계

현재 경보는 Prometheus에서만 평가하고 화면에서 확인한다. Alertmanager 전달, 알림 그룹화, silence,
외부 채널은 후속 작업에서 추가한다. Job·RAG·Outbox의 도메인 상태와 Queue 정체도 이 변경의
기본 인프라 경보에는 포함하지 않는다.
40 changes: 40 additions & 0 deletions docs/runbooks/backend-down.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# DocGrid Backend Down

## 의미

`DocGridBackendDown`은 Prometheus가 `docgrid-backend` target의 `/actuator/prometheus`를 1분 동안
수집하지 못했을 때 발생한다. Backend 프로세스 중단 외에도 Management 포트, 네트워크 경로,
Prometheus target 설정 오류가 원인일 수 있다.

## 확인

1. Prometheus `/targets`에서 `docgrid-backend`의 마지막 오류와 마지막 성공 시간을 확인한다.
2. Prometheus가 실행되는 위치에서 Backend Management endpoint에 접근한다.

```bash
curl -f http://docgrid-backend.internal:8081/actuator/health/liveness
curl -f http://docgrid-backend.internal:8081/actuator/prometheus
```

3. Backend 프로세스와 최근 종료 원인, OOM, 배포 이벤트를 확인한다.
4. Backend 로그에서 시작 실패, DB 연결 실패, 포트 충돌을 확인한다.
5. target 주소, Management 포트, 방화벽과 Security Group 변경을 확인한다.

로컬 Compose에서는 `docgrid-backend.internal` 대신 `host.docker.internal`을 사용한다.

## 복구

1. Backend가 중단됐다면 정상 배포 절차로 재시작한다.
2. 시작 실패라면 로그에 나온 설정·DB·포트 원인을 수정한 뒤 다시 시작한다.
3. Backend는 정상인데 scrape만 실패하면 target 파일과 Prometheus에서 Host까지의 네트워크 경로를
복구한다.
4. `/actuator/health/liveness`와 `/actuator/prometheus`가 모두 200인지 확인한다.
5. Prometheus `/targets`에서 `docgrid-backend`가 `UP`으로 돌아오는지 확인한다.
6. `/alerts`에서 `DocGridBackendDown`이 inactive로 전환됐는지 확인한다.

## 복구 완료 조건

- Backend liveness와 Prometheus endpoint가 200을 반환한다.
- `up{job="docgrid-backend"}` 값이 `1`이다.
- `DocGridBackendDown`이 inactive 상태다.
- 사용자 API의 정상 요청이 성공한다.
90 changes: 90 additions & 0 deletions docs/test-results/gimin-#330-prometheus-backend-availability.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# Prometheus Backend 수집과 가용성 경보 검증 결과

- 관련 이슈: #330
- 실행 일시: 2026-09-13 (Asia/Seoul)
- 수정 전 기준 Commit: `9b0f701` (`develop`)
- 환경: Prometheus·promtool 3.5.5, Spring Boot 3.5.16, PostgreSQL 17.8, Docker Desktop

## 검증 목적

- Docker의 Prometheus가 Host에서 실행한 Backend Management 포트를 수집해야 한다.
- 기존 Embedding Provider target은 변경 후에도 정상이어야 한다.
- Backend 중단이 1분 동안 지속될 때만 `DocGridBackendDown`이 firing해야 한다.
- Backend 복구 뒤 target과 경보가 정상 상태로 돌아와야 한다.
- DB pool과 HTTP 5xx 규칙의 지속 시간, 최소 표본, MCP 제외 조건을 단위 테스트로 고정해야 한다.

## 설정과 규칙 검증

고정된 `prom/prometheus:v3.5.5` 이미지의 promtool을 사용했다.

| 검증 | 결과 |
|---|---|
| `promtool check config` | 성공, rule file 2개 로드 |
| `promtool check rules` | 성공, Backend 3개·Embedding 7개 규칙 |
| `promtool test rules` | 성공, 4개 시나리오 |
| `docker compose config` | 성공 |
| `docker compose --profile monitoring config` | 성공, host gateway와 target mount 포함 |

규칙 테스트는 Backend down 1분, HikariCP 포화 2분, 5xx 최소 20건과 5% 초과, `/mcp` 제외를
검증했다. 낮은 요청량 cluster는 같은 10% 오류율에서도 경보가 발생하지 않았다.

Prometheus 이미지의 기본 entrypoint가 `prometheus`이므로 문서의 검증 명령은
`--entrypoint=promtool`로 명시했다. 실제 컨테이너 실행으로 이 명령 형식까지 확인했다.

## 실제 수집

Backend는 Host의 `8081` Management 포트에서 실행하고, 검증용 Prometheus는 기존 인스턴스를
건드리지 않도록 `19090`에 격리했다. Prometheus 컨테이너는 기존 `docgrid_docgrid-local` network와
Host gateway를 함께 사용했다.

첫 scrape 이후 target 상태는 다음과 같았다.

```text
docgrid-backend host.docker.internal:8081 local docgrid-local up
embedding-provider embedding-server:8000 up
```

Backend target에는 target 파일의 `environment=local`, `cluster=docgrid-local` label이 적용됐고
기존 Embedding Provider도 계속 `UP`이었다.

## Backend 중단과 복구

| 시각 | 관찰 결과 |
|---|---|
| 17:36:36 | Backend 종료 완료 |
| 17:36:58 | `DocGridBackendDown` activeAt, 상태 `pending` |
| 17:37:03 | target `down`, `connection refused` 기록 |
| 17:38:32 | 경보 `firing`, 값 `0` 확인 |
| 17:38:45 | Backend 재기동 완료 |
| 17:39:03 | target `up`, scrape 오류 없음 |
| 17:39:22 | `DocGridBackendDown` 조회 결과 0건, `inactive` 확인 |

중단 후 첫 실패 scrape와 rule 평가 주기 안에 pending으로 들어갔고, activeAt부터 설정한 1분이 지난
뒤 firing했다. Backend 재기동 후 첫 성공 scrape와 다음 평가에서 경보가 사라졌다.

## 실행 명령

```bash
docker run --rm --entrypoint=promtool \
-v "$PWD/monitoring/prometheus:/etc/prometheus:ro" \
prom/prometheus:v3.5.5 \
check config /etc/prometheus/prometheus.yml

docker run --rm --entrypoint=promtool \
-v "$PWD/monitoring/prometheus:/etc/prometheus:ro" \
prom/prometheus:v3.5.5 \
check rules \
/etc/prometheus/rules/embedding-provider-alerts.yml \
/etc/prometheus/rules/docgrid-backend-alerts.yml

docker run --rm --entrypoint=promtool \
-v "$PWD/monitoring/prometheus:/etc/prometheus:ro" \
prom/prometheus:v3.5.5 \
test rules /etc/prometheus/tests/docgrid-backend-alerts.test.yml

docker compose config
docker compose --profile monitoring config
```

격리 Prometheus와 Backend는 검증 후 종료했고, 테스트 전에 정지 상태였던 PostgreSQL도 다시
중지했다.
94 changes: 94 additions & 0 deletions monitoring/prometheus/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# DocGrid Prometheus

DocGrid가 함께 제공하는 Prometheus는 Host에서 실행한 Spring Backend와 Docker Compose의 Embedding
Provider를 수집한다. 기본 scrape 주기와 rule 평가 주기는 15초다.

## 번들 Prometheus 사용

Backend를 먼저 실행한다.

```bash
./backend/gradlew -p backend bootRun
```

Management endpoint는 기본적으로 Host의 `8081` 포트에서 열린다. 그다음 monitoring profile을
실행한다.

```bash
docker compose --profile monitoring up -d prometheus
```

Prometheus는 컨테이너에서 `host.docker.internal:8081`을 수집한다. Compose의 `extra_hosts`가
Linux의 host gateway를 같은 이름으로 연결하며 Docker Desktop도 같은 주소를 지원한다.

- Target 상태: <http://localhost:9090/targets>
- Alert 상태: <http://localhost:9090/alerts>

기본 Backend target과 배포 식별 label은
`monitoring/prometheus/targets/docgrid-backend.yml`에서 변경한다.

```yaml
- targets:
- host.docker.internal:8081
labels:
environment: local
cluster: docgrid-local
```

## 기존 Prometheus 사용

기존 Prometheus를 운영하는 환경에서는 다음 scrape job을 해당 Prometheus 설정에 추가한다.

```yaml
scrape_configs:
- job_name: docgrid-backend
metrics_path: /actuator/prometheus
static_configs:
- targets:
- docgrid-backend.internal:8081
labels:
environment: production
cluster: docgrid-production
```

`8081`은 사용자 API 포트가 아닌 Management 포트다. 방화벽, Security Group, 컨테이너 network로
Prometheus와 운영자만 접근하도록 제한한다.

Backend 경보를 함께 사용하려면 `monitoring/prometheus/rules/docgrid-backend-alerts.yml`을 기존
Prometheus의 `rule_files` 경로에 복사한다.

## 기본 Backend 경보

| 경보 | 조건 | 지속 시간 |
|---|---|---:|
| `DocGridBackendDown` | Backend scrape 실패 | 1분 |
| `DocGridDatabasePoolSaturated` | HikariCP active/max가 90% 초과 | 2분 |
| `DocGridBackendHighServerErrorRatio` | 5분간 20건 이상이며 5xx가 5% 초과 | 3분 |

HTTP 오류율에서는 Streamable HTTP 특성이 다른 `/mcp`를 제외한다. 이 경보들은 현재 Prometheus
화면에서 확인하며 외부 전달은 Alertmanager 설정을 추가한 뒤 활성화된다.

## 설정 검증

로컬에 promtool을 설치하지 않아도 고정된 Prometheus 이미지로 검사할 수 있다.

```bash
docker run --rm --entrypoint=promtool \
-v "$PWD/monitoring/prometheus:/etc/prometheus:ro" \
prom/prometheus:v3.5.5 \
check config /etc/prometheus/prometheus.yml

docker run --rm --entrypoint=promtool \
-v "$PWD/monitoring/prometheus:/etc/prometheus:ro" \
prom/prometheus:v3.5.5 \
check rules \
/etc/prometheus/rules/embedding-provider-alerts.yml \
/etc/prometheus/rules/docgrid-backend-alerts.yml

docker run --rm --entrypoint=promtool \
-v "$PWD/monitoring/prometheus:/etc/prometheus:ro" \
prom/prometheus:v3.5.5 \
test rules /etc/prometheus/tests/docgrid-backend-alerts.test.yml

docker compose config
```
7 changes: 7 additions & 0 deletions monitoring/prometheus/prometheus.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ rule_files:
- /etc/prometheus/rules/*.yml

scrape_configs:
- job_name: docgrid-backend
metrics_path: /actuator/prometheus
file_sd_configs:
# Host 실행과 외부 배포의 차이는 target 파일에서만 관리해 본체 설정을 공유한다.
- files:
- /etc/prometheus/targets/docgrid-backend.yml

- job_name: embedding-provider
metrics_path: /metrics
static_configs:
Expand Down
Loading