Browser
↓
DNS name of ALB
↓
AWS Application Load Balancer
↓
Ingress routing rule
↓
Kubernetes Service
↓
Pod
↓
Container
↓
EKS Worker Node
↓
EC2 in Node Group
One important correction before the lab: the Ingress resource does not process live requests. The AWS Load Balancer Controller watches the Ingress and configures AWS ALB resources. The ALB then handles the HTTP/HTTPS traffic.
Goal
By the end, students should be able to explain:
EKS Cluster
|
+--- Node Group
| |
| +--- EC2 Worker Node
| |
| +--- Pod
| |
| +--- Container
|
+--- Service
| |
| +--- selects Pods
|
+--- Ingress
|
+--- routing rules
AWS Load Balancer Controller
|
+--- watches Ingress
|
+--- configures AWS ALB
Part 1 — Check the EKS cluster
First:
aws eks list-clusters --region us-east-1
Connect kubectl:
aws eks update-kubeconfig \
--region us-east-1 \
--name YOUR_CLUSTER_NAME
Test:
kubectl get nodes
If you see:
NAME STATUS ROLES
ip-172-31-x-x.ec2.internal Ready <none>
you are connected.
Part 2 — Understand the Node Group
This is important before Deployment.
Run:
aws eks list-nodegroups \
--cluster-name YOUR_CLUSTER_NAME \
--region us-east-1
Then:
kubectl get nodes -o wide
Explain:
EKS CONTROL PLANE
AWS manages
|
|
↓
NODE GROUP
|
+------------------+
| |
↓ ↓
EC2 Worker 1 EC2 Worker 2
| |
Pods Pods
Node Group = group of worker EC2 instances managed together.
The Node Group does not contain the Deployment in the sense of ownership.
Instead:
Deployment
↓
ReplicaSet
↓
Pod
↓ scheduled by Kubernetes
Worker Node
Check where each Pod is running later with:
kubectl get pods -o wide
Part 3 — Create namespace
Let's keep our lab isolated:
kubectl create namespace jumptotech
Set it as default for this context if desired:
kubectl config set-context --current --namespace=jumptotech
Check:
kubectl get pods
Part 4 — Deployment
Create:
touch deployment.yaml
Use your ECR image. For example:
apiVersion: apps/v1
kind: Deployment
metadata:
name: rose-deployment
spec:
replicas: 3
selector:
matchLabels:
app: rose
template:
metadata:
labels:
app: rose
spec:
containers:
- name: rose-container
image: ACCOUNT_ID.dkr.ecr.us-east-1.amazonaws.com/jumptotech-lab:latest
ports:
- containerPort: 80
Replace ACCOUNT_ID with yours.
Apply:
kubectl apply -f deployment.yaml
Check:
kubectl get deployments
Then:
kubectl get rs
Then:
kubectl get pods -o wide
Stop here and explain:
rose-deployment
↓
ReplicaSet
↓
+------+------+------+
| | |
↓ ↓ ↓
Pod 1 Pod 2 Pod 3
|
↓
Worker Node
Students should notice the NODE column from:
kubectl get pods -o wide
Part 5 — Prove Pod ownership
Pick a Pod:
kubectl describe pod POD_NAME
Look for:
Controlled By: ReplicaSet/rose-deployment-xxxxxxxx
Then:
kubectl describe rs REPLICASET_NAME
Look for:
Controlled By: Deployment/rose-deployment
Now students have proved:
Deployment
↓ owns
ReplicaSet
↓ owns
Pod
Part 6 — Create Service
Create:
touch service.yaml
Add:
apiVersion: v1
kind: Service
metadata:
name: rose-service
spec:
type: ClusterIP
selector:
app: rose
ports:
- protocol: TCP
port: 80
targetPort: 80
Apply:
kubectl apply -f service.yaml
Check:
kubectl get svc
Then:
kubectl describe svc rose-service
And:
kubectl get endpoints rose-service
This is an excellent teaching moment.
You should see Pod backend IPs.
Explain:
rose-service
selector:
app=rose
|
|
+----------------+
| | |
↓ ↓ ↓
Pod 1 Pod 2 Pod 3
app=rose app=rose app=rose
The Service doesn't own them.
It selects them.
Part 7 — Test Service before adding ALB
This is important troubleshooting practice.
Run:
kubectl port-forward service/rose-service 8080:80
Then open:
http://localhost:8080
If your application appears, you have proved:
Deployment ✓
Pods ✓
Container ✓
Service ✓
Therefore, if ALB later doesn't work, you already know the backend application works.
Stop port-forward with:
Ctrl+C
Part 8 — Why can't we create Ingress yet?
Here's the important question.
If we simply create:
kind: Ingress
who is going to implement it?
Ingress is essentially configuration/routing intent.
We need an Ingress implementation/controller.
For AWS ALB, that's normally:
AWS Load Balancer Controller.
Architecture:
Kubernetes API
|
Ingress object
|
↓
AWS Load Balancer Controller
|
| AWS APIs
↓
AWS
|
↓
ALB
Part 9 — Check whether AWS Load Balancer Controller exists
Run:
kubectl get deployment \
-n kube-system \
aws-load-balancer-controller
If installed, you should see something like:
NAME READY
aws-load-balancer-controller 2/2
Also:
kubectl get pods -n kube-system
Look for:
aws-load-balancer-controller-xxxxx
aws-load-balancer-controller-yyyyy
If it's already installed, do not reinstall it.
Part 10 — If Controller is NOT installed
For class, explain the prerequisites before blindly installing it.
The controller needs permission to call AWS APIs:
AWS Load Balancer Controller
|
| AWS API
↓
ELBv2 / EC2 APIs
|
↓
Create/configure:
ALB
Target Groups
Listeners
Rules
Security Groups
In a standard EKS setup this means configuring the controller's AWS permissions, commonly through an IAM role associated with its Kubernetes service account (IRSA), or another supported EKS pod identity mechanism.
Installation details can change, so for the actual installation commands use the current AWS/EKS documentation rather than having students copy an old IAM policy blindly.
The key thing they need to understand is:
Controller
+
AWS permissions
=
ability to manage ALB resources
Part 11 — Verify IngressClass
After the controller is installed:
kubectl get ingressclass
You want to see an ALB class available, typically:
NAME CONTROLLER
alb ingress.k8s.aws/alb
Part 12 — Create Ingress
Create:
touch ingress.yaml
Add:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: rose-ingress
annotations:
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/target-type: ip
spec:
ingressClassName: alb
rules:
- http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: rose-service
port:
number: 80
Apply:
kubectl apply -f ingress.yaml
Part 13 — Watch the magic happen
Run:
kubectl get ingress
Initially you may see:
NAME CLASS HOSTS ADDRESS
rose-ingress alb *
Wait a little and run again:
kubectl get ingress
Eventually ADDRESS should contain the ALB DNS name if provisioning succeeds.
You can watch:
kubectl get ingress -w
Part 14 — What actually happened?
This is where I would stop typing commands and draw the architecture.
When you executed:
kubectl apply -f ingress.yaml
you did not directly create an ALB.
You created:
Ingress object
in Kubernetes.
Then:
ingress.yaml
↓
API Server
↓
Ingress object
↓
AWS Load Balancer Controller
↓
sees desired configuration
↓
calls AWS APIs
↓
ALB + Target Group + Listener/rules
That's what a controller does.
It continually tries to make reality match desired state.
Part 15 — Check the Controller logs
This is a great demonstration:
kubectl logs \
-n kube-system \
deployment/aws-load-balancer-controller
If there is an ALB problem, this is one of the places to investigate.
You can also:
kubectl describe ingress rose-ingress
Look at:
Rules
Annotations
Backend
Events
Part 16 — Open the ALB
Get:
kubectl get ingress
You'll see an ADDRESS.
Open that ALB DNS name in the browser.
Now the complete request flow is:
USER
|
| HTTP request
↓
ALB DNS NAME
|
↓
APPLICATION LOAD
BALANCER
|
routing rule
|
↓
rose-service
|
backend endpoints
|
+-------------+-------------+
| | |
↓ ↓ ↓
Pod 1 Pod 2 Pod 3
| | |
↓ ↓ ↓
Container Container Container
With alb.ingress.kubernetes.io/target-type: ip, the AWS target group can target Pod IPs directly, so don't teach students that traffic must always traverse NodePort.
Part 17 — Where is Ingress in the live request?
This is the subtle concept I want your students to get right.
Don't draw:
User → ALB → Ingress object → Controller → Service
as though every HTTP request physically passes through the YAML object/controller.
Instead:
Configuration/control flow
Ingress
↓
AWS Load Balancer Controller
↓
ALB configuration
Runtime traffic
Conceptually:
User
↓
ALB
↓
configured backend
↓
Service/Pod backend model
↓
Pod
The controller is not sitting in the middle forwarding every customer request.
Part 18 — Path-based routing
Now make the lab more interesting.
Imagine:
/
→ frontend-service
/api
→ api-service
/admin
→ admin-service
Ingress could contain:
rules:
- http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: api-service
port:
number: 80
- path: /admin
pathType: Prefix
backend:
service:
name: admin-service
port:
number: 80
- path: /
pathType: Prefix
backend:
service:
name: frontend-service
port:
number: 80
Then:
ALB
|
+------------+------------+
| | |
↓ ↓ ↓
/ /api /admin
↓ ↓ ↓
frontend API admin
service service service
↓ ↓ ↓
Pods Pods Pods
That's where your earlier GitHub-style /actions analogy becomes useful.
Part 19 — Break it intentionally 🔥
Now teach troubleshooting.
Change Service:
selector:
app: WRONG
Apply:
kubectl apply -f service.yaml
Check:
kubectl get endpoints rose-service
You may now have no endpoints.
Ask students:
ALB exists. Ingress exists. Service exists. Why doesn't the website work?
Because:
Service
selector app=WRONG
↓
No matching Pods
↓
No healthy application backend
Fix:
selector:
app: rose
Then:
kubectl apply -f service.yaml
Check again:
kubectl get endpoints rose-service
This is a much better lesson than only deploying a working configuration.
Part 20 — Full troubleshooting workflow
Tell students:
Follow the request. Don't randomly type commands.
Start:
kubectl get ingress
Then:
kubectl describe ingress rose-ingress
Then:
kubectl get svc
Then:
kubectl describe svc rose-service
Then:
kubectl get endpoints rose-service
Then:
kubectl get pods -o wide
Then:
kubectl describe pod POD_NAME
Finally:
kubectl logs POD_NAME
Think:
Browser doesn't work
↓
Does ALB exist?
↓
Does Ingress show ADDRESS/events?
↓
Is Controller healthy?
↓
Does Service exist?
↓
Does Service have endpoints?
↓
Are Pods READY?
↓
Is targetPort correct?
↓
Is container listening?
↓
Check application logs
Part 21 — Node Group vs Service vs Ingress
Students often mix these up.
Make them memorize the responsibilities:
| Component | Job |
|---|---|
| Node Group | Provides/manages worker EC2 capacity |
| Node | Machine where Pods run |
| Deployment | Declares/manages desired application replicas and rollout |
| ReplicaSet | Maintains the requested Pod replicas |
| Pod | Runs application container(s) |
| Service | Stable Kubernetes abstraction/selects backend Pods |
| Ingress | Declares HTTP/HTTPS routing rules |
| AWS Load Balancer Controller | Reconciles Kubernetes resources with AWS load-balancer resources |
| ALB | Handles actual Layer-7 HTTP/HTTPS traffic |
Final architecture to draw on the board
I would draw two diagrams, not one.
Kubernetes/AWS resources
EKS CLUSTER
┌───────────────────────────┐
│ │
│ Deployment │
│ ↓ │
│ ReplicaSet │
│ ↓ │
│ Pods │
│ ↓ ↓ ↓ │
│ Worker Nodes │
│ ↑ │
│ Node Group │
│ │
│ Service → Pods │
│ │
│ Ingress │
│ ↓ watched by │
│ AWS LB Controller │
│ │
└────────────┬──────────────┘
|
| AWS API
↓
AWS ALB
Customer request
Customer
|
| GET /
↓
DNS
↓
AWS ALB
|
| Layer 7 routing
↓
Backend for rose-service
↓
Pod
↓
Container
↓
Application
And finish by asking students:
Who creates Pods?
ReplicaSet, under the Deployment's control.
Who chooses which Node runs a Pod?
Kubernetes scheduler.
What provides the worker machines?
Node Group.
How does Service know its Pods?
Labels + selector, which Kubernetes turns into backend endpoint information.
What is Ingress?
Desired HTTP/HTTPS routing configuration.
What does AWS Load Balancer Controller do?
Watches/reconciles Kubernetes resources and configures AWS load-balancing infrastructure.
Who handles the actual customer's HTTP request?
ALB and the configured data path to the application backend.
Top comments (0)