diff --git a/docker-compose.yaml b/docker-compose.yaml index 77c00294..5fdc2d3a 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -131,6 +131,7 @@ services: volumes: - ./config:/config depends_on: + - fluent-bit - redis - spot-user - spot-store @@ -155,6 +156,7 @@ services: volumes: - ./config:/config depends_on: + - fluent-bit - db - redis - kafka @@ -179,11 +181,17 @@ services: volumes: - ./config:/config depends_on: + - fluent-bit - db - redis - kafka networks: - spot-network + logging: + driver: fluentd + options: + fluentd-address: 127.0.0.1:24224 + tag: spot-payment spot-store: build: @@ -199,6 +207,7 @@ services: volumes: - ./config:/config depends_on: + - fluent-bit - db - redis networks: @@ -220,6 +229,7 @@ services: volumes: - ./config:/config depends_on: + - fluent-bit - db - redis networks: diff --git a/observability/fluent-bit/fluent-bit.conf b/observability/fluent-bit/fluent-bit.conf new file mode 100644 index 00000000..97629bc3 --- /dev/null +++ b/observability/fluent-bit/fluent-bit.conf @@ -0,0 +1,18 @@ +[SERVICE] + Flush 1 + Daemon Off + Log_Level info + +[INPUT] + Name forward + Listen 0.0.0.0 + Port 24224 + +[OUTPUT] + Name loki + Match * + Host loki + Port 3100 + Labels job=fluent-bit + Line_Format json + diff --git a/observability/grafana/dashboards/loki.json b/observability/grafana/dashboards/loki.json new file mode 100644 index 00000000..c362752a --- /dev/null +++ b/observability/grafana/dashboards/loki.json @@ -0,0 +1,89 @@ +{ + "uid": "loki-poc", + "title": "Loki Logs - POC", + "timezone": "browser", + "schemaVersion": 38, + "version": 1, + "refresh": "10s", + "tags": ["loki", "logs", "poc"], + "time": { "from": "now-15m", "to": "now" }, + "templating": { + "list": [ + { + "name": "service", + "type": "query", + "datasource": { "type": "loki", "uid": "loki-thomas-vitale" }, + "refresh": 1, + "query": "label_values({job=\"fluent-bit\"}, container_name)", + "includeAll": true, + "allValue": ".*", + "multi": false, + "current": { "text": "All", "value": "$__all" } + }, + { + "name": "level", + "type": "custom", + "query": "ALL,ERROR,WARN,INFO,DEBUG", + "current": { "text": "ALL", "value": "ALL" } + } + ] + }, + "annotations": { "list": [] }, + "panels": [ + { + "type": "timeseries", + "title": "Error logs / min", + "id": 1, + "datasource": { "type": "loki", "uid": "__default__" }, + "gridPos": { "x": 0, "y": 0, "w": 12, "h": 8 }, + "targets": [ + { + "refId": "A", + "expr": "sum by (container_name) (rate({job=\"fluent-bit\", container_name=~\"$service\"} |~ \"(?i)error\" [1m]))", + "legendFormat": "{{container_name}}" + } + ], + "options": { + "legend": { "showLegend": true, "displayMode": "list", "placement": "bottom" }, + "tooltip": { "mode": "multi" } + } + }, + { + "type": "bargauge", + "title": "Top services by ERROR (last 15m)", + "id": 2, + "datasource": { "type": "loki", "uid": "__default__" }, + "gridPos": { "x": 12, "y": 0, "w": 12, "h": 8 }, + "targets": [ + { + "refId": "A", + "expr": "topk(10, sum by (container_name) (count_over_time({job=\"fluent-bit\", container_name=~\"$service\"} |~ \"(?i)error\" [15m])))", + "legendFormat": "{{container_name}}" + } + ], + "options": { + "displayMode": "basic", + "reduceOptions": { "calcs": ["lastNotNull"], "values": false } + } + }, + { + "type": "logs", + "title": "Recent logs", + "id": 3, + "datasource": { "type": "loki", "uid": "__default__" }, + "gridPos": { "x": 0, "y": 8, "w": 24, "h": 12 }, + "targets": [ + { + "refId": "A", + "expr": "{job=\"fluent-bit\", container_name=~\"$service\"}" + } + ], + "options": { + "showTime": true, + "wrapLogMessage": true, + "prettifyLogMessage": true, + "sortOrder": "Descending" + } + } + ] +} diff --git a/observability/grafana/dashboards/spring-gateway.json b/observability/grafana/dashboards/spring-gateway.json new file mode 100644 index 00000000..81ad2b36 --- /dev/null +++ b/observability/grafana/dashboards/spring-gateway.json @@ -0,0 +1,116 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { "type": "datasource", "uid": "grafana" }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { "limit": 100, "matchAny": false, "tags": [], "type": "dashboard" }, + "type": "dashboard" + } + ] + }, + "description": "Loki-only POC dashboard (logs only)", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 2, + "links": [], + "liveNow": false, + "panels": [ + { + "collapsed": false, + "datasource": { "type": "loki", "uid": "loki-thomas-vitale" }, + "gridPos": { "h": 1, "w": 24, "x": 0, "y": 0 }, + "id": 50, + "panels": [], + "targets": [{ "datasource": { "type": "loki", "uid": "loki-thomas-vitale" }, "refId": "A" }], + "title": "Logs", + "type": "row" + }, + { + "datasource": { "type": "loki", "uid": "loki-thomas-vitale" }, + "gridPos": { "h": 12, "w": 24, "x": 0, "y": 1 }, + "id": 1001, + "options": { + "dedupStrategy": "none", + "enableLogDetails": true, + "prettifyLogMessage": false, + "showCommonLabels": false, + "showLabels": false, + "showTime": true, + "sortOrder": "Descending", + "wrapLogMessage": true + }, + "targets": [ + { + "datasource": { "type": "loki", "uid": "loki-thomas-vitale" }, + "editorMode": "code", + "expr": "{job=~\"$job\"}", + "queryType": "range", + "refId": "A" + } + ], + "title": "Log Stream", + "type": "logs" + }, + { + "collapsed": false, + "datasource": { "type": "loki", "uid": "loki-thomas-vitale" }, + "gridPos": { "h": 1, "w": 24, "x": 0, "y": 13 }, + "id": 51, + "panels": [], + "targets": [{ "datasource": { "type": "loki", "uid": "loki-thomas-vitale" }, "refId": "A" }], + "title": "Notes", + "type": "row" + }, + { + "datasource": { "type": "loki", "uid": "loki-thomas-vitale" }, + "gridPos": { "h": 4, "w": 24, "x": 0, "y": 14 }, + "id": 1002, + "options": { + "content": "POC: Fluent Bit → Loki → Grafana. 이 대시보드는 Loki 로그만 확인합니다.", + "mode": "markdown" + }, + "title": "POC Memo", + "type": "text" + } + ], + "refresh": "1m", + "schemaVersion": 38, + "tags": [], + "templating": { + "list": [ + { + "current": { "selected": true, "text": "All", "value": ".*" }, + "datasource": { "type": "loki", "uid": "loki-thomas-vitale" }, + "definition": "label_values(job)", + "hide": 0, + "includeAll": true, + "label": "Job", + "multi": true, + "name": "job", + "options": [], + "query": { "query": "label_values(job)", "refId": "StandardVariableQuery" }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 1, + "type": "query" + } + ] + }, + "time": { "from": "now-15m", "to": "now" }, + "timepicker": { + "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"], + "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"] + }, + "timezone": "", + "title": "Spring Cloud Gateway (Loki Logs Only)", + "uid": "spring-cloud-gateway-loki-logs-only", + "version": 1, + "weekStart": "" +} diff --git a/observability/grafana/grafana.ini b/observability/grafana/grafana.ini new file mode 100644 index 00000000..1bb91819 --- /dev/null +++ b/observability/grafana/grafana.ini @@ -0,0 +1,6 @@ +[analytics] +enabled = false +reporting_enabled = false + +[users] +default_theme = light \ No newline at end of file diff --git a/test.sh b/test.sh index 8c82da16..91f166fe 100755 --- a/test.sh +++ b/test.sh @@ -12,6 +12,10 @@ docker volume ls -q | grep "kafka-data" | xargs -r docker volume rm # 컨테이너가 없을 경우 에러 메시지를 숨기기 위해 || true 사용 docker rm -f redis_cache local-postgres_db spot-gateway spot-user spot-store spot-order spot-payment 2>/dev/null || true +echo "=== Observability Stack 먼저 실행 (fluent-bit, loki, grafana) ===" +docker compose up -d fluent-bit loki grafana +sleep 3 + echo "=== 각 MSA 서비스 빌드 ===" for service in spot-gateway spot-user spot-store spot-order spot-payment; do echo ">> $service 빌드 시작" @@ -33,4 +37,4 @@ LOG_FILE="./logs/current_logs_$(date +'%Y%m%d_%H%M%S').txt" #docker compose logs -f | \ # grep --line-buffered -v -E "redis_cache|local-postgres_db" | \ -# tee -a "$LOG_FILE" \ No newline at end of file +# tee -a "$LOG_FILE"