#!/usr/bin/env bash set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" PKG_ROOT="$ROOT_DIR" ENV_EX="$PKG_ROOT/compose/.env.example" ENV_OUT="$PKG_ROOT/compose/.env" info(){ echo -e "\033[34m[CONFIG]\033[0m $*"; } err(){ echo -e "\033[31m[ERROR]\033[0m $*" >&2; } require(){ local ok=1; for c in "$@"; do command -v "$c" >/dev/null 2>&1 || { err "缺少依赖: $c"; ok=0; }; done; [[ $ok -eq 1 ]]; } # Compose 检测:优先 docker compose(v2),回退 docker-compose(v1) require_compose(){ if docker compose version >/dev/null 2>&1; then return 0; fi if command -v docker-compose >/dev/null 2>&1 && docker-compose version >/dev/null 2>&1; then return 0; fi err "未检测到 Docker Compose,请安装 docker compose v2 或 docker-compose v1"; exit 1 } require docker awk sed require_compose [[ -f "$ENV_EX" ]] || { err "缺少模板文件: $ENV_EX"; exit 1; } # 磁盘空间检查(简化版,默认 >= 5GB) check_disk(){ local p="$1"; local need="${2:-5120}"; local free; free=$(df -Pm "$p" 2>/dev/null | awk 'NR==2{print $4+0}') if [[ -z "$free" || "$free" -lt "$need" ]]; then err "磁盘空间不足: $p 剩余 ${free:-0}MB (<${need}MB),请清理后再继续" fi } check_disk "$PKG_ROOT" 5120 || true check_disk "/var/lib/docker" 5120 || true cp "$ENV_EX" "$ENV_OUT" # 读取/生成 SWARM_MANAGER_ADDR(ARM 下同样引导 Swarm + cluster-info) SWARM_MANAGER_ADDR=${SWARM_MANAGER_ADDR:-} if [[ -z "${SWARM_MANAGER_ADDR}" ]]; then read -rp "请输入本机管理地址 SWARM_MANAGER_ADDR(可填当前主机会被其他节点访问到的 IP 或主机名): " SWARM_MANAGER_ADDR fi info "SWARM_MANAGER_ADDR=$SWARM_MANAGER_ADDR" info "开始分配服务端口(起始=20000,避免系统占用与相互冲突)" is_port_used(){ local p="$1"; ss -tulnH 2>/dev/null | awk '{print $5}' | sed 's/.*://g' | grep -qx "$p"; } declare -A PRESENT=() CHOSEN=() USED=() START_PORT="${START_PORT:-20000}"; cur=$START_PORT ORDER=(MASTER_PORT PROMETHEUS_PORT GRAFANA_PORT ALERTMANAGER_PORT \ WEB_PROXY_PORT_8080 WEB_PROXY_PORT_8081 WEB_PROXY_PORT_8082 \ WEB_PROXY_PORT_8083 WEB_PROXY_PORT_8084 WEB_PROXY_PORT_8085) # 标记 .env.example 中实际存在的键 for key in "${ORDER[@]}"; do if grep -q "^${key}=" "$ENV_EX"; then PRESENT[$key]=1; fi done next_free(){ local p="$1"; while :; do if [[ -n "${USED[$p]:-}" ]] || is_port_used "$p"; then p=$((p+1)); else echo "$p"; return; fi; done; } for key in "${ORDER[@]}"; do [[ -z "${PRESENT[$key]:-}" ]] && continue p=$(next_free "$cur"); CHOSEN[$key]="$p"; USED[$p]=1; cur=$((p+1)) done info "端口分配结果:MASTER=${CHOSEN[MASTER_PORT]:-} PROM=${CHOSEN[PROMETHEUS_PORT]:-} GRAFANA=${CHOSEN[GRAFANA_PORT]:-} ALERT=${CHOSEN[ALERTMANAGER_PORT]:-} WEB_PROXY(8080..8085)=${CHOSEN[WEB_PROXY_PORT_8080]:-}/${CHOSEN[WEB_PROXY_PORT_8081]:-}/${CHOSEN[WEB_PROXY_PORT_8082]:-}/${CHOSEN[WEB_PROXY_PORT_8083]:-}/${CHOSEN[WEB_PROXY_PORT_8084]:-}/${CHOSEN[WEB_PROXY_PORT_8085]:-}" # 覆盖端口(按唯一化结果写回) for key in "${ORDER[@]}"; do val="${CHOSEN[$key]:-}" [[ -z "$val" ]] && continue sed -i -E "s#^$key=.*#$key=${val}#" "$ENV_OUT" done # 覆盖/补充 Overlay 名称 grep -q '^ARGUS_OVERLAY_NET=' "$ENV_OUT" || echo 'ARGUS_OVERLAY_NET=argus-sys-net' >> "$ENV_OUT" # 覆盖/补充构建账户 UID/GID,避免权限问题 RUID=$(id -u) RGID=$(id -g) if grep -q '^ARGUS_BUILD_UID=' "$ENV_OUT"; then sed -i -E "s#^ARGUS_BUILD_UID=.*#ARGUS_BUILD_UID=${RUID}#" "$ENV_OUT" else echo "ARGUS_BUILD_UID=${RUID}" >> "$ENV_OUT" fi if grep -q '^ARGUS_BUILD_GID=' "$ENV_OUT"; then sed -i -E "s#^ARGUS_BUILD_GID=.*#ARGUS_BUILD_GID=${RGID}#" "$ENV_OUT" else echo "ARGUS_BUILD_GID=${RGID}" >> "$ENV_OUT" fi # 更新 cluster-info.env 的 SWARM_MANAGER_ADDR(ARM 版本也保留该机制) CI="$PKG_ROOT/cluster-info.env" if [[ -f "$CI" ]]; then if grep -q '^SWARM_MANAGER_ADDR=' "$CI"; then sed -i -E "s#^SWARM_MANAGER_ADDR=.*#SWARM_MANAGER_ADDR=${SWARM_MANAGER_ADDR}#" "$CI" else echo "SWARM_MANAGER_ADDR=${SWARM_MANAGER_ADDR}" >> "$CI" fi else echo "SWARM_MANAGER_ADDR=${SWARM_MANAGER_ADDR}" > "$CI" fi info "已生成 compose/.env 并更新 cluster-info.env 的 SWARM_MANAGER_ADDR。" info "下一步可执行: scripts/install.sh"