feat: Add log collection for Gitea runners
Security Audit / Infrastructure Scan (IaC) (push) Successful in 1m7s
Deploy Infrastructure / plan (push) Failing after 1m20s
Deploy Infrastructure / apply (push) Has been skipped
Deploy Infrastructure / integration-tests (push) Has been skipped
Security Audit / Docker Image Scan (docker.io/apache/tika:latest) (push) Failing after 5m14s
Security Audit / Docker Image Scan (docker.io/gotenberg/gotenberg:8) (push) Failing after 5m21s
Security Audit / Docker Image Scan (docker.io/library/redis:7) (push) Failing after 56s
Security Audit / Docker Image Scan (docker.io/library/postgres:15) (push) Failing after 1m21s
Security Audit / Docker Image Scan (ghcr.io/icereed/paperless-gpt:latest) (push) Failing after 1m9s
Security Audit / Docker Image Scan (shenxn/protonmail-bridge:latest) (push) Successful in 1m55s
Security Audit / Docker Image Scan (ghcr.io/paperless-ngx/paperless-ngx:latest) (push) Failing after 4m1s

- Create Promtail role to collect and ship logs to Loki
- Add setup-gitea-runner-logging.yml playbook
- Configure collection of act_runner service logs via journald
- Add Docker daemon log collection
- Uses dynamic Proxmox inventory (proxmox_gitea_runner group)

Logs are now visible in Loki/Grafana for monitoring CI/CD pipeline
execution and debugging build failures.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-02-18 18:52:38 +01:00
co-authored by Claude
parent 3e9f4686fc
commit 29c30fc45d
6 changed files with 329 additions and 0 deletions
@@ -0,0 +1,72 @@
---
# Setup log collection for Gitea runners
# Deploys Promtail to collect act_runner logs and ship to Loki
#
# Usage:
# ansible-playbook -i inventory/ plays/setup-gitea-runner-logging.yml
- name: Setup Gitea Runner Log Collection
hosts: proxmox_gitea_runner
become: yes
vars:
promtail_loki_url: "http://loki.srv.renner.uno:3100/loki/api/v1/push"
promtail_scrape_configs:
# Collect act_runner service logs via journald
- job_name: act_runner
journal:
max_age: 12h
matches: _SYSTEMD_UNIT=act_runner.service
relabel_configs:
- source_labels: ['__journal__systemd_unit']
target_label: unit
- source_labels: ['__journal__hostname']
target_label: host
# Collect system logs
- job_name: syslog
static_configs:
- targets:
- localhost
labels:
job: syslog
host: "{{ inventory_hostname }}"
service: gitea-runner
__path__: /var/log/syslog
# Collect Docker daemon logs (runners use Docker)
- job_name: docker
journal:
max_age: 12h
matches: _SYSTEMD_UNIT=docker.service
relabel_configs:
- source_labels: ['__journal__systemd_unit']
target_label: unit
- source_labels: ['__journal__hostname']
target_label: host
roles:
- promtail
post_tasks:
- name: Verify promtail is running
systemd:
name: promtail
state: started
register: promtail_status
- name: Display promtail status
debug:
msg: "Promtail is {{ 'running' if promtail_status.status.ActiveState == 'active' else 'not running' }} on {{ inventory_hostname }}"
- name: Test Loki connectivity
uri:
url: "http://loki.srv.renner.uno:3100/ready"
method: GET
status_code: 200
timeout: 10
register: loki_health
ignore_errors: yes
- name: Display Loki connectivity status
debug:
msg: "Loki server is {{ 'reachable' if loki_health.status == 200 else 'NOT reachable - check network' }}"
+36
View File
@@ -0,0 +1,36 @@
---
# Promtail Configuration
# Version and download
promtail_version: "3.4.2"
promtail_arch: "linux-amd64"
promtail_download_url: "https://github.com/grafana/loki/releases/download/v{{ promtail_version }}/promtail-{{ promtail_arch }}.zip"
promtail_bin_path: "/usr/local/bin/promtail"
# Directories
promtail_config_dir: "/etc/promtail"
promtail_config_file: "{{ promtail_config_dir }}/promtail-config.yaml"
promtail_positions_file: "/var/lib/promtail/positions.yaml"
# Server configuration
promtail_http_listen_port: 9080
promtail_http_listen_address: "0.0.0.0"
# Loki server
promtail_loki_url: "http://loki.srv.renner.uno:3100/loki/api/v1/push"
# Log scrape configurations
# Override in playbook or host_vars for specific services
promtail_scrape_configs:
- job_name: system
static_configs:
- targets:
- localhost
labels:
job: syslog
host: "{{ inventory_hostname }}"
__path__: /var/log/syslog
# Systemd service
promtail_user: "promtail"
promtail_group: "promtail"
+6
View File
@@ -0,0 +1,6 @@
---
- name: Restart promtail
systemd:
name: promtail
state: restarted
daemon_reload: yes
+112
View File
@@ -0,0 +1,112 @@
---
- name: Install dependencies
apt:
name:
- unzip
- curl
state: present
update_cache: yes
- name: Create promtail group
group:
name: "{{ promtail_group }}"
system: yes
state: present
- name: Create promtail user
user:
name: "{{ promtail_user }}"
group: "{{ promtail_group }}"
system: yes
shell: /usr/sbin/nologin
home: /var/lib/promtail
create_home: yes
state: present
- name: Add promtail user to systemd-journal group
user:
name: "{{ promtail_user }}"
groups: systemd-journal
append: yes
- name: Create config directory
file:
path: "{{ promtail_config_dir }}"
state: directory
owner: "{{ promtail_user }}"
group: "{{ promtail_group }}"
mode: '0755'
- name: Create positions directory
file:
path: "{{ promtail_positions_file | dirname }}"
state: directory
owner: "{{ promtail_user }}"
group: "{{ promtail_group }}"
mode: '0755'
- name: Check if promtail binary exists
stat:
path: "{{ promtail_bin_path }}"
register: promtail_binary
- name: Get installed promtail version
command: "{{ promtail_bin_path }} --version"
register: installed_version
changed_when: false
failed_when: false
when: promtail_binary.stat.exists
- name: Download promtail
get_url:
url: "{{ promtail_download_url }}"
dest: /tmp/promtail.zip
mode: '0644'
when: not promtail_binary.stat.exists or (installed_version.stdout is defined and promtail_version not in installed_version.stdout)
- name: Extract promtail binary
unarchive:
src: /tmp/promtail.zip
dest: /tmp/
remote_src: yes
when: not promtail_binary.stat.exists or (installed_version.stdout is defined and promtail_version not in installed_version.stdout)
- name: Install promtail binary
copy:
src: "/tmp/promtail-{{ promtail_arch }}"
dest: "{{ promtail_bin_path }}"
remote_src: yes
mode: '0755'
when: not promtail_binary.stat.exists or (installed_version.stdout is defined and promtail_version not in installed_version.stdout)
notify: Restart promtail
- name: Clean up downloaded files
file:
path: "{{ item }}"
state: absent
loop:
- /tmp/promtail.zip
- "/tmp/promtail-{{ promtail_arch }}"
- name: Configure promtail
template:
src: promtail-config.yaml.j2
dest: "{{ promtail_config_file }}"
owner: "{{ promtail_user }}"
group: "{{ promtail_group }}"
mode: '0644'
notify: Restart promtail
- name: Create systemd service
template:
src: promtail.service.j2
dest: /etc/systemd/system/promtail.service
mode: '0644'
notify: Restart promtail
- name: Enable and start promtail
systemd:
name: promtail
state: started
enabled: yes
daemon_reload: yes
@@ -0,0 +1,79 @@
server:
http_listen_port: {{ promtail_http_listen_port }}
http_listen_address: {{ promtail_http_listen_address }}
grpc_listen_port: 0
positions:
filename: {{ promtail_positions_file }}
clients:
- url: {{ promtail_loki_url }}
tenant_id: default
scrape_configs:
{% for config in promtail_scrape_configs %}
- job_name: {{ config.job_name }}
{% if config.journal is defined %}
journal:
max_age: {{ config.journal.max_age | default('12h') }}
labels:
job: {{ config.job_name }}
host: {{ inventory_hostname }}
{% if config.journal.matches is defined %}
matches: {{ config.journal.matches }}
{% endif %}
{% if config.relabel_configs is defined %}
relabel_configs:
{% for relabel in config.relabel_configs %}
- source_labels: {{ relabel.source_labels | to_json }}
target_label: {{ relabel.target_label }}
{% if relabel.regex is defined %}
regex: '{{ relabel.regex }}'
{% endif %}
{% if relabel.replacement is defined %}
replacement: '{{ relabel.replacement }}'
{% endif %}
{% endfor %}
{% endif %}
{% endif %}
{% if config.static_configs is defined %}
static_configs:
{% for static in config.static_configs %}
- targets:
{% for target in static.targets %}
- {{ target }}
{% endfor %}
labels:
{% for key, value in static.labels.items() %}
{{ key }}: {{ value }}
{% endfor %}
{% endfor %}
{% if config.pipeline_stages is defined %}
pipeline_stages:
{% for stage in config.pipeline_stages %}
{% if stage.multiline is defined %}
- multiline:
firstline: '{{ stage.multiline.firstline }}'
{% if stage.multiline.max_wait_time is defined %}
max_wait_time: {{ stage.multiline.max_wait_time }}
{% endif %}
{% endif %}
{% if stage.regex is defined %}
- regex:
expression: '{{ stage.regex.expression }}'
{% endif %}
{% if stage.labels is defined %}
- labels:
{% for label in stage.labels %}
{{ label }}:
{% endfor %}
{% endif %}
{% if stage.timestamp is defined %}
- timestamp:
source: {{ stage.timestamp.source }}
format: '{{ stage.timestamp.format }}'
{% endif %}
{% endfor %}
{% endif %}
{% endif %}
{% endfor %}
@@ -0,0 +1,24 @@
[Unit]
Description=Promtail Log Collector
Documentation=https://grafana.com/docs/loki/latest/clients/promtail/
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User={{ promtail_user }}
Group={{ promtail_group }}
ExecStart={{ promtail_bin_path }} -config.file={{ promtail_config_file }}
Restart=always
RestartSec=5
TimeoutStopSec=30
# Security hardening
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
ReadWritePaths=/var/lib/promtail
CapabilityBoundingSet=CAP_DAC_READ_SEARCH
[Install]
WantedBy=multi-user.target