Skip to content

Commit 7d0f0b2

Browse files
committed
Merge branch 'dev'
2 parents 179baa8 + a884851 commit 7d0f0b2

1 file changed

Lines changed: 190 additions & 19 deletions

File tree

README.md

Lines changed: 190 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -81,20 +81,20 @@ With this we can able to:**
8181
pip freeze > requirements.txt
8282
```
8383

84-
## Project Local Setup
84+
## Project local setup
8585

86-
- **Install dependencies**
86+
**1. Install dependencies**
8787
```txt
8888
make install
8989
```
9090
- (or run pip install -r requirements.txt directly)
9191

92-
- **Run the App**
92+
**2. Run the App**
9393
```txt
9494
make run
9595
```
9696

97-
- **Flask app will start at:**
97+
**3. Flask app will start at:**
9898
```url
9999
http://127.0.0.1:5000
100100
```
@@ -104,7 +104,7 @@ With this we can able to:**
104104
- /health → Health check
105105
- /students → Manage students
106106

107-
- **Run all unit tests:**
107+
**4. Run all unit tests:**
108108
```txt
109109
make test
110110
```
@@ -123,9 +123,10 @@ With this we can able to:**
123123

124124
- Overall I managed to reduce the original `1.26GB` image to just `110MB`, achieving a `91.27%` size decrease while maintaining the same functionality and improving rebuild times by an average of 20 seconds.
125125

126-
#### Docker Commands
126+
### Docker Commands
127+
128+
**1. Build the Docker Image:**
127129

128-
- **Build the Docker Image:**
129130
- Built the image with semantic versioning tags:
130131
```sh
131132
make docker-build DOCKER_IMAGE_TAG=1.0.1
@@ -137,7 +138,7 @@ With this we can able to:**
137138
```
138139
- We can change the `DOCKER_IMAGE_TAG` to any version.
139140

140-
- **Run the Docker Container:**
141+
**2. Run the Docker Container:**
141142
- Run the container
142143
```sh
143144
make docker-run DOCKER_IMAGE_TAG=1.0.1
@@ -152,7 +153,7 @@ With this we can able to:**
152153
http://localhost:5000
153154
```
154155

155-
- **To Remove the Containers:**
156+
**3. To Remove the Containers:**
156157
- To remove the containers using make command
157158
```sh
158159
make docker-stop
@@ -165,7 +166,7 @@ With this we can able to:**
165166
docker rm ${DOCKER_CONTAINER}
166167
```
167168

168-
- **To Remove the unused images:**
169+
**4. To Remove the unused images:**
169170
- To remove the images using make command
170171
```sh
171172
make docker-clean
@@ -177,7 +178,7 @@ With this we can able to:**
177178
docker rmi ${DOCKER_IMAGE}:${DOCKER_IMAGE_TAG}
178179
```
179180

180-
- **For Troubleshooting Container issue:**
181+
**5. For Troubleshooting Container issue:**
181182
- To check container logs using make command
182183
```sh
183184
make docker logs
@@ -190,11 +191,11 @@ With this we can able to:**
190191

191192
## One click local development setup
192193

193-
- Created docker-compose.yml file to containerise Flask (API) and Postgres (DB) together, with persistent volumes for one click local development setup.
194+
**Created docker-compose.yml file to containerise Flask (API) and Postgres (DB) together, with persistent volumes for one click local development setup.**
194195

195196
### Docker Compose Commands
196197

197-
1. To start the service stack
198+
**1. To start the service stack**
198199
```sh
199200
docker compose up -d --build
200201
```
@@ -204,7 +205,7 @@ With this we can able to:**
204205
make up
205206
```
206207

207-
2. To stop and remove everything (including volumes)
208+
**2. To stop and remove everything (including volumes)**
208209
```sh
209210
docker compose down -v
210211
```
@@ -216,9 +217,9 @@ With this we can able to:**
216217
```
217218

218219
### For Database Migration & Seeding
219-
- After starting the containers, you need to run migrations (to create tables) and optionally seed data.
220+
**After starting the containers, you need to run migrations (to create tables) and optionally seed data.**
220221

221-
1. Apply migrations inside the Flask container
222+
**1. Apply migrations inside the Flask container**
222223
```sh
223224
docker exec -it flask-container flask db upgrade
224225
```
@@ -229,7 +230,7 @@ With this we can able to:**
229230
make migrate
230231
```
231232

232-
2. To Seed the database with initial data
233+
**2. To Seed the database with initial data**
233234
```sh
234235
docker exec -it flask-container python seed.py
235236
```
@@ -240,7 +241,7 @@ With this we can able to:**
240241
make seed
241242
```
242243

243-
3. Verify Database
244+
**3. Verify Database**
244245

245246
- To connect into the Postgres container
246247
```sh
@@ -253,7 +254,7 @@ With this we can able to:**
253254
SELECT * FROM students LIMIT 5;
254255
```
255256

256-
4. To Run API container (depends on DB + migrations + seed)
257+
**4. To Run API container (depends on DB + migrations + seed)**
257258

258259
```txt
259260
make run
@@ -266,3 +267,173 @@ With this we can able to:**
266267
http://localhost:5000/students
267268

268269
http://localhost:5000/health
270+
271+
272+
## Setup CI pipeline
273+
274+
**Automation for build, test, and publish of Docker images using GitHub Actions workflow for CI pipeline**
275+
276+
### pipeline stages
277+
- Build API → make sure it compiles.
278+
- Run tests → unit tests should pass.
279+
- Lint → run flake8/pylint/eslint.
280+
- Docker login → authenticate to registry (DockerHub/GHCR).
281+
- Docker build & push → push tagged image.
282+
283+
### Triggering
284+
285+
- Automatically when changes are made inside /api/**.
286+
- Manual trigger (workflow_dispatch).
287+
288+
### Self-hosted runner
289+
- GitHub Actions running on our laptop/VM to simulate real-world self-hosted CI.
290+
291+
**At the end: "Every commit to main will test your code and publish a Docker image"**
292+
293+
## Deploy on Bare Metal
294+
**To deploy on a “production-like” environment without Kubernetes — just Docker + Nginx on a Vagrant box.**
295+
296+
### Key Points
297+
298+
- Vagrantfile creates a VM (e.g., Ubuntu).
299+
- A provisioning script installs Docker, Docker Compose, Nginx.
300+
- docker-compose.yml deploys:
301+
- 2 API containers (scale with replicas).
302+
- 1 Postgres DB container.
303+
- 1 Nginx container (load balances API replicas).
304+
305+
### Nginx config
306+
307+
```nginx
308+
upstream api_backend {
309+
server api1:5000;
310+
server api2:5000;
311+
}
312+
server {
313+
listen 8080;
314+
location / {
315+
proxy_pass http://api_backend;
316+
}
317+
}
318+
```
319+
- **Access API at http://localhost:8080/api/v1/students.**
320+
- **At the end: "we’ll have a mini production setup with scaling + load balancing".**
321+
322+
## Setup Kubernetes Cluster
323+
324+
**Spin up a 3-node Kubernetes cluster with Minikube.**
325+
326+
### Key Points
327+
328+
- **Start minikube with 3 nodes:**
329+
```sh
330+
minikube start --nodes=3
331+
```
332+
333+
- **Label nodes:**
334+
- Node A → type=application
335+
- Node B → type=database
336+
- Node C → type=dependent_services
337+
338+
- This enforces workload isolation (apps on one node, DB on another, monitoring tools on another).
339+
340+
### Minikube Cluster setup commands
341+
342+
```sh
343+
# Start a 3-node cluster
344+
minikube start --nodes 3
345+
346+
# Check all nodes
347+
kubectl get nodes -o wide
348+
349+
# Label nodes for workload separation
350+
kubectl label node minikube type=application
351+
kubectl label node minikube-m02 type=database
352+
kubectl label node minikube-m03 type=dependent_services
353+
```
354+
355+
**At the end: "we have a real K8s cluster with node roles".**
356+
357+
## Deploy API, DB and other services in Kubernetes
358+
359+
**Move from Docker Compose → Kubernetes deployment.**
360+
361+
### Key Points
362+
363+
- **Manifests should be modular:**
364+
- **application.yml** → namespace, configmap, secret, deployment, service for API.
365+
- **database.yml** → namespace, deployment, service for Postgres.
366+
367+
- **Init container** → runs DB migrations before starting API.
368+
- **ConfigMaps** → non-sensitive configs (e.g., DB host).
369+
- **Secrets** → sensitive info (DB password).
370+
- **External Secrets Operator + Vault** → manage secrets properly.
371+
- **Services**
372+
- ClusterIP for DB (internal only).
373+
- NodePort/LoadBalancer for API (external access).
374+
- **Namespace isolation** → student-api for app + db, others for observability.
375+
- **Test via Postman**: all endpoints should work and return 200.
376+
377+
### K8s deployment and verification commands
378+
379+
- **Deploy API + DB**
380+
```sh
381+
kubectl apply -f k8s/database.yml
382+
kubectl apply -f k8s/application.yml
383+
```
384+
385+
- **Deploy Vault + ESO**
386+
```sh
387+
kubectl apply -f k8s/vault.yml
388+
kubectl apply -f k8s/external-secrets.yml
389+
```
390+
- **Verify Deployments**
391+
```sh
392+
# Check namespaces
393+
kubectl get ns
394+
395+
# Check pods
396+
kubectl get pods -n student-api
397+
398+
# Check deployments
399+
kubectl get deployments -n student-api
400+
401+
# Check services
402+
kubectl get svc -n student-api
403+
```
404+
- **Debugging**
405+
```sh
406+
# Describe pod for events/logs
407+
kubectl describe pod <pod-name> -n student-api
408+
409+
# View container logs
410+
kubectl logs -f <pod-name> -n student-api
411+
412+
# Exec into a running pod
413+
kubectl exec -it <pod-name> -n student-api -- /bin/sh
414+
```
415+
- **Port Forward (if no LoadBalancer)**
416+
```sh
417+
kubectl port-forward svc/student-api-service 8080:80 -n student-api
418+
```
419+
- Now access API at: **http://localhost:8080/api/v1/students**
420+
421+
- **Testing in Kubernetes**
422+
```sh
423+
# Healthcheck endpoint
424+
curl http://<node-ip>:<nodePort>/healthcheck
425+
```
426+
- Expected response:
427+
```json
428+
{"status": "ok"}
429+
```
430+
431+
- **Cleanup**
432+
```sh
433+
kubectl delete -f k8s/application.yml
434+
kubectl delete -f k8s/database.yml
435+
kubectl delete -f k8s/vault.yml
436+
kubectl delete -f k8s/external-secrets.yml
437+
```
438+
439+
**At the end: "Our app is cloud-ready, secure, and scalable on Kubernetes".**

0 commit comments

Comments
 (0)