Add Makefile as the entry point for manual operation

Knowing how to run something required reading ansible/README.md and
remembering to source .env first. Provide `make help` instead, with
targets grouped by purpose and pattern rules for the repetitive families:
deploy-%, dry-%, update-% and play-%.

.env is sourced automatically; targets that need Proxmox credentials fail
with an actionable message when it is missing. Destructive targets --
mihomo-harden, which rotates live credentials, the frozen monitoring
stack, and update-all -- require CONFIRM=1.

The interpreter is resolved at runtime rather than hardcoded to .venv:
the repository's venv is currently broken, so the Makefile falls back to
whatever is on PATH, which is what the Nix devshell provides.

gen-inventory-docs.py prints the host and group tables from
ansible-inventory, so documentation can be regenerated instead of being
maintained by hand and drifting.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GTocXkGUUazHdKKd3r9k71
This commit is contained in:
Dmitry
2026-08-26 22:10:29 +03:00
co-authored by Claude Opus 5
parent d535ef2d32
commit ef234b17f5
2 changed files with 437 additions and 0 deletions
+241
View File
@@ -0,0 +1,241 @@
# HomeLab Ansible — единая точка входа для РУЧНОГО управления инфраструктурой.
#
# Запускать из каталога ansible/ (или через `make -C ansible <цель>`):
#
# make список целей
# make check базовая проверка связности
# make deploy-gitea playbooks/pve-gitea.yml с загруженным .env
# make dry-gitea то же самое в режиме --check --diff
# make update-gitea playbooks/gitea-update.yml
# make docs markdown-таблица хостов в stdout
#
# Дополнительные флаги ansible-playbook передаются через EXTRA:
#
# make check EXTRA="--limit pve_nodes -vv"
#
# Интерпретатор определяется автоматически: если рядом есть ./.venv — берётся он,
# иначе бинарь ищется в PATH (nix devshell, системный ansible). Любой путь можно
# переопределить переменной окружения, например:
#
# ANSIBLE_PLAYBOOK=$(which ansible-playbook) make check
SHELL := /bin/sh
.DEFAULT_GOAL := help
MAKEFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST)))
ANSIBLE_DIR := $(patsubst %/,%,$(dir $(MAKEFILE_PATH)))
VENV_BIN := $(ANSIBLE_DIR)/.venv/bin
# Разрешение бинарей: сначала рабочий локальный venv, иначе PATH (nix devshell,
# системный ansible). VENV_OK проверяет, что интерпретатор venv реально запускается,
# — иначе сломанный .venv (например, после смены системного python) молча ломал бы
# все цели. Переменные окружения имеют приоритет благодаря `?=`.
VENV_OK := $(shell [ -x '$(VENV_BIN)/python3' ] && '$(VENV_BIN)/python3' -c '' >/dev/null 2>&1 && echo yes)
venv_bin = $(if $(VENV_OK),$(if $(wildcard $(VENV_BIN)/$(1)),$(VENV_BIN)/$(1),$(1)),$(1))
ANSIBLE_PLAYBOOK ?= $(call venv_bin,ansible-playbook)
ANSIBLE_INVENTORY_BIN ?= $(call venv_bin,ansible-inventory)
ANSIBLE_GALAXY ?= $(call venv_bin,ansible-galaxy)
ANSIBLE_LINT ?= $(call venv_bin,ansible-lint)
YAMLLINT ?= $(call venv_bin,yamllint)
# scripts/gen-inventory-docs.py обходится стандартной библиотекой, поэтому берём
# python3 из PATH, а не из .venv.
PYTHON ?= python3
ENV_FILE ?= .env
# Дополнительные аргументы ansible-playbook для любой цели.
EXTRA ?=
# Спрашивать sudo-пароль там, где нужен become. `make openvpn ASK_BECOME=` отключает.
ASK_BECOME ?= -K
# Спрашивать пароль Ansible Vault. `make gyro ASK_VAULT=` отключает.
ASK_VAULT ?= --ask-vault-pass
# Строгая загрузка ansible/.env: обязательна для Proxmox API и секретов.
# Каждая строка рецепта make — отдельный шелл, поэтому source и запуск идут одной строкой.
REQUIRE_ENV = if [ ! -f '$(ENV_FILE)' ]; then \
printf 'ОШИБКА: не найден %s/%s\n' '$(ANSIBLE_DIR)' '$(ENV_FILE)' >&2; \
printf 'Создай его и заполни реальными значениями:\n' >&2; \
printf ' cp .env.example .env\n' >&2; \
printf 'Нужны PROXMOX_* (и MONITORING_* / EMERGENCY_* для профильных целей).\n' >&2; \
exit 1; \
fi; \
set -a; . './$(ENV_FILE)'; set +a
# Мягкая загрузка: .env подхватывается если есть, иначе просто предупреждение.
LOAD_ENV = if [ -f '$(ENV_FILE)' ]; then set -a; . './$(ENV_FILE)'; set +a; \
else printf 'ВНИМАНИЕ: %s не найден, продолжаю без него.\n' '$(ENV_FILE)' >&2; fi
# Защита от случайного запуска опасных плейбуков.
REQUIRE_CONFIRM = if [ "$(CONFIRM)" != "1" ]; then \
printf 'ОПАСНАЯ ЦЕЛЬ. Повтори явно: make $@ CONFIRM=1\n' >&2; \
exit 1; \
fi
# host_vars/gyro/vault.yml зашифрован Vault. Для чтения инвентаря он не нужен,
# поэтому vars-плагины отключаются, чтобы make не спрашивал пароль Vault.
INVENTORY_ENV = ANSIBLE_VARS_ENABLED= ANSIBLE_NOCOLOR=1
##@ Справка
.PHONY: help
help: ## Показать этот список целей
@awk 'BEGIN { FS = ":.*##"; \
print ""; \
print "HomeLab Ansible — ручное управление инфраструктурой"; \
print ""; \
print " Использование: make <цель> [EXTRA=\"--limit host -vv\"]"; \
print " Плейбуки Proxmox API сами подхватывают ./.env"; \
} \
/^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5); next } \
/^[a-zA-Z0-9_%.-]+:.*##/ { printf " \033[36m%-24s\033[0m %s\n", $$1, $$2 } \
END { print "" }' $(MAKEFILE_PATH)
##@ Setup
.PHONY: setup
setup: ## Создать .venv, поставить requirements.txt и galaxy-коллекции (не нужно в nix)
$(PYTHON) -m venv $(ANSIBLE_DIR)/.venv
$(VENV_BIN)/pip install --upgrade pip
$(VENV_BIN)/pip install -r requirements.txt
$(VENV_BIN)/ansible-galaxy collection install -r requirements.yml -p collections
.PHONY: collections
collections: ## Доустановить только galaxy-коллекции из requirements.yml
$(ANSIBLE_GALAXY) collection install -r requirements.yml -p collections
.PHONY: env-check
env-check: ## Проверить наличие .env и заполненность ключевых переменных
@$(REQUIRE_ENV); \
rc=0; \
for v in PROXMOX_HOST PROXMOX_USER PROXMOX_TOKEN_ID PROXMOX_TOKEN_SECRET; do \
eval "val=\$$$$v"; \
if [ -z "$$val" ] || [ "$$val" = 'replace-me' ]; then \
printf 'не задано: %s\n' "$$v"; rc=1; \
else printf 'ok: %s\n' "$$v"; fi; \
done; \
exit $$rc
##@ Проверки и диагностика
.PHONY: check
check: ## Проверка связности и ожидаемых IP (playbooks/check.yml)
$(ANSIBLE_PLAYBOOK) playbooks/check.yml $(EXTRA)
.PHONY: status
status: ## Сводный статус инфраструктуры (playbooks/status.yml)
ANSIBLE_CALLBACK_RESULT_FORMAT=yaml $(ANSIBLE_PLAYBOOK) playbooks/status.yml $(EXTRA)
.PHONY: lint
lint: ## Прогнать ansible-lint и yamllint по репозиторию
$(ANSIBLE_LINT)
$(YAMLLINT) .
.PHONY: docs
docs: ## Напечатать markdown-таблицу хостов и групп в stdout
@ANSIBLE_INVENTORY_BIN='$(ANSIBLE_INVENTORY_BIN)' $(PYTHON) scripts/gen-inventory-docs.py
.PHONY: inventory
inventory: ## Показать дерево инвентаря (ansible-inventory --graph)
@$(INVENTORY_ENV) $(ANSIBLE_INVENTORY_BIN) -i inventory/hosts.yml --graph
.PHONY: openvpn-check
openvpn-check: ## Проверить OpenVPN-транспорт ru-vps <-> ovpn-mini
$(ANSIBLE_PLAYBOOK) playbooks/openvpn-check.yml $(EXTRA)
.PHONY: backup-audit
backup-audit: ## Аудит свежести бэкапов PBS и offsite restic
$(ANSIBLE_PLAYBOOK) playbooks/backup-audit.yml $(EXTRA)
dry-%: ## Прогон playbooks/pve-<имя>.yml в режиме --check --diff (пример: make dry-gitea)
@$(REQUIRE_ENV); $(ANSIBLE_PLAYBOOK) playbooks/pve-$*.yml --check --diff $(EXTRA)
##@ Деплой LXC/VM через Proxmox API (требует .env)
deploy-%: ## Применить playbooks/pve-<имя>.yml (пример: make deploy-gitea, deploy-adguard, deploy-monitoring)
@$(REQUIRE_ENV); $(ANSIBLE_PLAYBOOK) playbooks/pve-$*.yml $(EXTRA)
.PHONY: backup-jobs
backup-jobs: ## Настроить PBS backup jobs (playbooks/pve-backup-jobs.yml)
@$(REQUIRE_ENV); $(ANSIBLE_PLAYBOOK) playbooks/pve-backup-jobs.yml $(EXTRA)
.PHONY: bootstrap-pve-token
bootstrap-pve-token: ## Выпустить Proxmox API-токен прямо с ноды (нужен sudo)
$(ANSIBLE_PLAYBOOK) playbooks/bootstrap-pve-api-token.yml $(ASK_BECOME) $(EXTRA)
.PHONY: bootstrap-monitoring-token
bootstrap-monitoring-token: ## Выпустить read-only PVE-токен для мониторинга
$(ANSIBLE_PLAYBOOK) playbooks/bootstrap-monitoring-pve-token.yml $(EXTRA)
.PHONY: bootstrap-ansible-user
bootstrap-ansible-user: ## Завести сервисный аккаунт ansible на shell-хостах (нужен sudo)
$(ANSIBLE_PLAYBOOK) playbooks/bootstrap-ansible-user.yml $(ASK_BECOME) $(EXTRA)
##@ Настройка сервисов
.PHONY: reverse-proxy
reverse-proxy: ## Единый reverse-proxy для gitea/vaultwarden/grimmory
@$(LOAD_ENV); $(ANSIBLE_PLAYBOOK) playbooks/reverse-proxy.yml $(EXTRA)
.PHONY: openvpn
openvpn: ## Поднять OpenVPN-транспорт ru-vps <-> ovpn-mini (нужен sudo)
$(ANSIBLE_PLAYBOOK) playbooks/openvpn-vps-mini.yml $(ASK_BECOME) $(EXTRA)
.PHONY: openvpn-laptop
openvpn-laptop: ## Настроить OpenVPN-профиль ноутбука
$(ANSIBLE_PLAYBOOK) playbooks/openvpn-laptop.yml $(EXTRA)
.PHONY: bash-config
bash-config: ## Раскатать единый bash-конфиг на shell_hosts
$(ANSIBLE_PLAYBOOK) playbooks/bash-config.yml $(EXTRA)
.PHONY: user-ssh-key
user-ssh-key: ## Разложить публичный SSH-ключ пользователя на все хосты
$(ANSIBLE_PLAYBOOK) playbooks/user-ssh-key.yml $(EXTRA)
.PHONY: emergency-access
emergency-access: ## Настроить emergency reverse-SSH и Telegram-бота (нужны EMERGENCY_* в .env)
@$(REQUIRE_ENV); $(ANSIBLE_PLAYBOOK) playbooks/emergency-access.yml $(EXTRA)
.PHONY: gyro
gyro: ## Настроить gyro-аллокатор в CT 150 (спрашивает пароль Vault)
@$(REQUIRE_ENV); $(ANSIBLE_PLAYBOOK) playbooks/gyro.yml $(ASK_VAULT) $(EXTRA)
.PHONY: uptime-kuma
uptime-kuma: ## Развернуть/обновить Uptime Kuma на monitoring LXC
$(ANSIBLE_PLAYBOOK) playbooks/uptime-kuma.yml $(EXTRA)
.PHONY: offsite-restic
offsite-restic: ## Настроить offsite restic-бэкапы на Yandex.Disk
@$(LOAD_ENV); $(ANSIBLE_PLAYBOOK) playbooks/offsite-restic-yadisk.yml $(EXTRA)
play-%: ## Запустить произвольный playbooks/<имя>.yml с загруженным .env (пример: make play-check)
@$(LOAD_ENV); $(ANSIBLE_PLAYBOOK) playbooks/$*.yml $(EXTRA)
##@ Обновления сервисов (сначала свежий бэкап, потом апдейт, потом проверка)
update-%: ## Обновить сервис через playbooks/<имя>-update.yml (gitea, vaultwarden, adguard, mihomo, grimmory)
@$(LOAD_ENV); $(ANSIBLE_PLAYBOOK) playbooks/$*-update.yml $(EXTRA)
.PHONY: update-all
update-all: ## Последовательно обновить gitea, vaultwarden, adguard, mihomo, grimmory (требует CONFIRM=1)
@$(REQUIRE_CONFIRM)
$(MAKE) update-vaultwarden
$(MAKE) update-gitea
$(MAKE) update-adguard
$(MAKE) update-mihomo
$(MAKE) update-grimmory
##@ Опасное (только осознанно, требует CONFIRM=1)
.PHONY: mihomo-harden
mihomo-harden: ## РОТИРУЕТ живые SOCKS-креды Mihomo на ru-vps и убирает публичный доступ
@$(REQUIRE_CONFIRM)
@printf 'Ротация кредов необратима для клиентов без отката бэкапа конфига.\n' >&2
$(ANSIBLE_PLAYBOOK) playbooks/ru-vps-mihomo-harden.yml -e ru_vps_mihomo_harden_confirm=true $(EXTRA)
.PHONY: monitoring
monitoring: ## ЗАМОРОЖЕН: стек Prometheus. Запускать только при восстановлении мониторинга
@$(REQUIRE_CONFIRM)
@printf 'monitoring.yml заморожен, пока используется Uptime Kuma.\n' >&2
@$(REQUIRE_ENV); $(ANSIBLE_PLAYBOOK) playbooks/monitoring.yml $(EXTRA)