DEV Community

Aisalkyn Aidarova
Aisalkyn Aidarova

Posted on

JumpToTech — Production Kubernetes Complete Lab

Level: Beginner → Intermediate
Platform: AWS EKS
Duration: ~3 hours
Language: English
Goal: Build a production-style Kubernetes deployment from scratch and troubleshoot it.


1. What We Will Build

By the end of the lab:

                         INTERNET
                            │
                            ▼
                           ALB
                            │
                            ▼
                         INGRESS
                            │
                            ▼
                         SERVICE
                       ClusterIP :80
                            │
                    selector: app=web
                            │
              ┌─────────────┼─────────────┐
              ▼             ▼             ▼
            POD 1          POD 2         POD 3
              │             │             │
          Container     Container     Container
              │             │             │
              └─────────────┼─────────────┘
                            ▲
                        ReplicaSet
                            ▲
                            │
                        Deployment
                            │
             ┌──────────────┼───────────────┐
             │              │               │
         ConfigMap        Secret      ServiceAccount

Additional protection:

Requests/Limits → Resource management
Readiness       → Traffic readiness
Liveness        → Container health
Startup Probe   → Startup protection
PDB             → Disruption availability
HPA             → Automatic scaling
NetworkPolicy   → Network restrictions
RBAC            → Kubernetes authorization
Enter fullscreen mode Exit fullscreen mode

Everything will be deployed into:

jumptotech-prod
Enter fullscreen mode Exit fullscreen mode

2. Project Structure

Create the project:

mkdir production-k8s-lab
cd production-k8s-lab
Enter fullscreen mode Exit fullscreen mode

Our final directory:

production-k8s-lab/
│
├── 01-namespace.yaml
├── 02-configmap.yaml
├── 03-secret.yaml
├── 04-serviceaccount.yaml
├── 05-deployment.yaml
├── 06-service.yaml
├── 07-pdb.yaml
├── 08-hpa.yaml
├── 09-ingress.yaml
├── 10-networkpolicy.yaml
├── 11-role.yaml
└── 12-rolebinding.yaml
Enter fullscreen mode Exit fullscreen mode

This numbering is intentional because students can apply resources in a logical order.


3. Verify the Cluster

Before touching YAML:

kubectl get nodes
Enter fullscreen mode Exit fullscreen mode

Expected:

NAME                              STATUS
ip-172-31-10-20.ec2.internal     Ready
ip-172-31-20-30.ec2.internal     Ready
Enter fullscreen mode Exit fullscreen mode

Explain:

A Kubernetes cluster contains a Control Plane and Worker Nodes. On EKS, AWS manages the control plane. Our application workloads normally run on worker nodes.

Check where existing Pods are running:

kubectl get pods -A -o wide
Enter fullscreen mode Exit fullscreen mode

Architecture:

EKS
│
├── AWS-managed Control Plane
│
└── Worker Nodes
      │
      ├── kubelet
      ├── container runtime
      └── Pods
Enter fullscreen mode Exit fullscreen mode

4. Namespace

Create:

nano 01-namespace.yaml
Enter fullscreen mode Exit fullscreen mode
apiVersion: v1
kind: Namespace

metadata:
  name: jumptotech-prod

  labels:
    environment: production
    team: jumptotech
Enter fullscreen mode Exit fullscreen mode

Apply:

kubectl apply -f 01-namespace.yaml
Enter fullscreen mode Exit fullscreen mode

Verify:

kubectl get namespaces
Enter fullscreen mode Exit fullscreen mode

What is a Namespace?

A Namespace creates a logical scope inside a cluster.

EKS Cluster
│
├── default
│
├── kube-system
│
├── dev
│
├── test
│
└── jumptotech-prod
Enter fullscreen mode Exit fullscreen mode

It does not create another Kubernetes cluster.

It helps organize and scope resources, permissions, quotas, names, and policies.

For the rest of the lab:

kubectl config set-context --current --namespace=jumptotech-prod
Enter fullscreen mode Exit fullscreen mode

Check:

kubectl config view --minify | grep namespace
Enter fullscreen mode Exit fullscreen mode

Now our commands default to jumptotech-prod.


5. ConfigMap

Applications require configuration.

For example:

APP_NAME
APP_ENV
LOG_LEVEL
Enter fullscreen mode Exit fullscreen mode

These should not necessarily be hard-coded into the Docker image.

Create:

nano 02-configmap.yaml
Enter fullscreen mode Exit fullscreen mode
apiVersion: v1
kind: ConfigMap

metadata:
  name: web-config
  namespace: jumptotech-prod

data:
  APP_NAME: "JumpToTech"
  APP_ENV: "production"
  LOG_LEVEL: "info"
Enter fullscreen mode Exit fullscreen mode

Apply:

kubectl apply -f 02-configmap.yaml
Enter fullscreen mode Exit fullscreen mode

Check:

kubectl get configmap
Enter fullscreen mode Exit fullscreen mode

Then:

kubectl describe configmap web-config
Enter fullscreen mode Exit fullscreen mode

Explain:

Docker Image
    │
    ├── application code
    └── dependencies

ConfigMap
    │
    └── environment-specific
        non-sensitive configuration
Enter fullscreen mode Exit fullscreen mode

6. Secret

Should this go into ConfigMap?

DB_PASSWORD=my-secret-password
Enter fullscreen mode Exit fullscreen mode

No.

Kubernetes provides the Secret resource.

Create:

nano 03-secret.yaml
Enter fullscreen mode Exit fullscreen mode

For our lab only:

apiVersion: v1
kind: Secret

metadata:
  name: web-secret
  namespace: jumptotech-prod

type: Opaque

stringData:
  DB_USERNAME: "admin"
  DB_PASSWORD: "student-demo-password"
Enter fullscreen mode Exit fullscreen mode

Apply:

kubectl apply -f 03-secret.yaml
Enter fullscreen mode Exit fullscreen mode

Check:

kubectl get secrets
Enter fullscreen mode Exit fullscreen mode

Do not print real secrets during a production troubleshooting session.

Teach:

ConfigMap
    ↓
Non-sensitive configuration


Secret
    ↓
Sensitive configuration
Enter fullscreen mode Exit fullscreen mode

Important production lesson:

Kubernetes Secret is a Kubernetes object designed for sensitive data, but simply putting a value in a Secret does not make every secret-management problem disappear.

And:

Base64 ≠ Encryption
Enter fullscreen mode Exit fullscreen mode

For production GitOps, do not commit real plaintext passwords like this lab example.


7. ServiceAccount

Our application should have a Kubernetes workload identity.

Create:

nano 04-serviceaccount.yaml
Enter fullscreen mode Exit fullscreen mode
apiVersion: v1
kind: ServiceAccount

metadata:
  name: web-sa
  namespace: jumptotech-prod
Enter fullscreen mode Exit fullscreen mode

Apply:

kubectl apply -f 04-serviceaccount.yaml
Enter fullscreen mode Exit fullscreen mode

Check:

kubectl get serviceaccount
Enter fullscreen mode Exit fullscreen mode

Teach:

ServiceAccount
      ↓
WHO AM I?

RBAC
      ↓
WHAT AM I ALLOWED TO DO?
Enter fullscreen mode Exit fullscreen mode

A ServiceAccount does not automatically give the Pod administrator permissions.


8. Production Deployment

Now create the central object.

nano 05-deployment.yaml
Enter fullscreen mode Exit fullscreen mode

Replace YOUR_ECR_IMAGE with your actual ECR image.

apiVersion: apps/v1
kind: Deployment

metadata:
  name: web-deployment
  namespace: jumptotech-prod

  labels:
    app: web

spec:

  replicas: 3

  strategy:
    type: RollingUpdate

    rollingUpdate:
      maxUnavailable: 1
      maxSurge: 1

  selector:
    matchLabels:
      app: web

  template:

    metadata:
      labels:
        app: web
        environment: production

    spec:

      serviceAccountName: web-sa

      terminationGracePeriodSeconds: 30

      containers:

        - name: web-container

          image: YOUR_ECR_IMAGE

          imagePullPolicy: IfNotPresent

          ports:
            - name: http
              containerPort: 80
              protocol: TCP

          envFrom:
            - configMapRef:
                name: web-config

          env:

            - name: DB_USERNAME
              valueFrom:
                secretKeyRef:
                  name: web-secret
                  key: DB_USERNAME

            - name: DB_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: web-secret
                  key: DB_PASSWORD

          resources:

            requests:
              cpu: "100m"
              memory: "128Mi"

            limits:
              cpu: "500m"
              memory: "256Mi"

          startupProbe:

            httpGet:
              path: /
              port: http

            periodSeconds: 5
            failureThreshold: 12

          readinessProbe:

            httpGet:
              path: /
              port: http

            periodSeconds: 5
            timeoutSeconds: 2
            failureThreshold: 3

          livenessProbe:

            httpGet:
              path: /
              port: http

            periodSeconds: 10
            timeoutSeconds: 2
            failureThreshold: 3
Enter fullscreen mode Exit fullscreen mode

Don't apply yet.

Explain the important pieces first.


9. Deployment → ReplicaSet → Pod

We said:

replicas: 3
Enter fullscreen mode Exit fullscreen mode

This represents desired state.

Deployment
     │
     │ desired replicas = 3
     ▼
ReplicaSet
     │
     ├──────────┬──────────┐
     ▼          ▼          ▼
   Pod 1      Pod 2      Pod 3
Enter fullscreen mode Exit fullscreen mode

Deployment primarily handles things such as:

Desired application state
Rollouts
Rollbacks
ReplicaSet management
Enter fullscreen mode Exit fullscreen mode

ReplicaSet maintains the desired Pod replica count.


10. Labels and Selectors

Our Pod template has:

labels:
  app: web
Enter fullscreen mode Exit fullscreen mode

Deployment selector:

selector:
  matchLabels:
    app: web
Enter fullscreen mode Exit fullscreen mode

Later our Service will also use:

selector:
  app: web
Enter fullscreen mode Exit fullscreen mode

Students should memorize the relationship, not the syntax:

              app=web

Deployment ────────────────┐
                           │
                    ┌──────▼──────┐
                    │    POD      │
                    │             │
                    │ app = web   │
                    └──────▲──────┘
                           │
Service ───────────────────┘
selector app=web
Enter fullscreen mode Exit fullscreen mode

11. Requests

We have:

requests:
  cpu: "100m"
  memory: "128Mi"
Enter fullscreen mode Exit fullscreen mode

When a Pod needs scheduling:

New Pod
   │
   │ requests
   │ CPU: 100m
   │ RAM: 128Mi
   ▼
Scheduler
   │
   ├── Node A
   ├── Node B
   └── Node C
Enter fullscreen mode Exit fullscreen mode

The Scheduler uses requests when determining whether the Pod fits on a Node.

CPU:

1000m = 1 CPU
500m  = 0.5 CPU
250m  = 0.25 CPU
100m  = 0.1 CPU
Enter fullscreen mode Exit fullscreen mode

12. Limits

We have:

limits:
  cpu: "500m"
  memory: "256Mi"
Enter fullscreen mode Exit fullscreen mode

Simple classroom explanation:

REQUEST
   ↓
Scheduler capacity calculation


LIMIT
   ↓
Runtime resource ceiling
Enter fullscreen mode Exit fullscreen mode

If CPU demand exceeds an enforced CPU limit:

CPU
 ↓
throttling
Enter fullscreen mode Exit fullscreen mode

If memory usage exceeds an enforced memory limit:

Memory
  ↓
OOM
  ↓
process/container may be killed
  ↓
OOMKilled
Enter fullscreen mode Exit fullscreen mode

13. Startup Probe

Now we have three probes.

First:

startupProbe:
Enter fullscreen mode Exit fullscreen mode

Question:

Has my application successfully started?

This is especially useful for applications that can take time to initialize.

Container starts
       │
       ▼
 Startup Probe
       │
       ├── failing → still starting
       │
       └── succeeds
               │
               ▼
       Liveness/Readiness
       take over
Enter fullscreen mode Exit fullscreen mode

Our configuration:

periodSeconds: 5
failureThreshold: 12
Enter fullscreen mode Exit fullscreen mode

gives the application roughly up to 60 seconds of startup checking before startup failure reaches the configured threshold.


14. Readiness Probe

readinessProbe:
  httpGet:
    path: /
    port: http
Enter fullscreen mode Exit fullscreen mode

Question:

Should this Pod receive traffic?

When you run:

kubectl get pods
Enter fullscreen mode Exit fullscreen mode

you might see:

NAME                 READY   STATUS
web-abc123            1/1    Running
Enter fullscreen mode Exit fullscreen mode

1/1 means:

1 container Ready
───────────────
1 readiness-counted container in this Pod
Enter fullscreen mode Exit fullscreen mode

If:

0/1 Running
Enter fullscreen mode Exit fullscreen mode

the Pod can still:

Exist             YES
Be Running        YES
Have an IP        YES
Enter fullscreen mode Exit fullscreen mode

but:

Ready             NO
Enter fullscreen mode Exit fullscreen mode

Therefore it should not be used as a normal ready Service backend.


15. Liveness Probe

Question:

Is this application still healthy enough to keep running?

Repeated failures:

Liveness Probe
      │
      ▼
    FAIL
      │
    FAIL
      │
    FAIL
      │
      ▼
failureThreshold reached
      │
      ▼
    kubelet
      │
      ▼
restart container
Enter fullscreen mode Exit fullscreen mode

The important distinction:

READINESS
"Should I receive traffic?"


LIVENESS
"Should this container keep running?"


STARTUP
"Has the application successfully started?"
Enter fullscreen mode Exit fullscreen mode

16. RollingUpdate Strategy

We added:

strategy:
  type: RollingUpdate

  rollingUpdate:
    maxUnavailable: 1
    maxSurge: 1
Enter fullscreen mode Exit fullscreen mode

Suppose:

Current version = v1
Desired version = v2
Enter fullscreen mode Exit fullscreen mode

Kubernetes does not necessarily kill all v1 Pods simultaneously.

Conceptually:

v1   v1   v1

        ↓

v1   v1   v1   v2

        ↓

v1   v1   v2

        ↓

v1   v2   v2

        ↓

v2   v2   v2
Enter fullscreen mode Exit fullscreen mode

maxUnavailable controls how many desired Pods may be unavailable during the update.

maxSurge controls how many extra Pods above the desired replica count may temporarily be created.


17. Apply Deployment

Now:

kubectl apply -f 05-deployment.yaml
Enter fullscreen mode Exit fullscreen mode

Watch:

kubectl get pods -w
Enter fullscreen mode Exit fullscreen mode

Then:

kubectl get deployment
Enter fullscreen mode Exit fullscreen mode
kubectl get rs
Enter fullscreen mode Exit fullscreen mode
kubectl get pods -o wide
Enter fullscreen mode Exit fullscreen mode

Students should physically see:

Deployment
   ↓
ReplicaSet
   ↓
Pods
Enter fullscreen mode Exit fullscreen mode

18. Inspect a Pod

Get a Pod name:

kubectl get pods
Enter fullscreen mode Exit fullscreen mode

Then:

kubectl describe pod POD_NAME
Enter fullscreen mode Exit fullscreen mode

Find:

Node
Labels
IP
Service Account

Container:
  Image
  Port

Requests
Limits

Startup
Readiness
Liveness

Events
Enter fullscreen mode Exit fullscreen mode

This is a very important production troubleshooting command.


19. Verify ConfigMap

kubectl exec POD_NAME -- printenv APP_NAME
Enter fullscreen mode Exit fullscreen mode

Expected:

JumpToTech
Enter fullscreen mode Exit fullscreen mode

Then:

kubectl exec POD_NAME -- printenv APP_ENV
Enter fullscreen mode Exit fullscreen mode

Expected:

production
Enter fullscreen mode Exit fullscreen mode

We have now proved:

ConfigMap
    ↓
Deployment
    ↓
Pod
    ↓
Container
    ↓
Environment Variable
Enter fullscreen mode Exit fullscreen mode

20. Service

Pods are disposable.

Today:

Pod A
10.0.1.10
Enter fullscreen mode Exit fullscreen mode

Tomorrow:

Pod B
10.0.2.25
Enter fullscreen mode Exit fullscreen mode

Applications should not rely on individual Pod IP addresses.

Create:

nano 06-service.yaml
Enter fullscreen mode Exit fullscreen mode
apiVersion: v1
kind: Service

metadata:
  name: web-service
  namespace: jumptotech-prod

spec:

  type: ClusterIP

  selector:
    app: web

  ports:

    - name: http
      protocol: TCP
      port: 80
      targetPort: http
Enter fullscreen mode Exit fullscreen mode

Apply:

kubectl apply -f 06-service.yaml
Enter fullscreen mode Exit fullscreen mode

Check:

kubectl get svc
Enter fullscreen mode Exit fullscreen mode

21. Understand port vs targetPort

We have:

port: 80
targetPort: http
Enter fullscreen mode Exit fullscreen mode

Our named container port is:

ports:
  - name: http
    containerPort: 80
Enter fullscreen mode Exit fullscreen mode

Conceptually:

Client
   │
   ▼
Service
port 80
   │
   ▼
targetPort http
   │
   ▼
Pod
containerPort 80
Enter fullscreen mode Exit fullscreen mode

22. EndpointSlice

Run:

kubectl get pods -o wide
Enter fullscreen mode Exit fullscreen mode

Look at the Pod IPs.

Then:

kubectl get endpointslice \
-l kubernetes.io/service-name=web-service
Enter fullscreen mode Exit fullscreen mode

For more detail:

kubectl get endpointslice \
-l kubernetes.io/service-name=web-service \
-o yaml
Enter fullscreen mode Exit fullscreen mode

Architecture:

Service
   │
selector app=web
   │
   ▼
matching Pods
   │
   ▼
EndpointSlice
Enter fullscreen mode Exit fullscreen mode

This is one of the most useful troubleshooting relationships:

SERVICE HAS NO EXPECTED BACKENDS?

Check:

Service selector
       ↓
Pod labels
       ↓
Pod readiness
       ↓
EndpointSlice
Enter fullscreen mode Exit fullscreen mode

23. Test Service Before ALB

Run:

kubectl port-forward service/web-service 8080:80
Enter fullscreen mode Exit fullscreen mode

Open:

http://localhost:8080
Enter fullscreen mode Exit fullscreen mode

If it works:

Browser
   ↓
localhost:8080
   ↓
port-forward
   ↓
Service
   ↓
Pod
   ↓
Container
Enter fullscreen mode Exit fullscreen mode

This proves the application works before we add Ingress/ALB.


24. Production Troubleshooting Exercise — Break Readiness

Change:

readinessProbe:
  httpGet:
    path: /
Enter fullscreen mode Exit fullscreen mode

to:

readinessProbe:
  httpGet:
    path: /does-not-exist
Enter fullscreen mode Exit fullscreen mode

Apply:

kubectl apply -f 05-deployment.yaml
Enter fullscreen mode Exit fullscreen mode

Watch:

kubectl get pods -w
Enter fullscreen mode Exit fullscreen mode

You should see affected new Pods become:

0/1 Running
Enter fullscreen mode Exit fullscreen mode

Now:

kubectl describe pod POD_NAME
Enter fullscreen mode Exit fullscreen mode

Then:

kubectl get pods -o wide
Enter fullscreen mode Exit fullscreen mode

Show students:

Running: YES
IP:      YES
Ready:   NO
Enter fullscreen mode Exit fullscreen mode

Check:

kubectl get endpointslice \
-l kubernetes.io/service-name=web-service \
-o yaml
Enter fullscreen mode Exit fullscreen mode

Then fix the readiness path:

path: /
Enter fullscreen mode Exit fullscreen mode

and apply again.


25. Production Troubleshooting Exercise — Scheduler

Now break requests:

requests:
  cpu: "100"
  memory: "100Gi"
Enter fullscreen mode Exit fullscreen mode

Apply:

kubectl apply -f 05-deployment.yaml
Enter fullscreen mode Exit fullscreen mode

Check:

kubectl get pods
Enter fullscreen mode Exit fullscreen mode

New Pods may remain:

Pending
Enter fullscreen mode Exit fullscreen mode

Why?

Pod
requests:
100 CPU
100Gi memory
      │
      ▼
Scheduler
      │
      ├── Node 1 ❌
      ├── Node 2 ❌
      └── Node 3 ❌
              │
              ▼
            PENDING
Enter fullscreen mode Exit fullscreen mode

Prove it:

kubectl describe pod POD_NAME
Enter fullscreen mode Exit fullscreen mode

Look at:

Events
Enter fullscreen mode Exit fullscreen mode

You may see:

Insufficient cpu
Insufficient memory
Enter fullscreen mode Exit fullscreen mode

Restore:

requests:
  cpu: "100m"
  memory: "128Mi"
Enter fullscreen mode Exit fullscreen mode

Apply again.


26. PodDisruptionBudget

We have three replicas.

During supported voluntary disruptions, we want to preserve application availability.

Create:

nano 07-pdb.yaml
Enter fullscreen mode Exit fullscreen mode
apiVersion: policy/v1
kind: PodDisruptionBudget

metadata:
  name: web-pdb
  namespace: jumptotech-prod

spec:

  minAvailable: 2

  selector:
    matchLabels:
      app: web
Enter fullscreen mode Exit fullscreen mode

Apply:

kubectl apply -f 07-pdb.yaml
Enter fullscreen mode Exit fullscreen mode

Check:

kubectl get pdb
Enter fullscreen mode Exit fullscreen mode

Then:

kubectl describe pdb web-pdb
Enter fullscreen mode Exit fullscreen mode

Think:

3 replicas

[Pod 1] [Pod 2] [Pod 3]

        PDB

 minAvailable = 2
Enter fullscreen mode Exit fullscreen mode

Important:

PDB does not prevent every crash or Node failure.

It primarily constrains voluntary disruptions that use Kubernetes' eviction mechanisms.


27. Horizontal Pod Autoscaler

Check metrics first:

kubectl top pods
Enter fullscreen mode Exit fullscreen mode

If metrics are available, create:

nano 08-hpa.yaml
Enter fullscreen mode Exit fullscreen mode
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler

metadata:
  name: web-hpa
  namespace: jumptotech-prod

spec:

  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: web-deployment

  minReplicas: 3
  maxReplicas: 10

  behavior:

    scaleDown:
      stabilizationWindowSeconds: 300

  metrics:

    - type: Resource

      resource:

        name: cpu

        target:
          type: Utilization
          averageUtilization: 70
Enter fullscreen mode Exit fullscreen mode

Apply:

kubectl apply -f 08-hpa.yaml
Enter fullscreen mode Exit fullscreen mode

Check:

kubectl get hpa
Enter fullscreen mode Exit fullscreen mode

Watch:

kubectl get hpa -w
Enter fullscreen mode Exit fullscreen mode

Concept:

Metrics
   │
   ▼
 HPA
   │
   ▼
Deployment desired replicas
   │
   ▼
ReplicaSet
   │
   ▼
Pods


Low demand
↓
3 Pods


High CPU demand
↓
HPA
↓
more replicas
↓
up to 10 Pods
Enter fullscreen mode Exit fullscreen mode

Important connection:

For CPU utilization-based HPA, CPU requests matter because utilization is evaluated relative to requested CPU.


28. Ingress + AWS ALB

Before creating it:

kubectl get deployment \
aws-load-balancer-controller \
-n kube-system
Enter fullscreen mode Exit fullscreen mode

If the AWS Load Balancer Controller is installed, create:

nano 09-ingress.yaml
Enter fullscreen mode Exit fullscreen mode
apiVersion: networking.k8s.io/v1
kind: Ingress

metadata:
  name: web-ingress
  namespace: jumptotech-prod

  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: web-service

                port:
                  number: 80
Enter fullscreen mode Exit fullscreen mode

Apply:

kubectl apply -f 09-ingress.yaml
Enter fullscreen mode Exit fullscreen mode

Watch:

kubectl get ingress -w
Enter fullscreen mode Exit fullscreen mode

Eventually you may receive an ALB address.


29. Who Actually Creates the ALB?

This is important.

The flow is:

kubectl apply
       ↓
Ingress object
       ↓
Kubernetes API
       ↓
AWS Load Balancer Controller
       ↓
AWS API
       ↓
ALB
Listeners
Rules
Target Groups
Enter fullscreen mode Exit fullscreen mode

The controller reconciles Kubernetes configuration into AWS infrastructure.

But customer runtime traffic is different:

CUSTOMER
    │
    ▼
   ALB
    │
    ▼
configured targets
    │
    ▼
  PODS
Enter fullscreen mode Exit fullscreen mode

The AWS Load Balancer Controller does not sit in the middle of every HTTP request.


30. NetworkPolicy

Now introduce network security.

Without network policy enforcement, workloads may be able to communicate more broadly than you intend.

Create:

nano 10-networkpolicy.yaml
Enter fullscreen mode Exit fullscreen mode

Example policy:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy

metadata:
  name: web-ingress-policy
  namespace: jumptotech-prod

spec:

  podSelector:
    matchLabels:
      app: web

  policyTypes:
    - Ingress

  ingress:

    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: jumptotech-prod

      ports:
        - protocol: TCP
          port: 80
Enter fullscreen mode Exit fullscreen mode

Explain:

NetworkPolicy
      ↓
Which network connections
are allowed to/from selected Pods?
Enter fullscreen mode Exit fullscreen mode

However, tell students something very important:

Creating a NetworkPolicy object only has an enforcement effect when the cluster's networking implementation supports and enforces Kubernetes NetworkPolicy. Verify your EKS networking configuration before treating this as protection.

Also, this particular example is for teaching namespace-scoped ingress and may need adjustment for the actual ALB traffic path in your cluster. Don't blindly deploy a restrictive policy to production without testing.

For that reason, in class I would inspect this file first, then apply it only if your cluster's NetworkPolicy enforcement and ALB path are already verified.


31. RBAC — Role

ServiceAccount gave us identity.

Now we can demonstrate authorization.

Create:

nano 11-role.yaml
Enter fullscreen mode Exit fullscreen mode
apiVersion: rbac.authorization.k8s.io/v1
kind: Role

metadata:
  name: pod-reader
  namespace: jumptotech-prod

rules:

  - apiGroups:
      - ""

    resources:
      - pods

    verbs:
      - get
      - list
      - watch
Enter fullscreen mode Exit fullscreen mode

This says:

Allowed resource:
Pods

Allowed actions:
GET
LIST
WATCH
Enter fullscreen mode Exit fullscreen mode

It does not say:

delete Pods
create Pods
delete Deployments
Enter fullscreen mode Exit fullscreen mode

32. RoleBinding

Now connect:

ServiceAccount
      +
    Role
Enter fullscreen mode Exit fullscreen mode

Create:

nano 12-rolebinding.yaml
Enter fullscreen mode Exit fullscreen mode
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding

metadata:
  name: web-pod-reader
  namespace: jumptotech-prod

subjects:

  - kind: ServiceAccount
    name: web-sa
    namespace: jumptotech-prod

roleRef:

  apiGroup: rbac.authorization.k8s.io

  kind: Role

  name: pod-reader
Enter fullscreen mode Exit fullscreen mode

Apply:

kubectl apply -f 11-role.yaml
kubectl apply -f 12-rolebinding.yaml
Enter fullscreen mode Exit fullscreen mode

Now teach:

ServiceAccount
web-sa
      │
      ▼
RoleBinding
      │
      ▼
Role
pod-reader
      │
      ▼
get
list
watch
Pods
Enter fullscreen mode Exit fullscreen mode

Test authorization:

kubectl auth can-i list pods \
--as=system:serviceaccount:jumptotech-prod:web-sa \
-n jumptotech-prod
Enter fullscreen mode Exit fullscreen mode

Expected:

yes
Enter fullscreen mode Exit fullscreen mode

Try:

kubectl auth can-i delete pods \
--as=system:serviceaccount:jumptotech-prod:web-sa \
-n jumptotech-prod
Enter fullscreen mode Exit fullscreen mode

Expected:

no
Enter fullscreen mode Exit fullscreen mode

This is a fantastic live demonstration of least privilege.


33. Final Production Verification

Now run:

kubectl get deployment
kubectl get rs
kubectl get pods -o wide
kubectl get svc
kubectl get endpointslice
kubectl get configmap
kubectl get serviceaccount
kubectl get pdb
kubectl get hpa
kubectl get ingress
kubectl get networkpolicy
kubectl get role
kubectl get rolebinding
Enter fullscreen mode Exit fullscreen mode

And:

kubectl get all
Enter fullscreen mode Exit fullscreen mode

Important teaching point:

kubectl get all does not literally mean every Kubernetes resource type.

For example, you still separately inspect objects such as:

kubectl get ingress
kubectl get configmap
kubectl get secrets
kubectl get networkpolicy
kubectl get pdb
Enter fullscreen mode Exit fullscreen mode

34. Production Troubleshooting Method

Teach students not to randomly type commands.

For a website that is unavailable, troubleshoot layer by layer:

Internet
   ↓
ALB
   ↓
Ingress
   ↓
Service
   ↓
EndpointSlice
   ↓
Pod Ready?
   ↓
Container
   ↓
Application
Enter fullscreen mode Exit fullscreen mode

Use:

kubectl get ingress
kubectl describe ingress web-ingress

kubectl get svc
kubectl describe svc web-service

kubectl get endpointslice \
-l kubernetes.io/service-name=web-service

kubectl get pods -o wide

kubectl describe pod POD_NAME

kubectl logs POD_NAME
Enter fullscreen mode Exit fullscreen mode

This troubleshooting order is more important than memorizing 50 kubectl commands.


35. What Each Production Feature Solves

Have students explain this without looking at notes:

Feature Question it answers
Namespace Where are these resources logically scoped?
Deployment What workload state do I want?
ReplicaSet How many matching Pods should exist?
Pod Where does my workload execute?
Service How do clients reach replaceable Pods through a stable service abstraction?
EndpointSlice Which endpoints back the Service?
ConfigMap Where is non-sensitive configuration?
Secret Where is sensitive configuration represented?
ServiceAccount What Kubernetes identity does the workload use?
Role What namespaced Kubernetes API actions are permitted?
RoleBinding Who receives those Role permissions?
Requests What resources should scheduling account for?
Limits What runtime resource ceiling applies?
Startup Probe Has the application started?
Readiness Probe Should it receive traffic?
Liveness Probe Should kubelet restart an unhealthy container?
RollingUpdate How should a Deployment replace versions?
PDB How much availability should voluntary disruptions preserve?
HPA Should replica count scale automatically?
NetworkPolicy Which Pod network traffic should be permitted?
Ingress What HTTP/HTTPS routing should exist?

36. Final Architecture Students Should Be Able to Draw

                              INTERNET
                                  │
                                  ▼
                                 ALB
                                  │
                                  ▼
                               INGRESS
                                  │
                                  ▼
                            web-service
                             ClusterIP
                                  │
                         selector app=web
                                  │
                    ┌─────────────┼─────────────┐
                    ▼             ▼             ▼
                  POD 1         POD 2         POD 3
                    │             │             │
                Container     Container     Container
                    ▲             ▲             ▲
                    └─────────────┼─────────────┘
                                  │
                              ReplicaSet
                                  ▲
                                  │
                              Deployment
                                  │
             ┌────────────────────┼────────────────────┐
             │                    │                    │
             ▼                    ▼                    ▼
         ConfigMap              Secret          ServiceAccount
                                                      │
                                                      ▼
                                               RoleBinding
                                                      │
                                                      ▼
                                                    Role


              ┌───────────────────────────────────────────┐
              │          PRODUCTION CONTROLS              │
              │                                           │
              │ Requests/Limits → Resources               │
              │ Startup        → Startup protection       │
              │ Readiness      → Traffic readiness        │
              │ Liveness       → Health/restart           │
              │ RollingUpdate  → Deployment strategy      │
              │ PDB            → Disruption availability  │
              │ HPA            → Scaling                  │
              │ NetworkPolicy  → Network security         │
              └───────────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

Top comments (0)