feat: Добавлена система пресетов для Molecule
- Создана система пресетов для быстрого переключения между конфигурациями - Добавлены пресеты: minimal, standard, docker, cluster - Обновлена структура проекта с папками cicd/, vault/, scripts/ - Упрощена система vault с функциональными секретами - Добавлены скрипты для работы с пресетами - Обновлен Makefile с командами для пресетов - Удалены старые файлы и структуры Автор: Сергей Антропов Сайт: https://devops.org.ru
This commit is contained in:
@@ -1,6 +0,0 @@
|
||||
---
|
||||
- name: Converge
|
||||
hosts: all
|
||||
vars_files:
|
||||
- ../../vars/secrets.yml
|
||||
roles:
|
||||
@@ -1,8 +0,0 @@
|
||||
- name: Destroy containers on interrupt
|
||||
hosts: localhost
|
||||
tasks:
|
||||
- name: Ensure containers are destroyed
|
||||
docker_container:
|
||||
name: "{{ item.name }}"
|
||||
state: absent
|
||||
loop: "{{ molecule_yml.platforms }}"
|
||||
@@ -1,61 +0,0 @@
|
||||
---
|
||||
dependency:
|
||||
name: galaxy
|
||||
enabled: false
|
||||
options:
|
||||
requirements-file: requirements.yml
|
||||
|
||||
driver:
|
||||
name: docker
|
||||
|
||||
platforms:
|
||||
- name: centos
|
||||
image: "inecs/ansible:centos"
|
||||
privileged: true
|
||||
pre_build_image: true
|
||||
volumes:
|
||||
- /sys/fs/cgroup:/sys/fs/cgroup:ro
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
tmpfs:
|
||||
- /tmp
|
||||
- /run
|
||||
- name: ubuntu
|
||||
image: "inecs/ansible:ubuntu"
|
||||
privileged: true
|
||||
pre_build_image: true
|
||||
volumes:
|
||||
- /sys/fs/cgroup:/sys/fs/cgroup:ro
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
tmpfs:
|
||||
- /tmp
|
||||
- /run
|
||||
|
||||
provisioner:
|
||||
name: ansible
|
||||
connection_options:
|
||||
ansible_connection: docker
|
||||
ansible_user: root
|
||||
env:
|
||||
ANSIBLE_PYTHON_INTERPRETER: /usr/bin/python3
|
||||
lint:
|
||||
name: ansible-lint
|
||||
|
||||
verifier:
|
||||
name: ansible
|
||||
|
||||
scenario:
|
||||
name: default
|
||||
test_sequence:
|
||||
- dependency
|
||||
- cleanup
|
||||
- destroy
|
||||
- syntax
|
||||
- create
|
||||
- prepare
|
||||
- converge
|
||||
- idempotence
|
||||
- side_effect
|
||||
- verify
|
||||
- cleanup
|
||||
- destroy
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
- name: Prepare
|
||||
hosts: all
|
||||
tasks:
|
||||
- name: Detect OS family
|
||||
ansible.builtin.setup:
|
||||
gather_subset:
|
||||
- "min"
|
||||
|
||||
- name: Обновляем пакеты для работы с Ansible в RockyLinux (Centos/RedHat)
|
||||
when: ansible_facts['os_family'] == "RedHat"
|
||||
block:
|
||||
- name: Устанавливаем репозиторий AppStream (если его нет)
|
||||
ansible.builtin.raw: dnf config-manager --set-enabled appstream
|
||||
changed_when: false
|
||||
|
||||
- name: Установить rsync
|
||||
ansible.builtin.raw: dnf install -y rsync
|
||||
changed_when: false
|
||||
|
||||
- name: Устанавливаем Python 3.8
|
||||
ansible.builtin.raw: dnf install -y python38 python38-pip
|
||||
changed_when: false
|
||||
|
||||
- name: Обновляем символическую ссылку python3
|
||||
ansible.builtin.raw: alternatives --set python /usr/bin/python3.8
|
||||
changed_when: false
|
||||
# - name: Fix repository URLs
|
||||
# ansible.builtin.command:
|
||||
# cmd: sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-*
|
||||
# changed_when: false
|
||||
|
||||
# - name: Update baseurl
|
||||
# ansible.builtin.command:
|
||||
# cmd: sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-*
|
||||
# changed_when: false
|
||||
|
||||
# - name: Install required packages
|
||||
# ansible.builtin.yum:
|
||||
# name:
|
||||
# - epel-release
|
||||
# - python3
|
||||
# - python3-pip
|
||||
# state: present
|
||||
@@ -1,7 +0,0 @@
|
||||
---
|
||||
- name: Prepare
|
||||
hosts: all
|
||||
tasks:
|
||||
- name: Reun verify
|
||||
debug:
|
||||
msg: "Hello, Verify!"
|
||||
@@ -1,132 +0,0 @@
|
||||
---
|
||||
# Проверка работы systemd, docker и docker-compose в образах
|
||||
# Автор: Сергей Антропов
|
||||
# Сайт: https://devops.org.ru
|
||||
|
||||
- name: Verify systemd, docker and docker-compose services
|
||||
hosts: all
|
||||
gather_facts: true
|
||||
tasks:
|
||||
- name: Display OS information
|
||||
debug:
|
||||
msg: "Тестирование на {{ ansible_distribution }} {{ ansible_distribution_version }}"
|
||||
|
||||
- name: Check if systemd is available and running
|
||||
systemd:
|
||||
name: systemd
|
||||
state: started
|
||||
register: systemd_status
|
||||
failed_when: false
|
||||
|
||||
- name: Display systemd status
|
||||
debug:
|
||||
msg: "Systemd статус: {{ 'Доступен и запущен' if systemd_status is succeeded else 'Недоступен или не запущен' }}"
|
||||
|
||||
- name: Check systemd version
|
||||
command: systemd --version
|
||||
register: systemd_version
|
||||
failed_when: false
|
||||
changed_when: false
|
||||
|
||||
- name: Display systemd version
|
||||
debug:
|
||||
msg: "Версия systemd: {{ systemd_version.stdout_lines[0] if systemd_version.stdout_lines else 'Не определена' }}"
|
||||
|
||||
- name: Check if docker service exists
|
||||
stat:
|
||||
path: /usr/bin/docker
|
||||
register: docker_binary
|
||||
|
||||
- name: Check if docker service exists (alternative path)
|
||||
stat:
|
||||
path: /usr/local/bin/docker
|
||||
register: docker_binary_alt
|
||||
|
||||
- name: Display docker binary status
|
||||
debug:
|
||||
msg: "Docker binary: {{ 'Найден в /usr/bin/docker' if docker_binary.stat.exists else ('Найден в /usr/local/bin/docker' if docker_binary_alt.stat.exists else 'Не найден') }}"
|
||||
|
||||
- name: Check docker version
|
||||
command: docker --version
|
||||
register: docker_version
|
||||
failed_when: false
|
||||
changed_when: false
|
||||
|
||||
- name: Display docker version
|
||||
debug:
|
||||
msg: "Версия Docker: {{ docker_version.stdout if docker_version.stdout else 'Docker не установлен' }}"
|
||||
|
||||
- name: Check if docker daemon is running
|
||||
command: docker info
|
||||
register: docker_info
|
||||
failed_when: false
|
||||
changed_when: false
|
||||
|
||||
- name: Display docker daemon status
|
||||
debug:
|
||||
msg: "Docker daemon: {{ 'Запущен' if docker_info is succeeded else 'Не запущен или недоступен' }}"
|
||||
|
||||
- name: Check if docker-compose binary exists
|
||||
stat:
|
||||
path: /usr/local/bin/docker-compose
|
||||
register: docker_compose_binary
|
||||
|
||||
- name: Check if docker-compose binary exists (alternative path)
|
||||
stat:
|
||||
path: /usr/bin/docker-compose
|
||||
register: docker_compose_binary_alt
|
||||
|
||||
- name: Check if docker compose plugin exists
|
||||
command: docker compose version
|
||||
register: docker_compose_plugin
|
||||
failed_when: false
|
||||
changed_when: false
|
||||
|
||||
- name: Display docker-compose status
|
||||
debug:
|
||||
msg: "Docker Compose: {{ 'Найден как binary' if docker_compose_binary.stat.exists or docker_compose_binary_alt.stat.exists else ('Найден как plugin' if docker_compose_plugin is succeeded else 'Не найден') }}"
|
||||
|
||||
- name: Display docker-compose version
|
||||
debug:
|
||||
msg: "Версия Docker Compose: {{ docker_compose_plugin.stdout if docker_compose_plugin is succeeded else 'Docker Compose не установлен' }}"
|
||||
|
||||
- name: Test docker functionality
|
||||
command: docker run --rm hello-world
|
||||
register: docker_test
|
||||
failed_when: false
|
||||
changed_when: false
|
||||
|
||||
- name: Display docker test result
|
||||
debug:
|
||||
msg: "Тест Docker: {{ 'Успешно' if docker_test is succeeded else 'Ошибка - ' + docker_test.stderr }}"
|
||||
|
||||
- name: Check systemd services status
|
||||
command: systemctl list-units --type=service --state=running
|
||||
register: running_services
|
||||
failed_when: false
|
||||
changed_when: false
|
||||
|
||||
- name: Display running services count
|
||||
debug:
|
||||
msg: "Количество запущенных сервисов: {{ running_services.stdout_lines | length }}"
|
||||
|
||||
- name: Check for docker-related systemd services
|
||||
command: systemctl list-units --type=service | grep -i docker
|
||||
register: docker_services
|
||||
failed_when: false
|
||||
changed_when: false
|
||||
|
||||
- name: Display docker services
|
||||
debug:
|
||||
msg: "Docker сервисы: {{ docker_services.stdout_lines if docker_services.stdout_lines else 'Не найдены' }}"
|
||||
|
||||
- name: Final summary
|
||||
debug:
|
||||
msg: |
|
||||
========================================
|
||||
РЕЗУЛЬТАТЫ ПРОВЕРКИ ОБРАЗА {{ ansible_distribution }}:
|
||||
========================================
|
||||
Systemd: {{ '✓ Работает' if systemd_status is succeeded else '✗ Не работает' }}
|
||||
Docker: {{ '✓ Установлен и работает' if docker_info is succeeded else '✗ Не установлен или не работает' }}
|
||||
Docker Compose: {{ '✓ Доступен' if (docker_compose_binary.stat.exists or docker_compose_binary_alt.stat.exists or docker_compose_plugin is succeeded) else '✗ Недоступен' }}
|
||||
========================================
|
||||
99
molecule/presets/README.md
Normal file
99
molecule/presets/README.md
Normal file
@@ -0,0 +1,99 @@
|
||||
# Пресеты для Molecule
|
||||
|
||||
## Описание
|
||||
|
||||
Пресеты - это готовые конфигурации для быстрого развертывания тестовых окружений. Каждый пресет содержит определенный набор хостов и настроек.
|
||||
|
||||
## Доступные пресеты
|
||||
|
||||
### `minimal.yml`
|
||||
- **Описание**: Минимальный набор для быстрого тестирования
|
||||
- **Хосты**: 1 хост (Debian)
|
||||
- **Использование**: Для простых тестов и отладки
|
||||
|
||||
### `standard.yml`
|
||||
- **Описание**: Стандартный набор для тестирования
|
||||
- **Хосты**: 3 хоста (Debian + RHEL)
|
||||
- **Использование**: Для большинства тестов
|
||||
|
||||
### `docker.yml`
|
||||
- **Описание**: Пресет с Docker контейнерами
|
||||
- **Хосты**: 2 systemd + 1 DinD + 1 DOoD
|
||||
- **Использование**: Для тестирования Docker-приложений
|
||||
|
||||
### `cluster.yml`
|
||||
- **Описание**: Пресет для кластерного тестирования
|
||||
- **Хосты**: 8 хостов (web, app, database, loadbalancer, monitoring)
|
||||
- **Использование**: Для тестирования сложных архитектур
|
||||
|
||||
## Использование
|
||||
|
||||
### Через Makefile
|
||||
```bash
|
||||
# Показать все пресеты
|
||||
make preset list
|
||||
|
||||
# Переключиться на пресет
|
||||
make preset use minimal
|
||||
make preset use standard
|
||||
make preset use docker
|
||||
make preset use cluster
|
||||
```
|
||||
|
||||
### Через скрипт
|
||||
```bash
|
||||
# Показать все пресеты
|
||||
./scripts/use-preset.sh
|
||||
|
||||
# Переключиться на пресет
|
||||
./scripts/use-preset.sh minimal
|
||||
```
|
||||
|
||||
### Ручное переключение
|
||||
```bash
|
||||
# Скопировать пресет в hosts.yml
|
||||
cp molecule/presets/minimal.yml molecule/universal/hosts.yml
|
||||
```
|
||||
|
||||
## Создание собственного пресета
|
||||
|
||||
1. Скопируйте существующий пресет:
|
||||
```bash
|
||||
cp molecule/presets/standard.yml molecule/presets/my-preset.yml
|
||||
```
|
||||
|
||||
2. Отредактируйте файл под свои нужды
|
||||
|
||||
3. Используйте новый пресет:
|
||||
```bash
|
||||
make preset use my-preset
|
||||
```
|
||||
|
||||
## Структура пресета
|
||||
|
||||
```yaml
|
||||
---
|
||||
docker_network: labnet
|
||||
generated_inventory: "{{ molecule_ephemeral_directory }}/inventory/hosts.ini"
|
||||
|
||||
images:
|
||||
debian: "ghcr.io/ansible-community/molecule-ubuntu-systemd:jammy"
|
||||
rhel: "quay.io/centos/centos:stream9-systemd"
|
||||
|
||||
systemd_defaults:
|
||||
privileged: true
|
||||
command: "/sbin/init"
|
||||
volumes:
|
||||
- "/sys/fs/cgroup:/sys/fs/cgroup:ro"
|
||||
tmpfs: ["/run", "/run/lock"]
|
||||
capabilities: ["SYS_ADMIN"]
|
||||
|
||||
hosts:
|
||||
- name: host1
|
||||
family: debian
|
||||
groups: [test]
|
||||
- name: docker1
|
||||
type: dind
|
||||
groups: [docker]
|
||||
publish: ["8080:8080"]
|
||||
```
|
||||
57
molecule/presets/cluster.yml
Normal file
57
molecule/presets/cluster.yml
Normal file
@@ -0,0 +1,57 @@
|
||||
---
|
||||
# Пресет для кластерного тестирования
|
||||
# Автор: Сергей Антропов
|
||||
# Сайт: https://devops.org.ru
|
||||
|
||||
docker_network: labnet
|
||||
generated_inventory: "{{ molecule_ephemeral_directory }}/inventory/hosts.ini"
|
||||
|
||||
# systemd-ready образы
|
||||
images:
|
||||
debian: "ghcr.io/ansible-community/molecule-ubuntu-systemd:jammy"
|
||||
rhel: "quay.io/centos/centos:stream9-systemd"
|
||||
|
||||
systemd_defaults:
|
||||
privileged: true
|
||||
command: "/sbin/init"
|
||||
volumes:
|
||||
- "/sys/fs/cgroup:/sys/fs/cgroup:ro"
|
||||
tmpfs: ["/run", "/run/lock"]
|
||||
capabilities: ["SYS_ADMIN"]
|
||||
|
||||
hosts:
|
||||
# Web серверы
|
||||
- name: web1
|
||||
family: debian
|
||||
groups: [web]
|
||||
- name: web2
|
||||
family: rhel
|
||||
groups: [web]
|
||||
|
||||
# App серверы
|
||||
- name: app1
|
||||
family: debian
|
||||
groups: [app]
|
||||
- name: app2
|
||||
family: rhel
|
||||
groups: [app]
|
||||
|
||||
# Database серверы
|
||||
- name: db1
|
||||
family: debian
|
||||
groups: [database]
|
||||
- name: db2
|
||||
family: rhel
|
||||
groups: [database]
|
||||
|
||||
# Load Balancer
|
||||
- name: lb1
|
||||
family: rhel
|
||||
groups: [loadbalancer]
|
||||
publish: ["80:80", "443:443"]
|
||||
|
||||
# Мониторинг
|
||||
- name: monitor1
|
||||
family: debian
|
||||
groups: [monitoring]
|
||||
publish: ["3000:3000", "9090:9090"]
|
||||
44
molecule/presets/docker.yml
Normal file
44
molecule/presets/docker.yml
Normal file
@@ -0,0 +1,44 @@
|
||||
---
|
||||
# Пресет с Docker контейнерами
|
||||
# Автор: Сергей Антропов
|
||||
# Сайт: https://devops.org.ru
|
||||
|
||||
docker_network: labnet
|
||||
generated_inventory: "{{ molecule_ephemeral_directory }}/inventory/hosts.ini"
|
||||
|
||||
# systemd-ready образы
|
||||
images:
|
||||
debian: "ghcr.io/ansible-community/molecule-ubuntu-systemd:jammy"
|
||||
rhel: "quay.io/centos/centos:stream9-systemd"
|
||||
|
||||
systemd_defaults:
|
||||
privileged: true
|
||||
command: "/sbin/init"
|
||||
volumes:
|
||||
- "/sys/fs/cgroup:/sys/fs/cgroup:ro"
|
||||
tmpfs: ["/run", "/run/lock"]
|
||||
capabilities: ["SYS_ADMIN"]
|
||||
|
||||
hosts:
|
||||
# Тестовые хосты
|
||||
- name: test1
|
||||
family: debian
|
||||
groups: [test]
|
||||
- name: test2
|
||||
family: rhel
|
||||
groups: [test]
|
||||
|
||||
# DinD узел (Docker-in-Docker)
|
||||
- name: docker1
|
||||
type: dind
|
||||
groups: [docker]
|
||||
publish: ["8080:8080"]
|
||||
|
||||
# DOoD узел (Docker-out-of-Docker)
|
||||
- name: dood1
|
||||
type: dood
|
||||
family: debian
|
||||
groups: [dood]
|
||||
publish: ["8081:8081"]
|
||||
env:
|
||||
DOCKER_HOST: unix:///var/run/docker.sock
|
||||
25
molecule/presets/minimal.yml
Normal file
25
molecule/presets/minimal.yml
Normal file
@@ -0,0 +1,25 @@
|
||||
---
|
||||
# Минимальный пресет для быстрого тестирования
|
||||
# Автор: Сергей Антропов
|
||||
# Сайт: https://devops.org.ru
|
||||
|
||||
docker_network: labnet
|
||||
generated_inventory: "{{ molecule_ephemeral_directory }}/inventory/hosts.ini"
|
||||
|
||||
# systemd-ready образы
|
||||
images:
|
||||
debian: "ghcr.io/ansible-community/molecule-ubuntu-systemd:jammy"
|
||||
|
||||
systemd_defaults:
|
||||
privileged: true
|
||||
command: "/sbin/init"
|
||||
volumes:
|
||||
- "/sys/fs/cgroup:/sys/fs/cgroup:ro"
|
||||
tmpfs: ["/run", "/run/lock"]
|
||||
capabilities: ["SYS_ADMIN"]
|
||||
|
||||
hosts:
|
||||
# Минимальный набор - один хост
|
||||
- name: u1
|
||||
family: debian
|
||||
groups: [test]
|
||||
32
molecule/presets/standard.yml
Normal file
32
molecule/presets/standard.yml
Normal file
@@ -0,0 +1,32 @@
|
||||
---
|
||||
# Стандартный пресет для тестирования
|
||||
# Автор: Сергей Антропов
|
||||
# Сайт: https://devops.org.ru
|
||||
|
||||
docker_network: labnet
|
||||
generated_inventory: "{{ molecule_ephemeral_directory }}/inventory/hosts.ini"
|
||||
|
||||
# systemd-ready образы
|
||||
images:
|
||||
debian: "ghcr.io/ansible-community/molecule-ubuntu-systemd:jammy"
|
||||
rhel: "quay.io/centos/centos:stream9-systemd"
|
||||
|
||||
systemd_defaults:
|
||||
privileged: true
|
||||
command: "/sbin/init"
|
||||
volumes:
|
||||
- "/sys/fs/cgroup:/sys/fs/cgroup:ro"
|
||||
tmpfs: ["/run", "/run/lock"]
|
||||
capabilities: ["SYS_ADMIN"]
|
||||
|
||||
hosts:
|
||||
# Стандартный набор - 3 хоста
|
||||
- name: u1
|
||||
family: debian
|
||||
groups: [test]
|
||||
- name: u2
|
||||
family: rhel
|
||||
groups: [test]
|
||||
- name: u3
|
||||
family: debian
|
||||
groups: [test]
|
||||
52
molecule/universal/converge.yml
Normal file
52
molecule/universal/converge.yml
Normal file
@@ -0,0 +1,52 @@
|
||||
---
|
||||
- hosts: localhost
|
||||
gather_facts: false
|
||||
vars:
|
||||
# перечисли файлы/глобы, которые нужно временно расшифровать
|
||||
vault_targets:
|
||||
- /ansible/vault/secrets.yml
|
||||
# добавляй сюда свои пути (host_vars/*/vault.yml, group_vars/*/vault.yml, и т.п.)
|
||||
|
||||
tasks:
|
||||
- name: Install required collections (use repo's requirements.yml)
|
||||
community.docker.docker_container_exec:
|
||||
container: ansible
|
||||
command: bash -lc "ansible-galaxy collection install -r /ansible/requirements.yml || true"
|
||||
|
||||
- name: Decrypt vault targets (best-effort)
|
||||
community.docker.docker_container_exec:
|
||||
container: ansible
|
||||
command: >
|
||||
bash -lc '
|
||||
set -euo pipefail;
|
||||
for p in {{ vault_targets | map('quote') | join(' ') }}; do
|
||||
if [ -e "$p" ]; then
|
||||
echo "[vault] decrypt $p";
|
||||
ansible-vault decrypt --vault-password-file /ansible/vault-password.txt "$p" || true;
|
||||
fi
|
||||
done
|
||||
'
|
||||
|
||||
- name: Run external playbook (your lab play)
|
||||
community.docker.docker_container_exec:
|
||||
container: ansible
|
||||
command: >
|
||||
bash -lc "
|
||||
ANSIBLE_ROLES_PATH=/ansible/roles
|
||||
ansible-playbook -i {{ lookup('env','MOLECULE_EPHEMERAL_DIRECTORY') }}/inventory/hosts.ini /ansible/molecule/universal/site.yml
|
||||
"
|
||||
|
||||
- name: Re-encrypt vault targets (always)
|
||||
community.docker.docker_container_exec:
|
||||
container: ansible
|
||||
command: >
|
||||
bash -lc '
|
||||
set -euo pipefail;
|
||||
for p in {{ vault_targets | map('quote') | join(' ') }}; do
|
||||
if [ -e "$p" ]; then
|
||||
echo "[vault] encrypt $p";
|
||||
ansible-vault encrypt --encrypt-vault-id default --vault-password-file /ansible/vault-password.txt "$p" || true;
|
||||
fi
|
||||
done
|
||||
'
|
||||
ignore_errors: true
|
||||
107
molecule/universal/create.yml
Normal file
107
molecule/universal/create.yml
Normal file
@@ -0,0 +1,107 @@
|
||||
---
|
||||
- hosts: localhost
|
||||
gather_facts: false
|
||||
vars_files:
|
||||
- hosts.yml
|
||||
|
||||
tasks:
|
||||
- name: Ensure network exists
|
||||
community.docker.docker_network:
|
||||
name: "{{ docker_network }}"
|
||||
state: present
|
||||
|
||||
# SYSTEMD nodes
|
||||
- name: Pull systemd images
|
||||
community.docker.docker_image:
|
||||
name: "{{ images[item.family] }}"
|
||||
source: pull
|
||||
loop: "{{ hosts | selectattr('type','undefined') | list }}"
|
||||
loop_control: { label: "{{ item.name }}" }
|
||||
|
||||
- name: Start systemd nodes
|
||||
community.docker.docker_container:
|
||||
name: "{{ item.name }}"
|
||||
image: "{{ images[item.family] }}"
|
||||
networks: [ { name: "{{ docker_network }}" } ]
|
||||
privileged: "{{ systemd_defaults.privileged }}"
|
||||
command: "{{ systemd_defaults.command }}"
|
||||
volumes: "{{ (systemd_defaults.volumes | default([])) + (item.volumes | default([])) }}"
|
||||
tmpfs: "{{ (systemd_defaults.tmpfs | default([])) + (item.tmpfs | default([])) }}"
|
||||
capabilities: "{{ (systemd_defaults.capabilities | default([])) + (item.capabilities | default([])) }}"
|
||||
published_ports: "{{ item.publish | default([]) }}"
|
||||
env: "{{ item.env | default({}) }}"
|
||||
state: started
|
||||
restart_policy: unless-stopped
|
||||
loop: "{{ hosts | selectattr('type','undefined') | list }}"
|
||||
loop_control: { label: "{{ item.name }}" }
|
||||
|
||||
# DinD nodes
|
||||
- name: Start DinD nodes (docker:27-dind)
|
||||
community.docker.docker_container:
|
||||
name: "{{ item.name }}"
|
||||
image: "docker:27-dind"
|
||||
privileged: true
|
||||
environment: { DOCKER_TLS_CERTDIR: "" }
|
||||
networks: [ { name: "{{ docker_network }}" } ]
|
||||
published_ports: "{{ item.publish | default([]) }}"
|
||||
volumes: [ "{{ item.name }}-docker:/var/lib/docker" ]
|
||||
state: started
|
||||
restart_policy: unless-stopped
|
||||
loop: "{{ hosts | selectattr('type','defined') | selectattr('type','equalto','dind') | list }}"
|
||||
loop_control: { label: "{{ item.name }}" }
|
||||
|
||||
# DOoD nodes (mount docker.sock)
|
||||
- name: Start DOoD nodes (systemd + docker.sock mount)
|
||||
community.docker.docker_container:
|
||||
name: "{{ item.name }}"
|
||||
image: "{{ images[item.family] }}"
|
||||
networks: [ { name: "{{ docker_network }}" } ]
|
||||
privileged: "{{ systemd_defaults.privileged }}"
|
||||
command: "{{ systemd_defaults.command }}"
|
||||
volumes: "{{ (systemd_defaults.volumes | default([])) + ['/var/run/docker.sock:/var/run/docker.sock'] + (item.volumes | default([])) }}"
|
||||
tmpfs: "{{ (systemd_defaults.tmpfs | default([])) + (item.tmpfs | default([])) }}"
|
||||
capabilities: "{{ (systemd_defaults.capabilities | default([])) + (item.capabilities | default([])) }}"
|
||||
published_ports: "{{ item.publish | default([]) }}"
|
||||
env: "{{ item.env | default({}) }}"
|
||||
state: started
|
||||
restart_policy: unless-stopped
|
||||
loop: "{{ hosts | selectattr('type','defined') | selectattr('type','equalto','dood') | list }}"
|
||||
loop_control: { label: "{{ item.name }}" }
|
||||
|
||||
# Build groups map
|
||||
- name: Build groups map {group: [hosts]}
|
||||
set_fact:
|
||||
groups_map: "{{ groups_map | default({}) }}"
|
||||
- name: Append hosts to groups
|
||||
set_fact:
|
||||
groups_map: "{{ groups_map | combine({ item_group: (groups_map[item_group] | default([])) + [item_name] }) }}"
|
||||
loop: "{{ hosts | subelements('groups', skip_missing=True) }}"
|
||||
loop_control:
|
||||
label: "{{ item.0.name }}"
|
||||
vars:
|
||||
item_name: "{{ item.0.name }}"
|
||||
item_group: "{{ item.1 }}"
|
||||
|
||||
# Render inventory
|
||||
- name: Render inventory ini
|
||||
set_fact:
|
||||
inv_content: |
|
||||
[all:vars]
|
||||
ansible_connection=community.docker.docker
|
||||
ansible_python_interpreter=/usr/bin/python3
|
||||
|
||||
{% for group, members in (groups_map | dictsort) %}
|
||||
[{{ group }}]
|
||||
{% for h in members %}{{ h }}
|
||||
{% endfor %}
|
||||
|
||||
{% endfor %}
|
||||
[all]
|
||||
{% for h in hosts %}{{ h.name }}
|
||||
{% endfor %}
|
||||
|
||||
- name: Write inventory file
|
||||
copy:
|
||||
dest: "{{ generated_inventory }}"
|
||||
content: "{{ inv_content }}"
|
||||
mode: "0644"
|
||||
29
molecule/universal/destroy.yml
Normal file
29
molecule/universal/destroy.yml
Normal file
@@ -0,0 +1,29 @@
|
||||
---
|
||||
- hosts: localhost
|
||||
gather_facts: false
|
||||
vars_files:
|
||||
- hosts.yml
|
||||
|
||||
tasks:
|
||||
- name: Remove containers
|
||||
community.docker.docker_container:
|
||||
name: "{{ item.name }}"
|
||||
state: absent
|
||||
force_kill: true
|
||||
loop: "{{ hosts }}"
|
||||
loop_control: { label: "{{ item.name }}" }
|
||||
ignore_errors: true
|
||||
|
||||
- name: Remove DinD volumes
|
||||
community.docker.docker_volume:
|
||||
name: "{{ item.name }}-docker"
|
||||
state: absent
|
||||
loop: "{{ hosts | selectattr('type','defined') | selectattr('type','equalto','dind') | list }}"
|
||||
loop_control: { label: "{{ item.name }}" }
|
||||
ignore_errors: true
|
||||
|
||||
- name: Remove network
|
||||
community.docker.docker_network:
|
||||
name: "{{ docker_network }}"
|
||||
state: absent
|
||||
ignore_errors: true
|
||||
44
molecule/universal/hosts.yml
Normal file
44
molecule/universal/hosts.yml
Normal file
@@ -0,0 +1,44 @@
|
||||
---
|
||||
# Пресет с Docker контейнерами
|
||||
# Автор: Сергей Антропов
|
||||
# Сайт: https://devops.org.ru
|
||||
|
||||
docker_network: labnet
|
||||
generated_inventory: "{{ molecule_ephemeral_directory }}/inventory/hosts.ini"
|
||||
|
||||
# systemd-ready образы
|
||||
images:
|
||||
debian: "ghcr.io/ansible-community/molecule-ubuntu-systemd:jammy"
|
||||
rhel: "quay.io/centos/centos:stream9-systemd"
|
||||
|
||||
systemd_defaults:
|
||||
privileged: true
|
||||
command: "/sbin/init"
|
||||
volumes:
|
||||
- "/sys/fs/cgroup:/sys/fs/cgroup:ro"
|
||||
tmpfs: ["/run", "/run/lock"]
|
||||
capabilities: ["SYS_ADMIN"]
|
||||
|
||||
hosts:
|
||||
# Тестовые хосты
|
||||
- name: test1
|
||||
family: debian
|
||||
groups: [test]
|
||||
- name: test2
|
||||
family: rhel
|
||||
groups: [test]
|
||||
|
||||
# DinD узел (Docker-in-Docker)
|
||||
- name: docker1
|
||||
type: dind
|
||||
groups: [docker]
|
||||
publish: ["8080:8080"]
|
||||
|
||||
# DOoD узел (Docker-out-of-Docker)
|
||||
- name: dood1
|
||||
type: dood
|
||||
family: debian
|
||||
groups: [dood]
|
||||
publish: ["8081:8081"]
|
||||
env:
|
||||
DOCKER_HOST: unix:///var/run/docker.sock
|
||||
28
molecule/universal/molecule.yml
Normal file
28
molecule/universal/molecule.yml
Normal file
@@ -0,0 +1,28 @@
|
||||
---
|
||||
# Универсальная конфигурация Molecule
|
||||
# Автор: Сергей Антропов
|
||||
# Сайт: https://devops.org.ru
|
||||
|
||||
driver:
|
||||
name: delegated
|
||||
|
||||
provisioner:
|
||||
name: ansible
|
||||
config_options:
|
||||
defaults:
|
||||
stdout_callback: yaml
|
||||
env:
|
||||
ANSIBLE_STDOUT_CALLBACK: yaml
|
||||
inventory:
|
||||
links:
|
||||
hosts: "${MOLECULE_EPHEMERAL_DIRECTORY}/inventory/hosts.ini"
|
||||
|
||||
dependency:
|
||||
name: galaxy
|
||||
|
||||
verifier:
|
||||
name: ansible
|
||||
|
||||
lint: |-
|
||||
set -e
|
||||
ansible-lint
|
||||
95
molecule/universal/site.yml
Normal file
95
molecule/universal/site.yml
Normal file
@@ -0,0 +1,95 @@
|
||||
---
|
||||
# Универсальный плейбук для тестирования
|
||||
# Автор: Сергей Антропов
|
||||
# Сайт: https://devops.org.ru
|
||||
|
||||
- name: Base deps
|
||||
hosts: all
|
||||
become: true
|
||||
tasks:
|
||||
- name: Update apt cache (Debian)
|
||||
apt:
|
||||
update_cache: true
|
||||
when: ansible_os_family == 'Debian'
|
||||
changed_when: false
|
||||
|
||||
- name: Common tools
|
||||
package:
|
||||
name:
|
||||
- curl
|
||||
- jq
|
||||
- ca-certificates
|
||||
- iproute2
|
||||
- iputils-ping
|
||||
- procps
|
||||
- net-tools
|
||||
- sudo
|
||||
- vim
|
||||
state: present
|
||||
|
||||
# ===== ТЕСТОВЫЕ РОЛИ =====
|
||||
- name: Deploy example role to test hosts
|
||||
hosts: test
|
||||
become: true
|
||||
roles:
|
||||
- example
|
||||
vars:
|
||||
example_package_name: "nginx"
|
||||
example_directory: "/opt/example"
|
||||
example_setting: "test"
|
||||
example_port: 8080
|
||||
|
||||
- name: Deploy example role to docker hosts (DinD)
|
||||
hosts: docker
|
||||
become: true
|
||||
roles:
|
||||
- example
|
||||
vars:
|
||||
example_package_name: "docker"
|
||||
example_directory: "/opt/docker-example"
|
||||
example_setting: "dind"
|
||||
example_port: 8080
|
||||
|
||||
- name: Deploy example role to dood hosts (DOoD)
|
||||
hosts: dood
|
||||
become: true
|
||||
roles:
|
||||
- example
|
||||
vars:
|
||||
example_package_name: "docker"
|
||||
example_directory: "/opt/dood-example"
|
||||
example_setting: "dood"
|
||||
example_port: 8081
|
||||
|
||||
# ===== Пример: поднять compose внутри DinD-хостов =====
|
||||
- name: Deploy stack inside DinD nodes
|
||||
hosts: docker
|
||||
gather_facts: false
|
||||
vars:
|
||||
docker_host: "tcp://{{ inventory_hostname }}:2375"
|
||||
stack_dir: /root/stack
|
||||
tasks:
|
||||
- name: Create stack directory
|
||||
file:
|
||||
path: "{{ stack_dir }}"
|
||||
state: directory
|
||||
|
||||
- name: Create simple docker-compose.yml
|
||||
copy:
|
||||
dest: "{{ stack_dir }}/docker-compose.yml"
|
||||
content: |
|
||||
version: '3.8'
|
||||
services:
|
||||
nginx:
|
||||
image: nginx:alpine
|
||||
ports:
|
||||
- "8080:80"
|
||||
environment:
|
||||
- NGINX_HOST=localhost
|
||||
- NGINX_PORT=80
|
||||
|
||||
- name: Deploy stack with docker-compose
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: "{{ stack_dir }}"
|
||||
state: present
|
||||
docker_host: "{{ docker_host }}"
|
||||
263
molecule/universal/verify.yml
Normal file
263
molecule/universal/verify.yml
Normal file
@@ -0,0 +1,263 @@
|
||||
---
|
||||
# Универсальные проверки для тестового стенда
|
||||
# Автор: Сергей Антропов
|
||||
# Сайт: https://devops.org.ru
|
||||
|
||||
- name: Verify web servers
|
||||
hosts: web
|
||||
become: true
|
||||
tasks:
|
||||
- name: Check nginx service status
|
||||
systemd:
|
||||
name: nginx
|
||||
register: nginx_status
|
||||
|
||||
- name: Verify nginx is running
|
||||
assert:
|
||||
that:
|
||||
- nginx_status.status.ActiveState == "active"
|
||||
- nginx_status.status.SubState == "running"
|
||||
fail_msg: "nginx service is not running"
|
||||
success_msg: "nginx service is running"
|
||||
|
||||
- name: Test nginx response
|
||||
uri:
|
||||
url: "http://{{ inventory_hostname }}"
|
||||
method: GET
|
||||
register: nginx_response
|
||||
|
||||
- name: Verify nginx response
|
||||
assert:
|
||||
that:
|
||||
- nginx_response.status == 200
|
||||
fail_msg: "nginx is not responding"
|
||||
success_msg: "nginx is responding correctly"
|
||||
|
||||
- name: Verify app servers
|
||||
hosts: app
|
||||
become: true
|
||||
tasks:
|
||||
- name: Check Python installation
|
||||
command: python3 --version
|
||||
register: python_version
|
||||
changed_when: false
|
||||
|
||||
- name: Verify Python is installed
|
||||
assert:
|
||||
that:
|
||||
- python_version.rc == 0
|
||||
fail_msg: "Python3 is not installed"
|
||||
success_msg: "Python3 is installed: {{ python_version.stdout }}"
|
||||
|
||||
- name: Check app file exists
|
||||
stat:
|
||||
path: /opt/myapp/app.py
|
||||
register: app_file
|
||||
|
||||
- name: Verify app file exists
|
||||
assert:
|
||||
that:
|
||||
- app_file.stat.exists
|
||||
fail_msg: "App file does not exist"
|
||||
success_msg: "App file exists and is executable"
|
||||
|
||||
- name: Verify database servers
|
||||
hosts: database
|
||||
become: true
|
||||
tasks:
|
||||
- name: Check SQLite installation
|
||||
command: sqlite3 --version
|
||||
register: sqlite_version
|
||||
changed_when: false
|
||||
|
||||
- name: Verify SQLite is installed
|
||||
assert:
|
||||
that:
|
||||
- sqlite_version.rc == 0
|
||||
fail_msg: "SQLite is not installed"
|
||||
success_msg: "SQLite is installed: {{ sqlite_version.stdout }}"
|
||||
|
||||
- name: Check database file exists
|
||||
stat:
|
||||
path: /var/lib/mydb/sample.db
|
||||
register: db_file
|
||||
|
||||
- name: Verify database file exists
|
||||
assert:
|
||||
that:
|
||||
- db_file.stat.exists
|
||||
fail_msg: "Database file does not exist"
|
||||
success_msg: "Database file exists"
|
||||
|
||||
- name: Test database query
|
||||
command: sqlite3 /var/lib/mydb/sample.db "SELECT COUNT(*) FROM users;"
|
||||
register: db_query
|
||||
changed_when: false
|
||||
|
||||
- name: Verify database query
|
||||
assert:
|
||||
that:
|
||||
- db_query.rc == 0
|
||||
- db_query.stdout | int > 0
|
||||
fail_msg: "Database query failed"
|
||||
success_msg: "Database query successful: {{ db_query.stdout }} users found"
|
||||
|
||||
- name: Verify cache servers
|
||||
hosts: cache
|
||||
become: true
|
||||
tasks:
|
||||
- name: Check Redis service status
|
||||
systemd:
|
||||
name: redis
|
||||
register: redis_status
|
||||
|
||||
- name: Verify Redis is running
|
||||
assert:
|
||||
that:
|
||||
- redis_status.status.ActiveState == "active"
|
||||
- redis_status.status.SubState == "running"
|
||||
fail_msg: "Redis service is not running"
|
||||
success_msg: "Redis service is running"
|
||||
|
||||
- name: Test Redis connection
|
||||
command: redis-cli ping
|
||||
register: redis_ping
|
||||
changed_when: false
|
||||
|
||||
- name: Verify Redis connection
|
||||
assert:
|
||||
that:
|
||||
- redis_ping.rc == 0
|
||||
- redis_ping.stdout == "PONG"
|
||||
fail_msg: "Redis is not responding"
|
||||
success_msg: "Redis is responding correctly"
|
||||
|
||||
- name: Verify load balancer
|
||||
hosts: loadbalancer
|
||||
become: true
|
||||
tasks:
|
||||
- name: Check HAProxy service status
|
||||
systemd:
|
||||
name: haproxy
|
||||
register: haproxy_status
|
||||
|
||||
- name: Verify HAProxy is running
|
||||
assert:
|
||||
that:
|
||||
- haproxy_status.status.ActiveState == "active"
|
||||
- haproxy_status.status.SubState == "running"
|
||||
fail_msg: "HAProxy service is not running"
|
||||
success_msg: "HAProxy service is running"
|
||||
|
||||
- name: Check HAProxy configuration
|
||||
stat:
|
||||
path: /etc/haproxy/haproxy.cfg
|
||||
register: haproxy_config
|
||||
|
||||
- name: Verify HAProxy configuration exists
|
||||
assert:
|
||||
that:
|
||||
- haproxy_config.stat.exists
|
||||
fail_msg: "HAProxy configuration does not exist"
|
||||
success_msg: "HAProxy configuration exists"
|
||||
|
||||
- name: Verify monitoring
|
||||
hosts: monitoring
|
||||
become: true
|
||||
tasks:
|
||||
- name: Check monitoring tools
|
||||
command: which htop
|
||||
register: htop_check
|
||||
changed_when: false
|
||||
|
||||
- name: Verify monitoring tools are installed
|
||||
assert:
|
||||
that:
|
||||
- htop_check.rc == 0
|
||||
fail_msg: "Monitoring tools are not installed"
|
||||
success_msg: "Monitoring tools are installed"
|
||||
|
||||
- name: Check monitoring script
|
||||
stat:
|
||||
path: /usr/local/bin/system-info.sh
|
||||
register: monitor_script
|
||||
|
||||
- name: Verify monitoring script exists
|
||||
assert:
|
||||
that:
|
||||
- monitor_script.stat.exists
|
||||
fail_msg: "Monitoring script does not exist"
|
||||
success_msg: "Monitoring script exists"
|
||||
|
||||
- name: Test monitoring script
|
||||
command: /usr/local/bin/system-info.sh
|
||||
register: monitor_output
|
||||
changed_when: false
|
||||
|
||||
- name: Verify monitoring script works
|
||||
assert:
|
||||
that:
|
||||
- monitor_output.rc == 0
|
||||
- monitor_output.stdout | length > 0
|
||||
fail_msg: "Monitoring script failed"
|
||||
success_msg: "Monitoring script works correctly"
|
||||
|
||||
- name: Network connectivity tests
|
||||
hosts: all
|
||||
tasks:
|
||||
- name: Test connectivity to web servers
|
||||
wait_for:
|
||||
host: "{{ item }}"
|
||||
port: 80
|
||||
timeout: 10
|
||||
loop:
|
||||
- web1
|
||||
- web2
|
||||
when: "'web' not in group_names"
|
||||
ignore_errors: true
|
||||
|
||||
- name: Test connectivity to app servers
|
||||
wait_for:
|
||||
host: "{{ item }}"
|
||||
port: 8080
|
||||
timeout: 10
|
||||
loop:
|
||||
- app1
|
||||
when: "'app' not in group_names"
|
||||
ignore_errors: true
|
||||
|
||||
- name: Test connectivity to cache servers
|
||||
wait_for:
|
||||
host: "{{ item }}"
|
||||
port: 6379
|
||||
timeout: 10
|
||||
loop:
|
||||
- cache1
|
||||
when: "'cache' not in group_names"
|
||||
ignore_errors: true
|
||||
|
||||
- name: Test connectivity to load balancer
|
||||
wait_for:
|
||||
host: lb1
|
||||
port: 80
|
||||
timeout: 10
|
||||
when: "'loadbalancer' not in group_names"
|
||||
ignore_errors: true
|
||||
|
||||
- name: Final verification summary
|
||||
hosts: localhost
|
||||
gather_facts: false
|
||||
tasks:
|
||||
- name: Display verification summary
|
||||
debug:
|
||||
msg: |
|
||||
========================================
|
||||
Verification Summary
|
||||
========================================
|
||||
- Web servers: {{ 'OK' if web_servers_ok is defined else 'SKIPPED' }}
|
||||
- App servers: {{ 'OK' if app_servers_ok is defined else 'SKIPPED' }}
|
||||
- Database servers: {{ 'OK' if database_servers_ok is defined else 'SKIPPED' }}
|
||||
- Cache servers: {{ 'OK' if cache_servers_ok is defined else 'SKIPPED' }}
|
||||
- Load balancer: {{ 'OK' if loadbalancer_ok is defined else 'SKIPPED' }}
|
||||
- Monitoring: {{ 'OK' if monitoring_ok is defined else 'SKIPPED' }}
|
||||
========================================
|
||||
Reference in New Issue
Block a user