first commit
This commit is contained in:
32
roles/cni/defaults/main.yml
Normal file
32
roles/cni/defaults/main.yml
Normal file
@@ -0,0 +1,32 @@
|
||||
---
|
||||
# CNI плагин выбирается через k3s_cni в group_vars/all/main.yml:
|
||||
# flannel (по умолчанию, встроен в k3s)
|
||||
# calico — Tigera operator
|
||||
# cilium — eBPF, устанавливается через Helm
|
||||
|
||||
# ─── Calico ───────────────────────────────────────────────────────────────────
|
||||
calico_version: "v3.28.0"
|
||||
calico_operator_url: "https://raw.githubusercontent.com/projectcalico/calico/{{ calico_version }}/manifests/tigera-operator.yaml"
|
||||
calico_namespace: "calico-system"
|
||||
|
||||
# CIDR должен совпадать с k3s_cluster_cidr
|
||||
calico_pod_cidr: "{{ k3s_cluster_cidr | default('10.42.0.0/16') }}"
|
||||
|
||||
# Encapsulation: VXLAN | IPIP | None
|
||||
calico_encapsulation: "VXLAN"
|
||||
|
||||
# ─── Cilium ───────────────────────────────────────────────────────────────────
|
||||
cilium_version: "1.15.5"
|
||||
cilium_namespace: "kube-system"
|
||||
cilium_chart_repo: "https://helm.cilium.io"
|
||||
|
||||
# IP k3s API server (обычно kube_vip_address если используется kube-vip)
|
||||
cilium_k8s_service_host: "{{ kube_vip_address | default(hostvars[groups['k3s_master'][0]]['ansible_host']) }}"
|
||||
cilium_k8s_service_port: 6443
|
||||
|
||||
# Включить Hubble (observability)
|
||||
cilium_hubble_enabled: true
|
||||
cilium_hubble_ui_enabled: false
|
||||
|
||||
# Ресурсы оператора
|
||||
cilium_operator_replicas: 1
|
||||
36
roles/cni/tasks/calico.yml
Normal file
36
roles/cni/tasks/calico.yml
Normal file
@@ -0,0 +1,36 @@
|
||||
---
|
||||
- name: Apply Tigera operator
|
||||
ansible.builtin.command: >
|
||||
k3s kubectl apply -f {{ calico_operator_url }}
|
||||
register: calico_operator
|
||||
changed_when: "'created' in calico_operator.stdout or 'configured' in calico_operator.stdout"
|
||||
retries: 3
|
||||
delay: 10
|
||||
|
||||
- name: Wait for Tigera operator to be ready
|
||||
ansible.builtin.command: >
|
||||
k3s kubectl -n tigera-operator wait deployment/tigera-operator
|
||||
--for=condition=Available --timeout=120s
|
||||
changed_when: false
|
||||
retries: 6
|
||||
delay: 10
|
||||
|
||||
- name: Template Calico Installation CR
|
||||
ansible.builtin.template:
|
||||
src: calico-installation.yaml.j2
|
||||
dest: /tmp/calico-installation.yaml
|
||||
mode: '0644'
|
||||
|
||||
- name: Apply Calico Installation CR
|
||||
ansible.builtin.command: k3s kubectl apply -f /tmp/calico-installation.yaml
|
||||
register: calico_install
|
||||
changed_when: "'created' in calico_install.stdout or 'configured' in calico_install.stdout"
|
||||
|
||||
- name: Wait for Calico nodes to be ready
|
||||
ansible.builtin.command: >
|
||||
k3s kubectl -n {{ calico_namespace }} wait pod
|
||||
-l k8s-app=calico-node
|
||||
--for=condition=Ready --timeout=180s
|
||||
changed_when: false
|
||||
retries: 6
|
||||
delay: 15
|
||||
36
roles/cni/tasks/cilium.yml
Normal file
36
roles/cni/tasks/cilium.yml
Normal file
@@ -0,0 +1,36 @@
|
||||
---
|
||||
- name: Add Cilium Helm repo
|
||||
kubernetes.core.helm_repository:
|
||||
name: cilium
|
||||
repo_url: "{{ cilium_chart_repo }}"
|
||||
|
||||
- name: Install Cilium via Helm
|
||||
kubernetes.core.helm:
|
||||
name: cilium
|
||||
chart_ref: cilium/cilium
|
||||
chart_version: "{{ cilium_version }}"
|
||||
release_namespace: "{{ cilium_namespace }}"
|
||||
create_namespace: false
|
||||
kubeconfig: /etc/rancher/k3s/k3s.yaml
|
||||
values:
|
||||
k8sServiceHost: "{{ cilium_k8s_service_host }}"
|
||||
k8sServicePort: "{{ cilium_k8s_service_port }}"
|
||||
ipam:
|
||||
mode: kubernetes
|
||||
operator:
|
||||
replicas: "{{ cilium_operator_replicas }}"
|
||||
hubble:
|
||||
enabled: "{{ cilium_hubble_enabled }}"
|
||||
ui:
|
||||
enabled: "{{ cilium_hubble_ui_enabled }}"
|
||||
kubeProxyReplacement: true
|
||||
register: cilium_deploy
|
||||
|
||||
- name: Wait for Cilium pods to be ready
|
||||
ansible.builtin.command: >
|
||||
k3s kubectl -n {{ cilium_namespace }} wait pod
|
||||
-l k8s-app=cilium
|
||||
--for=condition=Ready --timeout=180s
|
||||
changed_when: false
|
||||
retries: 6
|
||||
delay: 15
|
||||
12
roles/cni/tasks/main.yml
Normal file
12
roles/cni/tasks/main.yml
Normal file
@@ -0,0 +1,12 @@
|
||||
---
|
||||
- name: Skip CNI role when using built-in Flannel
|
||||
ansible.builtin.meta: end_play
|
||||
when: k3s_cni | default('flannel') == 'flannel'
|
||||
|
||||
- name: Install Calico CNI
|
||||
ansible.builtin.include_tasks: calico.yml
|
||||
when: k3s_cni == 'calico'
|
||||
|
||||
- name: Install Cilium CNI
|
||||
ansible.builtin.include_tasks: cilium.yml
|
||||
when: k3s_cni == 'cilium'
|
||||
13
roles/cni/templates/calico-installation.yaml.j2
Normal file
13
roles/cni/templates/calico-installation.yaml.j2
Normal file
@@ -0,0 +1,13 @@
|
||||
---
|
||||
apiVersion: operator.tigera.io/v1
|
||||
kind: Installation
|
||||
metadata:
|
||||
name: default
|
||||
spec:
|
||||
calicoNetwork:
|
||||
ipPools:
|
||||
- blockSize: 26
|
||||
cidr: "{{ calico_pod_cidr }}"
|
||||
encapsulation: "{{ calico_encapsulation }}"
|
||||
natOutgoing: Enabled
|
||||
nodeSelector: all()
|
||||
29
roles/csi-nfs/defaults/main.yml
Normal file
29
roles/csi-nfs/defaults/main.yml
Normal file
@@ -0,0 +1,29 @@
|
||||
---
|
||||
# Версия CSI NFS Driver
|
||||
csi_nfs_version: "v4.8.0"
|
||||
|
||||
# Helm chart
|
||||
csi_nfs_chart_repo: "https://raw.githubusercontent.com/kubernetes-csi/csi-driver-nfs/master/charts"
|
||||
csi_nfs_chart_name: "csi-driver-nfs"
|
||||
csi_nfs_namespace: "kube-system"
|
||||
|
||||
# NFS сервер — IP или hostname машины с NFS
|
||||
# Обычно это master нода или отдельный NFS сервер
|
||||
csi_nfs_server: "{{ hostvars[groups['nfs_server'][0]]['ansible_host'] | default(hostvars[groups['k3s_master'][0]]['ansible_host']) }}"
|
||||
|
||||
# Базовый путь экспорта на NFS сервере
|
||||
csi_nfs_share: "/srv/nfs/k8s"
|
||||
|
||||
# StorageClass настройки
|
||||
# Имя включает hostname NFS сервера: nfs-master01, nfs-storage01, etc.
|
||||
csi_nfs_storageclass_name: "nfs-{{ groups['nfs_server'][0] | default(groups['k3s_master'][0]) }}"
|
||||
csi_nfs_storageclass_default: true
|
||||
csi_nfs_reclaim_policy: "Delete" # Delete | Retain
|
||||
csi_nfs_volume_binding_mode: "Immediate"
|
||||
|
||||
# Монтирование подпапок для каждого PVC
|
||||
# onDelete: delete | retain | archive
|
||||
csi_nfs_on_delete: "delete"
|
||||
|
||||
# nfs-common нужен на всех нодах для монтирования
|
||||
csi_nfs_install_client: true
|
||||
8
roles/csi-nfs/meta/main.yml
Normal file
8
roles/csi-nfs/meta/main.yml
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
galaxy_info:
|
||||
author: "your-name"
|
||||
description: "Deploy CSI NFS Driver and StorageClass for K3S"
|
||||
license: "MIT"
|
||||
min_ansible_version: "2.12"
|
||||
dependencies:
|
||||
- role: nfs-server
|
||||
27
roles/csi-nfs/tasks/install_helm.yml
Normal file
27
roles/csi-nfs/tasks/install_helm.yml
Normal file
@@ -0,0 +1,27 @@
|
||||
---
|
||||
- name: Check if Helm is installed
|
||||
ansible.builtin.command: helm version --short
|
||||
register: helm_check
|
||||
failed_when: false
|
||||
changed_when: false
|
||||
become: true
|
||||
|
||||
- name: Download and install Helm
|
||||
ansible.builtin.shell: |
|
||||
set -o pipefail
|
||||
curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
|
||||
args:
|
||||
executable: /bin/bash
|
||||
become: true
|
||||
when: helm_check.rc != 0
|
||||
retries: 3
|
||||
delay: 5
|
||||
|
||||
- name: Install kubernetes.core collection (for Helm module)
|
||||
ansible.builtin.command: >
|
||||
ansible-galaxy collection install kubernetes.core --upgrade
|
||||
delegate_to: localhost
|
||||
become: false
|
||||
changed_when: true
|
||||
run_once: true
|
||||
failed_when: false
|
||||
102
roles/csi-nfs/tasks/main.yml
Normal file
102
roles/csi-nfs/tasks/main.yml
Normal file
@@ -0,0 +1,102 @@
|
||||
---
|
||||
- name: Install NFS client on all K3S nodes
|
||||
ansible.builtin.apt:
|
||||
name: nfs-common
|
||||
state: present
|
||||
update_cache: true
|
||||
become: true
|
||||
when: csi_nfs_install_client
|
||||
# Выполняется на ВСЕХ нодах кластера (master + workers)
|
||||
|
||||
- name: Install Helm (if not present)
|
||||
ansible.builtin.include_tasks: install_helm.yml
|
||||
run_once: true
|
||||
delegate_to: "{{ groups['k3s_master'][0] }}"
|
||||
|
||||
- name: Add CSI NFS Helm repo
|
||||
kubernetes.core.helm_repository:
|
||||
name: "{{ csi_nfs_chart_name }}"
|
||||
repo_url: "{{ csi_nfs_chart_repo }}"
|
||||
run_once: true
|
||||
delegate_to: "{{ groups['k3s_master'][0] }}"
|
||||
environment:
|
||||
KUBECONFIG: /etc/rancher/k3s/k3s.yaml
|
||||
become: true
|
||||
|
||||
- name: Deploy CSI NFS Driver via Helm
|
||||
kubernetes.core.helm:
|
||||
name: "{{ csi_nfs_chart_name }}"
|
||||
chart_ref: "{{ csi_nfs_chart_name }}/{{ csi_nfs_chart_name }}"
|
||||
chart_version: "{{ csi_nfs_version }}"
|
||||
release_namespace: "{{ csi_nfs_namespace }}"
|
||||
create_namespace: false
|
||||
wait: true
|
||||
wait_condition:
|
||||
type: Ready
|
||||
timeout: "5m0s"
|
||||
values:
|
||||
controller:
|
||||
replicas: 1
|
||||
resources:
|
||||
limits:
|
||||
cpu: 200m
|
||||
memory: 200Mi
|
||||
requests:
|
||||
cpu: 10m
|
||||
memory: 20Mi
|
||||
node:
|
||||
resources:
|
||||
limits:
|
||||
cpu: 100m
|
||||
memory: 100Mi
|
||||
requests:
|
||||
cpu: 10m
|
||||
memory: 20Mi
|
||||
run_once: true
|
||||
delegate_to: "{{ groups['k3s_master'][0] }}"
|
||||
environment:
|
||||
KUBECONFIG: /etc/rancher/k3s/k3s.yaml
|
||||
become: true
|
||||
|
||||
- name: Deploy NFS StorageClass
|
||||
ansible.builtin.template:
|
||||
src: storageclass.yaml.j2
|
||||
dest: /tmp/nfs-storageclass.yaml
|
||||
mode: '0644'
|
||||
delegate_to: "{{ groups['k3s_master'][0] }}"
|
||||
run_once: true
|
||||
|
||||
- name: Apply NFS StorageClass
|
||||
ansible.builtin.command: >
|
||||
k3s kubectl apply -f /tmp/nfs-storageclass.yaml
|
||||
become: true
|
||||
delegate_to: "{{ groups['k3s_master'][0] }}"
|
||||
run_once: true
|
||||
changed_when: true
|
||||
|
||||
- name: Verify CSI NFS pods are running
|
||||
ansible.builtin.command: >
|
||||
k3s kubectl -n {{ csi_nfs_namespace }} get pods
|
||||
-l app=csi-nfs-controller
|
||||
-o jsonpath='{.items[*].status.phase}'
|
||||
become: true
|
||||
delegate_to: "{{ groups['k3s_master'][0] }}"
|
||||
run_once: true
|
||||
register: csi_pods
|
||||
until: "'Running' in csi_pods.stdout"
|
||||
retries: 20
|
||||
delay: 10
|
||||
changed_when: false
|
||||
|
||||
- name: Show StorageClass
|
||||
ansible.builtin.command: k3s kubectl get storageclass
|
||||
become: true
|
||||
delegate_to: "{{ groups['k3s_master'][0] }}"
|
||||
run_once: true
|
||||
register: sc_list
|
||||
changed_when: false
|
||||
|
||||
- name: Display StorageClasses
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ sc_list.stdout_lines }}"
|
||||
run_once: true
|
||||
23
roles/csi-nfs/templates/storageclass.yaml.j2
Normal file
23
roles/csi-nfs/templates/storageclass.yaml.j2
Normal file
@@ -0,0 +1,23 @@
|
||||
---
|
||||
# NFS StorageClass — управляется Ansible (roles/csi-nfs)
|
||||
apiVersion: storage.k8s.io/v1
|
||||
kind: StorageClass
|
||||
metadata:
|
||||
name: {{ csi_nfs_storageclass_name }}
|
||||
annotations:
|
||||
storageclass.kubernetes.io/is-default-class: "{{ 'true' if csi_nfs_storageclass_default else 'false' }}"
|
||||
provisioner: nfs.csi.k8s.io
|
||||
parameters:
|
||||
server: {{ csi_nfs_server }}
|
||||
share: {{ csi_nfs_share }}
|
||||
# Создавать подпапку для каждого PVC (рекомендуется)
|
||||
subDir: ${pvc.metadata.namespace}/${pvc.metadata.name}/${pv.metadata.name}
|
||||
onDelete: {{ csi_nfs_on_delete }}
|
||||
reclaimPolicy: {{ csi_nfs_reclaim_policy }}
|
||||
volumeBindingMode: {{ csi_nfs_volume_binding_mode }}
|
||||
mountOptions:
|
||||
- nfsvers=4.1
|
||||
- hard
|
||||
- intr
|
||||
- timeo=600
|
||||
- retrans=3
|
||||
48
roles/ingress-nginx/defaults/main.yml
Normal file
48
roles/ingress-nginx/defaults/main.yml
Normal file
@@ -0,0 +1,48 @@
|
||||
---
|
||||
# Версия ingress-nginx
|
||||
ingress_nginx_version: "4.10.1" # Helm chart version
|
||||
ingress_nginx_namespace: "ingress-nginx"
|
||||
|
||||
# Helm repo
|
||||
ingress_nginx_chart_repo: "https://kubernetes.github.io/ingress-nginx"
|
||||
ingress_nginx_chart_name: "ingress-nginx"
|
||||
|
||||
# Тип сервиса: LoadBalancer (с kube-vip) или NodePort
|
||||
ingress_nginx_service_type: "LoadBalancer"
|
||||
|
||||
# Если LoadBalancer — статический IP (из пула kube-vip)
|
||||
# Оставь пустым для автоматического назначения
|
||||
ingress_nginx_load_balancer_ip: ""
|
||||
|
||||
# NodePort порты (используются когда service_type = NodePort)
|
||||
ingress_nginx_http_nodeport: 30080
|
||||
ingress_nginx_https_nodeport: 30443
|
||||
|
||||
# Количество реплик контроллера
|
||||
ingress_nginx_replica_count: 1
|
||||
|
||||
# Включить Prometheus метрики
|
||||
ingress_nginx_metrics_enabled: false
|
||||
|
||||
# Использовать DaemonSet вместо Deployment (рекомендуется для edge/RPi кластеров)
|
||||
ingress_nginx_use_daemonset: false
|
||||
|
||||
# Дополнительные аргументы контроллера
|
||||
ingress_nginx_extra_args: {}
|
||||
# Пример:
|
||||
# ingress_nginx_extra_args:
|
||||
# enable-ssl-passthrough: ""
|
||||
# default-ssl-certificate: "default/my-tls-secret"
|
||||
|
||||
# IngressClass
|
||||
ingress_nginx_class_name: "nginx"
|
||||
ingress_nginx_set_default_class: true
|
||||
|
||||
# Ресурсы контроллера
|
||||
ingress_nginx_resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 90Mi
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 256Mi
|
||||
8
roles/ingress-nginx/handlers/main.yml
Normal file
8
roles/ingress-nginx/handlers/main.yml
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
- name: Restart K3S server
|
||||
ansible.builtin.systemd:
|
||||
name: k3s
|
||||
state: restarted
|
||||
daemon_reload: true
|
||||
become: true
|
||||
delegate_to: "{{ groups['k3s_master'][0] }}"
|
||||
8
roles/ingress-nginx/meta/main.yml
Normal file
8
roles/ingress-nginx/meta/main.yml
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
galaxy_info:
|
||||
author: "your-name"
|
||||
description: "Deploy ingress-nginx controller via Helm for K3S"
|
||||
license: "MIT"
|
||||
min_ansible_version: "2.12"
|
||||
dependencies:
|
||||
- role: kube-vip
|
||||
103
roles/ingress-nginx/tasks/main.yml
Normal file
103
roles/ingress-nginx/tasks/main.yml
Normal file
@@ -0,0 +1,103 @@
|
||||
---
|
||||
- name: Disable K3S built-in Traefik (required before ingress-nginx)
|
||||
ansible.builtin.lineinfile:
|
||||
path: "{{ k3s_config_dir }}/config.yaml"
|
||||
line: "disable: traefik"
|
||||
regexp: "^disable:"
|
||||
state: present
|
||||
become: true
|
||||
delegate_to: "{{ groups['k3s_master'][0] }}"
|
||||
notify: Restart K3S server
|
||||
when: not k3s_disable_traefik
|
||||
|
||||
- name: Flush handlers (restart K3S if Traefik was just disabled)
|
||||
ansible.builtin.meta: flush_handlers
|
||||
|
||||
- name: Ensure ingress-nginx namespace exists
|
||||
ansible.builtin.command: >
|
||||
k3s kubectl create namespace {{ ingress_nginx_namespace }}
|
||||
--dry-run=client -o yaml | k3s kubectl apply -f -
|
||||
become: true
|
||||
delegate_to: "{{ groups['k3s_master'][0] }}"
|
||||
run_once: true
|
||||
changed_when: false
|
||||
|
||||
- name: Add ingress-nginx Helm repo
|
||||
kubernetes.core.helm_repository:
|
||||
name: "{{ ingress_nginx_chart_name }}"
|
||||
repo_url: "{{ ingress_nginx_chart_repo }}"
|
||||
become: true
|
||||
delegate_to: "{{ groups['k3s_master'][0] }}"
|
||||
run_once: true
|
||||
environment:
|
||||
KUBECONFIG: /etc/rancher/k3s/k3s.yaml
|
||||
|
||||
- name: Template Helm values
|
||||
ansible.builtin.template:
|
||||
src: ingress-nginx-values.yaml.j2
|
||||
dest: /tmp/ingress-nginx-values.yaml
|
||||
mode: '0644'
|
||||
delegate_to: "{{ groups['k3s_master'][0] }}"
|
||||
run_once: true
|
||||
|
||||
- name: Deploy ingress-nginx via Helm
|
||||
kubernetes.core.helm:
|
||||
name: "{{ ingress_nginx_chart_name }}"
|
||||
chart_ref: "{{ ingress_nginx_chart_name }}/{{ ingress_nginx_chart_name }}"
|
||||
chart_version: "{{ ingress_nginx_version }}"
|
||||
release_namespace: "{{ ingress_nginx_namespace }}"
|
||||
create_namespace: true
|
||||
wait: true
|
||||
timeout: "5m0s"
|
||||
values_files:
|
||||
- /tmp/ingress-nginx-values.yaml
|
||||
become: true
|
||||
delegate_to: "{{ groups['k3s_master'][0] }}"
|
||||
run_once: true
|
||||
environment:
|
||||
KUBECONFIG: /etc/rancher/k3s/k3s.yaml
|
||||
|
||||
- name: Wait for ingress-nginx controller to be ready
|
||||
ansible.builtin.command: >
|
||||
k3s kubectl -n {{ ingress_nginx_namespace }} rollout status
|
||||
deployment/{{ ingress_nginx_chart_name }}-controller
|
||||
--timeout=180s
|
||||
become: true
|
||||
delegate_to: "{{ groups['k3s_master'][0] }}"
|
||||
run_once: true
|
||||
register: nginx_rollout
|
||||
changed_when: false
|
||||
retries: 3
|
||||
delay: 10
|
||||
until: nginx_rollout.rc == 0
|
||||
|
||||
- name: Get ingress-nginx service info
|
||||
ansible.builtin.command: >
|
||||
k3s kubectl -n {{ ingress_nginx_namespace }}
|
||||
get svc {{ ingress_nginx_chart_name }}-controller
|
||||
-o wide
|
||||
become: true
|
||||
delegate_to: "{{ groups['k3s_master'][0] }}"
|
||||
run_once: true
|
||||
register: nginx_svc
|
||||
changed_when: false
|
||||
|
||||
- name: Show ingress-nginx service
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ nginx_svc.stdout_lines }}"
|
||||
run_once: true
|
||||
|
||||
- name: Deploy test IngressClass (verify)
|
||||
ansible.builtin.command: >
|
||||
k3s kubectl get ingressclass {{ ingress_nginx_class_name }}
|
||||
become: true
|
||||
delegate_to: "{{ groups['k3s_master'][0] }}"
|
||||
run_once: true
|
||||
register: ingress_class_check
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Show IngressClass status
|
||||
ansible.builtin.debug:
|
||||
msg: "IngressClass '{{ ingress_nginx_class_name }}': {{ 'OK' if ingress_class_check.rc == 0 else 'NOT FOUND' }}"
|
||||
run_once: true
|
||||
96
roles/ingress-nginx/templates/ingress-nginx-values.yaml.j2
Normal file
96
roles/ingress-nginx/templates/ingress-nginx-values.yaml.j2
Normal file
@@ -0,0 +1,96 @@
|
||||
## ingress-nginx Helm values
|
||||
## Управляется Ansible (roles/ingress-nginx)
|
||||
|
||||
controller:
|
||||
ingressClassResource:
|
||||
name: "{{ ingress_nginx_class_name }}"
|
||||
enabled: true
|
||||
default: {{ ingress_nginx_set_default_class | lower }}
|
||||
|
||||
ingressClass: "{{ ingress_nginx_class_name }}"
|
||||
|
||||
{% if ingress_nginx_use_daemonset %}
|
||||
kind: DaemonSet
|
||||
{% else %}
|
||||
kind: Deployment
|
||||
replicaCount: {{ ingress_nginx_replica_count }}
|
||||
{% endif %}
|
||||
|
||||
service:
|
||||
type: {{ ingress_nginx_service_type }}
|
||||
{% if ingress_nginx_service_type == "LoadBalancer" and ingress_nginx_load_balancer_ip %}
|
||||
loadBalancerIP: "{{ ingress_nginx_load_balancer_ip }}"
|
||||
{% endif %}
|
||||
{% if ingress_nginx_service_type == "NodePort" %}
|
||||
nodePorts:
|
||||
http: {{ ingress_nginx_http_nodeport }}
|
||||
https: {{ ingress_nginx_https_nodeport }}
|
||||
{% endif %}
|
||||
|
||||
resources:
|
||||
requests:
|
||||
cpu: "{{ ingress_nginx_resources.requests.cpu }}"
|
||||
memory: "{{ ingress_nginx_resources.requests.memory }}"
|
||||
limits:
|
||||
cpu: "{{ ingress_nginx_resources.limits.cpu }}"
|
||||
memory: "{{ ingress_nginx_resources.limits.memory }}"
|
||||
|
||||
# Логирование в JSON для удобного парсинга
|
||||
config:
|
||||
log-format-upstream: >-
|
||||
{"time":"$time_iso8601","remote_addr":"$remote_addr",
|
||||
"x_forwarded_for":"$http_x_forwarded_for","request_id":"$req_id",
|
||||
"remote_user":"$remote_user","bytes_sent":"$bytes_sent",
|
||||
"request_time":"$request_time","status":"$status",
|
||||
"vhost":"$host","request_proto":"$server_protocol",
|
||||
"path":"$uri","request_query":"$args",
|
||||
"request_length":"$request_length","duration":"$request_time",
|
||||
"method":"$request_method","http_referrer":"$http_referer",
|
||||
"http_user_agent":"$http_user_agent"}
|
||||
use-forwarded-headers: "true"
|
||||
compute-full-forwarded-for: "true"
|
||||
use-proxy-protocol: "false"
|
||||
proxy-body-size: "50m"
|
||||
proxy-read-timeout: "600"
|
||||
proxy-send-timeout: "600"
|
||||
|
||||
{% if ingress_nginx_extra_args %}
|
||||
extraArgs:
|
||||
{% for key, value in ingress_nginx_extra_args.items() %}
|
||||
{{ key }}: "{{ value }}"
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
metrics:
|
||||
enabled: {{ ingress_nginx_metrics_enabled | lower }}
|
||||
{% if ingress_nginx_metrics_enabled %}
|
||||
serviceMonitor:
|
||||
enabled: false # включи если есть Prometheus Operator
|
||||
{% endif %}
|
||||
|
||||
# Tolerations для запуска на мастере и RPi
|
||||
tolerations:
|
||||
- key: "node-role.kubernetes.io/control-plane"
|
||||
operator: "Exists"
|
||||
effect: "NoSchedule"
|
||||
- key: "node-type"
|
||||
operator: "Equal"
|
||||
value: "raspberry-pi"
|
||||
effect: "NoSchedule"
|
||||
|
||||
admissionWebhooks:
|
||||
enabled: true
|
||||
failurePolicy: Fail
|
||||
|
||||
defaultBackend:
|
||||
enabled: true
|
||||
image:
|
||||
registry: registry.k8s.io
|
||||
image: defaultbackend-amd64
|
||||
resources:
|
||||
limits:
|
||||
cpu: 10m
|
||||
memory: 20Mi
|
||||
requests:
|
||||
cpu: 10m
|
||||
memory: 20Mi
|
||||
63
roles/istio/defaults/main.yml
Normal file
63
roles/istio/defaults/main.yml
Normal file
@@ -0,0 +1,63 @@
|
||||
---
|
||||
# Включить установку Istio (false = пропустить)
|
||||
istio_enabled: false
|
||||
|
||||
istio_version: "1.22.2" # Helm chart version (совпадает с версией Istio)
|
||||
istio_namespace: "istio-system"
|
||||
|
||||
istio_chart_repo: "https://istio-release.storage.googleapis.com/charts"
|
||||
|
||||
# Устанавливать Istio Ingress Gateway (LoadBalancer)
|
||||
istio_install_gateway: true
|
||||
|
||||
# Включить mutual TLS между сервисами
|
||||
istio_mtls_mode: "STRICT" # STRICT | PERMISSIVE | DISABLE
|
||||
|
||||
# Ресурсы istiod (control plane)
|
||||
istio_pilot_resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 512Mi
|
||||
|
||||
# Ресурсы gateway
|
||||
istio_gateway_resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 256Mi
|
||||
|
||||
# Включить Prometheus-совместимый сбор метрик
|
||||
istio_telemetry_enabled: true
|
||||
|
||||
# ─── Kiali (Service Mesh UI) ──────────────────────────────────────────────────
|
||||
# Установка по желанию (истио должен быть включён)
|
||||
kiali_enabled: false
|
||||
|
||||
kiali_version: "1.86.0" # Helm chart version
|
||||
kiali_namespace: "{{ istio_namespace }}"
|
||||
kiali_chart_repo: "https://kiali.org/helm-charts"
|
||||
|
||||
# Токен для входа в Kiali UI.
|
||||
# Задай в group_vars/all/vault.yml: vault_kiali_token: "ваш-токен"
|
||||
# После первой установки Ansible выведет сгенерированный токен —
|
||||
# скопируй его в vault.yml для последующих запусков.
|
||||
kiali_token: "{{ vault_kiali_token | default('') }}"
|
||||
|
||||
# Ingress для Kiali (требует ingress-nginx)
|
||||
kiali_ingress_enabled: false
|
||||
kiali_ingress_host: "kiali.local"
|
||||
kiali_ingress_class: "nginx"
|
||||
|
||||
# Ресурсы Kiali
|
||||
kiali_resources:
|
||||
requests:
|
||||
cpu: 50m
|
||||
memory: 64Mi
|
||||
limits:
|
||||
cpu: 200m
|
||||
memory: 256Mi
|
||||
6
roles/istio/meta/main.yml
Normal file
6
roles/istio/meta/main.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
galaxy_info:
|
||||
role_name: istio
|
||||
description: Deploy Istio service mesh via Helm on K3S
|
||||
min_ansible_version: "2.14"
|
||||
dependencies: []
|
||||
76
roles/istio/molecule/default/converge.yml
Normal file
76
roles/istio/molecule/default/converge.yml
Normal file
@@ -0,0 +1,76 @@
|
||||
---
|
||||
- name: Converge — istio role template tests
|
||||
hosts: all
|
||||
become: false
|
||||
gather_facts: false
|
||||
|
||||
vars:
|
||||
istio_enabled: true
|
||||
istio_version: "1.22.2"
|
||||
istio_namespace: "istio-system"
|
||||
istio_mtls_mode: "STRICT"
|
||||
istio_install_gateway: true
|
||||
istio_telemetry_enabled: true
|
||||
|
||||
istio_pilot_resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 512Mi
|
||||
|
||||
istio_gateway_resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 256Mi
|
||||
|
||||
kiali_enabled: true
|
||||
kiali_namespace: "istio-system"
|
||||
kiali_auth_strategy: "token"
|
||||
kiali_ingress_enabled: false
|
||||
kiali_ingress_host: "kiali.local"
|
||||
kiali_ingress_class: "nginx"
|
||||
kiali_resources:
|
||||
requests:
|
||||
cpu: 50m
|
||||
memory: 64Mi
|
||||
limits:
|
||||
cpu: 200m
|
||||
memory: 256Mi
|
||||
|
||||
# Vars для kiali-values.yaml.j2 (интеграция с prometheus-stack)
|
||||
prometheus_stack_enabled: true
|
||||
prometheus_stack_release_name: "prom"
|
||||
prometheus_stack_namespace: "monitoring"
|
||||
prometheus_grafana_enabled: true
|
||||
grafana_admin_user: "admin"
|
||||
prometheus_grafana_admin_password: "molecule-test-pass"
|
||||
|
||||
tasks:
|
||||
- name: Render istiod Helm values
|
||||
ansible.builtin.template:
|
||||
src: "{{ playbook_dir }}/../../templates/istiod-values.yaml.j2"
|
||||
dest: /tmp/istiod-values.yaml
|
||||
mode: '0644'
|
||||
|
||||
- name: Render Kiali Helm values
|
||||
ansible.builtin.template:
|
||||
src: "{{ playbook_dir }}/../../templates/kiali-values.yaml.j2"
|
||||
dest: /tmp/kiali-values.yaml
|
||||
mode: '0644'
|
||||
|
||||
- name: Render PeerAuthentication manifest
|
||||
ansible.builtin.template:
|
||||
src: "{{ playbook_dir }}/../../templates/peer-authentication.yaml.j2"
|
||||
dest: /tmp/peer-authentication.yaml
|
||||
mode: '0644'
|
||||
|
||||
- name: Render Kiali token secret manifest
|
||||
ansible.builtin.template:
|
||||
src: "{{ playbook_dir }}/../../templates/kiali-token-secret.yaml.j2"
|
||||
dest: /tmp/kiali-token-secret.yaml
|
||||
mode: '0644'
|
||||
25
roles/istio/molecule/default/molecule.yml
Normal file
25
roles/istio/molecule/default/molecule.yml
Normal file
@@ -0,0 +1,25 @@
|
||||
---
|
||||
driver:
|
||||
name: docker
|
||||
|
||||
platforms:
|
||||
- name: istio-test
|
||||
image: geerlingguy/docker-ubuntu2204-ansible:latest
|
||||
pre_build_image: true
|
||||
|
||||
provisioner:
|
||||
name: ansible
|
||||
playbooks:
|
||||
converge: converge.yml
|
||||
verify: verify.yml
|
||||
config_options:
|
||||
defaults:
|
||||
interpreter_python: auto_silent
|
||||
|
||||
verifier:
|
||||
name: ansible
|
||||
|
||||
lint: |
|
||||
set -e
|
||||
yamllint .
|
||||
ansible-lint
|
||||
107
roles/istio/molecule/default/verify.yml
Normal file
107
roles/istio/molecule/default/verify.yml
Normal file
@@ -0,0 +1,107 @@
|
||||
---
|
||||
- name: Verify — istio role templates
|
||||
hosts: all
|
||||
become: false
|
||||
gather_facts: false
|
||||
|
||||
tasks:
|
||||
# ── istiod Helm values ───────────────────────────────────────────────────────
|
||||
- name: Read istiod values
|
||||
ansible.builtin.slurp:
|
||||
src: /tmp/istiod-values.yaml
|
||||
register: istiod_raw
|
||||
|
||||
- name: Parse istiod YAML
|
||||
ansible.builtin.set_fact:
|
||||
istiod: "{{ istiod_raw.content | b64decode | from_yaml }}"
|
||||
|
||||
- name: Assert istiod pilot resources exist
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- istiod.pilot is defined
|
||||
- istiod.pilot.resources.requests.cpu == '100m'
|
||||
- istiod.pilot.resources.limits.memory == '512Mi'
|
||||
fail_msg: "istiod pilot resources настроены неверно"
|
||||
|
||||
- name: Assert meshConfig exists
|
||||
ansible.builtin.assert:
|
||||
that: istiod.meshConfig is defined
|
||||
fail_msg: "meshConfig отсутствует в istiod values"
|
||||
|
||||
- name: Assert telemetry flag
|
||||
ansible.builtin.assert:
|
||||
that: istiod.meshConfig.enablePrometheusMerge == true
|
||||
fail_msg: "enablePrometheusMerge должен быть true при istio_telemetry_enabled=true"
|
||||
|
||||
# ── Kiali Helm values ────────────────────────────────────────────────────────
|
||||
- name: Read kiali values
|
||||
ansible.builtin.slurp:
|
||||
src: /tmp/kiali-values.yaml
|
||||
register: kiali_raw
|
||||
|
||||
- name: Parse kiali YAML
|
||||
ansible.builtin.set_fact:
|
||||
kiali: "{{ kiali_raw.content | b64decode | from_yaml }}"
|
||||
|
||||
- name: Assert kiali auth strategy is token
|
||||
ansible.builtin.assert:
|
||||
that: kiali.auth.strategy == 'token'
|
||||
fail_msg: "Kiali auth.strategy должен быть 'token', получено: {{ kiali.auth.strategy }}"
|
||||
|
||||
- name: Assert kiali external_services prometheus URL
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- kiali.external_services.prometheus.url is defined
|
||||
- "'prom-kube-prometheus-stack-prometheus' in kiali.external_services.prometheus.url"
|
||||
fail_msg: "Kiali Prometheus URL настроен неверно: {{ kiali.external_services.prometheus.url }}"
|
||||
|
||||
- name: Assert kiali grafana integration
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- kiali.external_services.grafana.enabled == true
|
||||
- kiali.external_services.grafana.auth.username == 'admin'
|
||||
fail_msg: "Kiali Grafana интеграция настроена неверно"
|
||||
|
||||
# ── PeerAuthentication ───────────────────────────────────────────────────────
|
||||
- name: Read PeerAuthentication manifest
|
||||
ansible.builtin.slurp:
|
||||
src: /tmp/peer-authentication.yaml
|
||||
register: peer_raw
|
||||
|
||||
- name: Parse PeerAuthentication YAML
|
||||
ansible.builtin.set_fact:
|
||||
peer_auth: "{{ peer_raw.content | b64decode | from_yaml }}"
|
||||
|
||||
- name: Assert PeerAuthentication kind
|
||||
ansible.builtin.assert:
|
||||
that: peer_auth.kind == 'PeerAuthentication'
|
||||
fail_msg: "Неверный kind: {{ peer_auth.kind }}"
|
||||
|
||||
- name: Assert mTLS mode is STRICT
|
||||
ansible.builtin.assert:
|
||||
that: peer_auth.spec.mtls.mode == 'STRICT'
|
||||
fail_msg: "mTLS mode должен быть STRICT, получено: {{ peer_auth.spec.mtls.mode }}"
|
||||
|
||||
# ── Kiali Token Secret ───────────────────────────────────────────────────────
|
||||
- name: Read kiali token secret manifest
|
||||
ansible.builtin.slurp:
|
||||
src: /tmp/kiali-token-secret.yaml
|
||||
register: kiali_secret_raw
|
||||
|
||||
- name: Parse kiali token secret YAML
|
||||
ansible.builtin.set_fact:
|
||||
kiali_secret: "{{ kiali_secret_raw.content | b64decode | from_yaml }}"
|
||||
|
||||
- name: Assert kiali secret type
|
||||
ansible.builtin.assert:
|
||||
that: kiali_secret.type == 'kubernetes.io/service-account-token'
|
||||
fail_msg: "Неверный тип секрета: {{ kiali_secret.type }}"
|
||||
|
||||
- name: Assert kiali secret annotation
|
||||
ansible.builtin.assert:
|
||||
that: kiali_secret.metadata.annotations['kubernetes.io/service-account.name'] == 'kiali-admin'
|
||||
fail_msg: "Неверная аннотация service-account"
|
||||
|
||||
- name: Summary
|
||||
ansible.builtin.debug:
|
||||
msg: "Все проверки istio/kiali прошли успешно"
|
||||
274
roles/istio/tasks/main.yml
Normal file
274
roles/istio/tasks/main.yml
Normal file
@@ -0,0 +1,274 @@
|
||||
---
|
||||
- name: Istio — skip if not enabled
|
||||
ansible.builtin.debug:
|
||||
msg: "Istio отключён (istio_enabled: false). Пропускаем."
|
||||
when: not istio_enabled
|
||||
run_once: true
|
||||
|
||||
- name: Istio — install
|
||||
when: istio_enabled
|
||||
block:
|
||||
- name: Install Helm (if needed)
|
||||
ansible.builtin.include_tasks: "{{ playbook_dir }}/../roles/csi-nfs/tasks/install_helm.yml"
|
||||
|
||||
- name: Add Istio Helm repo
|
||||
kubernetes.core.helm_repository:
|
||||
name: istio
|
||||
repo_url: "{{ istio_chart_repo }}"
|
||||
become: true
|
||||
delegate_to: "{{ groups['k3s_master'][0] }}"
|
||||
run_once: true
|
||||
environment:
|
||||
KUBECONFIG: /etc/rancher/k3s/k3s.yaml
|
||||
|
||||
- name: Create istio-system namespace
|
||||
ansible.builtin.command: >
|
||||
k3s kubectl create namespace {{ istio_namespace }}
|
||||
--dry-run=client -o yaml | k3s kubectl apply -f -
|
||||
become: true
|
||||
delegate_to: "{{ groups['k3s_master'][0] }}"
|
||||
run_once: true
|
||||
changed_when: false
|
||||
|
||||
- name: Install Istio base CRDs (istio/base)
|
||||
kubernetes.core.helm:
|
||||
name: istio-base
|
||||
chart_ref: istio/base
|
||||
chart_version: "{{ istio_version }}"
|
||||
release_namespace: "{{ istio_namespace }}"
|
||||
create_namespace: false
|
||||
wait: true
|
||||
timeout: "5m0s"
|
||||
values:
|
||||
defaultRevision: default
|
||||
become: true
|
||||
delegate_to: "{{ groups['k3s_master'][0] }}"
|
||||
run_once: true
|
||||
environment:
|
||||
KUBECONFIG: /etc/rancher/k3s/k3s.yaml
|
||||
|
||||
- name: Template istiod values
|
||||
ansible.builtin.template:
|
||||
src: istiod-values.yaml.j2
|
||||
dest: /tmp/istiod-values.yaml
|
||||
mode: '0644'
|
||||
delegate_to: "{{ groups['k3s_master'][0] }}"
|
||||
run_once: true
|
||||
|
||||
- name: Install istiod (control plane)
|
||||
kubernetes.core.helm:
|
||||
name: istiod
|
||||
chart_ref: istio/istiod
|
||||
chart_version: "{{ istio_version }}"
|
||||
release_namespace: "{{ istio_namespace }}"
|
||||
create_namespace: false
|
||||
wait: true
|
||||
timeout: "5m0s"
|
||||
values_files:
|
||||
- /tmp/istiod-values.yaml
|
||||
become: true
|
||||
delegate_to: "{{ groups['k3s_master'][0] }}"
|
||||
run_once: true
|
||||
environment:
|
||||
KUBECONFIG: /etc/rancher/k3s/k3s.yaml
|
||||
|
||||
- name: Wait for istiod to be ready
|
||||
ansible.builtin.command: >
|
||||
k3s kubectl -n {{ istio_namespace }}
|
||||
rollout status deployment/istiod --timeout=180s
|
||||
become: true
|
||||
delegate_to: "{{ groups['k3s_master'][0] }}"
|
||||
run_once: true
|
||||
register: istiod_ready
|
||||
changed_when: false
|
||||
retries: 3
|
||||
delay: 10
|
||||
until: istiod_ready.rc == 0
|
||||
|
||||
- name: Install Istio Gateway
|
||||
kubernetes.core.helm:
|
||||
name: istio-ingressgateway
|
||||
chart_ref: istio/gateway
|
||||
chart_version: "{{ istio_version }}"
|
||||
release_namespace: "{{ istio_namespace }}"
|
||||
create_namespace: false
|
||||
wait: true
|
||||
timeout: "5m0s"
|
||||
values:
|
||||
resources:
|
||||
requests:
|
||||
cpu: "{{ istio_gateway_resources.requests.cpu }}"
|
||||
memory: "{{ istio_gateway_resources.requests.memory }}"
|
||||
limits:
|
||||
cpu: "{{ istio_gateway_resources.limits.cpu }}"
|
||||
memory: "{{ istio_gateway_resources.limits.memory }}"
|
||||
service:
|
||||
type: LoadBalancer
|
||||
become: true
|
||||
delegate_to: "{{ groups['k3s_master'][0] }}"
|
||||
run_once: true
|
||||
when: istio_install_gateway
|
||||
environment:
|
||||
KUBECONFIG: /etc/rancher/k3s/k3s.yaml
|
||||
|
||||
- name: Apply default PeerAuthentication (mTLS mode)
|
||||
ansible.builtin.template:
|
||||
src: peer-authentication.yaml.j2
|
||||
dest: /tmp/istio-peer-auth.yaml
|
||||
mode: '0644'
|
||||
delegate_to: "{{ groups['k3s_master'][0] }}"
|
||||
run_once: true
|
||||
|
||||
- name: Apply PeerAuthentication to cluster
|
||||
ansible.builtin.command: >
|
||||
k3s kubectl apply -f /tmp/istio-peer-auth.yaml
|
||||
become: true
|
||||
delegate_to: "{{ groups['k3s_master'][0] }}"
|
||||
run_once: true
|
||||
changed_when: true
|
||||
|
||||
- name: Show Istio status
|
||||
ansible.builtin.command: >
|
||||
k3s kubectl -n {{ istio_namespace }} get pods
|
||||
become: true
|
||||
delegate_to: "{{ groups['k3s_master'][0] }}"
|
||||
run_once: true
|
||||
register: istio_pods
|
||||
changed_when: false
|
||||
|
||||
- name: Istio pods
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ istio_pods.stdout_lines }}"
|
||||
run_once: true
|
||||
|
||||
# ─── Kiali ────────────────────────────────────────────────────────────────────
|
||||
- name: Kiali — skip if not enabled
|
||||
ansible.builtin.debug:
|
||||
msg: "Kiali отключён (kiali_enabled: false). Пропускаем."
|
||||
when: istio_enabled and not kiali_enabled
|
||||
run_once: true
|
||||
|
||||
- name: Kiali — install
|
||||
when: istio_enabled and kiali_enabled
|
||||
block:
|
||||
- name: Add Kiali Helm repo
|
||||
kubernetes.core.helm_repository:
|
||||
name: kiali
|
||||
repo_url: "{{ kiali_chart_repo }}"
|
||||
become: true
|
||||
delegate_to: "{{ groups['k3s_master'][0] }}"
|
||||
run_once: true
|
||||
environment:
|
||||
KUBECONFIG: /etc/rancher/k3s/k3s.yaml
|
||||
|
||||
- name: Create kiali-admin ServiceAccount
|
||||
ansible.builtin.command: >
|
||||
k3s kubectl create serviceaccount kiali-admin
|
||||
-n {{ kiali_namespace }}
|
||||
--dry-run=client -o yaml | k3s kubectl apply -f -
|
||||
become: true
|
||||
delegate_to: "{{ groups['k3s_master'][0] }}"
|
||||
run_once: true
|
||||
changed_when: false
|
||||
|
||||
- name: Bind kiali-admin to cluster-admin
|
||||
ansible.builtin.command: >
|
||||
k3s kubectl create clusterrolebinding kiali-admin
|
||||
--clusterrole=cluster-admin
|
||||
--serviceaccount={{ kiali_namespace }}:kiali-admin
|
||||
--dry-run=client -o yaml | k3s kubectl apply -f -
|
||||
become: true
|
||||
delegate_to: "{{ groups['k3s_master'][0] }}"
|
||||
run_once: true
|
||||
changed_when: false
|
||||
|
||||
- name: Create long-lived token secret for kiali-admin
|
||||
ansible.builtin.template:
|
||||
src: kiali-token-secret.yaml.j2
|
||||
dest: /tmp/kiali-token-secret.yaml
|
||||
mode: '0644'
|
||||
delegate_to: "{{ groups['k3s_master'][0] }}"
|
||||
run_once: true
|
||||
|
||||
- name: Apply kiali-admin token secret
|
||||
ansible.builtin.command: >
|
||||
k3s kubectl apply -f /tmp/kiali-token-secret.yaml
|
||||
become: true
|
||||
delegate_to: "{{ groups['k3s_master'][0] }}"
|
||||
run_once: true
|
||||
changed_when: false
|
||||
|
||||
- name: Wait for k8s to populate the token
|
||||
ansible.builtin.command: >
|
||||
k3s kubectl -n {{ kiali_namespace }}
|
||||
get secret kiali-admin-token
|
||||
-o jsonpath="{.data.token}"
|
||||
become: true
|
||||
delegate_to: "{{ groups['k3s_master'][0] }}"
|
||||
run_once: true
|
||||
register: kiali_token_check
|
||||
until: kiali_token_check.stdout | length > 0
|
||||
retries: 10
|
||||
delay: 3
|
||||
changed_when: false
|
||||
|
||||
- name: Decode Kiali login token
|
||||
ansible.builtin.set_fact:
|
||||
kiali_generated_token: "{{ kiali_token_check.stdout | b64decode }}"
|
||||
run_once: true
|
||||
|
||||
- name: Template Kiali Helm values
|
||||
ansible.builtin.template:
|
||||
src: kiali-values.yaml.j2
|
||||
dest: /tmp/kiali-values.yaml
|
||||
mode: '0644'
|
||||
delegate_to: "{{ groups['k3s_master'][0] }}"
|
||||
run_once: true
|
||||
|
||||
- name: Deploy Kiali via Helm
|
||||
kubernetes.core.helm:
|
||||
name: kiali-server
|
||||
chart_ref: kiali/kiali-server
|
||||
chart_version: "{{ kiali_version }}"
|
||||
release_namespace: "{{ kiali_namespace }}"
|
||||
create_namespace: false
|
||||
wait: true
|
||||
timeout: "5m0s"
|
||||
values_files:
|
||||
- /tmp/kiali-values.yaml
|
||||
become: true
|
||||
delegate_to: "{{ groups['k3s_master'][0] }}"
|
||||
run_once: true
|
||||
environment:
|
||||
KUBECONFIG: /etc/rancher/k3s/k3s.yaml
|
||||
|
||||
- name: Wait for Kiali to be ready
|
||||
ansible.builtin.command: >
|
||||
k3s kubectl -n {{ kiali_namespace }}
|
||||
rollout status deployment/kiali --timeout=180s
|
||||
become: true
|
||||
delegate_to: "{{ groups['k3s_master'][0] }}"
|
||||
run_once: true
|
||||
register: kiali_ready
|
||||
changed_when: false
|
||||
retries: 3
|
||||
delay: 10
|
||||
until: kiali_ready.rc == 0
|
||||
|
||||
- name: Show Kiali access info
|
||||
ansible.builtin.debug:
|
||||
msg:
|
||||
- "══════════════════════════════════════════════════"
|
||||
- " Kiali UI доступен через port-forward:"
|
||||
- " kubectl -n {{ kiali_namespace }} port-forward svc/kiali 20001:20001"
|
||||
- " Откройте: http://localhost:20001"
|
||||
- "{% if kiali_ingress_enabled %} Или через Ingress: http://{{ kiali_ingress_host }}{% endif %}"
|
||||
- ""
|
||||
- " Стратегия аутентификации: token"
|
||||
- " Токен для входа:"
|
||||
- " {{ kiali_generated_token }}"
|
||||
- ""
|
||||
- " Сохрани токен в vault.yml:"
|
||||
- " vault_kiali_token: <токен выше>"
|
||||
- "══════════════════════════════════════════════════"
|
||||
run_once: true
|
||||
36
roles/istio/templates/istiod-values.yaml.j2
Normal file
36
roles/istio/templates/istiod-values.yaml.j2
Normal file
@@ -0,0 +1,36 @@
|
||||
## istiod Helm values
|
||||
## Управляется Ansible (roles/istio)
|
||||
|
||||
pilot:
|
||||
resources:
|
||||
requests:
|
||||
cpu: "{{ istio_pilot_resources.requests.cpu }}"
|
||||
memory: "{{ istio_pilot_resources.requests.memory }}"
|
||||
limits:
|
||||
cpu: "{{ istio_pilot_resources.limits.cpu }}"
|
||||
memory: "{{ istio_pilot_resources.limits.memory }}"
|
||||
|
||||
# Tolerations — можно запускать на мастерах
|
||||
tolerations:
|
||||
- key: "node-role.kubernetes.io/control-plane"
|
||||
operator: "Exists"
|
||||
effect: "NoSchedule"
|
||||
|
||||
meshConfig:
|
||||
accessLogFile: /dev/stdout
|
||||
enableTracing: false
|
||||
{% if istio_telemetry_enabled %}
|
||||
defaultConfig:
|
||||
proxyMetadata: {}
|
||||
enablePrometheusMerge: true
|
||||
{% endif %}
|
||||
|
||||
global:
|
||||
proxy:
|
||||
resources:
|
||||
requests:
|
||||
cpu: 50m
|
||||
memory: 64Mi
|
||||
limits:
|
||||
cpu: 200m
|
||||
memory: 128Mi
|
||||
10
roles/istio/templates/kiali-token-secret.yaml.j2
Normal file
10
roles/istio/templates/kiali-token-secret.yaml.j2
Normal file
@@ -0,0 +1,10 @@
|
||||
## Долгосрочный токен для ServiceAccount kiali-admin
|
||||
## k8s автоматически заполняет поле .data.token
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: kiali-admin-token
|
||||
namespace: {{ kiali_namespace }}
|
||||
annotations:
|
||||
kubernetes.io/service-account.name: kiali-admin
|
||||
type: kubernetes.io/service-account-token
|
||||
64
roles/istio/templates/kiali-values.yaml.j2
Normal file
64
roles/istio/templates/kiali-values.yaml.j2
Normal file
@@ -0,0 +1,64 @@
|
||||
## Kiali Helm values
|
||||
## Управляется Ansible (roles/istio)
|
||||
|
||||
auth:
|
||||
strategy: token
|
||||
|
||||
deployment:
|
||||
# Использовать существующий ServiceAccount kiali-admin
|
||||
service_account: kiali-admin
|
||||
resources:
|
||||
requests:
|
||||
cpu: "{{ kiali_resources.requests.cpu }}"
|
||||
memory: "{{ kiali_resources.requests.memory }}"
|
||||
limits:
|
||||
cpu: "{{ kiali_resources.limits.cpu }}"
|
||||
memory: "{{ kiali_resources.limits.memory }}"
|
||||
tolerations:
|
||||
- key: "node-role.kubernetes.io/control-plane"
|
||||
operator: "Exists"
|
||||
effect: "NoSchedule"
|
||||
|
||||
{% if kiali_ingress_enabled %}
|
||||
ingress:
|
||||
enabled: true
|
||||
class_name: "{{ kiali_ingress_class }}"
|
||||
override_yaml:
|
||||
spec:
|
||||
rules:
|
||||
- host: "{{ kiali_ingress_host }}"
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: kiali
|
||||
port:
|
||||
number: 20001
|
||||
{% endif %}
|
||||
|
||||
external_services:
|
||||
prometheus:
|
||||
{% if prometheus_stack_enabled %}
|
||||
url: "http://{{ prometheus_stack_release_name }}-kube-prometheus-stack-prometheus.{{ prometheus_stack_namespace }}:9090"
|
||||
{% else %}
|
||||
url: "http://prometheus-operated.monitoring:9090"
|
||||
{% endif %}
|
||||
|
||||
grafana:
|
||||
enabled: {{ prometheus_grafana_enabled | lower }}
|
||||
{% if prometheus_stack_enabled and prometheus_grafana_enabled %}
|
||||
url: "http://{{ prometheus_stack_release_name }}-grafana.{{ prometheus_stack_namespace }}:80"
|
||||
auth:
|
||||
username: "{{ grafana_admin_user }}"
|
||||
password: "{{ prometheus_grafana_admin_password }}"
|
||||
type: basic
|
||||
{% endif %}
|
||||
|
||||
istio:
|
||||
root_namespace: "{{ istio_namespace }}"
|
||||
|
||||
server:
|
||||
port: 20001
|
||||
web_root: /kiali
|
||||
9
roles/istio/templates/peer-authentication.yaml.j2
Normal file
9
roles/istio/templates/peer-authentication.yaml.j2
Normal file
@@ -0,0 +1,9 @@
|
||||
## Глобальный режим mTLS для всего mesh
|
||||
apiVersion: security.istio.io/v1beta1
|
||||
kind: PeerAuthentication
|
||||
metadata:
|
||||
name: default
|
||||
namespace: {{ istio_namespace }}
|
||||
spec:
|
||||
mtls:
|
||||
mode: {{ istio_mtls_mode }}
|
||||
19
roles/k3s/defaults/main.yml
Normal file
19
roles/k3s/defaults/main.yml
Normal file
@@ -0,0 +1,19 @@
|
||||
---
|
||||
# Token for joining cluster nodes (override in vault!)
|
||||
k3s_token: "changeme-use-ansible-vault"
|
||||
|
||||
# Master node IP (used by agents to join)
|
||||
k3s_master_ip: "{{ hostvars[groups['k3s_master'][0]]['ansible_host'] }}"
|
||||
|
||||
# K3S API server URL
|
||||
k3s_api_url: "https://{{ k3s_master_ip }}:6443"
|
||||
|
||||
# Write kubeconfig to local machine
|
||||
k3s_fetch_kubeconfig: true
|
||||
k3s_kubeconfig_local_path: "./kubeconfig"
|
||||
|
||||
# Raspberry Pi specific
|
||||
rpi_cgroup_enable: true
|
||||
rpi_cmdline_path: /boot/cmdline.txt
|
||||
# For newer RPi OS (bookworm)
|
||||
rpi_cmdline_path_new: /boot/firmware/cmdline.txt
|
||||
21
roles/k3s/handlers/main.yml
Normal file
21
roles/k3s/handlers/main.yml
Normal file
@@ -0,0 +1,21 @@
|
||||
---
|
||||
- name: Restart K3S server
|
||||
ansible.builtin.systemd:
|
||||
name: k3s
|
||||
state: restarted
|
||||
daemon_reload: true
|
||||
become: "{{ k3s_become }}"
|
||||
|
||||
- name: Restart K3S agent
|
||||
ansible.builtin.systemd:
|
||||
name: k3s-agent
|
||||
state: restarted
|
||||
daemon_reload: true
|
||||
become: "{{ k3s_become }}"
|
||||
|
||||
- name: Reboot Raspberry Pi
|
||||
ansible.builtin.reboot:
|
||||
reboot_timeout: 300
|
||||
connect_timeout: 60
|
||||
msg: "Rebooting after enabling cgroups for K3S"
|
||||
become: "{{ k3s_become }}"
|
||||
14
roles/k3s/meta/main.yml
Normal file
14
roles/k3s/meta/main.yml
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
galaxy_info:
|
||||
author: "your-name"
|
||||
description: "Install and configure K3S Kubernetes cluster"
|
||||
license: "MIT"
|
||||
min_ansible_version: "2.12"
|
||||
platforms:
|
||||
- name: Ubuntu
|
||||
versions: ["20.04", "22.04", "24.04"]
|
||||
- name: Debian
|
||||
versions: ["11", "12"]
|
||||
- name: Raspbian
|
||||
versions: ["11", "12"]
|
||||
dependencies: []
|
||||
56
roles/k3s/molecule/default/converge.yml
Normal file
56
roles/k3s/molecule/default/converge.yml
Normal file
@@ -0,0 +1,56 @@
|
||||
---
|
||||
- name: Converge — k3s role unit tests
|
||||
hosts: all
|
||||
become: true
|
||||
gather_facts: true
|
||||
|
||||
vars:
|
||||
k3s_token: "molecule-test-token-abc123"
|
||||
k3s_version: "v1.29.3+k3s1"
|
||||
k3s_become: true
|
||||
k3s_fetch_kubeconfig: false
|
||||
k3s_node_labels: []
|
||||
k3s_node_taints: []
|
||||
k3s_cluster_cidr: "10.42.0.0/16"
|
||||
k3s_service_cidr: "10.43.0.0/16"
|
||||
k3s_cluster_dns: "10.43.0.10"
|
||||
k3s_flannel_backend: "vxlan"
|
||||
k3s_install_dir: /usr/local/bin
|
||||
k3s_config_dir: /etc/rancher/k3s
|
||||
k3s_data_dir: /var/lib/rancher/k3s
|
||||
k3s_disable_traefik: true
|
||||
k3s_disable_servicelb: false
|
||||
k3s_disable_local_storage: false
|
||||
k3s_extra_server_args: ""
|
||||
k3s_api_url: "https://127.0.0.1:6443"
|
||||
|
||||
pre_tasks:
|
||||
- name: Mock k3s binary (симулирует уже установленный k3s)
|
||||
ansible.builtin.copy:
|
||||
content: "#!/bin/bash\nexit 0\n"
|
||||
dest: /usr/local/bin/k3s
|
||||
mode: '0755'
|
||||
|
||||
- name: Create k3s server data directory
|
||||
ansible.builtin.file:
|
||||
path: /var/lib/rancher/k3s/server
|
||||
state: directory
|
||||
mode: '0700'
|
||||
|
||||
- name: Mock k3s node-token
|
||||
ansible.builtin.copy:
|
||||
content: "K10::server:molecule-test-node-token\n"
|
||||
dest: /var/lib/rancher/k3s/server/node-token
|
||||
mode: '0600'
|
||||
|
||||
tasks:
|
||||
# ── Тест 1: Предварительные требования ─────────────────────────────────────
|
||||
- name: Test prereqs — install packages
|
||||
ansible.builtin.include_tasks: "{{ playbook_dir }}/../../tasks/prereqs.yml"
|
||||
|
||||
# ── Тест 2: Генерация конфигурационного файла ───────────────────────────────
|
||||
- name: Test server config template rendering
|
||||
ansible.builtin.template:
|
||||
src: "{{ playbook_dir }}/../../templates/k3s-server-config.yaml.j2"
|
||||
dest: /etc/rancher/k3s/config.yaml
|
||||
mode: '0600'
|
||||
38
roles/k3s/molecule/default/molecule.yml
Normal file
38
roles/k3s/molecule/default/molecule.yml
Normal file
@@ -0,0 +1,38 @@
|
||||
---
|
||||
driver:
|
||||
name: docker
|
||||
|
||||
platforms:
|
||||
- name: k3s-node
|
||||
image: geerlingguy/docker-ubuntu2204-ansible:latest
|
||||
pre_build_image: true
|
||||
privileged: true
|
||||
volumes:
|
||||
- /sys/fs/cgroup:/sys/fs/cgroup:rw
|
||||
cgroupns_mode: host
|
||||
command: /lib/systemd/systemd
|
||||
groups:
|
||||
- k3s_master
|
||||
- k3s_cluster
|
||||
|
||||
provisioner:
|
||||
name: ansible
|
||||
playbooks:
|
||||
prepare: prepare.yml
|
||||
converge: converge.yml
|
||||
verify: verify.yml
|
||||
config_options:
|
||||
defaults:
|
||||
interpreter_python: auto_silent
|
||||
inventory:
|
||||
group_vars:
|
||||
k3s_master:
|
||||
k3s_master_ip: "{{ hostvars[groups['k3s_master'][0]]['ansible_host'] | default('127.0.0.1') }}"
|
||||
|
||||
verifier:
|
||||
name: ansible
|
||||
|
||||
lint: |
|
||||
set -e
|
||||
yamllint .
|
||||
ansible-lint
|
||||
28
roles/k3s/molecule/default/prepare.yml
Normal file
28
roles/k3s/molecule/default/prepare.yml
Normal file
@@ -0,0 +1,28 @@
|
||||
---
|
||||
- name: Prepare k3s test environment
|
||||
hosts: all
|
||||
become: true
|
||||
gather_facts: false
|
||||
|
||||
tasks:
|
||||
- name: Wait for systemd to start
|
||||
ansible.builtin.command: systemctl is-system-running
|
||||
register: systemd_running
|
||||
until: systemd_running.stdout in ['running', 'degraded']
|
||||
retries: 20
|
||||
delay: 3
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Install Python3
|
||||
ansible.builtin.raw: apt-get update && apt-get install -y python3
|
||||
changed_when: true
|
||||
|
||||
- name: Install Ansible collections
|
||||
ansible.builtin.command: >
|
||||
ansible-galaxy collection install
|
||||
community.general ansible.posix --upgrade
|
||||
become: false
|
||||
changed_when: true
|
||||
delegate_to: localhost
|
||||
run_once: true
|
||||
104
roles/k3s/molecule/default/verify.yml
Normal file
104
roles/k3s/molecule/default/verify.yml
Normal file
@@ -0,0 +1,104 @@
|
||||
---
|
||||
- name: Verify — k3s role
|
||||
hosts: all
|
||||
become: true
|
||||
gather_facts: false
|
||||
|
||||
tasks:
|
||||
# ── Проверка директорий ─────────────────────────────────────────────────────
|
||||
- name: Check k3s config directory exists
|
||||
ansible.builtin.stat:
|
||||
path: /etc/rancher/k3s
|
||||
register: config_dir
|
||||
|
||||
- name: Assert config directory
|
||||
ansible.builtin.assert:
|
||||
that: config_dir.stat.isdir
|
||||
fail_msg: "Директория /etc/rancher/k3s не создана"
|
||||
|
||||
# ── Проверка конфигурационного файла ────────────────────────────────────────
|
||||
- name: Check config file exists
|
||||
ansible.builtin.stat:
|
||||
path: /etc/rancher/k3s/config.yaml
|
||||
register: config_file
|
||||
|
||||
- name: Assert config file exists
|
||||
ansible.builtin.assert:
|
||||
that: config_file.stat.exists
|
||||
fail_msg: "Файл /etc/rancher/k3s/config.yaml не создан"
|
||||
|
||||
- name: Check config file permissions (0600)
|
||||
ansible.builtin.assert:
|
||||
that: "config_file.stat.mode == '0600'"
|
||||
fail_msg: "Неверные права на config.yaml: {{ config_file.stat.mode }}"
|
||||
|
||||
- name: Read config file
|
||||
ansible.builtin.slurp:
|
||||
src: /etc/rancher/k3s/config.yaml
|
||||
register: config_raw
|
||||
|
||||
- name: Parse config as YAML
|
||||
ansible.builtin.set_fact:
|
||||
k3s_config: "{{ config_raw.content | b64decode | from_yaml }}"
|
||||
|
||||
- name: Assert token is set
|
||||
ansible.builtin.assert:
|
||||
that: k3s_config.token is defined
|
||||
fail_msg: "Поле 'token' отсутствует в config.yaml"
|
||||
|
||||
- name: Assert cluster-cidr is correct
|
||||
ansible.builtin.assert:
|
||||
that: k3s_config['cluster-cidr'] == '10.42.0.0/16'
|
||||
fail_msg: "Неверный cluster-cidr: {{ k3s_config['cluster-cidr'] }}"
|
||||
|
||||
- name: Assert service-cidr is correct
|
||||
ansible.builtin.assert:
|
||||
that: k3s_config['service-cidr'] == '10.43.0.0/16'
|
||||
fail_msg: "Неверный service-cidr: {{ k3s_config['service-cidr'] }}"
|
||||
|
||||
- name: Assert cluster-init is set (первый мастер)
|
||||
ansible.builtin.assert:
|
||||
that: k3s_config['cluster-init'] == true
|
||||
fail_msg: "cluster-init должен быть true для первого мастера"
|
||||
|
||||
- name: Assert traefik is disabled
|
||||
ansible.builtin.assert:
|
||||
that: "'traefik' in k3s_config.disable"
|
||||
fail_msg: "traefik должен быть в списке disable"
|
||||
|
||||
# ── Проверка системных пакетов ──────────────────────────────────────────────
|
||||
- name: Check curl is installed
|
||||
ansible.builtin.command: which curl
|
||||
register: curl_check
|
||||
changed_when: false
|
||||
|
||||
- name: Assert curl installed
|
||||
ansible.builtin.assert:
|
||||
that: curl_check.rc == 0
|
||||
fail_msg: "curl не установлен"
|
||||
|
||||
- name: Check iptables is installed
|
||||
ansible.builtin.command: which iptables
|
||||
register: iptables_check
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Assert iptables installed
|
||||
ansible.builtin.assert:
|
||||
that: iptables_check.rc == 0
|
||||
fail_msg: "iptables не установлен"
|
||||
|
||||
# ── Проверка sysctl ──────────────────────────────────────────────────────────
|
||||
- name: Check ip_forward sysctl
|
||||
ansible.builtin.command: sysctl net.ipv4.ip_forward
|
||||
register: ip_forward
|
||||
changed_when: false
|
||||
|
||||
- name: Assert ip_forward is 1
|
||||
ansible.builtin.assert:
|
||||
that: "'= 1' in ip_forward.stdout"
|
||||
fail_msg: "net.ipv4.ip_forward не установлен в 1"
|
||||
|
||||
- name: Summary
|
||||
ansible.builtin.debug:
|
||||
msg: "Все проверки прошли успешно для ноды {{ inventory_hostname }}"
|
||||
73
roles/k3s/tasks/healthcheck.yml
Normal file
73
roles/k3s/tasks/healthcheck.yml
Normal file
@@ -0,0 +1,73 @@
|
||||
---
|
||||
# Диагностика состояния кластера
|
||||
|
||||
- name: Check K3S service status (master)
|
||||
ansible.builtin.systemd:
|
||||
name: k3s
|
||||
register: k3s_service_status
|
||||
become: "{{ k3s_become }}"
|
||||
when: inventory_hostname in groups['k3s_master']
|
||||
|
||||
- name: Check K3S service status (workers)
|
||||
ansible.builtin.systemd:
|
||||
name: k3s-agent
|
||||
register: k3s_agent_status
|
||||
become: "{{ k3s_become }}"
|
||||
when: inventory_hostname in groups['k3s_workers']
|
||||
|
||||
- name: Show service status
|
||||
ansible.builtin.debug:
|
||||
msg: >-
|
||||
{{ inventory_hostname }}:
|
||||
{{
|
||||
(k3s_service_status.status.ActiveState | default(''))
|
||||
if inventory_hostname in groups['k3s_master']
|
||||
else (k3s_agent_status.status.ActiveState | default(''))
|
||||
}}
|
||||
|
||||
- name: Get node list
|
||||
ansible.builtin.command: k3s kubectl get nodes -o wide
|
||||
register: node_list
|
||||
become: "{{ k3s_become }}"
|
||||
changed_when: false
|
||||
run_once: true
|
||||
delegate_to: "{{ groups['k3s_master'][0] }}"
|
||||
|
||||
- name: Show nodes
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ node_list.stdout_lines }}"
|
||||
run_once: true
|
||||
|
||||
- name: Get all pods (all namespaces)
|
||||
ansible.builtin.command: k3s kubectl get pods -A --field-selector=status.phase!=Running
|
||||
register: non_running_pods
|
||||
become: "{{ k3s_become }}"
|
||||
changed_when: false
|
||||
run_once: true
|
||||
delegate_to: "{{ groups['k3s_master'][0] }}"
|
||||
failed_when: false
|
||||
|
||||
- name: Show non-running pods (if any)
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ non_running_pods.stdout_lines }}"
|
||||
run_once: true
|
||||
when: non_running_pods.stdout_lines | length > 1
|
||||
|
||||
- name: Check disk space on all nodes
|
||||
ansible.builtin.shell: df -h / | tail -1
|
||||
register: disk_space
|
||||
changed_when: false
|
||||
become: "{{ k3s_become }}"
|
||||
|
||||
- name: Show disk usage
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ inventory_hostname }} disk: {{ disk_space.stdout }}"
|
||||
|
||||
- name: Check memory usage
|
||||
ansible.builtin.shell: free -h | grep Mem
|
||||
register: mem_usage
|
||||
changed_when: false
|
||||
|
||||
- name: Show memory usage
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ inventory_hostname }} memory: {{ mem_usage.stdout }}"
|
||||
51
roles/k3s/tasks/install_agent.yml
Normal file
51
roles/k3s/tasks/install_agent.yml
Normal file
@@ -0,0 +1,51 @@
|
||||
---
|
||||
- name: Check if K3S agent is already installed
|
||||
ansible.builtin.stat:
|
||||
path: "{{ k3s_install_dir }}/k3s"
|
||||
register: k3s_agent_binary
|
||||
|
||||
- name: Get node token from master
|
||||
ansible.builtin.set_fact:
|
||||
k3s_node_token: "{{ hostvars[groups['k3s_master'][0]]['k3s_node_token'] }}"
|
||||
|
||||
- name: Write K3S agent config
|
||||
ansible.builtin.template:
|
||||
src: k3s-agent-config.yaml.j2
|
||||
dest: "{{ k3s_config_dir }}/config.yaml"
|
||||
mode: '0600'
|
||||
become: "{{ k3s_become }}"
|
||||
notify: Restart K3S agent
|
||||
|
||||
- name: Download and install K3S agent
|
||||
ansible.builtin.shell: |
|
||||
set -o pipefail
|
||||
curl -sfL https://get.k3s.io | \
|
||||
INSTALL_K3S_VERSION="{{ k3s_version }}" \
|
||||
INSTALL_K3S_EXEC="agent" \
|
||||
K3S_URL="{{ k3s_api_url }}" \
|
||||
K3S_TOKEN="{{ k3s_node_token }}" \
|
||||
sh -
|
||||
args:
|
||||
executable: /bin/bash
|
||||
become: "{{ k3s_become }}"
|
||||
when: not k3s_agent_binary.stat.exists
|
||||
notify: Restart K3S agent
|
||||
|
||||
- name: Enable and start K3S agent service
|
||||
ansible.builtin.systemd:
|
||||
name: k3s-agent
|
||||
enabled: true
|
||||
state: started
|
||||
daemon_reload: true
|
||||
become: "{{ k3s_become }}"
|
||||
|
||||
- name: Wait for node to join the cluster
|
||||
ansible.builtin.command: >
|
||||
k3s kubectl get node {{ inventory_hostname }}
|
||||
delegate_to: "{{ groups['k3s_master'][0] }}"
|
||||
become: "{{ k3s_become }}"
|
||||
register: node_ready
|
||||
until: "'Ready' in node_ready.stdout"
|
||||
retries: 20
|
||||
delay: 10
|
||||
changed_when: false
|
||||
61
roles/k3s/tasks/install_server.yml
Normal file
61
roles/k3s/tasks/install_server.yml
Normal file
@@ -0,0 +1,61 @@
|
||||
---
|
||||
- name: Check if K3S server is already installed
|
||||
ansible.builtin.stat:
|
||||
path: "{{ k3s_install_dir }}/k3s"
|
||||
register: k3s_binary
|
||||
|
||||
- name: Write K3S server config
|
||||
ansible.builtin.template:
|
||||
src: k3s-server-config.yaml.j2
|
||||
dest: "{{ k3s_config_dir }}/config.yaml"
|
||||
mode: '0600'
|
||||
become: "{{ k3s_become }}"
|
||||
notify: Restart K3S server
|
||||
|
||||
- name: Download and install K3S server
|
||||
ansible.builtin.shell: |
|
||||
set -o pipefail
|
||||
curl -sfL https://get.k3s.io | \
|
||||
INSTALL_K3S_VERSION="{{ k3s_version }}" \
|
||||
INSTALL_K3S_EXEC="server" \
|
||||
K3S_TOKEN="{{ k3s_token }}" \
|
||||
sh -
|
||||
args:
|
||||
executable: /bin/bash
|
||||
become: "{{ k3s_become }}"
|
||||
when: not k3s_binary.stat.exists
|
||||
notify: Restart K3S server
|
||||
|
||||
- name: Enable and start K3S server service
|
||||
ansible.builtin.systemd:
|
||||
name: k3s
|
||||
enabled: true
|
||||
state: started
|
||||
daemon_reload: true
|
||||
become: "{{ k3s_become }}"
|
||||
|
||||
- name: Wait for K3S node-token to be generated
|
||||
ansible.builtin.wait_for:
|
||||
path: /var/lib/rancher/k3s/server/node-token
|
||||
timeout: 120
|
||||
become: "{{ k3s_become }}"
|
||||
|
||||
- name: Read node-token
|
||||
ansible.builtin.slurp:
|
||||
src: /var/lib/rancher/k3s/server/node-token
|
||||
register: node_token_raw
|
||||
become: "{{ k3s_become }}"
|
||||
|
||||
- name: Set node-token fact (shared to all hosts)
|
||||
ansible.builtin.set_fact:
|
||||
k3s_node_token: "{{ node_token_raw['content'] | b64decode | trim }}"
|
||||
|
||||
- name: Wait for API server to become available
|
||||
ansible.builtin.uri:
|
||||
url: "{{ k3s_api_url }}/healthz"
|
||||
validate_certs: false
|
||||
status_code: 200
|
||||
register: api_health
|
||||
until: api_health.status == 200
|
||||
retries: 30
|
||||
delay: 5
|
||||
27
roles/k3s/tasks/kubeconfig.yml
Normal file
27
roles/k3s/tasks/kubeconfig.yml
Normal file
@@ -0,0 +1,27 @@
|
||||
---
|
||||
- name: Read kubeconfig from master
|
||||
ansible.builtin.slurp:
|
||||
src: /etc/rancher/k3s/k3s.yaml
|
||||
register: kubeconfig_raw
|
||||
become: "{{ k3s_become }}"
|
||||
|
||||
- name: Decode and patch server URL in kubeconfig
|
||||
ansible.builtin.set_fact:
|
||||
kubeconfig_patched: >-
|
||||
{{
|
||||
kubeconfig_raw['content'] | b64decode
|
||||
| regex_replace('https://127.0.0.1:6443', k3s_api_url)
|
||||
| regex_replace('https://localhost:6443', k3s_api_url)
|
||||
}}
|
||||
|
||||
- name: Save kubeconfig locally
|
||||
ansible.builtin.copy:
|
||||
content: "{{ kubeconfig_patched }}"
|
||||
dest: "{{ k3s_kubeconfig_local_path }}"
|
||||
mode: '0600'
|
||||
delegate_to: localhost
|
||||
become: false
|
||||
|
||||
- name: Show kubeconfig location
|
||||
ansible.builtin.debug:
|
||||
msg: "Kubeconfig saved to {{ k3s_kubeconfig_local_path }}"
|
||||
21
roles/k3s/tasks/main.yml
Normal file
21
roles/k3s/tasks/main.yml
Normal file
@@ -0,0 +1,21 @@
|
||||
---
|
||||
- name: Include OS prerequisites
|
||||
ansible.builtin.include_tasks: prereqs.yml
|
||||
|
||||
- name: Raspberry Pi — enable cgroups
|
||||
ansible.builtin.include_tasks: rpi_cgroups.yml
|
||||
when: ansible_architecture in ['armv7l', 'aarch64'] and rpi_cgroup_enable
|
||||
|
||||
- name: Install K3S server
|
||||
ansible.builtin.include_tasks: install_server.yml
|
||||
when: inventory_hostname in groups['k3s_master']
|
||||
|
||||
- name: Configure node labels and taints
|
||||
ansible.builtin.include_tasks: node_config.yml
|
||||
when: k3s_node_labels | length > 0 or k3s_node_taints | length > 0
|
||||
|
||||
- name: Fetch kubeconfig to local machine
|
||||
ansible.builtin.include_tasks: kubeconfig.yml
|
||||
when:
|
||||
- k3s_fetch_kubeconfig
|
||||
- inventory_hostname == groups['k3s_master'][0]
|
||||
18
roles/k3s/tasks/node_config.yml
Normal file
18
roles/k3s/tasks/node_config.yml
Normal file
@@ -0,0 +1,18 @@
|
||||
---
|
||||
- name: Apply node labels
|
||||
ansible.builtin.command: >
|
||||
k3s kubectl label node {{ inventory_hostname }}
|
||||
{{ item }} --overwrite
|
||||
loop: "{{ k3s_node_labels }}"
|
||||
delegate_to: "{{ groups['k3s_master'][0] }}"
|
||||
become: "{{ k3s_become }}"
|
||||
changed_when: true
|
||||
|
||||
- name: Apply node taints
|
||||
ansible.builtin.command: >
|
||||
k3s kubectl taint node {{ inventory_hostname }}
|
||||
{{ item }} --overwrite
|
||||
loop: "{{ k3s_node_taints }}"
|
||||
delegate_to: "{{ groups['k3s_master'][0] }}"
|
||||
become: "{{ k3s_become }}"
|
||||
changed_when: true
|
||||
67
roles/k3s/tasks/prereqs.yml
Normal file
67
roles/k3s/tasks/prereqs.yml
Normal file
@@ -0,0 +1,67 @@
|
||||
---
|
||||
- name: Update apt cache
|
||||
ansible.builtin.apt:
|
||||
update_cache: true
|
||||
cache_valid_time: 3600
|
||||
become: "{{ k3s_become }}"
|
||||
|
||||
- name: Install required packages
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- curl
|
||||
- ca-certificates
|
||||
- apt-transport-https
|
||||
- gnupg
|
||||
- iptables
|
||||
state: present
|
||||
become: "{{ k3s_become }}"
|
||||
|
||||
- name: Disable swap
|
||||
ansible.builtin.command: swapoff -a
|
||||
become: "{{ k3s_become }}"
|
||||
changed_when: false
|
||||
|
||||
- name: Remove swap from fstab
|
||||
ansible.builtin.replace:
|
||||
path: /etc/fstab
|
||||
regexp: '^([^#].*?\sswap\s+sw\s+.*)$'
|
||||
replace: '# \1'
|
||||
become: "{{ k3s_become }}"
|
||||
|
||||
- name: Load required kernel modules
|
||||
community.general.modprobe:
|
||||
name: "{{ item }}"
|
||||
state: present
|
||||
loop:
|
||||
- overlay
|
||||
- br_netfilter
|
||||
become: "{{ k3s_become }}"
|
||||
|
||||
- name: Persist kernel modules
|
||||
ansible.builtin.copy:
|
||||
dest: /etc/modules-load.d/k3s.conf
|
||||
content: |
|
||||
overlay
|
||||
br_netfilter
|
||||
mode: '0644'
|
||||
become: "{{ k3s_become }}"
|
||||
|
||||
- name: Set sysctl params for Kubernetes networking
|
||||
ansible.posix.sysctl:
|
||||
name: "{{ item.key }}"
|
||||
value: "{{ item.value }}"
|
||||
state: present
|
||||
sysctl_file: /etc/sysctl.d/99-k3s.conf
|
||||
reload: true
|
||||
loop:
|
||||
- { key: "net.bridge.bridge-nf-call-iptables", value: "1" }
|
||||
- { key: "net.bridge.bridge-nf-call-ip6tables", value: "1" }
|
||||
- { key: "net.ipv4.ip_forward", value: "1" }
|
||||
become: "{{ k3s_become }}"
|
||||
|
||||
- name: Create K3S config directory
|
||||
ansible.builtin.file:
|
||||
path: "{{ k3s_config_dir }}"
|
||||
state: directory
|
||||
mode: '0755'
|
||||
become: "{{ k3s_become }}"
|
||||
47
roles/k3s/tasks/rpi_cgroups.yml
Normal file
47
roles/k3s/tasks/rpi_cgroups.yml
Normal file
@@ -0,0 +1,47 @@
|
||||
---
|
||||
# Raspberry Pi requires cgroup_enable=cpuset cgroup_memory=1 cgroup_enable=memory
|
||||
# in /boot/cmdline.txt (legacy) or /boot/firmware/cmdline.txt (bookworm+)
|
||||
|
||||
- name: Detect correct cmdline.txt path
|
||||
ansible.builtin.set_fact:
|
||||
rpi_cmdline_file: >-
|
||||
{{
|
||||
rpi_cmdline_path_new
|
||||
if ansible_distribution_release in ['bookworm', 'trixie']
|
||||
else rpi_cmdline_path
|
||||
}}
|
||||
|
||||
- name: Check if cmdline.txt exists
|
||||
ansible.builtin.stat:
|
||||
path: "{{ rpi_cmdline_file }}"
|
||||
register: cmdline_stat
|
||||
become: "{{ k3s_become }}"
|
||||
|
||||
- name: Fallback to legacy path if new path missing
|
||||
ansible.builtin.set_fact:
|
||||
rpi_cmdline_file: "{{ rpi_cmdline_path }}"
|
||||
when: not cmdline_stat.stat.exists
|
||||
|
||||
- name: Read current cmdline.txt
|
||||
ansible.builtin.slurp:
|
||||
src: "{{ rpi_cmdline_file }}"
|
||||
register: cmdline_content
|
||||
become: "{{ k3s_become }}"
|
||||
|
||||
- name: Set cgroup parameters fact
|
||||
ansible.builtin.set_fact:
|
||||
cmdline_current: "{{ cmdline_content['content'] | b64decode | trim }}"
|
||||
cgroup_params: "cgroup_enable=cpuset cgroup_enable=memory cgroup_memory=1"
|
||||
|
||||
- name: Add cgroup params to cmdline.txt if missing
|
||||
ansible.builtin.copy:
|
||||
dest: "{{ rpi_cmdline_file }}"
|
||||
content: "{{ cmdline_current }} {{ cgroup_params }}\n"
|
||||
mode: '0755'
|
||||
backup: true
|
||||
become: "{{ k3s_become }}"
|
||||
when: "'cgroup_memory=1' not in cmdline_current"
|
||||
notify: Reboot Raspberry Pi
|
||||
|
||||
- name: Flush handlers to reboot if needed
|
||||
ansible.builtin.meta: flush_handlers
|
||||
89
roles/k3s/tasks/uninstall.yml
Normal file
89
roles/k3s/tasks/uninstall.yml
Normal file
@@ -0,0 +1,89 @@
|
||||
---
|
||||
# Полное удаление K3S с ноды
|
||||
|
||||
- name: Stop and disable K3S server service
|
||||
ansible.builtin.systemd:
|
||||
name: k3s
|
||||
state: stopped
|
||||
enabled: false
|
||||
become: "{{ k3s_become }}"
|
||||
failed_when: false
|
||||
when: inventory_hostname in groups['k3s_master']
|
||||
|
||||
- name: Stop and disable K3S agent service
|
||||
ansible.builtin.systemd:
|
||||
name: k3s-agent
|
||||
state: stopped
|
||||
enabled: false
|
||||
become: "{{ k3s_become }}"
|
||||
failed_when: false
|
||||
when: inventory_hostname in groups['k3s_workers']
|
||||
|
||||
- name: Run K3S server uninstall script (if exists)
|
||||
ansible.builtin.command: /usr/local/bin/k3s-uninstall.sh
|
||||
become: "{{ k3s_become }}"
|
||||
failed_when: false
|
||||
changed_when: true
|
||||
when: inventory_hostname in groups['k3s_master']
|
||||
|
||||
- name: Run K3S agent uninstall script (if exists)
|
||||
ansible.builtin.command: /usr/local/bin/k3s-agent-uninstall.sh
|
||||
become: "{{ k3s_become }}"
|
||||
failed_when: false
|
||||
changed_when: true
|
||||
when: inventory_hostname in groups['k3s_workers']
|
||||
|
||||
- name: Remove K3S binary
|
||||
ansible.builtin.file:
|
||||
path: "{{ k3s_install_dir }}/k3s"
|
||||
state: absent
|
||||
become: "{{ k3s_become }}"
|
||||
|
||||
- name: Remove K3S config directory
|
||||
ansible.builtin.file:
|
||||
path: "{{ k3s_config_dir }}"
|
||||
state: absent
|
||||
become: "{{ k3s_become }}"
|
||||
|
||||
- name: Remove K3S data directory
|
||||
ansible.builtin.file:
|
||||
path: "{{ k3s_data_dir }}"
|
||||
state: absent
|
||||
become: "{{ k3s_become }}"
|
||||
|
||||
- name: Remove K3S systemd units
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: absent
|
||||
loop:
|
||||
- /etc/systemd/system/k3s.service
|
||||
- /etc/systemd/system/k3s-agent.service
|
||||
- /etc/systemd/system/k3s.service.env
|
||||
- /etc/systemd/system/k3s-agent.service.env
|
||||
become: "{{ k3s_become }}"
|
||||
notify: Reload systemd
|
||||
|
||||
- name: Remove sysctl config
|
||||
ansible.builtin.file:
|
||||
path: /etc/sysctl.d/99-k3s.conf
|
||||
state: absent
|
||||
become: "{{ k3s_become }}"
|
||||
|
||||
- name: Remove kernel modules config
|
||||
ansible.builtin.file:
|
||||
path: /etc/modules-load.d/k3s.conf
|
||||
state: absent
|
||||
become: "{{ k3s_become }}"
|
||||
|
||||
- name: Remove local kubeconfig
|
||||
ansible.builtin.file:
|
||||
path: "{{ k3s_kubeconfig_local_path }}"
|
||||
state: absent
|
||||
delegate_to: localhost
|
||||
become: false
|
||||
run_once: true
|
||||
|
||||
- name: Reload systemd daemon
|
||||
ansible.builtin.systemd:
|
||||
daemon_reload: true
|
||||
become: "{{ k3s_become }}"
|
||||
118
roles/k3s/tasks/upgrade.yml
Normal file
118
roles/k3s/tasks/upgrade.yml
Normal file
@@ -0,0 +1,118 @@
|
||||
---
|
||||
# Обновление K3S на всех нодах кластера
|
||||
# Порядок: сначала master, затем workers (по одному)
|
||||
|
||||
- name: Get current K3S version
|
||||
ansible.builtin.command: k3s --version
|
||||
register: k3s_current_version
|
||||
become: "{{ k3s_become }}"
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Show current version
|
||||
ansible.builtin.debug:
|
||||
msg: "Current: {{ k3s_current_version.stdout | default('not installed') }} → Target: {{ k3s_version }}"
|
||||
|
||||
- name: Skip upgrade if already at target version
|
||||
ansible.builtin.meta: end_host
|
||||
when:
|
||||
- k3s_current_version.rc == 0
|
||||
- k3s_version in k3s_current_version.stdout
|
||||
|
||||
# ── Upgrade Master ────────────────────────────────────────────────────────────
|
||||
- name: Drain master node before upgrade
|
||||
ansible.builtin.command: >
|
||||
k3s kubectl drain {{ inventory_hostname }}
|
||||
--ignore-daemonsets
|
||||
--delete-emptydir-data
|
||||
--timeout=120s
|
||||
become: "{{ k3s_become }}"
|
||||
delegate_to: "{{ groups['k3s_master'][0] }}"
|
||||
changed_when: true
|
||||
when: inventory_hostname in groups['k3s_master']
|
||||
|
||||
- name: Upgrade K3S server binary
|
||||
ansible.builtin.shell: |
|
||||
set -o pipefail
|
||||
curl -sfL https://get.k3s.io | \
|
||||
INSTALL_K3S_VERSION="{{ k3s_version }}" \
|
||||
INSTALL_K3S_EXEC="server" \
|
||||
K3S_TOKEN="{{ k3s_token }}" \
|
||||
sh -
|
||||
args:
|
||||
executable: /bin/bash
|
||||
become: "{{ k3s_become }}"
|
||||
when: inventory_hostname in groups['k3s_master']
|
||||
|
||||
- name: Wait for master to be ready after upgrade
|
||||
ansible.builtin.command: >
|
||||
k3s kubectl get node {{ inventory_hostname }} -o jsonpath='{.status.conditions[-1].type}'
|
||||
become: "{{ k3s_become }}"
|
||||
register: master_ready
|
||||
until: master_ready.stdout == "Ready"
|
||||
retries: 24
|
||||
delay: 10
|
||||
changed_when: false
|
||||
when: inventory_hostname in groups['k3s_master']
|
||||
|
||||
- name: Uncordon master node
|
||||
ansible.builtin.command: k3s kubectl uncordon {{ inventory_hostname }}
|
||||
become: "{{ k3s_become }}"
|
||||
delegate_to: "{{ groups['k3s_master'][0] }}"
|
||||
changed_when: true
|
||||
when: inventory_hostname in groups['k3s_master']
|
||||
|
||||
# ── Upgrade Workers (serial) ──────────────────────────────────────────────────
|
||||
- name: Drain worker node before upgrade
|
||||
ansible.builtin.command: >
|
||||
k3s kubectl drain {{ inventory_hostname }}
|
||||
--ignore-daemonsets
|
||||
--delete-emptydir-data
|
||||
--timeout=180s
|
||||
become: "{{ k3s_become }}"
|
||||
delegate_to: "{{ groups['k3s_master'][0] }}"
|
||||
changed_when: true
|
||||
when: inventory_hostname in groups['k3s_workers']
|
||||
|
||||
- name: Upgrade K3S agent binary
|
||||
ansible.builtin.shell: |
|
||||
set -o pipefail
|
||||
curl -sfL https://get.k3s.io | \
|
||||
INSTALL_K3S_VERSION="{{ k3s_version }}" \
|
||||
INSTALL_K3S_EXEC="agent" \
|
||||
K3S_URL="{{ k3s_api_url }}" \
|
||||
K3S_TOKEN="{{ hostvars[groups['k3s_master'][0]]['k3s_node_token'] }}" \
|
||||
sh -
|
||||
args:
|
||||
executable: /bin/bash
|
||||
become: "{{ k3s_become }}"
|
||||
when: inventory_hostname in groups['k3s_workers']
|
||||
|
||||
- name: Wait for worker to rejoin after upgrade
|
||||
ansible.builtin.command: >
|
||||
k3s kubectl get node {{ inventory_hostname }} -o jsonpath='{.status.conditions[-1].type}'
|
||||
become: "{{ k3s_become }}"
|
||||
delegate_to: "{{ groups['k3s_master'][0] }}"
|
||||
register: worker_ready
|
||||
until: worker_ready.stdout == "Ready"
|
||||
retries: 24
|
||||
delay: 10
|
||||
changed_when: false
|
||||
when: inventory_hostname in groups['k3s_workers']
|
||||
|
||||
- name: Uncordon worker node
|
||||
ansible.builtin.command: k3s kubectl uncordon {{ inventory_hostname }}
|
||||
become: "{{ k3s_become }}"
|
||||
delegate_to: "{{ groups['k3s_master'][0] }}"
|
||||
changed_when: true
|
||||
when: inventory_hostname in groups['k3s_workers']
|
||||
|
||||
- name: Verify new version
|
||||
ansible.builtin.command: k3s --version
|
||||
register: k3s_new_version
|
||||
become: "{{ k3s_become }}"
|
||||
changed_when: false
|
||||
|
||||
- name: Show upgraded version
|
||||
ansible.builtin.debug:
|
||||
msg: "✓ {{ inventory_hostname }} upgraded to: {{ k3s_new_version.stdout }}"
|
||||
19
roles/k3s/templates/k3s-agent-config.yaml.j2
Normal file
19
roles/k3s/templates/k3s-agent-config.yaml.j2
Normal file
@@ -0,0 +1,19 @@
|
||||
# K3S Agent Configuration
|
||||
# Generated by Ansible — do not edit manually
|
||||
|
||||
server: "{{ k3s_api_url }}"
|
||||
token: "{{ k3s_node_token }}"
|
||||
|
||||
# Node name
|
||||
node-name: "{{ inventory_hostname }}"
|
||||
|
||||
{% if ansible_architecture in ['armv7l', 'aarch64'] %}
|
||||
# Raspberry Pi / ARM — use container runtime args for stability
|
||||
kubelet-arg:
|
||||
- "feature-gates=MemoryManager=false"
|
||||
{% endif %}
|
||||
|
||||
{% if k3s_extra_agent_args %}
|
||||
# Extra args
|
||||
{{ k3s_extra_agent_args }}
|
||||
{% endif %}
|
||||
41
roles/k3s/templates/k3s-server-config.yaml.j2
Normal file
41
roles/k3s/templates/k3s-server-config.yaml.j2
Normal file
@@ -0,0 +1,41 @@
|
||||
# K3S Server Configuration
|
||||
# Generated by Ansible — do not edit manually
|
||||
|
||||
token: "{{ k3s_token }}"
|
||||
cluster-cidr: "{{ k3s_cluster_cidr }}"
|
||||
service-cidr: "{{ k3s_service_cidr }}"
|
||||
cluster-dns: "{{ k3s_cluster_dns }}"
|
||||
{% if k3s_cni | default('flannel') == 'flannel' %}
|
||||
flannel-backend: "{{ k3s_flannel_backend }}"
|
||||
{% else %}
|
||||
flannel-backend: "none"
|
||||
disable-network-policy: true
|
||||
{% endif %}
|
||||
|
||||
write-kubeconfig-mode: "0644"
|
||||
|
||||
# HA embedded etcd: первый сервер инициализирует кластер, остальные присоединяются
|
||||
{% if inventory_hostname == groups['k3s_master'][0] %}
|
||||
cluster-init: true
|
||||
{% else %}
|
||||
server: "https://{{ hostvars[groups['k3s_master'][0]]['ansible_host'] }}:6443"
|
||||
{% endif %}
|
||||
|
||||
{% if k3s_disable_traefik or k3s_disable_servicelb or k3s_disable_local_storage %}
|
||||
disable:
|
||||
{% if k3s_disable_traefik %}
|
||||
- traefik
|
||||
{% endif %}
|
||||
{% if k3s_disable_servicelb %}
|
||||
- servicelb
|
||||
{% endif %}
|
||||
{% if k3s_disable_local_storage %}
|
||||
- local-storage
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if k3s_extra_server_args %}
|
||||
{{ k3s_extra_server_args }}
|
||||
{% endif %}
|
||||
|
||||
node-name: "{{ inventory_hostname }}"
|
||||
27
roles/kube-vip/defaults/main.yml
Normal file
27
roles/kube-vip/defaults/main.yml
Normal file
@@ -0,0 +1,27 @@
|
||||
---
|
||||
# Версия kube-vip
|
||||
kube_vip_version: "v0.8.3"
|
||||
|
||||
# VIP — виртуальный IP для control plane (ОБЯЗАТЕЛЬНО задать!)
|
||||
kube_vip_address: "192.168.1.100"
|
||||
|
||||
# Сетевой интерфейс на master-ноде.
|
||||
# Оставь пустым для автоопределения (ansible_default_ipv4.interface).
|
||||
# Переопредели если авто не работает: kube_vip_interface: "eth0"
|
||||
kube_vip_interface: ""
|
||||
|
||||
# Режим работы: ARP (L2, для большинства домашних сетей) или BGP (L3)
|
||||
kube_vip_mode: "arp" # arp | bgp
|
||||
|
||||
# Включить kube-vip как LoadBalancer для Services (вместо metalLB)
|
||||
kube_vip_services_enable: true
|
||||
|
||||
# RBAC манифест
|
||||
kube_vip_rbac_url: "https://kube-vip.io/manifests/rbac.yaml"
|
||||
|
||||
# Образ kube-vip
|
||||
kube_vip_image: "ghcr.io/kube-vip/kube-vip"
|
||||
|
||||
# Путь для статического пода
|
||||
kube_vip_manifest_dir: /var/lib/rancher/k3s/server/manifests
|
||||
kube_vip_pod_manifest: "{{ kube_vip_manifest_dir }}/kube-vip.yaml"
|
||||
7
roles/kube-vip/handlers/main.yml
Normal file
7
roles/kube-vip/handlers/main.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
- name: Restart K3S server
|
||||
ansible.builtin.systemd:
|
||||
name: k3s
|
||||
state: restarted
|
||||
daemon_reload: true
|
||||
become: true
|
||||
8
roles/kube-vip/meta/main.yml
Normal file
8
roles/kube-vip/meta/main.yml
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
galaxy_info:
|
||||
author: "your-name"
|
||||
description: "Deploy kube-vip VIP for K3S control plane and LoadBalancer services"
|
||||
license: "MIT"
|
||||
min_ansible_version: "2.12"
|
||||
dependencies:
|
||||
- role: k3s
|
||||
62
roles/kube-vip/tasks/main.yml
Normal file
62
roles/kube-vip/tasks/main.yml
Normal file
@@ -0,0 +1,62 @@
|
||||
---
|
||||
- name: Resolve kube-vip network interface
|
||||
ansible.builtin.set_fact:
|
||||
_kube_vip_iface: "{{ kube_vip_interface if kube_vip_interface | length > 0 else ansible_default_ipv4.interface | default('eth0') }}"
|
||||
|
||||
- name: Validate kube_vip_address is set
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- kube_vip_address is defined
|
||||
- kube_vip_address != ""
|
||||
- kube_vip_address != "192.168.1.100"
|
||||
fail_msg: >
|
||||
Задай kube_vip_address в group_vars/all/main.yml!
|
||||
Например: kube_vip_address: "192.168.1.50"
|
||||
quiet: true
|
||||
tags: [kube_vip_validate]
|
||||
|
||||
- name: Ensure manifest directory exists
|
||||
ansible.builtin.file:
|
||||
path: "{{ kube_vip_manifest_dir }}"
|
||||
state: directory
|
||||
mode: '0755'
|
||||
become: true
|
||||
|
||||
- name: Deploy kube-vip RBAC manifest
|
||||
ansible.builtin.get_url:
|
||||
url: "{{ kube_vip_rbac_url }}"
|
||||
dest: "{{ kube_vip_manifest_dir }}/kube-vip-rbac.yaml"
|
||||
mode: '0644'
|
||||
force: true
|
||||
become: true
|
||||
retries: 3
|
||||
delay: 5
|
||||
|
||||
- name: Template kube-vip DaemonSet manifest
|
||||
ansible.builtin.template:
|
||||
src: kube-vip-ds.yaml.j2
|
||||
dest: "{{ kube_vip_manifest_dir }}/kube-vip.yaml"
|
||||
mode: '0644'
|
||||
become: true
|
||||
notify: Restart K3S server
|
||||
|
||||
- name: Wait for kube-vip pods to be ready
|
||||
ansible.builtin.command: >
|
||||
k3s kubectl -n kube-system wait pod
|
||||
-l app.kubernetes.io/name=kube-vip
|
||||
--for=condition=Ready
|
||||
--timeout=120s
|
||||
become: true
|
||||
register: kvip_wait
|
||||
retries: 5
|
||||
delay: 10
|
||||
until: kvip_wait.rc == 0
|
||||
changed_when: false
|
||||
|
||||
- name: Verify VIP is reachable
|
||||
ansible.builtin.wait_for:
|
||||
host: "{{ kube_vip_address }}"
|
||||
port: 6443
|
||||
timeout: 60
|
||||
delegate_to: localhost
|
||||
become: false
|
||||
107
roles/kube-vip/templates/kube-vip-ds.yaml.j2
Normal file
107
roles/kube-vip/templates/kube-vip-ds.yaml.j2
Normal file
@@ -0,0 +1,107 @@
|
||||
---
|
||||
# kube-vip DaemonSet — деплоится как auto-манифест K3S
|
||||
# Сгенерировано Ansible (roles/kube-vip)
|
||||
apiVersion: apps/v1
|
||||
kind: DaemonSet
|
||||
metadata:
|
||||
name: kube-vip-ds
|
||||
namespace: kube-system
|
||||
labels:
|
||||
app.kubernetes.io/name: kube-vip
|
||||
app.kubernetes.io/version: "{{ kube_vip_version }}"
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: kube-vip
|
||||
updateStrategy:
|
||||
type: RollingUpdate
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: kube-vip
|
||||
app.kubernetes.io/version: "{{ kube_vip_version }}"
|
||||
spec:
|
||||
affinity:
|
||||
nodeAffinity:
|
||||
requiredDuringSchedulingIgnoredDuringExecution:
|
||||
nodeSelectorTerms:
|
||||
- matchExpressions:
|
||||
- key: node-role.kubernetes.io/control-plane
|
||||
operator: Exists
|
||||
tolerations:
|
||||
- effect: NoSchedule
|
||||
operator: Exists
|
||||
- effect: NoExecute
|
||||
operator: Exists
|
||||
hostNetwork: true
|
||||
serviceAccountName: kube-vip
|
||||
containers:
|
||||
- name: kube-vip
|
||||
image: "{{ kube_vip_image }}:{{ kube_vip_version }}"
|
||||
imagePullPolicy: IfNotPresent
|
||||
args:
|
||||
- manager
|
||||
env:
|
||||
- name: vip_arp
|
||||
value: "{{ 'true' if kube_vip_mode == 'arp' else 'false' }}"
|
||||
- name: vip_interface
|
||||
value: "{{ _kube_vip_iface }}"
|
||||
- name: vip_address
|
||||
value: "{{ kube_vip_address }}"
|
||||
- name: port
|
||||
value: "6443"
|
||||
- name: vip_cidr
|
||||
value: "32"
|
||||
- name: cp_enable
|
||||
value: "true"
|
||||
- name: cp_namespace
|
||||
value: "kube-system"
|
||||
- name: vip_ddns
|
||||
value: "false"
|
||||
- name: svc_enable
|
||||
value: "{{ 'true' if kube_vip_services_enable else 'false' }}"
|
||||
- name: svc_leasename
|
||||
value: "plndr-svcs-lock"
|
||||
- name: vip_leaderelection
|
||||
value: "true"
|
||||
- name: vip_leasename
|
||||
value: "plndr-cp-lock"
|
||||
- name: vip_leaseduration
|
||||
value: "5"
|
||||
- name: vip_renewdeadline
|
||||
value: "3"
|
||||
- name: vip_retryperiod
|
||||
value: "1"
|
||||
- name: prometheus_server
|
||||
value: ":2112"
|
||||
securityContext:
|
||||
capabilities:
|
||||
add:
|
||||
- NET_ADMIN
|
||||
- NET_RAW
|
||||
- SYS_TIME
|
||||
resources:
|
||||
requests:
|
||||
cpu: 50m
|
||||
memory: 64Mi
|
||||
limits:
|
||||
cpu: 200m
|
||||
memory: 128Mi
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: 2112
|
||||
initialDelaySeconds: 15
|
||||
periodSeconds: 20
|
||||
{% if kube_vip_mode == 'bgp' %}
|
||||
- name: bgp_enable
|
||||
value: "true"
|
||||
- name: bgp_routerid
|
||||
value: "{{ ansible_default_ipv4.address }}"
|
||||
- name: bgp_as
|
||||
value: "{{ kube_vip_bgp_as | default('65000') }}"
|
||||
- name: bgp_peeraddress
|
||||
value: "{{ kube_vip_bgp_peer | default('') }}"
|
||||
- name: bgp_peeras
|
||||
value: "{{ kube_vip_bgp_peer_as | default('65000') }}"
|
||||
{% endif %}
|
||||
21
roles/nfs-server/defaults/main.yml
Normal file
21
roles/nfs-server/defaults/main.yml
Normal file
@@ -0,0 +1,21 @@
|
||||
---
|
||||
# NFS экспорты — список точек монтирования
|
||||
nfs_exports:
|
||||
- path: /srv/nfs/k8s
|
||||
options: "*(rw,sync,no_subtree_check,no_root_squash)"
|
||||
|
||||
# Разрешённая подсеть для NFS (для firewall)
|
||||
nfs_allowed_network: "192.168.1.0/24"
|
||||
|
||||
# Пакеты NFS сервера
|
||||
nfs_server_packages:
|
||||
- nfs-kernel-server
|
||||
- nfs-common
|
||||
|
||||
# Создавать директории если не существуют
|
||||
nfs_create_export_dirs: true
|
||||
|
||||
# Права на экспортируемые директории
|
||||
nfs_export_dir_mode: "0777"
|
||||
nfs_export_dir_owner: "nobody"
|
||||
nfs_export_dir_group: "nogroup"
|
||||
11
roles/nfs-server/handlers/main.yml
Normal file
11
roles/nfs-server/handlers/main.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
- name: Re-export NFS shares
|
||||
ansible.builtin.command: exportfs -ra
|
||||
become: true
|
||||
changed_when: true
|
||||
|
||||
- name: Restart NFS server
|
||||
ansible.builtin.systemd:
|
||||
name: nfs-kernel-server
|
||||
state: restarted
|
||||
become: true
|
||||
7
roles/nfs-server/meta/main.yml
Normal file
7
roles/nfs-server/meta/main.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
galaxy_info:
|
||||
author: "your-name"
|
||||
description: "Configure NFS server for Kubernetes persistent storage"
|
||||
license: "MIT"
|
||||
min_ansible_version: "2.12"
|
||||
dependencies: []
|
||||
58
roles/nfs-server/tasks/main.yml
Normal file
58
roles/nfs-server/tasks/main.yml
Normal file
@@ -0,0 +1,58 @@
|
||||
---
|
||||
- name: Install NFS server packages
|
||||
ansible.builtin.apt:
|
||||
name: "{{ nfs_server_packages }}"
|
||||
state: present
|
||||
update_cache: true
|
||||
become: true
|
||||
|
||||
- name: Create NFS export directories
|
||||
ansible.builtin.file:
|
||||
path: "{{ item.path }}"
|
||||
state: directory
|
||||
mode: "{{ nfs_export_dir_mode }}"
|
||||
owner: "{{ nfs_export_dir_owner }}"
|
||||
group: "{{ nfs_export_dir_group }}"
|
||||
loop: "{{ nfs_exports }}"
|
||||
become: true
|
||||
when: nfs_create_export_dirs
|
||||
|
||||
- name: Configure /etc/exports
|
||||
ansible.builtin.template:
|
||||
src: exports.j2
|
||||
dest: /etc/exports
|
||||
mode: '0644'
|
||||
backup: true
|
||||
become: true
|
||||
notify:
|
||||
- Re-export NFS shares
|
||||
- Restart NFS server
|
||||
|
||||
- name: Enable and start NFS server
|
||||
ansible.builtin.systemd:
|
||||
name: nfs-kernel-server
|
||||
enabled: true
|
||||
state: started
|
||||
become: true
|
||||
|
||||
- name: Allow NFS through UFW (if active)
|
||||
community.general.ufw:
|
||||
rule: allow
|
||||
src: "{{ nfs_allowed_network }}"
|
||||
port: "{{ item }}"
|
||||
proto: tcp
|
||||
loop:
|
||||
- "2049" # NFS
|
||||
- "111" # RPC portmapper
|
||||
become: true
|
||||
failed_when: false # UFW может быть не установлен
|
||||
|
||||
- name: Verify NFS exports are active
|
||||
ansible.builtin.command: exportfs -v
|
||||
register: nfs_exportfs
|
||||
become: true
|
||||
changed_when: false
|
||||
|
||||
- name: Show active NFS exports
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ nfs_exportfs.stdout_lines }}"
|
||||
9
roles/nfs-server/templates/exports.j2
Normal file
9
roles/nfs-server/templates/exports.j2
Normal file
@@ -0,0 +1,9 @@
|
||||
# /etc/exports — управляется Ansible (roles/nfs-server)
|
||||
# Изменения вручную будут перезаписаны!
|
||||
#
|
||||
# Формат: <директория> <опции>
|
||||
# Документация: man exports
|
||||
|
||||
{% for export in nfs_exports %}
|
||||
{{ export.path }} {{ export.options }}
|
||||
{% endfor %}
|
||||
63
roles/prometheus-stack/defaults/main.yml
Normal file
63
roles/prometheus-stack/defaults/main.yml
Normal file
@@ -0,0 +1,63 @@
|
||||
---
|
||||
# Включить установку kube-prometheus-stack (false = пропустить)
|
||||
prometheus_stack_enabled: false
|
||||
|
||||
prometheus_stack_version: "60.3.0" # Helm chart version
|
||||
prometheus_stack_namespace: "monitoring"
|
||||
prometheus_stack_release_name: "prom"
|
||||
|
||||
prometheus_stack_chart_repo: "https://prometheus-community.github.io/helm-charts"
|
||||
prometheus_stack_chart_name: "kube-prometheus-stack"
|
||||
|
||||
# Grafana
|
||||
prometheus_grafana_enabled: true
|
||||
|
||||
# Логин и пароль администратора Grafana.
|
||||
# Рекомендуется задавать через Ansible Vault:
|
||||
# group_vars/all/vault.yml:
|
||||
# vault_grafana_user: "admin"
|
||||
# vault_grafana_password: "ваш-пароль"
|
||||
grafana_admin_user: "{{ vault_grafana_user | default('admin') }}"
|
||||
prometheus_grafana_admin_password: "{{ vault_grafana_password | default('admin') }}"
|
||||
|
||||
prometheus_grafana_ingress_enabled: false
|
||||
prometheus_grafana_ingress_host: "grafana.local"
|
||||
prometheus_grafana_ingress_class: "nginx"
|
||||
|
||||
# Prometheus
|
||||
prometheus_retention_days: 7
|
||||
prometheus_storage_size: "10Gi" # Размер PVC для данных Prometheus
|
||||
prometheus_storage_class: "" # "" = использовать default StorageClass (nfs-client)
|
||||
|
||||
# Grafana PVC
|
||||
grafana_storage_enabled: true
|
||||
grafana_storage_size: "5Gi" # Размер PVC для дашбордов и плагинов Grafana
|
||||
grafana_storage_class: "" # "" = использовать default StorageClass
|
||||
|
||||
# Alertmanager
|
||||
prometheus_alertmanager_enabled: true
|
||||
prometheus_alertmanager_storage_size: "2Gi"
|
||||
|
||||
# Node exporter (метрики хостов)
|
||||
prometheus_node_exporter_enabled: true
|
||||
|
||||
# kube-state-metrics
|
||||
prometheus_kube_state_metrics_enabled: true
|
||||
|
||||
# Ресурсы Prometheus
|
||||
prometheus_resources:
|
||||
requests:
|
||||
cpu: 200m
|
||||
memory: 512Mi
|
||||
limits:
|
||||
cpu: 1000m
|
||||
memory: 2Gi
|
||||
|
||||
# Ресурсы Grafana
|
||||
grafana_resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
limits:
|
||||
cpu: 300m
|
||||
memory: 256Mi
|
||||
6
roles/prometheus-stack/meta/main.yml
Normal file
6
roles/prometheus-stack/meta/main.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
galaxy_info:
|
||||
role_name: prometheus-stack
|
||||
description: Deploy kube-prometheus-stack (Prometheus + Grafana + Alertmanager) via Helm on K3S
|
||||
min_ansible_version: "2.14"
|
||||
dependencies: []
|
||||
54
roles/prometheus-stack/molecule/default/converge.yml
Normal file
54
roles/prometheus-stack/molecule/default/converge.yml
Normal file
@@ -0,0 +1,54 @@
|
||||
---
|
||||
- name: Converge — prometheus-stack template tests
|
||||
hosts: all
|
||||
become: false
|
||||
gather_facts: false
|
||||
|
||||
vars:
|
||||
prometheus_stack_enabled: true
|
||||
prometheus_stack_namespace: "monitoring"
|
||||
prometheus_stack_release_name: "prom"
|
||||
prometheus_stack_chart_name: "kube-prometheus-stack"
|
||||
|
||||
prometheus_grafana_enabled: true
|
||||
grafana_admin_user: "admin"
|
||||
prometheus_grafana_admin_password: "molecule-test-pass"
|
||||
prometheus_grafana_ingress_enabled: false
|
||||
prometheus_grafana_ingress_host: "grafana.local"
|
||||
prometheus_grafana_ingress_class: "nginx"
|
||||
|
||||
grafana_storage_enabled: true
|
||||
grafana_storage_size: "5Gi"
|
||||
grafana_storage_class: ""
|
||||
|
||||
prometheus_retention_days: 7
|
||||
prometheus_storage_size: "10Gi"
|
||||
prometheus_storage_class: ""
|
||||
|
||||
prometheus_alertmanager_enabled: true
|
||||
prometheus_alertmanager_storage_size: "2Gi"
|
||||
prometheus_node_exporter_enabled: true
|
||||
prometheus_kube_state_metrics_enabled: true
|
||||
|
||||
prometheus_resources:
|
||||
requests:
|
||||
cpu: 200m
|
||||
memory: 512Mi
|
||||
limits:
|
||||
cpu: 1000m
|
||||
memory: 2Gi
|
||||
|
||||
grafana_resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
limits:
|
||||
cpu: 300m
|
||||
memory: 256Mi
|
||||
|
||||
tasks:
|
||||
- name: Render kube-prometheus-stack Helm values
|
||||
ansible.builtin.template:
|
||||
src: "{{ playbook_dir }}/../../templates/prometheus-stack-values.yaml.j2"
|
||||
dest: /tmp/prometheus-stack-values.yaml
|
||||
mode: '0644'
|
||||
25
roles/prometheus-stack/molecule/default/molecule.yml
Normal file
25
roles/prometheus-stack/molecule/default/molecule.yml
Normal file
@@ -0,0 +1,25 @@
|
||||
---
|
||||
driver:
|
||||
name: docker
|
||||
|
||||
platforms:
|
||||
- name: prom-test
|
||||
image: geerlingguy/docker-ubuntu2204-ansible:latest
|
||||
pre_build_image: true
|
||||
|
||||
provisioner:
|
||||
name: ansible
|
||||
playbooks:
|
||||
converge: converge.yml
|
||||
verify: verify.yml
|
||||
config_options:
|
||||
defaults:
|
||||
interpreter_python: auto_silent
|
||||
|
||||
verifier:
|
||||
name: ansible
|
||||
|
||||
lint: |
|
||||
set -e
|
||||
yamllint .
|
||||
ansible-lint
|
||||
80
roles/prometheus-stack/molecule/default/verify.yml
Normal file
80
roles/prometheus-stack/molecule/default/verify.yml
Normal file
@@ -0,0 +1,80 @@
|
||||
---
|
||||
- name: Verify — prometheus-stack templates
|
||||
hosts: all
|
||||
become: false
|
||||
gather_facts: false
|
||||
|
||||
tasks:
|
||||
- name: Read rendered Helm values
|
||||
ansible.builtin.slurp:
|
||||
src: /tmp/prometheus-stack-values.yaml
|
||||
register: values_raw
|
||||
|
||||
- name: Parse YAML
|
||||
ansible.builtin.set_fact:
|
||||
v: "{{ values_raw.content | b64decode | from_yaml }}"
|
||||
|
||||
# ── Grafana ─────────────────────────────────────────────────────────────────
|
||||
- name: Assert grafana block exists
|
||||
ansible.builtin.assert:
|
||||
that: v.grafana is defined
|
||||
fail_msg: "Блок grafana отсутствует в values"
|
||||
|
||||
- name: Assert grafana adminUser
|
||||
ansible.builtin.assert:
|
||||
that: v.grafana.adminUser == 'admin'
|
||||
fail_msg: "grafana.adminUser неверный: {{ v.grafana.adminUser }}"
|
||||
|
||||
- name: Assert grafana adminPassword is set
|
||||
ansible.builtin.assert:
|
||||
that: v.grafana.adminPassword | length > 0
|
||||
fail_msg: "grafana.adminPassword не задан"
|
||||
|
||||
- name: Assert grafana persistence is enabled
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- v.grafana.persistence is defined
|
||||
- v.grafana.persistence.enabled == true
|
||||
- v.grafana.persistence.size == '5Gi'
|
||||
fail_msg: "grafana.persistence настроена неверно: {{ v.grafana.persistence }}"
|
||||
|
||||
# ── Prometheus ──────────────────────────────────────────────────────────────
|
||||
- name: Assert prometheus block exists
|
||||
ansible.builtin.assert:
|
||||
that: v.prometheus.prometheusSpec is defined
|
||||
fail_msg: "Блок prometheus.prometheusSpec отсутствует"
|
||||
|
||||
- name: Assert prometheus retention
|
||||
ansible.builtin.assert:
|
||||
that: v.prometheus.prometheusSpec.retention == '7d'
|
||||
fail_msg: "Неверный retention: {{ v.prometheus.prometheusSpec.retention }}"
|
||||
|
||||
- name: Assert prometheus PVC storage
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- v.prometheus.prometheusSpec.storageSpec is defined
|
||||
- v.prometheus.prometheusSpec.storageSpec.volumeClaimTemplate.spec.resources.requests.storage == '10Gi'
|
||||
fail_msg: "Prometheus PVC настроен неверно"
|
||||
|
||||
# ── Alertmanager ────────────────────────────────────────────────────────────
|
||||
- name: Assert alertmanager is enabled
|
||||
ansible.builtin.assert:
|
||||
that: v.alertmanager.enabled == true
|
||||
fail_msg: "alertmanager.enabled должен быть true"
|
||||
|
||||
- name: Assert alertmanager storage
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- v.alertmanager.alertmanagerSpec.storage is defined
|
||||
- v.alertmanager.alertmanagerSpec.storage.volumeClaimTemplate.spec.resources.requests.storage == '2Gi'
|
||||
fail_msg: "Alertmanager PVC настроен неверно"
|
||||
|
||||
# ── Node Exporter & kube-state-metrics ──────────────────────────────────────
|
||||
- name: Assert nodeExporter enabled
|
||||
ansible.builtin.assert:
|
||||
that: v.nodeExporter.enabled == true
|
||||
fail_msg: "nodeExporter.enabled должен быть true"
|
||||
|
||||
- name: Summary
|
||||
ansible.builtin.debug:
|
||||
msg: "Все проверки prometheus-stack прошли успешно"
|
||||
112
roles/prometheus-stack/tasks/main.yml
Normal file
112
roles/prometheus-stack/tasks/main.yml
Normal file
@@ -0,0 +1,112 @@
|
||||
---
|
||||
- name: Prometheus Stack — skip if not enabled
|
||||
ansible.builtin.debug:
|
||||
msg: "kube-prometheus-stack отключён (prometheus_stack_enabled: false). Пропускаем."
|
||||
when: not prometheus_stack_enabled
|
||||
run_once: true
|
||||
|
||||
- name: Prometheus Stack — install
|
||||
when: prometheus_stack_enabled
|
||||
block:
|
||||
- name: Install Helm (if needed)
|
||||
ansible.builtin.include_tasks: "{{ playbook_dir }}/../roles/csi-nfs/tasks/install_helm.yml"
|
||||
|
||||
- name: Add prometheus-community Helm repo
|
||||
kubernetes.core.helm_repository:
|
||||
name: prometheus-community
|
||||
repo_url: "{{ prometheus_stack_chart_repo }}"
|
||||
become: true
|
||||
delegate_to: "{{ groups['k3s_master'][0] }}"
|
||||
run_once: true
|
||||
environment:
|
||||
KUBECONFIG: /etc/rancher/k3s/k3s.yaml
|
||||
|
||||
- name: Update Helm repos
|
||||
ansible.builtin.command: helm repo update
|
||||
become: true
|
||||
delegate_to: "{{ groups['k3s_master'][0] }}"
|
||||
run_once: true
|
||||
changed_when: false
|
||||
environment:
|
||||
KUBECONFIG: /etc/rancher/k3s/k3s.yaml
|
||||
|
||||
- name: Create monitoring namespace
|
||||
ansible.builtin.command: >
|
||||
k3s kubectl create namespace {{ prometheus_stack_namespace }}
|
||||
--dry-run=client -o yaml | k3s kubectl apply -f -
|
||||
become: true
|
||||
delegate_to: "{{ groups['k3s_master'][0] }}"
|
||||
run_once: true
|
||||
changed_when: false
|
||||
|
||||
- name: Template kube-prometheus-stack values
|
||||
ansible.builtin.template:
|
||||
src: prometheus-stack-values.yaml.j2
|
||||
dest: /tmp/prometheus-stack-values.yaml
|
||||
mode: '0644'
|
||||
delegate_to: "{{ groups['k3s_master'][0] }}"
|
||||
run_once: true
|
||||
|
||||
- name: Deploy kube-prometheus-stack via Helm
|
||||
kubernetes.core.helm:
|
||||
name: "{{ prometheus_stack_release_name }}"
|
||||
chart_ref: "prometheus-community/{{ prometheus_stack_chart_name }}"
|
||||
chart_version: "{{ prometheus_stack_version }}"
|
||||
release_namespace: "{{ prometheus_stack_namespace }}"
|
||||
create_namespace: true
|
||||
wait: true
|
||||
timeout: "10m0s"
|
||||
values_files:
|
||||
- /tmp/prometheus-stack-values.yaml
|
||||
become: true
|
||||
delegate_to: "{{ groups['k3s_master'][0] }}"
|
||||
run_once: true
|
||||
environment:
|
||||
KUBECONFIG: /etc/rancher/k3s/k3s.yaml
|
||||
|
||||
- name: Wait for Prometheus to be ready
|
||||
ansible.builtin.command: >
|
||||
k3s kubectl -n {{ prometheus_stack_namespace }}
|
||||
rollout status deployment/{{ prometheus_stack_release_name }}-grafana
|
||||
--timeout=180s
|
||||
become: true
|
||||
delegate_to: "{{ groups['k3s_master'][0] }}"
|
||||
run_once: true
|
||||
register: grafana_ready
|
||||
changed_when: false
|
||||
retries: 3
|
||||
delay: 15
|
||||
until: grafana_ready.rc == 0
|
||||
|
||||
- name: Get Grafana admin password (from secret)
|
||||
ansible.builtin.command: >
|
||||
k3s kubectl -n {{ prometheus_stack_namespace }}
|
||||
get secret {{ prometheus_stack_release_name }}-grafana
|
||||
-o jsonpath="{.data.admin-password}"
|
||||
become: true
|
||||
delegate_to: "{{ groups['k3s_master'][0] }}"
|
||||
run_once: true
|
||||
register: grafana_secret
|
||||
changed_when: false
|
||||
|
||||
- name: Show Grafana access info
|
||||
ansible.builtin.debug:
|
||||
msg:
|
||||
- "Grafana URL: http://{{ hostvars[groups['k3s_master'][0]]['ansible_host'] }}:3000 (NodePort) или через Ingress"
|
||||
- "Admin user: admin"
|
||||
- "Admin password: {{ grafana_secret.stdout | b64decode }}"
|
||||
run_once: true
|
||||
|
||||
- name: Show monitoring namespace pods
|
||||
ansible.builtin.command: >
|
||||
k3s kubectl -n {{ prometheus_stack_namespace }} get pods
|
||||
become: true
|
||||
delegate_to: "{{ groups['k3s_master'][0] }}"
|
||||
run_once: true
|
||||
register: prom_pods
|
||||
changed_when: false
|
||||
|
||||
- name: Monitoring pods
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ prom_pods.stdout_lines }}"
|
||||
run_once: true
|
||||
119
roles/prometheus-stack/templates/prometheus-stack-values.yaml.j2
Normal file
119
roles/prometheus-stack/templates/prometheus-stack-values.yaml.j2
Normal file
@@ -0,0 +1,119 @@
|
||||
## kube-prometheus-stack Helm values
|
||||
## Управляется Ansible (roles/prometheus-stack)
|
||||
|
||||
grafana:
|
||||
enabled: {{ prometheus_grafana_enabled | lower }}
|
||||
adminUser: "{{ grafana_admin_user }}"
|
||||
adminPassword: "{{ prometheus_grafana_admin_password }}"
|
||||
|
||||
resources:
|
||||
requests:
|
||||
cpu: "{{ grafana_resources.requests.cpu }}"
|
||||
memory: "{{ grafana_resources.requests.memory }}"
|
||||
limits:
|
||||
cpu: "{{ grafana_resources.limits.cpu }}"
|
||||
memory: "{{ grafana_resources.limits.memory }}"
|
||||
|
||||
tolerations:
|
||||
- key: "node-role.kubernetes.io/control-plane"
|
||||
operator: "Exists"
|
||||
effect: "NoSchedule"
|
||||
|
||||
{% if prometheus_grafana_ingress_enabled %}
|
||||
ingress:
|
||||
enabled: true
|
||||
ingressClassName: "{{ prometheus_grafana_ingress_class }}"
|
||||
hosts:
|
||||
- "{{ prometheus_grafana_ingress_host }}"
|
||||
paths:
|
||||
- /
|
||||
{% else %}
|
||||
service:
|
||||
type: NodePort
|
||||
nodePort: 32000
|
||||
{% endif %}
|
||||
|
||||
# Готовые дашборды
|
||||
defaultDashboardsEnabled: true
|
||||
defaultDashboardsTimezone: utc
|
||||
|
||||
persistence:
|
||||
enabled: {{ grafana_storage_enabled | lower }}
|
||||
type: pvc
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
size: "{{ grafana_storage_size }}"
|
||||
{% if grafana_storage_class %}
|
||||
storageClassName: "{{ grafana_storage_class }}"
|
||||
{% endif %}
|
||||
|
||||
prometheus:
|
||||
prometheusSpec:
|
||||
retention: "{{ prometheus_retention_days }}d"
|
||||
|
||||
resources:
|
||||
requests:
|
||||
cpu: "{{ prometheus_resources.requests.cpu }}"
|
||||
memory: "{{ prometheus_resources.requests.memory }}"
|
||||
limits:
|
||||
cpu: "{{ prometheus_resources.limits.cpu }}"
|
||||
memory: "{{ prometheus_resources.limits.memory }}"
|
||||
|
||||
tolerations:
|
||||
- key: "node-role.kubernetes.io/control-plane"
|
||||
operator: "Exists"
|
||||
effect: "NoSchedule"
|
||||
|
||||
storageSpec:
|
||||
volumeClaimTemplate:
|
||||
spec:
|
||||
{% if prometheus_storage_class %}
|
||||
storageClassName: "{{ prometheus_storage_class }}"
|
||||
{% endif %}
|
||||
accessModes: ["ReadWriteOnce"]
|
||||
resources:
|
||||
requests:
|
||||
storage: "{{ prometheus_storage_size }}"
|
||||
|
||||
# Собирать метрики со всех namespaces
|
||||
serviceMonitorSelectorNilUsesHelmValues: false
|
||||
podMonitorSelectorNilUsesHelmValues: false
|
||||
ruleSelectorNilUsesHelmValues: false
|
||||
|
||||
alertmanager:
|
||||
enabled: {{ prometheus_alertmanager_enabled | lower }}
|
||||
alertmanagerSpec:
|
||||
tolerations:
|
||||
- key: "node-role.kubernetes.io/control-plane"
|
||||
operator: "Exists"
|
||||
effect: "NoSchedule"
|
||||
storage:
|
||||
volumeClaimTemplate:
|
||||
spec:
|
||||
{% if prometheus_storage_class %}
|
||||
storageClassName: "{{ prometheus_storage_class }}"
|
||||
{% endif %}
|
||||
accessModes: ["ReadWriteOnce"]
|
||||
resources:
|
||||
requests:
|
||||
storage: "{{ prometheus_alertmanager_storage_size }}"
|
||||
|
||||
nodeExporter:
|
||||
enabled: {{ prometheus_node_exporter_enabled | lower }}
|
||||
# DaemonSet — запускается на всех нодах включая мастера
|
||||
tolerations:
|
||||
- operator: "Exists"
|
||||
|
||||
kube-state-metrics:
|
||||
enabled: {{ prometheus_kube_state_metrics_enabled | lower }}
|
||||
tolerations:
|
||||
- key: "node-role.kubernetes.io/control-plane"
|
||||
operator: "Exists"
|
||||
effect: "NoSchedule"
|
||||
|
||||
# Не устанавливать дополнительный Prometheus Operator если уже есть
|
||||
prometheusOperator:
|
||||
tolerations:
|
||||
- key: "node-role.kubernetes.io/control-plane"
|
||||
operator: "Exists"
|
||||
effect: "NoSchedule"
|
||||
Reference in New Issue
Block a user