Files
DevOpsLab/molecule/universal/verify.yml
Sergey Antropoff dc255d006a feat: Добавлены продвинутые фичи из dialog.txt
- YAML inventory с мультигруппами в create.yml
- Vault preflight проверки в converge.yml (шифрование/расшифровка)
- Pre_tasks с include_vars для lab preset
- Chaos Engineering playbook для тестирования отказоустойчивости
- Idempotence проверки в verify.yml
- Health Dashboard с JSON отчетом
- Secrets Inspector скрипт для проверки безопасности
- Common tools установка в site.yml

Новые команды:
- make chaos - запуск Chaos Engineering тестов
- make check-secrets - проверка безопасности секретов
- make idempotence - проверка идемпотентности

Обновления в файлах:
- molecule/universal/create.yml: добавлена генерация YAML inventory
- molecule/universal/molecule.yml: обновлен для использования YAML inventory
- molecule/universal/converge.yml: добавлены vault preflight проверки
- molecule/universal/verify.yml: добавлены idempotence и health dashboard
- files/playbooks/chaos.yml: новый Chaos Engineering playbook
- files/playbooks/site.yml: добавлены common tools
- scripts/secret_scan.sh: новый Secrets Inspector
- Makefile: добавлены новые команды
- README.md: обновлена документация

Преимущества:
- Мультигруппы в YAML inventory для сложных конфигураций
- Автоматическая проверка и нормализация vault файлов
- Тестирование отказоустойчивости через Chaos Engineering
- Проверка идемпотентности для качества ролей
- Health Dashboard для мониторинга состояния лаборатории
- Secrets Inspector для безопасности
- Установка common tools для всех хостов

Автор: Сергей Антропов
Сайт: https://devops.org.ru
2025-10-22 14:10:01 +03:00

306 lines
13 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
# Проверка работы универсальной лаборатории
# Автор: Сергей Антропов
# Сайт: https://devops.org.ru
- hosts: localhost
gather_facts: false
vars:
inv_yaml: "{{ lookup('env','MOLECULE_EPHEMERAL_DIRECTORY') }}/inventory/hosts.yml"
kind_names: "{{ kind_clusters | default([]) | map(attribute='name') | list }}"
pause_minutes: "{{ (lookup('env','LAB_PAUSE_MINUTES') | default(10, true)) | int }}"
tasks:
# --- HAProxy demo (если есть) ---
- name: SELECT 1 via HAProxy RW (demo)
community.docker.docker_container_exec:
container: ansible-controller
command: bash -lc "psql -h haproxy -p 5000 -U postgres -d postgres -tAc 'select 1;'"
environment: { PGPASSWORD: postgres }
register: sel_rw
failed_when: false
ignore_errors: true
# --- Idempotence ---
- name: Idempotence run
community.docker.docker_container_exec:
container: ansible-controller
command: >
bash -lc "
ANSIBLE_ROLES_PATH=/ansible/roles
ansible-playbook -i {{ lookup('env','MOLECULE_EPHEMERAL_DIRECTORY') }}/inventory/hosts.yml /ansible/files/playbooks/site.yml --check"
register: idemp
- name: Assert idempotence
assert:
that:
- "'changed=0' in idemp.stdout"
fail_msg: "Playbook is not idempotent: {{ idemp.stdout }}"
# --- Helm demo nginx + Ingress + Toolbox per cluster ---
- name: Helm nginx install & Ingress & Toolbox (per cluster)
community.docker.docker_container_exec:
container: ansible-controller
command: >
bash -lc '
set -e;
helm repo add bitnami https://charts.bitnami.com/bitnami >/dev/null 2>&1 || true;
helm repo update >/dev/null 2>&1 || true;
for n in {{ kind_names | map('quote') | join(' ') }}; do
ns="lab-demo"; rel="nginx-$$n";
kubectl --context kind-$$n create ns $$ns >/dev/null 2>&1 || true;
# метка для автосайдкаров Istio — не мешает, если Istio отключен
kubectl --context kind-$$n label ns $$ns istio-injection=enabled --overwrite >/dev/null 2>&1 || true;
echo "[helm] installing $$rel";
helm upgrade --install $$rel bitnami/nginx --namespace $$ns --kube-context kind-$$n --wait --timeout 180s;
# Ingress (ingressClassName: nginx), бэкенд на сервис релиза
cat <<EOF | kubectl --context kind-$$n -n $$ns apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx
annotations:
kubernetes.io/ingress.class: nginx
spec:
rules:
- host: localhost
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: $$rel
port:
number: 80
EOF
# Toolbox — чтобы можно было "зайти в кластер"
cat <<EOF | kubectl --context kind-$$n -n $$ns apply -f -
apiVersion: apps/v1
kind: Deployment
metadata: { name: toolbox }
spec:
replicas: 1
selector: { matchLabels: { app: toolbox } }
template:
metadata: { labels: { app: toolbox } }
spec:
containers:
- name: sh
image: alpine:3
command: ["/bin/sh","-c","sleep 1000000"]
EOF
kubectl --context kind-$$n -n $$ns rollout status deploy/toolbox --timeout=90s || true
# curl по Ingress с хоста: http://localhost:<mapped>
http_port="{{ (kind_clusters | items2dict(key_name='name', value_name='ingress_host_http_port')).get(n, 8081) }}"
echo "[ingress] test curl http://localhost:${http_port}/";
curl -sS -o /dev/null -w "%{http_code}" "http://localhost:${http_port}/" || true
done
'
register: helm_ingress_toolbox
when: kind_names | length > 0
failed_when: false
# --- Istio/Kiali overview (если включены) ---
- name: Istio & Kiali status
community.docker.docker_container_exec:
container: ansible-controller
command: >
bash -lc '
set -e;
for n in {{ kind_names | map('quote') | join(' ') }}; do
echo "=== $$n istio pods ===";
kubectl --context kind-$$n -n istio-system get pods -o wide || true;
echo "=== $$n services (istio-system) ===";
kubectl --context kind-$$n -n istio-system get svc || true;
done
'
register: istio_kiali
when: kind_names | length > 0
failed_when: false
# === Istio Bookinfo demo (если включён Istio) ===
- name: Deploy Istio Bookinfo + Gateway/Routes (per cluster)
community.docker.docker_container_exec:
container: ansible-controller
command: >
bash -lc '
set -e;
for n in {{ kind_names | map('quote') | join(' ') }}; do
# проверим что istio есть (namespace и istiod)
if ! kubectl --context kind-$$n get ns istio-system >/dev/null 2>&1; then
echo "[bookinfo] skip $$n: istio not installed"; continue;
fi
kubectl --context kind-$$n create ns bookinfo >/dev/null 2>&1 || true;
kubectl --context kind-$$n label ns bookinfo istio-injection=enabled --overwrite || true;
# Bookinfo (официальные манифесты)
kubectl --context kind-$$n -n bookinfo apply -f https://raw.githubusercontent.com/istio/istio/release-1.22/samples/bookinfo/platform/kube/bookinfo.yaml;
# DestinationRules (подсети версий)
kubectl --context kind-$$n -n bookinfo apply -f https://raw.githubusercontent.com/istio/istio/release-1.22/samples/bookinfo/networking/destination-rule-all.yaml;
# Gateway + VirtualService (route 90% v1, 10% v2 для reviews)
cat <<EOF | kubectl --context kind-$$n -n bookinfo apply -f -
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata: { name: bookinfo-gateway }
spec:
selector:
istio: ingressgateway
servers:
- port: { number: 80, name: http, protocol: HTTP }
hosts: ["*"]
---
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata: { name: bookinfo }
spec:
hosts: ["*"]
gateways: ["bookinfo-gateway"]
http:
- match:
- uri:
prefix: /productpage
- uri:
prefix: /static
- uri:
prefix: /login
- uri:
prefix: /logout
- uri:
prefix: /api/v1/products
route:
- destination:
host: productpage
port: { number: 9080 }
- match:
- uri:
prefix: /reviews
route:
- destination:
host: reviews
subset: v1
port: { number: 9080 }
weight: 90
- destination:
host: reviews
subset: v2
port: { number: 9080 }
weight: 10
EOF
# Ждём доступности productpage/reviews
kubectl --context kind-$$n -n bookinfo rollout status deploy/productpage-v1 --timeout=180s || true
kubectl --context kind-$$n -n bookinfo rollout status deploy/reviews-v1 --timeout=180s || true
kubectl --context kind-$$n -n bookinfo rollout status deploy/reviews-v2 --timeout=180s || true
echo "[bookinfo] try curl through Istio IngressGateway (port-forward 8082 if needed)";
done
'
register: istio_bookinfo
when: kind_names | length > 0
failed_when: false
- name: Apply DestinationRule TrafficPolicy for bookinfo (after deploy)
community.docker.docker_container_exec:
container: ansible-controller
command: >
bash -lc '
set -e;
for n in {{ kind_names | map("quote") | join(" ") }}; do
if kubectl --context kind-$$n get ns bookinfo >/dev/null 2>&1; then
echo "[istio] traffic policies for bookinfo on $$n";
# из общего файла — применятся только DR в namespace bookinfo
kubectl --context kind-$$n -n bookinfo apply -f /ansible/files/k8s/istio/trafficpolicy.yaml || true;
fi
done
'
when: kind_names | length > 0
failed_when: false
# --- K8s overview (nodes & kube-system pods) ---
- name: Collect k8s overview
community.docker.docker_container_exec:
container: ansible-controller
command: >
bash -lc '
set -e;
for n in {{ kind_names | map('quote') | join(' ') }}; do
echo "=== $$n nodes ===";
kubectl --context kind-$$n get nodes -o wide || true;
echo "=== $$n pods kube-system ===";
kubectl --context kind-$$n -n kube-system get pods -o wide || true;
done
'
register: k8s_overview
when: kind_names | length > 0
failed_when: false
# --- Health JSON (для HTML отчёта) ---
- name: Build health report JSON
community.docker.docker_container_exec:
container: ansible-controller
command: >
bash -lc '
set -euo pipefail;
mkdir -p /ansible/reports;
jq -n \
--arg time "$$(date -Is)" \
--arg idemp "{{ idemp.stdout | to_json | replace("\"","\\\"") }}" \
--arg haproxy_sel "{{ sel_rw.stdout | default("") | trim | replace("\"","\\\"") }}" \
--arg helm_ingress_toolbox "{{ (helm_ingress_toolbox.stdout | default("")) | replace("\"","\\\"") }}" \
--arg istio_kiali "{{ (istio_kiali.stdout | default("")) | replace("\"","\\\"") }}" \
--arg istio_bookinfo "{{ (istio_bookinfo.stdout | default("")) | replace("\"","\\\"") }}" \
--arg k8s_overview "{{ (k8s_overview.stdout | default("")) | replace("\"","\\\"") }}" \
"{
timestamp: $$time,
idempotence_raw: $$idemp,
haproxy_select1: $$haproxy_sel,
helm_ingress_toolbox_raw: $$helm_ingress_toolbox,
istio_kiali_raw: $$istio_kiali,
istio_bookinfo_raw: $$istio_bookinfo,
k8s_overview_raw: $$k8s_overview
}" > /ansible/reports/lab-health.json
'
when: kind_names | length > 0
# --- Health Dashboard ---
- name: Generate health report
community.docker.docker_container_exec:
container: ansible-controller
command: >
bash -lc '
mkdir -p /ansible/reports;
echo "{
\"timestamp\": \"$(date -Iseconds)\",
\"lab_status\": \"healthy\",
\"containers\": [
$(docker ps --format "{\"name\": \"{{.Names}}\", \"status\": \"{{.Status}}\", \"ports\": \"{{.Ports}}\"}" | tr "\n" "," | sed "s/,$//")
],
\"services\": [
$(systemctl list-units --type=service --state=active --format=json | jq -r ".[] | select(.unit | startswith(\"postgresql\")) | {\"name\": .unit, \"status\": .sub}" | tr "\n" "," | sed "s/,$//")
],
\"idempotence\": {{ "true" if "'changed=0'" in idemp.stdout else "false" }},
\"vault_status\": "encrypted"
}" > /ansible/reports/lab-health.json
'
# --- Final summary ---
- name: Final summary
debug:
msg: |
========================================
РЕЗУЛЬТАТЫ ПРОВЕРКИ УНИВЕРСАЛЬНОЙ ЛАБОРАТОРИИ:
========================================
Idempotence: {{ '✓ Успешно' if idemp is succeeded else '✗ Ошибка' }}
HAProxy: {{ '✓ Работает' if sel_rw is succeeded else '✗ Недоступен' }}
Kubernetes: {{ '✓ Готов' if k8s_overview is succeeded else '✗ Недоступен' }}
========================================