Logging inside Container
-
Problem? I have a small go application, which dumps logs on console, But when this application runs inside container on AWS,
How logs can be seen?
Solution? Loki + Promtail + Grafana
Solution = Loki + Promtail + Grafana
-
For small applications, We can deploy this pipeline
[Go Application] → (stdout/stderr) → [Docker Logs] → [Promtail] → [Loki] ← [Grafana]
-
Go Application: writes logs to stdout/stderr as it currently does
Docker/Container Runtime: Captures these stdout/stderr streams
Promtail:
Runs as a sidecar container or daemonset
Scrapes the container logs from the Docker log files
Adds labels to identify the source (container name, pod name, etc.)
Sends logs to Loki
Loki: Receives, stores logs from Promtail. Indexes the logs
Grafana: Connects to Loki & gets logs when user want to view the logs. Provides UI for querying and visualizing logs
Deployment on local docker using docker-compose
- docker-compose.yml
version: '3.8'
services:
db:
image: postgres:16
environment:
POSTGRES_USER: myuser
POSTGRES_PASSWORD: mypassword # Create user with username(myuser), password(mypassword)
POSTGRES_DB: mydb # Create database named mydb
ports:
- "5432:5432"
volumes:
- db_data:/var/lib/postgresql/data
- ./db/init.sql:/docker-entrypoint-initdb.d/init.sql # For auto-creating tables
app:
build:
context: .
dockerfile: Dockerfile
environment:
DB_HOST: db
DB_PORT: 5432
DB_USER: myuser
DB_PASSWORD: mypassword
DB_NAME: mydb
ports:
- "8080:8080"
depends_on:
- db
loki:
image: grafana/loki:3.4.1
container_name: loki
ports:
- "3100:3100"
volumes:
- ./loki-config.yaml:/mnt/config/loki-config.yaml
command: -config.file=/mnt/config/loki-config.yaml
networks:
- app-network
promtail:
image: grafana/promtail:3.4.1
container_name: promtail
volumes:
- ./promtail-config.yaml:/mnt/config/promtail-config.yaml
- /var/run/docker.sock:/var/run/docker.sock
- /var/lib/docker/containers:/var/lib/docker/containers:ro
command: -config.file=/mnt/config/promtail-config.yaml
depends_on:
- loki
networks:
- app-network
grafana:
image: grafana/grafana:latest
container_name: grafana
ports:
- "3000:3000"
environment:
- GF_AUTH_ANONYMOUS_ENABLED=true
- GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
volumes:
- ./grafana-datasources.yml:/etc/grafana/provisioning/datasources/datasources.yml
depends_on:
- loki
networks:
- app-network
volumes:
# db_data volume is a named Docker volume used to persist PostgreSQL data outside the container
# When you first start the database, this volume is empty. PostgreSQL will initialize it with its data files
db_data:
networks:
app-network:
driver: bridge
- promtail-config.yml
server:
http_listen_port: 9080
grpc_listen_port: 0
positions:
filename: /tmp/positions.yaml
clients:
- url: http://loki:3100/loki/api/v1/push
scrape_configs:
- job_name: docker
docker_sd_configs:
- host: "unix:///var/run/docker.sock"
refresh_interval: 15s
filters:
- name: label
values: ["logging=promtail"]
relabel_configs:
- source_labels: ['__meta_docker_container_name']
regex: '/(.*)'
target_label: 'container'
pipeline_stages:
- docker: {}
- loki-config.yml
auth_enabled: false
server:
http_listen_port: 3100
grpc_listen_port: 9096
schema_config:
configs:
- from: 2020-10-24
store: boltdb-shipper
object_store: filesystem
schema: v11
index:
prefix: loki_index_
period: 24h
storage_config:
boltdb_shipper:
active_index_directory: /loki/index
cache_location: /loki/index_cache
shared_store: filesystem
filesystem:
directory: /loki/chunks
- Grafanna will run on localhost:3000
grafana-datasource.yml
apiVersion: 1
datasources:
- name: Loki
type: loki
url: http://loki:3100
isDefault: true
- Run Containers
// Start docker desktop (Since docker cli will connect with docker engine)
> docker-compose up -d
[+] Running 7/7
✔ Network go-server_app-network Created 0.1s
✔ Network go-server_default Created 0.1s
✔ Container go-server-db-1 Started 3.6s
✔ Container loki Started 3.6s
✔ Container grafana Started 4.1s
✔ Container promtail Started 3.9s
✔ Container go-server-app-1 Started 3.6s
// List all containers
> docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS
NAMES
6725da48c337 go-server-app "./main" About a minute ago Up About a minute 0.0.0.0:8080->8080/tcp go-server-app-1
650e2ca190fd grafana/promtail:3.4.1 "/usr/bin/promtail -…" About a minute ago Up About a minute
promtail
78c2aaeda9f8 grafana/grafana:latest "/run.sh" About a minute ago Up About a minute 0.0.0.0:3000->3000/tcp grafana
48cba656a368 postgres:16 "docker-entrypoint.s…" About a minute ago Up About a minute 0.0.0.0:5432->5432/tcp go-server-db-1
// Go inside postgres container & list tables
> docker exec -it 48cba656a368 bash
root@48cba656a368:/# psql -U myuser mydb
psql (16.9 (Debian 16.9-1.pgdg120+1))
Type "help" for help.
mydb=# \dt
List of relations
Schema | Name | Type | Owner
--------+--------------+-------+--------
public | employee | table | myuser
public | followup | table | myuser
public | leads | table | myuser
public | password | table | myuser
public | requirements | table | myuser
public | tenants | table | myuser
(6 rows)
- Kibana dashboard
http://localhost:3000 [admin/admin]
Deployment inside AWS
ECS Instance 1
- Promtail runs as sidecar container
[ECS Cluster]
├── App Task
│ ├── Go App Container
│ └── Promtail Sidecar Container
└── Database Task (or better yet, AWS RDS)
└── PostgreSQL Container (with EFS volume for persistence)
ECS Instance-2
[ECS Service]
├── Loki Container (or separate service)
└── Grafana Container (or separate service)