derniers ajouts
This commit is contained in:
parent
a5c6b7d385
commit
84dd08f702
39 changed files with 997 additions and 0 deletions
94
infrastructure/ansible/vault/README.md
Normal file
94
infrastructure/ansible/vault/README.md
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
# Vault Ansible
|
||||
|
||||
Ce répertoire contient les **secrets chiffrés** utilisés par Ansible pour l’écosystème de l’Alliance Boréale.
|
||||
|
||||
## Rôle de ce répertoire
|
||||
|
||||
On y place les variables sensibles, par exemple :
|
||||
|
||||
- mots de passe applicatifs
|
||||
- secrets PostgreSQL
|
||||
- clés API
|
||||
- jetons d’intégration
|
||||
- secrets Keycloak / Forgejo / PowerDNS
|
||||
- mots de passe de comptes techniques
|
||||
|
||||
Ces fichiers sont destinés à être utilisés avec **Ansible Vault**.
|
||||
|
||||
## Fichiers typiques
|
||||
|
||||
Exemples :
|
||||
|
||||
- `production.yml`
|
||||
- `production-phase2.yml`
|
||||
|
||||
Des fichiers d’exemple non sensibles peuvent aussi exister :
|
||||
|
||||
- `production.yml.example`
|
||||
- `production-phase2.yml.example`
|
||||
|
||||
Les fichiers `*.example` servent de gabarits et **ne doivent contenir aucun vrai secret**.
|
||||
|
||||
## Commandes utiles
|
||||
|
||||
### Créer un nouveau fichier chiffré
|
||||
|
||||
```bash
|
||||
ansible-vault create vault/production.yml
|
||||
```
|
||||
|
||||
### Modifier un fichier chiffré
|
||||
|
||||
```bash
|
||||
ansible-vault edit vault/production.yml
|
||||
```
|
||||
|
||||
### Voir un fichier chiffré
|
||||
|
||||
```bash
|
||||
ansible-vault view vault/production.yml
|
||||
```
|
||||
|
||||
### Chiffrer un fichier existant
|
||||
|
||||
```bash
|
||||
ansible-vault encrypt vault/production.yml
|
||||
```
|
||||
|
||||
## Utilisation dans les playbooks
|
||||
|
||||
Exemple :
|
||||
|
||||
```bash
|
||||
ansible-playbook playbooks/site.yml --ask-vault-pass
|
||||
```
|
||||
|
||||
Ou avec un fichier de mot de passe :
|
||||
|
||||
```bash
|
||||
ansible-playbook playbooks/site.yml --vault-password-file ~/.ansible/vault-pass.txt
|
||||
```
|
||||
|
||||
## Discipline minimale
|
||||
|
||||
- Ne jamais committer de secret en clair.
|
||||
- Ne jamais renommer un fichier `.example` en fichier réel sans le chiffrer.
|
||||
- Garder les secrets regroupés par environnement ou par phase logique.
|
||||
- Préférer des noms explicites.
|
||||
- Éviter de mélanger secrets de prod et secrets de labo dans le même fichier.
|
||||
|
||||
## Convention recommandée
|
||||
|
||||
- `production.yml` : secrets communs de production
|
||||
- `production-phase2.yml` : secrets propres aux services phase 2
|
||||
- autres fichiers : seulement si un découpage clair est utile
|
||||
|
||||
## Rappel important
|
||||
|
||||
Le dépôt peut contenir :
|
||||
|
||||
- la structure,
|
||||
- les exemples,
|
||||
- les références de variables,
|
||||
|
||||
mais **jamais les secrets en clair**.
|
||||
31
infrastructure/icinga2-ansible-noc/Makefile
Normal file
31
infrastructure/icinga2-ansible-noc/Makefile
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
ANSIBLE ?= ansible-playbook
|
||||
INVENTORY ?= ansible/inventory
|
||||
PLAYBOOK ?= ansible/site.yml
|
||||
LIMIT ?=
|
||||
TAGS ?=
|
||||
|
||||
.PHONY: help ping bootstrap deploy check validate-icinga syntax
|
||||
|
||||
help:
|
||||
@echo "Targets:"
|
||||
@echo " make ping - tester SSH/Ansible"
|
||||
@echo " make syntax - validation syntaxique Ansible"
|
||||
@echo " make bootstrap - installation complète"
|
||||
@echo " make deploy - rejouer le déploiement complet"
|
||||
@echo " make check - checks locaux post-déploiement"
|
||||
@echo " make validate-icinga - icinga2 daemon -C sur la cible"
|
||||
|
||||
ping:
|
||||
ansible -i $(INVENTORY) all -m ping
|
||||
|
||||
syntax:
|
||||
$(ANSIBLE) -i $(INVENTORY) $(PLAYBOOK) --syntax-check
|
||||
|
||||
bootstrap deploy:
|
||||
$(ANSIBLE) -i $(INVENTORY) $(PLAYBOOK) $(if $(LIMIT),--limit $(LIMIT),) $(if $(TAGS),--tags $(TAGS),)
|
||||
|
||||
check:
|
||||
ansible -i $(INVENTORY) icinga_servers -m shell -a 'systemctl is-active icinga2 icingadb icingadb-redis mariadb apache2 && icinga2 daemon -C'
|
||||
|
||||
validate-icinga:
|
||||
ansible -i $(INVENTORY) icinga_servers -m shell -a 'icinga2 daemon -C'
|
||||
84
infrastructure/icinga2-ansible-noc/README.md
Normal file
84
infrastructure/icinga2-ansible-noc/README.md
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
# icinga2-ansible-noc
|
||||
|
||||
Dépôt Ansible minimal et reproductible pour déployer un serveur Icinga 2 moderne sur Debian 12, avec :
|
||||
|
||||
- dépôt officiel Icinga ;
|
||||
- Icinga 2 ;
|
||||
- Icinga DB + Redis ;
|
||||
- MariaDB ;
|
||||
- Icinga Web 2 ;
|
||||
- module Icinga DB Web ;
|
||||
- module Business Process Monitoring (BPM) ;
|
||||
- génération de configuration Icinga via rôles Ansible ;
|
||||
- commandes Makefile pour bootstrap, validation et déploiement.
|
||||
|
||||
## Principe
|
||||
|
||||
Icinga reste l'autorité de supervision technique. Le dépôt ne recode pas ce qu'Icinga sait déjà faire : checks, états, notifications, objets, groupes, templates et vues restent natifs.
|
||||
|
||||
Ansible sert à produire une installation reproductible et à générer les objets de supervision depuis des variables versionnées.
|
||||
|
||||
## Préparation
|
||||
|
||||
```bash
|
||||
cp ansible/inventory.example ansible/inventory
|
||||
cp ansible/group_vars/all.yml.example ansible/group_vars/all.yml
|
||||
$EDITOR ansible/inventory
|
||||
$EDITOR ansible/group_vars/all.yml
|
||||
```
|
||||
|
||||
## Commandes
|
||||
|
||||
```bash
|
||||
make ping
|
||||
make bootstrap
|
||||
make check
|
||||
make deploy
|
||||
make validate-icinga
|
||||
```
|
||||
|
||||
## URL
|
||||
|
||||
```text
|
||||
http://<serveur>/icingaweb2
|
||||
```
|
||||
|
||||
## Identifiants initiaux
|
||||
|
||||
Définis dans :
|
||||
|
||||
```text
|
||||
ansible/group_vars/all.yml
|
||||
```
|
||||
|
||||
Variables principales :
|
||||
|
||||
- `icingaweb_admin_user`
|
||||
- `icingaweb_admin_password`
|
||||
|
||||
## Structure
|
||||
|
||||
```text
|
||||
ansible/
|
||||
site.yml
|
||||
inventory.example
|
||||
group_vars/all.yml.example
|
||||
roles/
|
||||
common/
|
||||
icinga_repo/
|
||||
mariadb/
|
||||
icinga2/
|
||||
icingadb/
|
||||
icingaweb2/
|
||||
bpm/
|
||||
monitoring_config/
|
||||
Makefile
|
||||
```
|
||||
|
||||
## Philosophie
|
||||
|
||||
- Debian 12 vanille comme cible de départ.
|
||||
- Official Icinga packages pour rester latest and greatest.
|
||||
- Icinga DB au lieu de l'ancien IDO.
|
||||
- Configuration générée par Ansible, déposée dans `/etc/icinga2/zones.d/global-templates/chezlepro/`.
|
||||
- Validation systématique avec `icinga2 daemon -C`.
|
||||
11
infrastructure/icinga2-ansible-noc/ansible/ansible.cfg
Normal file
11
infrastructure/icinga2-ansible-noc/ansible/ansible.cfg
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
[defaults]
|
||||
inventory = inventory
|
||||
roles_path = roles
|
||||
host_key_checking = False
|
||||
retry_files_enabled = False
|
||||
stdout_callback = yaml
|
||||
interpreter_python = auto_silent
|
||||
|
||||
[privilege_escalation]
|
||||
become = True
|
||||
become_method = sudo
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
---
|
||||
timezone: America/Montreal
|
||||
|
||||
icingaweb_admin_user: icingaadmin
|
||||
icingaweb_admin_password: ChangeMeNow123!
|
||||
|
||||
mysql_root_password: ChangeMeRoot123!
|
||||
icingadb_database: icingadb
|
||||
icingadb_user: icingadb
|
||||
icingadb_password: ChangeMeIcingaDb123!
|
||||
icingaweb_database: icingaweb2
|
||||
icingaweb_db_user: icingaweb2
|
||||
icingaweb_db_password: ChangeMeIcingaWeb123!
|
||||
|
||||
icinga_api_root_password: ChangeMeApiRoot123!
|
||||
|
||||
monitoring_zone_dir: /etc/icinga2/zones.d/global-templates/chezlepro
|
||||
|
||||
notification_mail_to: daniel@example.test
|
||||
notification_mail_from: icinga@example.test
|
||||
|
||||
# Exemple volontairement simple. À remplacer par ton inventaire réel.
|
||||
infrastructure_hosts:
|
||||
- name: proxmox-01
|
||||
address: 192.168.12.11
|
||||
groups: [proxmox, linux]
|
||||
checks:
|
||||
- name: ping4
|
||||
command: hostalive
|
||||
- name: ssh
|
||||
command: ssh
|
||||
- name: pbs-01
|
||||
address: 192.168.12.21
|
||||
groups: [backup, linux]
|
||||
checks:
|
||||
- name: ping4
|
||||
command: hostalive
|
||||
- name: ssh
|
||||
command: ssh
|
||||
|
||||
hostgroups:
|
||||
- proxmox
|
||||
- linux
|
||||
- backup
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
[icinga_servers]
|
||||
icinga-noc ansible_host=192.168.12.50 ansible_user=ansible
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
---
|
||||
- name: Install Icinga Web 2 Business Process module
|
||||
ansible.builtin.apt:
|
||||
name: icingaweb2-module-businessprocess
|
||||
state: latest
|
||||
|
||||
- name: Ensure BPM process directory exists
|
||||
ansible.builtin.file:
|
||||
path: /etc/icingaweb2/modules/businessprocess/processes
|
||||
state: directory
|
||||
owner: www-data
|
||||
group: icingaweb2
|
||||
mode: '2770'
|
||||
|
||||
- name: Enable Business Process module
|
||||
ansible.builtin.command: icingacli module enable businessprocess
|
||||
args:
|
||||
creates: /etc/icingaweb2/enabledModules/businessprocess
|
||||
|
||||
- name: Deploy starter BPM process
|
||||
ansible.builtin.template:
|
||||
src: chezlepro-infra.conf.j2
|
||||
dest: /etc/icingaweb2/modules/businessprocess/processes/chezlepro-infra.conf
|
||||
owner: www-data
|
||||
group: icingaweb2
|
||||
mode: '0660'
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
# Business Process starter file generated by Ansible.
|
||||
# Ajuste ensuite dans l'interface BPM si nécessaire.
|
||||
|
||||
chezlepro-infrastructure = {% for host in infrastructure_hosts %}{{ host.name }};ping4{% if not loop.last %} & {% endif %}{% endfor %}
|
||||
chezlepro-infrastructure.display_name = Infrastructure Chezlepro
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
---
|
||||
- name: Set timezone
|
||||
ansible.builtin.timezone:
|
||||
name: "{{ timezone }}"
|
||||
|
||||
- name: Install base packages
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- ca-certificates
|
||||
- curl
|
||||
- gnupg
|
||||
- lsb-release
|
||||
- apt-transport-https
|
||||
- wget
|
||||
- python3-pymysql
|
||||
- python3-passlib
|
||||
- sudo
|
||||
state: present
|
||||
update_cache: true
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
- name: restart icinga2
|
||||
ansible.builtin.systemd:
|
||||
name: icinga2
|
||||
state: restarted
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
---
|
||||
- name: Install Icinga 2 and monitoring plugins
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- icinga2
|
||||
- monitoring-plugins
|
||||
state: latest
|
||||
|
||||
- name: Set up Icinga 2 API if not already configured
|
||||
ansible.builtin.command: icinga2 api setup
|
||||
args:
|
||||
creates: /etc/icinga2/features-enabled/api.conf
|
||||
notify: restart icinga2
|
||||
|
||||
- name: Configure API root user
|
||||
ansible.builtin.template:
|
||||
src: api-users.conf.j2
|
||||
dest: /etc/icinga2/conf.d/api-users.conf
|
||||
owner: nagios
|
||||
group: nagios
|
||||
mode: '0640'
|
||||
notify: restart icinga2
|
||||
|
||||
- name: Enable Icinga DB feature
|
||||
ansible.builtin.command: icinga2 feature enable icingadb
|
||||
args:
|
||||
creates: /etc/icinga2/features-enabled/icingadb.conf
|
||||
notify: restart icinga2
|
||||
|
||||
- name: Enable and start Icinga 2
|
||||
ansible.builtin.systemd:
|
||||
name: icinga2
|
||||
enabled: true
|
||||
state: started
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
object ApiUser "root" {
|
||||
password = "{{ icinga_api_root_password }}"
|
||||
permissions = [ "*" ]
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
---
|
||||
- name: Read Debian version id
|
||||
ansible.builtin.command: . /etc/os-release && echo $VERSION_ID
|
||||
register: debian_version_id
|
||||
changed_when: false
|
||||
|
||||
- name: Read Debian codename
|
||||
ansible.builtin.command: awk -F'[)(]+' '/VERSION=/ {print $2}' /etc/os-release
|
||||
register: debian_codename
|
||||
changed_when: false
|
||||
|
||||
- name: Download Icinga archive keyring package
|
||||
ansible.builtin.get_url:
|
||||
url: "https://packages.icinga.com/icinga-archive-keyring_latest+debian{{ debian_version_id.stdout }}.deb"
|
||||
dest: /tmp/icinga-archive-keyring.deb
|
||||
mode: '0644'
|
||||
|
||||
- name: Install Icinga archive keyring
|
||||
ansible.builtin.apt:
|
||||
deb: /tmp/icinga-archive-keyring.deb
|
||||
|
||||
- name: Configure official Icinga repository
|
||||
ansible.builtin.copy:
|
||||
dest: "/etc/apt/sources.list.d/{{ debian_codename.stdout }}-icinga.list"
|
||||
mode: '0644'
|
||||
content: |
|
||||
deb [signed-by=/usr/share/keyrings/icinga-archive-keyring.gpg] https://packages.icinga.com/debian icinga-{{ debian_codename.stdout }} main
|
||||
deb-src [signed-by=/usr/share/keyrings/icinga-archive-keyring.gpg] https://packages.icinga.com/debian icinga-{{ debian_codename.stdout }} main
|
||||
|
||||
- name: Refresh apt cache after Icinga repository setup
|
||||
ansible.builtin.apt:
|
||||
update_cache: true
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
- name: restart icingadb
|
||||
ansible.builtin.systemd:
|
||||
name: icingadb
|
||||
state: restarted
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
---
|
||||
- name: Install Icinga DB components
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- icingadb
|
||||
- icingadb-redis
|
||||
- icingaweb2-module-icingadb
|
||||
state: latest
|
||||
|
||||
- name: Enable and start Icinga DB Redis
|
||||
ansible.builtin.systemd:
|
||||
name: icingadb-redis
|
||||
enabled: true
|
||||
state: started
|
||||
|
||||
- name: Check whether Icinga DB schema is already imported
|
||||
ansible.builtin.shell: "mysql --batch --skip-column-names {{ icingadb_database }} -e 'SHOW TABLES LIKE \"host\";'"
|
||||
register: icingadb_schema_check
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Import Icinga DB schema
|
||||
ansible.builtin.shell: "mysql {{ icingadb_database }} < /usr/share/icingadb/schema/mysql/schema.sql"
|
||||
when: icingadb_schema_check.stdout | length == 0
|
||||
|
||||
- name: Configure Icinga DB daemon
|
||||
ansible.builtin.template:
|
||||
src: config.yml.j2
|
||||
dest: /etc/icingadb/config.yml
|
||||
owner: root
|
||||
group: icingadb
|
||||
mode: '0640'
|
||||
notify: restart icingadb
|
||||
|
||||
- name: Enable and start Icinga DB
|
||||
ansible.builtin.systemd:
|
||||
name: icingadb
|
||||
enabled: true
|
||||
state: started
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
database:
|
||||
type: mysql
|
||||
host: localhost
|
||||
port: 3306
|
||||
database: {{ icingadb_database }}
|
||||
user: {{ icingadb_user }}
|
||||
password: {{ icingadb_password }}
|
||||
|
||||
redis:
|
||||
host: localhost
|
||||
port: 6380
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
---
|
||||
- name: Install Apache, PHP and Icinga Web 2
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- apache2
|
||||
- libapache2-mod-php
|
||||
- php
|
||||
- php-cli
|
||||
- php-intl
|
||||
- php-mysql
|
||||
- php-gd
|
||||
- php-curl
|
||||
- php-mbstring
|
||||
- php-xml
|
||||
- icingaweb2
|
||||
- icingacli
|
||||
state: latest
|
||||
|
||||
- name: Ensure Icinga Web 2 config directories exist
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: directory
|
||||
owner: www-data
|
||||
group: icingaweb2
|
||||
mode: '2770'
|
||||
loop:
|
||||
- /etc/icingaweb2
|
||||
- /etc/icingaweb2/modules
|
||||
- /etc/icingaweb2/modules/icingadb
|
||||
- /etc/icingaweb2/modules/icingadb/config
|
||||
|
||||
- name: Configure Icinga Web resources
|
||||
ansible.builtin.template:
|
||||
src: resources.ini.j2
|
||||
dest: /etc/icingaweb2/resources.ini
|
||||
owner: www-data
|
||||
group: icingaweb2
|
||||
mode: '0660'
|
||||
|
||||
- name: Configure Icinga Web authentication
|
||||
ansible.builtin.template:
|
||||
src: authentication.ini.j2
|
||||
dest: /etc/icingaweb2/authentication.ini
|
||||
owner: www-data
|
||||
group: icingaweb2
|
||||
mode: '0660'
|
||||
|
||||
- name: Configure Icinga Web roles
|
||||
ansible.builtin.template:
|
||||
src: roles.ini.j2
|
||||
dest: /etc/icingaweb2/roles.ini
|
||||
owner: www-data
|
||||
group: icingaweb2
|
||||
mode: '0660'
|
||||
|
||||
- name: Create admin user password hash
|
||||
ansible.builtin.command: "openssl passwd -1 {{ icingaweb_admin_password }}"
|
||||
register: icingaweb_admin_hash
|
||||
changed_when: false
|
||||
no_log: true
|
||||
|
||||
- name: Configure local Icinga Web users
|
||||
ansible.builtin.template:
|
||||
src: users.ini.j2
|
||||
dest: /etc/icingaweb2/users.ini
|
||||
owner: www-data
|
||||
group: icingaweb2
|
||||
mode: '0660'
|
||||
no_log: true
|
||||
|
||||
- name: Configure Icinga DB Web module database
|
||||
ansible.builtin.template:
|
||||
src: icingadb-config.ini.j2
|
||||
dest: /etc/icingaweb2/modules/icingadb/config.ini
|
||||
owner: www-data
|
||||
group: icingaweb2
|
||||
mode: '0660'
|
||||
|
||||
- name: Configure Icinga DB Web command transport
|
||||
ansible.builtin.template:
|
||||
src: icingadb-commandtransports.ini.j2
|
||||
dest: /etc/icingaweb2/modules/icingadb/commandtransports.ini
|
||||
owner: www-data
|
||||
group: icingaweb2
|
||||
mode: '0660'
|
||||
|
||||
- name: Enable Icinga DB Web module
|
||||
ansible.builtin.command: icingacli module enable icingadb
|
||||
args:
|
||||
creates: /etc/icingaweb2/enabledModules/icingadb
|
||||
|
||||
- name: Enable and start Apache
|
||||
ansible.builtin.systemd:
|
||||
name: apache2
|
||||
enabled: true
|
||||
state: started
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
[icingaweb2]
|
||||
backend = "ini"
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
[icinga2]
|
||||
transport = "api"
|
||||
host = "localhost"
|
||||
port = "5665"
|
||||
username = "root"
|
||||
password = "{{ icinga_api_root_password }}"
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
[icingadb]
|
||||
resource = "icingadb"
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
[icingaweb_db]
|
||||
type = "db"
|
||||
db = "mysql"
|
||||
host = "localhost"
|
||||
port = "3306"
|
||||
dbname = "{{ icingaweb_database }}"
|
||||
username = "{{ icingaweb_db_user }}"
|
||||
password = "{{ icingaweb_db_password }}"
|
||||
charset = "utf8mb4"
|
||||
|
||||
[icingadb]
|
||||
type = "db"
|
||||
db = "mysql"
|
||||
host = "localhost"
|
||||
port = "3306"
|
||||
dbname = "{{ icingadb_database }}"
|
||||
username = "{{ icingadb_user }}"
|
||||
password = "{{ icingadb_password }}"
|
||||
charset = "utf8mb4"
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
[Administrators]
|
||||
users = "{{ icingaweb_admin_user }}"
|
||||
permissions = "*"
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
[{{ icingaweb_admin_user }}]
|
||||
password = "{{ icingaweb_admin_hash.stdout }}"
|
||||
active = "1"
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
---
|
||||
- name: Install MariaDB
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- mariadb-server
|
||||
- mariadb-client
|
||||
state: present
|
||||
|
||||
- name: Enable and start MariaDB
|
||||
ansible.builtin.systemd:
|
||||
name: mariadb
|
||||
enabled: true
|
||||
state: started
|
||||
|
||||
- name: Create Icinga DB database
|
||||
community.mysql.mysql_db:
|
||||
name: "{{ icingadb_database }}"
|
||||
state: present
|
||||
login_unix_socket: /run/mysqld/mysqld.sock
|
||||
|
||||
- name: Create Icinga DB user
|
||||
community.mysql.mysql_user:
|
||||
name: "{{ icingadb_user }}"
|
||||
password: "{{ icingadb_password }}"
|
||||
priv: "{{ icingadb_database }}.*:ALL"
|
||||
host: localhost
|
||||
state: present
|
||||
login_unix_socket: /run/mysqld/mysqld.sock
|
||||
|
||||
- name: Create Icinga Web database
|
||||
community.mysql.mysql_db:
|
||||
name: "{{ icingaweb_database }}"
|
||||
state: present
|
||||
login_unix_socket: /run/mysqld/mysqld.sock
|
||||
|
||||
- name: Create Icinga Web database user
|
||||
community.mysql.mysql_user:
|
||||
name: "{{ icingaweb_db_user }}"
|
||||
password: "{{ icingaweb_db_password }}"
|
||||
priv: "{{ icingaweb_database }}.*:ALL"
|
||||
host: localhost
|
||||
state: present
|
||||
login_unix_socket: /run/mysqld/mysqld.sock
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
---
|
||||
- name: validate and restart icinga2
|
||||
ansible.builtin.shell: icinga2 daemon -C && systemctl restart icinga2
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
---
|
||||
- name: Ensure generated Icinga config directory exists
|
||||
ansible.builtin.file:
|
||||
path: "{{ monitoring_zone_dir }}"
|
||||
state: directory
|
||||
owner: nagios
|
||||
group: nagios
|
||||
mode: '0750'
|
||||
|
||||
- name: Generate hostgroups
|
||||
ansible.builtin.template:
|
||||
src: hostgroups.conf.j2
|
||||
dest: "{{ monitoring_zone_dir }}/hostgroups.conf"
|
||||
owner: nagios
|
||||
group: nagios
|
||||
mode: '0640'
|
||||
notify: validate and restart icinga2
|
||||
|
||||
- name: Generate hosts
|
||||
ansible.builtin.template:
|
||||
src: hosts.conf.j2
|
||||
dest: "{{ monitoring_zone_dir }}/hosts.conf"
|
||||
owner: nagios
|
||||
group: nagios
|
||||
mode: '0640'
|
||||
notify: validate and restart icinga2
|
||||
|
||||
- name: Generate services
|
||||
ansible.builtin.template:
|
||||
src: services.conf.j2
|
||||
dest: "{{ monitoring_zone_dir }}/services.conf"
|
||||
owner: nagios
|
||||
group: nagios
|
||||
mode: '0640'
|
||||
notify: validate and restart icinga2
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
{% for group in hostgroups %}
|
||||
object HostGroup "{{ group }}" {
|
||||
display_name = "{{ group }}"
|
||||
}
|
||||
{% endfor %}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
{% for host in infrastructure_hosts %}
|
||||
object Host "{{ host.name }}" {
|
||||
import "generic-host"
|
||||
address = "{{ host.address }}"
|
||||
vars.os = "Linux"
|
||||
groups = [ {% for group in host.groups | default([]) %}"{{ group }}"{% if not loop.last %}, {% endif %}{% endfor %} ]
|
||||
}
|
||||
{% endfor %}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
{% for host in infrastructure_hosts %}
|
||||
{% for check in host.checks | default([]) %}
|
||||
apply Service "{{ check.name }}" {
|
||||
import "generic-service"
|
||||
check_command = "{{ check.command }}"
|
||||
assign where host.name == "{{ host.name }}"
|
||||
}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
13
infrastructure/icinga2-ansible-noc/ansible/site.yml
Normal file
13
infrastructure/icinga2-ansible-noc/ansible/site.yml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
---
|
||||
- name: Deploy Icinga 2 NOC server
|
||||
hosts: icinga_servers
|
||||
become: true
|
||||
roles:
|
||||
- common
|
||||
- icinga_repo
|
||||
- mariadb
|
||||
- icinga2
|
||||
- icingadb
|
||||
- icingaweb2
|
||||
- bpm
|
||||
- monitoring_config
|
||||
50
infrastructure/icinga2-ansible-noc/docs/ARCHITECTURE.md
Normal file
50
infrastructure/icinga2-ansible-noc/docs/ARCHITECTURE.md
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
# Architecture
|
||||
|
||||
## Cible
|
||||
|
||||
Ce dépôt installe une pile Icinga moderne sur Debian 12 :
|
||||
|
||||
```text
|
||||
Icinga 2 -> Icinga DB feature -> Redis -> Icinga DB daemon -> MariaDB -> Icinga Web 2
|
||||
-> BPM module
|
||||
```
|
||||
|
||||
## Choix structurants
|
||||
|
||||
- Icinga 2 demeure le moteur de supervision.
|
||||
- Icinga DB remplace l'ancien backend IDO pour rester aligné avec la pile moderne.
|
||||
- Icinga Web 2 fournit l'interface opérateur.
|
||||
- BPM sert à représenter des regroupements métier ou opérationnels.
|
||||
- Ansible génère les objets Icinga depuis `group_vars/all.yml`.
|
||||
|
||||
## Emplacement des configurations générées
|
||||
|
||||
```text
|
||||
/etc/icinga2/zones.d/global-templates/chezlepro/
|
||||
```
|
||||
|
||||
Fichiers générés :
|
||||
|
||||
- `hostgroups.conf`
|
||||
- `hosts.conf`
|
||||
- `services.conf`
|
||||
|
||||
## Cycle opératoire
|
||||
|
||||
```bash
|
||||
make deploy
|
||||
make validate-icinga
|
||||
make check
|
||||
```
|
||||
|
||||
## Extension prévue
|
||||
|
||||
Ajouter progressivement :
|
||||
|
||||
- templates SNMP ;
|
||||
- checks Proxmox ;
|
||||
- checks Ceph ;
|
||||
- checks PBS ;
|
||||
- checks HTTP/TLS ;
|
||||
- notifications mail ;
|
||||
- intégration agent Icinga sur les hôtes Linux.
|
||||
50
infrastructure/icinga2-ansible-noc/docs/RUNBOOK.md
Normal file
50
infrastructure/icinga2-ansible-noc/docs/RUNBOOK.md
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
# Runbook
|
||||
|
||||
## Premier déploiement
|
||||
|
||||
```bash
|
||||
ansible-galaxy collection install -r requirements.yml
|
||||
cp ansible/inventory.example ansible/inventory
|
||||
cp ansible/group_vars/all.yml.example ansible/group_vars/all.yml
|
||||
$EDITOR ansible/inventory
|
||||
$EDITOR ansible/group_vars/all.yml
|
||||
make bootstrap
|
||||
```
|
||||
|
||||
## Validation
|
||||
|
||||
```bash
|
||||
make validate-icinga
|
||||
make check
|
||||
```
|
||||
|
||||
## Ajouter un hôte
|
||||
|
||||
Modifier `ansible/group_vars/all.yml` :
|
||||
|
||||
```yaml
|
||||
infrastructure_hosts:
|
||||
- name: nouveau-serveur
|
||||
address: 192.168.12.99
|
||||
groups: [linux]
|
||||
checks:
|
||||
- name: ping4
|
||||
command: hostalive
|
||||
- name: ssh
|
||||
command: ssh
|
||||
```
|
||||
|
||||
Puis :
|
||||
|
||||
```bash
|
||||
make deploy
|
||||
```
|
||||
|
||||
## Dépannage rapide
|
||||
|
||||
```bash
|
||||
sudo systemctl status icinga2 icingadb icingadb-redis mariadb apache2 --no-pager
|
||||
sudo icinga2 daemon -C
|
||||
sudo journalctl -u icinga2 -n 100 --no-pager
|
||||
sudo journalctl -u icingadb -n 100 --no-pager
|
||||
```
|
||||
3
infrastructure/icinga2-ansible-noc/requirements.yml
Normal file
3
infrastructure/icinga2-ansible-noc/requirements.yml
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
---
|
||||
collections:
|
||||
- name: community.mysql
|
||||
201
post-mortems/post-mortem-vishnu-freeze-am5.md
Normal file
201
post-mortems/post-mortem-vishnu-freeze-am5.md
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
# Post-mortem : freeze hard d'un nœud Proxmox AM5 — diagnostic et mitigation
|
||||
|
||||
## TL;DR
|
||||
|
||||
Un nœud Proxmox/Ceph monté sur ASUS TUF X670E-Plus (Ryzen AM5) gelait sans trace, sans panic, sans rien dans les logs — reset manuel obligatoire. Cause : bug C-states profonds bien documenté sur AM5 sous Linux. Mitigation immédiate via `processor.max_cstate=2` au cmdline kernel. Correction durable : flash BIOS vers une AGESA récente. Bonus : découverte d'une install Proxmox EFI avec `grub-pc` à la place de `grub-efi-amd64` — silencieusement bancale depuis l'origine.
|
||||
|
||||
## Contexte
|
||||
|
||||
- **Cluster** : 3 nœuds Proxmox/Ceph (gandalf, asgard, vishnu)
|
||||
- **Nœud problématique** : vishnu — ASUS TUF Gaming X670E-Plus WiFi, BIOS 3602, AMD Raphael/Granite Ridge, kernel `6.8.12-20-pve`
|
||||
- **Particularité** : seul nœud avec passthrough USB (stick FTDI 0403:6015 vers VM Home Assistant)
|
||||
- **Storage local** : 1 OSD HDD 9.1 TiB (LUKS), 1 OSD NVMe 1.8 TiB, NVMe 100 GiB pour DB
|
||||
- **Symptôme** : freeze hard récurrent, écran figé, plus aucune réponse réseau ni console, reset hardware obligatoire
|
||||
|
||||
## Symptômes — ce qui rend le cas difficile
|
||||
|
||||
Le tableau clinique éliminait d'emblée plusieurs pistes classiques :
|
||||
|
||||
- Aucun kernel panic à l'écran ou en pstore → pas de panic propre
|
||||
- Aucun message dans `journalctl -k -b -1 | tail -100` avant la coupure → kernel n'a pas eu le temps d'écrire
|
||||
- Le softdog watchdog présent (`soft_margin=60`) n'a jamais déclenché de reboot automatique → pas un soft lockup détectable
|
||||
- Pas de redémarrage seul, intervention manuelle obligatoire
|
||||
- HEALTH_WARN Ceph avec slow ops BlueStore — mais sur osd.4 et osd.5 (situés sur les **autres** nœuds), pas sur osd.3 (vishnu). Donc Ceph subissait le freeze, n'en était pas la cause.
|
||||
|
||||
Bref : machine qui meurt instantanément, sans signal préalable. C'est le profil typique d'un blocage hardware ou d'un deadlock kernel total où plus aucune IRQ ne remonte.
|
||||
|
||||
## Hypothèses écartées en cours de diag
|
||||
|
||||
| Hypothèse | Pourquoi écartée |
|
||||
|-----------|------------------|
|
||||
| Disque OSD mourant (slow ops BlueStore) | Slow ops sur OSD distants, pas sur celui de vishnu — symptôme, pas cause |
|
||||
| Quorum corosync perdu | Logs corosync sains, quorum stable, MTU PMTUD négocié à 1397 |
|
||||
| OOM / pression mémoire | Aucun message OOM dans les logs |
|
||||
| MCE matérielle | Aucune entrée MCE décodée par le kernel |
|
||||
| Bug VFIO / IOMMU groups sales | Vérifié — pas de PCI passthrough actif (uniquement USB par vendor:product) |
|
||||
|
||||
## L'indice qui a tout débloqué
|
||||
|
||||
Trois faits convergents :
|
||||
|
||||
1. **Hardware AM5 récent** (Ryzen 7000/9000 sur X670E)
|
||||
2. **Cmdline kernel nu** : `BOOT_IMAGE=/boot/vmlinuz-6.8.12-20-pve root=/dev/mapper/pve-root ro quiet` — aucune mitigation, aucun paramètre IOMMU, aucun ajustement idle
|
||||
3. **Profil de freeze** : hard hang sans trace, propre à un seul nœud
|
||||
|
||||
Cette combinaison correspond à un bug largement documenté de l'écosystème AM5 sous Linux : sous certaines conditions de charge, le CPU descend dans un état d'idle profond (C3/C6) duquel il ne se réveille pas correctement à l'arrivée d'une IRQ. Le core est physiquement gelé — pas de fenêtre pour écrire un panic, pour qu'un watchdog software se déclenche, ou pour que la console réagisse.
|
||||
|
||||
C'est exactement ce que vishnu manifestait.
|
||||
|
||||
## La mitigation appliquée
|
||||
|
||||
### 1. Modification du cmdline kernel
|
||||
|
||||
```bash
|
||||
# Backup
|
||||
cp /etc/default/grub /etc/default/grub.bak.$(date +%Y%m%d)
|
||||
|
||||
# Édition
|
||||
sed -i 's|^GRUB_CMDLINE_LINUX_DEFAULT=.*|GRUB_CMDLINE_LINUX_DEFAULT="quiet amd_iommu=on iommu=pt processor.max_cstate=2"|' /etc/default/grub
|
||||
```
|
||||
|
||||
Trois paramètres :
|
||||
- `processor.max_cstate=2` — la mitigation principale, bloque l'accès aux C-states ≥ C3
|
||||
- `amd_iommu=on iommu=pt` — passage du mode DMA "Translated lazy" (défaut) au mode passthrough optimisé, recommandé dès qu'il y a virtualisation
|
||||
|
||||
### 2. Filets de sécurité pour les prochaines fois
|
||||
|
||||
```bash
|
||||
cat > /etc/sysctl.d/99-debug-freeze.conf <<EOF
|
||||
kernel.sysrq = 1
|
||||
kernel.nmi_watchdog = 1
|
||||
kernel.softlockup_panic = 0
|
||||
kernel.hung_task_timeout_secs = 60
|
||||
EOF
|
||||
```
|
||||
|
||||
Si le bug se reproduisait malgré la mitigation, le NMI watchdog hardware aurait une chance de réveiller un autre core et d'écrire une trace.
|
||||
|
||||
### 3. Découverte collatérale — install Proxmox EFI bancale
|
||||
|
||||
À l'`update-grub`, message inattendu :
|
||||
|
||||
```
|
||||
System booted in EFI-mode but 'grub-efi-amd64' meta-package not installed!
|
||||
```
|
||||
|
||||
Le serveur bootait en EFI mais avec le paquet `grub-pc` (BIOS legacy). L'EFI utilisait le binaire fallback `\EFI\BOOT\BOOTX64.EFI` au lieu d'une entrée `\EFI\proxmox\` propre. Fonctionnel, mais aucune mise à jour de bootloader ne pouvait remonter.
|
||||
|
||||
Correction :
|
||||
|
||||
```bash
|
||||
apt install grub-efi-amd64
|
||||
# Remplace automatiquement grub-pc, déploie un GRUB EFI propre,
|
||||
# crée l'entrée NVRAM Boot0000 'proxmox', relance update-grub
|
||||
```
|
||||
|
||||
Vérification post-install :
|
||||
|
||||
```bash
|
||||
efibootmgr -v
|
||||
# Boot0000* proxmox HD(...)/File(\EFI\proxmox\grubx64.efi) ← nouvelle, en première position
|
||||
# Boot0001* UEFI OS HD(...)/File(\EFI\BOOT\BOOTX64.EFI) ← l'ancienne, garde en fallback
|
||||
```
|
||||
|
||||
### 4. Précaution Ceph avant reboot
|
||||
|
||||
Vu qu'un rebalance était en cours (4 PG remappés, 0.86% misplaced) :
|
||||
|
||||
```bash
|
||||
ceph osd set noout # avant reboot
|
||||
# ... reboot ...
|
||||
ceph osd unset noout # au retour
|
||||
```
|
||||
|
||||
Sans `noout`, Ceph aurait considéré que les OSD locaux de vishnu étaient durablement perdus et déclenché un rebalance plus agressif pendant les ~2 min de coupure.
|
||||
|
||||
## Validation
|
||||
|
||||
Au retour du reboot :
|
||||
|
||||
```bash
|
||||
$ cat /proc/cmdline
|
||||
BOOT_IMAGE=/boot/vmlinuz-6.8.12-20-pve root=/dev/mapper/pve-root ro quiet \
|
||||
amd_iommu=on iommu=pt processor.max_cstate=2
|
||||
|
||||
$ cpupower idle-info
|
||||
CPUidle driver: acpi_idle
|
||||
Available idle states: POLL C1 C2
|
||||
```
|
||||
|
||||
Confirmation visuelle : les C-states ≥ C3 ne sont plus exposés par le driver. Le CPU continue à profiter de C2 (470 s cumulées d'idle dans les premières minutes), donc on n'a pas tué la gestion d'énergie — juste coupé l'accès aux états problématiques.
|
||||
|
||||
## Plan correctif définitif
|
||||
|
||||
La mitigation kernel masque le symptôme. La cause racine est dans le firmware AGESA. Plan en deux temps :
|
||||
|
||||
1. **Court terme (validation 72 h)** : laisser tourner sous charge normale avec `processor.max_cstate=2` et observer. Pas de freeze pendant 72 h = mitigation confirmée efficace.
|
||||
|
||||
2. **Moyen terme (fenêtre planifiée)** : flash BIOS vers la dernière version disponible (3842 au moment de l'incident, AGESA ComboAM5 PI 1.3.0.0a, qui mentionne explicitement des marges de stabilité supplémentaires pour configs Ryzen 9000). Procédure via USB BIOS Flashback — se fait à froid, sans CPU stable, en 5 min.
|
||||
|
||||
Si après le flash BIOS la mitigation kernel n'est plus nécessaire, on pourra retirer `processor.max_cstate=2` (mais conserver `amd_iommu=on iommu=pt`, qui restent recommandés en virtualisation).
|
||||
|
||||
## Si la mitigation ne suffit pas
|
||||
|
||||
Plan de repli progressif :
|
||||
|
||||
1. `processor.max_cstate=2` ne tient pas → descendre à `processor.max_cstate=1`
|
||||
2. Toujours pas → ajouter `idle=nomwait` (force la boucle d'idle à utiliser HLT au lieu de MWAIT)
|
||||
3. Toujours pas → désactiver `Global C-states control` dans le BIOS lui-même
|
||||
4. Si rien ne fonctionne, suspecter alimentation, VRM, ou barrette RAM marginale
|
||||
|
||||
## Leçons
|
||||
|
||||
**Le cmdline Proxmox par défaut est minimaliste.** Sur AM5, ça suffit pour un boot fonctionnel mais pas pour de la stabilité long-terme sous charge virtualisée. Trois paramètres devraient être systématiques sur ce hardware : `amd_iommu=on iommu=pt processor.max_cstate=2` (au moins jusqu'à BIOS récent confirmé sain).
|
||||
|
||||
**Le hardware grand public a sa place dans un homelab — avec discipline.** Une carte mère gaming AM5 fait un excellent nœud Proxmox, à condition de tenir le BIOS à jour et de connaître les ajustements kernel typiques de la plateforme. Ne pas le faire = freezes aléatoires que beaucoup imputent à tort à Linux ou à Proxmox.
|
||||
|
||||
**Vérifier le boot loader d'une install Proxmox même quand "tout marche".** Une install EFI avec `grub-pc` à la place de `grub-efi-amd64` est silencieuse jusqu'au jour où une mise à jour critique ne s'applique pas. À auditer sur tout cluster avec : `[ -d /sys/firmware/efi ] && dpkg -l | grep -E "grub-(pc|efi)"`.
|
||||
|
||||
**Quand un nœud freeze sec sans trace, lister ce qui le différencie des autres** est plus efficace que d'éplucher les logs (qui ne contiennent rien par définition). Ici c'était le hardware AM5 + le passthrough USB. Le second n'était pas la cause, mais la question "qu'est-ce que ce nœud fait que les autres ne font pas" a permis de cibler.
|
||||
|
||||
## Commandes de référence — diagnostic freeze hard sans trace
|
||||
|
||||
```bash
|
||||
# Hardware identification
|
||||
dmidecode -s baseboard-product-name
|
||||
dmidecode -s bios-version
|
||||
dmidecode -s bios-release-date
|
||||
|
||||
# IOMMU groups (utile si VFIO suspecté)
|
||||
for d in /sys/kernel/iommu_groups/*/devices/*; do
|
||||
n=${d#*/iommu_groups/*}; n=${n%%/*}
|
||||
printf 'IOMMU Group %s: ' "$n"; lspci -nns "${d##*/}"
|
||||
done | sort -V
|
||||
|
||||
# Cmdline kernel actuel
|
||||
cat /proc/cmdline
|
||||
|
||||
# C-states disponibles
|
||||
cpupower idle-info
|
||||
|
||||
# Mode boot (EFI ou BIOS)
|
||||
[ -d /sys/firmware/efi ] && echo "EFI mode" || echo "BIOS mode"
|
||||
|
||||
# Config bootloader Proxmox (LVM vs ZFS)
|
||||
proxmox-boot-tool status 2>/dev/null
|
||||
ls -la /boot/efi/EFI/
|
||||
|
||||
# Boot du dernier crash
|
||||
journalctl -k -b -1 | tail -100
|
||||
journalctl -b -1 -p err
|
||||
```
|
||||
|
||||
## Références utiles
|
||||
|
||||
- Proxmox Wiki — PCI(e) Passthrough : https://pve.proxmox.com/wiki/PCI(e)_Passthrough
|
||||
- Proxmox Wiki — Host Bootloader : https://pve.proxmox.com/wiki/Host_Bootloader
|
||||
- AMD AGESA changelog (par modèle de carte mère, sur le site du fabricant) — vérifier régulièrement les mentions "stability"
|
||||
|
||||
---
|
||||
|
||||
*Post-mortem rédigé suite à un diagnostic du 29 avril 2026. Hardware : ASUS TUF Gaming X670E-Plus WiFi, BIOS 3602. Stack : Proxmox VE 8 / kernel 6.8.12-20-pve / Ceph Quincy.*
|
||||
Loading…
Reference in a new issue