From 80dbf686b08fb28c688ef06a3fa0943704988f5b Mon Sep 17 00:00:00 2001 From: Sergey Antropoff Date: Sat, 25 Apr 2026 11:11:18 +0300 Subject: [PATCH] =?UTF-8?q?feat:=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D1=82=D1=8C=20=D0=B0=D0=B4=D0=B4=D0=BE=D0=BD=D1=8B=20postgresq?= =?UTF-8?q?l,=20mysql,=20databasus,=20minio,=20velero,=20crowdsec?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Базы данных: - 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 --- Makefile | 29 ++++++ addons/crowdsec/playbook.yml | 7 ++ addons/crowdsec/role/defaults/main.yml | 54 +++++++++++ addons/crowdsec/role/tasks/main.yml | 80 ++++++++++++++++ addons/databasus/playbook.yml | 7 ++ addons/databasus/role/defaults/main.yml | 39 ++++++++ addons/databasus/role/tasks/main.yml | 82 ++++++++++++++++ addons/minio/playbook.yml | 7 ++ addons/minio/role/defaults/main.yml | 38 ++++++++ addons/minio/role/tasks/main.yml | 77 +++++++++++++++ addons/mysql/playbook.yml | 7 ++ addons/mysql/role/defaults/main.yml | 29 ++++++ addons/mysql/role/tasks/main.yml | 65 +++++++++++++ addons/postgresql/playbook.yml | 7 ++ addons/postgresql/role/defaults/main.yml | 29 ++++++ addons/postgresql/role/tasks/main.yml | 65 +++++++++++++ addons/velero/playbook.yml | 7 ++ addons/velero/role/defaults/main.yml | 37 ++++++++ addons/velero/role/tasks/main.yml | 116 +++++++++++++++++++++++ group_vars/all/addons.yml | 71 ++++++++++++++ group_vars/all/vault.yml.example | 32 +++++++ playbooks/addons.yml | 48 ++++++++++ 22 files changed, 933 insertions(+) create mode 100644 addons/crowdsec/playbook.yml create mode 100644 addons/crowdsec/role/defaults/main.yml create mode 100644 addons/crowdsec/role/tasks/main.yml create mode 100644 addons/databasus/playbook.yml create mode 100644 addons/databasus/role/defaults/main.yml create mode 100644 addons/databasus/role/tasks/main.yml create mode 100644 addons/minio/playbook.yml create mode 100644 addons/minio/role/defaults/main.yml create mode 100644 addons/minio/role/tasks/main.yml create mode 100644 addons/mysql/playbook.yml create mode 100644 addons/mysql/role/defaults/main.yml create mode 100644 addons/mysql/role/tasks/main.yml create mode 100644 addons/postgresql/playbook.yml create mode 100644 addons/postgresql/role/defaults/main.yml create mode 100644 addons/postgresql/role/tasks/main.yml create mode 100644 addons/velero/playbook.yml create mode 100644 addons/velero/role/defaults/main.yml create mode 100644 addons/velero/role/tasks/main.yml diff --git a/Makefile b/Makefile index 2940026..3dae367 100644 --- a/Makefile +++ b/Makefile @@ -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//playbook.yml addon-%: _check_env _check_image @if [ ! -f "addons/$*/playbook.yml" ]; then \ diff --git a/addons/crowdsec/playbook.yml b/addons/crowdsec/playbook.yml new file mode 100644 index 0000000..b9a2a49 --- /dev/null +++ b/addons/crowdsec/playbook.yml @@ -0,0 +1,7 @@ +--- +- name: Install CrowdSec + hosts: k3s_master[0] + gather_facts: false + become: true + roles: + - role: "{{ playbook_dir }}/role" diff --git a/addons/crowdsec/role/defaults/main.yml b/addons/crowdsec/role/defaults/main.yml new file mode 100644 index 0000000..4881738 --- /dev/null +++ b/addons/crowdsec/role/defaults/main.yml @@ -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 diff --git a/addons/crowdsec/role/tasks/main.yml b/addons/crowdsec/role/tasks/main.yml new file mode 100644 index 0000000..bbd6efe --- /dev/null +++ b/addons/crowdsec/role/tasks/main.yml @@ -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 %}" + - "══════════════════════════════════════════════" diff --git a/addons/databasus/playbook.yml b/addons/databasus/playbook.yml new file mode 100644 index 0000000..292aa00 --- /dev/null +++ b/addons/databasus/playbook.yml @@ -0,0 +1,7 @@ +--- +- name: Install Databasus + hosts: k3s_master[0] + gather_facts: false + become: true + roles: + - role: "{{ playbook_dir }}/role" diff --git a/addons/databasus/role/defaults/main.yml b/addons/databasus/role/defaults/main.yml new file mode 100644 index 0000000..5bbdbbf --- /dev/null +++ b/addons/databasus/role/defaults/main.yml @@ -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 diff --git a/addons/databasus/role/tasks/main.yml b/addons/databasus/role/tasks/main.yml new file mode 100644 index 0000000..19f5cc6 --- /dev/null +++ b/addons/databasus/role/tasks/main.yml @@ -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 %}" + - "══════════════════════════════════════════════" diff --git a/addons/minio/playbook.yml b/addons/minio/playbook.yml new file mode 100644 index 0000000..f5f0cbb --- /dev/null +++ b/addons/minio/playbook.yml @@ -0,0 +1,7 @@ +--- +- name: Install MinIO + hosts: k3s_master[0] + gather_facts: false + become: true + roles: + - role: "{{ playbook_dir }}/role" diff --git a/addons/minio/role/defaults/main.yml b/addons/minio/role/defaults/main.yml new file mode 100644 index 0000000..6b2edf6 --- /dev/null +++ b/addons/minio/role/defaults/main.yml @@ -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 diff --git a/addons/minio/role/tasks/main.yml b/addons/minio/role/tasks/main.yml new file mode 100644 index 0000000..dda5848 --- /dev/null +++ b/addons/minio/role/tasks/main.yml @@ -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" + - "══════════════════════════════════════════════" diff --git a/addons/mysql/playbook.yml b/addons/mysql/playbook.yml new file mode 100644 index 0000000..95a1234 --- /dev/null +++ b/addons/mysql/playbook.yml @@ -0,0 +1,7 @@ +--- +- name: Install MySQL + hosts: k3s_master[0] + gather_facts: false + become: true + roles: + - role: "{{ playbook_dir }}/role" diff --git a/addons/mysql/role/defaults/main.yml b/addons/mysql/role/defaults/main.yml new file mode 100644 index 0000000..7651fd7 --- /dev/null +++ b/addons/mysql/role/defaults/main.yml @@ -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 diff --git a/addons/mysql/role/tasks/main.yml b/addons/mysql/role/tasks/main.yml new file mode 100644 index 0000000..c380bb4 --- /dev/null +++ b/addons/mysql/role/tasks/main.yml @@ -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" + - "══════════════════════════════════════════════" diff --git a/addons/postgresql/playbook.yml b/addons/postgresql/playbook.yml new file mode 100644 index 0000000..e6f9413 --- /dev/null +++ b/addons/postgresql/playbook.yml @@ -0,0 +1,7 @@ +--- +- name: Install PostgreSQL + hosts: k3s_master[0] + gather_facts: false + become: true + roles: + - role: "{{ playbook_dir }}/role" diff --git a/addons/postgresql/role/defaults/main.yml b/addons/postgresql/role/defaults/main.yml new file mode 100644 index 0000000..2a7f518 --- /dev/null +++ b/addons/postgresql/role/defaults/main.yml @@ -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 diff --git a/addons/postgresql/role/tasks/main.yml b/addons/postgresql/role/tasks/main.yml new file mode 100644 index 0000000..27e7c5e --- /dev/null +++ b/addons/postgresql/role/tasks/main.yml @@ -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" + - "══════════════════════════════════════════════" diff --git a/addons/velero/playbook.yml b/addons/velero/playbook.yml new file mode 100644 index 0000000..46ebcf2 --- /dev/null +++ b/addons/velero/playbook.yml @@ -0,0 +1,7 @@ +--- +- name: Install Velero + hosts: k3s_master[0] + gather_facts: false + become: true + roles: + - role: "{{ playbook_dir }}/role" diff --git a/addons/velero/role/defaults/main.yml b/addons/velero/role/defaults/main.yml new file mode 100644 index 0000000..734d9aa --- /dev/null +++ b/addons/velero/role/defaults/main.yml @@ -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 diff --git a/addons/velero/role/tasks/main.yml b/addons/velero/role/tasks/main.yml new file mode 100644 index 0000000..78f41a7 --- /dev/null +++ b/addons/velero/role/tasks/main.yml @@ -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" + - "══════════════════════════════════════════════" diff --git a/group_vars/all/addons.yml b/group_vars/all/addons.yml index 7020821..79bae3d 100644 --- a/group_vars/all/addons.yml +++ b/group_vars/all/addons.yml @@ -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 # сколько снимков хранить diff --git a/group_vars/all/vault.yml.example b/group_vars/all/vault.yml.example index c9aef8a..35bc16e 100644 --- a/group_vars/all/vault.yml.example +++ b/group_vars/all/vault.yml.example @@ -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: "" diff --git a/playbooks/addons.yml b/playbooks/addons.yml index 3a2e965..2f9ce5a 100644 --- a/playbooks/addons.yml +++ b/playbooks/addons.yml @@ -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"