added elk stack and updated the docs accordingly
This commit is contained in:
19
README.md
19
README.md
@@ -33,6 +33,14 @@ This is a real, running homelab — not a demo. It's deployed on a home Ubuntu s
|
|||||||
| **Semaphore** | `docker-compose/semaphore/` | Self-hosted Ansible UI (alternative to AWX) — see [Semaphore Guide](docs/Semaphore.md) |
|
| **Semaphore** | `docker-compose/semaphore/` | Self-hosted Ansible UI (alternative to AWX) — see [Semaphore Guide](docs/Semaphore.md) |
|
||||||
| **Ansible playbooks** | `ansible/` | Shared update role (pull → recreate → health-check → prune) reused across every stack, run via Semaphore — see [Ansible Playbooks](docs/AnsiblePlaybooks.md) |
|
| **Ansible playbooks** | `ansible/` | Shared update role (pull → recreate → health-check → prune) reused across every stack, run via Semaphore — see [Ansible Playbooks](docs/AnsiblePlaybooks.md) |
|
||||||
|
|
||||||
|
## Learning labs (not always-on)
|
||||||
|
|
||||||
|
Not everything in this repo runs permanently. Some folders are kept purely as reference from one-off learning exercises for my DevOps cert coursework — the compose files work, but they're not part of the day-to-day stack above.
|
||||||
|
|
||||||
|
| Lab | Folder | Purpose |
|
||||||
|
|---|---|---|
|
||||||
|
| **Elastic Stack (ELK)** | `docker-compose/elk/` | Elasticsearch + Kibana, security-enabled, sized for this hardware — a modern-workflow (Elastic Agent/Fleet) alternative to an older Filebeat/Metricbeat-based course reference. Not run continuously since Prometheus/Grafana already cover monitoring here — see [Elastic Stack (ELK)](docs/ElasticStack.md) |
|
||||||
|
|
||||||
## Repository structure
|
## Repository structure
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -72,9 +80,12 @@ homelab/
|
|||||||
│ │ └── docker-compose.yaml
|
│ │ └── docker-compose.yaml
|
||||||
│ ├── nginx-proxy-manager/
|
│ ├── nginx-proxy-manager/
|
||||||
│ │ └── docker-compose.yaml
|
│ │ └── docker-compose.yaml
|
||||||
│ └── semaphore/
|
│ ├── semaphore/
|
||||||
|
│ │ ├── docker-compose.yml
|
||||||
|
│ │ └── README.md
|
||||||
|
│ └── elk/
|
||||||
│ ├── docker-compose.yml
|
│ ├── docker-compose.yml
|
||||||
│ └── README.md
|
│ └── .env.example
|
||||||
├── ansible/
|
├── ansible/
|
||||||
│ └── playbooks/
|
│ └── playbooks/
|
||||||
│ ├── roles/
|
│ ├── roles/
|
||||||
@@ -110,6 +121,7 @@ homelab/
|
|||||||
│ ├── Semaphore.md
|
│ ├── Semaphore.md
|
||||||
│ ├── DockScope.md
|
│ ├── DockScope.md
|
||||||
│ ├── AnsiblePlaybooks.md
|
│ ├── AnsiblePlaybooks.md
|
||||||
|
│ ├── ElasticStack.md
|
||||||
│ └── images/
|
│ └── images/
|
||||||
└── .gitignore
|
└── .gitignore
|
||||||
```
|
```
|
||||||
@@ -126,7 +138,7 @@ docker compose up -d
|
|||||||
|
|
||||||
## Updating a service
|
## Updating a service
|
||||||
|
|
||||||
Rather than pulling and recreating manually, every stack has a matching Ansible playbook that pulls the latest images, recreates containers, verifies they're healthy, and prunes unused images — run through Semaphore's web UI. See [Ansible Playbooks](docs/AnsiblePlaybooks.md) for details on how the shared role works.
|
Rather than pulling and recreating manually, every always-on stack has a matching Ansible playbook that pulls the latest images, recreates containers, verifies they're healthy, and prunes unused images — run through Semaphore's web UI. See [Ansible Playbooks](docs/AnsiblePlaybooks.md) for details on how the shared role works.
|
||||||
|
|
||||||
## Security notes
|
## Security notes
|
||||||
|
|
||||||
@@ -143,6 +155,7 @@ Rather than pulling and recreating manually, every stack has a matching Ansible
|
|||||||
- Free-tier cloud AI APIs enforce regional access restrictions automatically — worth checking a provider's terms before building around "free tier" as an assumption. See [AI Coding Agent](docs/AI-Agent.md).
|
- Free-tier cloud AI APIs enforce regional access restrictions automatically — worth checking a provider's terms before building around "free tier" as an assumption. See [AI Coding Agent](docs/AI-Agent.md).
|
||||||
- Wildcard TLS certs (`*.home`) are rejected by browsers on single-label local domains — certificates for local reverse proxies need explicit hostnames listed instead. See [Reverse Proxy](docs/Reverse-Proxy.md).
|
- Wildcard TLS certs (`*.home`) are rejected by browsers on single-label local domains — certificates for local reverse proxies need explicit hostnames listed instead. See [Reverse Proxy](docs/Reverse-Proxy.md).
|
||||||
- Grouping all Compose stacks under one `docker-compose/` folder (rather than flat at repo root) kept the structure readable once Ansible playbooks were added as a sibling — a flat repo mixing deployment configs and automation code got confusing fast.
|
- Grouping all Compose stacks under one `docker-compose/` folder (rather than flat at repo root) kept the structure readable once Ansible playbooks were added as a sibling — a flat repo mixing deployment configs and automation code got confusing fast.
|
||||||
|
- Newer Docker images can silently raise the minimum CPU generation required (base-OS changes, not anything in the compose file itself) — hit this running Elasticsearch 9.x on older hardware. See [Elastic Stack (ELK)](docs/ElasticStack.md).
|
||||||
|
|
||||||
## Roadmap
|
## Roadmap
|
||||||
|
|
||||||
|
|||||||
6
docker-compose/ELK/.env.example
Normal file
6
docker-compose/ELK/.env.example
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# Rename to .env before running docker compose up.
|
||||||
|
# Generate your own values rather than reusing these if you ever bring this back up.
|
||||||
|
|
||||||
|
ELASTIC_PASSWORD=sSBdnyE9WupLYHyZ
|
||||||
|
KIBANA_PASSWORD=49c1VjcqgfDGNRFA
|
||||||
|
KIBANA_ENCRYPTION_KEY=625fef0e09b89fc810269149b5584f17
|
||||||
68
docker-compose/ELK/docker-compose.yml
Normal file
68
docker-compose/ELK/docker-compose.yml
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
services:
|
||||||
|
elasticsearch:
|
||||||
|
image: elasticsearch:8.19.19
|
||||||
|
container_name: elasticsearch
|
||||||
|
environment:
|
||||||
|
- node.name=elasticsearch
|
||||||
|
- discovery.type=single-node
|
||||||
|
- ELASTIC_PASSWORD=${ELASTIC_PASSWORD}
|
||||||
|
- xpack.security.enabled=true
|
||||||
|
- xpack.security.http.ssl.enabled=false
|
||||||
|
- xpack.security.transport.ssl.enabled=false
|
||||||
|
- xpack.ml.enabled=false
|
||||||
|
- ES_JAVA_OPTS=-Xms1g -Xmx1g
|
||||||
|
mem_limit: 1500m
|
||||||
|
cpus: 1.5
|
||||||
|
volumes:
|
||||||
|
- es-data:/usr/share/elasticsearch/data
|
||||||
|
ports:
|
||||||
|
- "9200:9200"
|
||||||
|
restart: unless-stopped
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "curl -s -u elastic:${ELASTIC_PASSWORD} http://localhost:9200 | grep -q cluster_name"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 30
|
||||||
|
|
||||||
|
kibana-setup:
|
||||||
|
image: elasticsearch:8.19.19
|
||||||
|
container_name: kibana-setup
|
||||||
|
depends_on:
|
||||||
|
elasticsearch:
|
||||||
|
condition: service_healthy
|
||||||
|
restart: "no"
|
||||||
|
entrypoint: ["curl"]
|
||||||
|
command:
|
||||||
|
- "-s"
|
||||||
|
- "-u"
|
||||||
|
- "elastic:${ELASTIC_PASSWORD}"
|
||||||
|
- "-X"
|
||||||
|
- "POST"
|
||||||
|
- "http://elasticsearch:9200/_security/user/kibana_system/_password"
|
||||||
|
- "-H"
|
||||||
|
- "Content-Type: application/json"
|
||||||
|
- "-d"
|
||||||
|
- "{\"password\":\"${KIBANA_PASSWORD}\"}"
|
||||||
|
|
||||||
|
kibana:
|
||||||
|
image: kibana:8.19.19
|
||||||
|
container_name: kibana
|
||||||
|
depends_on:
|
||||||
|
kibana-setup:
|
||||||
|
condition: service_completed_successfully
|
||||||
|
environment:
|
||||||
|
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
|
||||||
|
- ELASTICSEARCH_USERNAME=kibana_system
|
||||||
|
- ELASTICSEARCH_PASSWORD=${KIBANA_PASSWORD}
|
||||||
|
- XPACK_ENCRYPTEDSAVEDOBJECTS_ENCRYPTIONKEY=${KIBANA_ENCRYPTION_KEY}
|
||||||
|
- XPACK_SECURITY_ENCRYPTIONKEY=${KIBANA_ENCRYPTION_KEY}
|
||||||
|
- XPACK_REPORTING_ENCRYPTIONKEY=${KIBANA_ENCRYPTION_KEY}
|
||||||
|
- NODE_OPTIONS=--max-old-space-size=1024
|
||||||
|
mem_limit: 1200m
|
||||||
|
cpus: 1
|
||||||
|
ports:
|
||||||
|
- "5601:5601"
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
es-data:
|
||||||
60
docs/ElasticStack.md
Normal file
60
docs/ElasticStack.md
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
# Elastic Stack (ELK)
|
||||||
|
|
||||||
|
A hands-on lab for the observability chapter of my DevOps cert — not a permanent part of this homelab's monitoring, since Prometheus + Grafana + cAdvisor + node-exporter already cover that role here.
|
||||||
|
|
||||||
|
## Why
|
||||||
|
|
||||||
|
My course's reference material for this chapter is a few years old — it walks through a basic ELK setup (Elasticsearch + Logstash + Kibana) with security disabled and metrics wired in by hand through separate Beats agents. That workflow has since been superseded: the current recommended approach centers on a single **Elastic Agent**, centrally managed through **Fleet** in Kibana, with pre-built **Integrations** replacing hand-configured Filebeat/Metricbeat setups. This lab was about running the current version of that workflow rather than the older one, purely to understand it for the cert — not to replace the monitoring stack already documented in [Monitoring Stack](Monitoring-Stack.md).
|
||||||
|
|
||||||
|
## What actually got deployed
|
||||||
|
|
||||||
|
Elasticsearch + Kibana, secured (not the "turn off X-Pack security" pattern from older tutorials), sized for this hardware. Fleet/Elastic Agent integrations were browsed (System, Docker) but not fully enrolled before the lab was torn down — see [Semaphore](Semaphore.md) and [Ansible Playbooks](AnsiblePlaybooks.md) for the pattern this homelab actually uses for ongoing service management; ELK wasn't kept running long enough to need either.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
elasticsearch:
|
||||||
|
image: elasticsearch:8.19.19
|
||||||
|
environment:
|
||||||
|
- discovery.type=single-node
|
||||||
|
- xpack.security.enabled=true
|
||||||
|
- xpack.security.http.ssl.enabled=false
|
||||||
|
- xpack.ml.enabled=false
|
||||||
|
- ES_JAVA_OPTS=-Xms1g -Xmx1g
|
||||||
|
mem_limit: 1500m
|
||||||
|
|
||||||
|
kibana:
|
||||||
|
image: kibana:8.19.19
|
||||||
|
environment:
|
||||||
|
- NODE_OPTIONS=--max-old-space-size=1024
|
||||||
|
mem_limit: 1200m
|
||||||
|
```
|
||||||
|
|
||||||
|
(Full compose file with the setup container that provisions the `kibana_system` password is in `docker-compose/elk/`.)
|
||||||
|
|
||||||
|
## Hardware roadblocks — the actually useful part of this lab
|
||||||
|
|
||||||
|
Two CPU-specific failures came up, both worth documenting since they're the kind of thing that doesn't show up until you try running current-generation software on older hardware:
|
||||||
|
|
||||||
|
**1. `Fatal glibc error: CPU does not support x86-64-v2`**
|
||||||
|
|
||||||
|
Elasticsearch 9.x's Docker image failed outright on this box's CPU (AMD Phenom II, 2009-era). This turned out to be a base-OS change on Elastic's side — their 9.x images switched base images in a way that assumes a CPU baseline (`x86-64-v2`: SSE4.2, CMPXCHG16B, POPCNT) that this CPU predates by about two years. Fix: pinned to the **8.19.x branch**, which still targets an older baseline and started fine. Still a current, actively patched release (supported through mid-2027) — not a step back to something outdated.
|
||||||
|
|
||||||
|
**2. `Failure running machine learning native code`**
|
||||||
|
|
||||||
|
Elasticsearch's ML module ships a native binary that also failed to run on this CPU. ML wasn't needed for this lab (it's for anomaly detection, unrelated to basic log/metric ingestion), so it was disabled outright with `xpack.ml.enabled=false` rather than chased further.
|
||||||
|
|
||||||
|
**3. Kibana `JavaScript heap out of memory` crash loop**
|
||||||
|
|
||||||
|
Not CPU-related this time — Kibana 8.19 initializes 172 plugins on startup, and Node.js's auto-sized heap (based on the container's memory limit) wasn't enough to get through that without crashing. Fixed by raising Kibana's container memory limit and explicitly setting `NODE_OPTIONS=--max-old-space-size=1024` rather than relying on Node's auto-detection.
|
||||||
|
|
||||||
|
## Why it's not staying up
|
||||||
|
|
||||||
|
Between Elasticsearch's heap and Kibana's heap, this pushed total memory usage close to the ceiling of what's actually free on this box alongside the rest of the stack (see [Architecture and Hardware](Architecture-and-Hardware.md)) — workable for a lab session, not something worth running 24/7 next to a monitoring stack that already does the job. The compose file stays in this repo as a reference for re-running the lab, not as an always-on service.
|
||||||
|
|
||||||
|
## Lessons learned
|
||||||
|
|
||||||
|
- "Modern ELK" today means Elastic Agent + Fleet integrations replacing individually-configured Beats — worth knowing conceptually even without a permanently running deployment to point at.
|
||||||
|
- Docker image base-OS changes can silently raise the minimum CPU generation required, independent of anything in your own compose file — this will likely resurface on other images down the line on this hardware, not just Elasticsearch.
|
||||||
|
- Default resource auto-detection (Node.js heap sizing, in this case) doesn't always get it right inside a memory-constrained container — explicit sizing beats trusting the default when things are already tight.
|
||||||
|
|
||||||
|
[← Back to Home](Home.md)
|
||||||
@@ -16,6 +16,7 @@ Documentation for a self-hosted homelab running entirely on Docker Compose. This
|
|||||||
- [Semaphore](Semaphore.md)
|
- [Semaphore](Semaphore.md)
|
||||||
- [DockScope](DockScope.md)
|
- [DockScope](DockScope.md)
|
||||||
- [Ansible Playbooks](AnsiblePlaybooks.md)
|
- [Ansible Playbooks](AnsiblePlaybooks.md)
|
||||||
|
- [Elastic Stack (ELK) — Lab](ElasticStack.md)
|
||||||
|
|
||||||
## At a glance
|
## At a glance
|
||||||
|
|
||||||
@@ -31,3 +32,5 @@ Documentation for a self-hosted homelab running entirely on Docker Compose. This
|
|||||||
| **AI Agent** | OpenHands + Ollama |
|
| **AI Agent** | OpenHands + Ollama |
|
||||||
|
|
||||||
This is a running system, not a demo — screenshots throughout this wiki are from the live dashboards.
|
This is a running system, not a demo — screenshots throughout this wiki are from the live dashboards.
|
||||||
|
|
||||||
|
Not everything in this repo runs permanently — see [Elastic Stack (ELK) — Lab](ElasticStack.md) for a compose stack kept as a reference from a one-off learning exercise, not part of the always-on services above.
|
||||||
|
|||||||
Reference in New Issue
Block a user