Самодостаточный docker compose стек с деплоем через deploy.sh, bootstrap-установкой с нуля и конфигурацией через .env. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
141 lines
7 KiB
Bash
141 lines
7 KiB
Bash
#!/usr/bin/env bash
|
||
# Установка «с нуля» одной командой: ставит зависимости (git, curl, docker,
|
||
# compose, htpasswd), клонирует/обновляет репозиторий, создаёт .env (интерактивно
|
||
# при первом запуске) и запускает deploy.sh. Идемпотентен — повторный запуск
|
||
# просто подтянет свежий код и переприменит конфигурацию.
|
||
#
|
||
# Использование (на чистом сервере, от root):
|
||
# curl -fsSL https://git.sab-core.ru/ShiZa/server-monitoring/raw/branch/main/bootstrap.sh \
|
||
# | sudo bash -s -- --repo https://git.sab-core.ru/ShiZa/server-monitoring.git
|
||
#
|
||
# Или из уже склонированного репозитория:
|
||
# sudo bash bootstrap.sh
|
||
#
|
||
# Флаги: --repo <url> URL git-репозитория (обязателен вне клона)
|
||
# --dir <path> куда клонировать (по умолчанию /opt/monitoring)
|
||
# --branch <br> ветка (по умолчанию ветка по умолчанию репо)
|
||
set -euo pipefail
|
||
|
||
C_RED=$'\033[31m'; C_GREEN=$'\033[32m'; C_YELLOW=$'\033[33m'; C_RESET=$'\033[0m'
|
||
say() { echo "${C_GREEN}==>${C_RESET} $*"; }
|
||
warn() { echo "${C_YELLOW}[WARN]${C_RESET} $*"; }
|
||
die() { echo "${C_RED}[ERR]${C_RESET} $*" >&2; exit 1; }
|
||
|
||
REPO_URL=""
|
||
TARGET_DIR="/opt/monitoring"
|
||
BRANCH=""
|
||
|
||
while [ $# -gt 0 ]; do
|
||
case "$1" in
|
||
--repo) REPO_URL="${2:?}"; shift 2 ;;
|
||
--dir) TARGET_DIR="${2:?}"; shift 2 ;;
|
||
--branch) BRANCH="${2:?}"; shift 2 ;;
|
||
-h|--help) grep '^#' "$0" | head -20; exit 0 ;;
|
||
*) die "неизвестный аргумент: $1" ;;
|
||
esac
|
||
done
|
||
|
||
[ "$(id -u)" = 0 ] || die "нужны права root: запусти через sudo"
|
||
command -v apt-get >/dev/null 2>&1 || die "поддерживается только Debian/Ubuntu (нужен apt-get)"
|
||
|
||
# --- 1. Базовые пакеты -------------------------------------------------------
|
||
declare -A CMD_PKG=([curl]=curl [git]=git [htpasswd]=apache2-utils)
|
||
MISSING=()
|
||
for cmd in "${!CMD_PKG[@]}"; do
|
||
command -v "$cmd" >/dev/null 2>&1 || MISSING+=("${CMD_PKG[$cmd]}")
|
||
done
|
||
if [ "${#MISSING[@]}" -gt 0 ]; then
|
||
say "Ставлю пакеты: ${MISSING[*]}"
|
||
apt-get update -qq
|
||
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq ca-certificates "${MISSING[@]}"
|
||
fi
|
||
|
||
# --- 2. Docker + compose -----------------------------------------------------
|
||
if ! command -v docker >/dev/null 2>&1; then
|
||
say "Docker не найден — ставлю через get.docker.com"
|
||
curl -fsSL https://get.docker.com | sh
|
||
fi
|
||
if ! docker compose version >/dev/null 2>&1; then
|
||
say "Плагин docker compose не найден — ставлю docker-compose-plugin"
|
||
apt-get update -qq
|
||
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq docker-compose-plugin \
|
||
|| die "не удалось поставить docker-compose-plugin — поставь вручную"
|
||
fi
|
||
systemctl enable --now docker >/dev/null 2>&1 || true
|
||
docker info >/dev/null 2>&1 || die "docker-демон не запущен (systemctl status docker)"
|
||
|
||
# --- 3. Репозиторий ----------------------------------------------------------
|
||
if [ -z "${REPO_URL}" ] && [ -f ./deploy.sh ] && [ -f ./docker-compose.yml ]; then
|
||
# запущен из уже склонированного репозитория
|
||
TARGET_DIR="$(pwd)"
|
||
say "Использую текущий клон: ${TARGET_DIR}"
|
||
if [ -d .git ] && git remote get-url origin >/dev/null 2>&1; then
|
||
git pull --ff-only || warn "git pull не удался (локальные изменения?) — продолжаю с текущим кодом"
|
||
fi
|
||
elif [ -d "${TARGET_DIR}/.git" ]; then
|
||
say "Обновляю ${TARGET_DIR}"
|
||
git -C "${TARGET_DIR}" fetch --all --prune
|
||
if [ -n "${BRANCH}" ]; then git -C "${TARGET_DIR}" checkout "${BRANCH}"; fi
|
||
git -C "${TARGET_DIR}" pull --ff-only || warn "git pull не удался — продолжаю с текущим кодом"
|
||
else
|
||
[ -n "${REPO_URL}" ] || die "укажи --repo <url> (или запусти скрипт из клона репозитория)"
|
||
say "Клонирую ${REPO_URL} → ${TARGET_DIR}"
|
||
if [ -n "${BRANCH}" ]; then
|
||
git clone --branch "${BRANCH}" "${REPO_URL}" "${TARGET_DIR}"
|
||
else
|
||
git clone "${REPO_URL}" "${TARGET_DIR}"
|
||
fi
|
||
fi
|
||
cd "${TARGET_DIR}"
|
||
# стек может лежать в подкаталоге monitoring/ репозитория
|
||
[ -f deploy.sh ] || { [ -f monitoring/deploy.sh ] && cd monitoring; } || die "deploy.sh не найден в репозитории"
|
||
|
||
# --- 4. .env: интерактивное создание при первом запуске ----------------------
|
||
if [ ! -f .env ]; then
|
||
cp .env.example .env
|
||
chmod 600 .env
|
||
if [ -r /dev/tty ] && [ -w /dev/tty ]; then
|
||
say "Первый запуск — настрою .env (значения можно потом поменять: nano .env && bash deploy.sh)"
|
||
|
||
DEFAULT_IP=""
|
||
if command -v tailscale >/dev/null 2>&1; then
|
||
DEFAULT_IP="$(tailscale ip -4 2>/dev/null | head -1 || true)"
|
||
fi
|
||
[ -n "${DEFAULT_IP}" ] || DEFAULT_IP="$(hostname -I 2>/dev/null | awk '{print $1}')"
|
||
echo "Адреса на интерфейсах:" >/dev/tty
|
||
ip -o -4 addr show scope global 2>/dev/null | awk '{print " " $2 ": " $4}' >/dev/tty || true
|
||
|
||
read -r -p "BIND_IP (tailnet или публичный IP) [${DEFAULT_IP}]: " BIND_IP </dev/tty
|
||
BIND_IP="${BIND_IP:-${DEFAULT_IP}}"
|
||
[ -n "${BIND_IP}" ] || die "BIND_IP обязателен"
|
||
|
||
DEFAULT_NAME="$(hostname -s)"
|
||
read -r -p "SERVER_NAME [${DEFAULT_NAME}]: " SERVER_NAME </dev/tty
|
||
SERVER_NAME="${SERVER_NAME:-${DEFAULT_NAME}}"
|
||
|
||
GRAFANA_ADMIN_PASSWORD=""
|
||
while [ -z "${GRAFANA_ADMIN_PASSWORD}" ]; do
|
||
read -r -s -p "Пароль admin для Grafana: " GRAFANA_ADMIN_PASSWORD </dev/tty; echo >/dev/tty
|
||
done
|
||
|
||
read -r -s -p "METRICS_PASSWORD (basic auth на Prometheus/Alertmanager/node_exporter; ПУСТО допустимо только для tailnet-серверов): " METRICS_PASSWORD </dev/tty
|
||
echo >/dev/tty
|
||
[ -n "${METRICS_PASSWORD}" ] || warn "METRICS_PASSWORD пуст — метрики будут без аутентификации"
|
||
|
||
sed -i \
|
||
-e "s|^BIND_IP=.*|BIND_IP=${BIND_IP}|" \
|
||
-e "s|^SERVER_NAME=.*|SERVER_NAME=${SERVER_NAME}|" \
|
||
-e "s|^GRAFANA_ADMIN_PASSWORD=.*|GRAFANA_ADMIN_PASSWORD=${GRAFANA_ADMIN_PASSWORD}|" \
|
||
-e "s|^METRICS_PASSWORD=.*|METRICS_PASSWORD=${METRICS_PASSWORD}|" \
|
||
.env
|
||
say ".env создан. Списки CRITICAL_CONTAINERS / CRITICAL_UNITS и Telegram-токены — правь в $(pwd)/.env"
|
||
else
|
||
warn "Нет терминала для вопросов: создан $(pwd)/.env из шаблона."
|
||
warn "Заполни его (nano .env) и запусти: bash deploy.sh"
|
||
exit 0
|
||
fi
|
||
fi
|
||
|
||
# --- 5. Деплой ---------------------------------------------------------------
|
||
say "Запускаю deploy.sh"
|
||
bash deploy.sh
|