Skip to content

Commit eb17ae0

Browse files
committed
Lab 8, bonus task
1 parent a56d13c commit eb17ae0

File tree

13 files changed

+135
-9
lines changed

13 files changed

+135
-9
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: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,16 @@
1111
## Enhancements
1212

1313
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 6MiB respectively.
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: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,28 @@ volumes:
77

88
services:
99
app_py:
10-
image: kolay0ne/app_py:lab6
10+
image: kolay0ne/app_py:lab8
1111
ports:
1212
- "5000:5000"
1313
logging:
1414
options:
1515
max-size: 5m
1616
deploy:
1717
resources: {limits: {memory: 30M}}
18+
networks:
19+
- prometheus
1820

1921
app_go:
20-
image: kolay0ne/app_go:lab6
22+
image: kolay0ne/app_go:lab8
2123
ports:
2224
- "5500:5000"
2325
logging:
2426
options:
2527
max-size: 5m
2628
deploy:
27-
resources: {limits: {memory: 6M}}
29+
resources: {limits: {memory: 20M}}
30+
networks:
31+
- prometheus
2832

2933
loki:
3034
image: grafana/loki:2.9.2
572 KB
Loading

0 commit comments

Comments
 (0)