deploy hermes
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
|
||||
common_timezone: America/Chicago
|
||||
|
||||
common_ntp_server: 10.1.71.33 # sundial
|
||||
common_ntp_server: "sundial.local.mk-labs.cloud"
|
||||
|
||||
common_packages:
|
||||
- curl
|
||||
@@ -20,3 +20,15 @@ common_packages:
|
||||
- lsb-release
|
||||
- net-tools
|
||||
- dnsutils
|
||||
- lvm2
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# LVM root volume expansion
|
||||
# Override common_root_pv/vg/lv per host if the template uses different names.
|
||||
# Ubuntu 24.04 cloud templates use ubuntu-vg/ubuntu-lv by default.
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
common_expand_root_lvm: true
|
||||
common_root_pv: /dev/sda3 # Ubuntu cloud-init partition layout
|
||||
common_root_vg: ubuntu-vg
|
||||
common_root_lv: ubuntu-lv
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
---
|
||||
# ------------------------------------------------------------------------------
|
||||
# FILE: roles/common/handlers/main.yml
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
- name: restart chrony
|
||||
systemd:
|
||||
name: chrony
|
||||
state: restarted
|
||||
|
||||
- name: restart sshd
|
||||
systemd:
|
||||
name: "{{ 'ssh' if ansible_os_family == 'Debian' else 'sshd' }}"
|
||||
state: restarted
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
---
|
||||
# ------------------------------------------------------------------------------
|
||||
# FILE: roles/common/tasks/main.yml
|
||||
# DESCRIPTION: Baseline configuration applied to all managed hosts.
|
||||
# Handles hostname, timezone, core packages, NTP, and
|
||||
# ansible user setup.
|
||||
# DESCRIPTION: Baseline configuration applied to all managed Ubuntu hosts.
|
||||
# Handles hostname, timezone, core packages, NTP, ansible user,
|
||||
# and optional LVM root volume expansion.
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
- name: Set hostname
|
||||
@@ -10,7 +11,7 @@
|
||||
name: "{{ inventory_hostname | replace('_', '-') }}"
|
||||
|
||||
- name: Set timezone
|
||||
community.general.timezone:
|
||||
timezone:
|
||||
name: "{{ common_timezone }}"
|
||||
|
||||
- name: Update apt cache
|
||||
@@ -41,7 +42,7 @@
|
||||
state: started
|
||||
enabled: yes
|
||||
|
||||
- name: Ensure ansible user has sudo without password
|
||||
- name: Ensure ansible user has passwordless sudo
|
||||
lineinfile:
|
||||
path: /etc/sudoers.d/{{ ansible_user }}
|
||||
line: "{{ ansible_user }} ALL=(ALL) NOPASSWD:ALL"
|
||||
@@ -49,33 +50,28 @@
|
||||
mode: '0440'
|
||||
validate: 'visudo -cf %s'
|
||||
|
||||
# --- Step-CA SSH certificate enrollment ---
|
||||
- name: Enroll host in step-ca SSH CA
|
||||
include_tasks: step_ca_client.yml
|
||||
# ------------------------------------------------------------------------------
|
||||
# LVM root volume expansion
|
||||
# Extends the root PV to the full disk size and grows the LV + filesystem.
|
||||
# This codifies what was previously done manually after provisioning.
|
||||
# Runs only when common_expand_root_lvm is true (default: true).
|
||||
# Safe to re-run — pvresize and lvextend are idempotent when already at max.
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
- name: Create authorized principals directory
|
||||
ansible.builtin.file:
|
||||
path: /etc/ssh/auth_principals
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0755'
|
||||
- name: Expand root PV to full disk size
|
||||
command: pvresize {{ common_root_pv }}
|
||||
register: pvresize_result
|
||||
changed_when: "'changed' in pvresize_result.stdout or pvresize_result.rc == 0"
|
||||
when: common_expand_root_lvm | bool
|
||||
|
||||
- name: Create authorized principals files
|
||||
ansible.builtin.copy:
|
||||
content: "{{ item.principals | join('\n') }}\n"
|
||||
dest: "/etc/ssh/auth_principals/{{ item.local_user }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
loop: "{{ step_ca_principal_mappings }}"
|
||||
notify: restart sshd
|
||||
|
||||
- name: Configure sshd AuthorizedPrincipalsFile
|
||||
ansible.builtin.lineinfile:
|
||||
path: /etc/ssh/sshd_config
|
||||
line: "AuthorizedPrincipalsFile /etc/ssh/auth_principals/%u"
|
||||
regexp: "^#?AuthorizedPrincipalsFile"
|
||||
state: present
|
||||
notify: restart sshd
|
||||
|
||||
- name: Extend root LV to 100% of free VG space
|
||||
lvol:
|
||||
vg: "{{ common_root_vg }}"
|
||||
lv: "{{ common_root_lv }}"
|
||||
size: +100%FREE
|
||||
resizefs: yes
|
||||
when:
|
||||
- common_expand_root_lvm | bool
|
||||
ignore_errors: yes
|
||||
# ignore_errors because lvextend returns non-zero when already at max size.
|
||||
# resizefs: yes handles the resize2fs call inline — no separate task needed.
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
---
|
||||
# ------------------------------------------------------------------------------
|
||||
# FILE: roles/monitoring/defaults/main.yml
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# Directory layout on cinderella-castle
|
||||
monitoring_base_dir: /opt/docker/monitoring
|
||||
|
||||
# Data disk — LVM on /dev/sdb, mounted at this path for Prometheus TSDB
|
||||
prometheus_data_dir: /var/lib/prometheus
|
||||
prometheus_vg: prometheus-vg
|
||||
prometheus_lv: prometheus-lv
|
||||
|
||||
# Prometheus retention
|
||||
prometheus_retention_time: 15d
|
||||
prometheus_retention_size: 58GB # 64GB disk, leaving headroom
|
||||
|
||||
# Scrape interval
|
||||
prometheus_scrape_interval: 30s
|
||||
|
||||
# Grafana
|
||||
grafana_port: 3000
|
||||
grafana_admin_user: admin
|
||||
|
||||
# Service DNS (via Traefik)
|
||||
grafana_hostname: grafana.local.mk-labs.cloud
|
||||
|
||||
# Trusted proxy (lightning-lane)
|
||||
trusted_proxy_ip: 10.1.71.35
|
||||
|
||||
# Prometheus port (internal, not exposed via Traefik)
|
||||
prometheus_port: 9090
|
||||
|
||||
# Alertmanager port (internal)
|
||||
alertmanager_port: 9093
|
||||
|
||||
# Node exporter port
|
||||
node_exporter_port: 9100
|
||||
@@ -1,10 +0,0 @@
|
||||
---
|
||||
# ------------------------------------------------------------------------------
|
||||
# FILE: roles/monitoring/handlers/main.yml
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
- name: restart monitoring
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: "{{ monitoring_base_dir }}"
|
||||
state: present
|
||||
recreate: always
|
||||
@@ -1,203 +0,0 @@
|
||||
---
|
||||
# ------------------------------------------------------------------------------
|
||||
# FILE: roles/monitoring/tasks/main.yml
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# ── Data disk (LVM) ───────────────────────────────────────────────────────────
|
||||
|
||||
- name: Install LVM2
|
||||
ansible.builtin.apt:
|
||||
name: lvm2
|
||||
state: present
|
||||
update_cache: true
|
||||
|
||||
- name: Check if PV already exists on /dev/sdb
|
||||
ansible.builtin.command: pvs /dev/sdb
|
||||
register: pvs_result
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Check if disk has a partition table signature
|
||||
ansible.builtin.command: wipefs /dev/sdb
|
||||
register: wipefs_check
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
when: pvs_result.rc != 0
|
||||
|
||||
- name: Wipe GPT protective partition signature from /dev/sdb
|
||||
ansible.builtin.command: wipefs -a /dev/sdb
|
||||
when:
|
||||
- pvs_result.rc != 0
|
||||
- wipefs_check.stdout | length > 0
|
||||
|
||||
- name: Create LVM volume group on /dev/sdb
|
||||
community.general.lvg:
|
||||
vg: prometheus-vg
|
||||
pvs: /dev/sdb
|
||||
state: present
|
||||
when: pvs_result.rc != 0
|
||||
|
||||
- name: Check if logical volume already exists
|
||||
ansible.builtin.command: lvs /dev/prometheus-vg/prometheus-lv
|
||||
register: lvs_result
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Create logical volume for Prometheus TSDB
|
||||
community.general.lvol:
|
||||
vg: prometheus-vg
|
||||
lv: prometheus-lv
|
||||
size: 100%FREE
|
||||
state: present
|
||||
shrink: false
|
||||
when: lvs_result.rc != 0
|
||||
|
||||
- name: Format logical volume as ext4
|
||||
community.general.filesystem:
|
||||
fstype: ext4
|
||||
dev: /dev/prometheus-vg/prometheus-lv
|
||||
|
||||
- name: Create Prometheus data directory mount point
|
||||
ansible.builtin.file:
|
||||
path: "{{ prometheus_data_dir }}"
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0755"
|
||||
|
||||
- name: Mount Prometheus logical volume
|
||||
ansible.posix.mount:
|
||||
path: "{{ prometheus_data_dir }}"
|
||||
src: /dev/prometheus-vg/prometheus-lv
|
||||
fstype: ext4
|
||||
opts: defaults
|
||||
state: mounted
|
||||
|
||||
- name: Set ownership of Prometheus data directory
|
||||
ansible.builtin.file:
|
||||
path: "{{ prometheus_data_dir }}"
|
||||
state: directory
|
||||
owner: "65534" # nobody — Prometheus container runs as this UID
|
||||
group: "65534"
|
||||
mode: "0755"
|
||||
|
||||
# ── Directory layout ───────────────────────────────────────────────────────────
|
||||
|
||||
- name: Create monitoring directory structure
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: directory
|
||||
owner: "{{ ansible_user }}"
|
||||
group: docker
|
||||
mode: "0775"
|
||||
loop:
|
||||
- "{{ monitoring_base_dir }}"
|
||||
- "{{ monitoring_base_dir }}/prometheus"
|
||||
- "{{ monitoring_base_dir }}/grafana"
|
||||
- "{{ monitoring_base_dir }}/alertmanager"
|
||||
- "{{ monitoring_base_dir }}/pve-exporter"
|
||||
- "{{ monitoring_base_dir }}/snmp-exporter"
|
||||
|
||||
# ── Config files ───────────────────────────────────────────────────────────────
|
||||
|
||||
- name: Deploy Prometheus configuration
|
||||
ansible.builtin.template:
|
||||
src: prometheus.yaml.j2
|
||||
dest: "{{ monitoring_base_dir }}/prometheus/prometheus.yaml"
|
||||
owner: "{{ ansible_user }}"
|
||||
group: docker
|
||||
mode: "0644"
|
||||
notify: restart monitoring
|
||||
|
||||
- name: Deploy Alertmanager configuration
|
||||
ansible.builtin.template:
|
||||
src: alertmanager.yml.j2
|
||||
dest: "{{ monitoring_base_dir }}/alertmanager/alertmanager.yml"
|
||||
owner: "{{ ansible_user }}"
|
||||
group: docker
|
||||
mode: "0644"
|
||||
notify: restart monitoring
|
||||
|
||||
- name: Deploy Grafana provisioning datasource
|
||||
ansible.builtin.template:
|
||||
src: grafana-datasource.yml.j2
|
||||
dest: "{{ monitoring_base_dir }}/grafana/datasource.yml"
|
||||
owner: "{{ ansible_user }}"
|
||||
group: docker
|
||||
mode: "0644"
|
||||
notify: restart monitoring
|
||||
|
||||
- name: Deploy PVE exporter credentials file
|
||||
ansible.builtin.template:
|
||||
src: pve.yml.j2
|
||||
dest: "{{ monitoring_base_dir }}/pve-exporter/pve.yml"
|
||||
owner: "{{ ansible_user }}"
|
||||
group: docker
|
||||
mode: "0644"
|
||||
no_log: true
|
||||
notify: restart monitoring
|
||||
|
||||
# ── SNMP exporter config ───────────────────────────────────────────────────────
|
||||
# Extract the default snmp.yml from the image (contains full if_mib metrics
|
||||
# mapping) then inject the mk-labs community auth into the auths section.
|
||||
|
||||
- name: Extract default snmp.yml from snmp-exporter image
|
||||
ansible.builtin.shell:
|
||||
cmd: >
|
||||
docker run --rm --entrypoint cat prom/snmp-exporter:latest
|
||||
/etc/snmp_exporter/snmp.yml
|
||||
> {{ monitoring_base_dir }}/snmp-exporter/snmp.yml
|
||||
changed_when: true
|
||||
|
||||
- name: Inject mk_labs_v2c auth into snmp.yml
|
||||
ansible.builtin.shell:
|
||||
cmd: |
|
||||
python3 - << 'EOF'
|
||||
path = '{{ monitoring_base_dir }}/snmp-exporter/snmp.yml'
|
||||
with open(path, 'r') as f:
|
||||
content = f.read()
|
||||
auth = (
|
||||
' mk_labs_v2c:\n'
|
||||
' community: {{ vault_snmp_community }}\n'
|
||||
' security_level: noAuthNoPriv\n'
|
||||
' auth_protocol: MD5\n'
|
||||
' priv_protocol: DES\n'
|
||||
' version: 2\n'
|
||||
)
|
||||
if 'mk_labs_v2c' not in content:
|
||||
content = content.replace('\nmodules:\n', '\n' + auth + 'modules:\n', 1)
|
||||
with open(path, 'w') as f:
|
||||
f.write(content)
|
||||
print('injected')
|
||||
else:
|
||||
print('already present')
|
||||
EOF
|
||||
register: snmp_inject
|
||||
changed_when: "'injected' in snmp_inject.stdout"
|
||||
no_log: true
|
||||
notify: restart monitoring
|
||||
|
||||
# ── Compose stack ──────────────────────────────────────────────────────────────
|
||||
|
||||
- name: Deploy monitoring compose file from boilerplate
|
||||
ansible.builtin.copy:
|
||||
src: "{{ playbook_dir }}/../../boilerplates/monitoring/compose.yaml"
|
||||
dest: "{{ monitoring_base_dir }}/compose.yaml"
|
||||
owner: "{{ ansible_user }}"
|
||||
group: docker
|
||||
mode: "0644"
|
||||
notify: restart monitoring
|
||||
|
||||
- name: Deploy .env with secrets from vault
|
||||
ansible.builtin.template:
|
||||
src: env.j2
|
||||
dest: "{{ monitoring_base_dir }}/.env"
|
||||
owner: "{{ ansible_user }}"
|
||||
group: docker
|
||||
mode: "0600"
|
||||
notify: restart monitoring
|
||||
|
||||
- name: Start monitoring stack
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: "{{ monitoring_base_dir }}"
|
||||
state: present
|
||||
@@ -1,13 +0,0 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# FILE: roles/monitoring/templates/alertmanager.yml.j2
|
||||
# DESCRIPTION: Alertmanager configuration — routing deferred (P.XX)
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
route:
|
||||
receiver: blackhole
|
||||
group_wait: 30s
|
||||
group_interval: 5m
|
||||
repeat_interval: 4h
|
||||
|
||||
receivers:
|
||||
- name: blackhole
|
||||
@@ -1,11 +0,0 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# FILE: roles/monitoring/templates/env.j2
|
||||
# DESCRIPTION: Environment file for monitoring compose stack
|
||||
# Deployed to {{ monitoring_base_dir }}/.env
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
GRAFANA_ADMIN_USER={{ grafana_admin_user }}
|
||||
GRAFANA_ADMIN_PASSWORD={{ grafana_admin_password }}
|
||||
PROMETHEUS_RETENTION_TIME={{ prometheus_retention_time }}
|
||||
PROMETHEUS_RETENTION_SIZE={{ prometheus_retention_size }}
|
||||
TRUSTED_PROXIES={{ trusted_proxy_ip }}
|
||||
@@ -1,14 +0,0 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# FILE: roles/monitoring/templates/grafana-datasource.yml.j2
|
||||
# DESCRIPTION: Auto-provisions Prometheus as Grafana default datasource
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
apiVersion: 1
|
||||
|
||||
datasources:
|
||||
- name: Prometheus
|
||||
type: prometheus
|
||||
access: proxy
|
||||
url: http://prometheus:9090
|
||||
isDefault: true
|
||||
editable: false
|
||||
@@ -1,205 +0,0 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# FILE: ansible/playbooks/roles/monitoring/templates/prometheus.yaml.j2
|
||||
# DESCRIPTION: Prometheus scrape configuration
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
global:
|
||||
scrape_interval: 30s
|
||||
evaluation_interval: 30s
|
||||
|
||||
alerting:
|
||||
alertmanagers:
|
||||
- static_configs:
|
||||
- targets:
|
||||
- alertmanager:9093
|
||||
|
||||
rule_files: []
|
||||
|
||||
scrape_configs:
|
||||
|
||||
# Prometheus self-monitoring
|
||||
- job_name: prometheus
|
||||
static_configs:
|
||||
- targets:
|
||||
- localhost:9090
|
||||
|
||||
# cinderella-castle node exporter (this host)
|
||||
- job_name: node-cinderella-castle
|
||||
static_configs:
|
||||
- targets:
|
||||
- 10.1.71.31:9100
|
||||
labels:
|
||||
hostname: cinderella-castle
|
||||
|
||||
# Proxmox nodes (node exporter on each hypervisor)
|
||||
- job_name: proxmox-nodes
|
||||
static_configs:
|
||||
- targets:
|
||||
- 10.1.71.11:9100
|
||||
- 10.1.71.12:9100
|
||||
- 10.1.71.13:9100
|
||||
labels:
|
||||
job: proxmox
|
||||
|
||||
# Traefik metrics
|
||||
- job_name: traefik
|
||||
static_configs:
|
||||
- targets:
|
||||
- 10.1.71.35:8080
|
||||
labels:
|
||||
hostname: lightning-lane
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Proxmox VE Exporter — cluster-level metrics (VM states, storage, CPU)
|
||||
# Uses the multi-target pattern: Prometheus passes the cluster address as a
|
||||
# query parameter to the exporter, which proxies to the Proxmox API.
|
||||
# One scrape job covers all three nodes via the cluster API endpoint.
|
||||
# --------------------------------------------------------------------------
|
||||
- job_name: proxmox-cluster
|
||||
metrics_path: /pve
|
||||
params:
|
||||
module: [default]
|
||||
cluster: ["1"]
|
||||
node: [""]
|
||||
static_configs:
|
||||
- targets:
|
||||
- 10.1.71.11 # main-street-usa — cluster API endpoint
|
||||
labels:
|
||||
cluster: magic-kingdom
|
||||
relabel_configs:
|
||||
- source_labels: [__address__]
|
||||
target_label: __param_target
|
||||
- source_labels: [__param_target]
|
||||
target_label: instance
|
||||
- target_label: __address__
|
||||
replacement: pve-exporter:9221
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Pure Storage FlashArray -- utilidor (DEFERRED until Purity 6.7+)
|
||||
#
|
||||
# When utilidor is upgraded to Purity 6.7+, uncomment this job and remove
|
||||
# the legacy exporter container. No container needed -- Prometheus scrapes
|
||||
# the native OpenMetrics endpoint on the array directly over HTTPS.
|
||||
#
|
||||
# Also add vault_pure_utilidor_api_token to group_vars/all/vault and create
|
||||
# a read-only API token on utilidor under System -> Users -> API Tokens.
|
||||
# --------------------------------------------------------------------------
|
||||
# - job_name: pure-flasharray-utilidor
|
||||
# metrics_path: /metrics/array
|
||||
# scheme: https
|
||||
# tls_config:
|
||||
# insecure_skip_verify: true
|
||||
# authorization:
|
||||
# credentials: {{ vault_pure_utilidor_api_token }}
|
||||
# params:
|
||||
# namespace: ["purefa"]
|
||||
# static_configs:
|
||||
# - targets:
|
||||
# - utilidor.local.mk-labs.cloud
|
||||
# labels:
|
||||
# array: utilidor
|
||||
# array_type: physical
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# UniFi Network — SNMP scrape jobs (one per device for clean Grafana filtering)
|
||||
# All targets use the if_mib module for interface throughput, errors, and
|
||||
# up/down state. Auth uses the mk_labs_v2c community string from vault.
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
- job_name: snmp-udm-pro
|
||||
metrics_path: /snmp
|
||||
params:
|
||||
auth: [mk_labs_v2c]
|
||||
module: [if_mib]
|
||||
static_configs:
|
||||
- targets:
|
||||
- 192.168.1.1
|
||||
labels:
|
||||
device: world-drive
|
||||
model: UDM-Pro
|
||||
relabel_configs:
|
||||
- source_labels: [__address__]
|
||||
target_label: __param_target
|
||||
- source_labels: [__param_target]
|
||||
target_label: instance
|
||||
- target_label: __address__
|
||||
replacement: 10.1.71.31:9116
|
||||
|
||||
- job_name: snmp-usw-pro-aggregation
|
||||
metrics_path: /snmp
|
||||
params:
|
||||
auth: [mk_labs_v2c]
|
||||
module: [if_mib]
|
||||
static_configs:
|
||||
- targets:
|
||||
- 192.168.1.123
|
||||
labels:
|
||||
device: usw-pro-aggregation
|
||||
model: USW-Pro-Aggregation
|
||||
relabel_configs:
|
||||
- source_labels: [__address__]
|
||||
target_label: __param_target
|
||||
- source_labels: [__param_target]
|
||||
target_label: instance
|
||||
- target_label: __address__
|
||||
replacement: 10.1.71.31:9116
|
||||
|
||||
- job_name: snmp-usw-pro-max-24
|
||||
metrics_path: /snmp
|
||||
scrape_interval: 60s
|
||||
scrape_timeout: 55s
|
||||
params:
|
||||
auth: [mk_labs_v2c]
|
||||
module: [if_mib]
|
||||
static_configs:
|
||||
- targets:
|
||||
- 192.168.1.226
|
||||
labels:
|
||||
device: usw-pro-max-24
|
||||
model: USW-Pro-Max-24
|
||||
relabel_configs:
|
||||
- source_labels: [__address__]
|
||||
target_label: __param_target
|
||||
- source_labels: [__param_target]
|
||||
target_label: instance
|
||||
- target_label: __address__
|
||||
replacement: 10.1.71.31:9116
|
||||
|
||||
- job_name: snmp-usw-lite-16-poe
|
||||
metrics_path: /snmp
|
||||
params:
|
||||
auth: [mk_labs_v2c]
|
||||
module: [if_mib]
|
||||
static_configs:
|
||||
- targets:
|
||||
- 192.168.1.136
|
||||
labels:
|
||||
device: usw-lite-16-poe
|
||||
model: USW-Lite-16-PoE
|
||||
relabel_configs:
|
||||
- source_labels: [__address__]
|
||||
target_label: __param_target
|
||||
- source_labels: [__param_target]
|
||||
target_label: instance
|
||||
- target_label: __address__
|
||||
replacement: 10.1.71.31:9116
|
||||
|
||||
- job_name: snmp-ups-tower
|
||||
metrics_path: /snmp
|
||||
params:
|
||||
auth: [mk_labs_v2c]
|
||||
module: [if_mib]
|
||||
static_configs:
|
||||
- targets:
|
||||
- 192.168.1.233
|
||||
labels:
|
||||
device: ups-tower
|
||||
model: UPS-Tower
|
||||
relabel_configs:
|
||||
- source_labels: [__address__]
|
||||
target_label: __param_target
|
||||
- source_labels: [__param_target]
|
||||
target_label: instance
|
||||
- target_label: __address__
|
||||
replacement: 10.1.71.31:9116
|
||||
@@ -1,11 +0,0 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# FILE: ansible/playbooks/roles/monitoring/templates/pve.yml.j2
|
||||
# DESCRIPTION: Proxmox VE Exporter credentials file.
|
||||
# Mounted read-only into the pve-exporter container.
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
default:
|
||||
user: prometheus@pve
|
||||
token_name: prometheus-token
|
||||
token_value: {{ vault_proxmox_prometheus_token }}
|
||||
verify_ssl: false
|
||||
28
ansible/playbooks/roles/ollama/defaults/main.yml
Normal file
28
ansible/playbooks/roles/ollama/defaults/main.yml
Normal file
@@ -0,0 +1,28 @@
|
||||
---
|
||||
# ------------------------------------------------------------------------------
|
||||
# FILE: roles/ollama/defaults/main.yml
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# ROCm
|
||||
ollama_rocm_version: "6.2" # ROCm release stream — update when upgrading
|
||||
ollama_rocm_packages:
|
||||
- hip-runtime-amd # Pulls in full ROCm stack
|
||||
- rocminfo # GPU visibility verification tool
|
||||
- rocm-smi-lib # ROCm system management interface
|
||||
|
||||
# Ollama
|
||||
ollama_version: "latest"
|
||||
ollama_port: 11434
|
||||
ollama_host: "0.0.0.0" # Listen on all interfaces for cluster access
|
||||
ollama_default_model: "qwen3:8b"
|
||||
|
||||
# HSA override — required for RX 5000-series (gfx1010 / RDNA 1)
|
||||
# Tells ROCm to treat the GPU as gfx1010 which has supported LLVM targets.
|
||||
ollama_hsa_override_gfx_version: "10.1.0"
|
||||
|
||||
# Data disk
|
||||
ollama_setup_data_disk: true
|
||||
ollama_data_disk: /dev/sdb
|
||||
ollama_data_dir: /var/lib/ollama # Ollama default models path
|
||||
ollama_data_vg: ollama-vg
|
||||
ollama_data_lv: ollama-lv
|
||||
20
ansible/playbooks/roles/ollama/handlers/main.yml
Normal file
20
ansible/playbooks/roles/ollama/handlers/main.yml
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
# ------------------------------------------------------------------------------
|
||||
# FILE: roles/ollama/handlers/main.yml
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
- name: update apt cache
|
||||
apt:
|
||||
update_cache: yes
|
||||
|
||||
- name: update initramfs
|
||||
command: update-initramfs -u
|
||||
|
||||
- name: reload systemd
|
||||
systemd:
|
||||
daemon_reload: yes
|
||||
|
||||
- name: restart ollama
|
||||
systemd:
|
||||
name: ollama
|
||||
state: restarted
|
||||
229
ansible/playbooks/roles/ollama/tasks/main.yml
Normal file
229
ansible/playbooks/roles/ollama/tasks/main.yml
Normal file
@@ -0,0 +1,229 @@
|
||||
---
|
||||
# ------------------------------------------------------------------------------
|
||||
# FILE: roles/ollama/tasks/main.yml
|
||||
# DESCRIPTION: Deploys Ollama with AMD ROCm GPU acceleration on Ubuntu 24.04.
|
||||
# Handles:
|
||||
# - ROCm repository and driver stack installation
|
||||
# - Data disk preparation (LVM, ext4, persistent mount)
|
||||
# - Ollama installation and systemd service configuration
|
||||
# - HSA override for RX 5000-series (gfx1010) compatibility
|
||||
# - Initial model pull
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# ROCm prerequisites
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
- name: Install ROCm prerequisite packages
|
||||
apt:
|
||||
name:
|
||||
- wget
|
||||
- gnupg
|
||||
- ca-certificates
|
||||
- lvm2
|
||||
state: present
|
||||
update_cache: yes
|
||||
|
||||
- name: Add ROCm apt repository signing key
|
||||
apt_key:
|
||||
url: https://repo.radeon.com/rocm/rocm.gpg.key
|
||||
state: present
|
||||
|
||||
- name: Add ROCm apt repository
|
||||
apt_repository:
|
||||
repo: "deb [arch=amd64] https://repo.radeon.com/rocm/apt/{{ ollama_rocm_version }} {{ ansible_distribution_release }} main"
|
||||
state: present
|
||||
filename: rocm
|
||||
notify: update apt cache
|
||||
|
||||
- name: Flush handlers to update apt cache before ROCm install
|
||||
meta: flush_handlers
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# ROCm driver stack
|
||||
# hip-runtime-amd pulls in the full ROCm stack as a dependency.
|
||||
# rocminfo is used to verify GPU visibility after install.
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
- name: Install ROCm packages
|
||||
apt:
|
||||
name: "{{ ollama_rocm_packages }}"
|
||||
state: present
|
||||
notify: update initramfs
|
||||
|
||||
- name: Create /opt/rocm symlink to versioned directory
|
||||
file:
|
||||
src: "/opt/rocm-{{ ollama_rocm_version }}.0"
|
||||
dest: /opt/rocm
|
||||
state: link
|
||||
force: false
|
||||
|
||||
- name: Add ROCm binaries to system PATH
|
||||
copy:
|
||||
dest: /etc/profile.d/rocm.sh
|
||||
content: |
|
||||
export PATH=$PATH:/opt/rocm/bin
|
||||
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/rocm/lib
|
||||
mode: '0644'
|
||||
|
||||
- name: Add wed user to render and video groups for GPU access
|
||||
user:
|
||||
name: "{{ ansible_user }}"
|
||||
groups:
|
||||
- render
|
||||
- video
|
||||
append: yes
|
||||
|
||||
- name: Add ollama user to render and video groups (created by Ollama installer)
|
||||
user:
|
||||
name: ollama
|
||||
groups:
|
||||
- render
|
||||
- video
|
||||
append: yes
|
||||
ignore_errors: yes
|
||||
# ollama user does not exist yet at this point — Ollama installer creates it.
|
||||
# This task re-runs after install via the post-install handler.
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Data disk — LVM setup on /dev/sdb
|
||||
# Follows the same pattern as the monitoring role (cinderella-castle).
|
||||
# Wipes any stale GPT/partition signatures before creating the PV to avoid
|
||||
# the "GPT protective partition" error encountered during monitoring deploy.
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
- name: Wipe any existing partition signatures from data disk
|
||||
command: wipefs -a {{ ollama_data_disk }}
|
||||
args:
|
||||
creates: /dev/{{ ollama_data_vg }}
|
||||
when: ollama_setup_data_disk | bool
|
||||
|
||||
- name: Create LVM physical volume on data disk
|
||||
command: pvcreate {{ ollama_data_disk }}
|
||||
args:
|
||||
creates: /dev/{{ ollama_data_vg }}
|
||||
when: ollama_setup_data_disk | bool
|
||||
|
||||
- name: Create LVM volume group
|
||||
lvg:
|
||||
vg: "{{ ollama_data_vg }}"
|
||||
pvs: "{{ ollama_data_disk }}"
|
||||
when: ollama_setup_data_disk | bool
|
||||
|
||||
- name: Create LVM logical volume using 100% of VG
|
||||
lvol:
|
||||
vg: "{{ ollama_data_vg }}"
|
||||
lv: "{{ ollama_data_lv }}"
|
||||
size: 100%FREE
|
||||
shrink: false
|
||||
when: ollama_setup_data_disk | bool
|
||||
|
||||
- name: Format logical volume as ext4
|
||||
filesystem:
|
||||
fstype: ext4
|
||||
dev: "/dev/{{ ollama_data_vg }}/{{ ollama_data_lv }}"
|
||||
opts: "-L ollama-data"
|
||||
when: ollama_setup_data_disk | bool
|
||||
|
||||
- name: Create Ollama model directory mount point
|
||||
file:
|
||||
path: "{{ ollama_data_dir }}"
|
||||
state: directory
|
||||
mode: '0755'
|
||||
|
||||
- name: Mount Ollama data volume and add fstab entry
|
||||
mount:
|
||||
path: "{{ ollama_data_dir }}"
|
||||
src: "/dev/{{ ollama_data_vg }}/{{ ollama_data_lv }}"
|
||||
fstype: ext4
|
||||
opts: defaults,noatime
|
||||
state: mounted
|
||||
when: ollama_setup_data_disk | bool
|
||||
# noatime: eliminates inode access time writes on every model file read.
|
||||
# Meaningful win for large sequential reads like model loading.
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Ollama installation
|
||||
# Uses the official install script which creates the ollama user and
|
||||
# systemd service unit. We override the service with our own config.
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
- name: Download Ollama install script
|
||||
get_url:
|
||||
url: https://ollama.ai/install.sh
|
||||
dest: /tmp/ollama_install.sh
|
||||
mode: '0755'
|
||||
|
||||
- name: Run Ollama install script
|
||||
command: /tmp/ollama_install.sh
|
||||
environment: "{{ {} if ollama_version == 'latest' else {'OLLAMA_VERSION': ollama_version} }}"
|
||||
args:
|
||||
creates: /usr/local/bin/ollama
|
||||
|
||||
- name: Add ollama user to render and video groups (post-install)
|
||||
user:
|
||||
name: ollama
|
||||
groups:
|
||||
- render
|
||||
- video
|
||||
append: yes
|
||||
|
||||
- name: Set ownership of Ollama data directory to ollama user
|
||||
file:
|
||||
path: "{{ ollama_data_dir }}"
|
||||
owner: ollama
|
||||
group: ollama
|
||||
recurse: yes
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Systemd service override
|
||||
# Injects HSA_OVERRIDE_GFX_VERSION for RX 5000-series compatibility and
|
||||
# sets OLLAMA_MODELS to the data disk mount point.
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
- name: Create systemd override directory for ollama service
|
||||
file:
|
||||
path: /etc/systemd/system/ollama.service.d
|
||||
state: directory
|
||||
mode: '0755'
|
||||
|
||||
- name: Deploy ollama systemd service override
|
||||
template:
|
||||
src: ollama-override.conf.j2
|
||||
dest: /etc/systemd/system/ollama.service.d/override.conf
|
||||
mode: '0644'
|
||||
notify:
|
||||
- reload systemd
|
||||
- restart ollama
|
||||
|
||||
- name: Flush handlers to apply service config before model pull
|
||||
meta: flush_handlers
|
||||
|
||||
- name: Enable and start Ollama service
|
||||
systemd:
|
||||
name: ollama
|
||||
state: started
|
||||
enabled: yes
|
||||
daemon_reload: yes
|
||||
|
||||
- name: Wait for Ollama API to become available
|
||||
uri:
|
||||
url: "http://localhost:{{ ollama_port }}/api/tags"
|
||||
status_code: 200
|
||||
register: ollama_health
|
||||
retries: 12
|
||||
delay: 5
|
||||
until: ollama_health.status == 200
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Initial model pull
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
- name: Pull initial model
|
||||
command: ollama pull {{ ollama_default_model }}
|
||||
environment:
|
||||
OLLAMA_HOST: "http://localhost:{{ ollama_port }}"
|
||||
register: model_pull
|
||||
changed_when: "'pulled' in model_pull.stdout or model_pull.rc == 0"
|
||||
timeout: 600
|
||||
# Qwen3 8B Q4_K_M is ~5GB — allow 10 minutes on first pull.
|
||||
@@ -0,0 +1,13 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# FILE: roles/ollama/templates/ollama-override.conf.j2
|
||||
# DESCRIPTION: Systemd drop-in for the ollama service.
|
||||
# Sets environment variables required for AMD GPU operation
|
||||
# and configures the model storage path.
|
||||
# Managed by Ansible — do not edit manually.
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
[Service]
|
||||
Environment="OLLAMA_HOST={{ ollama_host }}:{{ ollama_port }}"
|
||||
Environment="OLLAMA_MODELS={{ ollama_data_dir }}"
|
||||
Environment="HSA_OVERRIDE_GFX_VERSION={{ ollama_hsa_override_gfx_version }}"
|
||||
Environment="ROCR_VISIBLE_DEVICES=0"
|
||||
Reference in New Issue
Block a user