PPrepLearn
Progress · 0/4 sections

📦 Week 2 — Kubernetes Core (Days 8–14)

5 min read · Days 8–14 · Notion

Core insight: Kubernetes is not a container orchestrator. It is a declarative state machine. You declare desired state. The control plane works continuously to make actual state match desired state. Understanding this reconciliation loop is understanding Kubernetes.


📅 Day 8 — Kubernetes Architecture

Control plane components

kube-apiserverthe front door. All kubectl commands hit this. REST API.
etcddistributed key-value store. The only source of truth.
kube-schedulerpicks which node a pod runs on.
kube-controller-managerreconciliation loops (ReplicaSet, Deployment, Node controllers)
cloud-controller-managercreates LoadBalancers, PVs from cloud APIs

Worker node components

kubeletagent on every node. Creates containers via CRI.
kube-proxymanages iptables rules for Service routing.
containerdcontainer runtime. kubelet talks to it via CRI.
# Create a 3-node kind cluster
cat > kind-config.yaml << 'EOF'
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
  - role: control-plane
    extraPortMappings:
      - containerPort: 80
        hostPort: 80
      - containerPort: 443
        hostPort: 443
  - role: worker
  - role: worker
EOF
 
kind create cluster --name dev --config kind-config.yaml
kubectl cluster-info --context kind-dev
kubectl get nodes -o wide
kubectl get pods -n kube-system
 
# Watch the reconciliation loop in action
kubectl run test --image=nginx
kubectl get pod test -w           # Pending → ContainerCreating → Running
kubectl delete pod test           # no ReplicaSet = not rescheduled

📅 Day 9 — Pods & Deployments

# deployment.yaml — production-grade
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-go-api
  namespace: default
  labels:
    app: my-go-api
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-go-api
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0     # zero-downtime: never kill before new pod is ready
  template:
    metadata:
      labels:
        app: my-go-api
        version: "1.0.0"
    spec:
      serviceAccountName: my-go-api
      terminationGracePeriodSeconds: 30
      containers:
      - name: api
        image: myapp:1.0.0
        ports:
        - containerPort: 8080
        resources:
          requests:
            cpu: 100m
            memory: 64Mi
          limits:
            cpu: 500m
            memory: 256Mi
        env:
        - name: PORT
          value: "8080"
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: app-secrets
              key: db-password
        startupProbe:
          httpGet: { path: /health, port: 8080 }
          failureThreshold: 30
          periodSeconds: 10
        readinessProbe:
          httpGet: { path: /ready, port: 8080 }
          initialDelaySeconds: 5
          periodSeconds: 10
          failureThreshold: 3
        livenessProbe:
          httpGet: { path: /health, port: 8080 }
          initialDelaySeconds: 15
          periodSeconds: 30
          failureThreshold: 3
kubectl apply -f deployment.yaml
kubectl rollout status deployment/my-go-api
kubectl set image deployment/my-go-api api=myapp:2.0.0
kubectl rollout undo deployment/my-go-api
kubectl scale deployment/my-go-api --replicas=5

📅 Day 10 — Services & DNS

apiVersion: v1
kind: Service
metadata:
  name: my-go-api
spec:
  type: ClusterIP
  selector:
    app: my-go-api
  ports:
  - port: 80
    targetPort: 8080
# DNS: <service>.<namespace>.svc.cluster.local
kubectl run dns-test --image=busybox --rm -it -- sh
# Inside: nslookup my-go-api
#         curl http://my-go-api/health
 
# Port forward for local testing
kubectl port-forward svc/my-go-api 8080:80

📅 Day 11 — ConfigMaps & Secrets

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  PORT: "8080"
  LOG_LEVEL: "info"
  DB_HOST: "postgres-service"
---
apiVersion: v1
kind: Secret
metadata:
  name: app-secrets
type: Opaque
stringData:
  db-password: "supersecret"
  jwt-key: "256-bit-secret"
# Sealed Secrets: encrypt secrets in git
brew install kubeseal
kubectl apply -f https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.26.0/controller.yaml
kubectl create secret generic app-secrets --dry-run=client -o yaml | \
  kubeseal --format yaml > sealed-secret.yaml
# sealed-secret.yaml is safe to commit to git!

📅 Day 12 — Namespaces & RBAC

apiVersion: v1
kind: Namespace
metadata:
  name: development
  labels:
    environment: dev
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: pod-reader
  namespace: development
rules:
- apiGroups: [""]
  resources: ["pods", "pods/log"]
  verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: ashu-pod-reader
  namespace: development
subjects:
- kind: ServiceAccount
  name: my-go-api
  namespace: development
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io
---
apiVersion: v1
kind: ResourceQuota
metadata:
  name: dev-quota
  namespace: development
spec:
  hard:
    requests.cpu: "4"
    requests.memory: 8Gi
    limits.cpu: "8"
    pods: "20"

📅 Day 13 — Ingress + cert-manager

# Install NGINX Ingress Controller
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml
kubectl wait --namespace ingress-nginx \
  --for=condition=ready pod \
  --selector=app.kubernetes.io/component=controller \
  --timeout=90s
 
# Install cert-manager
helm repo add jetstack https://charts.jetstack.io
helm install cert-manager jetstack/cert-manager \
  --namespace cert-manager --create-namespace \
  --set installCRDs=true
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-app-ingress
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    nginx.ingress.kubernetes.io/rate-limit: "100"
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  ingressClassName: nginx
  tls:
  - hosts: [api.myapp.com]
    secretName: api-tls-cert
  rules:
  - host: api.myapp.com
    http:
      paths:
      - path: /api
        pathType: Prefix
        backend:
          service:
            name: my-go-api
            port:
              number: 80

📅 Day 14 — Week 2 Project: Full Go App on k8s

Write and apply all manifests:

k8s/
  namespace.yaml, configmap.yaml, secret.yaml
  serviceaccount.yaml, rbac.yaml, resourcequota.yaml
  deployment-api.yaml, deployment-worker.yaml
  service-api.yaml, ingress.yaml
  postgres/, redis/

Deliverables:

  • kubectl apply -k k8s/ deploys everything
  • curl http://localhost/api/v1/health returns 200
  • Zero-downtime rolling update demonstrated
  • k9s: browse pods, logs, events interactively

⚠️ Common mistakes Week 2

No resource requests — scheduler blind, OOM kills cascade. Every container needs requests + limits.

No readiness probe — traffic hits pods still connecting to DB. Users get 502s during startup.

Secrets in ConfigMaps — ConfigMaps are not encrypted. Use Secrets + Sealed Secrets for anything sensitive.

Related