Home / Kubernetes · Edit on GitHub

Intermediate — Kubernetes

README · Beginner · Next: Production

You should already have applied the nginx Deployment and Service on kind and curled through port-forward. Work from 04-kubernetes/.

Intermediate: go deeper

ConfigMap as environment variables

A ConfigMap holds non-secret config (a color, a greeting, a feature flag). Inject it as env vars or as files. Do not put passwords here; that is a Secret (and Secrets are only base64 — see Production).

examples/configmap.yaml is a ConfigMap plus a one-replica Deployment that loads every key as an environment variable:

kubectl apply -f examples/configmap.yaml
kubectl exec deploy/config-demo -- env | grep -E 'APP_COLOR|GREETING'

Expected output: lines APP_COLOR=blue and GREETING=hello.

Change APP_COLOR in the file, kubectl apply again, then restart the Pods (kubectl rollout restart deploy/config-demo). Existing processes do not reread env vars.

Ingress (the object is not the controller)

An Ingress is an HTTP routing rule: host + path → Service. It does nothing until an Ingress controller is running (ingress-nginx, Traefik, a cloud load-balancer controller). kind does not install one.

This YAML is valid and will store an object. It will not make nginx.local work on your laptop:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: nginx
spec:
  rules:
    - host: nginx.local
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: nginx
                port:
                  number: 80

kubectl apply -f that file, then kubectl get ingress. You will see the object. You will not get an address until a controller writes one. For this module, keep using kubectl port-forward svc/nginx 8080:80.

Horizontal Pod Autoscaler (concept)

An HPA changes replicas on a Deployment from CPU (or custom) metrics. It needs a metrics API, usually metrics-server. kind does not ship metrics-server.

You do not need to install it here. The shape looks like this:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: nginx
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: nginx
  minReplicas: 1
  maxReplicas: 5
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70

Without metrics-server, the HPA object exists and sits on unknown metrics. Do not treat that as a broken cluster. For first scaling practice, keep using kubectl scale.

Requests and limits

requests are what the scheduler reserves. limits are the cap (CPU is throttled; memory above the limit is OOMKilled). Add them under the container:

resources:
  requests:
    cpu: 50m
    memory: 64Mi
  limits:
    cpu: 200m
    memory: 128Mi

50m is 0.05 of one CPU. These numbers fit nginx:1.27-alpine on a laptop. In Production, treat requests as required, not optional.

Namespaces

A namespace is a name scope (default, kube-system, or one you create). kubectl get pods lists default unless you pass -n or --all-namespaces.

kubectl create namespace learn
kubectl apply -n learn -f examples/nginx-deployment.yaml
kubectl get pods -n learn

Namespaces are not a security boundary by themselves. Isolation needs RBAC and NetworkPolicies (Production).

Pod vs Deployment vs Service vs Ingress

Object Job Lives until…
Pod Run one or more containers that share a network The Pod dies or you delete it
Deployment Keep N Pods that match a template You delete the Deployment
Service Stable DNS name + virtual IP in front of Pods You delete the Service
Ingress HTTP(S) routes from outside to Services You delete it — and a controller must exist

Reach for a Deployment when you have an app. Reach for a Service when something must call that app by name. Reach for Ingress when that caller is HTTP from outside the cluster and you have installed a controller. Reach for a naked Pod only for a throwaway debug container.

Important kubectl (after first success)

# What is running, and where
kubectl get pods -o wide
kubectl get deploy,rs,po,svc
kubectl get events --sort-by='.lastTimestamp'

# Why a Pod is not Ready
kubectl describe pod <pod-name>
kubectl logs <pod-name>
kubectl logs <pod-name> -c <container>   # multi-container Pod
kubectl logs deploy/nginx --previous     # last crashed instance

# Exec and generate YAML
kubectl exec -it deploy/nginx -- /bin/sh
kubectl create deployment nginx --image=nginx:1.27-alpine --dry-run=client -o yaml

# Scale and roll out
kubectl scale deploy/nginx --replicas=5
kubectl set image deploy/nginx nginx=nginx:1.27-alpine
kubectl rollout status deploy/nginx
kubectl rollout history deploy/nginx
kubectl rollout undo deploy/nginx

describe and logs are the two commands that replace guessing. Use them before you change YAML at random.

ConfigMap and Secret from literals (handy; prefer files in git for anything you will keep):

kubectl create configmap app-config --from-literal=APP_COLOR=blue --dry-run=client -o yaml
kubectl create secret generic db --from-literal=password=not-for-git --dry-run=client -o yaml