From 084c0a371989840422a71ea8937c86b2e970181c Mon Sep 17 00:00:00 2001 From: "xiuting.xu" Date: Thu, 16 Oct 2025 15:34:20 +0800 Subject: [PATCH] =?UTF-8?q?[#24]=20=E4=BC=98=E5=8C=96alertmanager=E5=92=8C?= =?UTF-8?q?web=E7=9A=84=E6=9E=84=E5=BB=BA=20[#25]=20=E5=8F=8D=E5=90=91?= =?UTF-8?q?=E4=BB=A3=E7=90=86=E5=8A=9F=E8=83=BD=E5=AE=9E=E7=8E=B0=20[#14]?= =?UTF-8?q?=20=E5=89=8D=E7=AB=AF=E9=A1=B5=E9=9D=A2=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/alert/alertmanager/build/Dockerfile | 7 +- src/alert/alertmanager/build/build.sh | 13 +++ .../alertmanager/build/start-am-supervised.sh | 1 + .../alertmanager/config/rule_files/README.md | 2 +- src/alert/tests/.env | 5 ++ .../tests/data/alertmanager/alertmanager.yml | 19 ---- src/alert/tests/data/alertmanager/nflog | 0 src/alert/tests/data/alertmanager/silences | 0 .../data/etc/alertmanager.alert.argus.com | 1 - src/alert/tests/docker-compose.yml | 10 +-- src/web/build_tools/frontend/Dockerfile | 8 +- src/web/build_tools/frontend/build.sh | 7 +- src/web/build_tools/frontend/health-check.sh | 2 +- src/web/build_tools/frontend/nginx.conf | 31 +------ .../frontend/start-web-supervised.sh | 14 +-- src/web/build_tools/proxy/Dockerfile | 6 +- src/web/build_tools/proxy/build.sh | 6 ++ src/web/build_tools/proxy/conf.d/alert.conf | 3 +- src/web/build_tools/proxy/conf.d/log.conf | 6 +- src/web/build_tools/proxy/conf.d/master.conf | 36 ++++---- src/web/build_tools/proxy/conf.d/metric.conf | 20 +++-- src/web/build_tools/proxy/conf.d/web.conf | 3 +- src/web/build_tools/proxy/nginx.conf.template | 19 ++-- .../proxy/start-proxy-supervised.sh | 4 +- src/web/src/components/AlertFilters.jsx | 10 +-- src/web/src/components/AlertTable.jsx | 66 ++++++++++---- src/web/src/components/NodeDetailDrawer.jsx | 13 +-- src/web/src/components/NodeHealthCard.jsx | 89 ++++++++++--------- src/web/src/components/NodeTable.jsx | 33 ++++--- src/web/src/components/Sidebar.jsx | 2 +- src/web/src/config/api.js | 9 +- src/web/src/config/entries.js | 1 - src/web/src/pages/Alerts.jsx | 24 +++-- src/web/src/pages/Dashboard.jsx | 24 ++--- src/web/src/pages/Metrics.jsx | 2 +- src/web/src/pages/NodePage.jsx | 44 +++++---- src/web/tests/data/etc/web.argus.com | 1 - src/web/tests/docker-compose.yml | 10 +-- 38 files changed, 306 insertions(+), 245 deletions(-) create mode 100644 src/alert/alertmanager/build/build.sh create mode 100644 src/alert/tests/.env delete mode 100644 src/alert/tests/data/alertmanager/alertmanager.yml delete mode 100644 src/alert/tests/data/alertmanager/nflog delete mode 100644 src/alert/tests/data/alertmanager/silences delete mode 100644 src/alert/tests/data/etc/alertmanager.alert.argus.com create mode 100644 src/web/build_tools/proxy/build.sh delete mode 100644 src/web/tests/data/etc/web.argus.com diff --git a/src/alert/alertmanager/build/Dockerfile b/src/alert/alertmanager/build/Dockerfile index d986e31..a606569 100644 --- a/src/alert/alertmanager/build/Dockerfile +++ b/src/alert/alertmanager/build/Dockerfile @@ -19,8 +19,11 @@ RUN wget https://github.com/prometheus/alertmanager/releases/download/v${ALERTMA rm alertmanager-${ALERTMANAGER_VERSION}.linux-amd64.tar.gz ENV ALERTMANAGER_BASE_PATH=/private/argus/alert/alertmanager -ENV ARGUS_UID=2133 -ENV ARGUS_GID=2015 + +ARG ARGUS_UID=2133 +ARG ARGUS_GID=2015 +ENV ARGUS_UID=${ARGUS_UID} +ENV ARGUS_GID=${ARGUS_GID} RUN mkdir -p /usr/share/alertmanager && \ mkdir -p ${ALERTMANAGER_BASE_PATH} && \ diff --git a/src/alert/alertmanager/build/build.sh b/src/alert/alertmanager/build/build.sh new file mode 100644 index 0000000..bbfadc6 --- /dev/null +++ b/src/alert/alertmanager/build/build.sh @@ -0,0 +1,13 @@ +#!/bin/bash +set -euo pipefail +docker pull ubuntu:24.04 + +source src/alert/tests/.env + +docker build \ + --build-arg ARGUS_UID=${ARGUS_UID} \ + --build-arg ARGUS_GID=${ARGUS_GID} \ + -f src/alert/alertmanager/build/Dockerfile \ + -t argus-alertmanager:latest . + +docker save -o argus-alertmanager.tar argus-alertmanager:latest \ No newline at end of file diff --git a/src/alert/alertmanager/build/start-am-supervised.sh b/src/alert/alertmanager/build/start-am-supervised.sh index 467d7fe..76bbb8a 100644 --- a/src/alert/alertmanager/build/start-am-supervised.sh +++ b/src/alert/alertmanager/build/start-am-supervised.sh @@ -18,6 +18,7 @@ DOMAIN=alertmanager.alert.argus.com IP=$(ifconfig | grep -A 1 eth0 | grep inet | awk '{print $2}') echo "current IP: ${IP}" echo "${IP}" > /private/argus/etc/${DOMAIN} +chmod 755 /private/argus/etc/${DOMAIN} echo "[INFO] Starting Alertmanager process..." diff --git a/src/alert/alertmanager/config/rule_files/README.md b/src/alert/alertmanager/config/rule_files/README.md index 778f332..be5762b 100644 --- a/src/alert/alertmanager/config/rule_files/README.md +++ b/src/alert/alertmanager/config/rule_files/README.md @@ -55,6 +55,6 @@ alerting: alertmanagers: - static_configs: - targets: - - "localhost:9093" # Alertmanager 地址 + - "alertmanager.alert.argus.com:9093" # Alertmanager 地址 ``` \ No newline at end of file diff --git a/src/alert/tests/.env b/src/alert/tests/.env new file mode 100644 index 0000000..00f4b76 --- /dev/null +++ b/src/alert/tests/.env @@ -0,0 +1,5 @@ +DATA_ROOT=/home/argus/tmp/private/argus +ARGUS_UID=1048 +ARGUS_GID=1048 + +USE_INTRANET=false diff --git a/src/alert/tests/data/alertmanager/alertmanager.yml b/src/alert/tests/data/alertmanager/alertmanager.yml deleted file mode 100644 index 26060aa..0000000 --- a/src/alert/tests/data/alertmanager/alertmanager.yml +++ /dev/null @@ -1,19 +0,0 @@ -global: - resolve_timeout: 5m - -route: - group_by: ['alertname', 'instance'] # 分组:相同 alertname + instance 的告警合并 - group_wait: 30s # 第一个告警后,等 30s 看是否有同组告警一起发 - group_interval: 5m # 同组告警变化后,至少 5 分钟再发一次 - repeat_interval: 3h # 相同告警,3 小时重复提醒一次 - receiver: 'null' - -receivers: - - name: 'null' - -inhibit_rules: - - source_match: - severity: 'critical' # critical 告警存在时 - target_match: - severity: 'warning' # 抑制相同 instance 的 warning 告警 - equal: ['instance'] diff --git a/src/alert/tests/data/alertmanager/nflog b/src/alert/tests/data/alertmanager/nflog deleted file mode 100644 index e69de29..0000000 diff --git a/src/alert/tests/data/alertmanager/silences b/src/alert/tests/data/alertmanager/silences deleted file mode 100644 index e69de29..0000000 diff --git a/src/alert/tests/data/etc/alertmanager.alert.argus.com b/src/alert/tests/data/etc/alertmanager.alert.argus.com deleted file mode 100644 index cd339f9..0000000 --- a/src/alert/tests/data/etc/alertmanager.alert.argus.com +++ /dev/null @@ -1 +0,0 @@ -172.18.0.2 diff --git a/src/alert/tests/docker-compose.yml b/src/alert/tests/docker-compose.yml index cef0204..63b9f40 100644 --- a/src/alert/tests/docker-compose.yml +++ b/src/alert/tests/docker-compose.yml @@ -1,4 +1,3 @@ -version: '3.8' services: alertmanager: build: @@ -17,20 +16,21 @@ services: ports: - "${ARGUS_PORT:-9093}:9093" volumes: - - ${DATA_ROOT:-./data}/alertmanager:/private/argus/alert/alertmanager + - ${DATA_ROOT:-./data}/alert/alertmanager:/private/argus/alert/alertmanager - ${DATA_ROOT:-./data}/etc:/private/argus/etc networks: - - argus-network + - argus-debug-net restart: unless-stopped logging: driver: "json-file" options: max-size: "10m" max-file: "3" + networks: - argus-network: + argus-debug-net: driver: bridge - name: argus-network + name: argus-debug-net volumes: alertmanager_data: diff --git a/src/web/build_tools/frontend/Dockerfile b/src/web/build_tools/frontend/Dockerfile index 13c8d6a..3c87684 100644 --- a/src/web/build_tools/frontend/Dockerfile +++ b/src/web/build_tools/frontend/Dockerfile @@ -24,8 +24,10 @@ RUN apt-get update && \ apt-get clean && rm -rf /var/lib/apt/lists/* ENV FRONTEND_BASE_PATH=/private/argus/web/frontend -ENV ARGUS_UID=2133 -ENV ARGUS_GID=2015 +ARG ARGUS_UID=2133 +ARG ARGUS_GID=2015 +ENV ARGUS_UID=${ARGUS_UID} +ENV ARGUS_GID=${ARGUS_GID} RUN mkdir -p ${FRONTEND_BASE_PATH} && \ mkdir -p /private/argus/etc @@ -82,7 +84,7 @@ COPY src/web/build_tools/frontend/health-check.sh /usr/local/bin/health-check.sh RUN chmod +x /usr/local/bin/health-check.sh # 暴露端口 -EXPOSE 80 +EXPOSE 8080 # 保持 root 用户,由 supervisor 控制 user 切换 USER root diff --git a/src/web/build_tools/frontend/build.sh b/src/web/build_tools/frontend/build.sh index 31bf953..f4b5281 100644 --- a/src/web/build_tools/frontend/build.sh +++ b/src/web/build_tools/frontend/build.sh @@ -1,4 +1,7 @@ docker pull node:20 docker pull ubuntu:24.04 -docker build -f src/web/build_tools/frontend/Dockerfile -t argus-web:0.1.1 . -rm -f argus-web-0.1.1.tar && sudo docker image save argus-web:0.1.1 > argus-web-0.1.1.tar +export ARGUS_UID=1048 +export ARGUS_GID=1048 + +docker build -f src/web/build_tools/frontend/Dockerfile -t argus-web-frontend:latest . +docker save -o argus-web-frontend-latest.tar argus-web-frontend:latest diff --git a/src/web/build_tools/frontend/health-check.sh b/src/web/build_tools/frontend/health-check.sh index e993031..1c18c1d 100644 --- a/src/web/build_tools/frontend/health-check.sh +++ b/src/web/build_tools/frontend/health-check.sh @@ -1,7 +1,7 @@ #!/bin/bash set -euo pipefail -URL="http://127.0.0.1:80" +URL="http://127.0.0.1:8080" echo "[INFO] Starting Argus web health check loop for $URL..." diff --git a/src/web/build_tools/frontend/nginx.conf b/src/web/build_tools/frontend/nginx.conf index 4d2cecb..96c6c35 100644 --- a/src/web/build_tools/frontend/nginx.conf +++ b/src/web/build_tools/frontend/nginx.conf @@ -12,7 +12,7 @@ http { # React 前端服务 server { - listen 80; + listen 8080; server_name web.argus.com; root /usr/share/nginx/html; @@ -24,33 +24,4 @@ http { } } - - # Master 服务,需要增加 CORS 支持 - server { - listen 80; - server_name master.argus.com; - - location / { - proxy_pass http://master.argus.com; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - - # CORS 支持 - add_header 'Access-Control-Allow-Origin' 'http://web.argus.com' always; - add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always; - add_header 'Access-Control-Allow-Headers' 'Origin, Content-Type, Accept, Authorization' always; - - if ($request_method = OPTIONS) { - add_header 'Access-Control-Allow-Origin' 'http://web.argus.com' always; - add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always; - add_header 'Access-Control-Allow-Headers' 'Origin, Content-Type, Accept, Authorization' always; - add_header 'Content-Length' 0; - add_header 'Content-Type' 'text/plain'; - return 204; - } - } -} - } diff --git a/src/web/build_tools/frontend/start-web-supervised.sh b/src/web/build_tools/frontend/start-web-supervised.sh index 8e94777..94e7a93 100644 --- a/src/web/build_tools/frontend/start-web-supervised.sh +++ b/src/web/build_tools/frontend/start-web-supervised.sh @@ -8,21 +8,12 @@ DNS_SCRIPT="${DNS_DIR}/update-dns.sh" DOMAIN=web.argus.com WEB_DOMAIN_FILE="${DNS_DIR}/${DOMAIN}" RUNTIME_USER="${ARGUS_RUNTIME_USER:-argus}" -RUNTIME_UID="${ARGUS_BUILD_UID:-2133}" -RUNTIME_GID="${ARGUS_BUILD_GID:-2015}" +RUNTIME_UID="${ARGUS_UID:-2133}" +RUNTIME_GID="${ARGUS_GID:-2015}" mkdir -p "$DNS_DIR" chown -R "$RUNTIME_UID:$RUNTIME_GID" "$DNS_DIR" 2>/dev/null || true -if [[ -x "$DNS_SCRIPT" ]]; then - echo "[INFO] Running update-dns.sh before master starts" - # 若脚本存在则执行,保证容器使用 bind 作为 DNS - "$DNS_SCRIPT" || echo "[WARN] update-dns.sh execution failed" -else - echo "[WARN] DNS update script not found or not executable: $DNS_SCRIPT" -fi - - # 记录容器 IP IP=$(ifconfig | grep -A 1 eth0 | grep inet | awk '{print $2}' || true) if [[ -n "${IP}" ]]; then @@ -32,6 +23,7 @@ if [[ -n "${IP}" ]]; then else echo "[WARN] Failed to detect web IP via ifconfig" fi +chmod 755 "$WEB_DOMAIN_FILE" echo "[INFO] Launching nginx..." diff --git a/src/web/build_tools/proxy/Dockerfile b/src/web/build_tools/proxy/Dockerfile index ba58515..e43e36f 100644 --- a/src/web/build_tools/proxy/Dockerfile +++ b/src/web/build_tools/proxy/Dockerfile @@ -8,8 +8,10 @@ RUN apt-get update && \ apt-get clean && rm -rf /var/lib/apt/lists/* ENV FRONTEND_BASE_PATH=/private/argus/web/proxy -ENV ARGUS_UID=2133 -ENV ARGUS_GID=2015 +ARG ARGUS_UID=2133 +ARG ARGUS_GID=2015 +ENV ARGUS_UID=${ARGUS_UID} +ENV ARGUS_GID=${ARGUS_GID} RUN mkdir -p ${FRONTEND_BASE_PATH} && \ mkdir -p /private/argus/etc diff --git a/src/web/build_tools/proxy/build.sh b/src/web/build_tools/proxy/build.sh new file mode 100644 index 0000000..46d2626 --- /dev/null +++ b/src/web/build_tools/proxy/build.sh @@ -0,0 +1,6 @@ +docker pull ubuntu:24.04 +export ARGUS_UID=1048 +export ARGUS_GID=1048 + +docker build -f src/web/build_tools/proxy/Dockerfile -t argus-web-proxy:latest . +docker save -o argus-web-proxy-latest.tar argus-web-proxy:latest diff --git a/src/web/build_tools/proxy/conf.d/alert.conf b/src/web/build_tools/proxy/conf.d/alert.conf index 4b770cd..1aa9224 100644 --- a/src/web/build_tools/proxy/conf.d/alert.conf +++ b/src/web/build_tools/proxy/conf.d/alert.conf @@ -3,6 +3,7 @@ server { server_name alertmanager.alert.argus.com; location / { - proxy_pass http://alertmanager.alert.argus.com:9093; + set $alert_backend http://alertmanager.alert.argus.com:9093; + proxy_pass $alert_backend; } } diff --git a/src/web/build_tools/proxy/conf.d/log.conf b/src/web/build_tools/proxy/conf.d/log.conf index 97858cc..0441bb5 100644 --- a/src/web/build_tools/proxy/conf.d/log.conf +++ b/src/web/build_tools/proxy/conf.d/log.conf @@ -4,7 +4,8 @@ server { server_name es.log.argus.com; location / { - proxy_pass http://es.log.argus.com; + set $es_backend http://es.log.argus.com:9200; + proxy_pass $es_backend; } } @@ -14,6 +15,7 @@ server { server_name kibana.log.argus.com; location / { - proxy_pass http://kibana.log.argus.com; + set $kibana_backend http://kibana.log.argus.com:5601; + proxy_pass $kibana_backend; } } diff --git a/src/web/build_tools/proxy/conf.d/master.conf b/src/web/build_tools/proxy/conf.d/master.conf index 96a19b5..a85a99f 100644 --- a/src/web/build_tools/proxy/conf.d/master.conf +++ b/src/web/build_tools/proxy/conf.d/master.conf @@ -3,25 +3,25 @@ server { server_name master.argus.com; location / { - # proxy_pass http://master.argus.com; - proxy_pass http://master.argus.com; - # proxy_set_header Host $host; - # proxy_set_header X-Real-IP $remote_addr; - # proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - # proxy_set_header X-Forwarded-Proto $scheme; + set $master_backend http://master.argus.com:3000; + proxy_pass $master_backend; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; - # # CORS 支持 - # add_header 'Access-Control-Allow-Origin' 'http://web.argus.com' always; - # add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always; - # add_header 'Access-Control-Allow-Headers' 'Origin, Content-Type, Accept, Authorization' always; + # CORS 支持 + add_header 'Access-Control-Allow-Origin' 'http://web.argus.com' always; + add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always; + add_header 'Access-Control-Allow-Headers' 'Origin, Content-Type, Accept, Authorization' always; - # if ($request_method = OPTIONS) { - # add_header 'Access-Control-Allow-Origin' 'http://web.argus.com' always; - # add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always; - # add_header 'Access-Control-Allow-Headers' 'Origin, Content-Type, Accept, Authorization' always; - # add_header 'Content-Length' 0; - # add_header 'Content-Type' 'text/plain'; - # return 204; - # } + if ($request_method = OPTIONS) { + add_header 'Access-Control-Allow-Origin' 'http://web.argus.com' always; + add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always; + add_header 'Access-Control-Allow-Headers' 'Origin, Content-Type, Accept, Authorization' always; + add_header 'Content-Length' 0; + add_header 'Content-Type' 'text/plain'; + return 204; + } } } diff --git a/src/web/build_tools/proxy/conf.d/metric.conf b/src/web/build_tools/proxy/conf.d/metric.conf index beb8c0e..81d68c2 100644 --- a/src/web/build_tools/proxy/conf.d/metric.conf +++ b/src/web/build_tools/proxy/conf.d/metric.conf @@ -4,16 +4,18 @@ server { server_name prometheus.metric.argus.com; location / { - proxy_pass http://prom.metric.argus.com; + set $prom_backend http://prom.metric.argus.com:9090; + proxy_pass $prom_backend; } } -# # Grafana -# server { -# listen 80; -# server_name grafana.metric.argus.com; +# Grafana +server { + listen 80; + server_name grafana.metric.argus.com; -# location / { -# proxy_pass http://grafana.metric.argus.com; -# } -# } + location / { + set $grafana_backend http://grafana.metric.argus.com:3000; + proxy_pass $grafana_backend; + } +} diff --git a/src/web/build_tools/proxy/conf.d/web.conf b/src/web/build_tools/proxy/conf.d/web.conf index c810710..27397d0 100644 --- a/src/web/build_tools/proxy/conf.d/web.conf +++ b/src/web/build_tools/proxy/conf.d/web.conf @@ -3,6 +3,7 @@ server { server_name web.argus.com; location / { - proxy_pass http://web.argus.com:80; + set $web_backend http://web.argus.com:8080; + proxy_pass $web_backend; } } diff --git a/src/web/build_tools/proxy/nginx.conf.template b/src/web/build_tools/proxy/nginx.conf.template index a7c6a19..41e29ec 100644 --- a/src/web/build_tools/proxy/nginx.conf.template +++ b/src/web/build_tools/proxy/nginx.conf.template @@ -5,14 +5,6 @@ events { worker_connections 1024; } -server { - listen 80 default_server; - server_name _; - - location / { - proxy_pass http://web.argus.com:80; - } -} http { include mime.types; @@ -32,5 +24,16 @@ http { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; + server { + listen 80 default_server; + server_name _; + + location / { + set $web_backend http://web.argus.com:8080; + proxy_pass $web_backend; + } + } + + include /etc/nginx/conf.d/*.conf; } diff --git a/src/web/build_tools/proxy/start-proxy-supervised.sh b/src/web/build_tools/proxy/start-proxy-supervised.sh index ac276dd..51c2b7b 100644 --- a/src/web/build_tools/proxy/start-proxy-supervised.sh +++ b/src/web/build_tools/proxy/start-proxy-supervised.sh @@ -9,8 +9,8 @@ DNS_CONF_PRIVATE="/private/argus/etc/dns.conf" DNS_CONF_SYSTEM="/etc/resolv.conf" DNS_DIR="/private/argus/etc" DNS_SCRIPT="${DNS_DIR}/update-dns.sh" -RUNTIME_UID="${ARGUS_BUILD_UID:-2133}" -RUNTIME_GID="${ARGUS_BUILD_GID:-2015}" +RUNTIME_UID="${ARGUS_UID:-2133}" +RUNTIME_GID="${ARGUS_GID:-2015}" mkdir -p "$DNS_DIR" chown -R "$RUNTIME_UID:$RUNTIME_GID" "$DNS_DIR" 2>/dev/null || true diff --git a/src/web/src/components/AlertFilters.jsx b/src/web/src/components/AlertFilters.jsx index 73a8a6f..dac24b2 100644 --- a/src/web/src/components/AlertFilters.jsx +++ b/src/web/src/components/AlertFilters.jsx @@ -8,10 +8,10 @@ export function AlertFilters({ filters, setFilters, nodeOptions }) { value={filters.severity} onChange={(value) => setFilters((f) => ({ ...f, severity: value }))} data={[ - { value: "all", label: "全部" }, - { value: "critical", label: "严重" }, - { value: "warning", label: "警告" }, - { value: "info", label: "信息" }, + { value: "all", label: "all" }, + { value: "critical", label: "critical" }, + { value: "warning", label: "warning" }, + { value: "info", label: "info" }, ]} w={150} /> @@ -20,7 +20,7 @@ export function AlertFilters({ filters, setFilters, nodeOptions }) { value={filters.state} onChange={(value) => setFilters((f) => ({ ...f, state: value }))} data={[ - { value: "all", label: "全部" }, + { value: "all", label: "all" }, { value: "active", label: "Active" }, { value: "resolved", label: "Resolved" }, ]} diff --git a/src/web/src/components/AlertTable.jsx b/src/web/src/components/AlertTable.jsx index 9bc2219..ef73228 100644 --- a/src/web/src/components/AlertTable.jsx +++ b/src/web/src/components/AlertTable.jsx @@ -1,5 +1,6 @@ -import { Table, Group, ActionIcon, Button } from "@mantine/core"; -import { IconChevronUp, IconChevronDown } from "@tabler/icons-react"; +import { Table, Group, ActionIcon, Button, Code } from "@mantine/core"; +import { IconChevronUp, IconChevronDown, IconInfoCircle } from "@tabler/icons-react"; +import { useState } from "react"; export function AlertTable({ alerts, @@ -16,6 +17,11 @@ export function AlertTable({ formatRelativeTime, }) { const totalPages = Math.ceil(sortedAlerts.length / pageSize); + const [expandedRow, setExpandedRow] = useState(null); + + const toggleExpand = (index) => { + setExpandedRow(expandedRow === index ? null : index); + }; return ( <> @@ -47,24 +53,52 @@ export function AlertTable({ ))} + 更多信息 + {paginatedAlerts.map((alert, i) => ( - - {alert.labels?.alertname || "-"} - {alert.labels?.instance || "-"} - - {alert.labels?.severity || "info"} - - {getStateBadge(alert.status?.state)} - {formatRelativeTime(alert.startsAt)} - - {alert.endsAt ? new Date(alert.endsAt).toLocaleString() : "-"} - - {formatRelativeTime(alert.updatedAt)} - {alert.annotations?.summary || "-"} - + <> + + {alert.labels?.alertname || "-"} + {alert.labels?.instance || "-"} + + {alert.labels?.severity || "info"} + + {getStateBadge(alert.status?.state)} + {formatRelativeTime(alert.startsAt)} + + {alert.endsAt ? new Date(alert.endsAt).toLocaleString() : "-"} + + {formatRelativeTime(alert.updatedAt)} + {alert.annotations?.summary || "-"} + + toggleExpand(i)} + variant="subtle" + color="blue" + title="显示/隐藏更多信息" + > + + + + + + {expandedRow === i && ( + + +
+ {Object.entries(alert.labels || {}).map(([k, v]) => ( +
+ {k} :{v} +
+ ))} +
+
+
+ )} + ))}
diff --git a/src/web/src/components/NodeDetailDrawer.jsx b/src/web/src/components/NodeDetailDrawer.jsx index 455d8aa..f682ab1 100644 --- a/src/web/src/components/NodeDetailDrawer.jsx +++ b/src/web/src/components/NodeDetailDrawer.jsx @@ -1,4 +1,4 @@ -import { useState, useEffect } from "react"; +import { useState, useEffect } from "react"; import { Drawer, Text, @@ -102,11 +102,6 @@ export default function NodeDetailDrawer({ opened, nodeId, onClose }) { {/* 滚动内容 */} - {/* 配置信息 */} - fetchNodeDetail(node.id)} /> - - {/* 标签信息 */} - fetchNodeDetail(node.id)} /> {/* 元数据 */} @@ -114,6 +109,12 @@ export default function NodeDetailDrawer({ opened, nodeId, onClose }) { {/* 健康信息 */} + {/* 配置信息 */} + fetchNodeDetail(node.id)} /> + + {/* 标签信息 */} + fetchNodeDetail(node.id)} /> + {/* 其他基础信息展示 */} 注册时间: {new Date(node.register_time).toLocaleString()} diff --git a/src/web/src/components/NodeHealthCard.jsx b/src/web/src/components/NodeHealthCard.jsx index 2039320..b265a32 100644 --- a/src/web/src/components/NodeHealthCard.jsx +++ b/src/web/src/components/NodeHealthCard.jsx @@ -2,54 +2,61 @@ import { useState } from "react"; import { Card, Text, Stack, Group, ActionIcon, Badge, Popover } from "@mantine/core"; import { IconInfoCircle } from "@tabler/icons-react"; +// 子组件:单条健康信息 +function HealthItem({ moduleName, data }) { + const status = data?.status || "unknown"; + const color = status === "healthy" ? "green" : status === "unhealthy" ? "red" : "gray"; + const [opened, setOpened] = useState(false); + + return ( + + {moduleName} + {status} + {(data?.error || data?.timestamp) && ( + setOpened(false)} + position="bottom" + withArrow + shadow="sm" + > + + setOpened((o) => !o)} + > + + + + + + {data.error && Error: {data.error}} + {data.timestamp && ( + + Updated: {new Date(data.timestamp).toLocaleString()} + + )} + + + + )} + + ); +} + +// 主组件:健康信息卡片 export default function NodeHealthCard({ node }) { const health = node.health || {}; - const renderHealthItem = (moduleName, data) => { - const status = data?.status || "unknown"; - const color = status === "healthy" ? "green" : status === "unhealthy" ? "red" : "gray"; - const [opened, setOpened] = useState(false); - - return ( - - {moduleName} - {status} - {(data?.error || data?.timestamp) && ( - setOpened(false)} - position="bottom" - withArrow - shadow="sm" - > - - setOpened((o) => !o)}> - - - - - - {data.error && Error: {data.error}} - {data.timestamp && ( - - Updated: {new Date(data.timestamp).toLocaleString()} - - )} - - - - )} - - ); - }; - return ( 健康信息 - {Object.entries(health).map(([moduleName, data]) => - renderHealthItem(moduleName, data) - )} + {Object.entries(health).map(([moduleName, data]) => ( + + ))} ); diff --git a/src/web/src/components/NodeTable.jsx b/src/web/src/components/NodeTable.jsx index a3320de..d00c12e 100644 --- a/src/web/src/components/NodeTable.jsx +++ b/src/web/src/components/NodeTable.jsx @@ -4,7 +4,7 @@ import { Link } from "react-router-dom"; import NodeStatus from "./NodeStatus"; import PaginationControl from "./PaginationControl"; import { apiRequest } from "../config/request"; -import { MASTER_API } from "../config/api"; +import { MASTER_API, EXTERNAL_HOST } from "../config/api"; export function NodeTable({ withSearch = false, @@ -56,13 +56,28 @@ export function NodeTable({ {node.version} {withActions && ( - + + {/* 查看详情 */} + + + {/* Grafana Dashboard 链接 */} + + )} @@ -71,8 +86,6 @@ export function NodeTable({ return ( {/* 标题 + 查看更多 */} - - {(title || viewMoreLink) && ( {title && {title}} diff --git a/src/web/src/components/Sidebar.jsx b/src/web/src/components/Sidebar.jsx index 7eeda28..e80a68c 100644 --- a/src/web/src/components/Sidebar.jsx +++ b/src/web/src/components/Sidebar.jsx @@ -12,7 +12,7 @@ export default function Sidebar() { const location = useLocation(); // 路由变化时会触发 Sidebar 重新渲染 const links = [ - { to: "/dashboard", label: "概览仪表盘", icon: }, + { to: "/dashboard", label: "仪表盘", icon: }, { to: "/nodeInfo", label: "节点信息", icon: }, { to: "/metrics", label: "指标详情", icon: }, { to: "/logs", label: "日志详情", icon: }, diff --git a/src/web/src/config/api.js b/src/web/src/config/api.js index 3aeb0ce..dbbfcdd 100644 --- a/src/web/src/config/api.js +++ b/src/web/src/config/api.js @@ -17,14 +17,13 @@ export const MASTER_API = { // 其他外部 API export const EXTERNAL_API = { - ALERTS_INFOS: "http://localhost:9093/api/v2/alerts", + ALERTS_INFOS: "http://alertmanager.alert.argus.com/api/v2/alerts", }; // 外部服务 Host export const EXTERNAL_HOST = { - ALERTS: "http://localhost:9093", - GRAFANA: "http://grafana.metric.argus.com", + ALERTS: "http://alertmanager.alert.argus.com", + GRAFANA: "http://grafana.metric.argus.com/d/cluster-dashboard/cluster-dashboard?orgId=1&refresh=5s", PROMETHEUS: "http://prometheus.metric.argus.com", - ES: "http://es.log.argus.com", - KIBANA: "http://kibana.log.argus.com", + KIBANA: "http://kibana.log.argus.com/app/discover", }; diff --git a/src/web/src/config/entries.js b/src/web/src/config/entries.js index 0277d49..9b779f4 100644 --- a/src/web/src/config/entries.js +++ b/src/web/src/config/entries.js @@ -10,7 +10,6 @@ export const metricsEntries = [ ]; export const logsEntries = [ - { label: "Elasticsearch", href: EXTERNAL_HOST.ES, icon: esLogo }, { label: "Kibana", href: EXTERNAL_HOST.KIBANA, icon: kibanaLogo }, ]; diff --git a/src/web/src/pages/Alerts.jsx b/src/web/src/pages/Alerts.jsx index 641cd21..06efd27 100644 --- a/src/web/src/pages/Alerts.jsx +++ b/src/web/src/pages/Alerts.jsx @@ -1,5 +1,5 @@ import { useEffect, useState, useMemo } from "react"; -import { Stack, Title, Loader, Center, Group, Button, Badge, ActionIcon } from "@mantine/core"; +import { Stack, Title, Loader, Center, Group, Button, Badge, ActionIcon, Switch } from "@mantine/core"; import { IconRefresh } from "@tabler/icons-react"; import { apiRequest } from "../config/request"; import { EXTERNAL_API } from "../config/api"; @@ -17,18 +17,20 @@ export default function Alerts() { const [page, setPage] = useState(1); const pageSize = 10; const [sortConfig, setSortConfig] = useState({ key: "startsAt", direction: "desc" }); + const [autoRefresh, setAutoRefresh] = useState(true); // 默认开启自动刷新 async function fetchAlerts() { setLoading(true); const data = await apiRequest(EXTERNAL_API.ALERTS_INFOS); if (data && Array.isArray(data)) { setAlerts(data); - const counts = { critical: 0, warning: 0, info: 0 }; + const counts = { critical: 0, warning: 0, info: 0, total: 0 }; data.forEach((alert) => { const sev = alert.labels?.severity || "info"; if (sev === "critical") counts.critical++; else if (sev === "warning") counts.warning++; else counts.info++; + counts.total++; }); setStats(counts); } @@ -37,9 +39,14 @@ export default function Alerts() { useEffect(() => { fetchAlerts(); - const timer = setInterval(fetchAlerts, 30000); + + let timer; + if (autoRefresh) { + timer = setInterval(fetchAlerts, 30000); + } + return () => clearInterval(timer); - }, []); + }, [autoRefresh]); // 节点选项 const nodeOptions = useMemo(() => { @@ -124,7 +131,14 @@ export default function Alerts() { 告警详情 - diff --git a/src/web/src/pages/Dashboard.jsx b/src/web/src/pages/Dashboard.jsx index 01916cd..bbd3335 100644 --- a/src/web/src/pages/Dashboard.jsx +++ b/src/web/src/pages/Dashboard.jsx @@ -1,5 +1,5 @@ import { useEffect, useState } from "react"; -import { Grid, Text } from "@mantine/core"; +import { Grid, Text, Stack, Title } from "@mantine/core"; import { NodeTable } from "../components/NodeTable"; import { HealthCard } from "../components/HealthCard"; import { AlertStats } from "../components/AlertStats"; @@ -14,12 +14,13 @@ export default function Dashboard() { const [loading, setLoading] = useState(true); const countAlerts = (data) => { - const stats = { critical: 0, warning: 0, info: 0 }; + const stats = { critical: 0, warning: 0, info: 0, total: 0 }; data?.forEach((alert) => { const severity = alert.labels?.severity || "info"; if (severity === "critical") stats.critical++; else if (severity === "warning") stats.warning++; else stats.info++; + stats.total++; }); return stats; }; @@ -41,7 +42,7 @@ export default function Dashboard() { status_statistics: healthRes?.status_statistics || [], }); - setAlerts(countAlerts(alertsRes?.data || [])); + setAlerts(countAlerts(alertsRes || [])); } catch (err) { console.error("获取 Dashboard 数据失败:", err); } finally { @@ -61,13 +62,16 @@ export default function Dashboard() { } return ( - + + 仪表盘 + - - - - - - + + + + + + + ); } diff --git a/src/web/src/pages/Metrics.jsx b/src/web/src/pages/Metrics.jsx index 311ed0a..fcd2365 100644 --- a/src/web/src/pages/Metrics.jsx +++ b/src/web/src/pages/Metrics.jsx @@ -5,7 +5,7 @@ import { metricsEntries } from "../config/entries"; export default function Metrics() { return ( - 指标入口 + 指标详情 {metricsEntries.map((entry) => ( diff --git a/src/web/src/pages/NodePage.jsx b/src/web/src/pages/NodePage.jsx index af8a257..5072fb8 100644 --- a/src/web/src/pages/NodePage.jsx +++ b/src/web/src/pages/NodePage.jsx @@ -1,5 +1,5 @@ import { useState } from "react"; -import { Grid } from "@mantine/core"; +import { Grid, Stack, Title } from "@mantine/core"; import { apiRequest } from "../config/request"; import { MASTER_API } from "../config/api"; import { NodeTable } from "../components/NodeTable"; @@ -23,24 +23,30 @@ export default function NodePage() { }; return ( - - {/* 左侧:节点表格 */} - - - + + + 节点信息 + - {/* 节点详情 Drawer */} - setDrawerOpen(false)} - nodeId={selectedNodeId} - loading={detailLoading} - /> - + + {/* 左侧:节点表格 */} + + + + + {/* 节点详情 Drawer */} + setDrawerOpen(false)} + nodeId={selectedNodeId} + loading={detailLoading} + /> + + ); } diff --git a/src/web/tests/data/etc/web.argus.com b/src/web/tests/data/etc/web.argus.com deleted file mode 100644 index f7395bb..0000000 --- a/src/web/tests/data/etc/web.argus.com +++ /dev/null @@ -1 +0,0 @@ -172.18.0.3 diff --git a/src/web/tests/docker-compose.yml b/src/web/tests/docker-compose.yml index 7212466..985e22c 100644 --- a/src/web/tests/docker-compose.yml +++ b/src/web/tests/docker-compose.yml @@ -1,4 +1,3 @@ -version: '3.8' services: web-frontend: build: @@ -20,7 +19,7 @@ services: - ${DATA_ROOT:-./data}/web:/private/argus/web/frontend - ${DATA_ROOT:-./data}/etc:/private/argus/etc networks: - - argus-network + - argus-debug-net restart: unless-stopped logging: driver: "json-file" @@ -45,7 +44,7 @@ services: volumes: - ${DATA_ROOT:-./data}/etc:/private/argus/etc networks: - - argus-network + - argus-debug-net restart: unless-stopped logging: driver: "json-file" @@ -54,9 +53,8 @@ services: max-file: "3" networks: - argus-network: - driver: bridge - name: argus-network + argus-debug-net: + external: true volumes: web-frontend_data: