Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions Vagrantfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,15 @@ Vagrant.configure("2") do |config|
# host_ip "0.0.0.0" lets WSL2 reach the forwarded ports through the host.
config.vm.network "forwarded_port", guest: 22, host: 2222, host_ip: "0.0.0.0", id: "ssh", auto_correct: true
config.vm.network "forwarded_port", guest: 8000, host: 8000, host_ip: "0.0.0.0", id: "app", auto_correct: true
config.vm.network "forwarded_port", guest: 3000, host: 3000, host_ip: "0.0.0.0", id: "grafana", auto_correct: true
config.vm.network "forwarded_port", guest: 3100, host: 3100, host_ip: "0.0.0.0", id: "loki", auto_correct: true
config.vm.network "forwarded_port", guest: 9080, host: 9080, host_ip: "0.0.0.0", id: "promtail", auto_correct: true

config.ssh.insert_key = true

config.vm.provider "virtualbox" do |vb|
vb.name = "lab05-ansible"
vb.memory = 2048
vb.name = "lab07-monitoring"
vb.memory = 3072
vb.cpus = 2
end
end
10 changes: 10 additions & 0 deletions ansible/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,13 @@ Workflow file: `.github/workflows/ansible-deploy.yml`.

For Vagrant/VirtualBox setups behind NAT, prefer a **self-hosted Linux runner** on the same machine
where you run Ansible (for example, WSL2 Ubuntu). This avoids inbound SSH exposure and stays free.

## Lab07 monitoring stack

```bash
# Deploy Loki + Promtail + Grafana + app stack on the target VM
ansible-playbook -i inventory/hosts.ini playbooks/deploy-monitoring.yml
```

The monitoring role builds the Python app locally on the target VM, so you do not need to push a new
application image to Docker Hub for Lab07.
1 change: 0 additions & 1 deletion ansible/group_vars/webservers.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,3 @@ app_internal_port: 8000

# Extra environment variables for the container (optional)
app_env: {}
dockerhub_username: dorley174
9 changes: 9 additions & 0 deletions ansible/playbooks/deploy-monitoring.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
- name: Deploy monitoring stack
hosts: webservers
become: true

roles:
- role: monitoring
tags:
- monitoring
48 changes: 48 additions & 0 deletions ansible/roles/monitoring/defaults/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
---
monitoring_project_dir: /opt/monitoring
monitoring_compose_project_name: devops-monitoring

monitoring_loki_version: "3.0.0"
monitoring_promtail_version: "3.0.0"
monitoring_grafana_version: "12.3.1"

monitoring_loki_port: 3100
monitoring_promtail_port: 9080
monitoring_grafana_port: 3000
monitoring_app_port: 8000
monitoring_app_internal_port: 8000

monitoring_loki_retention_period: "168h"
monitoring_grafana_admin_user: admin
monitoring_grafana_admin_password: ChangeMe_Lab07!
monitoring_grafana_datasource_uid: loki
monitoring_grafana_datasource_name: Loki

monitoring_app_service_name: app-python
monitoring_app_container_name: devops-python
monitoring_app_label: devops-python
monitoring_app_image: devops-info-service:lab07
monitoring_app_source_dir: "{{ playbook_dir }}/../../app_python"
monitoring_app_source_files:
- .dockerignore
- Dockerfile
- requirements.txt
- app.py
monitoring_app_env:
HOST: "0.0.0.0"
PORT: "{{ monitoring_app_internal_port | string }}"
DEBUG: "false"

monitoring_resource_limits:
loki:
limits: { cpus: '1.0', memory: 1G }
reservations: { cpus: '0.25', memory: 256M }
promtail:
limits: { cpus: '0.75', memory: 512M }
reservations: { cpus: '0.10', memory: 128M }
grafana:
limits: { cpus: '1.0', memory: 1G }
reservations: { cpus: '0.25', memory: 256M }
app_python:
limits: { cpus: '0.75', memory: 512M }
reservations: { cpus: '0.10', memory: 128M }
3 changes: 3 additions & 0 deletions ansible/roles/monitoring/meta/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
dependencies:
- role: docker
117 changes: 117 additions & 0 deletions ansible/roles/monitoring/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
---
- name: Deploy monitoring stack
block:
- name: Create monitoring directory structure
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: root
group: root
mode: "0755"
loop:
- "{{ monitoring_project_dir }}"
- "{{ monitoring_project_dir }}/loki"
- "{{ monitoring_project_dir }}/promtail"
- "{{ monitoring_project_dir }}/grafana"
- "{{ monitoring_project_dir }}/grafana/provisioning"
- "{{ monitoring_project_dir }}/grafana/provisioning/datasources"
- "{{ monitoring_project_dir }}/grafana/provisioning/dashboards"
- "{{ monitoring_project_dir }}/grafana/dashboards"
- "{{ monitoring_project_dir }}/app-python"

- name: Copy application source files for local image build
ansible.builtin.copy:
src: "{{ monitoring_app_source_dir }}/{{ item }}"
dest: "{{ monitoring_project_dir }}/app-python/{{ item }}"
owner: root
group: root
mode: "0644"
loop: "{{ monitoring_app_source_files }}"

- name: Template monitoring environment file
ansible.builtin.template:
src: env.j2
dest: "{{ monitoring_project_dir }}/.env"
owner: root
group: root
mode: "0600"

- name: Template monitoring stack files
ansible.builtin.template:
src: "{{ item.src }}"
dest: "{{ monitoring_project_dir }}/{{ item.dest }}"
owner: root
group: root
mode: "0644"
loop:
- { src: 'docker-compose.yml.j2', dest: 'docker-compose.yml' }
- { src: 'loki-config.yml.j2', dest: 'loki/config.yml' }
- { src: 'promtail-config.yml.j2', dest: 'promtail/config.yml' }
- { src: 'grafana-datasource.yml.j2', dest: 'grafana/provisioning/datasources/loki.yml' }
- { src: 'grafana-dashboard-provider.yml.j2', dest: 'grafana/provisioning/dashboards/dashboard-provider.yml' }
- { src: 'grafana-dashboard.json.j2', dest: 'grafana/dashboards/lab07-logging.json' }

- name: Deploy monitoring stack with Docker Compose v2
community.docker.docker_compose_v2:
project_src: "{{ monitoring_project_dir }}"
project_name: "{{ monitoring_compose_project_name }}"
state: present
build: always
recreate: auto
register: monitoring_compose

- name: Wait for Loki to become ready
ansible.builtin.uri:
url: "http://127.0.0.1:{{ monitoring_loki_port }}/ready"
method: GET
status_code: 200
register: loki_ready
retries: 20
delay: 3
until: loki_ready.status == 200

- name: Wait for Grafana API health endpoint
ansible.builtin.uri:
url: "http://127.0.0.1:{{ monitoring_grafana_port }}/api/health"
method: GET
user: "{{ monitoring_grafana_admin_user }}"
password: "{{ monitoring_grafana_admin_password }}"
force_basic_auth: true
status_code: 200
register: grafana_health
retries: 20
delay: 3
until: grafana_health.status == 200

- name: Wait for monitored application health endpoint
ansible.builtin.uri:
url: "http://127.0.0.1:{{ monitoring_app_port }}/health"
method: GET
status_code: 200
register: monitoring_app_health
retries: 20
delay: 3
until: monitoring_app_health.status == 200

- name: Verify Loki data source was provisioned
ansible.builtin.uri:
url: "http://127.0.0.1:{{ monitoring_grafana_port }}/api/datasources/uid/{{ monitoring_grafana_datasource_uid }}"
method: GET
user: "{{ monitoring_grafana_admin_user }}"
password: "{{ monitoring_grafana_admin_password }}"
force_basic_auth: true
status_code: 200

rescue:
- name: Monitoring deployment failure hint
ansible.builtin.debug:
msg: |
Monitoring stack deployment failed.
Helpful follow-up commands on the target host:
cd {{ monitoring_project_dir }}
docker compose ps
docker compose logs --no-color --tail=200

tags:
- monitoring
- monitoring_deploy
151 changes: 151 additions & 0 deletions ansible/roles/monitoring/templates/docker-compose.yml.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
name: {{ monitoring_compose_project_name }}

services:
loki:
image: grafana/loki:{{ monitoring_loki_version }}
container_name: devops-loki
command:
- -config.file=/etc/loki/config.yml
ports:
- "{{ monitoring_loki_port }}:3100"
volumes:
- ./loki/config.yml:/etc/loki/config.yml:ro
- loki-data:/loki
networks:
- logging
labels:
logging: "promtail"
app: "devops-loki"
restart: unless-stopped
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:3100/ready"]
interval: 15s
timeout: 5s
retries: 10
start_period: 20s
deploy:
resources:
limits:
cpus: "{{ monitoring_resource_limits.loki.limits.cpus }}"
memory: {{ monitoring_resource_limits.loki.limits.memory }}
reservations:
cpus: "{{ monitoring_resource_limits.loki.reservations.cpus }}"
memory: {{ monitoring_resource_limits.loki.reservations.memory }}

promtail:
image: grafana/promtail:{{ monitoring_promtail_version }}
container_name: devops-promtail
command:
- -config.file=/etc/promtail/config.yml
ports:
- "{{ monitoring_promtail_port }}:9080"
volumes:
- ./promtail/config.yml:/etc/promtail/config.yml:ro
- promtail-data:/var/lib/promtail
- /var/lib/docker/containers:/var/lib/docker/containers:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- logging
labels:
logging: "promtail"
app: "devops-promtail"
restart: unless-stopped
depends_on:
loki:
condition: service_healthy
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:9080/ready"]
interval: 15s
timeout: 5s
retries: 10
start_period: 20s
deploy:
resources:
limits:
cpus: "{{ monitoring_resource_limits.promtail.limits.cpus }}"
memory: {{ monitoring_resource_limits.promtail.limits.memory }}
reservations:
cpus: "{{ monitoring_resource_limits.promtail.reservations.cpus }}"
memory: {{ monitoring_resource_limits.promtail.reservations.memory }}

grafana:
image: grafana/grafana:{{ monitoring_grafana_version }}
container_name: devops-grafana
env_file:
- .env
environment:
GF_SECURITY_ADMIN_USER: "${GRAFANA_ADMIN_USER:-{{ monitoring_grafana_admin_user }}}"
GF_SECURITY_ADMIN_PASSWORD: "${GRAFANA_ADMIN_PASSWORD}"
GF_AUTH_ANONYMOUS_ENABLED: "false"
GF_SECURITY_ALLOW_EMBEDDING: "false"
ports:
- "{{ monitoring_grafana_port }}:3000"
volumes:
- grafana-data:/var/lib/grafana
- ./grafana/provisioning:/etc/grafana/provisioning:ro
- ./grafana/dashboards:/etc/grafana/dashboards:ro
networks:
- logging
labels:
logging: "promtail"
app: "devops-grafana"
restart: unless-stopped
depends_on:
loki:
condition: service_healthy
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:3000/api/health"]
interval: 15s
timeout: 5s
retries: 10
start_period: 30s
deploy:
resources:
limits:
cpus: "{{ monitoring_resource_limits.grafana.limits.cpus }}"
memory: {{ monitoring_resource_limits.grafana.limits.memory }}
reservations:
cpus: "{{ monitoring_resource_limits.grafana.reservations.cpus }}"
memory: {{ monitoring_resource_limits.grafana.reservations.memory }}

{{ monitoring_app_service_name }}:
build:
context: ./app-python
dockerfile: Dockerfile
image: {{ monitoring_app_image }}
container_name: {{ monitoring_app_container_name }}
environment:
{% for key, value in monitoring_app_env.items() %}
{{ key }}: {{ value | quote }}
{% endfor %}
ports:
- "{{ monitoring_app_port }}:{{ monitoring_app_internal_port }}"
networks:
- logging
labels:
logging: "promtail"
app: "{{ monitoring_app_label }}"
restart: unless-stopped
healthcheck:
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:{{ monitoring_app_internal_port }}/health')"]
interval: 15s
timeout: 5s
retries: 10
start_period: 20s
deploy:
resources:
limits:
cpus: "{{ monitoring_resource_limits.app_python.limits.cpus }}"
memory: {{ monitoring_resource_limits.app_python.limits.memory }}
reservations:
cpus: "{{ monitoring_resource_limits.app_python.reservations.cpus }}"
memory: {{ monitoring_resource_limits.app_python.reservations.memory }}

networks:
logging:
driver: bridge

volumes:
loki-data:
grafana-data:
promtail-data:
6 changes: 6 additions & 0 deletions ansible/roles/monitoring/templates/env.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
GRAFANA_ADMIN_USER={{ monitoring_grafana_admin_user }}
GRAFANA_ADMIN_PASSWORD={{ monitoring_grafana_admin_password }}
GRAFANA_PORT={{ monitoring_grafana_port }}
LOKI_PORT={{ monitoring_loki_port }}
PROMTAIL_PORT={{ monitoring_promtail_port }}
APP_PORT={{ monitoring_app_port }}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
apiVersion: 1

providers:
- name: lab07-logging
orgId: 1
folder: Lab07 Logging
type: file
disableDeletion: false
allowUiUpdates: true
updateIntervalSeconds: 30
options:
path: /etc/grafana/dashboards
Loading
Loading