From 3f9d4aefce3f0393ba5a4e90d08330d062667230 Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Thu, 22 Jan 2026 10:52:13 -0800 Subject: [PATCH] Switch Alloy from Homebrew to source-built binary with LaunchAgent CGO-enabled build required for macOS native DNS resolver (Tailscale MagicDNS). Homebrew bottle is built with CGO_ENABLED=0 which uses Go's pure DNS resolver that doesn't respect /etc/resolver/* on macOS. - Remove Homebrew installation, use ~/.local/bin/alloy - Add LaunchAgent plist (mcquack.eblume.alloy) - Update config paths to ~/.config/grafana-alloy - Add build instructions in defaults/main.yml - Add alloy's own logs to mcquack_logs collection --- ansible/roles/alloy/defaults/main.yml | 38 +++++++++++++++++-- ansible/roles/alloy/handlers/main.yml | 6 +-- ansible/roles/alloy/tasks/main.yml | 38 ++++++++++++++----- ansible/roles/alloy/templates/alloy.plist.j2 | 24 ++++++++++++ ansible/roles/alloy/templates/config.alloy.j2 | 2 +- 5 files changed, 91 insertions(+), 17 deletions(-) create mode 100644 ansible/roles/alloy/templates/alloy.plist.j2 diff --git a/ansible/roles/alloy/defaults/main.yml b/ansible/roles/alloy/defaults/main.yml index b01c845..85f420c 100644 --- a/ansible/roles/alloy/defaults/main.yml +++ b/ansible/roles/alloy/defaults/main.yml @@ -1,5 +1,33 @@ --- # Grafana Alloy configuration +# +# BUILDING FROM SOURCE (required for CGO DNS resolution on macOS): +# +# Alloy must be built with CGO_ENABLED=1 to use macOS native DNS resolver, +# which is required for Tailscale MagicDNS hostname resolution. +# The Homebrew bottle is built with CGO_ENABLED=0. +# +# Build on dev machine (gilbert), then copy to indri: +# +# 1. Clone from forge mirror: +# git clone ssh://forgejo@forge.tail8d86e.ts.net/eblume/alloy.git ~/code/3rd/alloy +# +# 2. Set up build tools via mise: +# cd ~/code/3rd/alloy && mise use go@1.25 node yarn +# +# 3. Build with CGO enabled (default in Makefile): +# cd ~/code/3rd/alloy && mise x -- make alloy +# +# 4. Copy binary to indri: +# scp ~/code/3rd/alloy/build/alloy indri:~/.local/bin/alloy +# +# 5. Run ansible to deploy config and LaunchAgent + +# Binary and paths +alloy_binary: /Users/erichblume/.local/bin/alloy +alloy_config_dir: /Users/erichblume/.config/grafana-alloy +alloy_data_dir: /Users/erichblume/.local/share/grafana-alloy +alloy_log_dir: /Users/erichblume/Library/Logs # Textfile collector directory (same as node_exporter for compatibility) alloy_textfile_dir: /opt/homebrew/var/node_exporter/textfile @@ -16,10 +44,6 @@ alloy_instance_label: indri # Scrape interval alloy_scrape_interval: "15s" -# Config paths -alloy_config_dir: /opt/homebrew/etc/grafana-alloy -alloy_data_dir: /opt/homebrew/var/lib/grafana-alloy/data - # Log paths to collect alloy_brew_logs: - path: /opt/homebrew/var/log/forgejo.log @@ -30,6 +54,12 @@ alloy_brew_logs: stream: stdout alloy_mcquack_logs: + - path: /Users/erichblume/Library/Logs/mcquack.alloy.out.log + service: alloy + stream: stdout + - path: /Users/erichblume/Library/Logs/mcquack.alloy.err.log + service: alloy + stream: stderr - path: /Users/erichblume/Library/Logs/mcquack.borgmatic.out.log service: borgmatic stream: stdout diff --git a/ansible/roles/alloy/handlers/main.yml b/ansible/roles/alloy/handlers/main.yml index 5948838..4132dfb 100644 --- a/ansible/roles/alloy/handlers/main.yml +++ b/ansible/roles/alloy/handlers/main.yml @@ -1,6 +1,6 @@ --- - name: Restart alloy - ansible.builtin.command: brew services restart grafana-alloy - async: 120 - poll: 0 + ansible.builtin.shell: | + launchctl unload ~/Library/LaunchAgents/mcquack.eblume.alloy.plist 2>/dev/null || true + launchctl load ~/Library/LaunchAgents/mcquack.eblume.alloy.plist changed_when: true diff --git a/ansible/roles/alloy/tasks/main.yml b/ansible/roles/alloy/tasks/main.yml index 644a6b2..99d256d 100644 --- a/ansible/roles/alloy/tasks/main.yml +++ b/ansible/roles/alloy/tasks/main.yml @@ -1,11 +1,18 @@ --- # Grafana Alloy installation and configuration -# Replaces node_exporter for metrics, adds log collection +# See defaults/main.yml for build instructions -- name: Install grafana-alloy via homebrew - community.general.homebrew: - name: grafana-alloy - state: present +- name: Verify alloy binary exists + ansible.builtin.stat: + path: "{{ alloy_binary }}" + register: alloy_binary_stat + +- name: Fail if alloy binary not found + ansible.builtin.fail: + msg: | + Alloy binary not found at {{ alloy_binary }}. + Please build from source first (see ansible/roles/alloy/defaults/main.yml) + when: not alloy_binary_stat.stat.exists - name: Ensure alloy config directory exists ansible.builtin.file: @@ -68,8 +75,21 @@ notify: Restart alloy no_log: true -- name: Ensure alloy service is started - ansible.builtin.command: brew services start grafana-alloy - register: alloy_brew_start - changed_when: "'Successfully started' in alloy_brew_start.stdout" +- name: Deploy alloy LaunchAgent plist + ansible.builtin.template: + src: alloy.plist.j2 + dest: ~/Library/LaunchAgents/mcquack.eblume.alloy.plist + mode: '0644' + notify: Restart alloy + +- name: Check if alloy LaunchAgent is loaded + ansible.builtin.command: launchctl list mcquack.eblume.alloy + register: alloy_launchctl_check + changed_when: false + failed_when: false + +- name: Load alloy LaunchAgent if not loaded + ansible.builtin.command: launchctl load ~/Library/LaunchAgents/mcquack.eblume.alloy.plist + when: alloy_launchctl_check.rc != 0 + changed_when: true failed_when: false diff --git a/ansible/roles/alloy/templates/alloy.plist.j2 b/ansible/roles/alloy/templates/alloy.plist.j2 new file mode 100644 index 0000000..a3a2353 --- /dev/null +++ b/ansible/roles/alloy/templates/alloy.plist.j2 @@ -0,0 +1,24 @@ + + + + + + Label + mcquack.eblume.alloy + ProgramArguments + + {{ alloy_binary }} + run + {{ alloy_config_dir }}/config.alloy + --storage.path={{ alloy_data_dir }} + + RunAtLoad + + KeepAlive + + StandardOutPath + {{ alloy_log_dir }}/mcquack.alloy.out.log + StandardErrorPath + {{ alloy_log_dir }}/mcquack.alloy.err.log + + diff --git a/ansible/roles/alloy/templates/config.alloy.j2 b/ansible/roles/alloy/templates/config.alloy.j2 index d6d2e75..1702505 100644 --- a/ansible/roles/alloy/templates/config.alloy.j2 +++ b/ansible/roles/alloy/templates/config.alloy.j2 @@ -43,7 +43,7 @@ prometheus.exporter.postgres "postgresql" { data_source_names = ["postgresql://{{ alloy_postgres_user }}:{{ alloy_postgres_password | urlencode }}@{{ alloy_postgres_host }}:{{ alloy_postgres_port }}/{{ alloy_postgres_database }}?sslmode=disable"] // Custom queries for vacuum and XID monitoring - custom_queries_config_path = "/opt/homebrew/etc/grafana-alloy/postgres_queries.yaml" + custom_queries_config_path = "{{ alloy_config_dir }}/postgres_queries.yaml" } // Scrape PostgreSQL metrics