You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
kubectl wait --for=condition=ready pod -l app=postgres --timeout=120s
120
+
121
+
# Wait for postgres to actually accept connections
122
+
kubectl exec $(kubectl get pod -l app=postgres -o jsonpath="{.items[0].metadata.name}") -- sh -c "until pg_isready -h 127.0.0.1 -U postgres; do echo 'waiting for postgres...'; sleep 2; done"
123
+
124
+
# Now create the table
125
+
kubectl exec $(kubectl get pod -l app=postgres -o jsonpath="{.items[0].metadata.name}") -- psql -h 127.0.0.1 -U postgres -d todo_db -c "CREATE TABLE IF NOT EXISTS todo (todo_id SERIAL PRIMARY KEY, description VARCHAR(255));"
126
+
127
+
kubectl wait --for=condition=ready pod -l app=backend --timeout=120s
128
+
kubectl wait --for=condition=ready pod -l app=frontend --timeout=120s
A full-stack PERN (PostgreSQL, Express, React, Node.js) task manager app deployed to Kubernetes via a fully automated CI/CD pipeline using GitHub Actions, kind, and K3s.
13
14
14
15
> **v3 of this project** — previously deployed with docker compose. Migrated to Kubernetes to move beyond single-host deployments. See [task-manager-cicd-pipeline](https://github.com/kithupag/task-manager-cicd-pipeline) for the Jenkins version.
16
+
=======
17
+
A full-stack PERN (PostgreSQL, Express, React, Node.js) task manager app with two deployment modes — Docker Compose on AWS EC2 and Kubernetes via kind/k3s — both automated through GitHub Actions.
15
18
16
-
---
17
-
18
-
## 🏗️ Architecture
19
+
> **v2 of this project** — previously deployed with Jenkins. Migrated to GitHub Actions and extended with Kubernetes support. See [task-manager-cicd-pipeline](https://github.com/kithupag/task-manager-cicd-pipeline) for the Jenkins version.
│ │ └── secret.yaml # DB credentials as k8s Secret
97
+
│ ├── backend/
98
+
│ │ └── deployment.yaml # Backend Deployment + Service
99
+
│ └── frontend/
100
+
│ └── deployment.yaml # Frontend Deployment + NodePort Service
101
+
│
84
102
├── database.sql # Mounted into postgres on fresh deploy
85
-
├── docker-compose.yaml # Production compose — image tags pinned by pipeline
103
+
├── docker-compose.yaml # Local dev + EC2 compose deployment
86
104
└── README.md
87
105
```
88
106
89
107
---
90
108
91
-
## 🔧 Key Technical Decisions
109
+
## Key Technical Decisions
92
110
93
111
**GitHub Actions over Jenkins**
94
112
Jenkins requires a dedicated server running 24/7. GitHub Actions runs on GitHub's infrastructure — no server to maintain, no Docker socket to mount, no SSH keys to manage inside a container. The pipeline logic is identical, the operational overhead is zero.
@@ -120,7 +138,7 @@ All three containers report real status. Backend and frontend are checked via HT
120
138
121
139
---
122
140
123
-
## 🔄 Comparison with Jenkins Version
141
+
## Comparison with Jenkins Version
124
142
125
143
| | Jenkins Version | GitHub Actions Version |
126
144
|--|----------------|----------------------|
@@ -135,7 +153,7 @@ All three containers report real status. Backend and frontend are checked via HT
135
153
136
154
---
137
155
138
-
## 🛠️ Local Development Setup
156
+
## Local Development Setup
139
157
140
158
### Prerequisites
141
159
- Docker & Docker Compose
@@ -169,7 +187,7 @@ DB_NAME=todo_db
169
187
170
188
---
171
189
172
-
## 📡 API Endpoints
190
+
## API Endpoints
173
191
174
192
| Method | Endpoint | Description |
175
193
|--------|----------|-------------|
@@ -181,7 +199,7 @@ DB_NAME=todo_db
181
199
182
200
---
183
201
184
-
## ⚙️ Replicating This Pipeline
202
+
## Replicating This Pipeline
185
203
186
204
### GitHub Secrets Required
187
205
@@ -222,7 +240,23 @@ mkdir -p ~/task-manager
222
240
223
241
---
224
242
225
-
## 💡 Lessons Learned
243
+
## Kubernetes Concepts Used
244
+
245
+
**Deployment** — manages desired pod state. If a pod crashes Kubernetes automatically replaces it.
246
+
247
+
**Service** — provides stable DNS names (, , ) so pods can reach each other regardless of their changing IPs.
248
+
249
+
**PersistentVolumeClaim** — requests persistent storage for postgres so data survives pod restarts.
250
+
251
+
**Secret** — stores database credentials as base64-encoded values injected as environment variables — never hardcoded in manifests.
252
+
253
+
**NodePort Service** — exposes the frontend on port 30080 on every node, accessible from outside the cluster.
254
+
255
+
**Readiness + Liveness Probes** — readiness controls when traffic is sent to a pod, liveness restarts pods that stop responding. Both use HTTP checks.
256
+
257
+
---
258
+
259
+
## Lessons Learned
226
260
227
261
**From migrating Jenkins → GitHub Actions:**
228
262
- Pipeline concepts are identical across tools — triggers, jobs, steps, secrets. Learning one makes the next trivial.
@@ -231,29 +265,42 @@ mkdir -p ~/task-manager
231
265
- `needs:`in GitHub Actions is more explicit than Jenkins stage ordering — you declare job dependencies intentionally.
232
266
- A running container might be from an old image — always verify the tag matches your latest build number before debugging.
233
267
268
+
**From migrating to Kubernetes:**
269
+
- `kubectl apply`succeeds even if the pod crashes seconds later — always verify with `kubectl wait`
270
+
- Pod ready does not mean application ready — use `pg_isready` not just Kubernetes readiness probes
271
+
- `-it`flags don't work in CI pipelines — no TTY available, always remove from `kubectl exec` in automation
272
+
- Kubernetes service names are DNS — containers reach each other by service name, not IP
273
+
- `kubectl describe pod`is more useful than `kubectl logs` when a pod won't start
274
+
234
275
**Carried over from Jenkins version:**
235
276
- Always use relative URLs in React — `localhost` in fetch calls breaks in production
236
277
- `docker compose ps`showing `Up` is not the same as healthy — always add health checks
237
278
- Env vars with duplicate keys in JS objects silently use the last value — never hardcode credentials
238
279
239
280
---
240
281
241
-
## 📌 Improvements
242
-
243
-
### ✅ Completed
244
-
- [x] Migrated from Jenkins to GitHub Actions
282
+
### Completed
283
+
- [x] Migrated from Jenkins to GitHub Actions — zero CI server overhead
245
284
- [x] Automated database table creation via `docker-entrypoint-initdb.d/`
246
285
- [x] Docker health checks on all services
247
286
- [x] Pinned image tags — exact build number deployed, never `:latest`
248
-
249
-
### 🔜 Up Next
250
-
- [ ] Provision EC2 infrastructure with Terraform
287
+
- [x] Kubernetes manifests for all three services
288
+
- [x] CI pipeline deploys to real kind cluster on every push
0 commit comments