#!/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 git-репозитория (обязателен вне клона) # --dir куда клонировать (по умолчанию /opt/monitoring) # --branch
ветка (по умолчанию ветка по умолчанию репо) 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 (или запусти скрипт из клона репозитория)" 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 done read -r -s -p "METRICS_PASSWORD (basic auth на Prometheus/Alertmanager/node_exporter; ПУСТО допустимо только для tailnet-серверов): " METRICS_PASSWORD /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