From 27829e0fa717fc0cc3e1e498457cfcfd9743e2e1 Mon Sep 17 00:00:00 2001 From: kanghana1 Date: Tue, 7 Jul 2026 16:26:57 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20grafana=20=EC=84=B8=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/cd.yml | 9 +- build.gradle | 4 + docker-compose.yml | 5 + result-bulkhead-after.json | 197 +++++++++++++++++++++++++++++ scripts/deploy.sh | 4 + src/main/resources/application.yml | 24 ++++ 6 files changed, 241 insertions(+), 2 deletions(-) create mode 100644 result-bulkhead-after.json diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 9589c8c09..ae1712d65 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -72,7 +72,8 @@ jobs: envs: >- DB_PASSWORD,GCS_BUCKET,GCS_BACKUP_BUCKET, KAKAO_CLIENT_ID,KAKAO_CLIENT_SECRET,KAKAO_REDIRECT_URI_PROD,KAKAO_REDIRECT_URI_STAGING,KAKAO_ADMIN_KEY, - JWT_SECRET_KEY,FIREBASE_SERVICE_ACCOUNT_KEY,FCM_FAKE_LATENCY_MS + JWT_SECRET_KEY,FIREBASE_SERVICE_ACCOUNT_KEY,FCM_FAKE_LATENCY_MS, + OTLP_METRICS_ENABLED,OTEL_URL,OTEL_AUTH,OTLP_STEP script: | chmod +x /home/ubuntu/cockple/scripts/deploy.sh bash /home/ubuntu/cockple/scripts/deploy.sh \ @@ -89,5 +90,9 @@ jobs: KAKAO_ADMIN_KEY: ${{ secrets.KAKAO_ADMIN_KEY }} JWT_SECRET_KEY: ${{ secrets.JWT_SECRET_KEY }} FIREBASE_SERVICE_ACCOUNT_KEY: ${{ secrets.FIREBASE_SERVICE_ACCOUNT_KEY }} - # [부하테스트용] FCM 지연(ms) 토글. GitHub Variable로 관리(미설정 시 빈값→deploy.sh에서 0). PR 없이 값 변경+deploy 재실행으로 on/off. FCM_FAKE_LATENCY_MS: ${{ vars.FCM_FAKE_LATENCY_MS }} + # 관측성(OTLP) — URL/토글/step은 Variable, 인증 토큰은 Secret + OTLP_METRICS_ENABLED: ${{ vars.OTLP_METRICS_ENABLED }} + OTEL_URL: ${{ vars.OTEL_URL }} + OTEL_AUTH: ${{ secrets.OTEL_AUTH }} + OTLP_STEP: ${{ vars.OTLP_STEP }} diff --git a/build.gradle b/build.gradle index 97dfd314c..3d82ba611 100644 --- a/build.gradle +++ b/build.gradle @@ -102,6 +102,10 @@ dependencies { // flyway implementation 'org.flywaydb:flyway-core' implementation 'org.flywaydb:flyway-mysql' + + // observability (Actuator + OTLP 메트릭 export) + implementation 'org.springframework.boot:spring-boot-starter-actuator' + implementation 'io.micrometer:micrometer-registry-otlp' } tasks.named('test') { diff --git a/docker-compose.yml b/docker-compose.yml index bdbf2b50f..637838ed0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -84,6 +84,11 @@ services: SPRING_PROFILES_ACTIVE: staging LOG_PATH: /logs FCM_FAKE_LATENCY_MS: ${FCM_FAKE_LATENCY_MS:-0} + # 관측성(Observability) — OTLP 메트릭 push (staging 전용). 미설정 시 off. + OTLP_METRICS_ENABLED: ${OTLP_METRICS_ENABLED:-false} + OTEL_URL: ${OTEL_URL:-} + OTEL_AUTH: ${OTEL_AUTH:-} + OTLP_STEP: ${OTLP_STEP:-1m} DB_PASSWORD: ${DB_PASSWORD} GCS_BUCKET: ${GCS_BUCKET} KAKAO_CLIENT_ID: ${KAKAO_CLIENT_ID} diff --git a/result-bulkhead-after.json b/result-bulkhead-after.json new file mode 100644 index 000000000..e11961027 --- /dev/null +++ b/result-bulkhead-after.json @@ -0,0 +1,197 @@ +{ + "metrics": { + "notif_errors": { + "passes": 0, + "fails": 1193, + "thresholds": { + "rate<0.10": false + }, + "value": 0 + }, + "ws_sessions": { + "rate": 0.00605198927861776, + "count": 2 + }, + "http_reqs": { + "rate": 3.6100116046954938, + "count": 1193 + }, + "iteration_duration": { + "p(95)": 3458.016249999999, + "avg": 2069.7809279497906, + "min": 200.4083, + "med": 1373.5478, + "max": 330469.8518, + "p(90)": 2980.5900200000005 + }, + "chat_messages_sent": { + "count": 1649, + "rate": 4.989865160220343 + }, + "checks": { + "fails": 0, + "passes": 1195, + "value": 1 + }, + "data_received": { + "count": 1343612, + "rate": 4065.7627093110827 + }, + "iterations": { + "count": 1195, + "rate": 3.6160635939741113 + }, + "ws_msgs_received": { + "count": 3302, + "rate": 9.99183429899792 + }, + "vus_max": { + "max": 12, + "value": 12, + "min": 12 + }, + "http_req_duration{expected_response:true}": { + "max": 5029.5472, + "p(90)": 2969.36652, + "p(95)": 3428.39952, + "avg": 1517.9153570829835, + "min": 200.4083, + "med": 1371.282 + }, + "chat_messages_received": { + "count": 1649, + "rate": 4.989865160220343 + }, + "vus": { + "value": 2, + "min": 2, + "max": 12 + }, + "http_req_blocked": { + "max": 173.8356, + "p(90)": 0, + "p(95)": 0, + "avg": 0.9890362950544845, + "min": 0, + "med": 0 + }, + "http_req_waiting": { + "avg": 1517.5404857502087, + "min": 200.4083, + "med": 1370.0408, + "max": 5029.5472, + "p(90)": 2968.8489, + "p(95)": 3428.04076 + }, + "ws_msgs_sent": { + "count": 1651, + "rate": 4.99591714949896 + }, + "data_sent": { + "count": 236904, + "rate": 716.8702340308308 + }, + "chat_e2e_latency": { + "p(95)": 29551.399999999998, + "avg": 6245.468162522741, + "min": 91, + "med": 283, + "max": 31462, + "p(90)": 25948.800000000003, + "thresholds": { + "p(95)<3000": true + } + }, + "http_req_connecting": { + "avg": 0.451170410729254, + "min": 0, + "med": 0, + "max": 111.1023, + "p(90)": 0, + "p(95)": 0 + }, + "http_req_tls_handshaking": { + "p(90)": 0, + "p(95)": 0, + "avg": 0.5351792958927074, + "min": 0, + "med": 0, + "max": 69.7766 + }, + "http_req_duration": { + "max": 5029.5472, + "p(90)": 2969.36652, + "p(95)": 3428.39952, + "avg": 1517.9153570829835, + "min": 200.4083, + "med": 1371.282 + }, + "ws_session_duration": { + "avg": 330469.8518, + "min": 330469.8518, + "med": 330469.8518, + "max": 330469.8518, + "p(90)": 330469.8518, + "p(95)": 330469.8518 + }, + "http_req_receiving": { + "avg": 0.17350494551550716, + "min": 0, + "med": 0, + "max": 4.9739, + "p(90)": 0.7003199999999999, + "p(95)": 0.8672999999999993 + }, + "http_req_sending": { + "avg": 0.20136638725901077, + "min": 0, + "med": 0, + "max": 8.9037, + "p(90)": 0.59272, + "p(95)": 0.6705799999999997 + }, + "http_req_failed": { + "fails": 1193, + "passes": 0, + "value": 0 + }, + "notif_req_duration": { + "p(95)": 3428.39952, + "avg": 1517.9153570829835, + "min": 200.4083, + "med": 1371.282, + "max": 5029.5472, + "p(90)": 2969.36652 + }, + "ws_connecting": { + "med": 467.2125, + "max": 467.2125, + "p(90)": 467.2125, + "p(95)": 467.2125, + "avg": 467.2125, + "min": 467.2125 + } + }, + "root_group": { + "name": "", + "path": "", + "id": "d41d8cd98f00b204e9800998ecf8427e", + "groups": {}, + "checks": { + "[notif] status 200": { + "name": "[notif] status 200", + "path": "::[notif] status 200", + "id": "9fc740afe9cf21a0327678441a348978", + "passes": 1193, + "fails": 0 + }, + "[ws] status 101": { + "id": "3cc6802624f263d3928ed6de98f3c352", + "passes": 2, + "fails": 0, + "name": "[ws] status 101", + "path": "::[ws] status 101" + } + } + } +} \ No newline at end of file diff --git a/scripts/deploy.sh b/scripts/deploy.sh index 963d1537f..eeb16e2b1 100644 --- a/scripts/deploy.sh +++ b/scripts/deploy.sh @@ -27,6 +27,10 @@ KAKAO_ADMIN_KEY=${KAKAO_ADMIN_KEY} JWT_SECRET_KEY=${JWT_SECRET_KEY} FIREBASE_SERVICE_ACCOUNT_KEY=${FIREBASE_SERVICE_ACCOUNT_KEY} FCM_FAKE_LATENCY_MS=${FCM_FAKE_LATENCY_MS:-0} +OTLP_METRICS_ENABLED=${OTLP_METRICS_ENABLED:-false} +OTEL_URL=${OTEL_URL:-} +OTEL_AUTH=${OTEL_AUTH:-} +OTLP_STEP=${OTLP_STEP:-1m} EOF echo "${FIREBASE_SERVICE_ACCOUNT_KEY}" > /home/ubuntu/cockple/firebase-service-account.json diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index ef55b172f..6120c9375 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -103,6 +103,30 @@ async: fcm: fake-latency-ms: 0 +# 관측성(Observability) — OTLP로 메트릭 push +# 기본 off, staging(및 추후 prod)에서 env로만 켠다. +management: + otlp: + metrics: + export: + enabled: ${OTLP_METRICS_ENABLED:false} + url: ${OTEL_URL:http://localhost:4318}/v1/metrics + step: ${OTLP_STEP:1m} + headers: + Authorization: ${OTEL_AUTH:} + opentelemetry: + resource-attributes: + service.name: ${spring.application.name} + deployment.environment: ${SPRING_PROFILES_ACTIVE:local} + metrics: + distribution: + percentiles-histogram: + http.server.requests: true + endpoints: + web: + exposure: + include: health # 웹 노출 최소화 (메트릭은 OTLP push라 endpoint 불필요) + logging: level: org.hibernate.SQL: WARN From fa83eb15102d10e32a744a171a18f05b830bedb0 Mon Sep 17 00:00:00 2001 From: kanghana1 Date: Tue, 7 Jul 2026 16:41:01 +0900 Subject: [PATCH 2/2] =?UTF-8?q?chore:=20=EB=B6=80=ED=95=98=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EA=B2=B0=EA=B3=BC=20=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=EC=B6=94=EC=A0=81=20=EC=A0=9C=EC=99=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + result-bulkhead-after.json | 197 ------------------------------------ result-bulkhead-before.json | 197 ------------------------------------ 3 files changed, 1 insertion(+), 394 deletions(-) delete mode 100644 result-bulkhead-after.json delete mode 100644 result-bulkhead-before.json diff --git a/.gitignore b/.gitignore index 5add0cde6..b87032d31 100644 --- a/.gitignore +++ b/.gitignore @@ -63,3 +63,4 @@ k6 ### logs ### logs/ +/result-bulkhead-*.json diff --git a/result-bulkhead-after.json b/result-bulkhead-after.json deleted file mode 100644 index e11961027..000000000 --- a/result-bulkhead-after.json +++ /dev/null @@ -1,197 +0,0 @@ -{ - "metrics": { - "notif_errors": { - "passes": 0, - "fails": 1193, - "thresholds": { - "rate<0.10": false - }, - "value": 0 - }, - "ws_sessions": { - "rate": 0.00605198927861776, - "count": 2 - }, - "http_reqs": { - "rate": 3.6100116046954938, - "count": 1193 - }, - "iteration_duration": { - "p(95)": 3458.016249999999, - "avg": 2069.7809279497906, - "min": 200.4083, - "med": 1373.5478, - "max": 330469.8518, - "p(90)": 2980.5900200000005 - }, - "chat_messages_sent": { - "count": 1649, - "rate": 4.989865160220343 - }, - "checks": { - "fails": 0, - "passes": 1195, - "value": 1 - }, - "data_received": { - "count": 1343612, - "rate": 4065.7627093110827 - }, - "iterations": { - "count": 1195, - "rate": 3.6160635939741113 - }, - "ws_msgs_received": { - "count": 3302, - "rate": 9.99183429899792 - }, - "vus_max": { - "max": 12, - "value": 12, - "min": 12 - }, - "http_req_duration{expected_response:true}": { - "max": 5029.5472, - "p(90)": 2969.36652, - "p(95)": 3428.39952, - "avg": 1517.9153570829835, - "min": 200.4083, - "med": 1371.282 - }, - "chat_messages_received": { - "count": 1649, - "rate": 4.989865160220343 - }, - "vus": { - "value": 2, - "min": 2, - "max": 12 - }, - "http_req_blocked": { - "max": 173.8356, - "p(90)": 0, - "p(95)": 0, - "avg": 0.9890362950544845, - "min": 0, - "med": 0 - }, - "http_req_waiting": { - "avg": 1517.5404857502087, - "min": 200.4083, - "med": 1370.0408, - "max": 5029.5472, - "p(90)": 2968.8489, - "p(95)": 3428.04076 - }, - "ws_msgs_sent": { - "count": 1651, - "rate": 4.99591714949896 - }, - "data_sent": { - "count": 236904, - "rate": 716.8702340308308 - }, - "chat_e2e_latency": { - "p(95)": 29551.399999999998, - "avg": 6245.468162522741, - "min": 91, - "med": 283, - "max": 31462, - "p(90)": 25948.800000000003, - "thresholds": { - "p(95)<3000": true - } - }, - "http_req_connecting": { - "avg": 0.451170410729254, - "min": 0, - "med": 0, - "max": 111.1023, - "p(90)": 0, - "p(95)": 0 - }, - "http_req_tls_handshaking": { - "p(90)": 0, - "p(95)": 0, - "avg": 0.5351792958927074, - "min": 0, - "med": 0, - "max": 69.7766 - }, - "http_req_duration": { - "max": 5029.5472, - "p(90)": 2969.36652, - "p(95)": 3428.39952, - "avg": 1517.9153570829835, - "min": 200.4083, - "med": 1371.282 - }, - "ws_session_duration": { - "avg": 330469.8518, - "min": 330469.8518, - "med": 330469.8518, - "max": 330469.8518, - "p(90)": 330469.8518, - "p(95)": 330469.8518 - }, - "http_req_receiving": { - "avg": 0.17350494551550716, - "min": 0, - "med": 0, - "max": 4.9739, - "p(90)": 0.7003199999999999, - "p(95)": 0.8672999999999993 - }, - "http_req_sending": { - "avg": 0.20136638725901077, - "min": 0, - "med": 0, - "max": 8.9037, - "p(90)": 0.59272, - "p(95)": 0.6705799999999997 - }, - "http_req_failed": { - "fails": 1193, - "passes": 0, - "value": 0 - }, - "notif_req_duration": { - "p(95)": 3428.39952, - "avg": 1517.9153570829835, - "min": 200.4083, - "med": 1371.282, - "max": 5029.5472, - "p(90)": 2969.36652 - }, - "ws_connecting": { - "med": 467.2125, - "max": 467.2125, - "p(90)": 467.2125, - "p(95)": 467.2125, - "avg": 467.2125, - "min": 467.2125 - } - }, - "root_group": { - "name": "", - "path": "", - "id": "d41d8cd98f00b204e9800998ecf8427e", - "groups": {}, - "checks": { - "[notif] status 200": { - "name": "[notif] status 200", - "path": "::[notif] status 200", - "id": "9fc740afe9cf21a0327678441a348978", - "passes": 1193, - "fails": 0 - }, - "[ws] status 101": { - "id": "3cc6802624f263d3928ed6de98f3c352", - "passes": 2, - "fails": 0, - "name": "[ws] status 101", - "path": "::[ws] status 101" - } - } - } -} \ No newline at end of file diff --git a/result-bulkhead-before.json b/result-bulkhead-before.json deleted file mode 100644 index 7aeddc6b1..000000000 --- a/result-bulkhead-before.json +++ /dev/null @@ -1,197 +0,0 @@ -{ - "root_group": { - "name": "", - "path": "", - "id": "d41d8cd98f00b204e9800998ecf8427e", - "groups": {}, - "checks": { - "[notif] status 200": { - "passes": 1567, - "fails": 0, - "name": "[notif] status 200", - "path": "::[notif] status 200", - "id": "9fc740afe9cf21a0327678441a348978" - }, - "[ws] status 101": { - "name": "[ws] status 101", - "path": "::[ws] status 101", - "id": "3cc6802624f263d3928ed6de98f3c352", - "passes": 2, - "fails": 0 - } - } - }, - "metrics": { - "ws_connecting": { - "max": 1155.4575, - "p(90)": 1155.34769, - "p(95)": 1155.402595, - "avg": 1154.90845, - "min": 1154.3594, - "med": 1154.90845 - }, - "ws_msgs_sent": { - "count": 1651, - "rate": 4.985553421339417 - }, - "ws_msgs_received": { - "count": 3304, - "rate": 9.977146277471492 - }, - "http_reqs": { - "count": 1567, - "rate": 4.731897160047769 - }, - "http_req_receiving": { - "p(90)": 0.7683200000000002, - "p(95)": 0.9485099999999989, - "avg": 0.21188544990427544, - "min": 0, - "med": 0, - "max": 6.9107 - }, - "checks": { - "passes": 1569, - "fails": 0, - "value": 1 - }, - "vus_max": { - "value": 12, - "min": 12, - "max": 12 - }, - "chat_messages_received": { - "count": 1648, - "rate": 4.97649426915043 - }, - "iterations": { - "count": 1569, - "rate": 4.737936594840427 - }, - "ws_sessions": { - "count": 2, - "rate": 0.006039434792658289 - }, - "http_req_duration{expected_response:true}": { - "p(90)": 2076.8168400000004, - "p(95)": 2721.69289, - "avg": 1151.817254243779, - "min": 431.0899, - "med": 977.7864, - "max": 4974.7035 - }, - "http_req_blocked": { - "p(90)": 0, - "p(95)": 0, - "avg": 1.902208615188258, - "min": 0, - "med": 0, - "max": 315.6305 - }, - "http_req_connecting": { - "max": 148.6131, - "p(90)": 0, - "p(95)": 0, - "avg": 0.895541671984684, - "min": 0, - "med": 0 - }, - "chat_messages_sent": { - "count": 1649, - "rate": 4.979513986546759 - }, - "http_req_sending": { - "med": 0, - "max": 3.377, - "p(90)": 0.6262200000000001, - "p(95)": 0.7184799999999996, - "avg": 0.17865022335673258, - "min": 0 - }, - "vus": { - "value": 2, - "min": 2, - "max": 12 - }, - "data_sent": { - "count": 251316, - "rate": 758.9032971758552 - }, - "http_req_waiting": { - "p(95)": 2721.5110299999997, - "avg": 1151.4267185705173, - "min": 430.2286, - "med": 977.2512, - "max": 4974.1908, - "p(90)": 2076.08822 - }, - "chat_e2e_latency": { - "min": 350, - "med": 404, - "max": 5422, - "p(90)": 3371.499999999998, - "p(95)": 4390.65, - "avg": 937.6231796116505, - "thresholds": { - "p(95)<3000": true - } - }, - "notif_req_duration": { - "med": 977.7864, - "max": 4974.7035, - "p(90)": 2076.8168400000004, - "p(95)": 2721.69289, - "avg": 1151.817254243779, - "min": 431.0899 - }, - "ws_session_duration": { - "min": 331155.5802, - "med": 331155.89955, - "max": 331156.2189, - "p(90)": 331156.15502999997, - "p(95)": 331156.186965, - "avg": 331155.89955 - }, - "notif_errors": { - "passes": 0, - "fails": 1567, - "thresholds": { - "rate<0.10": false - }, - "value": 0 - }, - "http_req_failed": { - "fails": 1567, - "passes": 0, - "value": 0 - }, - "iteration_duration": { - "avg": 1574.7665509878918, - "min": 431.6325, - "med": 982.5982, - "max": 331156.2189, - "p(90)": 2088.631680000001, - "p(95)": 2724.6312 - }, - "http_req_duration": { - "p(90)": 2076.8168400000004, - "p(95)": 2721.69289, - "avg": 1151.817254243779, - "min": 431.0899, - "med": 977.7864, - "max": 4974.7035 - }, - "data_received": { - "count": 1517803, - "rate": 4583.336123300564 - }, - "http_req_tls_handshaking": { - "max": 181.0315, - "p(90)": 0, - "p(95)": 0, - "avg": 1.003557562220804, - "min": 0, - "med": 0 - } - } -} \ No newline at end of file