LAB10: Deployment & Autoscaling

  1. Let's build app which computes prime numbers

chevron-rightPython app sourcehashtag
import os
import socket
import time
from typing import List

from fastapi import FastAPI, HTTPException
from pydantic import BaseModel

app = FastAPI()


class PrimeRequest(BaseModel):
    n: int


class PrimeResponse(BaseModel):
    n: int
    prime_count: int
    time_taken_seconds: float
    pod_name: str
    hostname: str
    primes: List[int]


def generate_primes(limit: int) -> List[int]:
    primes = []
    for num in range(2, limit + 1):
        is_prime = True
        # naive but CPU-heavy enough for HPA testing
        for i in range(2, int(num ** 0.5) + 1):
            if num % i == 0:
                is_prime = False
                break
        if is_prime:
            primes.append(num)
    return primes


@app.post("/primes", response_model=PrimeResponse)
def compute_primes(req: PrimeRequest):
    if req.n < 2:
        raise HTTPException(status_code=400, detail="n must be >= 2")

    start = time.perf_counter()
    primes = generate_primes(req.n)
    elapsed = time.perf_counter() - start

    pod_name = os.environ.get("POD_NAME", "unknown-pod")
    hostname = socket.gethostname()

    return PrimeResponse(
        n=req.n,
        prime_count=len(primes),
        time_taken_seconds=elapsed,
        pod_name=pod_name,
        hostname=hostname,
        primes=primes,
    )


@app.get("/info")
def info():
    pod_name = os.environ.get("POD_NAME", "unknown-pod")
    hostname = socket.gethostname()
    return {
        "message": "Prime calculator HPA lab",
        "pod_name": pod_name,
        "hostname": hostname,
    }

:::

:::spoiler requirements.txt

fastapi
uvicorn[standard]
    

:::

:::spoiler Dockerfile

FROM python:3.11-slim

WORKDIR /app

# Install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Copy app code
COPY app.py .

# Run the FastAPI app with uvicorn
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"]
  1. Docker build and push

docker build -t openlabfree/prime-hpa-lab:v1 .
docker push openlabfree/prime-hpa-lab:v1
  1. Check if you cluster has metric-server installed or not

kubectl top nodes
kubectl top pod

kubectl get apiservices | grep metrics
  1. If no metrics shown, then install metric server

kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
  1. Verify Metrics Server Pod

kubectl get pods -n kube-system | grep metrics
  1. Test API availability

Note: try editing deployment file , and adding --kubelet-insecure-tls flag in container start command.

  1. If not running, apply patch

  1. Delete old pods

Wait 10–20 seconds. 9. Verify result

  1. Test metrics

[Optional]

If you like to query directly to node metrics api

chevron-rightcreate-kubelet-admin-cert.shhashtag

```bash #!/bin/bash set -euo pipefail

echo "-----------------------------------------------------" echo " 🔐 Kubernetes system:masters Client Cert Generator" echo "-----------------------------------------------------"

--- CONFIG ---

CA_FILE="/etc/kubernetes/pki/ca.crt" CA_KEY="/etc/kubernetes/pki/ca.key" WORKDIR="$(pwd)"

ADMIN_KEY="$WORKDIR/kubelet-admin.key" ADMIN_CSR="$WORKDIR/kubelet-admin.csr" ADMIN_CERT="$WORKDIR/kubelet-admin.crt" ADMIN_CNF="$WORKDIR/admin-masters.cnf" FULLCHAIN="$WORKDIR/kubelet-full-chain.crt"

--- Detect Hostname for curl Example ---

NODE_NAME="$(hostname)" echo "📌 Node hostname detected: $NODE_NAME" echo ""

--- Verify CA ---

echo "🔍 Checking Kubernetes CA..." if [[ ! -f "$CA_FILE" || ! -f "$CA_KEY" ]]; then echo "❌ Kubernetes CA files not found." echo "This script must run on a control-plane node with CA access." exit 1 fi echo " ✓ CA verified: $CA_FILE" echo ""

--- Prevent Accidental Overwrite ---

for f in "$ADMIN_KEY" "$ADMIN_CSR" "$ADMIN_CERT" "$ADMIN_CNF" "$FULLCHAIN"; do if [[ -f "$f" ]]; then echo "⚠️ Removing existing file: $f" rm -f "$f" fi done

--- Generate Key ---

echo "🔐 Generating private key..." openssl genrsa -out "$ADMIN_KEY" 2048 >/dev/null echo " ✓ $ADMIN_KEY" echo ""

--- Create CSR Config ---

echo "📄 Writing CSR configuration..." cat > "$ADMIN_CNF" <<EOF [ req ] default_bits = 2048 prompt = no default_md = sha256 distinguished_name = dn req_extensions = v3_req

[ dn ] CN = admin O = system:masters

[ v3_req ] basicConstraints = CA:FALSE keyUsage = digitalSignature, keyEncipherment extendedKeyUsage = clientAuth EOF echo " ✓ CSR config: $ADMIN_CNF" echo ""

--- Generate CSR ---

echo "🔏 Generating CSR..." openssl req -new -key "$ADMIN_KEY" -out "$ADMIN_CSR" -config "$ADMIN_CNF" >/dev/null echo " ✓ CSR created: $ADMIN_CSR" echo ""

--- Sign Certificate ---

echo "🔐 Signing certificate with Kubernetes CA..." openssl x509 -req -in "$ADMIN_CSR" -CA "$CA_FILE" -CAkey "$CA_KEY" -CAcreateserial -out "$ADMIN_CERT" -days 365 -extensions v3_req -extfile "$ADMIN_CNF" >/dev/null echo " ✓ Certificate: $ADMIN_CERT" echo ""

--- Extract Full Kubelet CA chain ---

echo "🔍 Extracting kubelet CA certificate chain..." openssl s_client -showcerts -connect localhost:10250 </dev/null 2>/dev/null | sed -n '/BEGIN CERTIFICATE/,/END CERTIFICATE/p' > "$FULLCHAIN"

echo " ✓ Full chain written: $FULLCHAIN" echo ""

--- Show Certificate Details ---

echo "📜 Certificate details:" openssl x509 -in "$ADMIN_CERT" -noout -text | sed 's/^/ /' echo ""

--- Final Output ---

echo "-----------------------------------------------------" echo " 🎉 COMPLETED SUCCESSFULLY!" echo "-----------------------------------------------------" echo "Generated files:" echo " ✔ $ADMIN_KEY" echo " ✔ $ADMIN_CERT" echo " ✔ $ADMIN_CSR" echo " ✔ $ADMIN_CNF" echo " ✔ $FULLCHAIN" echo "" echo "You can now access kubelet securely:" echo "" echo "curl --cacert $FULLCHAIN \" echo " --cert $ADMIN_CERT \" echo " --key $ADMIN_KEY \" echo " https://$NODE_NAME:10250/stats/summary" echo "" echo "🟢 This certificate belongs to system:masters and has FULL kubelet privileges." echo "-----------------------------------------------------"

  1. Let's deploy the app

chevron-rightprime-hpa-lab.yamlhashtag

```yaml apiVersion: v1 kind: Namespace metadata: name: hpa-lab --- apiVersion: apps/v1 kind: Deployment metadata: name: prime-api-deployment namespace: hpa-lab spec: replicas: 1 selector: matchLabels: app: prime-api template: metadata: labels: app: prime-api spec: containers: - name: prime-api image: openlabfree/prime-hpa-lab:v1 imagePullPolicy: IfNotPresent ports: - containerPort: 8000 env: - name: POD_NAME valueFrom: fieldRef: fieldPath: metadata.name resources: # important: HPA uses CPU *requests* as baseline requests: cpu: "100m" memory: "128Mi" limits: cpu: "500m" memory: "256Mi" --- apiVersion: v1 kind: Service metadata: name: prime-api-service namespace: hpa-lab spec: selector: app: prime-api ports: - name: http port: 80 targetPort: 8000 type: ClusterIP

  1. Test quickly via port-forward:

  1. In another terminal:

  1. Horizontal Pod Autoscaler (HPA)

Prereq: metrics-server must be installed and working kubectl get apiservices | grep metrics kubectl top pods -A should return data.

  1. Create prime-hpa.yaml:

  1. Apply:

  1. Generate Load via Service (to trigger autoscale)

    1. Start launcher container

    Then:

    1. Verification inside the pod

    1. Load generator

    1. Example Output

    1. Watch Autoscaling in Action Metrics:

    HPA behavior:

    Scaling events:

References:

Last updated