@@ -1,428 +0,0 @@
---
- hosts : localhost
gather_facts : false
vars :
# Получаем preset из переменной окружения или используем default
preset_name : "{{ lookup('env', 'MOLECULE_PRESET') | default('default') }}"
# Проверяем сначала в папке k8s, затем в основной папке presets
preset_file : "{{ '/workspace/molecule/presets/k8s/' + preset_name + '.yml' if (preset_name in ['k8s-minimal', 'kubernetes', 'k8s-full'] or preset_name.startswith('k8s-')) else '/workspace/molecule/presets/' + preset_name + '.yml' }}"
# Fallback значения если preset файл не найден
docker_network : labnet
generated_inventory : "{{ molecule_ephemeral_directory }}/inventory/hosts.ini"
images :
alt : "inecs/ansible-lab:alt-linux-latest"
astra : "inecs/ansible-lab:astra-linux-latest"
rhel : "inecs/ansible-lab:rhel-latest"
centos : "inecs/ansible-lab:centos-latest"
alma : "inecs/ansible-lab:alma-latest"
rocky : "inecs/ansible-lab:rocky-latest"
redos : "inecs/ansible-lab:redos-latest"
ubuntu : "inecs/ansible-lab:ubuntu-latest"
debian : "inecs/ansible-lab:debian-latest"
systemd_defaults :
privileged : true
command : "/sbin/init"
volumes :
- "/sys/fs/cgroup:/sys/fs/cgroup:rw"
tmpfs : [ "/run" , "/run/lock" ]
capabilities : [ "SYS_ADMIN" ]
hosts :
- name : u1
family : debian
groups : [ test]
kind_clusters : [ ]
tasks :
# - name: Install required collections
# command: ansible-galaxy collection install -r /workspace/requirements.yml
# delegate_to: localhost
# ignore_errors: true
# register: collections_install
# changed_when: false
# run_once: true
# become: true
# vars:
# ansible_python_interpreter: /usr/bin/python3
# environment:
# ANSIBLE_COLLECTIONS_PATH: /usr/share/ansible/collections
# Определяем архитектуру системы для корректной загрузки образов
- name : Detect system architecture
shell : |
arch=$(uname -m)
case $arch in
x86_64) echo "linux/amd64" ;;
aarch64|arm64) echo "linux/arm64" ;;
armv7l) echo "linux/arm/v7" ;;
*) echo "linux/amd64" ;;
esac
register : detected_platform
changed_when : false
- name : Set ansible_architecture variable
set_fact :
ansible_architecture : "{{ detected_platform.stdout }}"
- name : Load preset configuration
include_vars : "{{ preset_file }}"
when : preset_file is file
ignore_errors : true
# Фильтрация хостов по поддерживаемым платформам
- name : Filter hosts by supported platforms
set_fact :
filtered_hosts : "{{ filtered_hosts | default([]) + [item] }}"
loop : "{{ hosts }}"
when : |
item.supported_platforms is not defined or
ansible_architecture in item.supported_platforms
- name : Update hosts list with filtered results
set_fact :
hosts : "{{ filtered_hosts | default(hosts) }}"
- name : Display filtered hosts
debug :
msg : "Platform {{ ansible_architecture }}: {{ hosts | length }} hosts will be deployed"
- name : Ensure network exists
community.docker.docker_network :
name : "{{ docker_network }}"
state : present
# SYSTEMD nodes
- name : Pull systemd images with correct platform
command : "docker pull --platform {{ ansible_architecture }} {{ images[item.family] }}"
loop : "{{ hosts | selectattr('type','undefined') | list }}"
loop_control : { label : "{{ item.name }}" }
when : item.family is defined and images[item.family] is defined
register : pull_result
ignore_errors : yes
- name : Display pull results
debug :
msg : "Pulled {{ item.item.name }}: {{ 'OK' if (item.rc is defined and item.rc == 0) else 'SKIPPED (not available for this platform)' }}"
loop : "{{ pull_result.results | default([]) }}"
loop_control :
label : "{{ item.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([]) }}"
capabilities : "{{ systemd_defaults.capabilities | default([]) }}"
published_ports : "{{ item.publish | default([]) }}"
env : "{{ item.env | default({}) }}"
# Специальные настройки для Astra Linux и RedOS (для совместимости с amd64 базовыми образами)
security_opts : "{{ ['seccomp=unconfined', 'apparmor=unconfined'] if item.family in ['astra', 'redos'] else [] }}"
platform : "{{ 'linux/amd64' if item.family in ['astra', 'redos'] else omit }}"
state : started
restart_policy : unless-stopped
loop : "{{ hosts | selectattr('type','undefined') | list }}"
loop_control : { label : "{{ item.name }}" }
when : item.family is defined and images[item.family] is defined
# Ожидание стабилизации контейнеров
- name : Wait for containers to be ready
pause :
seconds : 5
when : hosts | length > 0
# Создание tmp директории в контейнерах
- name : Create Ansible tmp directory in containers
community.docker.docker_container_exec :
container : "{{ item.name }}"
command : "mkdir -p /tmp/.ansible-tmp && chmod 755 /tmp/.ansible-tmp"
loop : "{{ hosts | selectattr('type','undefined') | list }}"
loop_control : { label : "{{ item.name }}" }
when : item.family is defined and images[item.family] is defined
ignore_errors : true
retries : 3
delay : 2
# DinD nodes
- name : Start DinD nodes (docker:27-dind)
community.docker.docker_container :
name : "{{ item.name }}"
image : "docker:27-dind"
networks :
- name : "{{ docker_network }}"
privileged : true
env :
DOCKER_TLS_CERTDIR : ""
published_ports : "{{ item.publish | default([]) }}"
volumes : "{{ (item.volumes | default([])) + [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([]) }}"
capabilities : "{{ systemd_defaults.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 }}" }
when : item.family is defined and images[item.family] is defined
# Build groups map
- name : Initialize groups map
set_fact :
groups_map : {}
- 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_remote_tmp=/tmp/.ansible-tmp
{% for group, members in (groups_map | dictsort) %}
[{{ group }}]
{% for h in members %}{{ h }}
{% endfor %}
{% endfor %}
[all]
{% for h in hosts %}{{ h.name }}
{% endfor %}
{# Группа с Debian-based системами (Debian, Ubuntu, Alt) - используем /usr/bin/python3 #}
{% set debian_hosts = [] %}
{% for h in hosts %}
{% if h.family in ['ubuntu', 'debian', 'alt'] %}
{% set _ = debian_hosts.append(h.name) %}
{% endif %}
{% endfor %}
{% if debian_hosts %}
[debian_family:vars]
ansible_python_interpreter=/usr/bin/python3
[debian_family]
{% for h in debian_hosts %}{{ h }}
{% endfor %}
{% endif %}
{# Группа с RHEL-based системами (RHEL, CentOS, Alma, Rocky, RedOS) #}
{% set rhel_hosts = [] %}
{% for h in hosts %}
{% if h.family in ['rhel', 'centos', 'alma', 'rocky', 'redos'] %}
{% set _ = rhel_hosts.append(h.name) %}
{% endif %}
{% endfor %}
{% if rhel_hosts %}
[rhel_family:vars]
ansible_python_interpreter=/usr/bin/python3
[rhel_family]
{% for h in rhel_hosts %}{{ h }}
{% endfor %}
{% endif %}
{# Astra Linux - используем /usr/bin/python3 #}
{% set astra_hosts = [] %}
{% for h in hosts %}
{% if h.family == 'astra' %}
{% set _ = astra_hosts.append(h.name) %}
{% endif %}
{% endfor %}
{% if astra_hosts %}
[astra_family:vars]
ansible_python_interpreter=/usr/bin/python3
[astra_family]
{% for h in astra_hosts %}{{ h }}
{% endfor %}
{% endif %}
{# Глобальный fallback для остальных хостов #}
[unmatched_hosts:vars]
ansible_python_interpreter=auto_silent
- name : Ensure inventory directory exists
file :
path : "{{ generated_inventory | dirname }}"
state : directory
mode : "0755"
- name : Write inventory file
copy :
dest : "{{ generated_inventory }}"
content : "{{ inv_content }}"
mode : "0644"
- name : Display inventory summary
debug :
msg : |
📋 Inventory Summary:
- Total hosts: {{ hosts | length }}
- Groups: {{ groups_map.keys() | list | join(', ') }}
- Systemd nodes: {{ hosts | selectattr('type','undefined') | list | length }}
- DinD nodes: {{ hosts | selectattr('type','defined') | selectattr('type','equalto','dind') | list | length }}
- DOoD nodes: {{ hosts | selectattr('type','defined') | selectattr('type','equalto','dood') | list | length }}
# ---------- Kind clusters (если определены) ----------
- name : Prepare kind cluster configs
set_fact :
kind_config_content : |
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
{% if (item.addons|default({})).ingress_nginx|default(false) %}
extraPortMappings:
- containerPort: 80
hostPort: {{ item.ingress_host_http_port | default(8081) }}
protocol: TCP
- containerPort: 443
hostPort: {{ item.ingress_host_https_port | default(8443) }}
protocol: TCP
{% endif %}
{% for i in range(item.workers | default(0)) %}
- role: worker
{% endfor %}
networking:
apiServerAddress: "0.0.0.0"
apiServerPort: {{ item.api_port | default(0) }}
loop : "{{ kind_clusters | default([]) }}"
when : (kind_clusters | default([])) | length > 0
- name : Create kind cluster configs
community.docker.docker_container_exec :
container : "{{ ansible_controller_container | default('ansible-controller') }}"
command : >
bash -c "
mkdir -p /ansible/.kind;
echo '{{ kind_config_content }}' > /ansible/.kind/{{ item.name }}.yaml
"
loop : "{{ kind_clusters | default([]) }}"
when : (kind_clusters | default([])) | length > 0
- name : Create kind clusters
community.docker.docker_container_exec :
container : ansible-controller
command : >
bash -lc '
set -e;
for n in {{ (kind_clusters | default([]) | map(attribute="name") | list) | map('quote') | join(' ') }}; do
if kind get clusters | grep -qx "$$n"; then
echo "[kind] cluster $$n already exists";
else
echo "[kind] creating $$n";
kind create cluster --name "$$n" --config "/ansible/.kind/$$n.yaml";
fi
done
'
when : (kind_clusters | default([])) | length > 0
- name : Install Ingress NGINX, Metrics Server, Istio, Kiali, Prometheus Stack (per cluster, if enabled)
community.docker.docker_container_exec :
container : ansible-controller
command : >
bash -lc '
set -e;
helm repo add kiali https://kiali.org/helm-charts >/dev/null 2>&1 || true;
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts >/dev/null 2>&1 || true;
helm repo update >/dev/null 2>&1 || true;
for n in {{ (kind_clusters | default([]) | map(attribute="name") | list) | map('quote') | join(' ') }}; do
# ingress-nginx
if {{ (kind_clusters | items2dict(key_name="name", value_name="addons")).get(n, {}).get("ingress_nginx", False) | to_json }}; then
echo "[addons] ingress-nginx on $$n";
kubectl --context kind-$$n apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml || true;
kubectl --context kind-$$n -n ingress-nginx rollout status deploy/ingress-nginx-controller --timeout=180s || true;
fi
# metrics-server
if {{ (kind_clusters | items2dict(key_name="name", value_name="addons")).get(n, {}).get("metrics_server", False) | to_json }}; then
echo "[addons] metrics-server on $$n";
kubectl --context kind-$$n apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml || true;
kubectl --context kind-$$n -n kube-system patch deploy metrics-server -p \
"{\"spec\":{\"template\":{\"spec\":{\"containers\":[{\"name\":\"metrics-server\",\"args\":[\"--kubelet-insecure-tls\",\"--kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname\"]}]}}}}}" || true;
fi
# istio (demo profile)
if {{ (kind_clusters | items2dict(key_name="name", value_name="addons")).get(n, {}).get("istio", False) | to_json }}; then
echo "[addons] istio (demo profile) on $$n";
istioctl install -y --set profile=demo --context kind-$$n;
kubectl --context kind-$$n -n istio-system rollout status deploy/istiod --timeout=180s || true;
kubectl --context kind-$$n -n istio-system rollout status deploy/istio-ingressgateway --timeout=180s || true;
fi
# kiali (server chart, anonymous auth) — требует istio/metrics
if {{ (kind_clusters | items2dict(key_name="name", value_name="addons")).get(n, {}).get("kiali", False) | to_json }}; then
echo "[addons] kiali on $$n";
kubectl --context kind-$$n create ns istio-system >/dev/null 2>&1 || true;
helm upgrade --install kiali-server kiali/kiali-server \
--namespace istio-system --kube-context kind-$$n \
--set auth.strategy=anonymous --wait --timeout 180s;
fi
# kube-prometheus-stack (Prometheus + Grafana)
if {{ (kind_clusters | items2dict(key_name="name", value_name="addons")).get(n, {}).get("prometheus_stack", False) | to_json }}; then
echo "[addons] kube-prometheus-stack on $$n";
kubectl --context kind-$$n create ns monitoring >/dev/null 2>&1 || true;
helm upgrade --install monitoring prometheus-community/kube-prometheus-stack \
--namespace monitoring --kube-context kind-$$n \
--set grafana.adminPassword=admin \
--set grafana.defaultDashboardsTimezone=browser \
--wait --timeout 600s;
# дождаться графаны
kubectl --context kind-$$n -n monitoring rollout status deploy/monitoring-grafana --timeout=300s || true;
fi
done
'
when : (kind_clusters | default([])) | length > 0
- name : Setup NodePort for addons
community.docker.docker_container_exec :
container : ansible-controller
command : >
bash -lc '
for n in {{ (kind_clusters | default([]) | map(attribute="name") | list) | map('quote') | join(' ') }}; do
{% for cluster in kind_clusters | default([]) %}
{% if cluster.addon_ports is defined %}
if [ "$$n" = "{{ cluster.name }}" ]; then
{% if cluster.addon_ports.prometheus is defined %}
echo "[ports] Prometheus: {{ cluster.addon_ports.prometheus }}";
kubectl --context kind-{{ cluster.name }} patch svc -n monitoring monitoring-kube-prom-prometheus --type='json' -p='[{"op": "replace", "path": "/spec/type", "value":"NodePort"},{"op": "replace", "path": "/spec/ports/0/nodePort", "value":{{ cluster.addon_ports.prometheus }}}]' 2>/dev/null || true;
{% endif %}
{% if cluster.addon_ports.grafana is defined %}
echo "[ports] Grafana: {{ cluster.addon_ports.grafana }}";
kubectl --context kind-{{ cluster.name }} patch svc -n monitoring monitoring-grafana --type='json' -p='[{"op": "replace", "path": "/spec/type", "value":"NodePort"},{"op": "replace", "path": "/spec/ports/0/nodePort", "value":{{ cluster.addon_ports.grafana }}}]' 2>/dev/null || true;
{% endif %}
{% if cluster.addon_ports.kiali is defined %}
echo "[ports] Kiali: {{ cluster.addon_ports.kiali }}";
kubectl --context kind-{{ cluster.name }} patch svc -n istio-system kiali --type='json' -p='[{"op": "replace", "path": "/spec/type", "value":"NodePort"},{"op": "replace", "path": "/spec/ports/0/nodePort", "value":{{ cluster.addon_ports.kiali }}}]' 2>/dev/null || true;
{% endif %}
fi
{% endif %}
{% endfor %}
done
'
when : (kind_clusters | default([])) | length > 0