feat: добавить аддоны postgresql, mysql, databasus, minio, velero, crowdsec

Базы данных:
- addons/postgresql: Bitnami PostgreSQL (Helm), vault_postgresql_*
- addons/mysql: Bitnami MySQL (Helm), vault_mysql_*

Объектное хранилище и backup:
- addons/minio: Bitnami MinIO в distributed режиме (4 ноды по умолчанию)
- addons/velero: backup кластера через Velero + MinIO как S3 backend;
  bucket создаётся автоматически через mc Job; daily schedule 02:00

Безопасность:
- addons/crowdsec: CrowdSec LAPI + DaemonSet агенты, мониторит ingress-nginx;
  опциональный nginx bouncer (crowdsec_nginx_bouncer_enabled: true)

Резервное копирование БД:
- addons/databasus: OCI chart, автоматически подключается к addon_postgresql
  и addon_mysql когда те включены (shared endpoint через postgresql_external_host
  и mysql_external_host)

Общее:
- group_vars/all/addons.yml: флаги addon_* + конфиги для всех 6 аддонов;
  shared DB endpoints postgresql_external_host / mysql_external_host
- group_vars/all/vault.yml.example: примеры паролей для всех аддонов
- Makefile: targets addon-postgresql/mysql/databasus/minio/velero/crowdsec
This commit is contained in:
Sergey Antropoff
2026-04-25 11:11:18 +03:00
parent a94039e0f1
commit 80dbf686b0
22 changed files with 933 additions and 0 deletions

View File

@@ -52,6 +52,8 @@ DOCKER_RUN := docker run --rm -it \
addon-ingress-nginx addon-cert-manager addon-nfs-server addon-csi-nfs addon-nfs \
addon-istio addon-prometheus-stack addon-metrics-server \
addon-argocd addon-longhorn addon-kubernetes-dashboard \
addon-postgresql addon-mysql addon-databasus \
addon-minio addon-velero addon-crowdsec \
add-node remove-node \
add-etcd-node remove-etcd-node \
etcd-backup etcd-restore etcd-list-snapshots \
@@ -304,6 +306,33 @@ addon-kubernetes-dashboard: _check_env _check_image ## Установить Kube
@printf "$(CYAN)Устанавливаю Kubernetes Dashboard...$(NC)\n"
$(DOCKER_RUN) addon kubernetes-dashboard $(ARGS)
# ── Базы данных ───────────────────────────────────────────────────────────────
addon-postgresql: _check_env _check_image ## Установить PostgreSQL (Bitnami; ARGS="-e postgresql_storage_size=20Gi")
@printf "$(CYAN)Устанавливаю PostgreSQL...$(NC)\n"
$(DOCKER_RUN) addon postgresql $(ARGS)
addon-mysql: _check_env _check_image ## Установить MySQL (Bitnami; ARGS="-e mysql_storage_size=20Gi")
@printf "$(CYAN)Устанавливаю MySQL...$(NC)\n"
$(DOCKER_RUN) addon mysql $(ARGS)
addon-databasus: _check_env _check_image ## Установить Databasus — управление резервными копиями БД (ARGS="-e databasus_ingress_host=backup.example.com")
@printf "$(CYAN)Устанавливаю Databasus...$(NC)\n"
$(DOCKER_RUN) addon databasus $(ARGS)
# ── Объектное хранилище и backup ──────────────────────────────────────────────
addon-minio: _check_env _check_image ## Установить MinIO — S3 объектное хранилище (ARGS="-e minio_storage_size=20Gi")
@printf "$(CYAN)Устанавливаю MinIO...$(NC)\n"
$(DOCKER_RUN) addon minio $(ARGS)
addon-velero: _check_env _check_image ## Установить Velero — backup кластера + PVC через S3/MinIO
@printf "$(CYAN)Устанавливаю Velero...$(NC)\n"
$(DOCKER_RUN) addon velero $(ARGS)
# ── Безопасность ──────────────────────────────────────────────────────────────
addon-crowdsec: _check_env _check_image ## Установить CrowdSec — обнаружение вторжений (ARGS="-e crowdsec_nginx_bouncer_enabled=true")
@printf "$(CYAN)Устанавливаю CrowdSec...$(NC)\n"
$(DOCKER_RUN) addon crowdsec $(ARGS)
# Generic цель — любой аддон из addons/<name>/playbook.yml
addon-%: _check_env _check_image
@if [ ! -f "addons/$*/playbook.yml" ]; then \

View File

@@ -0,0 +1,7 @@
---
- name: Install CrowdSec
hosts: k3s_master[0]
gather_facts: false
become: true
roles:
- role: "{{ playbook_dir }}/role"

View File

@@ -0,0 +1,54 @@
---
crowdsec_version: "0.10.1"
crowdsec_namespace: "crowdsec"
crowdsec_chart_repo: "https://crowdsecurity.github.io/helm-charts"
# ── Энролмент в CrowdSec Central API (опционально) ───────────────────────────
# Получи ключ на https://app.crowdsec.net → My Instances → +
# Задай в group_vars/all/vault.yml:
# vault_crowdsec_enroll_key: "..." # ключ энролмента (оставь "" для оффлайн-режима)
crowdsec_enroll_key: "{{ vault_crowdsec_enroll_key | default('') }}"
crowdsec_instance_name: "k3s-cluster"
crowdsec_enroll_tags: "k3s kubernetes"
# Container runtime для k3s
crowdsec_container_runtime: "containerd"
# ── Коллекции ─────────────────────────────────────────────────────────────────
# Список коллекций через пробел
crowdsec_collections: "crowdsecurity/linux crowdsecurity/nginx crowdsecurity/kubernetes"
# ── Мониторируемые поды (acquisition) ─────────────────────────────────────────
crowdsec_acquisition:
- namespace: ingress-nginx
podName: "ingress-nginx-controller-*"
program: nginx
# ── Nginx Bouncer (remediation component) ─────────────────────────────────────
# Блокирует IP через nginx auth_request — требует отдельного деплоя
# Инструкция в debug после установки
crowdsec_nginx_bouncer_enabled: false
crowdsec_nginx_bouncer_version: "0.1.6"
# ── LAPI — постоянное хранилище ───────────────────────────────────────────────
crowdsec_lapi_storage_enabled: true
crowdsec_lapi_storage_size: "1Gi"
crowdsec_lapi_storage_class: ""
# ── Ресурсы LAPI ───────────────────────────────────────────────────────────────
crowdsec_lapi_resources:
requests:
cpu: 100m
memory: 100Mi
limits:
cpu: 500m
memory: 256Mi
# ── Ресурсы агентов (DaemonSet) ───────────────────────────────────────────────
crowdsec_agent_resources:
requests:
cpu: 100m
memory: 100Mi
limits:
cpu: 500m
memory: 256Mi

View File

@@ -0,0 +1,80 @@
---
- name: Add CrowdSec Helm repo
kubernetes.core.helm_repository:
name: crowdsec
repo_url: "{{ crowdsec_chart_repo }}"
environment:
KUBECONFIG: "{{ k3s_kubeconfig_path }}"
- name: Deploy CrowdSec via Helm
kubernetes.core.helm:
name: crowdsec
chart_ref: crowdsec/crowdsec
chart_version: "{{ crowdsec_version }}"
release_namespace: "{{ crowdsec_namespace }}"
create_namespace: true
wait: true
timeout: "10m0s"
values:
container_runtime: "{{ crowdsec_container_runtime }}"
lapi:
env: >-
{{
([{'name': 'ENROLL_KEY', 'value': crowdsec_enroll_key},
{'name': 'ENROLL_INSTANCE_NAME', 'value': crowdsec_instance_name},
{'name': 'ENROLL_TAGS', 'value': crowdsec_enroll_tags}])
if crowdsec_enroll_key
else []
}}
persistentVolume:
config:
enabled: "{{ crowdsec_lapi_storage_enabled | bool }}"
size: "{{ crowdsec_lapi_storage_size }}"
storageClassName: "{{ crowdsec_lapi_storage_class }}"
resources: "{{ crowdsec_lapi_resources }}"
agent:
acquisition: "{{ crowdsec_acquisition }}"
env:
- name: COLLECTIONS
value: "{{ crowdsec_collections }}"
resources: "{{ crowdsec_agent_resources }}"
environment:
KUBECONFIG: "{{ k3s_kubeconfig_path }}"
- name: Deploy CrowdSec nginx bouncer
kubernetes.core.helm:
name: crowdsec-nginx-bouncer
chart_ref: crowdsec/crowdsec-nginx-bouncer
chart_version: "{{ crowdsec_nginx_bouncer_version }}"
release_namespace: "{{ crowdsec_namespace }}"
create_namespace: false
wait: true
timeout: "5m0s"
values:
bouncer:
crowdsec_lapi_url: "http://crowdsec-service.{{ crowdsec_namespace }}.svc.cluster.local:8080"
environment:
KUBECONFIG: "{{ k3s_kubeconfig_path }}"
when: crowdsec_nginx_bouncer_enabled | bool
- name: Show CrowdSec access info
ansible.builtin.debug:
msg:
- "══════════════════════════════════════════════"
- " CrowdSec установлен"
- "══════════════════════════════════════════════"
- " Namespace: {{ crowdsec_namespace }}"
- " Runtime: {{ crowdsec_container_runtime }}"
- " Коллекции: {{ crowdsec_collections }}"
- "{% if crowdsec_enroll_key %} Статус энролмента: см. https://app.crowdsec.net{% endif %}"
- "──────────────────────────────────────────────"
- " Статус агентов:"
- " kubectl exec -n {{ crowdsec_namespace }} deploy/crowdsec -- cscli metrics"
- " Список решений (бан/разбан):"
- " kubectl exec -n {{ crowdsec_namespace }} deploy/crowdsec -- cscli decisions list"
- " Список алертов:"
- " kubectl exec -n {{ crowdsec_namespace }} deploy/crowdsec -- cscli alerts list"
- "──────────────────────────────────────────────"
- "{% if not crowdsec_nginx_bouncer_enabled %} Nginx bouncer не установлен."
- " Включи: crowdsec_nginx_bouncer_enabled: true{% endif %}"
- "══════════════════════════════════════════════"

View File

@@ -0,0 +1,7 @@
---
- name: Install Databasus
hosts: k3s_master[0]
gather_facts: false
become: true
roles:
- role: "{{ playbook_dir }}/role"

View File

@@ -0,0 +1,39 @@
---
databasus_chart_ref: "oci://ghcr.io/databasus/charts/databasus"
databasus_version: "" # "" = latest; укажи конкретную: "1.0.0"
databasus_namespace: "databasus"
# ── Ingress ────────────────────────────────────────────────────────────────────
databasus_ingress_enabled: true
databasus_ingress_host: "backup.example.com"
databasus_ingress_class: "{{ ingress_nginx_class_name | default('nginx') }}"
databasus_ingress_tls: false
databasus_ingress_cert_issuer: "{{ cert_manager_default_issuer_name | default('letsencrypt-prod') }}"
# ── Интеграция с addon_postgresql ─────────────────────────────────────────────
# Автоматически используется когда addon_postgresql: true.
# Переопредели явно для кастомного PostgreSQL.
databasus_postgresql_enabled: "{{ addon_postgresql | default(false) | bool }}"
databasus_postgresql_host: "{{ postgresql_external_host | default('postgresql.postgresql.svc.cluster.local') }}"
databasus_postgresql_port: "{{ postgresql_external_port | default(5432) }}"
databasus_postgresql_username: "{{ postgresql_auth_username | default('appuser') }}"
databasus_postgresql_password: "{{ postgresql_auth_password | default('') }}"
databasus_postgresql_database: "{{ postgresql_auth_database | default('appdb') }}"
# ── Интеграция с addon_mysql ───────────────────────────────────────────────────
# Автоматически используется когда addon_mysql: true.
databasus_mysql_enabled: "{{ addon_mysql | default(false) | bool }}"
databasus_mysql_host: "{{ mysql_external_host | default('mysql.mysql.svc.cluster.local') }}"
databasus_mysql_port: "{{ mysql_external_port | default(3306) }}"
databasus_mysql_username: "{{ mysql_auth_username | default('appuser') }}"
databasus_mysql_password: "{{ mysql_auth_password | default('') }}"
databasus_mysql_database: "{{ mysql_auth_database | default('appdb') }}"
# ── Ресурсы ────────────────────────────────────────────────────────────────────
databasus_resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi

View File

@@ -0,0 +1,82 @@
---
- name: Build Databasus database connections
ansible.builtin.set_fact:
_databasus_databases: >-
{{
(databasus_postgresql_enabled | bool) | ternary(
[{'type': 'postgresql',
'host': databasus_postgresql_host,
'port': databasus_postgresql_port | int,
'username': databasus_postgresql_username,
'password': databasus_postgresql_password,
'database': databasus_postgresql_database}],
[]
)
+
(databasus_mysql_enabled | bool) | ternary(
[{'type': 'mysql',
'host': databasus_mysql_host,
'port': databasus_mysql_port | int,
'username': databasus_mysql_username,
'password': databasus_mysql_password,
'database': databasus_mysql_database}],
[]
)
}}
- name: Deploy Databasus via Helm (OCI chart)
kubernetes.core.helm:
name: databasus
chart_ref: "{{ databasus_chart_ref }}"
chart_version: "{{ databasus_version if databasus_version else omit }}"
release_namespace: "{{ databasus_namespace }}"
create_namespace: true
wait: true
timeout: "10m0s"
values:
ingress:
enabled: "{{ databasus_ingress_enabled | bool }}"
className: "{{ databasus_ingress_class }}"
hosts:
- host: "{{ databasus_ingress_host }}"
paths:
- path: /
pathType: Prefix
tls: >-
{{ [{'hosts': [databasus_ingress_host],
'secretName': 'databasus-tls'}]
if databasus_ingress_tls | bool else [] }}
annotations: >-
{{ {'cert-manager.io/cluster-issuer': databasus_ingress_cert_issuer}
if databasus_ingress_tls | bool else {} }}
databases: "{{ _databasus_databases }}"
resources: "{{ databasus_resources }}"
environment:
KUBECONFIG: "{{ k3s_kubeconfig_path }}"
- name: Wait for Databasus to be ready
ansible.builtin.command: >
k3s kubectl -n {{ databasus_namespace }}
rollout status deployment/databasus --timeout=180s
become: true
register: _databasus_ready
changed_when: false
retries: 3
delay: 10
until: _databasus_ready.rc == 0
failed_when: false
- name: Show Databasus access info
ansible.builtin.debug:
msg:
- "══════════════════════════════════════════════"
- " Databasus установлен"
- "══════════════════════════════════════════════"
- " Namespace: {{ databasus_namespace }}"
- "{% if databasus_ingress_enabled %} UI: http{{ 's' if databasus_ingress_tls | bool else '' }}://{{ databasus_ingress_host }}{% endif %}"
- " Port-forward: kubectl port-forward -n {{ databasus_namespace }} svc/databasus 8080:80"
- "──────────────────────────────────────────────"
- " Подключённые базы данных:"
- "{% for db in _databasus_databases %} - {{ db.type }}: {{ db.host }}:{{ db.port }} / {{ db.database }}{% endfor %}"
- "{% if _databasus_databases | length == 0 %} (нет — включи addon_postgresql или addon_mysql){% endif %}"
- "══════════════════════════════════════════════"

View File

@@ -0,0 +1,7 @@
---
- name: Install MinIO
hosts: k3s_master[0]
gather_facts: false
become: true
roles:
- role: "{{ playbook_dir }}/role"

View File

@@ -0,0 +1,38 @@
---
minio_version: "14.6.4"
minio_namespace: "minio"
minio_chart_repo: "https://charts.bitnami.com/bitnami"
# ── Авторизация ────────────────────────────────────────────────────────────────
# Задай в group_vars/all/vault.yml:
# vault_minio_root_user: "admin" # логин root
# vault_minio_root_password: "..." # пароль root (мин. 8 символов)
minio_root_user: "{{ vault_minio_root_user | default('minioadmin') }}"
minio_root_password: "{{ vault_minio_root_password | default('changeme-minio') }}"
# ── Режим развёртывания ────────────────────────────────────────────────────────
# distributed — StatefulSet с несколькими репликами (рекомендуется, HA)
# standalone — одна реплика без HA
minio_mode: "distributed"
minio_replicas: 4 # минимум 4 для distributed; каждая получает свой PVC
# ── Хранилище (PVC на каждую реплику) ─────────────────────────────────────────
minio_storage_size: "10Gi"
minio_storage_class: "" # "" = default StorageClass
# ── Ingress (консоль + S3 API) ─────────────────────────────────────────────────
minio_ingress_enabled: false
minio_console_ingress_host: "minio.example.com" # веб-консоль
minio_api_ingress_host: "s3.example.com" # S3 API endpoint
minio_ingress_class: "{{ ingress_nginx_class_name | default('nginx') }}"
minio_ingress_tls: false
minio_ingress_cert_issuer: "{{ cert_manager_default_issuer_name | default('letsencrypt-prod') }}"
# ── Ресурсы (на каждую реплику) ───────────────────────────────────────────────
minio_resources:
requests:
cpu: 250m
memory: 256Mi
limits:
cpu: "1"
memory: 1Gi

View File

@@ -0,0 +1,77 @@
---
- name: Add Bitnami Helm repo
kubernetes.core.helm_repository:
name: bitnami
repo_url: "{{ minio_chart_repo }}"
environment:
KUBECONFIG: "{{ k3s_kubeconfig_path }}"
- name: Deploy MinIO via Helm
kubernetes.core.helm:
name: minio
chart_ref: bitnami/minio
chart_version: "{{ minio_version }}"
release_namespace: "{{ minio_namespace }}"
create_namespace: true
wait: true
timeout: "15m0s"
values:
auth:
rootUser: "{{ minio_root_user }}"
rootPassword: "{{ minio_root_password }}"
mode: "{{ minio_mode }}"
statefulset:
replicaCount: "{{ minio_replicas }}"
persistence:
enabled: true
size: "{{ minio_storage_size }}"
storageClass: "{{ minio_storage_class }}"
ingress:
enabled: "{{ minio_ingress_enabled | bool }}"
ingressClassName: "{{ minio_ingress_class }}"
hostname: "{{ minio_console_ingress_host }}"
tls: "{{ minio_ingress_tls | bool }}"
annotations: >-
{{ {'cert-manager.io/cluster-issuer': minio_ingress_cert_issuer}
if minio_ingress_tls | bool else {} }}
apiIngress:
enabled: "{{ minio_ingress_enabled | bool }}"
ingressClassName: "{{ minio_ingress_class }}"
hostname: "{{ minio_api_ingress_host }}"
tls: "{{ minio_ingress_tls | bool }}"
annotations: >-
{{ {'cert-manager.io/cluster-issuer': minio_ingress_cert_issuer}
if minio_ingress_tls | bool else {} }}
resources: "{{ minio_resources }}"
environment:
KUBECONFIG: "{{ k3s_kubeconfig_path }}"
- name: Wait for MinIO StatefulSet to be ready
ansible.builtin.command: >
k3s kubectl -n {{ minio_namespace }}
rollout status statefulset/minio --timeout=300s
become: true
register: _minio_ready
changed_when: false
retries: 3
delay: 15
until: _minio_ready.rc == 0
failed_when: false
- name: Show MinIO access info
ansible.builtin.debug:
msg:
- "══════════════════════════════════════════════"
- " MinIO установлен (режим: {{ minio_mode }}, реплик: {{ minio_replicas }})"
- "══════════════════════════════════════════════"
- " Namespace: {{ minio_namespace }}"
- " S3 API: minio.{{ minio_namespace }}.svc.cluster.local:9000"
- " Root user: {{ minio_root_user }}"
- "{% if minio_ingress_enabled %} Консоль: http{{ 's' if minio_ingress_tls | bool else '' }}://{{ minio_console_ingress_host }}{% endif %}"
- "{% if minio_ingress_enabled %} S3 endpoint:http{{ 's' if minio_ingress_tls | bool else '' }}://{{ minio_api_ingress_host }}{% endif %}"
- "──────────────────────────────────────────────"
- " Port-forward консоли:"
- " kubectl port-forward -n {{ minio_namespace }} svc/minio 9001:9001"
- " Port-forward S3 API:"
- " kubectl port-forward -n {{ minio_namespace }} svc/minio 9000:9000"
- "══════════════════════════════════════════════"

View File

@@ -0,0 +1,7 @@
---
- name: Install MySQL
hosts: k3s_master[0]
gather_facts: false
become: true
roles:
- role: "{{ playbook_dir }}/role"

View File

@@ -0,0 +1,29 @@
---
mysql_version: "11.1.14"
mysql_namespace: "mysql"
mysql_chart_repo: "https://charts.bitnami.com/bitnami"
# ── База данных ────────────────────────────────────────────────────────────────
# Задай пароли в group_vars/all/vault.yml:
# vault_mysql_root_password: "..." # пароль root
# vault_mysql_password: "..." # пароль пользователя mysql_auth_username
mysql_auth_root_password: "{{ vault_mysql_root_password | default('changeme-root') }}"
mysql_auth_username: "appuser"
mysql_auth_database: "appdb"
mysql_auth_password: "{{ vault_mysql_password | default('changeme-app') }}"
# ── Хранилище ──────────────────────────────────────────────────────────────────
mysql_storage_size: "8Gi"
mysql_storage_class: "" # "" = default StorageClass
# ── Secondary реплики (read-only) ─────────────────────────────────────────────
mysql_secondary_replica_count: 0 # 0 = только primary
# ── Ресурсы ────────────────────────────────────────────────────────────────────
mysql_resources:
requests:
cpu: 250m
memory: 256Mi
limits:
cpu: "1"
memory: 512Mi

View File

@@ -0,0 +1,65 @@
---
- name: Add Bitnami Helm repo
kubernetes.core.helm_repository:
name: bitnami
repo_url: "{{ mysql_chart_repo }}"
environment:
KUBECONFIG: "{{ k3s_kubeconfig_path }}"
- name: Deploy MySQL via Helm
kubernetes.core.helm:
name: mysql
chart_ref: bitnami/mysql
chart_version: "{{ mysql_version }}"
release_namespace: "{{ mysql_namespace }}"
create_namespace: true
wait: true
timeout: "10m0s"
values:
auth:
rootPassword: "{{ mysql_auth_root_password }}"
username: "{{ mysql_auth_username }}"
password: "{{ mysql_auth_password }}"
database: "{{ mysql_auth_database }}"
primary:
persistence:
enabled: true
size: "{{ mysql_storage_size }}"
storageClass: "{{ mysql_storage_class }}"
resources: "{{ mysql_resources }}"
secondary:
replicaCount: "{{ mysql_secondary_replica_count }}"
persistence:
enabled: "{{ mysql_secondary_replica_count > 0 }}"
size: "{{ mysql_storage_size }}"
storageClass: "{{ mysql_storage_class }}"
resources: "{{ mysql_resources }}"
environment:
KUBECONFIG: "{{ k3s_kubeconfig_path }}"
- name: Wait for MySQL primary to be ready
ansible.builtin.command: >
k3s kubectl -n {{ mysql_namespace }}
rollout status statefulset/mysql-primary --timeout=180s
become: true
register: _mysql_ready
changed_when: false
retries: 3
delay: 15
until: _mysql_ready.rc == 0
- name: Show MySQL access info
ansible.builtin.debug:
msg:
- "══════════════════════════════════════════════"
- " MySQL установлен"
- "══════════════════════════════════════════════"
- " Namespace: {{ mysql_namespace }}"
- " Service: mysql.{{ mysql_namespace }}.svc.cluster.local:3306"
- " Database: {{ mysql_auth_database }}"
- " Root: root / {{ mysql_auth_root_password }}"
- " App user: {{ mysql_auth_username }} / {{ mysql_auth_password }}"
- "──────────────────────────────────────────────"
- " Port-forward для локального доступа:"
- " kubectl port-forward -n {{ mysql_namespace }} svc/mysql 3306:3306"
- "══════════════════════════════════════════════"

View File

@@ -0,0 +1,7 @@
---
- name: Install PostgreSQL
hosts: k3s_master[0]
gather_facts: false
become: true
roles:
- role: "{{ playbook_dir }}/role"

View File

@@ -0,0 +1,29 @@
---
postgresql_version: "16.2.1"
postgresql_namespace: "postgresql"
postgresql_chart_repo: "https://charts.bitnami.com/bitnami"
# ── База данных ────────────────────────────────────────────────────────────────
# Задай пароли в group_vars/all/vault.yml:
# vault_postgresql_postgres_password: "..." # пароль суперпользователя postgres
# vault_postgresql_password: "..." # пароль пользователя postgresql_auth_username
postgresql_auth_username: "appuser"
postgresql_auth_database: "appdb"
postgresql_auth_postgres_password: "{{ vault_postgresql_postgres_password | default('changeme-postgres') }}"
postgresql_auth_password: "{{ vault_postgresql_password | default('changeme-app') }}"
# ── Хранилище ──────────────────────────────────────────────────────────────────
postgresql_storage_size: "8Gi"
postgresql_storage_class: "" # "" = default StorageClass
# ── Репликация (standby реплики для HA) ───────────────────────────────────────
postgresql_replica_count: 0 # 0 = только primary; >=1 = primary + replicas
# ── Ресурсы ────────────────────────────────────────────────────────────────────
postgresql_resources:
requests:
cpu: 250m
memory: 256Mi
limits:
cpu: "1"
memory: 512Mi

View File

@@ -0,0 +1,65 @@
---
- name: Add Bitnami Helm repo
kubernetes.core.helm_repository:
name: bitnami
repo_url: "{{ postgresql_chart_repo }}"
environment:
KUBECONFIG: "{{ k3s_kubeconfig_path }}"
- name: Deploy PostgreSQL via Helm
kubernetes.core.helm:
name: postgresql
chart_ref: bitnami/postgresql
chart_version: "{{ postgresql_version }}"
release_namespace: "{{ postgresql_namespace }}"
create_namespace: true
wait: true
timeout: "10m0s"
values:
auth:
username: "{{ postgresql_auth_username }}"
password: "{{ postgresql_auth_password }}"
database: "{{ postgresql_auth_database }}"
postgresPassword: "{{ postgresql_auth_postgres_password }}"
primary:
persistence:
enabled: true
size: "{{ postgresql_storage_size }}"
storageClass: "{{ postgresql_storage_class }}"
resources: "{{ postgresql_resources }}"
readReplicas:
replicaCount: "{{ postgresql_replica_count }}"
persistence:
enabled: "{{ postgresql_replica_count > 0 }}"
size: "{{ postgresql_storage_size }}"
storageClass: "{{ postgresql_storage_class }}"
resources: "{{ postgresql_resources }}"
environment:
KUBECONFIG: "{{ k3s_kubeconfig_path }}"
- name: Wait for PostgreSQL primary to be ready
ansible.builtin.command: >
k3s kubectl -n {{ postgresql_namespace }}
rollout status statefulset/postgresql-primary --timeout=180s
become: true
register: _pg_ready
changed_when: false
retries: 3
delay: 15
until: _pg_ready.rc == 0
- name: Show PostgreSQL access info
ansible.builtin.debug:
msg:
- "══════════════════════════════════════════════"
- " PostgreSQL установлен"
- "══════════════════════════════════════════════"
- " Namespace: {{ postgresql_namespace }}"
- " Service: postgresql.{{ postgresql_namespace }}.svc.cluster.local:5432"
- " Database: {{ postgresql_auth_database }}"
- " Superuser: postgres / {{ postgresql_auth_postgres_password }}"
- " App user: {{ postgresql_auth_username }} / {{ postgresql_auth_password }}"
- "──────────────────────────────────────────────"
- " Port-forward для локального доступа:"
- " kubectl port-forward -n {{ postgresql_namespace }} svc/postgresql 5432:5432"
- "══════════════════════════════════════════════"

View File

@@ -0,0 +1,7 @@
---
- name: Install Velero
hosts: k3s_master[0]
gather_facts: false
become: true
roles:
- role: "{{ playbook_dir }}/role"

View File

@@ -0,0 +1,37 @@
---
velero_version: "7.0.0"
velero_namespace: "velero"
velero_chart_repo: "https://vmware-tanzu.github.io/helm-charts"
# AWS plugin для S3-совместимых хранилищ (MinIO)
velero_aws_plugin_version: "v1.9.0"
# ── S3/MinIO backend ──────────────────────────────────────────────────────────
# Задай в group_vars/all/vault.yml:
# vault_velero_s3_access_key: "..." # логин MinIO (обычно = vault_minio_root_user)
# vault_velero_s3_secret_key: "..." # пароль MinIO
velero_s3_access_key: "{{ vault_velero_s3_access_key | default('minioadmin') }}"
velero_s3_secret_key: "{{ vault_velero_s3_secret_key | default('changeme-minio') }}"
velero_s3_bucket: "velero"
velero_s3_region: "minio"
velero_s3_url: "http://minio.minio.svc.cluster.local:9000"
velero_s3_force_path_style: true
velero_s3_public_url: ""
velero_mc_image: "minio/mc:latest"
# ── Расписание резервного копирования ─────────────────────────────────────────
velero_schedule_enabled: true
velero_schedule_name: "daily-backup"
velero_schedule_cron: "0 2 * * *" # каждый день в 02:00
velero_schedule_ttl: "720h" # хранить 30 дней
# ── Ресурсы ────────────────────────────────────────────────────────────────────
velero_resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi

View File

@@ -0,0 +1,116 @@
---
- name: Add vmware-tanzu Helm repo
kubernetes.core.helm_repository:
name: vmware-tanzu
repo_url: "{{ velero_chart_repo }}"
environment:
KUBECONFIG: "{{ k3s_kubeconfig_path }}"
- name: Create velero bucket in MinIO
kubernetes.core.k8s:
state: present
definition:
apiVersion: batch/v1
kind: Job
metadata:
name: velero-bucket-init
namespace: "{{ velero_namespace }}"
spec:
ttlSecondsAfterFinished: 300
template:
spec:
restartPolicy: OnFailure
containers:
- name: mc
image: "{{ velero_mc_image }}"
command:
- /bin/sh
- -c
- |
mc alias set backend "{{ velero_s3_url }}" "$KEY" "$SECRET" &&
mc mb --ignore-existing "backend/{{ velero_s3_bucket }}"
env:
- name: KEY
value: "{{ velero_s3_access_key }}"
- name: SECRET
value: "{{ velero_s3_secret_key }}"
environment:
KUBECONFIG: "{{ k3s_kubeconfig_path }}"
- name: Deploy Velero via Helm
kubernetes.core.helm:
name: velero
chart_ref: vmware-tanzu/velero
chart_version: "{{ velero_version }}"
release_namespace: "{{ velero_namespace }}"
create_namespace: true
wait: true
timeout: "10m0s"
values:
initContainers:
- name: velero-plugin-for-aws
image: "velero/velero-plugin-for-aws:{{ velero_aws_plugin_version }}"
imagePullPolicy: IfNotPresent
volumeMounts:
- mountPath: /target
name: plugins
credentials:
useSecret: true
secretContents:
cloud: |
[default]
aws_access_key_id={{ velero_s3_access_key }}
aws_secret_access_key={{ velero_s3_secret_key }}
configuration:
backupStorageLocation:
- name: default
provider: aws
bucket: "{{ velero_s3_bucket }}"
default: true
config:
region: "{{ velero_s3_region }}"
s3ForcePathStyle: "{{ velero_s3_force_path_style | string }}"
s3Url: "{{ velero_s3_url }}"
publicUrl: "{{ velero_s3_public_url }}"
volumeSnapshotLocation:
- name: default
provider: aws
config:
region: "{{ velero_s3_region }}"
resources: "{{ velero_resources }}"
environment:
KUBECONFIG: "{{ k3s_kubeconfig_path }}"
- name: Create daily backup schedule
kubernetes.core.k8s:
state: present
definition:
apiVersion: velero.io/v1
kind: Schedule
metadata:
name: "{{ velero_schedule_name }}"
namespace: "{{ velero_namespace }}"
spec:
schedule: "{{ velero_schedule_cron }}"
template:
ttl: "{{ velero_schedule_ttl }}"
storageLocation: default
environment:
KUBECONFIG: "{{ k3s_kubeconfig_path }}"
when: velero_schedule_enabled | bool
- name: Show Velero access info
ansible.builtin.debug:
msg:
- "══════════════════════════════════════════════"
- " Velero установлен"
- "══════════════════════════════════════════════"
- " Namespace: {{ velero_namespace }}"
- " S3 endpoint:{{ velero_s3_url }}"
- " S3 bucket: {{ velero_s3_bucket }}"
- "{% if velero_schedule_enabled %} Расписание: {{ velero_schedule_cron }} (TTL: {{ velero_schedule_ttl }}){% endif %}"
- "──────────────────────────────────────────────"
- " Ручной backup: velero backup create my-backup --wait"
- " Список backups: velero backup get"
- " Восстановление: velero restore create --from-backup my-backup"
- "══════════════════════════════════════════════"

View File

@@ -15,6 +15,12 @@ addon_istio: false # Istio service mesh + Kiali UI
addon_argocd: false # ArgoCD (GitOps)
addon_longhorn: false # Longhorn (distributed block storage)
addon_kubernetes_dashboard: false # Kubernetes Dashboard
addon_postgresql: false # PostgreSQL (Bitnami)
addon_mysql: false # MySQL (Bitnami)
addon_databasus: false # Databasus (резервное копирование БД)
addon_minio: false # MinIO (S3-совместимое объектное хранилище)
addon_velero: false # Velero (резервное копирование кластера и PVC)
addon_crowdsec: false # CrowdSec (обнаружение вторжений)
# ─── NFS Server ───────────────────────────────────────────────────────────────
nfs_exports:
@@ -106,6 +112,71 @@ cert_manager_acme_email: "admin@example.com"
# kiali_enabled: false
# kiali_ingress_host: "kiali.example.com"
# ─── PostgreSQL ───────────────────────────────────────────────────────────────
# Пароли задаются в group_vars/all/vault.yml:
# vault_postgresql_postgres_password: "..."
# vault_postgresql_password: "..."
# postgresql_auth_username: "appuser"
# postgresql_auth_database: "appdb"
# postgresql_storage_size: "8Gi"
# postgresql_storage_class: "" # "" = default StorageClass
# postgresql_replica_count: 0 # 0 = только primary
# Shared endpoint — используется другими аддонами когда addon_postgresql: true
postgresql_external_host: "postgresql.postgresql.svc.cluster.local"
postgresql_external_port: 5432
# ─── MySQL ────────────────────────────────────────────────────────────────────
# Пароли задаются в group_vars/all/vault.yml:
# vault_mysql_root_password: "..."
# vault_mysql_password: "..."
# mysql_auth_username: "appuser"
# mysql_auth_database: "appdb"
# mysql_storage_size: "8Gi"
# mysql_storage_class: "" # "" = default StorageClass
# mysql_secondary_replica_count: 0 # 0 = только primary
# Shared endpoint — используется другими аддонами когда addon_mysql: true
mysql_external_host: "mysql.mysql.svc.cluster.local"
mysql_external_port: 3306
# ─── Databasus ────────────────────────────────────────────────────────────────
# Databasus автоматически получает подключение к PostgreSQL/MySQL
# если соответствующий аддон включён (addon_postgresql/addon_mysql: true).
databasus_ingress_enabled: true
databasus_ingress_host: "backup.example.com"
# databasus_ingress_tls: false
# databasus_version: "" # "" = latest
# ─── MinIO ────────────────────────────────────────────────────────────────────
# Логин/пароль задаются в vault.yml:
# vault_minio_root_user: "admin"
# vault_minio_root_password: "..."
# minio_mode: "standalone" # standalone | distributed
# minio_storage_size: "10Gi"
# minio_storage_class: "" # "" = default StorageClass
minio_ingress_enabled: false
minio_console_ingress_host: "minio.example.com"
minio_api_ingress_host: "s3.example.com"
# minio_ingress_tls: false
# ─── Velero ───────────────────────────────────────────────────────────────────
# Credentials задаются в vault.yml:
# vault_velero_s3_access_key: "..." # логин MinIO (обычно = vault_minio_root_user)
# vault_velero_s3_secret_key: "..." # пароль MinIO
# velero_s3_url: "http://minio.minio.svc.cluster.local:9000" # endpoint addon_minio
# velero_s3_bucket: "velero"
# velero_schedule_enabled: true
# velero_schedule_cron: "0 2 * * *" # каждый день в 02:00
# velero_schedule_ttl: "720h" # хранить 30 дней
# ─── CrowdSec ─────────────────────────────────────────────────────────────────
# Ключ энролмента задаётся в vault.yml (необязательно):
# vault_crowdsec_enroll_key: "..." # с https://app.crowdsec.net
# crowdsec_instance_name: "k3s-cluster"
# crowdsec_collections: "crowdsecurity/linux crowdsecurity/nginx crowdsecurity/kubernetes"
# crowdsec_nginx_bouncer_enabled: false
# ─── etcd backup ──────────────────────────────────────────────────────────────
etcd_backup_dir: "{{ k3s_data_dir }}/server/db/snapshots"
etcd_backup_retention: 5 # сколько снимков хранить

View File

@@ -11,4 +11,36 @@
# ansible-playbook site.yml --ask-vault-pass
# ansible-playbook site.yml --vault-password-file ~/.vault_pass
# ─── K3S ──────────────────────────────────────────────────────────────────────
vault_k3s_token: "ЗАМЕНИ_НА_НАДЁЖНЫЙ_СЕКРЕТНЫЙ_ТОКЕН_МИН_32_СИМВОЛА"
# ─── PostgreSQL ────────────────────────────────────────────────────────────────
vault_postgresql_postgres_password: "changeme-postgres" # пароль суперпользователя postgres
vault_postgresql_password: "changeme-app" # пароль пользователя postgresql_auth_username
# ─── MySQL ─────────────────────────────────────────────────────────────────────
vault_mysql_root_password: "changeme-root" # пароль root
vault_mysql_password: "changeme-app" # пароль пользователя mysql_auth_username
# ─── Prometheus / Grafana ──────────────────────────────────────────────────────
vault_grafana_user: "admin"
vault_grafana_password: "changeme-grafana"
# ─── Istio / Kiali ─────────────────────────────────────────────────────────────
# Заполняется после первой установки Istio:
# kubectl -n istio-system create token kiali --duration=8760h
vault_kiali_token: ""
# ─── MinIO ─────────────────────────────────────────────────────────────────────
vault_minio_root_user: "admin"
vault_minio_root_password: "changeme-minio"
# ─── Velero (использует addon_minio как S3 backend) ───────────────────────────
# Обычно те же значения что vault_minio_root_user / vault_minio_root_password
vault_velero_s3_access_key: "admin" # = vault_minio_root_user
vault_velero_s3_secret_key: "changeme-minio" # = vault_minio_root_password
# ─── CrowdSec ──────────────────────────────────────────────────────────────────
# Ключ энролмента — получить на https://app.crowdsec.net → My Instances → +
# Оставь пустым для оффлайн-режима (без Central API)
vault_crowdsec_enroll_key: ""

View File

@@ -87,3 +87,51 @@
when: addon_kubernetes_dashboard | default(false) | bool
roles:
- role: "{{ playbook_dir }}/../addons/kubernetes-dashboard/role"
- name: Install PostgreSQL
hosts: k3s_master[0]
gather_facts: false
become: true
when: addon_postgresql | default(false) | bool
roles:
- role: "{{ playbook_dir }}/../addons/postgresql/role"
- name: Install MySQL
hosts: k3s_master[0]
gather_facts: false
become: true
when: addon_mysql | default(false) | bool
roles:
- role: "{{ playbook_dir }}/../addons/mysql/role"
- name: Install Databasus
hosts: k3s_master[0]
gather_facts: false
become: true
when: addon_databasus | default(false) | bool
roles:
- role: "{{ playbook_dir }}/../addons/databasus/role"
- name: Install MinIO
hosts: k3s_master[0]
gather_facts: false
become: true
when: addon_minio | default(false) | bool
roles:
- role: "{{ playbook_dir }}/../addons/minio/role"
- name: Install Velero
hosts: k3s_master[0]
gather_facts: false
become: true
when: addon_velero | default(false) | bool
roles:
- role: "{{ playbook_dir }}/../addons/velero/role"
- name: Install CrowdSec
hosts: k3s_master[0]
gather_facts: false
become: true
when: addon_crowdsec | default(false) | bool
roles:
- role: "{{ playbook_dir }}/../addons/crowdsec/role"