Spring - Traefik (Rate Limit)
Overview
Application deployed on kubernetes, configured with Traefik ingress controller to rate limit.
Github: https://github.com/gitorko/project95
Traefik
Traefik is a reverse proxy and load balancer that makes deploying microservices easy.
We will deploy the spring rest application along with postgres db on kubernetes instance. Then we will configure Traefik as ingress controller and apply rate limit on it using Traefik Proxy Middleware. We will use docker desktop kubernetes instance.
Rate limiting is a technique for controlling the rate of requests to your application. It can save you from Denial-of-Service (DoS) or resource starvation problems. Without rate limits, a burst of traffic could bring down the whole service making it unavailable for everybody.
Code
1apiVersion: apps/v1
2kind: Deployment
3metadata:
4 name: project95
5spec:
6 selector:
7 matchLabels:
8 app: project95
9 strategy:
10 rollingUpdate:
11 maxSurge: 1
12 maxUnavailable: 1
13 type: RollingUpdate
14 replicas: 1
15 template:
16 metadata:
17 labels:
18 app: project95
19 spec:
20 containers:
21 - name: project95
22 image: project95:1.0.0
23 imagePullPolicy: IfNotPresent
24 ports:
25 - containerPort: 8080
26 resources:
27 limits:
28 cpu: "1"
29 memory: "500Mi"
30
31---
32apiVersion: v1
33kind: ConfigMap
34metadata:
35 name: postgres-config
36 labels:
37 app: postgres
38data:
39 POSTGRES_DB: test-db
40 POSTGRES_USER: test
41 POSTGRES_PASSWORD: test@123
42---
43apiVersion: v1
44kind: PersistentVolume
45metadata:
46 name: postgres-pv-volume
47 labels:
48 type: local
49 app: postgres
50spec:
51 storageClassName: manual
52 capacity:
53 storage: 5Gi
54 accessModes:
55 - ReadWriteMany
56 hostPath:
57 path: "/tmp/data"
58---
59apiVersion: v1
60kind: PersistentVolumeClaim
61metadata:
62 name: postgres-pv-claim
63 labels:
64 app: postgres
65spec:
66 storageClassName: manual
67 accessModes:
68 - ReadWriteMany
69 resources:
70 requests:
71 storage: 5Gi
72---
73apiVersion: apps/v1
74kind: Deployment
75metadata:
76 name: db-server
77spec:
78 replicas: 1
79 template:
80 metadata:
81 labels:
82 app: db-server
83 spec:
84 containers:
85 - name: db-server
86 image: postgres:9.6.10
87 imagePullPolicy: "IfNotPresent"
88 ports:
89 - containerPort: 5432
90 envFrom:
91 - configMapRef:
92 name: postgres-config
93 volumeMounts:
94 - mountPath: /var/lib/postgresql/data
95 name: postgredb
96 volumes:
97 - name: postgredb
98 persistentVolumeClaim:
99 claimName: postgres-pv-claim
100 selector:
101 matchLabels:
102 app: db-server
103---
104apiVersion: v1
105kind: Service
106metadata:
107 name: db-server
108 labels:
109 app: db-server
110spec:
111 type: NodePort
112 ports:
113 - port: 5432
114 selector:
115 app: db-server
116---
117kind: Service
118apiVersion: v1
119metadata:
120 name: project95
121spec:
122 ports:
123 - port: 8080
124 targetPort: 8080
125 name: http
126 selector:
127 app: project95
128 type: LoadBalancer
1apiVersion: apps/v1
2kind: Deployment
3metadata:
4 name: project95
5 labels:
6 app: project95
7spec:
8 selector:
9 matchLabels:
10 app: project95
11 strategy:
12 rollingUpdate:
13 maxSurge: 1
14 maxUnavailable: 1
15 type: RollingUpdate
16 replicas: 1
17 template:
18 metadata:
19 labels:
20 app: project95
21 spec:
22 containers:
23 - name: project95
24 image: gitorko/project95:1.0.0
25 imagePullPolicy: IfNotPresent
26 ports:
27 - containerPort: 8080
28 resources:
29 limits:
30 cpu: "1"
31 memory: "500Mi"
32
33---
34apiVersion: v1
35kind: ConfigMap
36metadata:
37 name: postgres-config
38 labels:
39 app: postgres
40data:
41 POSTGRES_DB: test-db
42 POSTGRES_USER: test
43 POSTGRES_PASSWORD: test@123
44---
45apiVersion: v1
46kind: PersistentVolume
47metadata:
48 name: postgres-pv-volume
49 labels:
50 type: local
51 app: postgres
52spec:
53 storageClassName: manual
54 capacity:
55 storage: 5Gi
56 accessModes:
57 - ReadWriteMany
58 hostPath:
59 path: "/tmp/data"
60---
61apiVersion: v1
62kind: PersistentVolumeClaim
63metadata:
64 name: postgres-pv-claim
65 labels:
66 app: postgres
67spec:
68 storageClassName: manual
69 accessModes:
70 - ReadWriteMany
71 resources:
72 requests:
73 storage: 5Gi
74---
75apiVersion: apps/v1
76kind: Deployment
77metadata:
78 name: db-server
79spec:
80 replicas: 1
81 template:
82 metadata:
83 labels:
84 app: db-server
85 spec:
86 containers:
87 - name: db-server
88 image: postgres:9.6.10
89 imagePullPolicy: "IfNotPresent"
90 ports:
91 - containerPort: 5432
92 envFrom:
93 - configMapRef:
94 name: postgres-config
95 volumeMounts:
96 - mountPath: /var/lib/postgresql/data
97 name: postgredb
98 volumes:
99 - name: postgredb
100 persistentVolumeClaim:
101 claimName: postgres-pv-claim
102 selector:
103 matchLabels:
104 app: db-server
105---
106apiVersion: v1
107kind: Service
108metadata:
109 name: db-server
110 labels:
111 app: db-server
112spec:
113 type: NodePort
114 ports:
115 - port: 5432
116 selector:
117 app: db-server
118---
119apiVersion: v1
120kind: Service
121metadata:
122 name: project95
123 labels:
124 app: project95
125spec:
126 type: ClusterIP
127 ports:
128 - port: 8080
129 selector:
130 app: project95
131---
132apiVersion: networking.k8s.io/v1
133kind: Ingress
134metadata:
135 name: my-ingress
136 annotations:
137 kubernetes.io/ingress.class: "traefik"
138spec:
139 rules:
140 - host: localhost.com
141 http:
142 paths:
143 - path: /rest
144 pathType: Prefix
145 backend:
146 service:
147 name: project95
148 port:
149 number: 8080
150---
1apiVersion: apps/v1
2kind: Deployment
3metadata:
4 name: project95
5 labels:
6 app: project95
7spec:
8 selector:
9 matchLabels:
10 app: project95
11 strategy:
12 rollingUpdate:
13 maxSurge: 1
14 maxUnavailable: 1
15 type: RollingUpdate
16 replicas: 1
17 template:
18 metadata:
19 labels:
20 app: project95
21 spec:
22 containers:
23 - name: project95
24 image: gitorko/project95:1.0.0
25 imagePullPolicy: IfNotPresent
26 ports:
27 - containerPort: 8080
28 resources:
29 limits:
30 cpu: "1"
31 memory: "500Mi"
32
33---
34apiVersion: v1
35kind: ConfigMap
36metadata:
37 name: postgres-config
38 labels:
39 app: postgres
40data:
41 POSTGRES_DB: test-db
42 POSTGRES_USER: test
43 POSTGRES_PASSWORD: test@123
44---
45apiVersion: v1
46kind: PersistentVolume
47metadata:
48 name: postgres-pv-volume
49 labels:
50 type: local
51 app: postgres
52spec:
53 storageClassName: manual
54 capacity:
55 storage: 5Gi
56 accessModes:
57 - ReadWriteMany
58 hostPath:
59 path: "/tmp/data"
60---
61apiVersion: v1
62kind: PersistentVolumeClaim
63metadata:
64 name: postgres-pv-claim
65 labels:
66 app: postgres
67spec:
68 storageClassName: manual
69 accessModes:
70 - ReadWriteMany
71 resources:
72 requests:
73 storage: 5Gi
74---
75apiVersion: apps/v1
76kind: Deployment
77metadata:
78 name: db-server
79spec:
80 replicas: 1
81 template:
82 metadata:
83 labels:
84 app: db-server
85 spec:
86 containers:
87 - name: db-server
88 image: postgres:9.6.10
89 imagePullPolicy: "IfNotPresent"
90 ports:
91 - containerPort: 5432
92 envFrom:
93 - configMapRef:
94 name: postgres-config
95 volumeMounts:
96 - mountPath: /var/lib/postgresql/data
97 name: postgredb
98 volumes:
99 - name: postgredb
100 persistentVolumeClaim:
101 claimName: postgres-pv-claim
102 selector:
103 matchLabels:
104 app: db-server
105---
106apiVersion: v1
107kind: Service
108metadata:
109 name: db-server
110 labels:
111 app: db-server
112spec:
113 type: NodePort
114 ports:
115 - port: 5432
116 selector:
117 app: db-server
118---
119apiVersion: v1
120kind: Service
121metadata:
122 name: project95
123 labels:
124 app: project95
125spec:
126 type: ClusterIP
127 ports:
128 - port: 8080
129 selector:
130 app: project95
131---
132apiVersion: traefik.containo.us/v1alpha1
133kind: Middleware
134metadata:
135 name: ratelimiter
136spec:
137 rateLimit:
138 average: 3
139 burst: 5
140---
141apiVersion: traefik.containo.us/v1alpha1
142kind: IngressRoute
143metadata:
144 name: myingressroute
145spec:
146 entryPoints:
147 - web
148 routes:
149 - match: Host(`localhost.com`) && PathPrefix(`/rest`)
150 kind: Rule
151 services:
152 - kind: Service
153 name: project95
154 port: 8080
155 middlewares:
156 - name: ratelimiter
Setup
Project 95
Traefik Rate Limit
https://gitorko.github.io/spring-boot-traefik-rate-limit/
Version
Check version
1$java --version
2openjdk 17.0.3 2022-04-19 LTS
3
4helm version --short
5v3.9.1+ga7c043a
6
7kubectl version --short
8Client Version: v1.24.3
9Kustomize Version: v4.5.4
Postgres DB
1docker run -p 5432:5432 --name pg-container -e POSTGRES_PASSWORD=password -d postgres:9.6.10
2docker ps
3docker exec -it pg-container psql -U postgres -W postgres
4CREATE USER test WITH PASSWORD 'test@123';
5CREATE DATABASE "test-db" WITH OWNER "test" ENCODING UTF8 TEMPLATE template0;
6grant all PRIVILEGES ON DATABASE "test-db" to test;
7
8docker stop pg-container
9docker start pg-container
Docker
For docker on laptop we cant use localhost as the hostname, so add this entry to the /etc/hosts file.
1127.0.0.1 localhost.com
Build the project and docker image
1cd project95
2./gradlew bootRun
3./gradlew clean build
4docker build -f docker/Dockerfile --force-rm -t project95:1.0.0 .
If you want to deploy via docker compose.
1docker tag project95:1.0.0 gitorko/project95:1.0.0
2docker push gitorko/project95:1.0.0
3docker-compose -f docker/docker-compose.yml up
Traefik
Deploy traefik via helm
1helm install traefik traefik/traefik
Traefik comes with the dashboard to visualize the config that is not exposed so run port forward command. If you dont need to visualize the config then you can skip this step as it is not mandatory
1kubectl port-forward $(kubectl get pods --selector "app.kubernetes.io/name=traefik" --output=name) 9000:9000
Open the dashboard url
http://127.0.0.1:9000/dashboard/
Kubernetes
Now deploy the application on kubernetes
If you want a plain deployment without traefik, This will deploy the spring boot application along with postgres, run the below command
1kubectl apply -f docker/deployment.yaml
To test the api, run the curl command
1curl --request GET 'http://localhost.com:8080/rest/time'
Clean up
1kubectl delete -f docker/deployment.yaml
Kubernetes & Traefik Ingress
If you want traefik as the ingress controller, run the below command
1kubectl apply -f docker/deployment-traefik.yaml
To test the api, run the curl command
1curl --request GET 'http://localhost.com/rest/time'
Clean up
1kubectl delete -f docker/deployment-traefik.yaml
Kubernetes & Traefik IngressRoute with Rate Limit
If you want traefik as the ingress & want to rate limit, run the below command
1kubectl apply -f docker/deployment-traefik-ratelimit.yaml
To test the api, run the curl command
1curl --request GET 'http://localhost.com/rest/time'
Clean up
1kubectl delete -f docker/deployment-traefik-ratelimit.yaml
Few command to look at the services
1kubectl get ingress
2kubectl describe ingress
3
4kubectl get ingressroute
5kubectl describe ingressroute
6
7kubectl get all
8
9k logs -f deployment.apps/project95 --all-containers=true
10
11helm uninstall traefik
Testing
Deploy the image to kubernetes
The dashboard will show the HTTP Routers & the middleware rate limit config
You can also look at success rate
To test the rate limit functionality open the RateLimit.jmx file in JMeter and run the test
Create a user, the data is persisted in the postgres db.
1curl --request POST 'http://localhost.com/rest/customer' \
2--header 'Content-Type: application/json' \
3--data-raw '{
4 "firstName" : "John",
5 "lastName" : "Doe",
6 "city": "NY"
7}'
Get the user
1curl --request GET 'http://localhost.com/rest/customer'