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
10 changes: 10 additions & 0 deletions ansible/playbooks/deploy-monitoring.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
- name: Deploy monitoring stack
hosts: webservers
become: true

roles:
- role: monitoring
tags:
- monitoring
- monitoring_deploy
12 changes: 12 additions & 0 deletions ansible/roles/monitoring/defaults/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
monitoring_project_dir: /opt/monitoring
monitoring_loki_image: grafana/loki:3.0.0
monitoring_promtail_image: grafana/promtail:3.0.0
monitoring_grafana_image: grafana/grafana:12.3.1
monitoring_loki_port: 3100
monitoring_promtail_port: 9080
monitoring_grafana_port: 3000
monitoring_retention_period: 168h
monitoring_grafana_admin_user: admin
monitoring_grafana_admin_password: change-me-in-vault
monitoring_compose_project_name: devops-monitoring
4 changes: 4 additions & 0 deletions ansible/roles/monitoring/meta/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
# Docker Engine and Compose plugin are required for monitoring stack deployment.
dependencies:
- role: docker
77 changes: 77 additions & 0 deletions ansible/roles/monitoring/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
---
- name: Deploy Loki monitoring stack
tags:
- monitoring_deploy
block:
- name: Create monitoring directory tree
ansible.builtin.file:
path: "{{ item }}"
state: directory
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"

- name: Render Loki configuration
ansible.builtin.template:
src: loki-config.yml.j2
dest: "{{ monitoring_project_dir }}/loki/config.yml"
mode: "0644"

- name: Render Promtail configuration
ansible.builtin.template:
src: promtail-config.yml.j2
dest: "{{ monitoring_project_dir }}/promtail/config.yml"
mode: "0644"

- name: Render Grafana datasource provisioning
ansible.builtin.template:
src: grafana-datasource.yml.j2
dest: "{{ monitoring_project_dir }}/grafana/provisioning/datasources/loki.yml"
mode: "0644"

- name: Render monitoring docker-compose file
ansible.builtin.template:
src: docker-compose.yml.j2
dest: "{{ monitoring_project_dir }}/docker-compose.yml"
mode: "0644"

- name: Deploy monitoring stack with Docker Compose v2
community.docker.docker_compose_v2:
project_src: "{{ monitoring_project_dir }}"
files:
- docker-compose.yml
pull: always
remove_orphans: true
recreate: auto
state: present
register: monitoring_compose_result

- name: Wait for Loki readiness endpoint
ansible.builtin.uri:
url: "http://127.0.0.1:{{ monitoring_loki_port }}/ready"
status_code: 200
timeout: 10
register: monitoring_loki_ready
retries: 10
delay: 5
until: monitoring_loki_ready.status == 200

- name: Wait for Grafana health endpoint
ansible.builtin.uri:
url: "http://127.0.0.1:{{ monitoring_grafana_port }}/api/health"
status_code: 200
timeout: 10
register: monitoring_grafana_health
retries: 12
delay: 5
until: monitoring_grafana_health.status == 200

rescue:
- name: Show monitoring deployment diagnostics
ansible.builtin.debug:
var: monitoring_compose_result
67 changes: 67 additions & 0 deletions ansible/roles/monitoring/templates/docker-compose.yml.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
name: {{ monitoring_compose_project_name }}

services:
loki:
image: {{ monitoring_loki_image }}
container_name: devops-loki
command: -config.file=/etc/loki/config.yml
ports:
- "{{ monitoring_loki_port }}:{{ monitoring_loki_port }}"
volumes:
- ./loki/config.yml:/etc/loki/config.yml:ro
- loki-data:/loki
labels:
logging: "promtail"
app: "devops-loki"
healthcheck:
test: ["CMD-SHELL", "wget -qO- http://localhost:{{ monitoring_loki_port }}/ready >/dev/null 2>&1 || curl -fsS http://localhost:{{ monitoring_loki_port }}/ready >/dev/null 2>&1"]
interval: 15s
timeout: 5s
retries: 10
start_period: 20s

promtail:
image: {{ monitoring_promtail_image }}
container_name: devops-promtail
command: -config.file=/etc/promtail/config.yml
ports:
- "{{ monitoring_promtail_port }}:{{ monitoring_promtail_port }}"
volumes:
- ./promtail/config.yml:/etc/promtail/config.yml:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
depends_on:
loki:
condition: service_healthy
labels:
logging: "promtail"
app: "devops-promtail"

grafana:
image: {{ monitoring_grafana_image }}
container_name: devops-grafana
ports:
- "{{ monitoring_grafana_port }}:{{ monitoring_grafana_port }}"
environment:
GF_AUTH_ANONYMOUS_ENABLED: "false"
GF_SECURITY_ADMIN_USER: "{{ monitoring_grafana_admin_user }}"
GF_SECURITY_ADMIN_PASSWORD: "{{ monitoring_grafana_admin_password }}"
GF_USERS_ALLOW_SIGN_UP: "false"
volumes:
- grafana-data:/var/lib/grafana
- ./grafana/provisioning:/etc/grafana/provisioning:ro
depends_on:
loki:
condition: service_healthy
labels:
logging: "promtail"
app: "devops-grafana"
healthcheck:
test: ["CMD-SHELL", "wget -qO- http://localhost:{{ monitoring_grafana_port }}/api/health >/dev/null 2>&1 || curl -fsS http://localhost:{{ monitoring_grafana_port }}/api/health >/dev/null 2>&1"]
interval: 15s
timeout: 5s
retries: 10
start_period: 30s

volumes:
loki-data:
grafana-data:
10 changes: 10 additions & 0 deletions ansible/roles/monitoring/templates/grafana-datasource.yml.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
apiVersion: 1

datasources:
- name: Loki
uid: loki
type: loki
access: proxy
url: http://loki:{{ monitoring_loki_port }}
isDefault: true
editable: true
43 changes: 43 additions & 0 deletions ansible/roles/monitoring/templates/loki-config.yml.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
auth_enabled: false

server:
http_listen_port: {{ monitoring_loki_port }}
log_level: info

common:
path_prefix: /loki
replication_factor: 1
ring:
kvstore:
store: inmemory

schema_config:
configs:
- from: "2024-01-01"
store: tsdb
object_store: filesystem
schema: v13
index:
prefix: index_
period: 24h

storage_config:
tsdb_shipper:
active_index_directory: /loki/index
cache_location: /loki/index_cache
filesystem:
directory: /loki/chunks

compactor:
working_directory: /loki/compactor
compaction_interval: 10m
retention_enabled: true
delete_request_store: filesystem

limits_config:
retention_period: {{ monitoring_retention_period }}
reject_old_samples: true
reject_old_samples_max_age: {{ monitoring_retention_period }}

analytics:
reporting_enabled: false
29 changes: 29 additions & 0 deletions ansible/roles/monitoring/templates/promtail-config.yml.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
server:
http_listen_port: {{ monitoring_promtail_port }}
grpc_listen_port: 0

positions:
filename: /tmp/positions.yaml

clients:
- url: http://loki:{{ monitoring_loki_port }}/loki/api/v1/push

scrape_configs:
- job_name: docker
docker_sd_configs:
- host: unix:///var/run/docker.sock
refresh_interval: 5s
pipeline_stages:
- docker: {}
relabel_configs:
- source_labels: ["__meta_docker_container_name"]
regex: "/(.*)"
target_label: container
- source_labels: ["__meta_docker_container_label_app"]
target_label: app
- source_labels: ["__meta_docker_container_label_com_docker_compose_service"]
target_label: service
- source_labels: ["__meta_docker_container_log_stream"]
target_label: stream
- target_label: job
replacement: docker
20 changes: 19 additions & 1 deletion app_python/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,25 @@ Configuration is done via environment variables:

All configuration is read in `app.py` at startup, so restart the application after changing environment variables.

## Structured Logging (Lab 7)

The service writes JSON logs to `stdout` for centralized log collection (Loki/Promtail).

Example:

```json
{"timestamp":"2026-03-12T20:45:10.123456+00:00","level":"INFO","logger":"devops-info-service","message":"HTTP request handled","method":"GET","path":"/health","status_code":200,"client_ip":"127.0.0.1","duration_ms":1.11}
```

Each request log includes:

- `method`
- `path`
- `status_code`
- `client_ip`
- `duration_ms`
- `user_agent`

## Docker

How to use the containerized application (patterns):
Expand All @@ -66,4 +85,3 @@ How to use the containerized application (patterns):
Notes:
- The container exposes port `5002` by default (see `app.py`).
- The image runs as a non-root user for improved security.

Loading
Loading