Files

205 lines
7.2 KiB
Markdown
Raw Permalink Normal View History

2026-09-05 08:11:34 +03:30
# Deploying Two Observability Stacks with Ansible
This project deploys two independent stacks on a single Ubuntu/Debian host using Docker Compose:
1. **ELK + Grafana:** Filebeat → Logstash → Elasticsearch → Kibana/Grafana
2. **Grafana Observability:** Prometheus + Alertmanager + Loki + Tempo + Grafana, along with Alloy and Node Exporter for data collection
> The default settings are suitable for Lab, Demo, and training purposes. For Production, be sure to read the "Production Notes" section.
## Operational Diagrams
- `diagrams/01-elk-grafana-operational.png` and its SVG vector version
- `diagrams/02-grafana-lgtm-operational.png` and its SVG vector version
Path colors in the second diagram:
- Green: Metrics
- Blue: Logs
- Purple: Traces
- Dashed orange: Query/Response
- Red: Alert delivery
## Project Structure
```text
inventory/hosts.yml SSH targets
group_vars/all.yml Versions, Grafana password, and variables
playbooks/elk-grafana.yml Installs ELK + Grafana
playbooks/grafana-stack.yml Installs Grafana/Prometheus/Loki/Tempo
roles/docker/ Installs Docker Engine and Compose v2
roles/observability/ Templates, execution, and health checks
diagrams/ PNG and SVG diagrams
```
## Prerequisites
- Control node: Ansible Core 2.16 or newer
- Target: Ubuntu 22.04/24.04 or Debian 12, with SSH and sudo access
- Recommended minimum for the ELK Lab: 4 vCPUs, 8 GB RAM, and 50 GB disk
- Recommended minimum for the Grafana Stack Lab: 2 vCPUs, 4 GB RAM, and 30 GB disk
On the control node:
```bash
cd observability-ansible
ansible-galaxy collection install -r requirements.yml
```
## 1. Configure the Inventory
Edit the `inventory/hosts.yml` file:
```yaml
all:
children:
observability:
hosts:
obs01:
ansible_host: 10.10.20.15
ansible_user: ubuntu
ansible_ssh_private_key_file: ~/.ssh/id_ed25519
```
Test the connection:
```bash
ansible observability -m ping
```
## 2. Configure Variables and Secrets
Versions and core settings live in `group_vars/all.yml`. Be sure to change the default Grafana password. To store the password securely:
```bash
ansible-vault encrypt_string 'A-strong-password' --name grafana_admin_password
```
Replace the `grafana_admin_password` value with the Vault output, and run the playbook with `--ask-vault-pass`.
If your application exposes a `/metrics` endpoint, add it here:
```yaml
prometheus_extra_scrape_jobs:
- job_name: checkout-api
targets:
- 10.20.0.15:8080
- 10.20.0.16:8080
```
To send alerts to a webhook, also set the following value:
```yaml
alertmanager_webhook_url: "https://example.internal/alerts"
```
## 3. Install ELK + Grafana
```bash
ansible-playbook playbooks/elk-grafana.yml
```
Endpoints:
| Service | URL/Port | Purpose |
| --- | --- | --- |
| Elasticsearch | `http://SERVER:9200` | Index and Search API |
| Logstash Beats input | `SERVER:5044` | Receives Filebeat |
| Logstash JSON/TCP | `SERVER:5000` | Testing and direct JSON Lines ingest |
| Kibana | `http://SERVER:5601` | Discover and Elastic analysis |
| Grafana | `http://SERVER:3000` | Dashboards on top of Elasticsearch |
Filebeat in this project reads the Docker container logs on the same host and sends them to Logstash. Logstash parses the JSON and writes it into the daily `logs-YYYY.MM.dd` index.
Test ingest:
```bash
printf '%s\n' '{"message":"checkout completed","level":"info","service":"checkout"}' \
| nc SERVER 5000
curl 'http://SERVER:9200/logs-*/_search?pretty'
```
## 4. Install Grafana + Prometheus + Alertmanager + Loki + Tempo
```bash
ansible-playbook playbooks/grafana-stack.yml
```
Endpoints:
| Service | URL/Port | Purpose |
| --- | --- | --- |
| Grafana | `http://SERVER:3000` | Explore, Dashboards, and correlation |
| Prometheus | `http://SERVER:9090` | Metrics, PromQL, and alert rules |
| Alertmanager | `http://SERVER:9093` | Grouping, deduplication, and routing of alerts |
| Loki | `http://SERVER:3100` | Log ingestion/query and LogQL |
| Tempo | `http://SERVER:3200` | Trace query and TraceQL |
| Alloy UI | `http://SERVER:12345` | Collector pipeline status |
| OTLP/gRPC | `SERVER:4317` | Receives traces from the application |
| OTLP/HTTP | `SERVER:4318` | Receives traces from the application |
| Node Exporter | `http://SERVER:9100/metrics` | Host-level metrics |
The Prometheus, Loki, Tempo, and Alertmanager data sources are automatically provisioned in Grafana. Grafana uses exemplars for Metrics → Trace, `trace_id` for Logs → Trace, and Prometheus for the Tempo service graph.
Example OpenTelemetry environment variables for your application:
```bash
export OTEL_SERVICE_NAME=checkout-api
export OTEL_EXPORTER_OTLP_ENDPOINT=http://SERVER:4318
export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
```
Alloy pushes Docker container logs to Loki and forwards OTLP traces to Tempo. Prometheus scrapes internal services and Node Exporter. The sample rules `HostExporterDown` and `HostHighCPU` are also installed.
## Day-to-Day Operations
On the target server:
```bash
cd /opt/observability/elk-grafana
docker compose ps
docker compose logs -f --tail=100
cd /opt/observability/grafana-stack
docker compose ps
docker compose logs -f --tail=100
```
Re-running the playbook is idempotent; changing a template or version tag triggers a recreate/restart of the relevant stack. Data is kept in Docker named volumes.
## Removing a Stack
To stop and remove the containers without deleting the data:
```bash
cd /opt/observability/STACK_NAME
docker compose down
```
The `-v` flag deletes all volumes and monitoring data; only use it when you have a backup and specifically intend to do this.
## Production Notes
The default playbook intentionally builds a single-node, easy-to-understand deployment. For Production:
- Enable TLS, authentication, and RBAC on all endpoints; don't expose storage ports directly to the internet.
- Keep secrets in Ansible Vault or a Secret Manager.
- Run Elasticsearch with at least three nodes, a proper snapshot repository, and ILM.
- Use S3-compatible object storage and replication for Loki and Tempo.
- Design Prometheus with remote-write/Thanos/Mimir, retention, and adequate disk capacity.
- Run Alertmanager in HA and test your notification routes.
- Add a reverse proxy, DNS, certificates, firewall rules, and backup/restore testing.
- Upgrade versions in staging first; they are intentionally pinned in `group_vars/all.yml`.
## Official Resources
- [Elastic Docker deployment](https://www.elastic.co/docs/deploy-manage/deploy/self-managed/install-elasticsearch-docker-compose)
- [Grafana provisioning](https://grafana.com/docs/grafana/latest/administration/provisioning/)
- [Prometheus configuration](https://prometheus.io/docs/prometheus/latest/configuration/configuration/)
- [Alertmanager configuration](https://prometheus.io/docs/alerting/latest/configuration/)
- [Grafana Loki configuration](https://grafana.com/docs/loki/latest/configure/)
- [Grafana Tempo configuration](https://grafana.com/docs/tempo/latest/configuration/)
- [Grafana Alloy](https://grafana.com/docs/alloy/latest/)
- [Ansible Docker Compose v2 module](https://docs.ansible.com/ansible/latest/collections/community/docker/docker_compose_v2_module.html)