Agentless Ansible 集合,用於在 RHEL、Fedora、Ubuntu 與 SUSE 上執行 Linux fleet 健康檢查。已於四種系統上以真實 systemd 容器測試每次推送。
AI 翻譯
Agentless Ansible collection for Linux fleet health checks across RHEL, Fedora, Ubuntu, and SUSE. Tested on all four with real systemd containers on every push.
外掛Python · JinjaGitHub @sameeralam3127上線中營運者未確認
試用過了嗎? 點一下就好,不需要登入。
正在載入留言。
正在確認登入狀態。
LinuxVitals — agentless Ansible collection for Linux fleet health checks (RHEL/Fedora/Ubuntu/SUSE): baseline/postcheck comparison, opt-in self-healing, and a self-contained HTML dashboard.
翻譯準備中
取自儲存庫 README
正在載入完整 README。 以下為已儲存的摘錄。
Agentless health checks for a mixed Linux fleet. Scan every host over SSH,
get one self-contained HTML dashboard and a JSON report, and optionally let it
restart what it can fix.
Nothing is installed on managed hosts. Every check is read-only unless you
explicitly opt in to remediation.
Three consecutive runs against the same fleet — healthy, broken, then healed.
Watch the columns that move: Boot Space Healthy → Low, Reboot
No → Required, Auto-Fixed 0 → 3. Every frame is a real run.檢視完整 README ↗Agentless health checks for a mixed Linux fleet. Scan every host over SSH, get one self-contained HTML dashboard and a JSON report, and optionally let it restart what it can fix.
Nothing is installed on managed hosts. Every check is read-only unless you explicitly opt in to remediation.
Collection: sameeralam3127.linux_vitals · Browse a sample dashboard · Ansible Galaxy
Three consecutive runs against the same fleet — healthy, broken, then healed.
Watch the columns that move: Boot Space Healthy → Low, Reboot
No → Required, Auto-Fixed 0 → 3. Every frame is a real run.
One HTML file per run. No CDN, no external requests, no server to host it — open it from a laptop or attach it to a change ticket. Search, filter, and sort the fleet; expand any host for its findings, kernel and bootloader state, boot space, and self-healing outcome.
| Expanded host detail | Baseline vs. postcheck |
|---|---|
Full tour, JSON schema, and every finding it can raise: Report Guide.
Spins up Ubuntu, Rocky, and Fedora containers, breaks them on purpose, and shows LinuxVitals detecting and remediating the damage:
git clone https://github.com/sameeralam3127/linux-vitals
cd linux-vitals/demo && ./run.sh
You end up with three dashboards to compare — healthy, broken, healed — covering
failed services, a required reboot, boot-space pressure, journal errors, and a
memory threshold breach. Needs only Docker and ansible-core.
./run.sh clean removes every trace of it.
demo/README.md has the per-fault tags, and an honest account of which faults are genuinely induced and which are staged.
ansible-galaxy collection install sameeralam3127.linux_vitals
That pulls community.general too, which the notification tasks need.
If the control node runs ansible-core 2.16, install both collections with a
community.general <12 constraint instead; see the
2.16 installation instructions.
Create inventory.ini — the shipped playbooks all target a group called
linux_servers:
[linux_servers]
rhel01 ansible_host=192.0.2.10
ubuntu01 ansible_host=192.0.2.11
[linux_servers:vars]
ansible_user=automation
ansible_become=true
A fuller starting point, with key paths and a mixed fleet, is in examples/inventory/hosts.example.ini.
ansible-playbook -i inventory.ini sameeralam3127.linux_vitals.healthcheck
reports/linux_vitals_report.html
Reports land next to your inventory, not inside the installed collection.
Every path — reports, snapshots, and the optional .env — resolves from your
inventory directory, so the same command works whether you installed from
Galaxy or cloned this repo.
That is the whole loop. Everything below is optional.
| Area | Checks |
|---|---|
| System | CPU and platform discovery, virtualization type, uptime, last reboot, asset serial |
| Memory & load | RAM usage; 5-minute load per CPU; pressure stalls (PSI) for CPU, memory and I/O; swap paging rate -- each against warning and critical thresholds |
| Services | sssd, systemd-journald, and the resolved time-sync unit |
| Logs | Journal entries at priority err or worse (configurable) in a bounded window, plus kernel/bootloader install failures |
| Kernel & boot | Running vs. latest installed kernel, bootloader default entry, reboot-required (per distro), /boot free space, rescue image |
| Security | SELinux, AppArmor, recent failed logins |
| TLS (opt-in) | Certificate expiry, weak signatures, obsolete TLS versions, served-vs-on-disk mismatches |
Every finding carries a severity, and each host rolls up to the worst one it has. Results render as an HTML dashboard and a JSON report, with optional Slack, email, and generic-webhook summaries.
Findings are classified info, warning, or critical:
| Severity | Meaning |
|---|---|
critical | Something is broken now, or you are blind to it |
warning | Something will break, or a control you rely on is off |
info | Worth seeing, too noisy to page on |
By default a host fails on any finding, which is how every release before severity existed behaved. Raise the threshold to turn severity into triage — every finding is still reported, but only ones at or above the threshold count against the host:
linux_vitals_fail_on_severity: critical # default: info
Retune individual findings rather than replacing the whole map, so findings added in a later release keep a sensible default:
linux_vitals_finding_severity_overrides:
apparmor_disabled: info # we do not run AppArmor
reboot_required: critical # our change window is tight
Because final_status drives the fleet health score, raising the threshold
raises the score — but a host where a check could not run never counts as
healthy (see Status).
The full finding reference — id, severity, and trigger for
every check — is in the Report Guide, and what
to do about each one is in the Operator Runbook.
Defaults are conservative and safe. The four most people change:
linux_vitals_ram_critical_threshold: 95 # RAM % that fails a host
linux_vitals_fail_on_severity: info # severity that fails a host
linux_vitals_heal_enabled: false # opt in to service restarts
linux_vitals_certs_enabled: false # opt in to TLS checks
Set them in group_vars, inventory, or with -e.
Variable Reference documents every variable with defaults and descriptions — examples/group_vars/all.yml.example is a copy-paste starting point with every one of them commented out.
Put secrets in a .env next to your inventory (see .env.example):
SLACK_WEBHOOK_URL="https://hooks.slack.com/services/your/team/webhook"
GENERIC_WEBHOOK_URL="https://example.com/health-events"
EMAIL_SMTP_HOST="smtp.example.com"
EMAIL_SMTP_USERNAME="smtp-user"
EMAIL_SMTP_PASSWORD="smtp-password"
Email also needs enabling and addressing:
linux_vitals_email_enabled: true
linux_vitals_email_to: ["[email protected]"]
Resolution order is the same for every channel: an explicit
inventory/group_vars/extra-vars value wins, otherwise the matching .env
value, otherwise the channel is skipped. More than one can be active at once,
and credentials never reach a managed host or a report.
linux_vitals_heal_enabled: true
Off by default — nothing on a managed host changes until you set this. With it
on, LinuxVitals attempts exactly one restart per service that is both
enabled at boot and in a failed state, then re-checks the required
services so the dashboard reflects the post-restart result. A restart is only
reported Fixed once the unit has settled and stayed up through a short
stability window; one that could not be verified is reported as unknown,
not as fixed.
There is no allowlist: it restarts whatever is enabled and failed. Enable it per group rather than globally, and see Enabling self-healing safely for the one command that shows what it would restart before you switch it on.
linux_vitals_certs_enabled: true
linux_vitals_cert_warning_days: 30
linux_vitals_cert_critical_days: 7
# Optional. Connections are made *from each managed host*, so `localhost`
# means the certificate that host itself serves. Empty by default — enabling
# the role connects to nothing until you name a target.
linux_vitals_cert_endpoints:
- host: localhost
port: 443
server_name: www.example.com
Reports expiry, weak signature algorithms, self-signed served certificates, obsolete negotiated TLS versions, and certificates being served that match nothing on disk — usually a service that was never reloaded after renewal.
Certificates are parsed with openssl and the handshake uses the Python
standard library, so nothing new is installed on managed hosts. The system
CA trust store is scanned but only reported when a certificate has already
expired, so hundreds of root certificates that are not yours do not bury the
findings that are.
Read the threat model before enabling endpoint checks. This is the only role that opens outbound connections, and reading
/etc/letsencrypt/liveneedsbecome.
Run the same maintenance id before and after a change window to get an automatic comparison:
MAINT_ID="$(date +%Y-%m-%d)-patch-window"
ansible-playbook -i inventory.ini sameeralam3127.linux_vitals.baseline \
-e linux_vitals_maintenance_id="$MAINT_ID"
# ... do your maintenance ...
ansible-playbook -i inventory.ini sameeralam3127.linux_vitals.postcheck \
-e linux_vitals_maintenance_id="$MAINT_ID"
The postcheck dashboard gains Regressed / Improved / New filters and a per-host before/after panel. Hosts with no matching baseline are marked New rather than failing the run. Step-by-step: Running a maintenance window.
Splitting a large fleet across several runs? Every shard keeps the window's
maintenance id and sets its own linux_vitals_snapshot_shard, so a shard id
reused for a different set of hosts is refused rather than silently merged
into the comparison: Sharding a large fleet.
Every task is tagged, so you can run a slice instead of the whole scan:
| Tag | Runs |
|---|---|
discovery | Facts, services, memory, logs, kernel, boot, security |
kernel | Kernel and reboot-required checks |
security | SELinux, AppArmor, failed logins |
boot | Boot partition and rescue image |
self_healing | Restart attempts (only acts if enabled) |
certs | TLS certificate checks (only acts if enabled) |
reporting, notifications | Rebuild reports and send summaries |
ansible-playbook -i inventory.ini playbooks/healthcheck.yml --tags kernel,reporting
Include
reportingwith any focused tag, or the run produces no output file.
Four composable roles sharing one linux_vitals_* namespace — run them
together via playbooks/healthcheck.yml, or individually in your own playbook.
Solid red roles always run; dashed ones are opt-in and off by default.
flowchart TB
subgraph FLEET["Managed hosts · SSH only, nothing installed"]
direction LR
DEB["<b>Debian family</b><br/>Ubuntu · Debian"]
RH["<b>Red Hat family</b><br/>RHEL · Rocky<br/>Alma · Fedora<br/>Amazon Linux"]
SUSE["<b>SUSE family</b><br/>openSUSE · SLES"]
DEB ~~~ RH ~~~ SUSE
end
subgraph ROLES["Ansible control node · roles run in order"]
direction LR
SCAN["<b>vitals_scan</b><br/>read-only<br/>checks"]
HEAL["<b>vitals_heal</b><br/>restarts failed units"]
CERTS["<b>vitals_certs</b><br/>TLS certificates"]
REPORT["<b>vitals_report</b><br/>render · notify<br/>compare · gate"]
SCAN --> HEAL --> CERTS --> REPORT
end
subgraph OUT["What you get"]
direction LR
HTML["<b>Dashboard</b><br/>one HTML file"]
JSON["<b>JSON report</b><br/>schema 2.1"]
SNAP["<b>Snapshots</b><br/>before → after"]
MSG["<b>Notifications</b><br/>Slack · email<br/>webhook"]
HTML ~~~ JSON ~~~ SNAP ~~~ MSG
end
FLEET -->|"over SSH"| ROLES
ROLES --> OUT
classDef core fill:#ee0000,stroke:#a60000,color:#ffffff
classDef optin fill:#ffffff,stroke:#ee0000,stroke-width:2px,stroke-dasharray:6 4,color:#151515
classDef item fill:#f2f2f2,stroke:#8a8d90,color:#151515
class SCAN,REPORT core
class HEAL,CERTS optin
class DEB,RH,SUSE,HTML,JSON,SNAP,MSG item
style ROLES fill:none,stroke:#ee0000,stroke-width:1px,color:#8a8d90
style FLEET fill:none,stroke:#8a8d90,stroke-dasharray:4 4,color:#8a8d90
style OUT fill:none,stroke:#8a8d90,stroke-dasharray:4 4,color:#8a8d90
Why the roles share one variable namespace, and why paths resolve from
inventory_dir: Architecture.
reports/linux_vitals_report.json is schema 2.1. Findings are objects
({id, message, severity, status}, plus a reason when status is
unknown), not strings, and each carries a stable id you can join on —
message wording may change between releases, ids will not.
| Control node | Python 3.10+, ansible-core 2.16+, and the community.general collection (use 9.x–11.x with ansible-core 2.16; see installation) |
| Managed hosts | Linux with systemd. Nothing installed — no agent, no Python packages beyond what Ansible itself needs |
| Access | SSH from the control node, plus become for the checks that read privileged state |
become is never set by the collection — it is entirely your decision, made in
inventory. The scan needs to read privileged state, not write, so a narrow
sudoers rule is enough for everything except self-healing:
what become actually needs.
Getting started
Using it day to day
Reference
Security
become, self-healing limits, credentials, what lands in a reportProject
Clone, then symlink the repo so FQCN roles resolve (already wired into
ansible.cfg, and gitignored):
mkdir -p .dev-collections/ansible_collections/sameeralam3127
ln -s "$(pwd)" .dev-collections/ansible_collections/sameeralam3127/linux_vitals
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements-dev.txt
ansible-galaxy collection install -r requirements.yml
Checks, matching what CI runs on every push and pull request (ci.yml):
pre-commit install
pre-commit run --all-files
ANSIBLE_LOCAL_TEMP=.ansible/tmp ANSIBLE_REMOTE_TEMP=.ansible/tmp \
ansible-lint roles/ playbooks/ molecule/ demo/
ansible-playbook playbooks/healthcheck.yml --syntax-check
pytest -q
CI additionally runs the per-distro Molecule scenarios (Ubuntu, Debian, Rocky, Fedora, Amazon Linux 2023, openSUSE) — see Testing to run those locally.
Setup, test suite, and the Galaxy publishing process: CONTRIBUTING.md. Found a security issue? Please report it privately — SECURITY.md.
MIT — see LICENSE.