Running containers on a single server works. Then traffic spikes, containers crash, you need to scale, an update breaks something. Containers are easy. Orchestrating them in production is another story. That's why you need Kubernetes.
We at Meteora Web have been working with Kubernetes since 2017. Not because it's trendy, but because we've seen dozens of projects choke on homegrown stacks when load grows. Kubernetes is not simple, but it's the only standard way to manage containers in production reliably, scalably, and reproducibly. This pillar guide covers everything you need to go from zero to a production-ready cluster.
Kubernetes from Scratch: Architecture, Pod, Deployment, Service, Namespace
Kubernetes is an orchestrator. It takes your containers and decides where to place them, when to restart them, how to expose them. The smallest unit is the Pod: one or more containers sharing networking and storage. Pods are ephemeral; you don't manage them directly but through a Deployment that declares the desired state (replicas, image, resources) and Kubernetes maintains that reality. To connect to Pods you need a Service: a stable IP address and load balancing. Namespaces isolate resources between teams or environments.
Common mistake: using Pod directly
We often see people creating Pods by hand. Don't. A Deployment guarantees rolling updates, self-healing, scaling. The Pod is just the atomic element.
Hands-on: your first Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.25
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
selector:
app: nginx
ports:
- port: 80
targetPort: 80
type: ClusterIP
What to do now: Create a test namespace with kubectl create namespace test and apply the file with kubectl apply -f deploy-service.yaml -n test.
Sponsored Protocol
kubectl: Essential Commands for Cluster Management
kubectl is the Swiss Army knife of Kubernetes. We use it every day for inspection, debugging, deployment, and troubleshooting. These are the commands that really matter, beyond the tutorial.
Get, Describe, Logs, Exec
# List resources
kubectl get pods -n production
kubectl get deploy,svc,ingress -A
# Detail and troubleshooting
kubectl describe pod nginx-deployment-xxxx
kubectl logs -f deployment/nginx --tail=50
# Execute in a container
kubectl exec -it deployment/nginx -- /bin/sh
# Port forwarding for local debugging
kubectl port-forward svc/nginx-service 8080:80
Pro tip: Use alias kubectl get pods -w for continuous watch. We keep it in a tmux window during critical deployments.
Helm: The Kubernetes Package Manager
Helm is to Kubernetes what apt is to Debian or composer to PHP. It packages YAML resources into Charts with templates and variable values. Manage releases, rollbacks, upgrades. Don't write YAML from scratch for every component; use or create Charts.
Creating a custom Chart
# Chart.yaml
apiVersion: v2
name: my-app
description: A deployment with service and ingress
version: 0.1.0
# values.yaml
replicaCount: 2
image:
repository: nginx
tag: 1.25
service:
port: 80
ingress:
enabled: true
host: app.mydomain.com
Templates in templates/deployment.yaml use {{ .Values.replicaCount }}. Install with helm install my-app ./chart -f values.prod.yaml.
Sponsored Protocol
Common mistake: not versioning Charts in Git. Do it. Every deployment is traceable.
Ingress Controller: Nginx, Traefik, and HTTP/HTTPS Routing
Services of type NodePort or LoadBalancer expose ports, but for hostname-based routing, path-based routing, and TLS termination, you need an Ingress Controller. It's not included by default; you install it (Nginx, Traefik, HAProxy, Contour).
Configuring Ingress with Nginx
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app-ingress
spec:
ingressClassName: nginx
tls:
- hosts:
- app.mydomain.com
secretName: tls-secret
rules:
- host: app.mydomain.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nginx-service
port:
number: 80
What to do now: Install the Nginx Ingress Controller with Helm: helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx && helm upgrade --install ingress-nginx ingress-nginx/ingress-nginx. Then create the Ingress above.
Persistent Volumes and StatefulSet: Storage for Stateful Applications
Pods are ephemeral. Data is not. For databases, queues, file storage, Kubernetes provides PersistentVolume (cluster resource) and PersistentVolumeClaim (Pod request). For applications that need stable identity (e.g., databases) use StatefulSet instead of Deployment.
Example: PVC mounted on a Deployment
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: data-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
---
spec:
template:
spec:
containers:
- volumeMounts:
- mountPath: /data
name: storage
volumes:
- name: storage
persistentVolumeClaim:
claimName: data-pvc
Note: StatefulSet requires a headless service and manages startup order. Do not use it for stateless apps.
Sponsored Protocol
Horizontal Pod Autoscaler: Automatic Scaling Under Load
HPA adjusts the number of replicas based on CPU, memory, or custom metrics. Without HPA you pay for unused capacity or suffer performance collapses. Always set it up.
Basic HPA configuration
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: nginx-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: nginx-deployment
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
Operational: Ensure the cluster has the metrics-server installed (kubectl top pods). Otherwise HPA won't work.
ConfigMap and Secret: Managing Configuration and Credentials
Separating configuration from code is a golden rule. ConfigMap for non-sensitive data (environment variables, config files). Secret for credentials, tokens, keys. Both can be mounted as volumes or environment variables.
Creating and using a Secret
kubectl create secret generic db-credentials \
--from-literal=username=app_user \
--from-literal=password='S3cur3P@ss'
# In the deployment
env:
- name: DB_USER
valueFrom:
secretKeyRef:
name: db-credentials
key: username
Common mistake: versioning Secrets in plaintext on Git. We use SealedSecrets to encrypt data in the repository.
Kubernetes RBAC: Access Security and ServiceAccount
The cluster is not owned by a single user. With RBAC (Role-Based Access Control) you define who can do what on which resources. Use ServiceAccount for Pods (not admin) and Role/ClusterRole for humans or automation.
Sponsored Protocol
Example: ServiceAccount for an app that reads Pods
apiVersion: v1
kind: ServiceAccount
metadata:
name: pod-reader
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: production
name: pod-reader
rules:
- apiGroups: [""]
resources: ["pods", "pods/log"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: pod-reader-binding
namespace: production
subjects:
- kind: ServiceAccount
name: pod-reader
namespace: production
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io
Operational: Check audit logs: kubectl logs -n kube-system kube-apiserver --tail=100 | grep -i forbidden. You'll find who tries to do unauthorized actions.
Managed Kubernetes: GKE, EKS, AKS Compared
Managing the control plane yourself (kubeadm) is possible, but for most companies a managed service is better: Google Kubernetes Engine (GKE), Amazon EKS, Azure AKS. We have clients on all three. Here are the practical differences:
- GKE: automatic control plane upgrades, native integration with Cloud Armor, free nodes. Ideal if you use Google Cloud.
- EKS: deep AWS ecosystem (IAM roles for service accounts, ALB ingress controller). More expensive in fixed costs ($0.10/hour control plane).
- AKS: good value, free nodes, integration with Azure AD. Great for hybrid environments.
What to do now: Evaluate your provider. If you're already on a cloud, pick its managed Kubernetes. Otherwise, GKE remains the most mature.
Sponsored Protocol
ArgoCD and GitOps: Declarative Deployment and Synchronization
GitOps flips the flow: you don't run kubectl apply. You declare desired state in a Git repository, and an operator (ArgoCD) applies it and keeps it synced. If someone manually modifies resources, ArgoCD reverts them to Git state. This is how we manage all production clusters.
Quick ArgoCD installation
kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
Then configure an ArgoCD Application pointing to the Git repo. Every push triggers synchronization. For deeper CI/CD integration, read our guide on Automated CI Pipeline.
Common mistake: not enabling auto-sync. The difference between GitOps and manual deploy is automation. Set syncPolicy: with automated: {prune: true, selfHeal: true}.
In summary — What to do now
Kubernetes is not learned in a day. But you can start immediately with concrete actions:
- Install minikube or Kind on your laptop to experiment. No cost, no risk.
- Read the official documentation: kubernetes.io/docs is the bible.
- Rewrite a Docker Compose deploy into Deployment + Service. You'll immediately see the difference.
- Install Helm and explore official charts for PostgreSQL, Redis, Nginx. Study the templates.
- Set up RBAC even if you're the only user. Good practice.
- Configure HPA on an app with load. Use
kubectl run load-generatorto test. - Don't be afraid to break things in a test environment. Recover with
minikube delete.
We at Meteora Web help teams and businesses move from artisanal stacks to Kubernetes in production. If you want a hands-on discussion, reach out. Containers are the raw material; orchestration is the art.