v1.6.0: Docker, docker-compose, Forgejo CI/CD и откат по тегу
Some checks failed
CI / test (push) Successful in 6m10s
Deploy / deploy (push) Failing after 42s

- Dockerfile + entrypoint (alembic + uvicorn), compose с healthcheck
- .gitea/workflows: ci (pytest), deploy (SSH + compose по тегу v*)
- docs/CICD.md: секреты, pvestandt9, ручной откат через workflow_dispatch

Made-with: Cursor
This commit is contained in:
Alexandr
2026-04-03 09:11:52 +03:00
parent 89b5983526
commit f275260b0d
12 changed files with 245 additions and 3 deletions

13
.dockerignore Normal file
View File

@ -0,0 +1,13 @@
.git
.github
.gitea
.venv
__pycache__
*.pyc
.pytest_cache
.env
.env.*
web/node_modules
web/dist
*.md
!README.md

24
.gitea/workflows/ci.yml Normal file
View File

@ -0,0 +1,24 @@
# Forgejo / Gitea Actions — проверка перед деплоем
name: CI
on:
push:
branches: [main]
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Python 3.12
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Pytest
run: |
pip install -e ".[dev]"
pytest -q

View File

@ -0,0 +1,48 @@
# Деплой на сервер по SSH после пуша тега v* или вручную (в т.ч. откат на старый тег).
name: Deploy
on:
push:
tags:
- "v*"
workflow_dispatch:
inputs:
ref:
description: "Git ref (тег для релиза или отката, напр. v1.5.0 или v1.4.1)"
required: true
default: "main"
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Определить ревизию
id: pick
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
echo "ref=${{ inputs.ref }}" >> "$GITHUB_OUTPUT"
else
echo "ref=${{ github.ref_name }}" >> "$GITHUB_OUTPUT"
fi
- name: SSH — fetch, checkout, docker compose
uses: appleboy/ssh-action@v1.2.0
with:
host: ${{ secrets.DEPLOY_HOST }}
username: ${{ secrets.DEPLOY_USER }}
key: ${{ secrets.DEPLOY_SSH_KEY }}
script_stop: true
command_timeout: 20m
script: |
set -euo pipefail
cd "${{ secrets.DEPLOY_PATH }}"
git fetch origin --tags --prune
git checkout "${{ steps.pick.outputs.ref }}"
if git show-ref --verify --quiet "refs/remotes/origin/${{ steps.pick.outputs.ref }}"; then
git reset --hard "origin/${{ steps.pick.outputs.ref }}"
else
git reset --hard "${{ steps.pick.outputs.ref }}"
fi
docker compose build
docker compose up -d
docker compose ps

View File

@ -2,6 +2,17 @@
Формат: семантическое версионирование `MAJOR.MINOR.PATCH`. Git-теги `v1.0.0`, `v1.1.0` и т.д. — см. [docs/VERSIONING.md](docs/VERSIONING.md). Формат: семантическое версионирование `MAJOR.MINOR.PATCH`. Git-теги `v1.0.0`, `v1.1.0` и т.д. — см. [docs/VERSIONING.md](docs/VERSIONING.md).
## [1.6.0] — 2026-04-03
Docker-образ, `docker-compose.yml`, CI/CD Forgejo/Gitea Actions.
### Добавлено
- **`Dockerfile`**, **`docker-compose.yml`**, **`deploy/entrypoint.sh`** — `alembic upgrade` + `uvicorn` (отключение: `SKIP_ALEMBIC=1`).
- **`.gitea/workflows/ci.yml`** — pytest на push в `main` и PR.
- **`.gitea/workflows/deploy.yml`** — деплой по пушу тега `v*` или вручную; **откат** = тот же workflow с `ref` = старый тег.
- **[docs/CICD.md](docs/CICD.md)** — секреты, подготовка `root@pvestandt9`, порядок релиза и отката.
## [1.5.0] — 2026-04-03 ## [1.5.0] — 2026-04-03
IRM-ядро: инциденты, задачи, эскалации, миграция БД, документация. IRM-ядро: инциденты, задачи, эскалации, миграция БД, документация.

20
Dockerfile Normal file
View File

@ -0,0 +1,20 @@
FROM python:3.12-slim-bookworm
WORKDIR /app
RUN apt-get update \
&& apt-get install -y --no-install-recommends libpq5 \
&& rm -rf /var/lib/apt/lists/*
COPY pyproject.toml README.md alembic.ini ./
COPY alembic ./alembic
COPY onguard24 ./onguard24
COPY deploy/entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh \
&& pip install --no-cache-dir --upgrade pip \
&& pip install --no-cache-dir .
ENV PYTHONUNBUFFERED=1
EXPOSE 8080
ENTRYPOINT ["/entrypoint.sh"]

View File

@ -1,6 +1,6 @@
# onGuard24 # onGuard24
**Версия: 1.5.0** · Модульный монолит на **Python (FastAPI)**: ядро, приём алертов из Grafana, заготовки модулей (дежурства, контакты, светофор), PostgreSQL, проверки Vault / Grafana / Forgejo. **Версия: 1.6.0** · Модульный монолит на **Python (FastAPI)**: ядро, приём алертов из Grafana, заготовки модулей (дежурства, контакты, светофор), PostgreSQL, проверки Vault / Grafana / Forgejo.
| Документ | Назначение | | Документ | Назначение |
|----------|------------| |----------|------------|
@ -11,6 +11,7 @@
| [docs/DOMAIN.md](docs/DOMAIN.md) | Сущности (инцидент, алерт, эскалация), шина событий | | [docs/DOMAIN.md](docs/DOMAIN.md) | Сущности (инцидент, алерт, эскалация), шина событий |
| [docs/MODULES.md](docs/MODULES.md) | Как добавлять модули и подписки на события | | [docs/MODULES.md](docs/MODULES.md) | Как добавлять модули и подписки на события |
| [docs/IRM.md](docs/IRM.md) | Функционал IRM: что делаем, что в Grafana | | [docs/IRM.md](docs/IRM.md) | Функционал IRM: что делаем, что в Grafana |
| [docs/CICD.md](docs/CICD.md) | Forgejo Actions: деплой на сервер, откат по тегу |
**Репозиторий:** [forgejo.pvenode.ru/admin/onGuard24](https://forgejo.pvenode.ru/admin/onGuard24) **Репозиторий:** [forgejo.pvenode.ru/admin/onGuard24](https://forgejo.pvenode.ru/admin/onGuard24)
@ -97,3 +98,11 @@ pytest
``` ```
Покрытие: `/health`, `/api/v1/status`, webhook Grafana; внешние вызовы (Vault, Grafana, Forgejo) в тестах статуса подменяются моками. Покрытие: `/health`, `/api/v1/status`, webhook Grafana; внешние вызовы (Vault, Grafana, Forgejo) в тестах статуса подменяются моками.
## Docker и CI/CD
```bash
docker compose build && docker compose up -d
```
Деплой через **Forgejo Actions** (тег `v*`, SSH на сервер): см. [docs/CICD.md](docs/CICD.md). **Откат:** вручную запустить workflow **Deploy** с полем `ref` = нужный старый тег.

7
deploy/entrypoint.sh Normal file
View File

@ -0,0 +1,7 @@
#!/bin/sh
set -e
cd /app
if [ -n "${DATABASE_URL:-}" ] && [ "${SKIP_ALEMBIC:-0}" != "1" ]; then
alembic upgrade head
fi
exec uvicorn onguard24.main:app --host 0.0.0.0 --port "${PORT:-8080}"

21
docker-compose.yml Normal file
View File

@ -0,0 +1,21 @@
# Прод: на сервере рядом с репозиторием лежит .env (не в git).
# Сборка: docker compose build && docker compose up -d
services:
onguard24:
build: .
image: onguard24:latest
container_name: onguard24
restart: unless-stopped
env_file:
- .env
ports:
- "${ONGUARD_HTTP_PORT:-8080}:8080"
environment:
SKIP_ALEMBIC: ${SKIP_ALEMBIC:-0}
PORT: "8080"
healthcheck:
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8080/health')"]
interval: 30s
timeout: 5s
retries: 3
start_period: 15s

View File

@ -22,9 +22,13 @@ onGuard24/
│ │ └── forgejo_api.py # Forgejo/Gitea API (token + probe/fallback) │ │ └── forgejo_api.py # Forgejo/Gitea API (token + probe/fallback)
│ └── modules/ # IRM: incidents, tasks, escalations, … + registry + ui_support │ └── modules/ # IRM: incidents, tasks, escalations, … + registry + ui_support
├── web/ # Vite + React (опционально) ├── web/ # Vite + React (опционально)
├── Dockerfile
├── docker-compose.yml
├── deploy/entrypoint.sh
├── pyproject.toml ├── pyproject.toml
├── pytest.ini ├── pytest.ini
├── tests/ # pytest: health, status, ingress ├── tests/ # pytest: health, status, ingress
├── .gitea/workflows/ # CI + SSH deploy (Forgejo Actions)
├── CHANGELOG.md ├── CHANGELOG.md
└── docs/ └── docs/
``` ```

85
docs/CICD.md Normal file
View File

@ -0,0 +1,85 @@
# CI/CD (Forgejo / Gitea) и деплой на `pvestandt9`
Цель: **пуш тега `v*`** → автоматический деплой на сервер; **откат** — повторный запуск workflow с другим тегом.
## Что в репозитории
| Файл | Назначение |
|------|------------|
| `.gitea/workflows/ci.yml` | На `push` в `main` и PR: `pytest`. |
| `.gitea/workflows/deploy.yml` | На `push` тега `v*`: SSH на сервер → `git checkout``docker compose build/up`. |
| `Dockerfile` | Образ Python 3.12, `pip install .`, entrypoint: `alembic upgrade` + `uvicorn`. |
| `docker-compose.yml` | Сервис `onguard24`, порт `8080`, `env_file: .env`. |
| `deploy/entrypoint.sh` | Перед стартом: `alembic upgrade head` (отключить: `SKIP_ALEMBIC=1` в `.env`). |
## Включить Actions в Forgejo
1. Админка инстанса или репозитория: включить **Actions**.
2. Зарегистрировать **runner** с меткой `ubuntu-latest` (или изменить `runs-on` в YAML на вашу метку, например `self-hosted`).
3. Если образы `actions/checkout` недоступны, в настройках Actions задайте зеркало GitHub или используйте встроенные экшены Forgejo (см. документацию вашей версии).
## Секреты репозитория
**Настройки репозитория → Actions → Secrets:**
| Секрет | Пример | Описание |
|--------|--------|----------|
| `DEPLOY_HOST` | `pvestandt9` или IP | Хост SSH. |
| `DEPLOY_USER` | `root` | Пользователь SSH. |
| `DEPLOY_SSH_KEY` | содержимое `id_rsa` | Приватный ключ (весь PEM, многострочный). |
| `DEPLOY_PATH` | `/opt/onGuard24` | Каталог **клона git** на сервере (там же `docker-compose.yml`). |
Ключ лучше отдельный **deploy key** только на чтение репозитория или полный доступ, если runner делает только `git fetch`.
## Однократная подготовка сервера (`root@pvestandt9`)
```bash
ssh root@pvestandt9
apt-get update && apt-get install -y git docker.io docker-compose-v2
# или Docker CE по инструкции вашей ОС
mkdir -p /opt/onGuard24
cd /opt
git clone https://forgejo.pvenode.ru/admin/onGuard24.git onGuard24
cd onGuard24
```
Создайте **`/opt/onGuard24/.env`** (как в `.env.example`): `DATABASE_URL`, `HTTP_ADDR` (в контейнере порт задаёт compose; для приложения можно `0.0.0.0:8080`), секреты Grafana и т.д.
Настройте **`git remote`** и доступ (`~/.ssh` deploy key или `git credential`), чтобы **`git fetch` без пароля** работал от пользователя, под которым заходит CI (часто `root`).
Проверка вручную:
```bash
cd /opt/onGuard24
docker compose build && docker compose up -d
curl -s http://127.0.0.1:8080/health
```
При необходимости пробросьте порт наружу (nginx, firewall).
## Релиз (деплой новой версии)
1. Закоммитить код, запушить тег: `git tag v1.6.0 && git push origin v1.6.0`.
2. Workflow **Deploy** запустится сам, на сервере обновится код и пересоберётся контейнер.
## Откат версии (простой процесс)
1. В Forgejo: **Actions → Deploy → Run workflow**.
2. В поле **ref** указать **старый тег**, например `v1.4.1`.
3. Запустить. На сервере выполнится `checkout` и `reset` на этот ref, затем `docker compose build && up -d`.
Убедитесь, что старый тег есть в `origin` (`git push --tags` не удалялся).
## Миграции БД
По умолчанию при каждом старте контейнера выполняется **`alembic upgrade head`**. Если нужно отключить (и гонять миграции вручную), в `.env` на сервере: `SKIP_ALEMBIC=1`.
## Устранение неполадок
- **Runner не берёт job** — проверьте `runs-on` и метки runner.
- **SSH fails** — `ssh -i key root@DEPLOY_HOST` с машины runner; `known_hosts` при необходимости добавьте в экшен (расширение `appleboy/ssh-action` / `ssh-keyscan`).
- **`git checkout` fails** — выполните на сервере `git fetch --tags` вручную, проверьте remote URL и ключ.
- **База недоступна из контейнера** — в `DATABASE_URL` укажите хост, доступный **из Docker** (не `127.0.0.1` хоста, если БД на хосте — используйте IP хоста или `host.docker.internal` где поддерживается).
См. также [VERSIONING.md](VERSIONING.md) и [IRM.md](IRM.md).

View File

@ -1,3 +1,3 @@
"""onGuard24 — модульный монолит (ядро + модули).""" """onGuard24 — модульный монолит (ядро + модули)."""
__version__ = "1.5.0" __version__ = "1.6.0"

View File

@ -1,6 +1,6 @@
[project] [project]
name = "onguard24" name = "onguard24"
version = "1.5.0" version = "1.6.0"
description = "onGuard24 — модульный сервис (аналог IRM)" description = "onGuard24 — модульный сервис (аналог IRM)"
readme = "README.md" readme = "README.md"
requires-python = ">=3.11" requires-python = ">=3.11"