Compare commits
12 Commits
dev_1.0.0_
...
2ff7c55f3b
| Author | SHA1 | Date | |
|---|---|---|---|
| 2ff7c55f3b | |||
| 9858f4471e | |||
| c8279997a4 | |||
| 4ed5c64804 | |||
| 3551360687 | |||
| 3202e02b42 | |||
| 29eb75a374 | |||
| ccc141f557 | |||
| ed0d1ca904 | |||
|
|
b6da5bc8b8 | ||
| 59a38513a4 | |||
|
|
d1b89c0cf6 |
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
||||
src/metric/client-plugins/all-in-one-full/plugins/*/bin/* filter=lfs diff=lfs merge=lfs -text
|
||||
@@ -10,20 +10,28 @@ Usage: $0 [OPTIONS]
|
||||
Options:
|
||||
--intranet Use intranet mirror for log/bind builds
|
||||
--master-offline Build master offline image (requires src/master/offline_wheels.tar.gz)
|
||||
--metric Build metric module images (ftp, prometheus, grafana, test nodes)
|
||||
--no-cache Build all images without using Docker layer cache
|
||||
--only LIST Comma-separated targets to build: core,master,metric,web,alert,sys,all
|
||||
-h, --help Show this help message
|
||||
|
||||
Examples:
|
||||
$0 # Build with default sources
|
||||
$0 --intranet # Build with intranet mirror
|
||||
$0 --master-offline # Additionally build argus-master:offline
|
||||
$0 --intranet --master-offline
|
||||
$0 --metric # Additionally build metric module images
|
||||
$0 --intranet --master-offline --metric
|
||||
EOF
|
||||
}
|
||||
|
||||
use_intranet=false
|
||||
build_core=true
|
||||
build_master=true
|
||||
build_master_offline=false
|
||||
build_metric=true
|
||||
build_web=true
|
||||
build_alert=true
|
||||
build_sys=true
|
||||
no_cache=false
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
@@ -41,10 +49,35 @@ while [[ $# -gt 0 ]]; do
|
||||
build_master_offline=true
|
||||
shift
|
||||
;;
|
||||
--metric)
|
||||
build_metric=true
|
||||
shift
|
||||
;;
|
||||
--no-cache)
|
||||
no_cache=true
|
||||
shift
|
||||
;;
|
||||
--only)
|
||||
if [[ -z ${2:-} ]]; then
|
||||
echo "--only requires a target list" >&2; exit 1
|
||||
fi
|
||||
sel="$2"; shift 2
|
||||
# reset all, then enable selected
|
||||
build_core=false; build_master=false; build_metric=false; build_web=false; build_alert=false; build_sys=false
|
||||
IFS=',' read -ra parts <<< "$sel"
|
||||
for p in "${parts[@]}"; do
|
||||
case "$p" in
|
||||
core) build_core=true ;;
|
||||
master) build_master=true ;;
|
||||
metric) build_metric=true ;;
|
||||
web) build_web=true ;;
|
||||
alert) build_alert=true ;;
|
||||
sys) build_sys=true ;;
|
||||
all) build_core=true; build_master=true; build_metric=true; build_web=true; build_alert=true; build_sys=true ;;
|
||||
*) echo "Unknown --only target: $p" >&2; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
;;
|
||||
-h|--help)
|
||||
show_help
|
||||
exit 0
|
||||
@@ -115,14 +148,22 @@ build_image() {
|
||||
local image_name=$1
|
||||
local dockerfile_path=$2
|
||||
local tag=$3
|
||||
local context="."
|
||||
shift 3
|
||||
|
||||
if [[ $# -gt 0 ]]; then
|
||||
context=$1
|
||||
shift
|
||||
fi
|
||||
|
||||
local extra_args=("$@")
|
||||
|
||||
echo "🔄 Building $image_name image..."
|
||||
echo " Dockerfile: $dockerfile_path"
|
||||
echo " Tag: $tag"
|
||||
echo " Context: $context"
|
||||
|
||||
if docker build "${build_args[@]}" "${extra_args[@]}" -f "$dockerfile_path" -t "$tag" .; then
|
||||
if docker build "${build_args[@]}" "${extra_args[@]}" -f "$dockerfile_path" -t "$tag" "$context"; then
|
||||
echo "✅ $image_name image built successfully"
|
||||
return 0
|
||||
else
|
||||
@@ -131,29 +172,59 @@ build_image() {
|
||||
fi
|
||||
}
|
||||
|
||||
pull_base_image() {
|
||||
local image_ref=$1
|
||||
local attempts=${2:-3}
|
||||
local delay=${3:-5}
|
||||
|
||||
# If the image already exists locally, skip pulling.
|
||||
if docker image inspect "$image_ref" >/dev/null 2>&1; then
|
||||
echo " Local image present; skip pull: $image_ref"
|
||||
return 0
|
||||
fi
|
||||
|
||||
for ((i=1; i<=attempts; i++)); do
|
||||
echo " Pulling base image ($i/$attempts): $image_ref"
|
||||
if docker pull "$image_ref" >/dev/null; then
|
||||
echo " Base image ready: $image_ref"
|
||||
return 0
|
||||
fi
|
||||
echo " Pull failed: $image_ref"
|
||||
if (( i < attempts )); then
|
||||
echo " Retrying in ${delay}s..."
|
||||
sleep "$delay"
|
||||
fi
|
||||
done
|
||||
|
||||
echo "❌ Unable to pull base image after ${attempts} attempts: $image_ref"
|
||||
return 1
|
||||
}
|
||||
|
||||
images_built=()
|
||||
build_failed=false
|
||||
|
||||
if build_image "Elasticsearch" "src/log/elasticsearch/build/Dockerfile" "argus-elasticsearch:latest"; then
|
||||
images_built+=("argus-elasticsearch:latest")
|
||||
else
|
||||
build_failed=true
|
||||
fi
|
||||
if [[ "$build_core" == true ]]; then
|
||||
if build_image "Elasticsearch" "src/log/elasticsearch/build/Dockerfile" "argus-elasticsearch:latest"; then
|
||||
images_built+=("argus-elasticsearch:latest")
|
||||
else
|
||||
build_failed=true
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo ""
|
||||
|
||||
if build_image "Kibana" "src/log/kibana/build/Dockerfile" "argus-kibana:latest"; then
|
||||
images_built+=("argus-kibana:latest")
|
||||
else
|
||||
build_failed=true
|
||||
fi
|
||||
if build_image "Kibana" "src/log/kibana/build/Dockerfile" "argus-kibana:latest"; then
|
||||
images_built+=("argus-kibana:latest")
|
||||
else
|
||||
build_failed=true
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo ""
|
||||
|
||||
if build_image "BIND9" "src/bind/build/Dockerfile" "argus-bind9:latest"; then
|
||||
images_built+=("argus-bind9:latest")
|
||||
else
|
||||
build_failed=true
|
||||
if build_image "BIND9" "src/bind/build/Dockerfile" "argus-bind9:latest"; then
|
||||
images_built+=("argus-bind9:latest")
|
||||
else
|
||||
build_failed=true
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
@@ -184,6 +255,127 @@ if [[ "$build_master" == true ]]; then
|
||||
popd >/dev/null
|
||||
fi
|
||||
|
||||
if [[ "$build_metric" == true ]]; then
|
||||
echo ""
|
||||
echo "Building Metric module images..."
|
||||
|
||||
metric_base_images=(
|
||||
"ubuntu:22.04"
|
||||
"ubuntu/prometheus:3-24.04_stable"
|
||||
"grafana/grafana:11.1.0"
|
||||
)
|
||||
|
||||
for base_image in "${metric_base_images[@]}"; do
|
||||
if ! pull_base_image "$base_image"; then
|
||||
build_failed=true
|
||||
fi
|
||||
done
|
||||
|
||||
metric_builds=(
|
||||
"Metric FTP|src/metric/ftp/build/Dockerfile|argus-metric-ftp:latest|src/metric/ftp/build"
|
||||
"Metric Prometheus|src/metric/prometheus/build/Dockerfile|argus-metric-prometheus:latest|src/metric/prometheus/build"
|
||||
"Metric Grafana|src/metric/grafana/build/Dockerfile|argus-metric-grafana:latest|src/metric/grafana/build"
|
||||
)
|
||||
|
||||
for build_spec in "${metric_builds[@]}"; do
|
||||
IFS='|' read -r image_label dockerfile_path image_tag build_context <<< "$build_spec"
|
||||
if build_image "$image_label" "$dockerfile_path" "$image_tag" "$build_context"; then
|
||||
images_built+=("$image_tag")
|
||||
else
|
||||
build_failed=true
|
||||
fi
|
||||
echo ""
|
||||
done
|
||||
fi
|
||||
|
||||
# =======================================
|
||||
# Sys (system tests) node images
|
||||
# =======================================
|
||||
|
||||
if [[ "$build_sys" == true ]]; then
|
||||
echo ""
|
||||
echo "Building Sys node images..."
|
||||
|
||||
sys_base_images=(
|
||||
"ubuntu:22.04"
|
||||
"nvidia/cuda:12.2.2-runtime-ubuntu22.04"
|
||||
)
|
||||
|
||||
for base_image in "${sys_base_images[@]}"; do
|
||||
if ! pull_base_image "$base_image"; then
|
||||
build_failed=true
|
||||
fi
|
||||
done
|
||||
|
||||
sys_builds=(
|
||||
"Sys Node|src/sys/build/node/Dockerfile|argus-sys-node:latest|."
|
||||
"Sys Metric Test Node|src/sys/build/test-node/Dockerfile|argus-sys-metric-test-node:latest|."
|
||||
"Sys Metric Test GPU Node|src/sys/build/test-gpu-node/Dockerfile|argus-sys-metric-test-gpu-node:latest|."
|
||||
)
|
||||
|
||||
for build_spec in "${sys_builds[@]}"; do
|
||||
IFS='|' read -r image_label dockerfile_path image_tag build_context <<< "$build_spec"
|
||||
if build_image "$image_label" "$dockerfile_path" "$image_tag" "$build_context"; then
|
||||
images_built+=("$image_tag")
|
||||
else
|
||||
build_failed=true
|
||||
fi
|
||||
echo ""
|
||||
done
|
||||
fi
|
||||
|
||||
# =======================================
|
||||
# Web & Alert module images
|
||||
# =======================================
|
||||
|
||||
if [[ "$build_web" == true || "$build_alert" == true ]]; then
|
||||
echo ""
|
||||
echo "Building Web and Alert module images..."
|
||||
|
||||
# Pre-pull commonly used base images for stability
|
||||
web_alert_base_images=(
|
||||
"node:20"
|
||||
"ubuntu:24.04"
|
||||
)
|
||||
|
||||
for base_image in "${web_alert_base_images[@]}"; do
|
||||
if ! pull_base_image "$base_image"; then
|
||||
build_failed=true
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ "$build_web" == true ]]; then
|
||||
web_builds=(
|
||||
"Web Frontend|src/web/build_tools/frontend/Dockerfile|argus-web-frontend:latest|."
|
||||
"Web Proxy|src/web/build_tools/proxy/Dockerfile|argus-web-proxy:latest|."
|
||||
)
|
||||
for build_spec in "${web_builds[@]}"; do
|
||||
IFS='|' read -r image_label dockerfile_path image_tag build_context <<< "$build_spec"
|
||||
if build_image "$image_label" "$dockerfile_path" "$image_tag" "$build_context"; then
|
||||
images_built+=("$image_tag")
|
||||
else
|
||||
build_failed=true
|
||||
fi
|
||||
echo ""
|
||||
done
|
||||
fi
|
||||
|
||||
if [[ "$build_alert" == true ]]; then
|
||||
alert_builds=(
|
||||
"Alertmanager|src/alert/alertmanager/build/Dockerfile|argus-alertmanager:latest|."
|
||||
)
|
||||
for build_spec in "${alert_builds[@]}"; do
|
||||
IFS='|' read -r image_label dockerfile_path image_tag build_context <<< "$build_spec"
|
||||
if build_image "$image_label" "$dockerfile_path" "$image_tag" "$build_context"; then
|
||||
images_built+=("$image_tag")
|
||||
else
|
||||
build_failed=true
|
||||
fi
|
||||
echo ""
|
||||
done
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "======================================="
|
||||
echo "📦 Build Summary"
|
||||
echo "======================================="
|
||||
@@ -210,7 +402,6 @@ if [[ "$build_master_offline" == true ]]; then
|
||||
echo ""
|
||||
echo "🧳 Master offline wheels 已解压到 $master_offline_dir"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "🚀 Next steps:"
|
||||
echo " ./build/save_images.sh --compress # 导出镜像"
|
||||
|
||||
@@ -68,6 +68,12 @@ declare -A images=(
|
||||
["argus-kibana:latest"]="argus-kibana-latest.tar"
|
||||
["argus-bind9:latest"]="argus-bind9-latest.tar"
|
||||
["argus-master:offline"]="argus-master-offline.tar"
|
||||
["argus-metric-ftp:latest"]="argus-metric-ftp-latest.tar"
|
||||
["argus-metric-prometheus:latest"]="argus-metric-prometheus-latest.tar"
|
||||
["argus-metric-grafana:latest"]="argus-metric-grafana-latest.tar"
|
||||
["argus-web-frontend:latest"]="argus-web-frontend-latest.tar"
|
||||
["argus-web-proxy:latest"]="argus-web-proxy-latest.tar"
|
||||
["argus-alertmanager:latest"]="argus-alertmanager-latest.tar"
|
||||
)
|
||||
|
||||
# 函数:检查镜像是否存在
|
||||
|
||||
1
deployment/.gitignore
vendored
Normal file
1
deployment/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
artifact/
|
||||
16
deployment/build/README.md
Normal file
16
deployment/build/README.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# Deployment Build Toolkit
|
||||
|
||||
This folder provides scripts to produce offline server/client packages and publish the client package to FTP.
|
||||
|
||||
Commands
|
||||
- build_server_package.sh [--version YYYYMMDD]
|
||||
- build_client_package.sh [--version YYYYMMDD]
|
||||
- publish_client.sh --version YYYYMMDD --server <host> --user ftpuser --password <pass> [--port 21]
|
||||
|
||||
Outputs
|
||||
- deployment/artifact/server/<YYYYMMDD>/
|
||||
- deployment/artifact/client/<YYYYMMDD>/
|
||||
|
||||
Notes
|
||||
- Server package contains docker images (single all-images.tar.gz), compose/, scripts/, docs/, private/ skeleton.
|
||||
- Client package reuses all-in-one-full artifact, repacked as argus-metric_<YYYYMMDD>.tar.gz (compatible with setup.sh).
|
||||
90
deployment/build/build_client_package.sh
Executable file
90
deployment/build/build_client_package.sh
Executable file
@@ -0,0 +1,90 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
BUILD_DIR="$ROOT_DIR/deployment/build"
|
||||
ART_ROOT="$ROOT_DIR/deployment/artifact"
|
||||
|
||||
. "$BUILD_DIR/common.sh"
|
||||
|
||||
usage() { cat <<'EOF'
|
||||
Build Argus Client Offline Package
|
||||
|
||||
Usage: build_client_package.sh [--version YYYYMMDD] [--out DIR]
|
||||
|
||||
Produces: deployment/artifact/client/<YYYYMMDD>/argus-metric_<YYYYMMDD>.tar.gz
|
||||
EOF
|
||||
}
|
||||
|
||||
VERSION="$(today_version)"
|
||||
OUT_DIR=""
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--version) VERSION="$2"; shift 2;;
|
||||
--out) OUT_DIR="$2"; shift 2;;
|
||||
-h|--help) usage; exit 0;;
|
||||
*) err "unknown arg: $1"; usage; exit 1;;
|
||||
esac
|
||||
done
|
||||
|
||||
PKG_DIR="${OUT_DIR:-$ART_ROOT/client/$VERSION}"
|
||||
make_dir "$PKG_DIR"
|
||||
|
||||
log "Packaging client from all-in-one-full artifact"
|
||||
PLUGIN_DIR="$ROOT_DIR/src/metric/client-plugins/all-in-one-full"
|
||||
require_cmd bash tar gzip
|
||||
|
||||
(cd "$PLUGIN_DIR" && bash scripts/package_artifact.sh --force)
|
||||
|
||||
# pick latest artifact dir
|
||||
ART_BASE="$PLUGIN_DIR/artifact"
|
||||
latest_dir=$(ls -1dt "$ART_BASE"/*/ 2>/dev/null | head -n1 || true)
|
||||
[[ -n "$latest_dir" ]] || { err "no client artifact found in $ART_BASE"; exit 1; }
|
||||
|
||||
tmpdir=$(mktemp -d)
|
||||
trap 'rm -rf "$tmpdir"' EXIT
|
||||
# Filter-only copy: keep install_order files + scripts + deps + version.json
|
||||
mkdir -p "$tmpdir/src"
|
||||
cp -f "$latest_dir/version.json" "$tmpdir/src/version.json"
|
||||
if command -v jq >/dev/null 2>&1; then
|
||||
mapfile -t files < <(jq -r '.install_order[]' "$latest_dir/version.json")
|
||||
else
|
||||
files=( $(grep -E '"install_order"' -A10 "$latest_dir/version.json" | sed -n 's/\s*"\(.*\.tar\.gz\)".*/\1/p') )
|
||||
fi
|
||||
for f in "${files[@]}"; do
|
||||
[[ -f "$latest_dir/$f" ]] && cp -f "$latest_dir/$f" "$tmpdir/src/$f"
|
||||
done
|
||||
for aux in install.sh uninstall.sh check_health.sh check_version.sh sync_dns.sh restart_unhealthy.sh config.env; do
|
||||
[[ -f "$latest_dir/$aux" ]] && cp -f "$latest_dir/$aux" "$tmpdir/src/$aux";
|
||||
done
|
||||
if [[ -d "$latest_dir/deps" ]]; then
|
||||
mkdir -p "$tmpdir/src/deps" && rsync -a "$latest_dir/deps/" "$tmpdir/src/deps/" >/dev/null 2>&1 || cp -r "$latest_dir/deps/." "$tmpdir/src/deps/";
|
||||
fi
|
||||
|
||||
out_name="argus-metric_$(echo "$VERSION" | sed 's/\./_/g').tar.gz"
|
||||
|
||||
(cd "$tmpdir/src" && tar -czf "$PKG_DIR/$out_name" .)
|
||||
|
||||
log "Client package ready: $PKG_DIR/$out_name"
|
||||
echo "$VERSION" > "$PKG_DIR/LATEST_VERSION"
|
||||
|
||||
# include publish helper and setup.sh for convenience (place first)
|
||||
PUBLISH_TPL="$BUILD_DIR/templates/client/publish.sh"
|
||||
if [[ -f "$PUBLISH_TPL" ]]; then
|
||||
cp "$PUBLISH_TPL" "$PKG_DIR/publish.sh"
|
||||
fi
|
||||
|
||||
# also place a copy of setup.sh alongside
|
||||
SETUP_SRC="$ROOT_DIR/src/metric/client-plugins/all-in-one-full/scripts/setup.sh"
|
||||
[[ -f "$SETUP_SRC" ]] && cp "$SETUP_SRC" "$PKG_DIR/setup.sh" || true
|
||||
|
||||
# docs for end users (this may overwrite file modes), then fix execute bits
|
||||
CLIENT_DOC_DIR="$BUILD_DIR/templates/client"
|
||||
if [[ -d "$CLIENT_DOC_DIR" ]]; then
|
||||
rsync -a "$CLIENT_DOC_DIR/" "$PKG_DIR/" >/dev/null 2>&1 || cp -r "$CLIENT_DOC_DIR/." "$PKG_DIR/"
|
||||
fi
|
||||
|
||||
# ensure helpers are executable
|
||||
chmod +x "$PKG_DIR/publish.sh" "$PKG_DIR/setup.sh" 2>/dev/null || true
|
||||
|
||||
exit 0
|
||||
138
deployment/build/build_server_package.sh
Executable file
138
deployment/build/build_server_package.sh
Executable file
@@ -0,0 +1,138 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
BUILD_DIR="$ROOT_DIR/deployment/build"
|
||||
ART_ROOT="$ROOT_DIR/deployment/artifact"
|
||||
|
||||
. "$BUILD_DIR/common.sh"
|
||||
|
||||
usage() { cat <<'EOF'
|
||||
Build Argus Server Offline Package
|
||||
|
||||
Usage: build_server_package.sh [--version YYYYMMDD] [--out DIR] [--resave-image]
|
||||
|
||||
Outputs into deployment/artifact/server/<YYYYMMDD>/ by default.
|
||||
EOF
|
||||
}
|
||||
|
||||
VERSION="$(today_version)"
|
||||
OUT_DIR=""
|
||||
RESAVE_IMAGE=false
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--version) VERSION="$2"; shift 2;;
|
||||
--out) OUT_DIR="$2"; shift 2;;
|
||||
--resave-image) RESAVE_IMAGE=true; shift;;
|
||||
-h|--help) usage; exit 0;;
|
||||
*) err "unknown arg: $1"; usage; exit 1;;
|
||||
esac
|
||||
done
|
||||
|
||||
PKG_DIR="${OUT_DIR:-$ART_ROOT/server/$VERSION}"
|
||||
STAGE="$(mktemp -d)"
|
||||
trap 'rm -rf "$STAGE"' EXIT
|
||||
|
||||
log "Version: $VERSION"
|
||||
log "Staging: $STAGE"
|
||||
|
||||
# 1) Layout
|
||||
make_dir "$STAGE/images"
|
||||
make_dir "$STAGE/compose"
|
||||
make_dir "$STAGE/scripts"
|
||||
make_dir "$STAGE/docs"
|
||||
make_dir "$STAGE/private/argus"
|
||||
|
||||
# 2) Compose: derive from sys/tests by removing test-only services
|
||||
SRC_COMPOSE="$ROOT_DIR/src/sys/tests/docker-compose.yml"
|
||||
[[ -f "$SRC_COMPOSE" ]] || { err "missing $SRC_COMPOSE"; exit 1; }
|
||||
# 2.1 filter out test services
|
||||
tmp_compose1="$STAGE/compose/docker-compose.filtered.yml"
|
||||
awk -f "$BUILD_DIR/templates/docker-compose.filter.awk" -v remove="node-a,node-b,test-node,test-gpu-node" "$SRC_COMPOSE" > "$tmp_compose1"
|
||||
# 2.2 transform to external overlay network (remove sysnet and per-service blocks)
|
||||
awk -f "$BUILD_DIR/templates/docker-compose.overlay.awk" "$tmp_compose1" > "$STAGE/compose/docker-compose.yml"
|
||||
cp "$BUILD_DIR/templates/.env.example" "$STAGE/compose/.env.example"
|
||||
# fix relative private path to match package layout (compose/ and private/ are siblings)
|
||||
sed -i 's#\./private/#../private/#g' "$STAGE/compose/docker-compose.yml"
|
||||
# also handle bind mount form without trailing slash
|
||||
sed -i -E 's#- \./private:/private#- ../private:/private#g' "$STAGE/compose/docker-compose.yml"
|
||||
# drop timezone file bind which may not exist on target distros (e.g. NixOS)
|
||||
sed -i '/\/etc\/timezone:\/etc\/timezone:ro/d' "$STAGE/compose/docker-compose.yml"
|
||||
|
||||
# sanity-check: ensure test services are absent and external network present
|
||||
if grep -E '^(\s{2})(node-a|node-b|test-node|test-gpu-node):\s*$' -n "$STAGE/compose/docker-compose.yml" >/dev/null; then
|
||||
err "compose filter failed: test services still present"; exit 1;
|
||||
fi
|
||||
if ! grep -q '^networks:' "$STAGE/compose/docker-compose.yml" || ! grep -q 'argus-sys-net:' "$STAGE/compose/docker-compose.yml"; then
|
||||
err "compose overlay transform failed: external network missing"; exit 1;
|
||||
fi
|
||||
|
||||
# 3) Images (reuse if already exported unless --resave-image)
|
||||
existing_images_tar="$PKG_DIR/images/all-images.tar.gz"
|
||||
if [[ "$RESAVE_IMAGE" == false && -f "$existing_images_tar" ]]; then
|
||||
log "Reusing existing images tar: $existing_images_tar"
|
||||
cp "$existing_images_tar" "$STAGE/images/"
|
||||
elif [[ "$RESAVE_IMAGE" == false ]]; then
|
||||
# Try cross-version reuse from latest server_*.tar.gz
|
||||
latest_pkg=$(ls -1t "$ART_ROOT/server"/server_*.tar.gz 2>/dev/null | head -n1 || true)
|
||||
if [[ -n "$latest_pkg" ]]; then
|
||||
log "Reusing images from: $latest_pkg"
|
||||
mkdir -p "$STAGE/images"
|
||||
# extract matching file regardless of top-level dir
|
||||
if tar -xzf "$latest_pkg" -C "$STAGE" --wildcards '*/images/all-images.tar.gz' 2>/dev/null; then
|
||||
# locate and move
|
||||
found=$(find "$STAGE" -type f -path '*/images/all-images.tar.gz' | head -n1 || true)
|
||||
if [[ -n "$found" ]]; then
|
||||
mv "$found" "$STAGE/images/all-images.tar.gz"
|
||||
# cleanup leftover extracted dir
|
||||
dir_to_clean=$(dirname "$found")
|
||||
rm -rf "${dir_to_clean%/images}" 2>/dev/null || true
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# If still not present, save from local docker daemon
|
||||
if [[ ! -f "$STAGE/images/all-images.tar.gz" ]]; then
|
||||
require_cmd docker gzip
|
||||
images=(
|
||||
argus-bind9:latest
|
||||
argus-master:latest
|
||||
argus-elasticsearch:latest
|
||||
argus-kibana:latest
|
||||
argus-metric-ftp:latest
|
||||
argus-metric-prometheus:latest
|
||||
argus-metric-grafana:latest
|
||||
argus-alertmanager:latest
|
||||
argus-web-frontend:latest
|
||||
argus-web-proxy:latest
|
||||
)
|
||||
log "Saving images: ${#images[@]}"
|
||||
tarfile="$STAGE/images/all-images.tar"
|
||||
docker save -o "$tarfile" "${images[@]}"
|
||||
gzip -f "$tarfile"
|
||||
fi
|
||||
|
||||
# 4) Scripts & Docs
|
||||
copy_tree "$BUILD_DIR/templates/scripts" "$STAGE/scripts"
|
||||
copy_tree "$BUILD_DIR/templates/docs" "$STAGE/docs"
|
||||
|
||||
# 5) Manifests
|
||||
gen_manifest "$STAGE" "$STAGE/manifest.txt"
|
||||
checksum_dir "$STAGE" "$STAGE/checksums.txt"
|
||||
|
||||
# 6) Move to artifact
|
||||
make_dir "$PKG_DIR"
|
||||
rsync -a "$STAGE/" "$PKG_DIR/" 2>/dev/null || cp -r "$STAGE/." "$PKG_DIR/"
|
||||
log "Server package ready: $PKG_DIR"
|
||||
|
||||
echo "$VERSION" > "$PKG_DIR/version.json"
|
||||
|
||||
# 7) Create distributable tarball
|
||||
OUT_TAR_DIR="$(dirname "$PKG_DIR")"
|
||||
OUT_TAR="$OUT_TAR_DIR/server_${VERSION}.tar.gz"
|
||||
log "Creating tarball: $OUT_TAR"
|
||||
(cd "$PKG_DIR/.." && tar -czf "$OUT_TAR" "$(basename "$PKG_DIR")")
|
||||
log "Tarball ready: $OUT_TAR"
|
||||
|
||||
exit 0
|
||||
33
deployment/build/common.sh
Executable file
33
deployment/build/common.sh
Executable file
@@ -0,0 +1,33 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
log() { echo -e "\033[0;34m[INFO]\033[0m $*"; }
|
||||
warn() { echo -e "\033[1;33m[WARN]\033[0m $*"; }
|
||||
err() { echo -e "\033[0;31m[ERR ]\033[0m $*" >&2; }
|
||||
|
||||
require_cmd() {
|
||||
for c in "$@"; do
|
||||
command -v "$c" >/dev/null 2>&1 || { err "missing command: $c"; exit 1; }
|
||||
done
|
||||
}
|
||||
|
||||
today_version() {
|
||||
date +%Y%m%d
|
||||
}
|
||||
|
||||
checksum_dir() {
|
||||
local dir="$1"; local out="$2"; : > "$out";
|
||||
(cd "$dir" && find . -type f -print0 | sort -z | xargs -0 sha256sum) >> "$out"
|
||||
}
|
||||
|
||||
make_dir() { mkdir -p "$1"; }
|
||||
|
||||
copy_tree() {
|
||||
local src="$1" dst="$2"; rsync -a --delete "$src/" "$dst/" 2>/dev/null || cp -r "$src/." "$dst/";
|
||||
}
|
||||
|
||||
gen_manifest() {
|
||||
local root="$1"; local out="$2"; : > "$out";
|
||||
(cd "$root" && find . -maxdepth 3 -type f -printf "%p\n" | sort) >> "$out"
|
||||
}
|
||||
|
||||
32
deployment/build/templates/.env.example
Normal file
32
deployment/build/templates/.env.example
Normal file
@@ -0,0 +1,32 @@
|
||||
# UID/GID for service processes
|
||||
ARGUS_BUILD_UID=1000
|
||||
ARGUS_BUILD_GID=1000
|
||||
|
||||
# Host ports (adjust if occupied)
|
||||
MASTER_PORT=32300
|
||||
ES_HTTP_PORT=9200
|
||||
KIBANA_PORT=5601
|
||||
NODE_A_PORT=2020
|
||||
NODE_B_PORT=2021
|
||||
PROMETHEUS_PORT=9090
|
||||
GRAFANA_PORT=3000
|
||||
ALERTMANAGER_PORT=9093
|
||||
WEB_PROXY_PORT_8080=8080
|
||||
WEB_PROXY_PORT_8081=8081
|
||||
WEB_PROXY_PORT_8082=8082
|
||||
WEB_PROXY_PORT_8083=8083
|
||||
WEB_PROXY_PORT_8084=8084
|
||||
WEB_PROXY_PORT_8085=8085
|
||||
|
||||
# FTP
|
||||
FTP_PORT=21
|
||||
FTP_DATA_PORT=20
|
||||
FTP_PASSIVE_HOST_RANGE=21100-21110
|
||||
FTP_PASSWORD=ZGClab1234!
|
||||
FTP_DOMAIN=ftp.metric.argus.com
|
||||
|
||||
# GPU profile disabled by default
|
||||
ENABLE_GPU=false
|
||||
|
||||
# External overlay network (Swarm attachable)
|
||||
OVERLAY_NET_NAME=argus-sys-net
|
||||
44
deployment/build/templates/client/INSTALL_CLIENT_zh.md
Normal file
44
deployment/build/templates/client/INSTALL_CLIENT_zh.md
Normal file
@@ -0,0 +1,44 @@
|
||||
# Argus Metric 客户端安装指南(容器内普通用户场景)
|
||||
|
||||
## 准备与连通性检查
|
||||
- FTP 连接(需要账号密码,默认 `ftpuser / ZGClab1234!`)
|
||||
- `curl -u ftpuser:ZGClab1234! -I ftp://<FTP_IP>:21/LATEST_VERSION`
|
||||
- `curl -u ftpuser:ZGClab1234! -s ftp://<FTP_IP>:21/ | head`
|
||||
- 下载安装脚本
|
||||
- `curl -u ftpuser:ZGClab1234! -fsSL ftp://<FTP_IP>:21/setup.sh -o /tmp/setup.sh`
|
||||
- `chmod +x /tmp/setup.sh`
|
||||
|
||||
## 元数据与主机名
|
||||
- Agent 需要元数据(env/user/instance)与 Master 地址:
|
||||
- 方式A:hostname 形如 `env-user-instance-xxx`(推荐)
|
||||
- 方式B:导出环境变量:
|
||||
- `export AGENT_ENV=dev`
|
||||
- `export AGENT_USER=<your_user>`
|
||||
- `export AGENT_INSTANCE=<node_id>`
|
||||
- Master 地址:
|
||||
- `export MASTER_ENDPOINT=http://master.argus.com:3000`
|
||||
|
||||
> 安装脚本会在开头检查上述条件,缺失时会终止并给出修复提示。
|
||||
|
||||
## 执行安装
|
||||
- 以 root 运行(容器内如为非 root 用户请切换为 root):
|
||||
- `sudo /tmp/setup.sh --server <FTP_IP> --user ftpuser --password 'ZGClab1234!' --port 21`
|
||||
- 如需自定义安装根目录:`--install-dir /opt/argus-metric`
|
||||
|
||||
提示(容器接入 overlay 网络时):
|
||||
- 在执行 setup 前,先将容器内 DNS 指向 Bind9 的 overlay IP:
|
||||
- `echo "nameserver <BIND_OVERLAY_IP>" > /etc/resolv.conf`
|
||||
- 这样 `master.argus.com`、`es.log.argus.com` 等域名即可解析;首次下载 `setup.sh` 仍建议使用 FTP 的 overlay IP。
|
||||
|
||||
更多快速步骤请参考:`QUICK_NODE_DEPLOY_zh.md`。
|
||||
|
||||
## 安装后自检(setup 自动执行)
|
||||
- setup 会等待最多 5 分钟,确认以下条件后才报告完成:
|
||||
- `/private/argus/agent/<hostname>/node.json` 已生成;
|
||||
- `last_report` 在持续更新;
|
||||
- `health.metric-argus-agent|metric-node-exporter|metric-fluent-bit|metric-dcgm-exporter` 均为 `healthy` 且 `error` 为空。
|
||||
|
||||
## 手工验证(可选)
|
||||
- `cat /private/argus/agent/$(hostname)/node.json | jq '.'`
|
||||
- `curl -s -o /dev/null -w "%{http_code}\n" http://localhost:9100/metrics` 应为 200
|
||||
- 查看日志:`/var/log/argus-agent.log`、`/opt/argus-metric/versions/*/.install.log`
|
||||
57
deployment/build/templates/client/PUBLISH_CLIENT_zh.md
Normal file
57
deployment/build/templates/client/PUBLISH_CLIENT_zh.md
Normal file
@@ -0,0 +1,57 @@
|
||||
# Argus Metric 客户端发布说明(FTP)
|
||||
|
||||
本说明面向“发布人员”,讲清楚如何把客户端离线包发布到 FTP,供各节点通过 `curl` 自动安装。
|
||||
|
||||
## 目录结构(构建后)
|
||||
- `client-YYYYMMDD/`
|
||||
- `argus-metric_YYYYMMDD.tar.gz` 客户端离线包
|
||||
- `setup.sh` 客户端安装入口脚本(提供给节点用 curl 下载)
|
||||
- `publish.sh` 发布脚本(将上述两项与 `LATEST_VERSION` 上传到 FTP)
|
||||
- `LATEST_VERSION` 文本(内容为 `YYYYMMDD`,或 `YYYYMMDD-rN`)
|
||||
- `INSTALL_CLIENT_zh.md` 本地安装指南(给使用者看,不会上载到 FTP)
|
||||
- `PUBLISH_CLIENT_zh.md` 本说明
|
||||
|
||||
> 注意:`publish.sh`/`setup.sh` 为可执行脚本;构建脚本已保证二者具有执行权限。
|
||||
|
||||
## 前置条件
|
||||
- FTP 服务已运行(默认容器:`argus-ftp`),并打开端口:21、20、21100–21110(被动模式)。
|
||||
- FTP 账号:默认 `ftpuser / ZGClab1234!`(如有更改,以实际为准)。
|
||||
|
||||
## 发布步骤(在 server 机器或能直连 FTP 的任意机器上)
|
||||
1) 进入发布目录:
|
||||
- `cd client-YYYYMMDD`
|
||||
|
||||
2) 执行发布:
|
||||
- `./publish.sh --server <FTP_HOST> --user <USER> --password '<PASS>' [--port 21]`
|
||||
- 例如在服务端本机:`./publish.sh --server localhost --user ftpuser --password 'ZGClab1234!' --port 21`
|
||||
|
||||
脚本会上传三类文件到 FTP 根:
|
||||
- `setup.sh`
|
||||
- `argus-metric_YYYYMMDD[ -rN ].tar.gz`
|
||||
- `LATEST_VERSION`(内容为当前版本号)
|
||||
|
||||
3) 发布后验证:
|
||||
- `curl -u ftpuser:****** -I ftp://<FTP_HOST>:21/LATEST_VERSION` 应返回 200
|
||||
- `curl -u ftpuser:****** -fsSL ftp://<FTP_HOST>:21/LATEST_VERSION` 内容为版本号(如 `20251104`)
|
||||
- `curl -u ftpuser:****** -I ftp://<FTP_HOST>:21/argus-metric_YYYYMMDD.tar.gz` 返回 200
|
||||
|
||||
## 节点侧使用方式(摘要)
|
||||
- 首次下载用 FTP 的“IP 地址”:
|
||||
- `curl -u ftpuser:****** -fsSL ftp://<FTP_IP>:21/setup.sh -o /tmp/setup.sh && chmod +x /tmp/setup.sh`
|
||||
- 执行安装:
|
||||
- 必需元数据:`AGENT_ENV/AGENT_USER/AGENT_INSTANCE`,以及 `MASTER_ENDPOINT=http://master.argus.com:3000`
|
||||
- `sudo /tmp/setup.sh --server <FTP_IP> --user ftpuser --password '******' --port 21`
|
||||
- overlay 容器场景:
|
||||
- 先将容器内 DNS 指向 Bind9 的 overlay IP:`echo "nameserver <BIND_OVERLAY_IP>" > /etc/resolv.conf`
|
||||
- 然后再执行上述安装;安装后约 1–2 分钟内 DNS 即可解析 `*.argus.com` 域名。
|
||||
|
||||
## 常见问题
|
||||
- `530 Access denied`:用户名/密码错误或 FTP 目录无权限;请核对账号与 FTP 容器状态。
|
||||
- `Permission denied` 执行 `publish.sh`:为脚本权限问题;`chmod +x publish.sh`。构建脚本已修复默认权限。
|
||||
- 被动端口不通导致失败:请开放 21100–21110。
|
||||
- 客户端安装后短时 `curl http://master.argus.com:3000` 为 000:服务冷启动或 DNS 同步延迟,等待 1–2 分钟再试。
|
||||
|
||||
## 版本与回滚
|
||||
- `LATEST_VERSION` 决定客户端默认安装的版本号。
|
||||
- 如需回滚:将旧版本号写回 `LATEST_VERSION` 并重新发布(或手动指定 `--version` 安装)。
|
||||
|
||||
58
deployment/build/templates/client/QUICK_NODE_DEPLOY_zh.md
Normal file
58
deployment/build/templates/client/QUICK_NODE_DEPLOY_zh.md
Normal file
@@ -0,0 +1,58 @@
|
||||
# Argus Metric 节点快速部署(Overlay 网络容器)
|
||||
|
||||
本文档给出在 Docker Swarm external overlay 网络中,快速拉起一个测试节点并完成注册的最小可行步骤。
|
||||
|
||||
## 前提
|
||||
- 服务端已在 Manager 机安装完成并运行良好(`server-selfcheck` 通过)。
|
||||
- Overlay 网络名称:`argus-sys-net`(默认)。
|
||||
- 已通过 FTP 发布 `setup.sh` 与客户端包,且能从 FTP 获取 `LATEST_VERSION`。
|
||||
- 用于测试的镜像:`argus-sys-metric-test-node:latest` 已存在于目标机器。
|
||||
|
||||
## 步骤
|
||||
|
||||
- 获取 FTP 和 Bind 的 overlay IP(在 Manager 上执行)
|
||||
- `FTPIP=$(docker inspect -f '{{ (index .NetworkSettings.Networks "argus-sys-net").IPAddress }}' argus-ftp)`
|
||||
- `BINDIP=$(docker inspect -f '{{ (index .NetworkSettings.Networks "argus-sys-net").IPAddress }}' argus-bind-sys)`
|
||||
- `echo "FTP=$FTPIP BIND=$BINDIP"`
|
||||
|
||||
- 准备宿主挂载目录(以 s4 为例)
|
||||
- `mkdir -p /home2/yuyr/deploy/test-metric-node/s4`
|
||||
|
||||
- 启动测试节点容器(接入 overlay)
|
||||
- `docker run -d --name argus-metric-test-node-s4 \
|
||||
--hostname dev2-yuyr-node002s4 \
|
||||
--network argus-sys-net \
|
||||
-v /home2/yuyr/deploy/test-metric-node/s4:/private/argus/agent \
|
||||
argus-sys-metric-test-node:latest sleep infinity`
|
||||
|
||||
- 在容器内执行安装(先用 FTP IP 引导,DNS 指向 Bind)
|
||||
- `docker exec -it argus-metric-test-node-s4 bash`
|
||||
- `echo "nameserver $BINDIP" > /etc/resolv.conf`
|
||||
- `curl --ftp-method nocwd -u ftpuser:ZGClab1234! -fsSL "ftp://$FTPIP:21/setup.sh" -o /tmp/setup.sh`
|
||||
- `chmod +x /tmp/setup.sh`
|
||||
- `export AGENT_ENV=dev2 AGENT_USER=yuyr AGENT_INSTANCE=node002s4`
|
||||
- `export MASTER_ENDPOINT=http://master.argus.com:3000`
|
||||
- `/tmp/setup.sh --server "$FTPIP" --user ftpuser --password 'ZGClab1234!' --port 21`
|
||||
- 说明:setup 会自动执行安装后自检(最多 5 分钟),无需手动轮询。
|
||||
|
||||
## 验证(推荐在容器内执行,避免宿主权限问题)
|
||||
|
||||
- 查看 node.json 关键字段
|
||||
- `cat /private/argus/agent/dev2-yuyr-node002s4/node.json | jq '{last_report, health}'`
|
||||
- 期望:四个 health 全部 healthy;等待 ≥70s 再查看,`last_report` 持续更新。
|
||||
|
||||
- 指标端口
|
||||
- `curl -s -o /dev/null -w '%{http_code}\n' http://localhost:9100/metrics`(期望 200)
|
||||
- (如测试 GPU)`curl -s -o /dev/null -w '%{http_code}\n' http://localhost:9400/metrics`(有 GPU 时 200)
|
||||
|
||||
- 与服务端连通(域名经 Bind 解析)
|
||||
- `curl -s -o /dev/null -w '%{http_code}\n' http://master.argus.com:3000/readyz`(期望 200)
|
||||
- `curl -s -o /dev/null -w '%{http_code}\n' http://es.log.argus.com:9200/_cluster/health`(期望 200)
|
||||
|
||||
## (可选)在服务器主机侧观察 Prometheus 目标更新
|
||||
- `cat /home2/yuyr/deploy/versions/<VERSION>/private/argus/metric/prometheus/nodes.json | jq '.'`
|
||||
|
||||
## 常见提示
|
||||
- 初次安装后短时 `curl` 域名返回 000/超时属正常,多等待 1–2 分钟 DNS 同步/组件冷启动完成。
|
||||
- 如在宿主直接读取挂载的 node.json 报 Permission denied,请使用 `docker exec` 在容器内查看。
|
||||
- MASTER_ENDPOINT 固定使用域名 `http://master.argus.com:3000`,客户端无需固定 IP。
|
||||
54
deployment/build/templates/client/publish.sh
Normal file
54
deployment/build/templates/client/publish.sh
Normal file
@@ -0,0 +1,54 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
usage() { cat <<'EOF'
|
||||
Publish Argus client package to FTP
|
||||
|
||||
Usage:
|
||||
./publish.sh --server HOST --user USER --password PASS [--port 21]
|
||||
|
||||
Notes:
|
||||
- This script expects to run inside the built client artifact directory.
|
||||
- It reads LATEST_VERSION and uploads setup.sh, argus-metric_<ver>.tar.gz, and LATEST_VERSION.
|
||||
EOF
|
||||
}
|
||||
|
||||
HOST=""; USERNAME=""; PASSWORD=""; PORT=21
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--server) HOST="$2"; shift 2;;
|
||||
--user) USERNAME="$2"; shift 2;;
|
||||
--password) PASSWORD="$2"; shift 2;;
|
||||
--port) PORT="$2"; shift 2;;
|
||||
-h|--help) usage; exit 0;;
|
||||
*) echo "unknown arg: $1" >&2; usage; exit 1;;
|
||||
esac
|
||||
done
|
||||
|
||||
[[ -n "$HOST" && -n "$USERNAME" && -n "$PASSWORD" ]] || { usage; exit 1; }
|
||||
|
||||
here="$(pwd)"
|
||||
if [[ ! -f "$here/LATEST_VERSION" ]]; then
|
||||
echo "LATEST_VERSION not found in $(pwd)" >&2; exit 1;
|
||||
fi
|
||||
VER=$(cat "$here/LATEST_VERSION" | tr -d '\n')
|
||||
PKG="argus-metric_${VER}.tar.gz"
|
||||
|
||||
if [[ ! -f "$here/$PKG" ]]; then
|
||||
echo "client tar not found: $PKG" >&2; exit 1
|
||||
fi
|
||||
|
||||
# locate setup.sh (prefer colocated, fallback to bundled path if provided)
|
||||
SETUP="${here}/setup.sh"
|
||||
if [[ ! -f "$SETUP" ]]; then
|
||||
echo "setup.sh not found in $(pwd)" >&2; exit 1
|
||||
fi
|
||||
|
||||
echo "[PUBLISH] server=$HOST port=$PORT version=$VER"
|
||||
|
||||
curl -u "$USERNAME:$PASSWORD" -sfT "$SETUP" "ftp://$HOST:$PORT/setup.sh"
|
||||
curl -u "$USERNAME:$PASSWORD" -sfT "$PKG" "ftp://$HOST:$PORT/$PKG"
|
||||
printf "%s" "$VER" | curl -u "$USERNAME:$PASSWORD" -sfT - "ftp://$HOST:$PORT/LATEST_VERSION"
|
||||
|
||||
echo "[OK] publish completed"
|
||||
|
||||
41
deployment/build/templates/docker-compose.filter.awk
Normal file
41
deployment/build/templates/docker-compose.filter.awk
Normal file
@@ -0,0 +1,41 @@
|
||||
#!/usr/bin/awk -f
|
||||
# Remove specific service blocks from a docker-compose.yml by service name.
|
||||
# Usage: awk -f docker-compose.filter.awk -v remove="node-a,node-b,test-node,test-gpu-node" input.yml > output.yml
|
||||
|
||||
BEGIN{
|
||||
split(remove, rm, ",");
|
||||
for(i in rm){
|
||||
gsub(/^\s+|\s+$/,"",rm[i]);
|
||||
if (rm[i] != "") skipname[rm[i]] = 1;
|
||||
}
|
||||
in_services=0; skipping=0;
|
||||
}
|
||||
|
||||
function service_header(line, m) {
|
||||
# match exactly two leading spaces followed by name:
|
||||
if (match(line, /^ ([A-Za-z0-9_-]+):[ ]*$/, m)) return m[1];
|
||||
return "";
|
||||
}
|
||||
|
||||
{
|
||||
# Track top-level sections (no indentation)
|
||||
if ($0 ~ /^[A-Za-z0-9_-]+:[ ]*$/ && $0 !~ /^\s/) {
|
||||
in_services = ($0 ~ /^services:[ ]*$/) ? 1 : 0;
|
||||
}
|
||||
|
||||
if (skipping) {
|
||||
# Stop skipping at next service header or another top-level section
|
||||
if (service_header($0) != "" || ($0 ~ /^(networks|volumes):[ ]*$/ && $0 !~ /^\s/)) {
|
||||
skipping=0;
|
||||
} else {
|
||||
next;
|
||||
}
|
||||
}
|
||||
|
||||
if (in_services) {
|
||||
name = service_header($0);
|
||||
if (name != "" && (name in skipname)) { skipping=1; next; }
|
||||
}
|
||||
|
||||
print;
|
||||
}
|
||||
74
deployment/build/templates/docker-compose.overlay.awk
Normal file
74
deployment/build/templates/docker-compose.overlay.awk
Normal file
@@ -0,0 +1,74 @@
|
||||
#!/usr/bin/awk -f
|
||||
# Transform docker-compose.yml to use an external overlay network for all services
|
||||
# - Remove top-level networks definition
|
||||
# - Remove per-service networks block (including ipv4_address and sysnet refs)
|
||||
# - Insert per-service networks: [argus-sys-net]
|
||||
# - Append external networks mapping at the end
|
||||
|
||||
BEGIN{
|
||||
in_top_networks=0; in_services=0; in_service=0; svc_indent=0; curr_name="";
|
||||
}
|
||||
|
||||
function is_service_header(line){ return svc_name(line)!=""; }
|
||||
function svc_name(line, m){ if (match(line, /^ ([A-Za-z0-9_-]+):[ ]*$/, m)) return m[1]; return ""; }
|
||||
|
||||
function indent_len(s, n){ n=match(s,/[^ ]/)-1; if(n<0) n=0; return n; }
|
||||
|
||||
{
|
||||
# Detect entry into top-level sections
|
||||
if ($0 ~ /^[A-Za-z0-9_-]+:[ ]*$/ && $0 !~ /^\s/) {
|
||||
in_services = ($0 ~ /^services:[ ]*$/);
|
||||
# If a new top-level section starts, stop skipping top networks
|
||||
in_top_networks = 0;
|
||||
}
|
||||
|
||||
# Handle removal of initial top-level 'networks:' block
|
||||
if ($0 ~ /^networks:[ ]*$/ && $0 !~ /^\s/) {
|
||||
in_top_networks = 1; next;
|
||||
}
|
||||
if (in_top_networks) {
|
||||
# skip until next top-level section (non-indented key)
|
||||
next;
|
||||
}
|
||||
|
||||
if (in_services) {
|
||||
# Track service boundaries
|
||||
if (is_service_header($0)) {
|
||||
in_service=1; svc_indent=2; networks_inserted=0; curr_name=svc_name($0); print; next;
|
||||
}
|
||||
if (in_service) {
|
||||
# If line is indented <= service indent, we've left this service
|
||||
if (indent_len($0) <= svc_indent && $0 !~ /^\s*$/) {
|
||||
in_service=0;
|
||||
}
|
||||
}
|
||||
|
||||
if (in_service) {
|
||||
# Skip any existing networks block under the service
|
||||
if ($0 ~ /^\s{4}networks:[ ]*$/) { skipping_nets=1; next; }
|
||||
if (skipping_nets) {
|
||||
if (indent_len($0) <= 4) { skipping_nets=0; }
|
||||
else next;
|
||||
}
|
||||
|
||||
# After container_name or image, inject networks once
|
||||
if (!networks_inserted && ($0 ~ /^\s{4}container_name:/ || $0 ~ /^\s{4}image:/)) {
|
||||
print;
|
||||
print " networks:";
|
||||
print " - argus-sys-net";
|
||||
networks_inserted=1; next;
|
||||
}
|
||||
# no host port injection; bind serves DNS inside overlay only
|
||||
}
|
||||
}
|
||||
|
||||
print;
|
||||
}
|
||||
|
||||
END{
|
||||
print "";
|
||||
print "networks:";
|
||||
print " argus-sys-net:";
|
||||
print " external: true";
|
||||
print " name: ${OVERLAY_NET_NAME:-argus-sys-net}";
|
||||
}
|
||||
50
deployment/build/templates/docs/INSTALL_SERVER.md
Normal file
50
deployment/build/templates/docs/INSTALL_SERVER.md
Normal file
@@ -0,0 +1,50 @@
|
||||
# Argus Server Offline Installation
|
||||
|
||||
## Prerequisites
|
||||
- Linux x86_64 (Ubuntu 22.04 recommended; see OS compatibility for NixOS)
|
||||
- Docker & Docker Compose installed
|
||||
- Open ports: 32300, 9200, 5601, 9090, 9093, 8080..8085, 21, 20, 21100–21110 (or auto-fallback to high ports)
|
||||
|
||||
## Quick Start
|
||||
1. Extract to a target dir, e.g. `/opt/argus-deploy/versions/<YYYYMMDD>`
|
||||
2. `./server-install.sh` (non‑root is supported: it will precreate minimal dirs and auto-fix Kibana/ES/Bind in containers)
|
||||
3. `./server-status.sh`
|
||||
4. `./server-selfcheck.sh` (on failure it auto-runs diagnose and writes logs under `logs/`)
|
||||
5. `./server-uninstall.sh` to tear down
|
||||
|
||||
## What the Installer Does
|
||||
- Loads local images (`images/all-images.tar.gz`)
|
||||
- Generates OS-compat override (`security_opt: ["label=disable"]`, `userns_mode: host`, bind `tmpfs:/run/named`)
|
||||
- Starts server-only services: bind/master/es/kibana/ftp/prometheus/grafana/alertmanager/web-frontend/web-proxy
|
||||
- DNS Bootstrap:
|
||||
- Ensure `/private/argus/etc/dns.conf` exists (write `172.31.0.2` if missing);
|
||||
- Run `/private/argus/etc/update-dns.sh` in dependent containers so `/etc/resolv.conf` points to bind;
|
||||
- Wait for `*.argus.com` hint files, then reload bind;
|
||||
- Restart web‑proxy to re-render nginx resolver from `dns.conf`;
|
||||
- Writes `logs/selfcheck.json` as final summary
|
||||
|
||||
## OS Compatibility
|
||||
- NixOS / non-xattr FS: containers run with `security_opt: ["label=disable"]` and `userns_mode: host`.
|
||||
- If you cannot use sudo, the installer will:
|
||||
- create minimal data dirs (incl. `private/argus/log/{elasticsearch,kibana}`) with permissive perms when possible;
|
||||
- ensure inside containers: Kibana `data` → `/private/argus/log/kibana`, Elasticsearch `data` → `/private/argus/log/elasticsearch`, and Bind `rndc.key` is generated.
|
||||
(Manual pre-creation scripts are no longer required.)
|
||||
|
||||
## Files & Layout
|
||||
- `compose/` (docker-compose.yml, .env)
|
||||
- `private/` (data mounts)
|
||||
- `scripts/` (install/uninstall/status/selfcheck/diagnose)
|
||||
- `logs/` (selfcheck + diagnose outputs)
|
||||
|
||||
## Troubleshooting (Quick)
|
||||
- Run `./server-selfcheck.sh` → see `logs/selfcheck.json`
|
||||
- Run `./server-diagnose.sh` → produces timestamped logs:
|
||||
- `logs/diagnose_details_YYYYMMDD-HHMMSSZ.log`
|
||||
- `logs/diagnose_error_YYYYMMDD-HHMMSSZ.log`
|
||||
And updates `diagnose_details.log`/`diagnose_error.log` to the latest
|
||||
- Error lines are tagged `[service][source]`, e.g. `[kibana][http] /api/status=503`
|
||||
|
||||
Common issues:
|
||||
- Kibana 503: wait cold start or fix DNS so `es.log.argus.com` resolves
|
||||
- web‑proxy 504: check nginx `resolver` includes `172.31.0.2 127.0.0.11`
|
||||
- EACCES/locks: ensure `sudo ./server-prepare-dirs.sh` ran and ownership matches UID:GID
|
||||
29
deployment/build/templates/docs/INSTALL_SERVER_zh.md
Normal file
29
deployment/build/templates/docs/INSTALL_SERVER_zh.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# Argus 服务端离线安装指南
|
||||
|
||||
## 先决条件
|
||||
- Linux x86_64(推荐 Ubuntu 22.04;NixOS 见“兼容说明”)
|
||||
- 已安装 Docker 与 Docker Compose
|
||||
- 端口:32300, 9200, 5601, 9090, 9093, 8080..8085, 21, 20, 21100–21110
|
||||
|
||||
## 快速开始
|
||||
1. 解压到目标目录(例如 `/opt/argus-deploy/versions/<YYYYMMDD>`)
|
||||
2. 安装:`./server-install.sh`(支持普通用户:会自动创建最小目录并在容器内修复 Kibana/ES/Bind)
|
||||
3. 状态:`./server-status.sh`
|
||||
4. 自检:`./server-selfcheck.sh`(失败会自动采集诊断)
|
||||
5. 卸载:`./server-uninstall.sh`
|
||||
|
||||
## 安装流程要点
|
||||
- 仅启动 10 个服务端组件(不包含测试节点);
|
||||
- DNS Bootstrap:补齐首次部署 DNS 依赖(生成/确认 `dns.conf`、统一容器 resolv.conf、写入 `*.argus.com`、reload bind、重启 web‑proxy);
|
||||
- 输出自检结果到 `logs/selfcheck.json`。
|
||||
|
||||
## 兼容说明(NixOS 等)
|
||||
- 使用 `security_opt: ["label=disable"]` 与 `userns_mode: host`。
|
||||
- 非 root 场景:安装器会创建最小目录(含 `private/argus/log/{elasticsearch,kibana}`),并在容器内完成:
|
||||
- Kibana 的 `data` 软链到 `/private/argus/log/kibana`
|
||||
- Elasticsearch 的 `data` 软链到 `/private/argus/log/elasticsearch`
|
||||
- Bind 生成 `/etc/bind/rndc.key`
|
||||
|
||||
## 故障排查(见下文 Troubleshooting_zh)
|
||||
- `./server-selfcheck.sh` → `logs/selfcheck.json`
|
||||
- `./server-diagnose.sh` → `logs/diagnose_error_*.log` / `logs/diagnose_details_*.log`
|
||||
50
deployment/build/templates/docs/SWARM_DEPLOY_zh.md
Normal file
50
deployment/build/templates/docs/SWARM_DEPLOY_zh.md
Normal file
@@ -0,0 +1,50 @@
|
||||
# Argus 多机部署(Docker Swarm + External Overlay)
|
||||
|
||||
- 前提:Docker ≥ 20.10;Manager/Worker 节点开放 2377/tcp、7946/tcp+udp、4789/udp。
|
||||
- DNS:Bind9 为统一权威,解析 *.argus.com 至部署机固定对外主机 IP。
|
||||
|
||||
## 在部署机(Manager)
|
||||
- 初始化 Swarm:`docker swarm init --advertise-addr <manager_ip>`
|
||||
- 创建 overlay:`docker network create --driver overlay --attachable argus-sys-net`
|
||||
- 解压离线包后执行:
|
||||
- `./server-install.sh`(会验证 Swarm 状态、确保 overlay 存在、写入 dns.conf)
|
||||
- `./server-selfcheck.sh`(失败会自动触发诊断)
|
||||
|
||||
## 在节点机(Worker 或非 Docker 主机)
|
||||
- Swarm Worker:执行 Manager 的 `docker swarm join ...`;
|
||||
- 运行客户端容器:
|
||||
- `docker run -d --name argus-metric-node-001 --network argus-sys-net -v /data/argus/agent:/private/argus/agent argus-sys-metric-test-node:latest sleep infinity`
|
||||
- 进入容器安装(先 IP 引导,后域名):
|
||||
- `curl -u ftpuser:*** -fsSL ftp://<FTP_IP>:21/setup.sh -o /tmp/setup.sh && chmod +x /tmp/setup.sh`
|
||||
- `AGENT_ENV=dev AGENT_USER=alice AGENT_INSTANCE=node001 MASTER_ENDPOINT=http://master.argus.com:3000 /tmp/setup.sh --server ftp.argus.com --user ftpuser --password '***' --port 21`
|
||||
|
||||
## 关键点
|
||||
- 首次必须使用 FTP 的 IP 引导(下载 setup.sh 与 dns.conf)
|
||||
- MASTER_ENDPOINT 永远使用域名:`http://master.argus.com:3000`
|
||||
- docker compose 改为 external overlay;容器内不使用 Docker 服务名;web-proxy 与组件上游统一用域名
|
||||
|
||||
## 找回/轮换 Swarm 加入令牌与解锁密钥
|
||||
|
||||
在任意一个 Manager 节点上执行以下命令即可查看或轮换加入令牌(join token):
|
||||
|
||||
- 查看加入 Worker 的命令:
|
||||
- `docker swarm join-token worker`
|
||||
- 只打印 Worker 的 token:
|
||||
- `docker swarm join-token -q worker`
|
||||
- 查看加入 Manager 的命令:
|
||||
- `docker swarm join-token manager`
|
||||
- 只打印 Manager 的 token:
|
||||
- `docker swarm join-token -q manager`
|
||||
|
||||
在待加入节点执行(示例,替换 Manager_IP):
|
||||
- `docker swarm join --token <上面查到的token> <Manager_IP>:2377`
|
||||
|
||||
轮换 token(怀疑泄露或需要更新时):
|
||||
- 轮换 Worker:`docker swarm join-token --rotate worker`
|
||||
- 轮换 Manager:`docker swarm join-token --rotate manager`
|
||||
|
||||
如果你指的是“解锁密钥”(autolock 的 unlock key),在 Manager 上:
|
||||
- 查看:`docker swarm unlock-key`
|
||||
- 轮换:`docker swarm unlock-key --rotate`
|
||||
|
||||
提示:当看到 “This node is not a swarm manager.” 时,说明当前节点不是 Manager,需要到 Manager 节点执行,或在现有 Manager 上 `docker node promote <NODE-ID>` 将其提升为 Manager。
|
||||
20
deployment/build/templates/docs/TROUBLESHOOTING.md
Normal file
20
deployment/build/templates/docs/TROUBLESHOOTING.md
Normal file
@@ -0,0 +1,20 @@
|
||||
# Troubleshooting
|
||||
|
||||
- Status: `scripts/server-status.sh`
|
||||
- Selfcheck: `scripts/server-selfcheck.sh`
|
||||
- Diagnose: `scripts/server-diagnose.sh`
|
||||
|
||||
Outputs:
|
||||
- `logs/selfcheck.json`
|
||||
- `logs/diagnose_details_*.log` (full details)
|
||||
- `logs/diagnose_error_*.log` (tagged errors)
|
||||
|
||||
Web‑Proxy:
|
||||
- 8083 expects 200/302/403; 8084/8085 must include CORS header
|
||||
- nginx resolver should be `172.31.0.2 127.0.0.11`
|
||||
|
||||
Kibana/ES:
|
||||
- Verify `es.log.argus.com` resolves inside Kibana
|
||||
|
||||
Permissions:
|
||||
- The installer auto-creates minimal dirs and applies container-side fixes (Kibana/ES/Bind). If you still see EACCES/lock errors, rerun `./server-install.sh` and review diagnose logs.
|
||||
16
deployment/build/templates/docs/TROUBLESHOOTING_zh.md
Normal file
16
deployment/build/templates/docs/TROUBLESHOOTING_zh.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# 故障排查
|
||||
|
||||
- 状态:`scripts/server-status.sh`
|
||||
- 自检:`scripts/server-selfcheck.sh`
|
||||
- 诊断:`scripts/server-diagnose.sh`
|
||||
|
||||
输出:
|
||||
- `logs/selfcheck.json`
|
||||
- `logs/diagnose_error_*.log`(错误摘要)
|
||||
- `logs/diagnose_details_*.log`(详细信息)
|
||||
|
||||
Web‑Proxy:8083=200/302/403;8084/8085 需包含 CORS
|
||||
Kibana:确认可解析 `es.log.argus.com`
|
||||
权限:
|
||||
- 非 root 安装时,安装器会创建最小目录并在容器内修复 Kibana/ES/Bind;
|
||||
- 如仍有 `EACCES`/锁文件报错,先重跑 `./server-install.sh`(会重复容器内修复),并查看诊断日志。
|
||||
147
deployment/build/templates/scripts/server-diagnose.sh
Executable file
147
deployment/build/templates/scripts/server-diagnose.sh
Executable file
@@ -0,0 +1,147 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
|
||||
ENV_FILE="$ROOT/compose/.env"; [[ -f "$ENV_FILE" ]] && set -a && source "$ENV_FILE" && set +a
|
||||
|
||||
ts="$(date -u +%Y%m%d-%H%M%SZ)"
|
||||
LOG_DIR="$ROOT/logs"
|
||||
mkdir -p "$LOG_DIR" || true
|
||||
# Fallback to /tmp when logs dir is not writable
|
||||
if ! ( : > "$LOG_DIR/.w" 2>/dev/null ); then
|
||||
LOG_DIR="/tmp/argus-logs"
|
||||
mkdir -p "$LOG_DIR" || true
|
||||
fi
|
||||
DETAILS="$LOG_DIR/diagnose_details_${ts}.log"
|
||||
ERRORS="$LOG_DIR/diagnose_error_${ts}.log"
|
||||
: > "$DETAILS"; : > "$ERRORS"
|
||||
|
||||
logd() { echo "$(date '+%F %T') $*" >> "$DETAILS"; }
|
||||
append_err() { echo "$*" >> "$ERRORS"; }
|
||||
|
||||
http_code() { curl -s -o /dev/null -w "%{http_code}" "$1" || echo 000; }
|
||||
http_body_head() { curl -s --max-time 3 "$1" 2>/dev/null | sed -n '1,5p' || true; }
|
||||
header_val() { curl -s -D - -o /dev/null "$@" | awk -F': ' 'BEGIN{IGNORECASE=1}$1=="Access-Control-Allow-Origin"{gsub("\r","",$2);print $2}'; }
|
||||
|
||||
section() {
|
||||
local name="$1"; logd "===== [$name] ====="; }
|
||||
|
||||
svc() {
|
||||
local svc_name="$1"; local cname="$2"; shift 2
|
||||
section "$svc_name ($cname)"
|
||||
logd "docker ps:"; docker ps -a --format '{{.Names}}\t{{.Status}}\t{{.Image}}' | awk -v n="$cname" '$1==n' >> "$DETAILS" || true
|
||||
logd "docker inspect (state/restartcount):"; docker inspect -f '{{.State.Status}} rc={{.RestartCount}} started={{.State.StartedAt}}' "$cname" >> "$DETAILS" 2>&1 || true
|
||||
logd "last 200 container logs:"; docker logs --tail 200 "$cname" >> "$DETAILS" 2>&1 || true
|
||||
|
||||
# extract error lines from container logs
|
||||
docker logs --tail 200 "$cname" 2>&1 | \
|
||||
grep -Ei '\b(error|failed|fail|exception|panic|fatal|critical|unhealthy|permission denied|forbidden|refused|traceback|错误|失败)\b' | \
|
||||
sed "s/^/[${svc_name}][container] /" >> "$ERRORS" || true
|
||||
|
||||
# supervisor status and logs
|
||||
if docker exec "$cname" sh -lc 'command -v supervisorctl >/dev/null 2>&1' >/dev/null 2>&1; then
|
||||
logd "supervisorctl status:"; docker exec "$cname" sh -lc 'supervisorctl status' >> "$DETAILS" 2>&1 || true
|
||||
# iterate supervisor logs and collect tails + errors per file
|
||||
local files
|
||||
files=$(docker exec "$cname" sh -lc 'ls /var/log/supervisor/*.log 2>/dev/null' || true)
|
||||
for f in $files; do
|
||||
logd "tail -n 80 $f:"; docker exec "$cname" sh -lc "tail -n 80 $f 2>/dev/null || true" >> "$DETAILS" 2>&1 || true
|
||||
docker exec "$cname" sh -lc "tail -n 200 $f 2>/dev/null" 2>/dev/null | \
|
||||
grep -Ei '\b(error|failed|fail|exception|panic|fatal|critical|unhealthy|permission denied|forbidden|refused|traceback|错误|失败)\b' | \
|
||||
sed "s/^/[${svc_name}][supervisor:$(basename $f)] /" >> "$ERRORS" || true
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
# Core services
|
||||
svc bind argus-bind-sys
|
||||
svc master argus-master-sys
|
||||
svc es argus-es-sys
|
||||
svc kibana argus-kibana-sys
|
||||
svc ftp argus-ftp
|
||||
svc prometheus argus-prometheus
|
||||
svc grafana argus-grafana
|
||||
svc alertmanager argus-alertmanager
|
||||
svc web-frontend argus-web-frontend
|
||||
svc web-proxy argus-web-proxy
|
||||
|
||||
# HTTP checks (host side)
|
||||
section HTTP
|
||||
logd "ES: $(http_code "http://localhost:${ES_HTTP_PORT:-9200}/_cluster/health")"
|
||||
http_body_head "http://localhost:${ES_HTTP_PORT:-9200}/_cluster/health" >> "$DETAILS" 2>&1 || true
|
||||
|
||||
logd "Kibana: $(http_code "http://localhost:${KIBANA_PORT:-5601}/api/status")"
|
||||
http_body_head "http://localhost:${KIBANA_PORT:-5601}/api/status" >> "$DETAILS" 2>&1 || true
|
||||
|
||||
logd "Master readyz: $(http_code "http://localhost:${MASTER_PORT:-32300}/readyz")"
|
||||
|
||||
logd "Prometheus: $(http_code "http://localhost:${PROMETHEUS_PORT:-9090}/-/ready")"
|
||||
logd "Grafana: $(http_code "http://localhost:${GRAFANA_PORT:-3000}/api/health")"
|
||||
http_body_head "http://localhost:${GRAFANA_PORT:-3000}/api/health" >> "$DETAILS" 2>&1 || true
|
||||
logd "Alertmanager: $(http_code "http://localhost:${ALERTMANAGER_PORT:-9093}/api/v2/status")"
|
||||
|
||||
cors8084=$(header_val -H "Origin: http://localhost:${WEB_PROXY_PORT_8080:-8080}" "http://localhost:${WEB_PROXY_PORT_8084:-8084}/api/v2/status" || true)
|
||||
cors8085=$(header_val -H "Origin: http://localhost:${WEB_PROXY_PORT_8080:-8080}" "http://localhost:${WEB_PROXY_PORT_8085:-8085}/api/v1/master/nodes" || true)
|
||||
logd "Web-Proxy 8080: $(http_code "http://localhost:${WEB_PROXY_PORT_8080:-8080}/")"
|
||||
logd "Web-Proxy 8083: $(http_code "http://localhost:${WEB_PROXY_PORT_8083:-8083}/")"
|
||||
logd "Web-Proxy 8084 CORS: ${cors8084}"
|
||||
logd "Web-Proxy 8085 CORS: ${cors8085}"
|
||||
|
||||
# Overlay network diagnostics
|
||||
section OVERLAY-NET
|
||||
if docker network inspect "${OVERLAY_NET_NAME:-argus-sys-net}" >/dev/null 2>&1; then
|
||||
logd "overlay present: ${OVERLAY_NET_NAME:-argus-sys-net}"
|
||||
docker network inspect "${OVERLAY_NET_NAME:-argus-sys-net}" | sed -n '1,60p' >> "$DETAILS" 2>/dev/null || true
|
||||
else
|
||||
append_err "[overlay][network] missing ${OVERLAY_NET_NAME:-argus-sys-net}"
|
||||
fi
|
||||
|
||||
# Domain resolution & reachability from inside web-proxy (bind-backed)
|
||||
section DOMAIN
|
||||
for d in master.argus.com ftp.argus.com kibana.argus.com es.log.argus.com prom.argus.com grafana.argus.com; do
|
||||
logd "getent $d (web-proxy):"
|
||||
docker exec argus-web-proxy sh -lc "getent hosts $d || true" >> "$DETAILS" 2>&1 || true
|
||||
done
|
||||
logd "HTTP (web-proxy): master.readyz=$(docker exec argus-web-proxy sh -lc \"curl -s -o /dev/null -w '%{http_code}' http://master.argus.com:3000/readyz\" 2>/dev/null || echo 000)"
|
||||
logd "HTTP (web-proxy): es.health=$(docker exec argus-web-proxy sh -lc \"curl -s -o /dev/null -w '%{http_code}' http://es.log.argus.com:9200/_cluster/health\" 2>/dev/null || echo 000)"
|
||||
logd "HTTP (web-proxy): kibana.status=$(docker exec argus-web-proxy sh -lc \"curl -s -o /dev/null -w '%{http_code}' http://kibana.argus.com:5601/api/status\" 2>/dev/null || echo 000)"
|
||||
|
||||
# FTP share writability (container perspective)
|
||||
section FTP-SHARE
|
||||
docker exec argus-ftp sh -lc 'ls -ld /private/argus/ftp /private/argus/ftp/share; test -w /private/argus/ftp/share && echo "write:OK" || echo "write:FAIL"' >> "$DETAILS" 2>&1 || true
|
||||
|
||||
# Collect system info for context
|
||||
section SYSTEM
|
||||
logd "uname -a:"; uname -a >> "$DETAILS"
|
||||
logd "docker version:"; docker version --format '{{.Server.Version}}' >> "$DETAILS" 2>&1 || true
|
||||
logd "compose ps:"; (cd "$ROOT/compose" && if docker compose version >/dev/null 2>&1; then docker compose -p argus-sys ps; else docker-compose -p argus-sys ps; fi) >> "$DETAILS" 2>&1 || true
|
||||
|
||||
section SUMMARY
|
||||
# Add HTTP failures and CORS problems to error log with tags
|
||||
[[ $(http_code "http://localhost:${ES_HTTP_PORT:-9200}/_cluster/health") != 200 ]] && echo "[es][http] health not 200" >> "$ERRORS"
|
||||
kbcode=$(http_code "http://localhost:${KIBANA_PORT:-5601}/api/status"); [[ "$kbcode" != 200 ]] && echo "[kibana][http] /api/status=$kbcode" >> "$ERRORS"
|
||||
[[ $(http_code "http://localhost:${MASTER_PORT:-32300}/readyz") != 200 ]] && echo "[master][http] /readyz not 200" >> "$ERRORS"
|
||||
[[ $(http_code "http://localhost:${PROMETHEUS_PORT:-9090}/-/ready") != 200 ]] && echo "[prometheus][http] /-/ready not 200" >> "$ERRORS"
|
||||
gfcode=$(http_code "http://localhost:${GRAFANA_PORT:-3000}/api/health"); [[ "$gfcode" != 200 ]] && echo "[grafana][http] /api/health=$gfcode" >> "$ERRORS"
|
||||
[[ $(http_code "http://localhost:${ALERTMANAGER_PORT:-9093}/api/v2/status") != 200 ]] && echo "[alertmanager][http] /api/v2/status not 200" >> "$ERRORS"
|
||||
[[ -z "$cors8084" ]] && echo "[web-proxy][cors] 8084 missing Access-Control-Allow-Origin" >> "$ERRORS"
|
||||
[[ -z "$cors8085" ]] && echo "[web-proxy][cors] 8085 missing Access-Control-Allow-Origin" >> "$ERRORS"
|
||||
|
||||
# Deduplicate errors
|
||||
sort -u -o "$ERRORS" "$ERRORS"
|
||||
|
||||
echo "Diagnostic details -> $DETAILS"
|
||||
echo "Detected errors -> $ERRORS"
|
||||
|
||||
if [[ "$LOG_DIR" == "$ROOT/logs" ]]; then
|
||||
# maintain latest symlinks when writing under package logs
|
||||
ln -sfn "$(basename "$DETAILS")" "$ROOT/logs/diagnose_details.log" 2>/dev/null || cp "$DETAILS" "$ROOT/logs/diagnose_details.log" 2>/dev/null || true
|
||||
ln -sfn "$(basename "$ERRORS")" "$ROOT/logs/diagnose_error.log" 2>/dev/null || cp "$ERRORS" "$ROOT/logs/diagnose_error.log" 2>/dev/null || true
|
||||
else
|
||||
echo "Diagnostic details -> $DETAILS"
|
||||
echo "Detected errors -> $ERRORS"
|
||||
fi
|
||||
|
||||
exit 0
|
||||
281
deployment/build/templates/scripts/server-install.sh
Executable file
281
deployment/build/templates/scripts/server-install.sh
Executable file
@@ -0,0 +1,281 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PKG_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" # version root
|
||||
|
||||
PROJECT_NAME="argus-sys"
|
||||
|
||||
log() { echo -e "\033[0;34m[INSTALL]\033[0m $*"; }
|
||||
err() { echo -e "\033[0;31m[ERROR ]\033[0m $*" >&2; }
|
||||
|
||||
require() { command -v "$1" >/dev/null 2>&1 || { err "missing command: $1"; exit 1; }; }
|
||||
|
||||
require docker
|
||||
if docker compose version >/dev/null 2>&1; then COMPOSE=(docker compose); else require docker-compose; COMPOSE=(docker-compose); fi
|
||||
|
||||
ENV_FILE="$PKG_ROOT/compose/.env"
|
||||
ENV_TEMPLATE="$PKG_ROOT/compose/.env.example"
|
||||
|
||||
find_free_port() {
|
||||
local prefer="$1"; local start=${2:-20000}; local max=${3:-65000};
|
||||
if ! ss -ltnH 2>/dev/null | awk -v pat=":"$prefer"$" '$4 ~ pat{f=1} END{exit f?0:1}'; then echo "$prefer"; return; fi
|
||||
for ((p=start; p<=max; p++)); do
|
||||
if ! ss -ltnH 2>/dev/null | awk -v pat=":"$p"$" '$4 ~ pat{f=1} END{exit f?0:1}'; then echo "$p"; return; fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
prepare_env() {
|
||||
if [[ -f "$ENV_FILE" ]]; then log ".env exists, keep as-is"; return; fi
|
||||
[[ -f "$ENV_TEMPLATE" ]] || { err "missing $ENV_TEMPLATE"; exit 1; }
|
||||
cp "$ENV_TEMPLATE" "$ENV_FILE"
|
||||
# overlay 模式下,避免为不同服务分配到同一新端口;保持模板端口不做自动改写
|
||||
}
|
||||
|
||||
prepare_data_dirs() {
|
||||
if [[ $EUID -ne 0 ]]; then
|
||||
echo -e "\033[1;33m[WARN]\033[0m running as non-root: will not chown data dirs."
|
||||
echo -e "\033[1;33m[WARN]\033[0m If you hit Permission denied, run: sudo $SCRIPT_DIR/server-prepare-dirs.sh"
|
||||
# still ensure basic directories exist (no chown)
|
||||
mkdir -p \
|
||||
"$PKG_ROOT/private/argus/etc" \
|
||||
"$PKG_ROOT/private/argus/log/elasticsearch" \
|
||||
"$PKG_ROOT/private/argus/log/kibana" \
|
||||
"$PKG_ROOT/private/argus/metric/prometheus" \
|
||||
"$PKG_ROOT/private/argus/metric/prometheus/data" \
|
||||
"$PKG_ROOT/private/argus/metric/prometheus/rules" \
|
||||
"$PKG_ROOT/private/argus/metric/grafana" \
|
||||
"$PKG_ROOT/private/argus/metric/grafana/data" \
|
||||
"$PKG_ROOT/private/argus/metric/grafana/logs" \
|
||||
"$PKG_ROOT/private/argus/metric/grafana/plugins" \
|
||||
"$PKG_ROOT/private/argus/metric/grafana/provisioning/datasources" \
|
||||
"$PKG_ROOT/private/argus/metric/grafana/provisioning/dashboards" \
|
||||
"$PKG_ROOT/private/argus/metric/grafana/data/sessions" \
|
||||
"$PKG_ROOT/private/argus/metric/grafana/data/dashboards" \
|
||||
"$PKG_ROOT/private/argus/alert/alertmanager" \
|
||||
"$PKG_ROOT/private/argus/metric/ftp/share"
|
||||
# non-root: relax permissions to avoid container UID mismatch blocking writes
|
||||
chmod -R a+rwx "$PKG_ROOT/private/argus" 2>/dev/null || true
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_swarm_and_overlay() {
|
||||
local net_name="${OVERLAY_NET_NAME:-argus-sys-net}"
|
||||
# Require swarm active
|
||||
local state
|
||||
state=$(docker info --format '{{.Swarm.LocalNodeState}}' 2>/dev/null || echo "")
|
||||
if [[ "$state" != "active" ]]; then
|
||||
err "Docker Swarm is not active. On this host run:"
|
||||
err " docker swarm init --advertise-addr <this_host_ip>"
|
||||
exit 1
|
||||
fi
|
||||
# Create attachable overlay if missing
|
||||
if ! docker network inspect "$net_name" >/dev/null 2>&1; then
|
||||
log "creating attachable overlay network: $net_name"
|
||||
docker network create --driver overlay --attachable "$net_name" >/dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
bootstrap_dns_conf() {
|
||||
local etc_dir="$PKG_ROOT/private/argus/etc"
|
||||
mkdir -p "$etc_dir"
|
||||
local dns_file="$etc_dir/dns.conf"
|
||||
if [[ ! -s "$dns_file" ]]; then
|
||||
# detect host primary IP
|
||||
local host_ip
|
||||
host_ip=$(ip route get 1.1.1.1 2>/dev/null | awk '/src/ {print $7; exit}')
|
||||
[[ -z "$host_ip" ]] && host_ip=$(hostname -I 2>/dev/null | awk '{print $1}')
|
||||
if [[ -n "$host_ip" ]]; then
|
||||
echo "$host_ip" > "$dns_file"
|
||||
log "wrote initial dns.conf with host IP: $host_ip"
|
||||
else
|
||||
err "failed to determine host IP for dns.conf; please edit $dns_file manually"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
load_images() {
|
||||
local tar="$PKG_ROOT/images/all-images.tar.gz"
|
||||
[[ -f "$tar" ]] || { err "missing images tar: $tar"; exit 1; }
|
||||
log "loading images from $(basename "$tar") (may take minutes)"
|
||||
gunzip -c "$tar" | docker load >/dev/null
|
||||
}
|
||||
|
||||
bring_up() {
|
||||
log "starting services via compose"
|
||||
ensure_swarm_and_overlay
|
||||
bootstrap_dns_conf
|
||||
local ov="$PKG_ROOT/compose/docker-compose.os-compat.override.yml"
|
||||
if [[ ! -f "$ov" ]]; then
|
||||
cat > "$ov" <<'YAML'
|
||||
services:
|
||||
bind:
|
||||
security_opt: ["label=disable"]
|
||||
userns_mode: "host"
|
||||
tmpfs:
|
||||
- /run/named
|
||||
master:
|
||||
security_opt: ["label=disable"]
|
||||
userns_mode: "host"
|
||||
es:
|
||||
security_opt: ["label=disable"]
|
||||
userns_mode: "host"
|
||||
kibana:
|
||||
security_opt: ["label=disable"]
|
||||
userns_mode: "host"
|
||||
ftp:
|
||||
security_opt: ["label=disable"]
|
||||
userns_mode: "host"
|
||||
prometheus:
|
||||
security_opt: ["label=disable"]
|
||||
userns_mode: "host"
|
||||
grafana:
|
||||
security_opt: ["label=disable"]
|
||||
userns_mode: "host"
|
||||
alertmanager:
|
||||
security_opt: ["label=disable"]
|
||||
userns_mode: "host"
|
||||
# ensure runtime path matches container expectation
|
||||
volumes:
|
||||
- ../private/argus/etc:/private/argus/etc
|
||||
- ../private/argus/alert/alertmanager:/alertmanager
|
||||
web-frontend:
|
||||
security_opt: ["label=disable"]
|
||||
userns_mode: "host"
|
||||
web-proxy:
|
||||
security_opt: ["label=disable"]
|
||||
userns_mode: "host"
|
||||
YAML
|
||||
log "generated OS-compat override: $(basename "$ov")"
|
||||
fi
|
||||
# 仅启动服务端组件,避免误起测试节点(node-a/node-b/test-node/test-gpu-node)
|
||||
local services=(bind master es kibana ftp prometheus grafana alertmanager web-frontend web-proxy)
|
||||
log "services: ${services[*]}"
|
||||
(cd "$PKG_ROOT/compose" && "${COMPOSE[@]}" -p "$PROJECT_NAME" -f docker-compose.yml -f $(basename "$ov") up -d "${services[@]}")
|
||||
}
|
||||
|
||||
# Post bootstrap container-side fixes that do not require sudo on host.
|
||||
post_bootstrap_fixes() {
|
||||
# Kibana: ensure /usr/share/kibana/data is a symlink into mounted path to avoid EACCES
|
||||
if docker ps --format '{{.Names}}' | grep -q '^argus-kibana-sys$'; then
|
||||
docker exec argus-kibana-sys bash -lc '
|
||||
set -e
|
||||
mkdir -p /private/argus/log/kibana && chmod 777 /private/argus/log/kibana || true
|
||||
if [ -d /usr/share/kibana/data ] && [ ! -L /usr/share/kibana/data ]; then rm -rf /usr/share/kibana/data; fi
|
||||
if [ ! -e /usr/share/kibana/data ]; then ln -s /private/argus/log/kibana /usr/share/kibana/data; fi
|
||||
' >/dev/null 2>&1 || true
|
||||
fi
|
||||
# Elasticsearch: ensure data path points to mounted path and is writable
|
||||
if docker ps --format '{{.Names}}' | grep -q '^argus-es-sys$'; then
|
||||
docker exec argus-es-sys bash -lc '
|
||||
set -e
|
||||
mkdir -p /private/argus/log/elasticsearch && chmod 777 /private/argus/log/elasticsearch || true
|
||||
if [ -d /usr/share/elasticsearch/data ] && [ ! -L /usr/share/elasticsearch/data ]; then rm -rf /usr/share/elasticsearch/data; fi
|
||||
if [ ! -e /usr/share/elasticsearch/data ]; then ln -s /private/argus/log/elasticsearch /usr/share/elasticsearch/data; fi
|
||||
' >/dev/null 2>&1 || true
|
||||
fi
|
||||
# Bind9: ensure rndc.key exists
|
||||
if docker ps --format '{{.Names}}' | grep -q '^argus-bind-sys$'; then
|
||||
docker exec argus-bind-sys bash -lc '
|
||||
set -e
|
||||
mkdir -p /etc/bind
|
||||
if [ ! -f /etc/bind/rndc.key ]; then rndc-confgen -a -c /etc/bind/rndc.key; fi
|
||||
chmod 644 /etc/bind/rndc.key || true
|
||||
' >/dev/null 2>&1 || true
|
||||
fi
|
||||
}
|
||||
|
||||
dns_bootstrap() {
|
||||
log "DNS bootstrap: initializing shared dns.conf and container resolv.conf"
|
||||
local etc_dir="$PKG_ROOT/private/argus/etc"
|
||||
mkdir -p "$etc_dir"
|
||||
# 1) ensure dns.conf exists (fallback to bind IP 172.31.0.2)
|
||||
if [[ ! -s "$etc_dir/dns.conf" ]]; then
|
||||
if echo "172.31.0.2" > "$etc_dir/dns.conf" 2>/dev/null; then
|
||||
log "wrote fallback dns.conf with 172.31.0.2"
|
||||
else
|
||||
# host-side write denied (ownership 1000:1000); write via bind container instead
|
||||
if docker ps --format '{{.Names}}' | grep -q '^argus-bind-sys$'; then
|
||||
docker exec argus-bind-sys sh -lc 'echo 172.31.0.2 > /private/argus/etc/dns.conf && chmod 644 /private/argus/etc/dns.conf' || true
|
||||
log "fallback dns.conf written via bind container"
|
||||
else
|
||||
log "bind not ready; skip writing fallback dns.conf"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
# 2) wait briefly for bind to copy update-dns.sh into shared etc (bind startup.sh does this)
|
||||
local i=0
|
||||
while [[ ! -x "$etc_dir/update-dns.sh" && $i -lt 20 ]]; do
|
||||
sleep 0.5; ((i++));
|
||||
done
|
||||
if [[ ! -x "$etc_dir/update-dns.sh" ]]; then
|
||||
log "update-dns.sh not present yet; continuing with existing resolv.conf"
|
||||
fi
|
||||
# 3) run update-dns.sh inside key containers so /etc/resolv.conf points to bind
|
||||
local c
|
||||
for c in argus-master-sys argus-es-sys argus-kibana-sys argus-grafana argus-prometheus argus-ftp argus-web-frontend argus-web-proxy argus-alertmanager; do
|
||||
if docker ps --format '{{.Names}}' | grep -q "^${c}$"; then
|
||||
docker exec "$c" sh -lc 'test -x /private/argus/etc/update-dns.sh && /private/argus/etc/update-dns.sh || true' >/dev/null 2>&1 || true
|
||||
fi
|
||||
done
|
||||
# 4) wait for service A-record hint files generated by services (best-effort)
|
||||
local need=( es.log.argus.com kibana.log.argus.com master.argus.com grafana.metric.argus.com prom.metric.argus.com alertmanager.alert.argus.com )
|
||||
local waited=0; local missing=1
|
||||
while (( waited < 15 )); do
|
||||
missing=0
|
||||
for f in "${need[@]}"; do [[ -s "$etc_dir/$f" ]] || { missing=1; break; }; done
|
||||
[[ $missing -eq 0 ]] && break
|
||||
sleep 1; ((waited++))
|
||||
done
|
||||
# 5) reload bind zone (script uses supervisor to restart bind9)
|
||||
if docker ps --format '{{.Names}}' | grep -q '^argus-bind-sys$'; then
|
||||
docker exec argus-bind-sys sh -lc '/usr/local/bin/reload-bind9.sh' >/dev/null 2>&1 || true
|
||||
fi
|
||||
# 6) restart web-proxy once to re-render nginx resolver with latest dns.conf
|
||||
if docker ps --format '{{.Names}}' | grep -q '^argus-web-proxy$'; then
|
||||
docker restart argus-web-proxy >/dev/null 2>&1 || true
|
||||
fi
|
||||
}
|
||||
|
||||
selfcheck() {
|
||||
# Initial selfcheck with retries to absorb cold starts
|
||||
local max_retries="${SELF_CHECK_RETRIES:-5}" # 重试次数(不含首次),默认 5
|
||||
local wait_seconds="${SELF_CHECK_WAIT_SECONDS:-30}" # 每次重试前等待秒数,默认 30s
|
||||
|
||||
local attempt=0
|
||||
while :; do
|
||||
attempt=$((attempt+1))
|
||||
if (( attempt == 1 )); then
|
||||
log "running selfcheck (attempt ${attempt})"
|
||||
else
|
||||
log "running selfcheck (attempt ${attempt}/${max_retries}+1)"
|
||||
fi
|
||||
|
||||
if bash "$PKG_ROOT/scripts/server-selfcheck.sh"; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
# failed
|
||||
if (( attempt > max_retries )); then
|
||||
err "selfcheck failed after ${attempt} attempt(s)"
|
||||
exit 1
|
||||
fi
|
||||
log "selfcheck not ready yet; retrying in ${wait_seconds}s..."
|
||||
sleep "$wait_seconds"
|
||||
done
|
||||
}
|
||||
|
||||
main() {
|
||||
mkdir -p "$PKG_ROOT/logs"
|
||||
prepare_env
|
||||
prepare_data_dirs
|
||||
load_images
|
||||
bring_up
|
||||
post_bootstrap_fixes
|
||||
dns_bootstrap
|
||||
selfcheck
|
||||
log "install completed. See logs in $PKG_ROOT/logs/"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
73
deployment/build/templates/scripts/server-prepare-dirs.sh
Executable file
73
deployment/build/templates/scripts/server-prepare-dirs.sh
Executable file
@@ -0,0 +1,73 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PKG_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
|
||||
if [[ $EUID -ne 0 ]]; then
|
||||
echo "[PREPARE] This script requires root (sudo)." >&2
|
||||
echo " Try: sudo $0" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ENV_FILE="$PKG_ROOT/compose/.env"
|
||||
[[ -f "$ENV_FILE" ]] && set -a && source "$ENV_FILE" && set +a
|
||||
UIDV="${ARGUS_BUILD_UID:-1000}"; GIDV="${ARGUS_BUILD_GID:-1000}"
|
||||
|
||||
echo "[PREPARE] Using owner ${UIDV}:${GIDV}"
|
||||
|
||||
# Core etc and service data dirs (aligned with src/sys/tests/scripts/01_bootstrap.sh)
|
||||
mkdir -p \
|
||||
"$PKG_ROOT/private/argus/etc" \
|
||||
"$PKG_ROOT/private/argus/bind" \
|
||||
"$PKG_ROOT/private/argus/master" \
|
||||
"$PKG_ROOT/private/argus/agent" \
|
||||
"$PKG_ROOT/private/argus/log/elasticsearch" \
|
||||
"$PKG_ROOT/private/argus/log/kibana"
|
||||
|
||||
# Prometheus
|
||||
mkdir -p \
|
||||
"$PKG_ROOT/private/argus/metric/prometheus" \
|
||||
"$PKG_ROOT/private/argus/metric/prometheus/data" \
|
||||
"$PKG_ROOT/private/argus/metric/prometheus/rules" \
|
||||
"$PKG_ROOT/private/argus/metric/prometheus/targets"
|
||||
|
||||
# Grafana
|
||||
mkdir -p \
|
||||
"$PKG_ROOT/private/argus/metric/grafana" \
|
||||
"$PKG_ROOT/private/argus/metric/grafana/data" \
|
||||
"$PKG_ROOT/private/argus/metric/grafana/logs" \
|
||||
"$PKG_ROOT/private/argus/metric/grafana/plugins" \
|
||||
"$PKG_ROOT/private/argus/metric/grafana/provisioning/datasources" \
|
||||
"$PKG_ROOT/private/argus/metric/grafana/provisioning/dashboards" \
|
||||
"$PKG_ROOT/private/argus/metric/grafana/data/sessions" \
|
||||
"$PKG_ROOT/private/argus/metric/grafana/data/dashboards" \
|
||||
"$PKG_ROOT/private/argus/metric/grafana/config"
|
||||
|
||||
# FTP
|
||||
mkdir -p "$PKG_ROOT/private/argus/metric/ftp/share"
|
||||
|
||||
# Alertmanager
|
||||
mkdir -p "$PKG_ROOT/private/argus/alert/alertmanager"
|
||||
|
||||
chown -R "$UIDV":"$GIDV" \
|
||||
"$PKG_ROOT/private/argus/etc" \
|
||||
"$PKG_ROOT/private/argus/bind" \
|
||||
"$PKG_ROOT/private/argus/master" \
|
||||
"$PKG_ROOT/private/argus/agent" \
|
||||
"$PKG_ROOT/private/argus/log/elasticsearch" \
|
||||
"$PKG_ROOT/private/argus/log/kibana" \
|
||||
"$PKG_ROOT/private/argus/metric/prometheus" \
|
||||
"$PKG_ROOT/private/argus/metric/grafana" \
|
||||
"$PKG_ROOT/private/argus/metric/ftp" \
|
||||
"$PKG_ROOT/private/argus/alert"
|
||||
|
||||
chmod -R g+w "$PKG_ROOT/private/argus/alert" "$PKG_ROOT/private/argus/etc" || true
|
||||
|
||||
# Ensure parent directories also owned by runtime user for consistency
|
||||
chown "$UIDV":"$GIDV" \
|
||||
"$PKG_ROOT/private/argus" \
|
||||
"$PKG_ROOT/private/argus/log" \
|
||||
"$PKG_ROOT/private/argus/metric" || true
|
||||
|
||||
echo "[PREPARE] Done. You can now run server-install.sh"
|
||||
104
deployment/build/templates/scripts/server-selfcheck.sh
Executable file
104
deployment/build/templates/scripts/server-selfcheck.sh
Executable file
@@ -0,0 +1,104 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
|
||||
log() { echo -e "\033[0;34m[CHECK]\033[0m $*"; }
|
||||
err() { echo -e "\033[0;31m[ERROR]\033[0m $*" >&2; }
|
||||
|
||||
ENV_FILE="$ROOT/compose/.env"; [[ -f "$ENV_FILE" ]] && set -a && source "$ENV_FILE" && set +a
|
||||
|
||||
wait_http() { local url="$1"; local attempts=${2:-120}; local i=1; while ((i<=attempts)); do curl -fsS "$url" >/dev/null 2>&1 && return 0; echo "[..] waiting $url ($i/$attempts)"; sleep 5; ((i++)); done; return 1; }
|
||||
code_for() { curl -s -o /dev/null -w "%{http_code}" "$1" || echo 000; }
|
||||
header_val() { curl -s -D - -o /dev/null "$@" | awk -F': ' 'BEGIN{IGNORECASE=1}$1=="Access-Control-Allow-Origin"{gsub("\r","",$2);print $2}'; }
|
||||
|
||||
LOG_DIR="$ROOT/logs"
|
||||
mkdir -p "$LOG_DIR" || true
|
||||
OUT_JSON="$LOG_DIR/selfcheck.json"
|
||||
tmp=$(mktemp)
|
||||
|
||||
ok=1
|
||||
|
||||
log "checking overlay network"
|
||||
net_ok=false
|
||||
if docker network inspect "${OVERLAY_NET_NAME:-argus-sys-net}" >/dev/null 2>&1; then
|
||||
if docker network inspect "${OVERLAY_NET_NAME:-argus-sys-net}" | grep -q '"Driver": "overlay"'; then net_ok=true; fi
|
||||
fi
|
||||
[[ "$net_ok" == true ]] || ok=0
|
||||
|
||||
log "checking Elasticsearch (via domain inside web-proxy)"
|
||||
if docker exec argus-web-proxy sh -lc "curl -fsS http://es.log.argus.com:9200/_cluster/health?wait_for_status=yellow&timeout=1s" >/dev/null 2>&1; then es_ok=true; else es_ok=false; ok=0; fi
|
||||
|
||||
log "checking Kibana (via domain inside web-proxy)"
|
||||
kb_code=$(docker exec argus-web-proxy sh -lc "curl -s -o /dev/null -w '%{http_code}' http://kibana.log.argus.com:5601/api/status" || echo 000)
|
||||
kb_ok=false
|
||||
if [[ "$kb_code" == "200" ]]; then body=$(curl -sS "http://localhost:${KIBANA_PORT:-5601}/api/status"); echo "$body" | grep -q '"level":"available"' && kb_ok=true; fi
|
||||
[[ "$kb_ok" == true ]] || ok=0
|
||||
|
||||
log "checking Master (via domain inside web-proxy)"
|
||||
if docker exec argus-web-proxy sh -lc "curl -fsS http://master.argus.com:3000/readyz" >/dev/null 2>&1; then true; else ok=0; fi
|
||||
|
||||
log "checking FTP"
|
||||
if docker ps --format '{{.Names}}' | grep -q '^argus-ftp$'; then
|
||||
if docker exec argus-ftp sh -lc 'test -w /private/argus/ftp/share'; then ftp_ok=true; else ftp_ok=false; ok=0; fi
|
||||
else
|
||||
ftp_ok=false; ok=0;
|
||||
fi
|
||||
|
||||
log "checking Prometheus"
|
||||
wait_http "http://localhost:${PROMETHEUS_PORT:-9090}/-/ready" 60 || ok=0
|
||||
|
||||
log "checking Grafana"
|
||||
gf_code=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:${GRAFANA_PORT:-3000}/api/health" || echo 000)
|
||||
gf_ok=false; if [[ "$gf_code" == "200" ]]; then body=$(curl -sS "http://localhost:${GRAFANA_PORT:-3000}/api/health"); echo "$body" | grep -q '"database"\s*:\s*"ok"' && gf_ok=true; fi
|
||||
[[ "$gf_ok" == true ]] || ok=0
|
||||
|
||||
log "checking Alertmanager"
|
||||
wait_http "http://localhost:${ALERTMANAGER_PORT:-9093}/api/v2/status" 60 || ok=0
|
||||
|
||||
log "checking Web-Proxy"
|
||||
p8080=$(code_for "http://localhost:${WEB_PROXY_PORT_8080:-8080}/")
|
||||
p8083=$(code_for "http://localhost:${WEB_PROXY_PORT_8083:-8083}/")
|
||||
cors8084=$(header_val -H "Origin: http://localhost:${WEB_PROXY_PORT_8080:-8080}" "http://localhost:${WEB_PROXY_PORT_8084:-8084}/api/v2/status" || true)
|
||||
cors8085=$(header_val -H "Origin: http://localhost:${WEB_PROXY_PORT_8080:-8080}" "http://localhost:${WEB_PROXY_PORT_8085:-8085}/api/v1/master/nodes" || true)
|
||||
wp_ok=true
|
||||
# 有些环境首页可能 403,此处接受 200/403
|
||||
([[ "$p8080" == 200 || "$p8080" == 403 ]]) || wp_ok=false
|
||||
([[ "$p8083" == 200 || "$p8083" == 302 || "$p8083" == 403 ]]) || wp_ok=false
|
||||
[[ -n "$cors8084" && -n "$cors8085" ]] || wp_ok=false
|
||||
[[ "$wp_ok" == true ]] || ok=0
|
||||
|
||||
cat > "$tmp" <<JSON
|
||||
{
|
||||
"es": $es_ok,
|
||||
"kibana": $kb_ok,
|
||||
"master_readyz": true,
|
||||
"ftp_share_writable": $ftp_ok,
|
||||
"prometheus": true,
|
||||
"grafana": $gf_ok,
|
||||
"alertmanager": true,
|
||||
"web_proxy": $wp_ok,
|
||||
"overlay_net": $net_ok,
|
||||
"timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
||||
}
|
||||
JSON
|
||||
|
||||
if ! mv "$tmp" "$OUT_JSON" 2>/dev/null; then
|
||||
# fallback when logs dir not writable (no sudo allowed)
|
||||
OUT_JSON="/tmp/selfcheck_$(date -u +%Y%m%d-%H%M%SZ).json"
|
||||
cp "$tmp" "$OUT_JSON"
|
||||
log "selfcheck.json written to $OUT_JSON (logs dir not writable)"
|
||||
fi
|
||||
if [[ "$ok" == 1 ]]; then
|
||||
log "selfcheck OK"
|
||||
exit 0
|
||||
else
|
||||
err "selfcheck FAILED (see $OUT_JSON)"
|
||||
# If diagnose script exists, run it to collect more details
|
||||
if [[ -x "$SCRIPT_DIR/server-diagnose.sh" ]]; then
|
||||
# run diagnose; it will print the actual timestamped file paths and update 'latest' symlinks
|
||||
"$SCRIPT_DIR/server-diagnose.sh" || true
|
||||
fi
|
||||
exit 1
|
||||
fi
|
||||
28
deployment/build/templates/scripts/server-status.sh
Executable file
28
deployment/build/templates/scripts/server-status.sh
Executable file
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
|
||||
PROJECT_NAME="argus-sys"
|
||||
|
||||
if docker compose version >/dev/null 2>&1; then COMPOSE=(docker compose); else COMPOSE=(docker-compose); fi
|
||||
|
||||
echo "== Containers =="
|
||||
(cd "$ROOT/compose" && "${COMPOSE[@]}" -p "$PROJECT_NAME" ps)
|
||||
|
||||
echo
|
||||
echo "== Key Endpoints =="
|
||||
ENV_FILE="$ROOT/compose/.env"; [[ -f "$ENV_FILE" ]] && set -a && source "$ENV_FILE" && set +a
|
||||
printf "master http://localhost:%s/readyz\n" "${MASTER_PORT:-32300}"
|
||||
printf "es http://localhost:%s/_cluster/health\n" "${ES_HTTP_PORT:-9200}"
|
||||
printf "kibana http://localhost:%s/api/status\n" "${KIBANA_PORT:-5601}"
|
||||
printf "prom http://localhost:%s/-/ready\n" "${PROMETHEUS_PORT:-9090}"
|
||||
printf "grafana http://localhost:%s/api/health\n" "${GRAFANA_PORT:-3000}"
|
||||
printf "alert http://localhost:%s/api/v2/status\n" "${ALERTMANAGER_PORT:-9093}"
|
||||
printf "web http://localhost:%s/ (8080)\n" "${WEB_PROXY_PORT_8080:-8080}"
|
||||
|
||||
echo
|
||||
echo "== Selfcheck result =="
|
||||
cat "$ROOT/logs/selfcheck.json" 2>/dev/null || echo "(no selfcheck yet)"
|
||||
|
||||
16
deployment/build/templates/scripts/server-uninstall.sh
Executable file
16
deployment/build/templates/scripts/server-uninstall.sh
Executable file
@@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PKG_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
|
||||
PROJECT_NAME="argus-sys"
|
||||
|
||||
log() { echo -e "\033[0;34m[UNINSTALL]\033[0m $*"; }
|
||||
|
||||
if docker compose version >/dev/null 2>&1; then COMPOSE=(docker compose); else COMPOSE=(docker-compose); fi
|
||||
|
||||
(cd "$PKG_ROOT/compose" && "${COMPOSE[@]}" -p "$PROJECT_NAME" down -v || true)
|
||||
log "compose stack removed"
|
||||
log "you may remove data under $PKG_ROOT/private if you want a clean slate"
|
||||
|
||||
BIN
doc/metric_lists.xlsx
Normal file
BIN
doc/metric_lists.xlsx
Normal file
Binary file not shown.
@@ -12,6 +12,8 @@ VENV_DIR="$BUILD_ROOT/venv"
|
||||
|
||||
AGENT_BUILD_IMAGE="${AGENT_BUILD_IMAGE:-python:3.11-slim-bullseye}"
|
||||
AGENT_BUILD_USE_DOCKER="${AGENT_BUILD_USE_DOCKER:-1}"
|
||||
# 默认在容器内忽略代理以避免公司内网代理在 Docker 网络不可达导致 pip 失败(可用 0 关闭)
|
||||
AGENT_BUILD_IGNORE_PROXY="${AGENT_BUILD_IGNORE_PROXY:-1}"
|
||||
USED_DOCKER=0
|
||||
|
||||
run_host_build() {
|
||||
@@ -71,6 +73,7 @@ run_docker_build() {
|
||||
pass_env_if_set http_proxy
|
||||
pass_env_if_set https_proxy
|
||||
pass_env_if_set no_proxy
|
||||
pass_env_if_set AGENT_BUILD_IGNORE_PROXY
|
||||
|
||||
build_script=$(cat <<'INNER'
|
||||
set -euo pipefail
|
||||
@@ -82,6 +85,10 @@ rm -rf build dist
|
||||
mkdir -p build/pyinstaller dist
|
||||
python3 -m venv --copies build/venv
|
||||
source build/venv/bin/activate
|
||||
# 若指定忽略代理,则清空常见代理与 pip 镜像环境变量,避免容器内代理不可达
|
||||
if [ "${AGENT_BUILD_IGNORE_PROXY:-1}" = "1" ]; then
|
||||
unset http_proxy https_proxy HTTP_PROXY HTTPS_PROXY PIP_INDEX_URL PIP_EXTRA_INDEX_URL PIP_TRUSTED_HOST
|
||||
fi
|
||||
pip install --upgrade pip
|
||||
pip install .
|
||||
pip install pyinstaller==6.6.0
|
||||
|
||||
@@ -9,21 +9,21 @@ RUN apt-get update && \
|
||||
apt-get install -y wget supervisor net-tools inetutils-ping vim ca-certificates passwd && \
|
||||
apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# 设置 Alertmanager 版本
|
||||
# 设置 Alertmanager 版本(与本地离线包保持一致)
|
||||
ARG ALERTMANAGER_VERSION=0.28.1
|
||||
|
||||
# 下载并解压 Alertmanager 二进制
|
||||
RUN wget https://github.com/prometheus/alertmanager/releases/download/v${ALERTMANAGER_VERSION}/alertmanager-${ALERTMANAGER_VERSION}.linux-amd64.tar.gz && \
|
||||
tar xvf alertmanager-${ALERTMANAGER_VERSION}.linux-amd64.tar.gz && \
|
||||
mv alertmanager-${ALERTMANAGER_VERSION}.linux-amd64 /usr/local/alertmanager && \
|
||||
rm alertmanager-${ALERTMANAGER_VERSION}.linux-amd64.tar.gz
|
||||
# 使用仓库内预置的离线包构建(无需联网)
|
||||
COPY src/alert/alertmanager/build/alertmanager-${ALERTMANAGER_VERSION}.linux-amd64.tar.gz /tmp/
|
||||
RUN tar xvf /tmp/alertmanager-${ALERTMANAGER_VERSION}.linux-amd64.tar.gz -C /tmp && \
|
||||
mv /tmp/alertmanager-${ALERTMANAGER_VERSION}.linux-amd64 /usr/local/alertmanager && \
|
||||
rm -f /tmp/alertmanager-${ALERTMANAGER_VERSION}.linux-amd64.tar.gz
|
||||
|
||||
ENV ALERTMANAGER_BASE_PATH=/private/argus/alert/alertmanager
|
||||
|
||||
ARG ARGUS_UID=2133
|
||||
ARG ARGUS_GID=2015
|
||||
ENV ARGUS_UID=${ARGUS_UID}
|
||||
ENV ARGUS_GID=${ARGUS_GID}
|
||||
ARG ARGUS_BUILD_UID=2133
|
||||
ARG ARGUS_BUILD_GID=2015
|
||||
ENV ARGUS_BUILD_UID=${ARGUS_BUILD_UID}
|
||||
ENV ARGUS_BUILD_GID=${ARGUS_BUILD_GID}
|
||||
|
||||
RUN mkdir -p /usr/share/alertmanager && \
|
||||
mkdir -p ${ALERTMANAGER_BASE_PATH} && \
|
||||
@@ -33,16 +33,24 @@ RUN mkdir -p /usr/share/alertmanager && \
|
||||
|
||||
# 创建 alertmanager 用户(可自定义 UID/GID)
|
||||
# 创建 alertmanager 用户组
|
||||
RUN groupadd -g ${ARGUS_GID} alertmanager
|
||||
RUN set -eux; \
|
||||
# 确保目标 GID 存在;若已被占用,直接使用该 GID(组名不限)\
|
||||
if ! getent group "${ARGUS_BUILD_GID}" >/dev/null; then \
|
||||
groupadd -g "${ARGUS_BUILD_GID}" alertmanager || true; \
|
||||
fi; \
|
||||
# 确保存在 alertmanager 用户;若 UID 已被占用,跳过并继续使用现有 UID 的用户
|
||||
if ! id alertmanager >/dev/null 2>&1; then \
|
||||
if getent passwd "${ARGUS_BUILD_UID}" >/dev/null; then \
|
||||
# UID 已占用,则创建同名用户但不指定 UID(避免冲突),仅保证 user 存在
|
||||
useradd -M -s /usr/sbin/nologin -g "${ARGUS_BUILD_GID}" alertmanager || true; \
|
||||
else \
|
||||
useradd -M -s /usr/sbin/nologin -u "${ARGUS_BUILD_UID}" -g "${ARGUS_BUILD_GID}" alertmanager || true; \
|
||||
fi; \
|
||||
else \
|
||||
usermod -g "${ARGUS_BUILD_GID}" alertmanager || true; \
|
||||
fi
|
||||
|
||||
# 创建 alertmanager 用户并指定组
|
||||
RUN useradd -M -s /usr/sbin/nologin -u ${ARGUS_UID} -g ${ARGUS_GID} alertmanager
|
||||
|
||||
RUN chown -R alertmanager:alertmanager /usr/share/alertmanager && \
|
||||
chown -R alertmanager:alertmanager /alertmanager && \
|
||||
chown -R alertmanager:alertmanager ${ALERTMANAGER_BASE_PATH} && \
|
||||
chown -R alertmanager:alertmanager /private/argus/etc && \
|
||||
chown -R alertmanager:alertmanager /usr/local/bin
|
||||
RUN chown -R "${ARGUS_BUILD_UID}:${ARGUS_BUILD_GID}" /usr/share/alertmanager /alertmanager ${ALERTMANAGER_BASE_PATH} /private/argus/etc /usr/local/bin || true
|
||||
|
||||
# 配置内网 apt 源 (如果指定了内网选项)
|
||||
RUN if [ "$USE_INTRANET" = "true" ]; then \
|
||||
@@ -86,4 +94,3 @@ EXPOSE 9093
|
||||
|
||||
# 使用 supervisor 作为入口点
|
||||
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]
|
||||
|
||||
|
||||
Binary file not shown.
@@ -5,8 +5,8 @@ docker pull ubuntu:24.04
|
||||
source src/alert/tests/.env
|
||||
|
||||
docker build \
|
||||
--build-arg ARGUS_UID=${ARGUS_UID} \
|
||||
--build-arg ARGUS_GID=${ARGUS_GID} \
|
||||
--build-arg ARGUS_BUILD_UID=${ARGUS_BUILD_UID} \
|
||||
--build-arg ARGUS_BUILD_GID=${ARGUS_BUILD_GID} \
|
||||
-f src/alert/alertmanager/build/Dockerfile \
|
||||
-t argus-alertmanager:latest .
|
||||
|
||||
|
||||
22
src/alert/alertmanager/build/fetch-dist.sh
Normal file
22
src/alert/alertmanager/build/fetch-dist.sh
Normal file
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# 下载 Alertmanager 离线安装包到本目录,用于 Docker 构建时 COPY
|
||||
# 用法:
|
||||
# ./fetch-dist.sh [version]
|
||||
# 示例:
|
||||
# ./fetch-dist.sh 0.28.1
|
||||
|
||||
VER="${1:-0.28.1}"
|
||||
OUT="alertmanager-${VER}.linux-amd64.tar.gz"
|
||||
URL="https://github.com/prometheus/alertmanager/releases/download/v${VER}/${OUT}"
|
||||
|
||||
if [[ -f "$OUT" ]]; then
|
||||
echo "[INFO] $OUT already exists, skip download"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "[INFO] Downloading $URL"
|
||||
curl -fL --retry 3 --connect-timeout 10 -o "$OUT" "$URL"
|
||||
echo "[OK] Saved to $(pwd)/$OUT"
|
||||
|
||||
@@ -7,10 +7,8 @@ ALERTMANAGER_BASE_PATH=${ALERTMANAGER_BASE_PATH:-/private/argus/alert/alertmanag
|
||||
|
||||
echo "[INFO] Alertmanager base path: ${ALERTMANAGER_BASE_PATH}"
|
||||
|
||||
# 生成配置文件
|
||||
echo "[INFO] Generating Alertmanager configuration file..."
|
||||
sed "s|\${ALERTMANAGER_BASE_PATH}|${ALERTMANAGER_BASE_PATH}|g" \
|
||||
/etc/alertmanager/alertmanager.yml > ${ALERTMANAGER_BASE_PATH}/alertmanager.yml
|
||||
# 使用容器内的 /etc/alertmanager/alertmanager.yml 作为配置文件,避免写入挂载卷导致的权限问题
|
||||
echo "[INFO] Using /etc/alertmanager/alertmanager.yml as configuration"
|
||||
|
||||
|
||||
# 记录容器 IP 地址
|
||||
|
||||
@@ -6,7 +6,7 @@ user=root
|
||||
|
||||
[program:alertmanager]
|
||||
command=/usr/local/bin/start-am-supervised.sh
|
||||
user=alertmanager
|
||||
user=ubuntu
|
||||
stdout_logfile=/var/log/supervisor/alertmanager.log
|
||||
stderr_logfile=/var/log/supervisor/alertmanager_error.log
|
||||
autorestart=true
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
DATA_ROOT=/home/argus/tmp/private/argus
|
||||
ARGUS_UID=1048
|
||||
ARGUS_GID=1048
|
||||
ARGUS_BUILD_UID=1048
|
||||
ARGUS_BUILD_GID=1048
|
||||
|
||||
USE_INTRANET=false
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
DATA_ROOT=/home/argus/tmp/private/argus
|
||||
ARGUS_UID=1048
|
||||
ARGUS_GID=1048
|
||||
ARGUS_BUILD_UID=1048
|
||||
ARGUS_BUILD_GID=1048
|
||||
|
||||
USE_INTRANET=false
|
||||
@@ -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']
|
||||
@@ -1 +0,0 @@
|
||||
172.18.0.2
|
||||
@@ -4,15 +4,15 @@ services:
|
||||
context: ../../../
|
||||
dockerfile: src/alert/alertmanager/build/Dockerfile
|
||||
args:
|
||||
ARGUS_UID: ${ARGUS_UID:-2133}
|
||||
ARGUS_GID: ${ARGUS_GID:-2015}
|
||||
ARGUS_BUILD_UID: ${ARGUS_BUILD_UID:-2133}
|
||||
ARGUS_BUILD_GID: ${ARGUS_BUILD_GID:-2015}
|
||||
USE_INTRANET: ${USE_INTRANET:-false}
|
||||
image: argus-alertmanager:latest
|
||||
container_name: argus-alertmanager
|
||||
environment:
|
||||
- ALERTMANAGER_BASE_PATH=/private/argus/alert/alertmanager
|
||||
- ARGUS_UID=${ARGUS_UID:-2133}
|
||||
- ARGUS_GID=${ARGUS_GID:-2015}
|
||||
- ARGUS_BUILD_UID=${ARGUS_BUILD_UID:-2133}
|
||||
- ARGUS_BUILD_GID=${ARGUS_BUILD_GID:-2015}
|
||||
ports:
|
||||
- "${ARGUS_PORT:-9093}:9093"
|
||||
volumes:
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../" && pwd)"
|
||||
project_root="$(cd "$root/../../.." && pwd)"
|
||||
|
||||
source "$project_root/scripts/common/build_user.sh"
|
||||
load_build_user
|
||||
|
||||
# 创建新的private目录结构 (基于argus目录结构)
|
||||
echo "[INFO] Creating private directory structure for supervisor-based containers..."
|
||||
mkdir -p "$root/private/argus/alert/alertmanager"
|
||||
mkdir -p "$root/private/argus/etc/"
|
||||
|
||||
# 设置数据目录权限
|
||||
echo "[INFO] Setting permissions for data directories..."
|
||||
chown -R "${ARGUS_BUILD_UID}:${ARGUS_BUILD_GID}" "$root/private/argus/alert/alertmanager" 2>/dev/null || true
|
||||
chown -R "${ARGUS_BUILD_UID}:${ARGUS_BUILD_GID}" "$root/private/argus/etc" 2>/dev/null || true
|
||||
|
||||
echo "[INFO] Supervisor-based containers will manage their own scripts and configurations"
|
||||
@@ -1,10 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
compose_cmd="docker compose"
|
||||
if ! $compose_cmd version >/dev/null 2>&1; then
|
||||
if command -v docker-compose >/dev/null 2>&1; then compose_cmd="docker-compose"; else
|
||||
echo "需要 Docker Compose,请安装后重试" >&2; exit 1; fi
|
||||
fi
|
||||
$compose_cmd -p alert-mvp up -d --remove-orphans
|
||||
echo "[OK] 服务已启动:Alertmanager http://localhost:9093"
|
||||
@@ -1,106 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
# ==========================================================
|
||||
# Alertmanager 测试脚本
|
||||
# ==========================================================
|
||||
|
||||
ALERTMANAGER_URL="http://localhost:9093"
|
||||
TEST_ALERT_NAME_CRITICAL="NodeDown"
|
||||
TEST_ALERT_NAME_WARNING="HighCPU"
|
||||
TMP_LOG="/tmp/test-alertmanager.log"
|
||||
|
||||
# 等待参数
|
||||
am_wait_attempts=30
|
||||
am_wait_interval=2
|
||||
|
||||
GREEN="\033[1;32m"
|
||||
RED="\033[1;31m"
|
||||
YELLOW="\033[1;33m"
|
||||
RESET="\033[0m"
|
||||
|
||||
# ==========================================================
|
||||
# 函数定义
|
||||
# ==========================================================
|
||||
|
||||
wait_for_alertmanager() {
|
||||
local attempt=1
|
||||
echo "[INFO] 等待 Alertmanager 启动中..."
|
||||
while (( attempt <= am_wait_attempts )); do
|
||||
if curl -fsS "${ALERTMANAGER_URL}/api/v2/status" >/dev/null 2>&1; then
|
||||
echo -e "${GREEN}[OK] Alertmanager 已就绪 (attempt=${attempt}/${am_wait_attempts})${RESET}"
|
||||
return 0
|
||||
fi
|
||||
echo "[..] Alertmanager 尚未就绪 (${attempt}/${am_wait_attempts})"
|
||||
sleep "${am_wait_interval}"
|
||||
(( attempt++ ))
|
||||
done
|
||||
echo -e "${RED}[ERROR] Alertmanager 在 ${am_wait_attempts} 次尝试后仍未就绪${RESET}"
|
||||
return 1
|
||||
}
|
||||
|
||||
log_step() {
|
||||
echo -e "${YELLOW}==== $1 ====${RESET}"
|
||||
}
|
||||
|
||||
# ==========================================================
|
||||
# 主流程
|
||||
# ==========================================================
|
||||
|
||||
log_step "测试 Alertmanager 开始"
|
||||
echo "[INFO] Alertmanager 地址: $ALERTMANAGER_URL"
|
||||
|
||||
# Step 1: 等待 Alertmanager 启动
|
||||
wait_for_alertmanager
|
||||
|
||||
# Step 2: 触发一个critical测试告警
|
||||
echo "[INFO] 发送critical测试告警..."
|
||||
curl -fsS -X POST "${ALERTMANAGER_URL}/api/v2/alerts" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '[
|
||||
{
|
||||
"labels": {
|
||||
"alertname": "'"${TEST_ALERT_NAME_CRITICAL}"'",
|
||||
"instance": "node-1",
|
||||
"severity": "critical"
|
||||
},
|
||||
"annotations": {
|
||||
"summary": "节点 node-1 宕机"
|
||||
}
|
||||
}
|
||||
]' \
|
||||
-o "$TMP_LOG"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo -e "${GREEN}[OK] 已成功发送critical测试告警${RESET}"
|
||||
else
|
||||
echo -e "${RED}[ERROR] critical告警发送失败!${RESET}"
|
||||
cat "$TMP_LOG"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Step 3: 触发一个warning测试告警
|
||||
echo "[INFO] 发送warning测试告警..."
|
||||
curl -fsS -X POST "${ALERTMANAGER_URL}/api/v2/alerts" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '[
|
||||
{
|
||||
"labels": {
|
||||
"alertname": "'"${TEST_ALERT_NAME_WARNING}"'",
|
||||
"instance": "node-1",
|
||||
"severity": "warning"
|
||||
},
|
||||
"annotations": {
|
||||
"summary": "节点 node-1 CPU 使用率过高"
|
||||
}
|
||||
}
|
||||
]' \
|
||||
-o "$TMP_LOG"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo -e "${GREEN}[OK] 已成功发送warning测试告警${RESET}"
|
||||
else
|
||||
echo -e "${RED}[ERROR] warning告警发送失败!${RESET}"
|
||||
cat "$TMP_LOG"
|
||||
exit 1
|
||||
fi
|
||||
@@ -1,71 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
# ==========================================================
|
||||
# Alertmanager 测试脚本(含启动等待)
|
||||
# ==========================================================
|
||||
|
||||
ALERTMANAGER_URL="http://localhost:9093"
|
||||
TEST_ALERT_NAME_CRITICAL="NodeDown"
|
||||
TEST_ALERT_NAME_WARNING="HighCPU"
|
||||
TMP_LOG="/tmp/test-alertmanager.log"
|
||||
|
||||
# 等待参数
|
||||
am_wait_attempts=30
|
||||
am_wait_interval=2
|
||||
|
||||
GREEN="\033[1;32m"
|
||||
RED="\033[1;31m"
|
||||
YELLOW="\033[1;33m"
|
||||
RESET="\033[0m"
|
||||
|
||||
# ==========================================================
|
||||
# 函数定义
|
||||
# ==========================================================
|
||||
|
||||
wait_for_alertmanager() {
|
||||
local attempt=1
|
||||
echo "[INFO] 等待 Alertmanager 启动中..."
|
||||
while (( attempt <= am_wait_attempts )); do
|
||||
if curl -fsS "${ALERTMANAGER_URL}/api/v2/status" >/dev/null 2>&1; then
|
||||
echo -e "${GREEN}[OK] Alertmanager 已就绪 (attempt=${attempt}/${am_wait_attempts})${RESET}"
|
||||
return 0
|
||||
fi
|
||||
echo "[..] Alertmanager 尚未就绪 (${attempt}/${am_wait_attempts})"
|
||||
sleep "${am_wait_interval}"
|
||||
(( attempt++ ))
|
||||
done
|
||||
echo -e "${RED}[ERROR] Alertmanager 在 ${am_wait_attempts} 次尝试后仍未就绪${RESET}"
|
||||
return 1
|
||||
}
|
||||
|
||||
log_step() {
|
||||
echo -e "${YELLOW}==== $1 ====${RESET}"
|
||||
}
|
||||
|
||||
# ==========================================================
|
||||
# 主流程
|
||||
# ==========================================================
|
||||
|
||||
log_step "查询 Alertmanager 当前告警列表开始"
|
||||
echo "[INFO] Alertmanager 地址: $ALERTMANAGER_URL"
|
||||
|
||||
# Step 1: 等待 Alertmanager 启动
|
||||
wait_for_alertmanager
|
||||
|
||||
# Step 2: 查询当前告警列表
|
||||
echo "[INFO] 查询当前告警..."
|
||||
sleep 1
|
||||
curl -fsS "${ALERTMANAGER_URL}/api/v2/alerts" | jq '.' || {
|
||||
echo -e "${RED}[WARN] 无法解析返回 JSON,请检查 jq 是否安装${RESET}"
|
||||
curl -s "${ALERTMANAGER_URL}/api/v2/alerts"
|
||||
}
|
||||
|
||||
# Step 3: 检查告警是否包含 NodeDown
|
||||
if curl -fsS "${ALERTMANAGER_URL}/api/v2/alerts" | grep -q "${TEST_ALERT_NAME_CRITICAL}"; then
|
||||
echo -e "${GREEN}✅ 测试通过:Alertmanager 已成功接收告警 ${TEST_ALERT_NAME_CRITICAL}${RESET}"
|
||||
else
|
||||
echo -e "${RED}❌ 测试失败:未检测到告警 ${TEST_ALERT_NAME_CRITICAL}${RESET}"
|
||||
fi
|
||||
|
||||
log_step "测试结束"
|
||||
@@ -1,21 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
compose_cmd="docker compose"
|
||||
if ! $compose_cmd version >/dev/null 2>&1; then
|
||||
if command -v docker-compose >/dev/null 2>&1; then compose_cmd="docker-compose"; else
|
||||
echo "需要 Docker Compose,请安装后重试" >&2; exit 1; fi
|
||||
fi
|
||||
$compose_cmd -p alert-mvp down
|
||||
echo "[OK] 已停止所有容器"
|
||||
|
||||
# 清理private目录内容
|
||||
echo "[INFO] 清理private目录内容..."
|
||||
cd "$(dirname "$0")/.."
|
||||
if [ -d "private" ]; then
|
||||
# 删除private目录及其所有内容
|
||||
rm -rf private
|
||||
echo "[OK] 已清理private目录"
|
||||
else
|
||||
echo "[INFO] private目录不存在,无需清理"
|
||||
fi
|
||||
@@ -1,105 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
echo "======================================="
|
||||
echo "ARGUS Alert System End-to-End Test"
|
||||
echo "======================================="
|
||||
echo ""
|
||||
|
||||
# 记录测试开始时间
|
||||
test_start_time=$(date +%s)
|
||||
|
||||
# 函数:等待服务就绪
|
||||
wait_for_services() {
|
||||
echo "[INFO] Waiting for all services to be ready..."
|
||||
local max_attempts=${SERVICE_WAIT_ATTEMPTS:-120}
|
||||
local attempt=1
|
||||
|
||||
while [ $attempt -le $max_attempts ]; do
|
||||
if curl -fs http://localhost:9093/api/v2/status >/dev/null 2>&1; then
|
||||
echo "[OK] All services are ready!"
|
||||
return 0
|
||||
fi
|
||||
echo " Waiting for services... ($attempt/$max_attempts)"
|
||||
sleep 5
|
||||
((attempt++))
|
||||
done
|
||||
|
||||
echo "[ERROR] Services not ready after $max_attempts attempts"
|
||||
return 1
|
||||
}
|
||||
|
||||
# 函数:显示测试步骤
|
||||
show_step() {
|
||||
echo ""
|
||||
echo "🔄 Step $1: $2"
|
||||
echo "----------------------------------------"
|
||||
}
|
||||
|
||||
# 函数:验证步骤结果
|
||||
verify_step() {
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "✅ $1 - SUCCESS"
|
||||
else
|
||||
echo "❌ $1 - FAILED"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 开始端到端测试
|
||||
show_step "1" "Bootstrap - Initialize environment"
|
||||
./scripts/01_bootstrap.sh
|
||||
verify_step "Bootstrap"
|
||||
|
||||
show_step "2" "Startup - Start all services"
|
||||
./scripts/02_up.sh
|
||||
verify_step "Service startup"
|
||||
|
||||
# 等待服务完全就绪
|
||||
wait_for_services || exit 1
|
||||
|
||||
# 发送告警数据
|
||||
show_step "3" "Add alerts - Send test alerts to Alertmanager"
|
||||
./scripts/03_alertmanager_add_alert.sh
|
||||
verify_step "Send test alerts"
|
||||
|
||||
# 查询告警数据
|
||||
show_step "4" "Verify data - Query Alertmanager"
|
||||
./scripts/04_query_alerts.sh
|
||||
verify_step "Data verification"
|
||||
|
||||
|
||||
# 检查服务健康状态
|
||||
show_step "Health" "Check service health"
|
||||
echo "[INFO] Checking service health..."
|
||||
|
||||
# 检查 Alertmanager 状态
|
||||
if curl -fs "http://localhost:9093/api/v2/status" >/dev/null 2>&1; then
|
||||
am_status="available"
|
||||
echo "✅ Alertmanager status: $am_status"
|
||||
else
|
||||
am_status="unavailable"
|
||||
echo "⚠️ Alertmanager status: $am_status"
|
||||
fi
|
||||
verify_step "Service health check"
|
||||
|
||||
# 清理环境
|
||||
show_step "5" "Cleanup - Stop all services"
|
||||
./scripts/05_down.sh
|
||||
verify_step "Service cleanup"
|
||||
|
||||
# 计算总测试时间
|
||||
test_end_time=$(date +%s)
|
||||
total_time=$((test_end_time - test_start_time))
|
||||
|
||||
echo ""
|
||||
echo "======================================="
|
||||
echo "🎉 END-TO-END TEST COMPLETED SUCCESSFULLY!"
|
||||
echo "======================================="
|
||||
echo "📊 Test Summary:"
|
||||
echo " • Total time: ${total_time}s"
|
||||
echo " • Alertmanager status: $am_status"
|
||||
echo " • All services started and stopped successfully"
|
||||
echo ""
|
||||
echo "✅ The ARGUS Alert system is working correctly!"
|
||||
echo ""
|
||||
113
src/alert/tests/scripts/verify_alertmanager.sh
Normal file
113
src/alert/tests/scripts/verify_alertmanager.sh
Normal file
@@ -0,0 +1,113 @@
|
||||
#!/bin/bash
|
||||
# verify_alertmanager.sh
|
||||
# 用于部署后验证 Prometheus 与 Alertmanager 通信链路是否正常
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
#=============================
|
||||
# 基础配置
|
||||
#=============================
|
||||
PROM_URL="${PROM_URL:-http://prom.metric.argus.com:9090}"
|
||||
ALERT_URL="${ALERT_URL:-http://alertmanager.alert.argus.com:9093}"
|
||||
# TODO: 根据实际部署环境调整规则目录
|
||||
DATA_ROOT="${DATA_ROOT:-/private/argus}"
|
||||
RULE_DIR = "$DATA_ROOT/metric/prometheus/rules"
|
||||
TMP_RULE="/tmp/test_rule.yml"
|
||||
|
||||
#=============================
|
||||
# 辅助函数
|
||||
#=============================
|
||||
GREEN="\033[32m"; RED="\033[31m"; YELLOW="\033[33m"; RESET="\033[0m"
|
||||
|
||||
log_info() { echo -e "${YELLOW}[INFO]${RESET} $1"; }
|
||||
log_success() { echo -e "${GREEN}[OK]${RESET} $1"; }
|
||||
log_error() { echo -e "${RED}[ERROR]${RESET} $1"; }
|
||||
|
||||
fail_exit() { log_error "$1"; exit 1; }
|
||||
|
||||
#=============================
|
||||
# Step 1: 检查 Alertmanager 是否可访问
|
||||
#=============================
|
||||
log_info "检查 Alertmanager 状态..."
|
||||
if curl -sSf "${ALERT_URL}/api/v2/status" >/dev/null 2>&1; then
|
||||
log_success "Alertmanager 服务正常 (${ALERT_URL})"
|
||||
else
|
||||
fail_exit "无法访问 Alertmanager,请检查端口映射与容器状态。"
|
||||
fi
|
||||
|
||||
#=============================
|
||||
# Step 2: 手动发送测试告警
|
||||
#=============================
|
||||
log_info "发送手动测试告警..."
|
||||
curl -s -XPOST "${ALERT_URL}/api/v2/alerts" -H "Content-Type: application/json" -d '[
|
||||
{
|
||||
"labels": {
|
||||
"alertname": "ManualTestAlert",
|
||||
"severity": "info"
|
||||
},
|
||||
"annotations": {
|
||||
"summary": "This is a test alert from deploy verification"
|
||||
},
|
||||
"startsAt": "'$(date -Iseconds)'"
|
||||
}
|
||||
]' >/dev/null && log_success "测试告警已成功发送到 Alertmanager"
|
||||
|
||||
#=============================
|
||||
# Step 3: 检查 Prometheus 配置中是否包含 Alertmanager
|
||||
#=============================
|
||||
log_info "检查 Prometheus 是否配置了 Alertmanager..."
|
||||
if curl -s "${PROM_URL}/api/v1/status/config" | grep -q "alertmanagers"; then
|
||||
log_success "Prometheus 已配置 Alertmanager 目标"
|
||||
else
|
||||
fail_exit "Prometheus 未配置 Alertmanager,请检查 prometheus.yml"
|
||||
fi
|
||||
|
||||
#=============================
|
||||
# Step 4: 创建并加载测试告警规则
|
||||
#=============================
|
||||
log_info "创建临时测试规则 ${TMP_RULE} ..."
|
||||
cat <<EOF > "${TMP_RULE}"
|
||||
groups:
|
||||
- name: deploy-verify-group
|
||||
rules:
|
||||
- alert: DeployVerifyAlert
|
||||
expr: vector(1)
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "Deployment verification alert"
|
||||
EOF
|
||||
|
||||
mkdir -p "${RULE_DIR}"
|
||||
cp "${TMP_RULE}" "${RULE_DIR}/test_rule.yml"
|
||||
|
||||
log_info "重载 Prometheus 以加载新规则..."
|
||||
if curl -s -X POST "${PROM_URL}/-/reload" >/dev/null; then
|
||||
log_success "Prometheus 已重载规则"
|
||||
else
|
||||
fail_exit "Prometheus reload 失败,请检查 API 可访问性。"
|
||||
fi
|
||||
|
||||
#=============================
|
||||
# Step 5: 等待并验证 Alertmanager 是否收到告警
|
||||
#=============================
|
||||
log_info "等待告警触发 (约5秒)..."
|
||||
sleep 5
|
||||
|
||||
if curl -s "${ALERT_URL}/api/v2/alerts" | grep -q "DeployVerifyAlert"; then
|
||||
log_success "Prometheus → Alertmanager 告警链路验证成功"
|
||||
else
|
||||
fail_exit "未在 Alertmanager 中检测到 DeployVerifyAlert,请检查网络或配置。"
|
||||
fi
|
||||
|
||||
#=============================
|
||||
# Step 6: 清理测试规则
|
||||
#=============================
|
||||
log_info "清理临时测试规则..."
|
||||
rm -f "${RULE_DIR}/test_rule.yml" "${TMP_RULE}"
|
||||
|
||||
curl -s -X POST "${PROM_URL}/-/reload" >/dev/null \
|
||||
&& log_success "Prometheus 已清理验证规则" \
|
||||
|| log_error "Prometheus reload 清理失败,请手动确认。"
|
||||
|
||||
log_success "部署验证全部通过!Prometheus ↔ Alertmanager 通信正常。"
|
||||
@@ -26,6 +26,7 @@ RUN apt-get update && \
|
||||
apt-get install -y \
|
||||
bind9 \
|
||||
bind9utils \
|
||||
dnsutils \
|
||||
bind9-doc \
|
||||
supervisor \
|
||||
net-tools \
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,47 +1,96 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
echo "[INFO] Starting Fluent Bit setup in Ubuntu container..."
|
||||
echo "[INFO] Starting Fluent Bit setup in Ubuntu container (offline-first)..."
|
||||
|
||||
# 安装必要的工具
|
||||
echo "[INFO] Installing required packages..."
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
apt-get update -qq
|
||||
apt-get install -y -qq curl
|
||||
|
||||
# 解压bundle到/tmp
|
||||
echo "[INFO] Extracting fluent-bit bundle..."
|
||||
cp -r /private/etc /tmp
|
||||
cp -r /private/packages /tmp
|
||||
cd /tmp
|
||||
# Stage bundle to /tmp (read-only mount under /private)
|
||||
echo "[INFO] Staging fluent-bit bundle..."
|
||||
rm -rf /tmp/flb && mkdir -p /tmp/flb
|
||||
cp -r /private/etc /tmp/flb/
|
||||
mkdir -p /tmp/flb/packages
|
||||
cp -r /private/packages/* /tmp/flb/packages/ 2>/dev/null || true
|
||||
|
||||
# 安装 Fluent Bit 从 deb 包
|
||||
echo "[INFO] Installing Fluent Bit from deb package..."
|
||||
dpkg -i /tmp/packages/fluent-bit_3.1.9_amd64.deb || true
|
||||
apt-get install -f -y -qq # 解决依赖问题
|
||||
# Helper: check and install a local deb if not already satisfied
|
||||
ensure_lib() {
|
||||
local soname="$1"; shift
|
||||
local pattern="$1"; shift
|
||||
if ldconfig -p 2>/dev/null | grep -q "$soname"; then
|
||||
echo "[OK] $soname already present"
|
||||
return 0
|
||||
fi
|
||||
local deb="$(ls /tmp/flb/packages/$pattern 2>/dev/null | head -n1 || true)"
|
||||
if [[ -n "$deb" ]]; then
|
||||
echo "[INFO] Installing local dependency: $(basename "$deb")"
|
||||
dpkg -i "$deb" >/dev/null 2>&1 || true
|
||||
else
|
||||
echo "[WARN] Local deb for $soname not found (pattern=$pattern)"
|
||||
fi
|
||||
if ! ldconfig -p 2>/dev/null | grep -q "$soname"; then
|
||||
echo "[WARN] $soname still missing after local install; attempting apt fallback"
|
||||
apt-get update -qq || true
|
||||
case "$soname" in
|
||||
libpq.so.5) apt-get install -y -qq libpq5 || true ;;
|
||||
libyaml-0.so.2) apt-get install -y -qq libyaml-0-2 || true ;;
|
||||
esac
|
||||
fi
|
||||
ldconfig 2>/dev/null || true
|
||||
}
|
||||
|
||||
# Offline-first: satisfy runtime deps from local debs, fallback to apt only if necessary
|
||||
ensure_lib "libpq.so.5" "libpq5_*_amd64.deb"
|
||||
ensure_lib "libyaml-0.so.2" "libyaml-0-2_*_amd64.deb"
|
||||
ensure_lib "libsasl2.so.2" "libsasl2-2_*_amd64.deb"
|
||||
ensure_lib "libldap-2.5.so.0" "libldap-2.5-0_*_amd64.deb"
|
||||
|
||||
# Install fluent-bit main package from local bundle
|
||||
FLB_DEB="$(ls /tmp/flb/packages/fluent-bit_*_amd64.deb 2>/dev/null | head -n1 || true)"
|
||||
if [[ -z "$FLB_DEB" ]]; then
|
||||
echo "[ERROR] fluent-bit deb not found under /private/packages" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "[INFO] Installing Fluent Bit: $(basename "$FLB_DEB")"
|
||||
dpkg -i "$FLB_DEB" >/dev/null 2>&1 || true
|
||||
|
||||
# If dpkg reported unresolved dependencies, try apt -f only as last resort
|
||||
if ! command -v /opt/fluent-bit/bin/fluent-bit >/dev/null 2>&1; then
|
||||
echo "[WARN] fluent-bit binary missing after dpkg; attempting apt --fix-broken"
|
||||
apt-get install -f -y -qq || true
|
||||
fi
|
||||
|
||||
# Ensure runtime library dependencies are satisfied (libsasl2, libldap are required via libpq/curl)
|
||||
MISSING=$(ldd /opt/fluent-bit/bin/fluent-bit 2>/dev/null | awk '/not found/{print $1}' | xargs -r echo || true)
|
||||
if [[ -n "$MISSING" ]]; then
|
||||
echo "[WARN] missing shared libs: $MISSING"
|
||||
apt-get update -qq || true
|
||||
apt-get install -y -qq libsasl2-2 libldap-2.5-0 || true
|
||||
apt-get install -f -y -qq || true
|
||||
fi
|
||||
|
||||
# 验证 Fluent Bit 可以运行
|
||||
echo "[INFO] Fluent Bit version:"
|
||||
/opt/fluent-bit/bin/fluent-bit --version
|
||||
/opt/fluent-bit/bin/fluent-bit --version || { echo "[ERROR] fluent-bit not installed or libraries missing" >&2; exit 1; }
|
||||
|
||||
# 创建配置目录
|
||||
# Place configuration
|
||||
mkdir -p /etc/fluent-bit
|
||||
cp -r /tmp/etc/* /etc/fluent-bit/
|
||||
cp -r /tmp/flb/etc/* /etc/fluent-bit/
|
||||
|
||||
# 创建日志和缓冲区目录
|
||||
# Create logs/buffers dirs
|
||||
mkdir -p /logs/train /logs/infer /buffers
|
||||
chmod 755 /logs/train /logs/infer /buffers
|
||||
|
||||
# 等待 Elasticsearch 就绪
|
||||
echo "[INFO] Waiting for Elasticsearch to be ready..."
|
||||
while ! curl -fs http://${ES_HOST}:${ES_PORT}/_cluster/health >/dev/null 2>&1; do
|
||||
echo " Waiting for ES at ${ES_HOST}:${ES_PORT}..."
|
||||
sleep 5
|
||||
# Wait for Elasticsearch via bash /dev/tcp to avoid curl dependency
|
||||
echo "[INFO] Waiting for Elasticsearch to be ready (tcp ${ES_HOST}:${ES_PORT})..."
|
||||
for i in $(seq 1 120); do
|
||||
if exec 3<>/dev/tcp/${ES_HOST}/${ES_PORT}; then
|
||||
exec 3<&- 3>&-
|
||||
echo "[INFO] Elasticsearch is ready"
|
||||
break
|
||||
fi
|
||||
[[ $i -eq 120 ]] && { echo "[ERROR] ES not reachable" >&2; exit 1; }
|
||||
sleep 1
|
||||
done
|
||||
echo "[INFO] Elasticsearch is ready"
|
||||
|
||||
# 启动 Fluent Bit
|
||||
echo "[INFO] Starting Fluent Bit with configuration from /etc/fluent-bit/"
|
||||
echo "[INFO] Command: /opt/fluent-bit/bin/fluent-bit --config=/etc/fluent-bit/fluent-bit.conf"
|
||||
exec /opt/fluent-bit/bin/fluent-bit \
|
||||
--config=/etc/fluent-bit/fluent-bit.conf
|
||||
exec /opt/fluent-bit/bin/fluent-bit --config=/etc/fluent-bit/fluent-bit.conf
|
||||
|
||||
@@ -32,3 +32,42 @@ fi
|
||||
|
||||
echo "[OK] 初始化完成: private/argus/log/{elasticsearch,kibana}"
|
||||
echo "[INFO] Fluent-bit files should be in fluent-bit/ directory"
|
||||
|
||||
# 准备 Fluent Bit 离线依赖(从 metric all-in-one-full 复制 deb 到 ../fluent-bit/build/packages)
|
||||
FLB_BUILD_PACKAGES_DIR="$root/../fluent-bit/build/packages"
|
||||
mkdir -p "$FLB_BUILD_PACKAGES_DIR"
|
||||
for deb in \
|
||||
"$project_root/src/metric/client-plugins/all-in-one-full/plugins/fluent-bit/bin/libyaml-0-2_"*_amd64.deb \
|
||||
"$project_root/src/metric/client-plugins/all-in-one-full/plugins/fluent-bit/bin/libpq5_"*_amd64.deb ; do
|
||||
if ls $deb >/dev/null 2>&1; then
|
||||
for f in $deb; do
|
||||
base="$(basename "$f")"
|
||||
if [[ ! -f "$FLB_BUILD_PACKAGES_DIR/$base" ]]; then
|
||||
cp "$f" "$FLB_BUILD_PACKAGES_DIR/"
|
||||
echo " [+] copied $base"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
done
|
||||
|
||||
# 额外:从 all-in-one-full 的 ubuntu22/curl.tar.gz 解包必要依赖(libsasl2/ldap),便于离线安装
|
||||
CURLOPT_TAR="$project_root/src/metric/client-plugins/all-in-one-full/deps/ubuntu22/curl.tar.gz"
|
||||
if [[ -f "$CURLOPT_TAR" ]]; then
|
||||
tmpdir=$(mktemp -d)
|
||||
if tar -xzf "$CURLOPT_TAR" -C "$tmpdir" 2>/dev/null; then
|
||||
for p in \
|
||||
libsasl2-2_*_amd64.deb \
|
||||
libsasl2-modules-db_*_amd64.deb \
|
||||
libldap-2.5-0_*_amd64.deb \
|
||||
libidn2-0_*_amd64.deb \
|
||||
libbrotli1_*_amd64.deb \
|
||||
libssl3_*_amd64.deb ; do
|
||||
src=$(ls "$tmpdir"/curl/$p 2>/dev/null | head -n1 || true)
|
||||
if [[ -n "$src" ]]; then
|
||||
base="$(basename "$src")"
|
||||
[[ -f "$FLB_BUILD_PACKAGES_DIR/$base" ]] || cp "$src" "$FLB_BUILD_PACKAGES_DIR/" && echo " [+] staged $base"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
rm -rf "$tmpdir"
|
||||
fi
|
||||
|
||||
2
src/metric/.gitignore
vendored
2
src/metric/.gitignore
vendored
@@ -4,4 +4,4 @@
|
||||
/client-plugins/demo-all-in-one/publish/
|
||||
/client-plugins/demo-all-in-one/checklist
|
||||
/client-plugins/demo-all-in-one/VERSION
|
||||
/client-plugins/all-in-one-full/
|
||||
/client-plugins/all-in-one-full/artifact/
|
||||
|
||||
@@ -104,7 +104,26 @@ log_info "文件所有者: $OWNER"
|
||||
|
||||
# 确保发布目录存在
|
||||
log_info "确保发布目录存在: $PUBLISH_DIR"
|
||||
sudo mkdir -p "$PUBLISH_DIR"
|
||||
mkdir -p "$PUBLISH_DIR"
|
||||
|
||||
IFS=':' read -r OWNER_UID OWNER_GID <<< "$OWNER"
|
||||
if [[ -z "$OWNER_UID" || -z "$OWNER_GID" ]]; then
|
||||
log_error "--owner 格式不正确,应为 uid:gid"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
CURRENT_UID=$(id -u)
|
||||
CURRENT_GID=$(id -g)
|
||||
if [[ "$OWNER_UID" != "$CURRENT_UID" || "$OWNER_GID" != "$CURRENT_GID" ]]; then
|
||||
if [[ "$CURRENT_UID" -ne 0 ]]; then
|
||||
log_error "当前用户 (${CURRENT_UID}:${CURRENT_GID}) 无法设置所有者为 ${OWNER_UID}:${OWNER_GID}"
|
||||
log_error "请以目标用户运行脚本或预先调整目录权限"
|
||||
exit 1
|
||||
fi
|
||||
NEED_CHOWN=true
|
||||
else
|
||||
NEED_CHOWN=false
|
||||
fi
|
||||
|
||||
# 创建临时目录用于打包
|
||||
TEMP_PACKAGE_DIR="/tmp/argus-metric-package-$$"
|
||||
@@ -208,26 +227,31 @@ fi
|
||||
TAR_NAME="argus-metric_$(echo $VERSION | tr '.' '_').tar.gz"
|
||||
log_info "创建发布包: $TAR_NAME"
|
||||
cd "$TEMP_PACKAGE_DIR"
|
||||
sudo tar -czf "$PUBLISH_DIR/$TAR_NAME" *
|
||||
tar -czf "$PUBLISH_DIR/$TAR_NAME" *
|
||||
cd - > /dev/null
|
||||
|
||||
# 设置文件所有者
|
||||
log_info "设置文件所有者为: $OWNER"
|
||||
sudo chown "$OWNER" "$PUBLISH_DIR/$TAR_NAME"
|
||||
if [[ "$NEED_CHOWN" == true ]]; then
|
||||
log_info "设置文件所有者为: $OWNER"
|
||||
chown "$OWNER" "$PUBLISH_DIR/$TAR_NAME"
|
||||
fi
|
||||
|
||||
# 清理临时目录
|
||||
rm -rf "$TEMP_PACKAGE_DIR"
|
||||
|
||||
# 更新 LATEST_VERSION 文件
|
||||
log_info "更新 LATEST_VERSION 文件..."
|
||||
echo "$VERSION" | sudo tee "$PUBLISH_DIR/LATEST_VERSION" > /dev/null
|
||||
sudo chown "$OWNER" "$PUBLISH_DIR/LATEST_VERSION"
|
||||
echo "$VERSION" > "$PUBLISH_DIR/LATEST_VERSION"
|
||||
if [[ "$NEED_CHOWN" == true ]]; then
|
||||
chown "$OWNER" "$PUBLISH_DIR/LATEST_VERSION"
|
||||
fi
|
||||
|
||||
# 复制 DNS 配置文件到发布目录根目录(直接从 config 目录复制)
|
||||
if [[ -f "config/dns.conf" ]]; then
|
||||
log_info "复制 DNS 配置文件到发布目录根目录..."
|
||||
sudo cp "config/dns.conf" "$PUBLISH_DIR/"
|
||||
sudo chown "$OWNER" "$PUBLISH_DIR/dns.conf"
|
||||
cp "config/dns.conf" "$PUBLISH_DIR/"
|
||||
if [[ "$NEED_CHOWN" == true ]]; then
|
||||
chown "$OWNER" "$PUBLISH_DIR/dns.conf"
|
||||
fi
|
||||
log_success "DNS 配置文件复制完成: $PUBLISH_DIR/dns.conf"
|
||||
else
|
||||
log_warning "未找到 config/dns.conf 文件,跳过 DNS 配置文件复制"
|
||||
@@ -236,8 +260,10 @@ fi
|
||||
# 复制 setup.sh 到发布目录
|
||||
if [[ -f "scripts/setup.sh" ]]; then
|
||||
log_info "复制 setup.sh 到发布目录..."
|
||||
sudo cp "scripts/setup.sh" "$PUBLISH_DIR/"
|
||||
sudo chown "$OWNER" "$PUBLISH_DIR/setup.sh"
|
||||
cp "scripts/setup.sh" "$PUBLISH_DIR/"
|
||||
if [[ "$NEED_CHOWN" == true ]]; then
|
||||
chown "$OWNER" "$PUBLISH_DIR/setup.sh"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 显示发布结果
|
||||
|
||||
59
src/metric/client-plugins/all-in-one-full/README.md
Normal file
59
src/metric/client-plugins/all-in-one-full/README.md
Normal file
@@ -0,0 +1,59 @@
|
||||
# 客户侧组件安装包构建、发布流程
|
||||
|
||||
## 第一步:配置版本和组件
|
||||
|
||||
首先搞定配置文件:
|
||||
|
||||
1. 把 `.checklist.example` 重命名成 `checklist`
|
||||
2. 把 `.VERSION.example` 重命名成 `VERSION`
|
||||
|
||||
### checklist 文件格式
|
||||
```
|
||||
# 组件名称 目录路径 版本号 [依赖组件] [安装顺序]
|
||||
dcgm-exporter-installer /path/to/dcgm-exporter-installer 1.1.0
|
||||
node-exporter-installer /path/to/node-exporter-installer 1.1.0
|
||||
```
|
||||
|
||||
### VERSION 文件
|
||||
设置需要发布的版本号,比如 `1.29.0`
|
||||
|
||||
> 建议用 `version-manager.sh` 来管理版本
|
||||
|
||||
## 第二步:构建安装包
|
||||
|
||||
直接跑脚本:
|
||||
```bash
|
||||
./package_artifact.sh
|
||||
```
|
||||
|
||||
构建完的东西会放在 `artifact/` 目录下,按版本分文件夹。
|
||||
|
||||
如果版本已经存在了,想要覆盖重新构建:
|
||||
```bash
|
||||
./package_artifact.sh --force
|
||||
```
|
||||
|
||||
构建完可以手工测试安装包。
|
||||
|
||||
## 第三步:发布安装包
|
||||
|
||||
用这个脚本发布:
|
||||
```bash
|
||||
./publish_artifact.sh
|
||||
```
|
||||
|
||||
发布后的内容在 `publish/` 目录里,包含:
|
||||
- 压缩版本的安装包
|
||||
- 一键安装的bash脚本
|
||||
|
||||
## 第四步:部署到FTP服务器
|
||||
|
||||
把发布的内容上传到FTP服务器,客户端就可以通过一键命令安装:
|
||||
|
||||
```bash
|
||||
curl -fsSL http://your-ftp-server/install.sh | sh -
|
||||
|
||||
curl -fsSL "ftp://ftpuser:{PASSWD}!@10.211.55.4/share/setup.sh" | sudo bash -s -- --server 10.211.55.4 --user ftpuser --password {PASSWD}
|
||||
```
|
||||
|
||||
这样客户就能直接从FTP服务器下载并安装组件了。
|
||||
@@ -0,0 +1 @@
|
||||
1.29.0
|
||||
@@ -0,0 +1,3 @@
|
||||
# 组件名称 目录路径 版本号 [依赖组件] [安装顺序]
|
||||
dcgm-exporter-installer /Users/sundapeng/Project/nlp/aiops/client-plugins/dcgm-exporter-installer 1.1.0
|
||||
node-exporter-installer /Users/sundapeng/Project/nlp/aiops/client-plugins/node-exporter-installer 1.1.0
|
||||
1
src/metric/client-plugins/all-in-one-full/config/VERSION
Normal file
1
src/metric/client-plugins/all-in-one-full/config/VERSION
Normal file
@@ -0,0 +1 @@
|
||||
1.40.0
|
||||
@@ -0,0 +1,5 @@
|
||||
# 组件名称 目录路径 版本号 [依赖组件] [安装顺序]
|
||||
argus-agent plugins/argus-agent 1.0.0
|
||||
node-exporter plugins/node-exporter 1.0.0
|
||||
dcgm-exporter plugins/dcgm-exporter 1.0.0
|
||||
fluent-bit plugins/fluent-bit 1.0.0
|
||||
14
src/metric/client-plugins/all-in-one-full/config/config.env
Normal file
14
src/metric/client-plugins/all-in-one-full/config/config.env
Normal file
@@ -0,0 +1,14 @@
|
||||
# Elasticsearch
|
||||
ES_HOST=es.log.argus.com
|
||||
ES_PORT=9200
|
||||
|
||||
# Argus-Agent
|
||||
# 连接master服务
|
||||
MASTER_ENDPOINT=master.argus.com:3000
|
||||
# 上报状态间隔描述
|
||||
REPORT_INTERVAL_SECONDS=5
|
||||
|
||||
# FTP
|
||||
FTP_SERVER=172.31.0.40
|
||||
FTP_USER=ftpuser
|
||||
FTP_PASSWORD=ZGClab1234!
|
||||
@@ -0,0 +1,8 @@
|
||||
# Argus Metric 配置文件示例
|
||||
# 复制此文件为 config.env 并根据需要修改配置
|
||||
|
||||
# 连接master服务
|
||||
MASTER_ENDPOINT=master.argus.com:3000
|
||||
|
||||
# 上报状态间隔描述(秒)
|
||||
REPORT_INTERVAL_SECONDS=60
|
||||
@@ -0,0 +1 @@
|
||||
172.31.0.2
|
||||
Binary file not shown.
BIN
src/metric/client-plugins/all-in-one-full/deps/jq-curl.tar.gz
Normal file
BIN
src/metric/client-plugins/all-in-one-full/deps/jq-curl.tar.gz
Normal file
Binary file not shown.
BIN
src/metric/client-plugins/all-in-one-full/deps/ubuntu20/cron.tar.gz
Executable file
BIN
src/metric/client-plugins/all-in-one-full/deps/ubuntu20/cron.tar.gz
Executable file
Binary file not shown.
BIN
src/metric/client-plugins/all-in-one-full/deps/ubuntu20/curl.tar.gz
Executable file
BIN
src/metric/client-plugins/all-in-one-full/deps/ubuntu20/curl.tar.gz
Executable file
Binary file not shown.
BIN
src/metric/client-plugins/all-in-one-full/deps/ubuntu20/jq.tar.gz
Executable file
BIN
src/metric/client-plugins/all-in-one-full/deps/ubuntu20/jq.tar.gz
Executable file
Binary file not shown.
BIN
src/metric/client-plugins/all-in-one-full/deps/ubuntu22/cron.tar.gz
Executable file
BIN
src/metric/client-plugins/all-in-one-full/deps/ubuntu22/cron.tar.gz
Executable file
Binary file not shown.
BIN
src/metric/client-plugins/all-in-one-full/deps/ubuntu22/curl.tar.gz
Executable file
BIN
src/metric/client-plugins/all-in-one-full/deps/ubuntu22/curl.tar.gz
Executable file
Binary file not shown.
BIN
src/metric/client-plugins/all-in-one-full/deps/ubuntu22/jq.tar.gz
Executable file
BIN
src/metric/client-plugins/all-in-one-full/deps/ubuntu22/jq.tar.gz
Executable file
Binary file not shown.
@@ -0,0 +1,94 @@
|
||||
# Argus Agent 插件
|
||||
|
||||
这是 Argus Agent 的安装和管理插件,提供了完整的安装、卸载、健康检查功能。
|
||||
|
||||
## 文件结构
|
||||
|
||||
```
|
||||
argus-agent/
|
||||
├── bin/
|
||||
│ └── argus-agent # Argus Agent 二进制文件
|
||||
├── config/ # 配置文件目录
|
||||
├── install.sh # 安装脚本
|
||||
├── uninstall.sh # 卸载脚本
|
||||
├── check_health.sh # 健康检查脚本
|
||||
├── package.sh # 打包脚本
|
||||
└── README.md # 说明文档
|
||||
```
|
||||
|
||||
## 使用方法
|
||||
|
||||
### 安装
|
||||
|
||||
```bash
|
||||
sudo ./install.sh
|
||||
```
|
||||
|
||||
安装脚本会:
|
||||
- 检查系统要求
|
||||
- 停止可能运行的服务
|
||||
- 安装二进制文件到 `/usr/local/bin/argus-agent`
|
||||
- 创建 `argus-agent` 用户
|
||||
- 创建配置和数据目录
|
||||
- 启动服务并记录 PID
|
||||
|
||||
### 卸载
|
||||
|
||||
```bash
|
||||
sudo ./uninstall.sh
|
||||
```
|
||||
|
||||
卸载脚本会:
|
||||
- 停止所有 argus-agent 进程
|
||||
- 删除二进制文件
|
||||
- 删除配置和数据目录
|
||||
- 清理日志文件
|
||||
- 更新安装记录
|
||||
|
||||
### 健康检查
|
||||
|
||||
```bash
|
||||
./check_health.sh
|
||||
```
|
||||
|
||||
健康检查脚本会:
|
||||
- 检查安装记录中的 PID
|
||||
- 验证进程是否正在运行
|
||||
- 输出 JSON 格式的健康状态
|
||||
|
||||
### 打包
|
||||
|
||||
```bash
|
||||
./package.sh
|
||||
```
|
||||
|
||||
打包脚本会:
|
||||
- 检查所有必要文件
|
||||
- 创建时间戳命名的压缩包
|
||||
- 输出安装包信息
|
||||
|
||||
## 安装后的文件位置
|
||||
|
||||
- 二进制文件: `/usr/local/bin/argus-agent`
|
||||
- 配置目录: `/etc/argus-agent/`
|
||||
- 数据目录: `/var/lib/argus-agent/`
|
||||
- 日志文件: `/var/log/argus-agent.log`
|
||||
- PID 文件: `/var/run/argus-agent.pid`
|
||||
- 安装记录: `/opt/argus-metric/current/.install_record`
|
||||
|
||||
## 健康检查输出格式
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "argus-agent",
|
||||
"status": "health|unhealth",
|
||||
"reason": "状态说明"
|
||||
}
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 安装和卸载脚本需要 root 权限
|
||||
2. 健康检查脚本使用安装记录中的 PID 来验证进程状态
|
||||
3. 如果 jq 命令不可用,健康检查会使用简单的文本解析
|
||||
4. 卸载时会保留 `argus-agent` 用户,避免影响其他服务
|
||||
BIN
src/metric/client-plugins/all-in-one-full/plugins/argus-agent/bin/argus-agent
LFS
Executable file
BIN
src/metric/client-plugins/all-in-one-full/plugins/argus-agent/bin/argus-agent
LFS
Executable file
Binary file not shown.
@@ -0,0 +1,69 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Argus Agent 健康检查脚本
|
||||
# 输出 JSON 格式结果
|
||||
|
||||
set -e
|
||||
|
||||
# 检查 Argus Agent 健康状态
|
||||
check_health() {
|
||||
local name="argus-agent"
|
||||
local status="unhealth"
|
||||
local reason=""
|
||||
local install_record="/opt/argus-metric/current/.install_record"
|
||||
|
||||
# 首先尝试通过安装记录文件检查进程
|
||||
if [[ -f "$install_record" ]]; then
|
||||
# 尝试使用jq解析JSON格式的安装记录文件
|
||||
local pid=""
|
||||
if command -v jq &> /dev/null; then
|
||||
pid=$(jq -r '.components."argus-agent".pid // empty' "$install_record" 2>/dev/null || echo "")
|
||||
else
|
||||
# 如果没有jq,使用简单的文本解析方法
|
||||
pid=$(grep -A 10 '"argus-agent"' "$install_record" | grep '"pid"' | cut -d'"' -f4 | head -1)
|
||||
fi
|
||||
|
||||
if [[ -n "$pid" && "$pid" =~ ^[0-9]+$ ]]; then
|
||||
if kill -0 "$pid" 2>/dev/null; then
|
||||
# 进程存在且运行正常
|
||||
status="health"
|
||||
reason="进程运行正常 (PID: $pid)"
|
||||
echo "{\"name\": \"$name\", \"status\": \"$status\", \"reason\": \"$reason\"}"
|
||||
exit 0
|
||||
else
|
||||
reason="安装记录中的 PID $pid 进程不存在"
|
||||
echo "{\"name\": \"$name\", \"status\": \"$status\", \"reason\": \"$reason\"}"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
reason="安装记录文件中未找到有效的 argus-agent PID"
|
||||
echo "{\"name\": \"$name\", \"status\": \"$status\", \"reason\": \"$reason\"}"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
# 如果安装记录文件不存在,尝试查找 argus-agent 进程
|
||||
local pids=$(pgrep -f "argus-agent" 2>/dev/null || true)
|
||||
if [[ -n "$pids" ]]; then
|
||||
# 取第一个找到的 PID
|
||||
local pid=$(echo "$pids" | head -1)
|
||||
status="health"
|
||||
reason="发现 argus-agent 进程运行 (PID: $pid),但未找到安装记录"
|
||||
echo "{\"name\": \"$name\", \"status\": \"$status\", \"reason\": \"$reason\"}"
|
||||
exit 0
|
||||
else
|
||||
reason="未找到 argus-agent 进程,且安装记录文件不存在"
|
||||
echo "{\"name\": \"$name\", \"status\": \"$status\", \"reason\": \"$reason\"}"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
check_health
|
||||
}
|
||||
|
||||
# 脚本入口
|
||||
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||
main "$@"
|
||||
fi
|
||||
289
src/metric/client-plugins/all-in-one-full/plugins/argus-agent/install.sh
Executable file
289
src/metric/client-plugins/all-in-one-full/plugins/argus-agent/install.sh
Executable file
@@ -0,0 +1,289 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
# 颜色定义
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# 日志函数
|
||||
log_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# 显示帮助信息
|
||||
show_help() {
|
||||
echo "Argus Agent 安装脚本"
|
||||
echo
|
||||
echo "用法: $0 [选项]"
|
||||
echo
|
||||
echo "选项:"
|
||||
echo " --help 显示此帮助信息"
|
||||
echo
|
||||
echo "示例:"
|
||||
echo " $0 # 安装 Argus Agent"
|
||||
echo
|
||||
}
|
||||
|
||||
# 解析命令行参数
|
||||
INSTALL_DIR=""
|
||||
for arg in "$@"; do
|
||||
case $arg in
|
||||
--help|-h)
|
||||
show_help
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
# 如果参数不是以--开头,则认为是安装目录
|
||||
if [[ ! "$arg" =~ ^-- ]]; then
|
||||
INSTALL_DIR="$arg"
|
||||
else
|
||||
log_error "未知参数: $arg"
|
||||
show_help
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# 检查是否为 root 用户
|
||||
check_root() {
|
||||
if [[ $EUID -ne 0 ]]; then
|
||||
log_error "此脚本需要 root 权限运行"
|
||||
log_info "请使用: sudo $0"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 检查系统要求
|
||||
check_system() {
|
||||
log_info "检查系统要求..."
|
||||
|
||||
# 检查操作系统
|
||||
if [[ ! -f /etc/os-release ]]; then
|
||||
log_error "无法检测操作系统版本"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
source /etc/os-release
|
||||
log_info "检测到操作系统: $NAME $VERSION"
|
||||
|
||||
# 检查是否为 Linux 系统
|
||||
if [[ "$ID" != "ubuntu" && "$ID" != "debian" && "$ID" != "centos" && "$ID" != "rhel" && "$ID" != "fedora" ]]; then
|
||||
log_warning "此脚本主要针对常见 Linux 发行版,其他系统可能需要调整"
|
||||
fi
|
||||
|
||||
# 检查系统架构
|
||||
local arch=$(uname -m)
|
||||
log_info "系统架构: $arch"
|
||||
|
||||
if [[ "$arch" != "x86_64" && "$arch" != "amd64" ]]; then
|
||||
log_warning "当前架构为 $arch,argus-agent 主要支持 x86_64/amd64"
|
||||
fi
|
||||
}
|
||||
|
||||
# 停止可能运行的服务
|
||||
stop_existing_service() {
|
||||
log_info "检查并停止可能运行的服务..."
|
||||
local pid_file="/var/run/argus-agent.pid"
|
||||
|
||||
if [[ -f "$pid_file" ]]; then
|
||||
local pid=$(cat "$pid_file")
|
||||
if ps -p "$pid" -o comm= | grep -q "^argus-agent$"; then
|
||||
kill "$pid" 2>/dev/null || true
|
||||
sleep 2
|
||||
kill -9 "$pid" 2>/dev/null || true
|
||||
log_success "服务已停止"
|
||||
fi
|
||||
rm -f "$pid_file"
|
||||
fi
|
||||
|
||||
local pids=$(pgrep -x argus-agent 2>/dev/null || true)
|
||||
if [[ -n "$pids" ]]; then
|
||||
for pid in $pids; do kill -9 "$pid" 2>/dev/null || true; done
|
||||
fi
|
||||
|
||||
# 检查僵尸进程
|
||||
local zombies=$(ps -eo pid,stat,comm | grep '[a]rgus-agent' | awk '$2 ~ /Z/ {print $1}')
|
||||
if [[ -n "$zombies" ]]; then
|
||||
for pid in $zombies; do
|
||||
local ppid=$(ps -o ppid= -p $pid)
|
||||
log_warning "检测到僵尸 argus-agent (PID=$pid, PPID=$ppid),尝试清理"
|
||||
[[ "$ppid" -ne 1 ]] && kill -9 "$ppid" 2>/dev/null || true
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
# 安装 Argus Agent 二进制文件
|
||||
install_argus_agent() {
|
||||
log_info "安装 Argus Agent..."
|
||||
local binary_file="bin/argus-agent"
|
||||
local install_dir="/usr/local/bin"
|
||||
local target_file="$install_dir/argus-agent"
|
||||
|
||||
[[ ! -f "$binary_file" ]] && log_error "找不到 Argus Agent 二进制文件: $binary_file" && exit 1
|
||||
|
||||
stop_existing_service
|
||||
|
||||
local timeout=10
|
||||
while [[ $timeout -gt 0 ]]; do
|
||||
remaining_pids=$(pgrep -x argus-agent | grep -vw $$ || true)
|
||||
[[ -z "$remaining_pids" ]] && break
|
||||
if ps -eo pid,stat,comm | grep -E 'argus-agent' | grep -q 'Z'; then
|
||||
log_warning "检测到僵尸 argus-agent,跳过等待"
|
||||
break
|
||||
fi
|
||||
log_warning "等待 argus-agent 完全退出... ($timeout)"
|
||||
sleep 1
|
||||
((timeout--))
|
||||
done
|
||||
|
||||
cp "$binary_file" "${target_file}.new"
|
||||
chmod +x "${target_file}.new"
|
||||
mv -f "${target_file}.new" "$target_file"
|
||||
log_success "Argus Agent 二进制文件安装完成"
|
||||
}
|
||||
|
||||
|
||||
# 创建用户和组
|
||||
create_user() {
|
||||
log_info "创建 argus-agent 用户..."
|
||||
|
||||
# 检查用户是否已存在
|
||||
if id "argus-agent" &>/dev/null; then
|
||||
log_info "用户 argus-agent 已存在"
|
||||
else
|
||||
useradd --no-create-home --shell /bin/false argus-agent
|
||||
log_success "用户 argus-agent 创建完成"
|
||||
fi
|
||||
}
|
||||
|
||||
# 安装配置文件
|
||||
install_config() {
|
||||
log_info "安装配置文件..."
|
||||
|
||||
local config_dir="/etc/argus-agent"
|
||||
|
||||
# 创建配置目录
|
||||
mkdir -p "$config_dir"
|
||||
|
||||
# 创建健康检查目录
|
||||
mkdir -p "/var/lib/argus-agent/health"
|
||||
chown argus-agent:argus-agent "/var/lib/argus-agent/health"
|
||||
}
|
||||
|
||||
# 启动 Argus Agent 服务
|
||||
start_argus_agent() {
|
||||
log_info "启动 Argus Agent 服务..."
|
||||
local binary_path="/usr/local/bin/argus-agent"
|
||||
local log_file="/var/log/argus-agent.log"
|
||||
local pid_file="/var/run/argus-agent.pid"
|
||||
|
||||
[[ -f "$pid_file" ]] && rm -f "$pid_file"
|
||||
|
||||
log_info "正在启动 Argus Agent..."
|
||||
setsid "$binary_path" > "$log_file" 2>&1 < /dev/null &
|
||||
local pid=$!
|
||||
echo "$pid" > "$pid_file"
|
||||
sleep 2
|
||||
|
||||
if kill -0 "$pid" 2>/dev/null; then
|
||||
log_success "Argus Agent 服务启动成功 (PID: $pid)"
|
||||
else
|
||||
log_error "Argus Agent 启动失败"
|
||||
[[ -f "$log_file" ]] && tail -n 10 "$log_file"
|
||||
rm -f "$pid_file"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
# 更新安装记录
|
||||
update_install_record() {
|
||||
local pid="$1"
|
||||
# 使用传入的安装目录参数,如果没有则使用默认值
|
||||
local install_base_dir="${2:-/opt/argus-metric/current}"
|
||||
local install_record="$install_base_dir/.install_record"
|
||||
|
||||
# 如果安装记录文件不存在,说明是首次安装,由主安装脚本统一创建
|
||||
if [[ ! -f "$install_record" ]]; then
|
||||
log_info "安装记录文件不存在,将由主安装脚本创建"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# 如果文件存在,说明是重启场景,只更新 PID 字段
|
||||
if command -v jq &> /dev/null; then
|
||||
# 读取当前 PID
|
||||
local current_pid=$(jq -r '.components."argus-agent".pid // ""' "$install_record" 2>/dev/null)
|
||||
|
||||
if [[ -z "$current_pid" ]]; then
|
||||
log_warning "无法读取当前 PID,跳过更新"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 使用 jq 只更新 pid 字段,保持字符串类型,保留其他字段
|
||||
jq --arg new_pid "$pid" '.components."argus-agent".pid = $new_pid' "$install_record" > "$install_record.tmp" && mv "$install_record.tmp" "$install_record"
|
||||
log_info "PID 已更新: $current_pid -> $pid"
|
||||
else
|
||||
log_warning "jq 命令不可用,无法更新安装记录文件"
|
||||
fi
|
||||
}
|
||||
|
||||
# 显示安装信息
|
||||
show_install_info() {
|
||||
log_success "Argus Agent 安装完成!"
|
||||
echo
|
||||
echo "安装信息:"
|
||||
echo " 二进制文件: /usr/local/bin/argus-agent"
|
||||
echo " 运行用户: argus-agent"
|
||||
echo " 配置目录: /etc/argus-agent/"
|
||||
echo " 健康检查目录: /var/lib/argus-agent/health"
|
||||
echo
|
||||
echo "使用方法:"
|
||||
echo " 手动启动: /usr/local/bin/argus-agent"
|
||||
echo " 后台启动: nohup /usr/local/bin/argus-agent &"
|
||||
echo
|
||||
echo "健康检查:"
|
||||
echo " ./check_health.sh"
|
||||
echo
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
echo "=========================================="
|
||||
echo " Argus Agent 安装脚本 v1.0"
|
||||
echo "=========================================="
|
||||
echo
|
||||
|
||||
check_root
|
||||
check_system
|
||||
|
||||
log_info "开始安装 Argus Agent..."
|
||||
|
||||
install_argus_agent
|
||||
create_user
|
||||
install_config
|
||||
start_argus_agent
|
||||
|
||||
show_install_info
|
||||
}
|
||||
|
||||
# 脚本入口
|
||||
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||
main "$@"
|
||||
fi
|
||||
87
src/metric/client-plugins/all-in-one-full/plugins/argus-agent/package.sh
Executable file
87
src/metric/client-plugins/all-in-one-full/plugins/argus-agent/package.sh
Executable file
@@ -0,0 +1,87 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
# 颜色定义
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
log_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
# 获取当前目录
|
||||
CURRENT_DIR=$(pwd)
|
||||
PACKAGE_NAME="argus-agent-$(date +%Y%m%d-%H%M%S)"
|
||||
PACKAGE_FILE="${PACKAGE_NAME}.tar.gz"
|
||||
|
||||
log_info "开始打包 Argus Agent 安装包..."
|
||||
|
||||
# 检查必要文件
|
||||
log_info "检查必要文件..."
|
||||
|
||||
required_files=(
|
||||
"install.sh"
|
||||
"uninstall.sh"
|
||||
"bin/argus-agent"
|
||||
"check_health.sh"
|
||||
)
|
||||
|
||||
missing_files=()
|
||||
for file in "${required_files[@]}"; do
|
||||
if [[ ! -f "$file" ]]; then
|
||||
missing_files+=("$file")
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ ${#missing_files[@]} -gt 0 ]]; then
|
||||
echo "缺少以下文件:"
|
||||
for file in "${missing_files[@]}"; do
|
||||
echo " - $file"
|
||||
done
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_success "所有必要文件检查完成"
|
||||
|
||||
# 创建临时目录
|
||||
TEMP_DIR=$(mktemp -d)
|
||||
log_info "创建临时目录: $TEMP_DIR"
|
||||
|
||||
# 复制文件到临时目录
|
||||
cp -r . "$TEMP_DIR/$PACKAGE_NAME"
|
||||
|
||||
# 进入临时目录
|
||||
cd "$TEMP_DIR"
|
||||
|
||||
# 创建压缩包
|
||||
log_info "创建压缩包: $PACKAGE_FILE"
|
||||
tar -czf "$PACKAGE_FILE" "$PACKAGE_NAME"
|
||||
|
||||
# 移动压缩包到原目录
|
||||
mv "$PACKAGE_FILE" "$CURRENT_DIR/"
|
||||
|
||||
# 清理临时目录
|
||||
rm -rf "$TEMP_DIR"
|
||||
|
||||
# 返回原目录
|
||||
cd "$CURRENT_DIR"
|
||||
|
||||
# 显示结果
|
||||
log_success "打包完成!"
|
||||
echo
|
||||
echo "安装包文件: $PACKAGE_FILE"
|
||||
echo "文件大小: $(du -h "$PACKAGE_FILE" | cut -f1)"
|
||||
echo
|
||||
echo "使用方法:"
|
||||
echo "1. 将 $PACKAGE_FILE 传输到目标服务器"
|
||||
echo "2. 解压: tar -xzf $PACKAGE_FILE"
|
||||
echo "3. 进入目录: cd $PACKAGE_NAME"
|
||||
echo "4. 运行安装: sudo ./install.sh"
|
||||
echo
|
||||
echo "注意: 请确保所有必要文件都存在"
|
||||
255
src/metric/client-plugins/all-in-one-full/plugins/argus-agent/uninstall.sh
Executable file
255
src/metric/client-plugins/all-in-one-full/plugins/argus-agent/uninstall.sh
Executable file
@@ -0,0 +1,255 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Argus Agent 卸载脚本
|
||||
# 版本: 1.0
|
||||
# 作者: AIOps Team
|
||||
# 日期: $(date +%Y-%m-%d)
|
||||
|
||||
set -e
|
||||
|
||||
# 颜色定义
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# 日志函数
|
||||
log_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# 检查是否为 root 用户
|
||||
check_root() {
|
||||
if [[ $EUID -ne 0 ]]; then
|
||||
log_error "此脚本需要 root 权限运行"
|
||||
log_info "请使用: sudo $0"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 停止运行中的进程
|
||||
stop_processes() {
|
||||
log_info "停止 Argus Agent 进程..."
|
||||
|
||||
local pid_file="/var/run/argus-agent.pid"
|
||||
local stopped=false
|
||||
|
||||
# 首先尝试通过 PID 文件停止服务
|
||||
if [[ -f "$pid_file" ]]; then
|
||||
local pid=$(cat "$pid_file")
|
||||
if kill -0 "$pid" 2>/dev/null; then
|
||||
log_info "通过 PID 文件停止服务 (PID: $pid)..."
|
||||
kill "$pid"
|
||||
sleep 3
|
||||
|
||||
# 检查进程是否已停止
|
||||
if kill -0 "$pid" 2>/dev/null; then
|
||||
log_warning "进程未响应,强制终止..."
|
||||
kill -9 "$pid" 2>/dev/null || true
|
||||
fi
|
||||
log_success "Argus Agent 进程已停止"
|
||||
stopped=true
|
||||
else
|
||||
log_warning "PID 文件存在但进程已不存在,清理 PID 文件"
|
||||
rm -f "$pid_file"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 查找并杀死所有 argus-agent 进程
|
||||
local pids=$(pgrep -f "argus-agent" 2>/dev/null || true)
|
||||
if [[ -n "$pids" ]]; then
|
||||
log_info "发现 argus-agent 进程,正在停止..."
|
||||
for pid in $pids; do
|
||||
log_info "停止进程 PID: $pid"
|
||||
kill "$pid" 2>/dev/null || true
|
||||
done
|
||||
sleep 2
|
||||
|
||||
# 检查是否还有进程在运行,如果有则强制终止
|
||||
local remaining_pids=$(pgrep -f "argus-agent" 2>/dev/null || true)
|
||||
if [[ -n "$remaining_pids" ]]; then
|
||||
log_warning "进程未响应,强制终止..."
|
||||
for pid in $remaining_pids; do
|
||||
log_info "强制终止进程 PID: $pid"
|
||||
kill -9 "$pid" 2>/dev/null || true
|
||||
done
|
||||
sleep 1
|
||||
fi
|
||||
|
||||
# 最终检查
|
||||
if pgrep -f "argus-agent" > /dev/null; then
|
||||
log_error "无法停止所有 argus-agent 进程"
|
||||
else
|
||||
log_success "所有 Argus Agent 进程已停止"
|
||||
stopped=true
|
||||
fi
|
||||
else
|
||||
log_info "Argus Agent 进程未运行"
|
||||
fi
|
||||
|
||||
# 清理 PID 文件
|
||||
rm -f "$pid_file"
|
||||
|
||||
if [[ "$stopped" == "false" ]]; then
|
||||
log_warning "未发现需要停止的 Argus Agent 进程"
|
||||
fi
|
||||
}
|
||||
|
||||
# 删除二进制文件
|
||||
remove_binary() {
|
||||
log_info "删除 Argus Agent 二进制文件..."
|
||||
|
||||
local binary_files=(
|
||||
"/usr/local/bin/argus-agent"
|
||||
)
|
||||
|
||||
local deleted=false
|
||||
for binary_file in "${binary_files[@]}"; do
|
||||
if [[ -f "$binary_file" ]]; then
|
||||
rm -f "$binary_file"
|
||||
log_success "二进制文件已删除: $binary_file"
|
||||
deleted=true
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ "$deleted" == "false" ]]; then
|
||||
log_info "二进制文件不存在"
|
||||
fi
|
||||
}
|
||||
|
||||
# 删除配置文件
|
||||
remove_config() {
|
||||
log_info "删除配置文件..."
|
||||
|
||||
local config_dir="/etc/argus-agent"
|
||||
|
||||
if [[ -d "$config_dir" ]]; then
|
||||
rm -rf "$config_dir"
|
||||
log_success "配置目录已删除"
|
||||
else
|
||||
log_info "配置目录不存在"
|
||||
fi
|
||||
}
|
||||
|
||||
# 删除数据目录
|
||||
remove_data_dir() {
|
||||
log_info "删除数据目录..."
|
||||
|
||||
local data_dir="/var/lib/argus-agent"
|
||||
|
||||
if [[ -d "$data_dir" ]]; then
|
||||
rm -rf "$data_dir"
|
||||
log_success "数据目录已删除"
|
||||
else
|
||||
log_info "数据目录不存在"
|
||||
fi
|
||||
}
|
||||
|
||||
# 检查用户状态(可选)
|
||||
check_user_status() {
|
||||
log_info "检查 argus-agent 用户状态..."
|
||||
|
||||
if id "argus-agent" &>/dev/null; then
|
||||
log_info "检测到 argus-agent 用户存在"
|
||||
log_warning "argus-agent 是系统用户,可能被其他服务使用"
|
||||
log_info "为了系统稳定性,将保留 argus-agent 用户"
|
||||
log_info "如需手动删除,请运行: sudo userdel argus-agent"
|
||||
else
|
||||
log_info "argus-agent 用户不存在"
|
||||
fi
|
||||
}
|
||||
|
||||
# 清理日志文件
|
||||
cleanup_logs() {
|
||||
log_info "清理日志文件..."
|
||||
|
||||
# 删除安装脚本创建的日志文件
|
||||
rm -f /var/log/argus-agent.log
|
||||
|
||||
log_success "日志文件已清理"
|
||||
}
|
||||
|
||||
# 清理安装记录
|
||||
cleanup_install_record() {
|
||||
log_info "清理安装记录..."
|
||||
|
||||
local install_record="/opt/argus-metric/current/.install_record"
|
||||
|
||||
if [[ -f "$install_record" ]]; then
|
||||
if command -v jq &> /dev/null; then
|
||||
# 使用 jq 删除 argus-agent 记录
|
||||
jq 'del(.components."argus-agent")' "$install_record" > "$install_record.tmp" && mv "$install_record.tmp" "$install_record"
|
||||
log_success "安装记录已更新"
|
||||
else
|
||||
log_warning "jq 命令不可用,无法清理安装记录"
|
||||
fi
|
||||
else
|
||||
log_info "安装记录文件不存在"
|
||||
fi
|
||||
}
|
||||
|
||||
# 显示卸载信息
|
||||
show_uninstall_info() {
|
||||
log_success "Argus Agent 卸载完成!"
|
||||
echo
|
||||
echo "已删除的内容:"
|
||||
echo " - 二进制文件: /usr/local/bin/argus-agent"
|
||||
echo " - 配置目录: /etc/argus-agent"
|
||||
echo " - 数据目录: /var/lib/argus-agent"
|
||||
echo " - 相关日志文件"
|
||||
echo
|
||||
echo "注意:"
|
||||
echo " - argus-agent 用户已保留(系统用户,可能被其他服务使用)"
|
||||
echo " - 如需完全清理,请手动检查并删除相关文件"
|
||||
echo
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
echo "=========================================="
|
||||
echo " Argus Agent 卸载脚本 v1.0"
|
||||
echo "=========================================="
|
||||
echo
|
||||
|
||||
check_root
|
||||
|
||||
log_warning "此操作将完全卸载 Argus Agent"
|
||||
read -p "确认继续?(y/N): " confirm
|
||||
|
||||
if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then
|
||||
log_info "取消卸载操作"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
log_info "开始卸载 Argus Agent..."
|
||||
|
||||
stop_processes
|
||||
remove_binary
|
||||
remove_config
|
||||
remove_data_dir
|
||||
cleanup_logs
|
||||
cleanup_install_record
|
||||
|
||||
# 检查用户状态
|
||||
check_user_status
|
||||
|
||||
show_uninstall_info
|
||||
}
|
||||
|
||||
# 脚本入口
|
||||
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||
main "$@"
|
||||
fi
|
||||
Binary file not shown.
BIN
src/metric/client-plugins/all-in-one-full/plugins/dcgm-exporter/bin/dcgm-exporter
LFS
Executable file
BIN
src/metric/client-plugins/all-in-one-full/plugins/dcgm-exporter/bin/dcgm-exporter
LFS
Executable file
Binary file not shown.
@@ -0,0 +1,55 @@
|
||||
#!/bin/bash
|
||||
|
||||
# DCGM Exporter 健康检查脚本
|
||||
# 输出 JSON 格式结果
|
||||
|
||||
set -e
|
||||
|
||||
# 检查 DCGM Exporter 健康状态
|
||||
check_health() {
|
||||
local url="http://localhost:9400"
|
||||
local metrics_url="$url/metrics"
|
||||
local name="dcgm-exporter"
|
||||
local status="unhealth"
|
||||
local reason=""
|
||||
|
||||
# 检查 curl 是否可用
|
||||
if ! command -v curl &> /dev/null; then
|
||||
reason="curl 命令不可用,无法进行健康检查"
|
||||
echo "{\"name\": \"$name\", \"status\": \"$status\", \"reason\": \"$reason\"}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 测试根路径连接
|
||||
local http_code=$(curl -s -o /dev/null -w "%{http_code}" "$url" 2>/dev/null || echo "000")
|
||||
|
||||
if [[ "$http_code" == "200" ]]; then
|
||||
# 测试 metrics 端点
|
||||
local metrics_code=$(curl -s -o /dev/null -w "%{http_code}" "$metrics_url" 2>/dev/null || echo "000")
|
||||
|
||||
if [[ "$metrics_code" == "200" ]]; then
|
||||
status="health"
|
||||
reason="success"
|
||||
echo "{\"name\": \"$name\", \"status\": \"$status\", \"reason\": \"$reason\"}"
|
||||
exit 0
|
||||
else
|
||||
reason="Metrics 端点异常 (HTTP $metrics_code)"
|
||||
echo "{\"name\": \"$name\", \"status\": \"$status\", \"reason\": \"$reason\"}"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
reason="HTTP 服务异常 (HTTP $http_code),请检查 DCGM Exporter 是否正在运行在端口 9400"
|
||||
echo "{\"name\": \"$name\", \"status\": \"$status\", \"reason\": \"$reason\"}"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
check_health
|
||||
}
|
||||
|
||||
# 脚本入口
|
||||
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||
main "$@"
|
||||
fi
|
||||
@@ -0,0 +1,77 @@
|
||||
# Format
|
||||
# If line starts with a '#' it is considered a comment
|
||||
# DCGM FIELD, Prometheus metric type, help message
|
||||
|
||||
# Clocks
|
||||
DCGM_FI_DEV_SM_CLOCK, gauge, SM clock frequency (in MHz).
|
||||
DCGM_FI_DEV_MEM_CLOCK, gauge, Memory clock frequency (in MHz).
|
||||
# DCGM_EXP_CLOCK_EVENTS_COUNT, gauge, Count of clock events within the user-specified time window (see clock-events-count-window-size param).
|
||||
|
||||
# Temperature
|
||||
DCGM_FI_DEV_MEMORY_TEMP, gauge, Memory temperature (in C).
|
||||
DCGM_FI_DEV_GPU_TEMP, gauge, GPU temperature (in C).
|
||||
|
||||
# Power
|
||||
DCGM_FI_DEV_POWER_USAGE, gauge, Power draw (in W).
|
||||
DCGM_FI_DEV_TOTAL_ENERGY_CONSUMPTION, counter, Total energy consumption since boot (in mJ).
|
||||
|
||||
# PCIE
|
||||
DCGM_FI_PROF_PCIE_TX_BYTES, counter, Total number of bytes transmitted through PCIe TX via NVML.
|
||||
DCGM_FI_PROF_PCIE_RX_BYTES, counter, Total number of bytes received through PCIe RX via NVML.
|
||||
DCGM_FI_DEV_PCIE_REPLAY_COUNTER, counter, Total number of PCIe retries.
|
||||
|
||||
# Utilization (the sample period varies depending on the product)
|
||||
DCGM_FI_DEV_GPU_UTIL, gauge, GPU utilization (in %).
|
||||
DCGM_FI_DEV_MEM_COPY_UTIL, gauge, Memory utilization (in %).
|
||||
DCGM_FI_DEV_ENC_UTIL, gauge, Encoder utilization (in %).
|
||||
DCGM_FI_DEV_DEC_UTIL , gauge, Decoder utilization (in %).
|
||||
|
||||
# Errors and violations
|
||||
DCGM_FI_DEV_XID_ERRORS, gauge, Value of the last XID error encountered.
|
||||
# DCGM_FI_DEV_POWER_VIOLATION, counter, Throttling duration due to power constraints (in us).
|
||||
# DCGM_FI_DEV_THERMAL_VIOLATION, counter, Throttling duration due to thermal constraints (in us).
|
||||
# DCGM_FI_DEV_SYNC_BOOST_VIOLATION, counter, Throttling duration due to sync-boost constraints (in us).
|
||||
# DCGM_FI_DEV_BOARD_LIMIT_VIOLATION, counter, Throttling duration due to board limit constraints (in us).
|
||||
# DCGM_FI_DEV_LOW_UTIL_VIOLATION, counter, Throttling duration due to low utilization (in us).
|
||||
# DCGM_FI_DEV_RELIABILITY_VIOLATION, counter, Throttling duration due to reliability constraints (in us).
|
||||
# DCGM_EXP_XID_ERRORS_COUNT, gauge, Count of XID Errors within user-specified time window (see xid-count-window-size param).
|
||||
# Memory usage
|
||||
DCGM_FI_DEV_FB_FREE, gauge, Frame buffer memory free (in MB).
|
||||
DCGM_FI_DEV_FB_USED, gauge, Frame buffer memory used (in MB).
|
||||
|
||||
# ECC
|
||||
# DCGM_FI_DEV_ECC_SBE_VOL_TOTAL, counter, Total number of single-bit volatile ECC errors.
|
||||
# DCGM_FI_DEV_ECC_DBE_VOL_TOTAL, counter, Total number of double-bit volatile ECC errors.
|
||||
# DCGM_FI_DEV_ECC_SBE_AGG_TOTAL, counter, Total number of single-bit persistent ECC errors.
|
||||
# DCGM_FI_DEV_ECC_DBE_AGG_TOTAL, counter, Total number of double-bit persistent ECC errors.
|
||||
|
||||
# Retired pages
|
||||
# DCGM_FI_DEV_RETIRED_SBE, counter, Total number of retired pages due to single-bit errors.
|
||||
# DCGM_FI_DEV_RETIRED_DBE, counter, Total number of retired pages due to double-bit errors.
|
||||
# DCGM_FI_DEV_RETIRED_PENDING, counter, Total number of pages pending retirement.
|
||||
|
||||
# NVLink
|
||||
# DCGM_FI_DEV_NVLINK_CRC_FLIT_ERROR_COUNT_TOTAL, counter, Total number of NVLink flow-control CRC errors.
|
||||
# DCGM_FI_DEV_NVLINK_CRC_DATA_ERROR_COUNT_TOTAL, counter, Total number of NVLink data CRC errors.
|
||||
# DCGM_FI_DEV_NVLINK_REPLAY_ERROR_COUNT_TOTAL, counter, Total number of NVLink retries.
|
||||
# DCGM_FI_DEV_NVLINK_RECOVERY_ERROR_COUNT_TOTAL, counter, Total number of NVLink recovery errors.
|
||||
DCGM_FI_DEV_NVLINK_BANDWIDTH_TOTAL, counter, Total number of NVLink bandwidth counters for all lanes
|
||||
|
||||
# VGPU License status
|
||||
DCGM_FI_DEV_VGPU_LICENSE_STATUS, gauge, vGPU License status
|
||||
|
||||
# Remapped rows
|
||||
DCGM_FI_DEV_UNCORRECTABLE_REMAPPED_ROWS, counter, Number of remapped rows for uncorrectable errors
|
||||
DCGM_FI_DEV_CORRECTABLE_REMAPPED_ROWS, counter, Number of remapped rows for correctable errors
|
||||
DCGM_FI_DEV_ROW_REMAP_FAILURE, gauge, Whether remapping of rows has failed
|
||||
|
||||
# Static configuration information. These appear as labels on the other metrics
|
||||
DCGM_FI_DRIVER_VERSION, label, Driver Version
|
||||
# DCGM_FI_NVML_VERSION, label, NVML Version
|
||||
# DCGM_FI_DEV_BRAND, label, Device Brand
|
||||
# DCGM_FI_DEV_SERIAL, label, Device Serial Number
|
||||
# DCGM_FI_DEV_OEM_INFOROM_VER, label, OEM inforom version
|
||||
# DCGM_FI_DEV_ECC_INFOROM_VER, label, ECC inforom version
|
||||
# DCGM_FI_DEV_POWER_INFOROM_VER, label, Power management object inforom version
|
||||
# DCGM_FI_DEV_INFOROM_IMAGE_VER, label, Inforom image version
|
||||
# DCGM_FI_DEV_VBIOS_VERSION, label, VBIOS version of the device
|
||||
|
365
src/metric/client-plugins/all-in-one-full/plugins/dcgm-exporter/install.sh
Executable file
365
src/metric/client-plugins/all-in-one-full/plugins/dcgm-exporter/install.sh
Executable file
@@ -0,0 +1,365 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
# 颜色定义
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# 日志函数
|
||||
log_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# 更新安装记录
|
||||
update_install_record() {
|
||||
local pid="$1"
|
||||
# 使用传入的安装目录参数,如果没有则使用默认值
|
||||
local install_base_dir="${2:-/opt/argus-metric/current}"
|
||||
local install_record="$install_base_dir/.install_record"
|
||||
|
||||
# 如果安装记录文件不存在,说明是首次安装,由主安装脚本统一创建
|
||||
if [[ ! -f "$install_record" ]]; then
|
||||
log_info "安装记录文件不存在,将由主安装脚本创建"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# 如果文件存在,说明是重启场景,只更新 PID 字段
|
||||
if command -v jq &> /dev/null; then
|
||||
# 读取当前 PID
|
||||
local current_pid=$(jq -r '.components."dcgm-exporter".pid // ""' "$install_record" 2>/dev/null)
|
||||
|
||||
if [[ -z "$current_pid" ]]; then
|
||||
log_warning "无法读取当前 PID,跳过更新"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 使用 jq 只更新 pid 字段,保持字符串类型,保留其他字段
|
||||
jq --arg new_pid "$pid" '.components."dcgm-exporter".pid = $new_pid' "$install_record" > "$install_record.tmp" && mv "$install_record.tmp" "$install_record"
|
||||
log_info "PID 已更新: $current_pid -> $pid"
|
||||
else
|
||||
log_warning "jq 命令不可用,无法更新安装记录文件"
|
||||
fi
|
||||
}
|
||||
|
||||
# 显示帮助信息
|
||||
show_help() {
|
||||
echo "DCGM Exporter 安装脚本"
|
||||
echo
|
||||
echo "用法: $0 [选项]"
|
||||
echo
|
||||
echo "选项:"
|
||||
echo " --help 显示此帮助信息"
|
||||
echo
|
||||
echo "示例:"
|
||||
echo " $0 # 安装 DCGM Exporter"
|
||||
echo
|
||||
}
|
||||
|
||||
# 解析命令行参数
|
||||
INSTALL_DIR=""
|
||||
for arg in "$@"; do
|
||||
case $arg in
|
||||
--help|-h)
|
||||
show_help
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
# 如果参数不是以--开头,则认为是安装目录
|
||||
if [[ ! "$arg" =~ ^-- ]]; then
|
||||
INSTALL_DIR="$arg"
|
||||
else
|
||||
log_error "未知参数: $arg"
|
||||
show_help
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# 检查是否为 root 用户
|
||||
check_root() {
|
||||
if [[ $EUID -ne 0 ]]; then
|
||||
log_error "此脚本需要 root 权限运行"
|
||||
log_info "请使用: sudo $0"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 检查系统要求
|
||||
check_system() {
|
||||
log_info "检查系统要求..."
|
||||
|
||||
# 检查操作系统
|
||||
if [[ ! -f /etc/os-release ]]; then
|
||||
log_error "无法检测操作系统版本"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
source /etc/os-release
|
||||
log_info "检测到操作系统: $NAME $VERSION"
|
||||
|
||||
# 检查是否为 Ubuntu/Debian
|
||||
if [[ "$ID" != "ubuntu" && "$ID" != "debian" ]]; then
|
||||
log_warning "此脚本主要针对 Ubuntu/Debian 系统,其他系统可能需要调整"
|
||||
fi
|
||||
|
||||
# 检查 NVIDIA GPU
|
||||
if ! command -v nvidia-smi &> /dev/null; then
|
||||
log_warning "未检测到 nvidia-smi,请确保已安装 NVIDIA 驱动"
|
||||
else
|
||||
log_success "检测到 NVIDIA GPU"
|
||||
nvidia-smi --query-gpu=name --format=csv,noheader,nounits | head -1
|
||||
fi
|
||||
}
|
||||
|
||||
# 安装 DCGM 依赖
|
||||
install_dcgm_dependency() {
|
||||
log_info "安装 DCGM 依赖..."
|
||||
|
||||
local deb_file="bin/datacenter-gpu-manager_3.3.9_amd64.deb"
|
||||
|
||||
if [[ ! -f "$deb_file" ]]; then
|
||||
log_error "找不到 DCGM 依赖文件: $deb_file"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 安装 deb 包
|
||||
dpkg -i "$deb_file" || {
|
||||
log_warning "dpkg 安装失败,尝试使用 apt 修复依赖..."
|
||||
apt-get update
|
||||
apt-get install -f -y
|
||||
dpkg -i "$deb_file"
|
||||
}
|
||||
|
||||
log_success "DCGM 依赖安装完成"
|
||||
}
|
||||
|
||||
# 检查 DCGM 服务状态
|
||||
check_dcgm_service() {
|
||||
log_info "检查 DCGM 服务状态..."
|
||||
|
||||
# 检查 DCGM 服务是否在运行
|
||||
if systemctl is-active --quiet dcgm 2>/dev/null; then
|
||||
log_success "DCGM 服务已在运行"
|
||||
elif pgrep -f nv-hostengine > /dev/null; then
|
||||
log_success "nv-hostengine 进程已在运行"
|
||||
else
|
||||
log_warning "DCGM 服务未运行,需要手动启动"
|
||||
log_info "启动 DCGM 服务的方法:"
|
||||
log_info " 1. 使用 systemd: sudo systemctl start dcgm"
|
||||
log_info " 2. 手动启动: nohup nv-hostengine > /var/log/nv-hostengine.log 2>&1 &"
|
||||
fi
|
||||
|
||||
# 测试 DCGM 连接
|
||||
if systemctl is-active --quiet dcgm 2>/dev/null || pgrep -f nv-hostengine > /dev/null; then
|
||||
log_info "测试 DCGM 连接..."
|
||||
if dcgmi discovery -l > /dev/null 2>&1; then
|
||||
log_success "DCGM 连接测试成功"
|
||||
else
|
||||
log_warning "DCGM 连接测试失败,请检查服务状态"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# 停止可能运行的服务
|
||||
stop_existing_service() {
|
||||
log_info "检查并停止可能运行的服务..."
|
||||
|
||||
local pid_file="/var/run/dcgm-exporter.pid"
|
||||
|
||||
# 检查并停止通过 PID 文件管理的服务
|
||||
if [[ -f "$pid_file" ]]; then
|
||||
local pid=$(cat "$pid_file")
|
||||
if kill -0 "$pid" 2>/dev/null; then
|
||||
log_info "发现正在运行的 DCGM Exporter 服务 (PID: $pid),正在停止..."
|
||||
kill "$pid" > /dev/null 2>&1 || true
|
||||
sleep 2
|
||||
if kill -0 "$pid" 2>/dev/null; then
|
||||
log_warning "进程未响应,强制终止..."
|
||||
kill -9 "$pid" > /dev/null 2>&1 || true
|
||||
fi
|
||||
rm -f "$pid_file"
|
||||
log_success "服务已停止"
|
||||
else
|
||||
log_warning "发现过期的 PID 文件,正在清理..."
|
||||
rm -f "$pid_file"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 查找并停止所有 dcgm-exporter 进程(排除脚本自身)
|
||||
local exporter_bin="/usr/local/bin/dcgm-exporter"
|
||||
local pids=$(pgrep -f "$exporter_bin")
|
||||
|
||||
if [[ -n "$pids" ]]; then
|
||||
log_info "发现其他 dcgm-exporter 进程,正在停止..."
|
||||
for pid in $pids; do
|
||||
if [[ "$pid" != "$$" ]]; then
|
||||
kill "$pid" > /dev/null 2>&1 || true
|
||||
sleep 1
|
||||
if kill -0 "$pid" 2>/dev/null; then
|
||||
log_warning "进程 $pid 未响应,强制终止..."
|
||||
kill -9 "$pid" > /dev/null 2>&1 || true
|
||||
fi
|
||||
fi
|
||||
done
|
||||
log_success "所有 dcgm-exporter 进程已停止"
|
||||
fi
|
||||
}
|
||||
|
||||
# 安装 DCGM Exporter 二进制文件
|
||||
install_dcgm_exporter() {
|
||||
log_info "安装 DCGM Exporter..."
|
||||
|
||||
local binary_file="bin/dcgm-exporter"
|
||||
local install_dir="/usr/local/bin"
|
||||
|
||||
if [[ ! -f "$binary_file" ]]; then
|
||||
log_error "找不到 DCGM Exporter 二进制文件: $binary_file"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 停止可能运行的服务
|
||||
stop_existing_service
|
||||
|
||||
# 复制二进制文件
|
||||
cp "$binary_file" "$install_dir/"
|
||||
chmod +x "$install_dir/dcgm-exporter"
|
||||
|
||||
log_success "DCGM Exporter 二进制文件安装完成"
|
||||
}
|
||||
|
||||
# 安装配置文件
|
||||
install_config() {
|
||||
log_info "安装配置文件..."
|
||||
|
||||
local config_dir="/etc/dcgm-exporter"
|
||||
local config_file="config/default-counters.csv"
|
||||
|
||||
# 创建配置目录
|
||||
mkdir -p "$config_dir"
|
||||
|
||||
if [[ -f "$config_file" ]]; then
|
||||
cp "$config_file" "$config_dir/"
|
||||
log_success "配置文件安装完成"
|
||||
else
|
||||
log_warning "未找到配置文件,使用默认配置"
|
||||
fi
|
||||
}
|
||||
|
||||
# 启动 DCGM Exporter 服务
|
||||
start_dcgm_exporter() {
|
||||
log_info "启动 DCGM Exporter 服务..."
|
||||
|
||||
local binary_path="/usr/local/bin/dcgm-exporter"
|
||||
local log_file="/var/log/dcgm-exporter.log"
|
||||
local pid_file="/var/run/dcgm-exporter.pid"
|
||||
|
||||
# 检查服务是否已经在运行
|
||||
if [[ -f "$pid_file" ]]; then
|
||||
local pid=$(cat "$pid_file")
|
||||
if kill -0 "$pid" 2>/dev/null; then
|
||||
log_info "DCGM Exporter 服务已在运行 (PID: $pid)"
|
||||
return 0
|
||||
else
|
||||
log_warning "发现过期的 PID 文件,正在清理..."
|
||||
rm -f "$pid_file"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 检查端口是否被占用
|
||||
if netstat -tuln 2>/dev/null | grep -q ":9400 "; then
|
||||
log_warning "端口 9400 已被占用,请检查是否有其他服务在运行"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 启动服务
|
||||
log_info "正在启动 DCGM Exporter..."
|
||||
nohup "$binary_path" --address=:9400 > "$log_file" 2>&1 &
|
||||
local pid=$!
|
||||
|
||||
# 保存 PID
|
||||
echo "$pid" > "$pid_file"
|
||||
|
||||
# 等待服务启动
|
||||
sleep 2
|
||||
|
||||
# 检查服务是否成功启动
|
||||
if kill -0 "$pid" 2>/dev/null; then
|
||||
log_success "DCGM Exporter 服务启动成功 (PID: $pid)"
|
||||
log_info "日志文件: $log_file"
|
||||
log_info "PID 文件: $pid_file"
|
||||
|
||||
# 更新安装记录
|
||||
update_install_record "$pid" "$INSTALL_DIR"
|
||||
else
|
||||
log_error "DCGM Exporter 服务启动失败"
|
||||
rm -f "$pid_file"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
|
||||
# 显示安装信息
|
||||
show_install_info() {
|
||||
log_success "DCGM Exporter 安装完成!"
|
||||
echo
|
||||
echo "安装信息:"
|
||||
echo " 二进制文件: /usr/local/bin/dcgm-exporter"
|
||||
echo " 配置文件: /etc/dcgm-exporter/default-counters.csv"
|
||||
echo " 默认端口: 9400"
|
||||
echo
|
||||
echo "使用方法:"
|
||||
echo " 1. 启动 DCGM 服务:"
|
||||
echo " sudo systemctl start dcgm"
|
||||
echo " 或: nohup nv-hostengine > /var/log/nv-hostengine.log 2>&1 &"
|
||||
echo " 2. 启动 DCGM Exporter:"
|
||||
echo " /usr/local/bin/dcgm-exporter --address=:9400"
|
||||
echo " 或: nohup /usr/local/bin/dcgm-exporter --address=:9400 &"
|
||||
echo
|
||||
echo "测试连接:"
|
||||
echo " curl http://localhost:9400/metrics"
|
||||
echo
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
echo "=========================================="
|
||||
echo " DCGM Exporter 安装脚本 v1.0"
|
||||
echo "=========================================="
|
||||
echo
|
||||
|
||||
check_root
|
||||
check_system
|
||||
|
||||
log_info "开始安装 DCGM Exporter..."
|
||||
|
||||
install_dcgm_dependency
|
||||
check_dcgm_service
|
||||
install_dcgm_exporter
|
||||
install_config
|
||||
start_dcgm_exporter
|
||||
|
||||
show_install_info
|
||||
}
|
||||
|
||||
# 脚本入口
|
||||
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||
main "$@"
|
||||
fi
|
||||
97
src/metric/client-plugins/all-in-one-full/plugins/dcgm-exporter/package.sh
Executable file
97
src/metric/client-plugins/all-in-one-full/plugins/dcgm-exporter/package.sh
Executable file
@@ -0,0 +1,97 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
# 颜色定义
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
log_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
# 获取当前目录
|
||||
CURRENT_DIR=$(pwd)
|
||||
PACKAGE_NAME="dcgm-exporter-$(date +%Y%m%d-%H%M%S)"
|
||||
PACKAGE_FILE="${PACKAGE_NAME}.tar.gz"
|
||||
|
||||
log_info "开始打包 DCGM Exporter 安装包..."
|
||||
|
||||
# 检查必要文件
|
||||
log_info "检查必要文件..."
|
||||
|
||||
required_files=(
|
||||
"install.sh"
|
||||
"uninstall.sh"
|
||||
"bin/dcgm-exporter"
|
||||
"bin/datacenter-gpu-manager_3.3.9_amd64.deb"
|
||||
"check_health.sh"
|
||||
)
|
||||
|
||||
missing_files=()
|
||||
for file in "${required_files[@]}"; do
|
||||
if [[ ! -f "$file" ]]; then
|
||||
missing_files+=("$file")
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ ${#missing_files[@]} -gt 0 ]]; then
|
||||
echo "缺少以下文件:"
|
||||
for file in "${missing_files[@]}"; do
|
||||
echo " - $file"
|
||||
done
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 防御:阻止将 Git LFS 指针文件打包
|
||||
for f in bin/dcgm-exporter bin/datacenter-gpu-manager_3.3.9_amd64.deb; do
|
||||
if head -n1 "$f" 2>/dev/null | grep -q '^version https://git-lfs.github.com/spec/v1$'; then
|
||||
echo "[ERROR] $f 是 Git LFS 指针文件,未还原为真实制品"
|
||||
echo " 请在仓库根目录执行: git lfs fetch --all && git lfs checkout"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
log_success "所有必要文件检查完成"
|
||||
|
||||
# 创建临时目录
|
||||
TEMP_DIR=$(mktemp -d)
|
||||
log_info "创建临时目录: $TEMP_DIR"
|
||||
|
||||
# 复制文件到临时目录
|
||||
cp -r . "$TEMP_DIR/$PACKAGE_NAME"
|
||||
|
||||
# 进入临时目录
|
||||
cd "$TEMP_DIR"
|
||||
|
||||
# 创建压缩包
|
||||
log_info "创建压缩包: $PACKAGE_FILE"
|
||||
tar -czf "$PACKAGE_FILE" "$PACKAGE_NAME"
|
||||
|
||||
# 移动压缩包到原目录
|
||||
mv "$PACKAGE_FILE" "$CURRENT_DIR/"
|
||||
|
||||
# 清理临时目录
|
||||
rm -rf "$TEMP_DIR"
|
||||
|
||||
# 返回原目录
|
||||
cd "$CURRENT_DIR"
|
||||
|
||||
# 显示结果
|
||||
log_success "打包完成!"
|
||||
echo
|
||||
echo "安装包文件: $PACKAGE_FILE"
|
||||
echo "文件大小: $(du -h "$PACKAGE_FILE" | cut -f1)"
|
||||
echo
|
||||
echo "使用方法:"
|
||||
echo "1. 将 $PACKAGE_FILE 传输到目标服务器"
|
||||
echo "2. 解压: tar -xzf $PACKAGE_FILE"
|
||||
echo "3. 进入目录: cd $PACKAGE_NAME"
|
||||
echo "4. 运行安装: sudo ./install.sh"
|
||||
echo
|
||||
echo "注意: 请确保 config/default-counters.csv 文件存在"
|
||||
216
src/metric/client-plugins/all-in-one-full/plugins/dcgm-exporter/uninstall.sh
Executable file
216
src/metric/client-plugins/all-in-one-full/plugins/dcgm-exporter/uninstall.sh
Executable file
@@ -0,0 +1,216 @@
|
||||
#!/bin/bash
|
||||
|
||||
# DCGM Exporter 卸载脚本
|
||||
# 版本: 1.0
|
||||
# 作者: AIOps Team
|
||||
# 日期: $(date +%Y-%m-%d)
|
||||
|
||||
set -e
|
||||
|
||||
# 颜色定义
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# 日志函数
|
||||
log_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# 检查是否为 root 用户
|
||||
check_root() {
|
||||
if [[ $EUID -ne 0 ]]; then
|
||||
log_error "此脚本需要 root 权限运行"
|
||||
log_info "请使用: sudo $0"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 停止运行中的进程
|
||||
stop_processes() {
|
||||
log_info "停止 DCGM Exporter 进程..."
|
||||
|
||||
local pid_file="/var/run/dcgm-exporter.pid"
|
||||
local stopped=false
|
||||
|
||||
# 首先尝试通过 PID 文件停止服务
|
||||
if [[ -f "$pid_file" ]]; then
|
||||
local pid=$(cat "$pid_file")
|
||||
if kill -0 "$pid" 2>/dev/null; then
|
||||
log_info "通过 PID 文件停止服务 (PID: $pid)..."
|
||||
kill "$pid"
|
||||
sleep 3
|
||||
|
||||
# 检查进程是否已停止
|
||||
if kill -0 "$pid" 2>/dev/null; then
|
||||
log_warning "进程未响应,强制终止..."
|
||||
kill -9 "$pid" 2>/dev/null || true
|
||||
fi
|
||||
log_success "DCGM Exporter 进程已停止"
|
||||
stopped=true
|
||||
else
|
||||
log_warning "PID 文件存在但进程已不存在,清理 PID 文件"
|
||||
rm -f "$pid_file"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 查找并杀死所有 dcgm-exporter 进程
|
||||
local pids=$(pgrep -f "dcgm-exporter" 2>/dev/null || true)
|
||||
if [[ -n "$pids" ]]; then
|
||||
log_info "发现 dcgm-exporter 进程,正在停止..."
|
||||
for pid in $pids; do
|
||||
log_info "停止进程 PID: $pid"
|
||||
kill "$pid" 2>/dev/null || true
|
||||
done
|
||||
sleep 2
|
||||
|
||||
# 检查是否还有进程在运行,如果有则强制终止
|
||||
local remaining_pids=$(pgrep -f "dcgm-exporter" 2>/dev/null || true)
|
||||
if [[ -n "$remaining_pids" ]]; then
|
||||
log_warning "进程未响应,强制终止..."
|
||||
for pid in $remaining_pids; do
|
||||
log_info "强制终止进程 PID: $pid"
|
||||
kill -9 "$pid" 2>/dev/null || true
|
||||
done
|
||||
sleep 1
|
||||
fi
|
||||
|
||||
# 最终检查
|
||||
if pgrep -f "dcgm-exporter" > /dev/null; then
|
||||
log_error "无法停止所有 dcgm-exporter 进程"
|
||||
else
|
||||
log_success "所有 DCGM Exporter 进程已停止"
|
||||
stopped=true
|
||||
fi
|
||||
else
|
||||
log_info "DCGM Exporter 进程未运行"
|
||||
fi
|
||||
|
||||
# 清理 PID 文件
|
||||
rm -f "$pid_file"
|
||||
|
||||
if [[ "$stopped" == "false" ]]; then
|
||||
log_warning "未发现需要停止的 DCGM Exporter 进程"
|
||||
fi
|
||||
}
|
||||
|
||||
# 删除二进制文件
|
||||
remove_binary() {
|
||||
log_info "删除 DCGM Exporter 二进制文件..."
|
||||
|
||||
local binary_file="/usr/local/bin/dcgm-exporter"
|
||||
|
||||
if [[ -f "$binary_file" ]]; then
|
||||
rm -f "$binary_file"
|
||||
log_success "二进制文件已删除"
|
||||
else
|
||||
log_info "二进制文件不存在"
|
||||
fi
|
||||
}
|
||||
|
||||
# 删除配置文件
|
||||
remove_config() {
|
||||
log_info "删除配置文件..."
|
||||
|
||||
local config_dir="/etc/dcgm-exporter"
|
||||
|
||||
if [[ -d "$config_dir" ]]; then
|
||||
rm -rf "$config_dir"
|
||||
log_success "配置目录已删除"
|
||||
else
|
||||
log_info "配置目录不存在"
|
||||
fi
|
||||
}
|
||||
|
||||
# 卸载 DCGM 依赖(可选)
|
||||
remove_dcgm_dependency() {
|
||||
log_info "检查 DCGM 依赖状态..."
|
||||
|
||||
# 检查是否安装了 DCGM 包
|
||||
if dpkg -l | grep -q datacenter-gpu-manager; then
|
||||
log_info "检测到 DCGM 依赖包已安装"
|
||||
log_warning "DCGM 是系统级依赖,可能被其他应用程序使用"
|
||||
log_info "为了系统稳定性,将保留 DCGM 依赖包"
|
||||
log_info "如需手动卸载,请运行: sudo apt-get remove --purge datacenter-gpu-manager"
|
||||
else
|
||||
log_info "DCGM 依赖包未安装"
|
||||
fi
|
||||
}
|
||||
|
||||
# 清理日志文件
|
||||
cleanup_logs() {
|
||||
log_info "清理日志文件..."
|
||||
|
||||
# 清理 journal 日志
|
||||
journalctl --vacuum-time=1s --quiet || true
|
||||
|
||||
# 删除可能的日志文件
|
||||
rm -f /var/log/nv-hostengine.log
|
||||
rm -f /var/log/dcgm-exporter.log
|
||||
|
||||
log_success "日志文件已清理"
|
||||
}
|
||||
|
||||
# 显示卸载信息
|
||||
show_uninstall_info() {
|
||||
log_success "DCGM Exporter 卸载完成!"
|
||||
echo
|
||||
echo "已删除的内容:"
|
||||
echo " - 二进制文件: /usr/local/bin/dcgm-exporter"
|
||||
echo " - 配置目录: /etc/dcgm-exporter"
|
||||
echo " - 相关日志文件"
|
||||
echo
|
||||
echo "注意:"
|
||||
echo " - DCGM 依赖包可能仍然存在"
|
||||
echo " - 如需完全清理,请手动检查并删除相关文件"
|
||||
echo
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
echo "=========================================="
|
||||
echo " DCGM Exporter 卸载脚本 v1.0"
|
||||
echo "=========================================="
|
||||
echo
|
||||
|
||||
check_root
|
||||
|
||||
log_warning "此操作将完全卸载 DCGM Exporter"
|
||||
read -p "确认继续?(y/N): " confirm
|
||||
|
||||
if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then
|
||||
log_info "取消卸载操作"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
log_info "开始卸载 DCGM Exporter..."
|
||||
|
||||
stop_processes
|
||||
remove_binary
|
||||
remove_config
|
||||
cleanup_logs
|
||||
|
||||
# 询问是否卸载 DCGM 依赖
|
||||
remove_dcgm_dependency
|
||||
|
||||
show_uninstall_info
|
||||
}
|
||||
|
||||
# 脚本入口
|
||||
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||
main "$@"
|
||||
fi
|
||||
@@ -0,0 +1,181 @@
|
||||
# Fluent Bit 安装包
|
||||
|
||||
这是一个 Fluent Bit 的自动化安装包,提供了完整的安装、卸载和健康检查功能。
|
||||
|
||||
## 目录结构
|
||||
|
||||
```
|
||||
fluent-bit-installer/
|
||||
├── install.sh # 安装脚本
|
||||
├── uninstall.sh # 卸载脚本
|
||||
├── package.sh # 打包脚本
|
||||
├── check_health.sh # 健康检查脚本
|
||||
├── bin/
|
||||
│ └── fluent-bit_3.1.9_amd64.deb # Fluent Bit 安装包
|
||||
└── config/
|
||||
├── fluent-bit.conf # 主配置文件
|
||||
├── inject_labels.lua # Lua 脚本
|
||||
├── parsers.conf # 解析器配置
|
||||
├── inputs.d/ # 输入配置目录
|
||||
│ ├── 10-train.conf
|
||||
│ └── 20-infer.conf
|
||||
└── outputs.d/ # 输出配置目录
|
||||
└── 10-es.conf
|
||||
```
|
||||
|
||||
## 功能特性
|
||||
|
||||
- **自动化安装**: 一键安装 Fluent Bit 及其依赖
|
||||
- **配置管理**: 自动部署预配置的配置文件
|
||||
- **服务管理**: 自动启动和停止 Fluent Bit 服务
|
||||
- **健康检查**: 提供 JSON 格式的健康状态检查
|
||||
- **完整卸载**: 彻底清理所有相关文件和配置
|
||||
- **用户管理**: 自动创建专用的 fluent-bit 用户
|
||||
|
||||
## 使用方法
|
||||
|
||||
### 1. 打包安装包
|
||||
|
||||
```bash
|
||||
./package.sh
|
||||
```
|
||||
|
||||
这将创建一个带时间戳的压缩包,例如:`fluent-bit-installer-20250924-160954.tar.gz`
|
||||
|
||||
### 2. 安装 Fluent Bit
|
||||
|
||||
```bash
|
||||
# 解压安装包
|
||||
tar -xzf fluent-bit-installer-*.tar.gz
|
||||
cd fluent-bit-installer-*
|
||||
|
||||
# 运行安装脚本(需要 root 权限)
|
||||
sudo ./install.sh
|
||||
```
|
||||
|
||||
### 3. 健康检查
|
||||
|
||||
```bash
|
||||
./check_health.sh
|
||||
```
|
||||
|
||||
输出示例:
|
||||
```json
|
||||
{"name": "fluent-bit", "status": "health", "reason": "success"}
|
||||
```
|
||||
|
||||
### 4. 卸载 Fluent Bit
|
||||
|
||||
```bash
|
||||
sudo ./uninstall.sh
|
||||
```
|
||||
|
||||
## 安装后的文件位置
|
||||
|
||||
- **二进制文件**: `/opt/fluent-bit/bin/fluent-bit`
|
||||
- **配置文件**: `/etc/fluent-bit/`
|
||||
- **日志文件**: `/var/log/fluent-bit/`
|
||||
- **缓冲区目录**: `/var/lib/fluent-bit/buffers/`
|
||||
- **运行用户**: `fluent-bit`
|
||||
- **HTTP 端口**: `2020`
|
||||
|
||||
## 配置说明
|
||||
|
||||
### 主配置文件
|
||||
|
||||
主配置文件位于 `/etc/fluent-bit/fluent-bit.conf`,包含以下主要部分:
|
||||
|
||||
- **SERVICE**: 服务配置,包括 HTTP 服务器设置
|
||||
- **INPUT**: 输入配置,通过 `inputs.d/` 目录管理
|
||||
- **FILTER**: 过滤器配置,包括解析器和标签注入
|
||||
- **OUTPUT**: 输出配置,通过 `outputs.d/` 目录管理
|
||||
|
||||
### 输入配置
|
||||
|
||||
- `10-train.conf`: 训练日志输入配置
|
||||
- `20-infer.conf`: 推理日志输入配置
|
||||
|
||||
### 输出配置
|
||||
|
||||
- `10-es.conf`: Elasticsearch 输出配置
|
||||
|
||||
## 服务管理
|
||||
|
||||
### 手动启动
|
||||
|
||||
```bash
|
||||
/opt/fluent-bit/bin/fluent-bit --config=/etc/fluent-bit/fluent-bit.conf
|
||||
```
|
||||
|
||||
### 后台启动
|
||||
|
||||
```bash
|
||||
nohup /opt/fluent-bit/bin/fluent-bit --config=/etc/fluent-bit/fluent-bit.conf &
|
||||
```
|
||||
|
||||
### 检查服务状态
|
||||
|
||||
```bash
|
||||
# 检查进程
|
||||
ps aux | grep fluent-bit
|
||||
|
||||
# 检查端口
|
||||
netstat -tuln | grep 2020
|
||||
|
||||
# 检查日志
|
||||
tail -f /var/log/fluent-bit/fluent-bit.log
|
||||
```
|
||||
|
||||
## API 接口
|
||||
|
||||
Fluent Bit 提供 HTTP API 用于监控和管理:
|
||||
|
||||
- **根路径**: `http://localhost:2020`
|
||||
- **状态接口**: `http://localhost:2020/api/v1/status`
|
||||
- **指标接口**: `http://localhost:2020/api/v1/metrics`
|
||||
|
||||
## 故障排除
|
||||
|
||||
### 常见问题
|
||||
|
||||
1. **端口被占用**
|
||||
- 检查端口 2020 是否被其他服务占用
|
||||
- 修改配置文件中的端口设置
|
||||
|
||||
2. **权限问题**
|
||||
- 确保 fluent-bit 用户有足够的权限访问日志文件
|
||||
- 检查目录权限设置
|
||||
|
||||
3. **配置文件错误**
|
||||
- 检查配置文件语法
|
||||
- 查看日志文件中的错误信息
|
||||
|
||||
### 日志查看
|
||||
|
||||
```bash
|
||||
# 查看服务日志
|
||||
tail -f /var/log/fluent-bit/fluent-bit.log
|
||||
|
||||
# 查看系统日志
|
||||
journalctl -u fluent-bit -f
|
||||
```
|
||||
|
||||
## 系统要求
|
||||
|
||||
- **操作系统**: Ubuntu/Debian/CentOS/RHEL/Fedora
|
||||
- **架构**: x86_64/amd64
|
||||
- **权限**: root 权限(用于安装和卸载)
|
||||
- **依赖**: curl(用于健康检查)
|
||||
|
||||
## 版本信息
|
||||
|
||||
- **Fluent Bit 版本**: 3.1.9
|
||||
- **安装包版本**: 1.0
|
||||
- **支持架构**: amd64
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 安装前请确保系统已更新
|
||||
2. 卸载时会保留 fluent-bit 用户(系统用户,可能被其他服务使用)
|
||||
3. 配置文件包含环境变量,请根据实际环境调整
|
||||
4. 建议在生产环境使用前进行充分测试
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
69
src/metric/client-plugins/all-in-one-full/plugins/fluent-bit/check_health.sh
Executable file
69
src/metric/client-plugins/all-in-one-full/plugins/fluent-bit/check_health.sh
Executable file
@@ -0,0 +1,69 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Fluent Bit 健康检查脚本
|
||||
# 输出 JSON 格式结果
|
||||
|
||||
set -e
|
||||
|
||||
# 检查 Fluent Bit 健康状态
|
||||
check_health() {
|
||||
local name="fluent-bit"
|
||||
local status="unhealth"
|
||||
local reason=""
|
||||
local install_record="/opt/argus-metric/current/.install_record"
|
||||
|
||||
# 首先尝试通过安装记录文件检查进程
|
||||
if [[ -f "$install_record" ]]; then
|
||||
# 尝试使用jq解析JSON格式的安装记录文件
|
||||
local pid=""
|
||||
if command -v jq &> /dev/null; then
|
||||
pid=$(jq -r '.components."fluent-bit".pid // empty' "$install_record" 2>/dev/null || echo "")
|
||||
else
|
||||
# 如果没有jq,使用简单的文本解析方法
|
||||
pid=$(grep -A 10 '"fluent-bit"' "$install_record" | grep '"pid"' | cut -d'"' -f4 | head -1)
|
||||
fi
|
||||
|
||||
if [[ -n "$pid" && "$pid" =~ ^[0-9]+$ ]]; then
|
||||
if kill -0 "$pid" 2>/dev/null; then
|
||||
# 进程存在且运行正常
|
||||
status="health"
|
||||
reason="进程运行正常 (PID: $pid)"
|
||||
echo "{\"name\": \"$name\", \"status\": \"$status\", \"reason\": \"$reason\"}"
|
||||
exit 0
|
||||
else
|
||||
reason="安装记录中的 PID $pid 进程不存在"
|
||||
echo "{\"name\": \"$name\", \"status\": \"$status\", \"reason\": \"$reason\"}"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
reason="安装记录文件中未找到有效的 fluent-bit PID"
|
||||
echo "{\"name\": \"$name\", \"status\": \"$status\", \"reason\": \"$reason\"}"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
# 如果安装记录文件不存在,尝试查找 fluent-bit 进程
|
||||
local pids=$(pgrep -f "fluent-bit" 2>/dev/null || true)
|
||||
if [[ -n "$pids" ]]; then
|
||||
# 取第一个找到的 PID
|
||||
local pid=$(echo "$pids" | head -1)
|
||||
status="health"
|
||||
reason="发现 fluent-bit 进程运行 (PID: $pid),但未找到安装记录"
|
||||
echo "{\"name\": \"$name\", \"status\": \"$status\", \"reason\": \"$reason\"}"
|
||||
exit 0
|
||||
else
|
||||
reason="未找到 fluent-bit 进程,且安装记录文件不存在"
|
||||
echo "{\"name\": \"$name\", \"status\": \"$status\", \"reason\": \"$reason\"}"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
check_health
|
||||
}
|
||||
|
||||
# 脚本入口
|
||||
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||
main "$@"
|
||||
fi
|
||||
@@ -0,0 +1,37 @@
|
||||
[SERVICE]
|
||||
Daemon Off
|
||||
Parsers_File parsers.conf
|
||||
HTTP_Server On
|
||||
HTTP_Listen 0.0.0.0
|
||||
HTTP_Port 2020
|
||||
storage.path /buffers
|
||||
storage.sync normal
|
||||
storage.checksum on
|
||||
storage.backlog.mem_limit 128M
|
||||
# 备注:该镜像默认未开启 Hot Reload,修改配置后请重启容器。
|
||||
|
||||
@INCLUDE inputs.d/*.conf
|
||||
|
||||
[FILTER]
|
||||
Name parser
|
||||
Match app.*
|
||||
Key_Name log
|
||||
Parser timestamp_parser
|
||||
Reserve_Data On
|
||||
Preserve_Key On
|
||||
Unescape_Key On
|
||||
|
||||
[FILTER]
|
||||
Name record_modifier
|
||||
Match *
|
||||
Record cluster ${CLUSTER}
|
||||
Record rack ${RACK}
|
||||
Record host ${HOSTNAME}
|
||||
|
||||
[FILTER]
|
||||
Name lua
|
||||
Match app.*
|
||||
script inject_labels.lua
|
||||
call add_labels
|
||||
|
||||
@INCLUDE outputs.d/*.conf
|
||||
@@ -0,0 +1,15 @@
|
||||
function add_labels(tag, ts, record)
|
||||
record["job_id"] = os.getenv("FB_JOB_ID") or record["job_id"] or "unknown"
|
||||
record["user"] = os.getenv("FB_USER") or record["user"] or "unknown"
|
||||
record["model"] = os.getenv("FB_MODEL") or record["model"] or "unknown"
|
||||
record["gpu_id"] = os.getenv("FB_GPU_ID") or record["gpu_id"] or "na"
|
||||
local p = record["log_path"] or ""
|
||||
if string.find(p, "/logs/infer/") then
|
||||
record["role"] = "infer"
|
||||
elseif string.find(p, "/logs/train/") then
|
||||
record["role"] = "train"
|
||||
else
|
||||
record["role"] = record["role"] or "app"
|
||||
end
|
||||
return 1, ts, record
|
||||
end
|
||||
@@ -0,0 +1,10 @@
|
||||
[INPUT]
|
||||
Name tail
|
||||
Path /logs/train/*.log
|
||||
Tag app.train
|
||||
Path_Key log_path
|
||||
Refresh_Interval 5
|
||||
DB /buffers/train.db
|
||||
Skip_Long_Lines On
|
||||
storage.type filesystem
|
||||
multiline.parser python,go,java
|
||||
@@ -0,0 +1,10 @@
|
||||
[INPUT]
|
||||
Name tail
|
||||
Path /logs/infer/*.log
|
||||
Tag app.infer
|
||||
Path_Key log_path
|
||||
Refresh_Interval 5
|
||||
DB /buffers/infer.db
|
||||
Skip_Long_Lines On
|
||||
storage.type filesystem
|
||||
multiline.parser python,go,java
|
||||
@@ -0,0 +1,24 @@
|
||||
# 重要:使用 Logstash_Format + Logstash_Prefix,生成 train-*/infer-* 索引
|
||||
[OUTPUT]
|
||||
Name es
|
||||
Match app.train
|
||||
Host ${ES_HOST:-localhost}
|
||||
Port ${ES_PORT:-9200}
|
||||
Logstash_Format On
|
||||
Logstash_Prefix train
|
||||
Replace_Dots On
|
||||
Generate_ID On
|
||||
Retry_Limit False
|
||||
Suppress_Type_Name On
|
||||
|
||||
[OUTPUT]
|
||||
Name es
|
||||
Match app.infer
|
||||
Host ${ES_HOST:-localhost}
|
||||
Port ${ES_PORT:-9200}
|
||||
Logstash_Format On
|
||||
Logstash_Prefix infer
|
||||
Replace_Dots On
|
||||
Generate_ID On
|
||||
Retry_Limit False
|
||||
Suppress_Type_Name On
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user