Подготовить безопасный production rollout
This commit is contained in:
@@ -58,53 +58,95 @@
|
||||
Полный локальный сброс БД выполняется командой `docker compose down -v`, но она удалит
|
||||
все локальные данные. Обычная остановка без удаления данных: `docker compose down`.
|
||||
|
||||
## 2. Первый запуск в production
|
||||
## 2. Обновление существующего production
|
||||
|
||||
1. Подготовьте K3s/Kubernetes-кластер и убедитесь, что `kubectl get nodes` работает.
|
||||
PostgreSQL в текущие манифесты не входит: заранее создайте доступную из кластера БД для
|
||||
каждого университета. Пользователь БД должен иметь права на создание и изменение схемы —
|
||||
Flyway применит `V1__init.sql` автоматически.
|
||||
Простой push в `main` для первого обновления **недостаточен**. Он выполняет проверки,
|
||||
собирает и сканирует образы, но job `deploy-to-k8s` запускается только вручную через
|
||||
`workflow_dispatch`. Новые файлы `../k8s` CI не применяет. Gitea 1.25 игнорирует
|
||||
`environment` и `concurrency`, поэтому они не используются как production-защита.
|
||||
|
||||
2. Настройте DNS для `magistr.zuev.company` и остальных tenant-доменов на публичный
|
||||
reverse proxy/Ingress. В текущем `../k8s/ingress.yaml` нет секции TLS: для HTTPS нужно
|
||||
настроить внешний Caddy с сертификатом либо добавить cert-manager/TLS в кластер.
|
||||
1. Подготовьте секреты по [`docs/SECURITY_RUNBOOK.md`](docs/SECURITY_RUNBOOK.md):
|
||||
|
||||
3. Создайте namespace и четыре обязательных Secret по
|
||||
[`docs/SECURITY_RUNBOOK.md`](docs/SECURITY_RUNBOOK.md):
|
||||
- `app-secret`: новый случайный `JWT_SECRET` не короче 32 байт, `POSTGRES_USER`,
|
||||
`POSTGRES_PASSWORD`;
|
||||
- `tenants-secret`: новый `tenants.json` с актуальными JDBC URL и ротированными паролями;
|
||||
- `otel-postgres-secret`: восемь обязательных параметров подключений Collector;
|
||||
- `gitea-registry`: доступ K3s к registry.
|
||||
|
||||
- `app-secret`: `JWT_SECRET`, `POSTGRES_USER`, `POSTGRES_PASSWORD`;
|
||||
- `tenants-secret`: файл `tenants.json`;
|
||||
- `otel-postgres-secret`: подключения Collector к БД;
|
||||
- `gitea-registry`: доступ к приватным Docker-образам.
|
||||
Для `magistr.zuev.company` нужен tenant с `"domain": "magistr"`, для
|
||||
`n8n.zuev.company` — `"domain": "n8n"`. Секреты не сохраняйте в Git или CI-логах.
|
||||
|
||||
Для `magistr.zuev.company` запись в `tenants.json` должна иметь `"domain": "magistr"`
|
||||
и реальный JDBC URL. Не сохраняйте значения Secret в Git или логах.
|
||||
2. Выполните локальные проверки, затем отправьте изменения в `main`:
|
||||
|
||||
4. В Gitea Actions добавьте секреты `ZUEV_TOKEN` (доступ к registry) и
|
||||
`KUBECONFIG_DATA` (kubeconfig в Base64). Push в `main` соберёт, проверит и опубликует
|
||||
образы. При самом первом запуске автоматический deploy может ещё не найти Deployment —
|
||||
возьмите два опубликованных digest из CI/registry и выполните bootstrap вручную:
|
||||
```bash
|
||||
K8S_DIR=../k8s bash scripts/check-production-secrets.sh
|
||||
K8S_DIR=../k8s bash scripts/check-artifact-pinning.sh
|
||||
bash scripts/test-deploy-images.sh
|
||||
kubectl kustomize ../k8s >/dev/null
|
||||
```
|
||||
|
||||
Дождитесь успешных checks, сборки и Trivy scan. Скопируйте точные backend/frontend
|
||||
ссылки вида `image@sha256:...` из CI/registry.
|
||||
|
||||
3. Объявите техническое окно и временно закройте публичный доступ во внешнем Caddy. Затем
|
||||
остановите backend:
|
||||
|
||||
```bash
|
||||
kubectl scale deployment/backend -n magistr --replicas=0
|
||||
```
|
||||
|
||||
Полностью пересоздайте **каждую** PostgreSQL tenant-БД, указанную в новом
|
||||
`tenants.json`. Недостаточно удалить `flyway_schema_history` или отдельные таблицы:
|
||||
миграции V2–V7 объединены в новую `V1__init.sql`, поэтому старая БД получит checksum
|
||||
error, а непустая БД без истории может быть ошибочно принята Flyway за baseline.
|
||||
Пользователь БД должен уметь создавать схему, функции, триггеры и расширения
|
||||
`pgcrypto`/`btree_gist`.
|
||||
|
||||
4. С рабочей станции с актуальным каталогом `../k8s` выполните первый rollout вручную:
|
||||
|
||||
```bash
|
||||
export BACKEND_IMAGE_REF='gitea.zuev.company/zuev/magistr-backend@sha256:<digest>'
|
||||
export FRONTEND_IMAGE_REF='gitea.zuev.company/zuev/magistr-frontend@sha256:<digest>'
|
||||
K8S_DIR=../k8s bash scripts/check-production-secrets.sh
|
||||
K8S_DIR=../k8s bash scripts/check-artifact-pinning.sh
|
||||
kubectl kustomize ../k8s >/dev/null
|
||||
bash ../k8s/deploy.sh apply
|
||||
```
|
||||
|
||||
5. Проверьте результат:
|
||||
Скрипт применит ConfigMap, RBAC, Secret mount, probes, OTel и Ingress, поднимет две
|
||||
реплики backend и дождётся Flyway/readiness. CI deploy после push не ожидает
|
||||
подтверждения: он будет пропущен. Следующие image-only обновления можно запускать
|
||||
вручную кнопкой `Run workflow`.
|
||||
|
||||
5. Перед открытием публичного доступа проверьте:
|
||||
|
||||
```bash
|
||||
bash ../k8s/deploy.sh status
|
||||
kubectl rollout status deployment/backend -n magistr --timeout=300s
|
||||
kubectl rollout status deployment/frontend -n magistr --timeout=120s
|
||||
kubectl get pods -n magistr
|
||||
kubectl logs -n magistr -l app=backend --tail=200
|
||||
curl -fsS https://magistr.zuev.company/
|
||||
curl -fsSI https://magistr.zuev.company/
|
||||
```
|
||||
|
||||
После первого входа немедленно смените/отключите все тестовые пароли из `V1__init.sql`.
|
||||
Следующие push в `main` CI/CD сможет развёртывать автоматически.
|
||||
Проверьте в браузере login → reload → refresh → logout для каждого tenant-домена.
|
||||
Refresh-cookie должна иметь `HttpOnly`, `Secure`, `SameSite=Lax`, path `/api/auth`;
|
||||
access JWT не должен находиться в Local/Session Storage. До снятия технического окна
|
||||
смените или отключите все демонстрационные учётные записи из `V1__init.sql`.
|
||||
|
||||
6. После проверки удалите старый `ConfigMap/tenants-config`, отзовите старые DB/OTel
|
||||
пароли и верните обычный доступ через Caddy. В дальнейшем image-only изменения можно
|
||||
разворачивать ручным запуском workflow, но любые изменения `../k8s` по-прежнему нужно
|
||||
применять отдельно или переносить этот каталог в управляемый CI репозиторий.
|
||||
|
||||
### Важные ограничения rollback
|
||||
|
||||
- Смена `JWT_SECRET` и сброс БД завершат все старые пользовательские сессии.
|
||||
- Readiness требует доступности и успешной миграции всех tenant-БД; одна старая или
|
||||
недоступная БД остановит rollout.
|
||||
- Автоматический rollback возвращает только два image reference. Он не откатывает БД,
|
||||
Secret, ConfigMap или RBAC; после применения новой V1 безопаснее исправлять проблему
|
||||
вперёд либо снова пересоздавать БД под старую версию.
|
||||
- CI не запускает Testcontainers integration tests: полный PostgreSQL-прогон нужно
|
||||
повторить вручную, если после зафиксированных 300 успешных тестов код ещё менялся.
|
||||
- `TRUSTED_PROXY_CIDRS=10.42.0.0/16` соответствует текущей pod-сети K3s. При смене
|
||||
cluster CIDR его нужно обновить, иначе rate limit увидит неверные клиентские IP.
|
||||
|
||||
## 3. Что могу выполнить я
|
||||
|
||||
|
||||
Reference in New Issue
Block a user