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
1# Project 95
2
3Traefik Rate Limit
4
5[https://gitorko.github.io/spring-boot-traefik-rate-limit/](https://gitorko.github.io/spring-boot-traefik-rate-limit/)
6
7### Version
8
9Check version
10
11```bash
12$java --version
13openjdk version "21.0.3" 2024-04-16 LTS
14
15helm version --short
16v3.9.1+ga7c043a
17
18kubectl version --short
19Client Version: v1.24.3
20Kustomize Version: v4.5.4
21```
22
23### Postgres DB
24
25```
26docker run -p 5432:5432 --name pg-container -e POSTGRES_PASSWORD=password -d postgres:9.6.10
27docker ps
28docker exec -it pg-container psql -U postgres -W postgres
29CREATE USER test WITH PASSWORD 'test@123';
30CREATE DATABASE "test-db" WITH OWNER "test" ENCODING UTF8 TEMPLATE template0;
31grant all PRIVILEGES ON DATABASE "test-db" to test;
32
33docker stop pg-container
34docker start pg-container
35```
36
37### Docker
38
39For docker on laptop we cant use localhost as the hostname, so add this entry to the /etc/hosts file.
40
41```bash
42127.0.0.1 localhost.com
43```
44
45Build the project and docker image
46
47```bash
48cd project95
49./gradlew bootRun
50./gradlew clean build
51docker build -f docker/Dockerfile --force-rm -t project95:1.0.0 .
52```
53
54If you want to deploy via docker compose.
55
56```bash
57docker tag project95:1.0.0 gitorko/project95:1.0.0
58docker push gitorko/project95:1.0.0
59docker-compose -f docker/docker-compose.yml up
60```
61
62### Traefik
63
64Deploy traefik via helm
65
66```bash
67helm install traefik traefik/traefik
68```
69
70Traefik 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
71
72```bash
73kubectl port-forward $(kubectl get pods --selector "app.kubernetes.io/name=traefik" --output=name) 9000:9000
74```
75
76Open the dashboard url
77
78[http://127.0.0.1:9000/dashboard/](http://127.0.0.1:9000/dashboard/)
79
80### Kubernetes
81
82Now deploy the application on kubernetes
83
84If you want a plain deployment without traefik, This will deploy the spring boot application along with postgres, run the below command
85
86```bash
87kubectl apply -f docker/deployment.yaml
88```
89
90To test the api, run the curl command
91
92```bash
93curl --request GET 'http://localhost.com:8080/rest/time'
94```
95
96Clean up
97
98```bash
99kubectl delete -f docker/deployment.yaml
100```
101
102### Kubernetes & Traefik Ingress
103
104If you want traefik as the ingress controller, run the below command
105
106```bash
107kubectl apply -f docker/deployment-traefik.yaml
108```
109
110To test the api, run the curl command
111
112```bash
113curl --request GET 'http://localhost.com/rest/time'
114```
115
116Clean up
117
118```bash
119kubectl delete -f docker/deployment-traefik.yaml
120```
121
122### Kubernetes & Traefik IngressRoute with Rate Limit
123
124If you want traefik as the ingress & want to rate limit, run the below command
125
126```bash
127kubectl apply -f docker/deployment-traefik-ratelimit.yaml
128```
129
130To test the api, run the curl command
131
132```bash
133curl --request GET 'http://localhost.com/rest/time'
134```
135
136Clean up
137
138```bash
139kubectl delete -f docker/deployment-traefik-ratelimit.yaml
140```
141
142Few command to look at the services
143
144```bash
145kubectl get ingress
146kubectl describe ingress
147
148kubectl get ingressroute
149kubectl describe ingressroute
150
151kubectl get all
152
153k logs -f deployment.apps/project95 --all-containers=true
154
155helm uninstall traefik
156```
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'