"Same code, different colors."
A lightweight Python Flask API and modern visual dashboard that dynamically responds to environment settings injected at runtime via Kubernetes ConfigMaps and Secrets. This project demonstrates the core concepts of Immutable Infrastructure, decoupling configuration from code, and cloud-native application patterns.
chameleon-api/
├── app/
│ ├── app.py # Flask application code (info, health, root UI)
│ ├── requirements.txt # Python dependencies (Flask, Gunicorn)
│ └── templates/
│ └── index.html # Premium glassmorphic HTML dashboard (dynamic styling)
├── k8s/
│ ├── configmap.yaml # Public configurations (environment name, theme color)
│ ├── secret.yaml # Private credentials (API Key)
│ ├── deployment.yaml # Deployment template with health probes & env injections
│ └── service.yaml # NodePort Service to expose the app locally
├── Dockerfile # Multi-stage optimized Docker build instruction
├── .dockerignore # Exclusions list to keep docker context small
├── .gitignore # Git exclusions list (venv, caches, etc.)
└── README.md # Complete project guide and learning checklist
To run this project inside Kubernetes, follow these steps.
If you do not have a cluster running, start one using Minikube or Kind:
# For Minikube
minikube start --driver=docker
# Or For Kind
kind create cluster --name chameleon-clusterSince we want to build the Docker image without uploading it to a public registry, build it directly inside your local cluster node:
Run the environment hook to direct your host's Docker command line to the Minikube internal Docker daemon:
# In PowerShell:
& minikube -p minikube docker-env | Invoke-Expression
# Build the image directly inside Minikube
docker build -t chameleon-api:v1 .Build the image locally on your machine, then load it into the Kind cluster:
# Build image locally (Ensure Docker Desktop is running)
docker build -t chameleon-api:v1 .
# Load image into the Kind cluster
kind load docker-image chameleon-api:v1 --name chameleon-clusterApply all files inside the k8s/ directory in one command:
kubectl apply -f k8s/Verify that the pods are running and the services are deployed:
# Check Pod status
kubectl get pods -l app=chameleon-api
# Check Service details
kubectl get service chameleon-serviceRun this command to bind port 5000 on your localhost directly to the service:
kubectl port-forward service/chameleon-service 5000:80Open your browser and navigate to: http://localhost:5000.
If using Minikube, you can open the NodePort directly:
minikube service chameleon-serviceNow, verify the power of dynamic configuration injection:
- Open http://localhost:5000 in your browser. Notice the primary theme is Green (the default in
configmap.yaml). - Open
k8s/configmap.yamland changeAPP_COLORfrom"Green"to another color (e.g."Red","Blue","Purple", or"Orange"):data: ENVIRONMENT_NAME: "Production" APP_COLOR: "Red" FEATURE_BETA: "Disabled"
- Apply the ConfigMap changes:
kubectl apply -f k8s/configmap.yaml
- Restart the rollout to replace the pods with the new configuration:
kubectl rollout restart deployment chameleon-api - Refresh your browser page. The dashboard interface dynamically updates to a high-end glassmorphic Red theme.
- Notice that this change happened instantly without rebuilding or repushing the Docker image!
1. Immutable Infrastructure: Why is it better to use a ConfigMap instead of editing app.py and rebuilding?
- Consistency: The exact same binary/container image is guaranteed to run in Development, Staging, and Production. If we rebuilt the container for each environment, we risk compiling different dependencies or versions, introducing silent bugs.
- Speed: Rebuilding, tagging, and pushing a Docker container image to a remote registry takes minutes. Modifying a ConfigMap and restarting a pod takes milliseconds.
- Separation of Concerns: Developers own the code logic (
app.py), and platform/DevOps operators control the runtime variables (configmap.yaml) without needing access to code repositories.
env: Used to inject individual variables, often mapping them to a custom environment variable name. It is ideal when you want to rename keys or inject sensitive fields (like a single value from aSecretviasecretKeyRef).envFrom: Maps all key-value pairs inside a ConfigMap (or Secret) directly as environment variables in the container. This is very clean and convenient when your app accepts configurations matching the ConfigMap key names directly, avoiding duplicate lines in deployment YAMLs.
- Pods are Ephemeral: Pods in Kubernetes are temporary and can be destroyed, rescheduled, or autoscaled at any time. When a pod is replaced, it receives a completely new IP address.
- Load Balancing: A Service provides a single, stable IP address and DNS name. It automatically routes traffic to the underlying active pods (load balancing) and filters out unhealthy pods based on the readiness probes.
You can stream logs using the kubectl logs command:
# View logs for a single pod
kubectl logs <pod-name>
# Stream logs in real-time
kubectl logs -f <pod-name>
# View logs for all pods in the deployment
kubectl logs deployment/chameleon-api -f --tail=100