Skip to content

Commit 3ddd08a

Browse files
committed
docs(k8s): add lab solution
1 parent d7fe44a commit 3ddd08a

4 files changed

Lines changed: 343 additions & 0 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,3 +398,4 @@ terraform.rc
398398
*.pem
399399
/aws/
400400
test
401+
.github/agents/*

labs/k8s/README.md

Lines changed: 282 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
1+
# Lab09: Minikube
2+
3+
## Task 1-3
4+
5+
- [deployment.yaml](./deployment.yaml) (replicas 3)
6+
- [service.yaml](./service.yaml)
7+
8+
Usage:
9+
10+
```sh
11+
kubectl apply -f labs/k8s/deployment.yaml
12+
kubectl get deployment python-info-service
13+
kubectl describe deployment python-info-service
14+
kubectl get pods -l app=python-info-service
15+
```
16+
17+
```sh
18+
kubectl get deployment python-info-service
19+
NAME READY UP-TO-DATE AVAILABLE AGE
20+
python-info-service 3/3 3 3 11m
21+
22+
kubectl describe deployment python-info-service
23+
Name: python-info-service
24+
Namespace: default
25+
CreationTimestamp: Thu, 26 Mar 2026 21:33:30 +0300
26+
Labels: app=python-info-service
27+
Annotations: deployment.kubernetes.io/revision: 1
28+
Selector: app=python-info-service
29+
Replicas: 3 desired | 3 updated | 3 total | 3 available | 0 unavailable
30+
StrategyType: RollingUpdate
31+
MinReadySeconds: 0
32+
RollingUpdateStrategy: 0 max unavailable, 1 max surge
33+
Pod Template:
34+
Labels: app=python-info-service
35+
Containers:
36+
python-info-service:
37+
Image: projacktor/python-info-service:latest
38+
Port: 8080/TCP
39+
Host Port: 0/TCP
40+
Limits:
41+
cpu: 200m
42+
memory: 256Mi
43+
Requests:
44+
cpu: 100m
45+
memory: 128Mi
46+
Liveness: tcp-socket :8080 delay=15s timeout=2s period=10s #success=1 #failure=3
47+
Readiness: tcp-socket :8080 delay=5s timeout=2s period=5s #success=1 #failure=3
48+
Environment: <none>
49+
Mounts: <none>
50+
Volumes: <none>
51+
Node-Selectors: <none>
52+
Tolerations: <none>
53+
Conditions:
54+
Type Status Reason
55+
---- ------ ------
56+
Available True MinimumReplicasAvailable
57+
Progressing True NewReplicaSetAvailable
58+
OldReplicaSets: <none>
59+
NewReplicaSet: python-info-service-85d87fc579 (3/3 replicas created)
60+
Events:
61+
Type Reason Age From Message
62+
---- ------ ---- ---- -------
63+
Normal ScalingReplicaSet 11m deployment-controller Scaled up replica set python-info-service-85d87fc579 from 0 to 3
64+
65+
kubectl get pods -l app=python-info-service
66+
NAME READY STATUS RESTARTS AGE
67+
python-info-service-85d87fc579-rjbx8 1/1 Running 0 11m
68+
python-info-service-85d87fc579-sxbp5 1/1 Running 0 11m
69+
python-info-service-85d87fc579-zq7gd 1/1 Running 0 11m
70+
```
71+
72+
```sh
73+
projacktor@projacktorLaptop ~/P/e/DevOps-Core-Course (lab9)> kubectl apply -f labs/k8s/service.yaml
74+
service/python-info-service created
75+
projacktor@projacktorLaptop ~/P/e/DevOps-Core-Course (lab9)> kubectl get svc -n default
76+
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
77+
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 23h
78+
python-info-service NodePort 10.104.130.60 <none> 8080:30080/TCP 6s
79+
projacktor@projacktorLaptop ~/P/e/DevOps-Core-Course (lab9)> kubectl get svc python-info-service -n default
80+
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
81+
python-info-service NodePort 10.104.130.60 <none> 8080:30080/TCP 16s
82+
projacktor@projacktorLaptop ~/P/e/DevOps-Core-Course (lab9)> minikube service python-info-service --url
83+
http://192.168.49.2:30080
84+
projacktor@projacktorLaptop ~/P/e/DevOps-Core-Course (lab9)> kubectl get services
85+
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
86+
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 23h
87+
python-info-service NodePort 10.104.130.60 <none> 8080:30080/TCP 68s
88+
projacktor@projacktorLaptop ~/P/e/DevOps-Core-Course (lab9)> kubectl get services python-info-service
89+
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
90+
python-info-service NodePort 10.104.130.60 <none> 8080:30080/TCP 79s
91+
projacktor@projacktorLaptop ~/P/e/DevOps-Core-Course (lab9)> kubectl get endpoints
92+
Warning: v1 Endpoints is deprecated in v1.33+; use discovery.k8s.io/v1 EndpointSlice
93+
NAME ENDPOINTS AGE
94+
kubernetes 192.168.49.2:8443 23h
95+
python-info-service 10.244.0.4:8080,10.244.0.5:8080,10.244.0.6:8080 85s
96+
```
97+
98+
## Task 4
99+
100+
Increase replicas:
101+
102+
```sh
103+
kubectl scale deployment/python-info-service --replicas=5
104+
105+
deployment.apps/python-info-service scaled
106+
107+
kubectl get pods -w
108+
NAME READY STATUS RESTARTS AGE
109+
python-info-service-85d87fc579-c8z74 1/1 Running 0 33s
110+
python-info-service-85d87fc579-nqxrh 1/1 Running 0 33s
111+
python-info-service-85d87fc579-rjbx8 1/1 Running 0 16m
112+
python-info-service-85d87fc579-sxbp5 1/1 Running 0 16m
113+
python-info-service-85d87fc579-zq7gd 1/1 Running 0 16m
114+
115+
116+
^C⏎
117+
118+
kubectl rollout status deployment/python-info-service
119+
deployment "python-info-service" successfully rolled out
120+
```
121+
122+
## Task 5
123+
124+
### Architecture Overview
125+
126+
- Cluster: minikube (single-node local Kubernetes)
127+
- Workload: Deployment/python-info-service
128+
- Replicas: 3 (scaled to 5 in Task 4)
129+
- Networking: Service/python-info-service type NodePort (8080 -> 30080)
130+
- Traffic flow: Client -> NodeIP:30080 -> Service -> Pod:8080
131+
- Resources per Pod:
132+
- requests: 100m CPU, 128Mi RAM
133+
- limits: 200m CPU, 256Mi RAM
134+
135+
### Manifest Files
136+
137+
- deployment.yaml
138+
- replicas: 3
139+
- RollingUpdate strategy (maxSurge: 1, maxUnavailable: 0)
140+
- livenessProbe/readinessProbe via TCP 8080
141+
- resources.requests/limits set for predictable scheduling
142+
- service.yaml
143+
- type: NodePort
144+
- selector app: python-info-service
145+
- port: 8080, targetPort: 8080, nodePort: 30080
146+
147+
### Deployment Evidence
148+
```sh
149+
kubectl get all
150+
kubectl get all
151+
NAME READY STATUS RESTARTS AGE
152+
pod/python-info-service-85d87fc579-c8z74 1/1 Running 0 5m34s
153+
pod/python-info-service-85d87fc579-nqxrh 1/1 Running 0 5m34s
154+
pod/python-info-service-85d87fc579-rjbx8 1/1 Running 0 21m
155+
pod/python-info-service-85d87fc579-sxbp5 1/1 Running 0 21m
156+
pod/python-info-service-85d87fc579-zq7gd 1/1 Running 0 21m
157+
158+
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
159+
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 23h
160+
service/python-info-service NodePort 10.104.130.60 <none> 8080:30080/TCP 14m
161+
162+
NAME READY UP-TO-DATE AVAILABLE AGE
163+
deployment.apps/python-info-service 5/5 5 5 21m
164+
165+
NAME DESIRED CURRENT READY AGE
166+
replicaset.apps/python-info-service-85d87fc579 5 5 5 21m
167+
```
168+
169+
```sh
170+
kubectl get pods,svc -o wide
171+
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
172+
pod/python-info-service-85d87fc579-c8z74 1/1 Running 0 5m56s 10.244.0.8 minikube <none> <none>
173+
pod/python-info-service-85d87fc579-nqxrh 1/1 Running 0 5m56s 10.244.0.7 minikube <none> <none>
174+
pod/python-info-service-85d87fc579-rjbx8 1/1 Running 0 22m 10.244.0.4 minikube <none> <none>
175+
pod/python-info-service-85d87fc579-sxbp5 1/1 Running 0 22m 10.244.0.6 minikube <none> <none>
176+
pod/python-info-service-85d87fc579-zq7gd 1/1 Running 0 22m 10.244.0.5 minikube <none> <none>
177+
178+
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
179+
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 23h <none>
180+
service/python-info-service NodePort 10.104.130.60 <none> 8080:30080/TCP 14m app=python-info-service
181+
```
182+
```sh
183+
kubectl describe deployment python-info-service
184+
Name: python-info-service
185+
Namespace: default
186+
CreationTimestamp: Thu, 26 Mar 2026 21:33:30 +0300
187+
Labels: app=python-info-service
188+
Annotations: deployment.kubernetes.io/revision: 1
189+
Selector: app=python-info-service
190+
Replicas: 5 desired | 5 updated | 5 total | 5 available | 0 unavailable
191+
StrategyType: RollingUpdate
192+
MinReadySeconds: 0
193+
RollingUpdateStrategy: 0 max unavailable, 1 max surge
194+
Pod Template:
195+
Labels: app=python-info-service
196+
Containers:
197+
python-info-service:
198+
Image: projacktor/python-info-service:latest
199+
Port: 8080/TCP
200+
Host Port: 0/TCP
201+
Limits:
202+
cpu: 200m
203+
memory: 256Mi
204+
Requests:
205+
cpu: 100m
206+
memory: 128Mi
207+
Liveness: tcp-socket :8080 delay=15s timeout=2s period=10s #success=1 #failure=3
208+
Readiness: tcp-socket :8080 delay=5s timeout=2s period=5s #success=1 #failure=3
209+
Environment: <none>
210+
Mounts: <none>
211+
Volumes: <none>
212+
Node-Selectors: <none>
213+
Tolerations: <none>
214+
Conditions:
215+
Type Status Reason
216+
---- ------ ------
217+
Progressing True NewReplicaSetAvailable
218+
Available True MinimumReplicasAvailable
219+
OldReplicaSets: <none>
220+
NewReplicaSet: python-info-service-85d87fc579 (5/5 replicas created)
221+
Events:
222+
Type Reason Age From Message
223+
---- ------ ---- ---- -------
224+
Normal ScalingReplicaSet 22m deployment-controller Scaled up replica set python-info-service-85d87fc579 from 0 to 3
225+
Normal ScalingReplicaSet 6m26s deployment-controller Scaled up replica set python-info-service-85d87fc579 from 3 to 5
226+
```
227+
228+
#### App accessibility check
229+
```sh
230+
curl http://192.168.49.2:30080/
231+
{"service":{"name":"devops-info-service","version":"1.0.0","description":"DevOps course info service","framework":"FastAPI"},"system":{"hostname":"python-info-service-85d87fc579-nqxrh","platform":"Linux","platform_version":"#37~24.04.1-Ubuntu SMP PREEMPT_DYNAMIC Thu Nov 20 10:25:38 UTC 2","architecture":"x86_64","cpu_count":16,"python_version":"3.12.13"},"runtime":{"uptime_seconds":428,"uptime_human":"0 hours, 7 minutes","current_time":"2026-03-26T18:56:57.746777","timezone":"UTC"},"request":{"client_ip":"10.244.0.1","user_agent":"curl/8.5.0","method":"GET","path":"/"},"endpoints":[{"path":"/","method":"GET","description":"Service information"},{"path":"/health","method":"GET","description":"Health check"}]}⏎
232+
```
233+
234+
235+
### Operations Performed
236+
237+
#### Deploy
238+
- ```kubectl apply -f labs/k8s/deployment.yaml```
239+
- ```kubectl apply -f labs/k8s/service.yaml```
240+
241+
#### Scale
242+
- ```kubectl scale deployment/python-info-service --replicas=5```
243+
- ```kubectl rollout status deployment/python-info-service```
244+
245+
#### Rolling update
246+
- Performed by changing runtime config:
247+
- ```kubectl set env deployment/python-info-service RELEASE=lab09-update```
248+
- Verified:
249+
- ```kubectl rollout status deployment/python-info-service```
250+
- ```kubectl rollout history deployment/python-info-service```
251+
252+
#### Rollback
253+
- ```kubectl rollout undo deployment/python-info-service```
254+
- Verified:
255+
- ```kubectl rollout status deployment/python-info-service```
256+
- ```kubectl rollout history deployment/python-info-service```
257+
258+
### Production Considerations
259+
260+
- Health checks:
261+
- readiness probe prevents routing traffic to not-ready Pods
262+
- liveness probe restarts hung container
263+
- Resource limits:
264+
- protect node from noisy-neighbor effect
265+
- guarantee minimum CPU/memory via requests
266+
- Production improvements:
267+
- use fixed image tags (not latest)
268+
- add startupProbe for slow starts
269+
- add HPA + metrics-server
270+
- central logging/monitoring (Prometheus + Grafana, Loki/ELK)
271+
272+
### Challenges & Solutions
273+
274+
- Issue: Service not found in minikube
275+
- Cause: wrong kubectl context/namespace
276+
- Fix: kubectl config use-context minikube, then re-apply manifests
277+
- Issue: Deployment initially showed 0/3 available
278+
- Cause: probes and startup delay
279+
- Fix: waited for readiness and checked with kubectl rollout status
280+
- Learning:
281+
- Kubernetes is declarative; actual state converges asynchronously
282+
- Labels/selectors are critical for Service-to-Pod routing

labs/k8s/deployment.yaml

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
apiVersion: apps/v1
2+
kind: Deployment
3+
metadata:
4+
name: python-info-service
5+
labels:
6+
app: python-info-service
7+
spec:
8+
replicas: 3
9+
strategy:
10+
type: RollingUpdate
11+
rollingUpdate:
12+
maxSurge: 1
13+
maxUnavailable: 0
14+
selector:
15+
matchLabels:
16+
app: python-info-service
17+
template:
18+
metadata:
19+
labels:
20+
app: python-info-service
21+
spec:
22+
containers:
23+
- name: python-info-service
24+
image: projacktor/python-info-service:latest
25+
ports:
26+
- containerPort: 8080
27+
resources:
28+
requests:
29+
cpu: "100m"
30+
memory: "128Mi"
31+
limits:
32+
cpu: "200m"
33+
memory: "256Mi"
34+
livenessProbe:
35+
tcpSocket:
36+
port: 8080
37+
initialDelaySeconds: 15
38+
periodSeconds: 10
39+
timeoutSeconds: 2
40+
failureThreshold: 3
41+
readinessProbe:
42+
tcpSocket:
43+
port: 8080
44+
initialDelaySeconds: 5
45+
periodSeconds: 5
46+
timeoutSeconds: 2
47+
failureThreshold: 3

labs/k8s/service.yaml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
apiVersion: v1
2+
kind: Service
3+
metadata:
4+
name: python-info-service
5+
spec:
6+
type: NodePort
7+
selector:
8+
app: python-info-service
9+
ports:
10+
- protocol: TCP
11+
port: 8080
12+
targetPort: 8080
13+
nodePort: 30080

0 commit comments

Comments
 (0)