Skip to content

Commit 314f56c

Browse files
authored
Merge pull request #8 from kolayne-IU-assignments/lab8
Lab8
2 parents d68d289 + eb17ae0 commit 314f56c

17 files changed

+245
-19
lines changed

app_go/Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ COPY go.mod *.go /usr/src/app/
66
# No dependencies yet
77
#RUN go mod download && go mod verify
88

9+
RUN ["env", "CGO_ENABLED=0", "go", "get"]
910
RUN ["env", "CGO_ENABLED=0", "go", "build", "-o", "catfact_webapp", "."]
1011

1112

app_go/go.mod

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
11
module catfact_webapp
22

33
go 1.21.6
4+
5+
require github.com/prometheus/client_golang v1.19.0
6+
7+
require (
8+
github.com/beorn7/perks v1.0.1 // indirect
9+
github.com/cespare/xxhash/v2 v2.2.0 // indirect
10+
github.com/prometheus/client_model v0.5.0 // indirect
11+
github.com/prometheus/common v0.48.0 // indirect
12+
github.com/prometheus/procfs v0.12.0 // indirect
13+
golang.org/x/sys v0.16.0 // indirect
14+
google.golang.org/protobuf v1.32.0 // indirect
15+
)

app_go/go.sum

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
2+
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
3+
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
4+
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
5+
github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU=
6+
github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k=
7+
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
8+
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
9+
github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
10+
github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
11+
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
12+
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
13+
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
14+
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
15+
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
16+
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=

app_go/main.go

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,14 @@ import (
44
"fmt"
55
"log"
66
"net/http"
7+
"time"
8+
9+
"github.com/prometheus/client_golang/prometheus"
10+
"github.com/prometheus/client_golang/prometheus/promauto"
11+
"github.com/prometheus/client_golang/prometheus/promhttp"
712
)
813

9-
func handler(w http.ResponseWriter, r *http.Request) {
14+
func index(w http.ResponseWriter, r *http.Request) {
1015
fact, err := catFact()
1116
if err == nil {
1217
w.WriteHeader(http.StatusOK)
@@ -17,9 +22,55 @@ func handler(w http.ResponseWriter, r *http.Request) {
1722
}
1823
}
1924

25+
26+
var (
27+
reqCnt = promauto.NewCounter(prometheus.CounterOpts{
28+
Name: "go_requests_count",
29+
Help: "Number of HTTP requests",
30+
})
31+
32+
reqHandleTime = promauto.NewHistogram(prometheus.HistogramOpts{
33+
Name: "go_request_handle_time",
34+
Help: "Time to handle a request",
35+
})
36+
)
37+
38+
func noteTimeMiddleware(next http.Handler) http.Handler {
39+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
40+
start := time.Now()
41+
defer func() {
42+
var dtSec float64 = time.Since(start).Seconds()
43+
reqCnt.Inc()
44+
reqHandleTime.Observe(dtSec)
45+
}()
46+
47+
next.ServeHTTP(w, r)
48+
})
49+
}
50+
51+
2052
func main() {
21-
http.HandleFunc("/", handler)
53+
businessLogic := http.NewServeMux()
54+
businessLogic.Handle("/", asHandler(index))
55+
// Note: keeping /metrics under middleware too for consistency with app_py
56+
businessLogic.Handle("/metrics", promhttp.Handler())
57+
58+
wrapped := noteTimeMiddleware(businessLogic)
59+
2260
hostPort := "0.0.0.0:5000"
2361
_, _ = fmt.Println("Listening on http://" + hostPort)
24-
log.Fatal(http.ListenAndServe(hostPort, nil))
62+
log.Fatal(http.ListenAndServe(hostPort, wrapped))
63+
}
64+
65+
66+
type dummyHandler struct {
67+
handlerFunc func (http.ResponseWriter, *http.Request)
68+
}
69+
70+
func (h dummyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
71+
h.handlerFunc(w, r)
72+
}
73+
74+
func asHandler(handlerFunc func (http.ResponseWriter, *http.Request)) dummyHandler {
75+
return dummyHandler{handlerFunc: handlerFunc}
2576
}

app_go/main_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import (
1313
func TestFactLoads(t *testing.T) {
1414
w := httptest.NewRecorder()
1515

16-
handler(w, nil)
16+
index(w, nil)
1717
resp := w.Result()
1818

1919
if resp.StatusCode != http.StatusOK {

app_python/moscow_time/__init__.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,31 @@
11
import datetime
2+
from time import monotonic
23

3-
from flask import Flask
4+
from flask import Flask, request, Response
45
import requests
6+
import prometheus_client
57

68
from .cache import cache_for
79

810

911
app = Flask(__name__)
1012

1113

14+
REQUEST_COUNT = prometheus_client.Counter('py_requests_count', 'Number of HTTP requests')
15+
REQUEST_HANDLE_TIME = prometheus_client.Histogram('py_request_handle_time', 'Time to handle a request')
16+
17+
@app.before_request
18+
def note_request_start_time():
19+
request.start_time = monotonic()
20+
21+
@app.after_request
22+
def update_prometheus(response):
23+
handle_time = monotonic() - request.start_time
24+
REQUEST_COUNT.inc()
25+
REQUEST_HANDLE_TIME.observe(handle_time)
26+
return response
27+
28+
1229
# In case of high load, to avoid frequent requests, cache results for
1330
# one second
1431
@cache_for(1000)
@@ -30,3 +47,7 @@ def index():
3047
time = get_time()
3148
return f"In MSK it's {time.hour}:{time.minute}:{time.second}. " \
3249
"Have you brushed your teeth today yet?"
50+
51+
@app.route('/metrics')
52+
def prometheus_metrics():
53+
return Response(prometheus_client.generate_latest(), mimetype='text/plain')

app_python/requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
Flask~=3.0.0
22
requests~=2.31.0
3+
prometheus_client~=0.20.0

monitoring/METRICS.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Prometheus metrics
2+
3+
## Screenshots
4+
5+
![Prometheus targets](pics/prometheus_targets.png)
6+
7+
![Grafana promtail dashboard](pics/grafana_promtail_dashboard.png)
8+
9+
![Grafana prometheus dashboard (impoarted)](pics/grafana_prometheus_dashboard_(imported).png)
10+
11+
## Enhancements
12+
13+
Grafana, Loki, and Promtail each have the RAM limit of 100MiB, Prometheus's memory is limited
14+
to 50MiB, limits for app_py and app_go are 30MiB and 20MiB respectively.
15+
16+
## Metrics from web apps
17+
18+
The Python app exports metrics corresponding to the web app (requests count, request handle
19+
time), as well as metrics related to python runtime exposed by the `promteheus_client`, for instance:
20+
21+
![Prometheus, python web_app](pics/prometheus_app_py_sample.png)
22+
23+
The Go app exports metrics corresponding to the web app (requests count, request handle time),
24+
as well as metrics related to go runtime exposed by the prometheus client, for instance:
25+
26+
![Prometheus, go web_app](pics/prometheus_app_go_sample.png)

monitoring/docker-compose.yml

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,34 @@
11
networks:
22
loki:
3+
prometheus:
4+
5+
volumes:
6+
grafana-storage:
37

48
services:
59
app_py:
6-
image: kolay0ne/app_py:lab6
10+
image: kolay0ne/app_py:lab8
711
ports:
812
- "5000:5000"
13+
logging:
14+
options:
15+
max-size: 5m
16+
deploy:
17+
resources: {limits: {memory: 30M}}
18+
networks:
19+
- prometheus
920

1021
app_go:
11-
image: kolay0ne/app_go:lab6
22+
image: kolay0ne/app_go:lab8
1223
ports:
1324
- "5500:5000"
25+
logging:
26+
options:
27+
max-size: 5m
28+
deploy:
29+
resources: {limits: {memory: 20M}}
30+
networks:
31+
- prometheus
1432

1533
loki:
1634
image: grafana/loki:2.9.2
@@ -19,6 +37,12 @@ services:
1937
#command: -config.file=/etc/loki/local-config.yaml
2038
networks:
2139
- loki
40+
- prometheus
41+
logging:
42+
options:
43+
max-size: 5m
44+
deploy:
45+
resources: {limits: {memory: 100M}}
2246

2347
promtail:
2448
image: grafana/promtail:2.9.2
@@ -28,16 +52,43 @@ services:
2852
command: -config.file=/etc/promtail/config.yml
2953
networks:
3054
- loki
55+
- prometheus
56+
logging:
57+
options:
58+
max-size: 5m
59+
deploy:
60+
resources: {limits: {memory: 100M}}
3161

3262
grafana:
3363
image: grafana/grafana:10.0.12
3464
ports:
3565
- "3000:3000"
3666
volumes:
67+
- grafana-storage:/var/lib/grafana
3768
- ./grafana_datasources.yml:/etc/grafana/provisioning/datasources/ds.yaml
3869
environment:
3970
- GF_PATHS_PROVISIONING=/etc/grafana/provisioning
4071
- GF_AUTH_ANONYMOUS_ENABLED=true
4172
- GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
4273
networks:
4374
- loki
75+
- prometheus
76+
logging:
77+
options:
78+
max-size: 5m
79+
deploy:
80+
resources: {limits: {memory: 100M}}
81+
82+
prometheus:
83+
image: prom/prometheus:v2.51.0
84+
ports:
85+
- "9090:9090"
86+
volumes:
87+
- ./prometheus_config.yml:/etc/prometheus/prometheus.yml
88+
networks:
89+
- prometheus
90+
logging:
91+
options:
92+
max-size: 5m
93+
deploy:
94+
resources: {limits: {memory: 50M}}

monitoring/grafana_datasources.yml

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,35 @@
11
apiVersion: 1
2+
23
datasources:
3-
- name: Loki
4-
type: loki
5-
access: proxy
6-
orgId: 1
7-
url: http://loki:3100
8-
basicAuth: false
9-
isDefault: true
10-
version: 1
11-
editable: false
4+
- name: Loki
5+
type: loki
6+
access: proxy
7+
orgId: 1
8+
url: http://loki:3100
9+
basicAuth: false
10+
isDefault: true
11+
version: 1
12+
editable: false
13+
14+
- name: Prometheus
15+
type: prometheus
16+
access: proxy
17+
url: http://prometheus:9090
18+
jsonData:
19+
httpMethod: POST
20+
manageAlerts: true
21+
prometheusType: Prometheus
22+
prometheusVersion: 2.51.0
23+
cacheLevel: 'High'
24+
disableRecordingRules: false
25+
incrementalQueryOverlapWindow: 10m
26+
examplarTraceIdDestinations:
27+
# Field with internal link pointing to data source in Grafana.
28+
# datasourceUid value can be anything, but it should be unique across all defined data source uids.
29+
- datasourceUid: my_jaeger_uid
30+
name: traceID
31+
32+
# Field with external link.
33+
- name: traceID
34+
url: 'http://grafana:3000/explore?orgId=1&left=%5B%22now-1h%22,%22now%22,%22Jaeger%22,%7B%22query%22:%22$${__value.raw}%22%7D%5D'
35+
editable: true

0 commit comments

Comments
 (0)