Category: Kubernetes
How to Deploy a Kubernetes Application with a Clean Namespace Structure
How to Deploy a Kubernetes Application with a Clean Namespace Structure
When you deploy an application to Kubernetes in production, you shouldn’t throw everything into the default namespace or a single giant YAML file. A proper setup uses:
- A dedicated namespace for the app
- A ServiceAccount and RBAC for security
- ConfigMap and Secret for configuration
- Deployment, Service, and Ingress for runtime and traffic
- HPA, PDB, and NetworkPolicies for reliability and security
HPA vs PDB (Summary)
========================
Feature HPA PDB Scales pods based on load ✔ YES ❌ NO Ensures minimum pods stay up ❌ NO ✔ YES Helps with traffic spikes ✔ YES ❌ NO Protects during upgrades/drains ❌ NO ✔ YES Operates on load (CPU/metrics) ✔ YES ❌ NO Operates on disruptions ❌ NO ✔ YES Controls min/max replicas ✔ YES ❌ NO Controls disruption limits ❌ NO ✔ YES
In this post, we’ll walk through a clean, real-world Kubernetes namespace structure, show the YAML for each section, and explain what it does. You can drop all of these files into a directory and apply them in one go with:
kubectl apply -f k8s/
1. Directory Structure
Create a folder for your Kubernetes manifests, for example:
k8s/
namespace.yaml
serviceaccount.yaml
rbac.yaml
configmap.yaml
secret.yaml
deployment.yaml
service.yaml
ingress.yaml
hpa.yaml
pdb.yaml
networkpolicy-default-deny.yaml
networkpolicy-allow-ingress.yaml
Kubernetes will treat all of these files as one desired state when you run kubectl apply -f k8s/, similar to how Terraform reads multiple .tf files in one directory.
2. Namespace – Isolating the Application
A namespace is a logical boundary in the cluster. Think of it as a dedicated “folder” for your application’s resources.
apiVersion: v1
kind: Namespace
metadata:
name: prod-app
labels:
name: prod-app
pod-security.kubernetes.io/enforce: "restricted"
pod-security.kubernetes.io/enforce-version: "latest"
What this does:
- Creates a namespace called
prod-app. - Applies Pod Security labels to enforce restricted policies.
- Gives you a clean way to separate dev, staging, and prod environments.
3. ServiceAccount – Identity for the Pods
A ServiceAccount represents the identity your pods use inside Kubernetes. Instead of relying on the default ServiceAccount, you create a dedicated one for your app.
apiVersion: v1
kind: ServiceAccount
metadata:
name: app-sa
namespace: prod-app
What this does:
- Creates a ServiceAccount named
app-sain theprod-appnamespace. - Your Deployment will run pods using this identity, not the insecure default.
4. RBAC – Roles and RoleBindings
RBAC (Role-Based Access Control) defines what your application is allowed to do inside the namespace. You don’t want your app to have full cluster access; you give it just enough permissions.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: app-read-config
namespace: prod-app
rules:
- apiGroups: [""]
resources: ["configmaps", "secrets"]
verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: app-read-config-binding
namespace: prod-app
subjects:
- kind: ServiceAccount
name: app-sa
namespace: prod-app
roleRef:
kind: Role
name: app-read-config
apiGroup: rbac.authorization.k8s.io
What this does:
Roleapp-read-config:- Allows reading (
get,list) ConfigMaps and Secrets in this namespace.
- Allows reading (
RoleBinding:- Attaches that Role to the
app-saServiceAccount. - Any pod running as
app-sacan now read ConfigMaps and Secrets inprod-app.
- Attaches that Role to the
5. ConfigMap – Non-Sensitive Configuration
A ConfigMap holds non-secret configuration such as runtime flags, modes, switches, or log levels.
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: prod-app
data:
APP_ENV: "production"
APP_LOG_LEVEL: "info"
What this does:
- Stores plain-text configuration for your application.
- Lets you change behavior without rebuilding the container image.
6. Secret – Sensitive Configuration
Secrets hold confidential settings such as database URLs, API keys, and credentials.
apiVersion: v1
kind: Secret
metadata:
name: app-secret
namespace: prod-app
type: Opaque
stringData:
DATABASE_URL: "postgres://user:password@db.prod:5432/app"
API_KEY_EXTERNAL_SERVICE: "replace-me"
What this does:
- Stores sensitive data separately from code.
- Works with RBAC so only the right ServiceAccount can read it.
7. Deployment – The Application Workload
The Deployment defines how your containers run: image, replicas, health checks, resources, and security context. This is the core of your application’s runtime behavior.
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-deployment
namespace: prod-app
labels:
app: my-app
spec:
replicas: 3
revisionHistoryLimit: 5
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 0
maxSurge: 1
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
serviceAccountName: app-sa
automountServiceAccountToken: false
securityContext:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
containers:
- name: app
image: your-registry/your-image:TAG
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
name: http
envFrom:
- configMapRef:
name: app-config
- secretRef:
name: app-secret
resources:
requests:
cpu: "200m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "512Mi"
readinessProbe:
httpGet:
path: /healthz
port: http
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet:
path: /livez
port: http
initialDelaySeconds: 15
periodSeconds: 20
securityContext:
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
terminationGracePeriodSeconds: 30
What this does:
- Runs 3 replicas of your application for availability.
- Uses a safe rolling update strategy with zero downtime (
maxUnavailable: 0,maxSurge: 1). - Runs pods under the
app-saServiceAccount, inheriting its RBAC permissions. - Injects configuration from
ConfigMapandSecret. - Defines health checks (
readinessProbe,livenessProbe) so Kubernetes knows when to route traffic and when to restart pods. - Applies strict security settings (non-root user, no privilege escalation, read-only root filesystem).
8. Service – Internal Load Balancer
A Service provides a stable, cluster-internal endpoint to reach your pods.
apiVersion: v1
kind: Service
metadata:
name: app-service
namespace: prod-app
labels:
app: my-app
spec:
type: ClusterIP
selector:
app: my-app
ports:
- name: http
port: 80
targetPort: http
What this does:
- Maps port 80 on the Service to port 8080 on the pods (via the named port
http). - Provides stable DNS:
app-service.prod-app.svc.cluster.local. - Load balances traffic across all healthy pods with
app: my-app.
9. Ingress – External HTTP/HTTPS Access
Ingress exposes your Service to the outside world using a hostname and optional TLS.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-ingress
namespace: prod-app
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
tls:
- hosts:
- app.your-domain.com
secretName: app-tls-secret
rules:
- host: app.your-domain.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app-service
port:
number: 80
What this does:
- Routes traffic from
https://app.your-domain.comtoapp-serviceon port 80. - Uses
app-tls-secretfor TLS termination (usually created by cert-manager). - Relies on an Ingress controller (e.g., NGINX) running in the cluster.
10. Horizontal Pod Autoscaler (HPA) – Scaling on Load
The HPA automatically adjusts the number of replicas based on metrics like CPU usage.
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: app-hpa
namespace: prod-app
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: app-deployment
minReplicas: 3
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 60
What this does:
- Keeps at least 3 pods running, and can scale up to 10.
- Targets the
app-deploymentDeployment. - Scales based on CPU usage (e.g., above 60% average).
11. PodDisruptionBudget (PDB) – Protecting Availability
A PodDisruptionBudget ensures that voluntary disruptions (node drains, upgrades) don’t take down too many pods at once.
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: app-pdb
namespace: prod-app
spec:
minAvailable: 2
selector:
matchLabels:
app: my-app
What this does:
- Guarantees that at least 2 pods are always available.
- Protects your app during maintenance and cluster upgrades.
12. Network Policies – Zero-Trust Networking
By default, Kubernetes allows every pod to talk to every other pod. NetworkPolicies let you move to a zero-trust model.
Default Deny Policy
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: prod-app
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
What this does:
- Blocks all ingress and egress traffic for all pods in the namespace, unless explicitly allowed.
Allow Traffic from Ingress Controller to the App
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-from-ingress
namespace: prod-app
spec:
podSelector:
matchLabels:
app: my-app
policyTypes:
- Ingress
- Egress
ingress:
- from:
- namespaceSelector:
matchLabels:
app.kubernetes.io/name: ingress-nginx
ports:
- protocol: TCP
port: 80
egress:
- to:
- namespaceSelector: {}
ports:
- protocol: TCP
port: 5432
- protocol: TCP
port: 443
What this does:
- Allows only the Ingress controller namespace (e.g.
ingress-nginx) to send HTTP traffic to the app. - Restricts egress traffic to specific ports (e.g., PostgreSQL and HTTPS).
Applying Everything
Once you have all these files in your k8s/ directory, you deploy the entire application stack with:
kubectl apply -f k8s/
Kubernetes reads all files, builds the state internally, and creates or updates resources in the correct order, very similar to how Terraform applies all .tf files in a directory.
Conclusion
A production-ready Kubernetes deployment is not just a Deployment and a Service. It is a structured set of manifests that cover identity, security, configuration, scaling, networking, and reliability.
- Namespace – isolates your application.
- ServiceAccount + RBAC – define identity and permissions.
- ConfigMap + Secret – handle configuration and sensitive data.
- Deployment + Service + Ingress – run the app and expose it.
- HPA + PDB – keep it scalable and resilient.
- NetworkPolicies – secure communication with a zero-trust model.
With this structure in place, you have a clean, repeatable Kubernetes deployment that fits naturally into Git, CI/CD, and GitOps workflows.
Kubernetes Cheat Sheet
kubectl Context and Configuration
Manage which Kubernetes cluster kubectl communicates with, and configure authentication and namespace defaults.
kubectl config view # View merged kubeconfig
# Use multiple kubeconfig files simultaneously
export KUBECONFIG=~/.kube/config:~/.kube/kubconfig2
kubectl config view
# Extract a specific user's password
kubectl config view -o jsonpath='{.users[?(@.name == "e2e")].user.password}'
# List users
kubectl config view -o jsonpath='{.users[*].name}'
# Context management
kubectl config get-contexts # List contexts
kubectl config current-context # Show active context
kubectl config use-context my-cluster # Switch context
# Add a cluster entry
kubectl config set-cluster my-cluster
# Set proxy URL for cluster entry
kubectl config set-cluster my-cluster --proxy-url=my-proxy-url
# Add a user with basic authentication
kubectl config set-credentials kubeuser/foo.kubernetes.com \
--username=kubeuser --password=kubepassword
# Set default namespace for current context
kubectl config set-context --current --namespace=production
# Set a new context with specific namespace and user
kubectl config set-context gce --user=cluster-admin --namespace=foo \
&& kubectl config use-context gce
# Delete a user
kubectl config unset users.foo
Helpful aliases:
# Quickly switch or show context
alias kx='f() { [ "$1" ] && kubectl config use-context $1 || kubectl config current-context ; } ; f'
# Quickly switch or show namespace
alias kn='f() { [ "$1" ] && kubectl config set-context --current --namespace $1 \
|| kubectl config view --minify | grep namespace | cut -d" " -f6 ; } ; f'
kubectl apply (Declarative Management)
kubectl apply is the recommended method for managing resources in production. It creates or updates resources by applying a desired state.
kubectl apply -f ./app.yaml # Apply single file
kubectl apply -f ./manifests/ # Apply directory
kubectl apply -f https://example.com/app.yaml # Apply from URL
kubectl create deployment nginx --image=nginx # Quick one-shot deployment
Create multiple manifests via stdin:
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: pod-one
spec:
containers:
- name: c
image: busybox
args: ["sleep", "1000"]
---
apiVersion: v1
kind: Pod
metadata:
name: pod-two
spec:
containers:
- name: c
image: busybox
args: ["sleep", "2000"]
EOF
Create a secret:
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Secret
metadata:
name: mysecret
type: Opaque
data:
username: $(echo -n "jane" | base64 -w0)
password: $(echo -n "s33msi4" | base64 -w0)
EOF
Viewing and Finding Resources
kubectl get pods # Pods in namespace
kubectl get pods -A # All namespaces
kubectl get pods -o wide # Pod node placement
kubectl get deployments # Deployments
kubectl get svc # Services
kubectl describe pod my-pod # Detailed pod info
kubectl describe node my-node # Node details
Sorting:
kubectl get pods --sort-by='.status.containerStatuses[0].restartCount'
kubectl get pv --sort-by=.spec.capacity.storage
Field and label selectors:
kubectl get pods --field-selector=status.phase=Running
kubectl get pods -l app=web
kubectl get nodes --selector='!node-role.kubernetes.io/control-plane'
Retrieve specific fields:
kubectl get configmap myconfig -o jsonpath='{.data.ca\.crt}'
kubectl get secret my-secret -o jsonpath='{.data.username}' | base64 --decode
Updating Resources and Rolling Updates
kubectl set image deployment/web web=nginx:1.25 # Update image
kubectl rollout history deployment/web # View history
kubectl rollout undo deployment/web # Roll back
kubectl rollout restart deployment/web # Rolling restart
kubectl rollout status deployment/web # Watch rollout
Patching Resources
kubectl patch node node1 -p '{"spec": {"unschedulable": true}}'
# Strategic merge patch
kubectl patch pod app-pod -p '{
"spec": {"containers":[{"name":"app","image":"new-image"}]}
}'
# JSON patch
kubectl patch pod app-pod --type=json -p='[
{"op":"replace","path":"/spec/containers/0/image","value":"new-image"}
]'
Editing Resources
kubectl edit svc/web-service
KUBE_EDITOR="nano" kubectl edit deployment/web
Change between:
ClusterIP
NodePort
LoadBalancer
ExternalName
Port
Targetport
NodePort
Protocol
Scaling Resources
kubectl scale deployment/web --replicas=5
kubectl scale -f deployment.yaml --replicas=4
Deleting Resources
kubectl delete -f ./app.yaml
kubectl delete pod my-pod --now
kubectl delete pods,svc -l app=web
kubectl delete pod,svc --all -n test
Interacting With Running Pods
kubectl logs my-pod
kubectl logs -f my-pod
kubectl exec my-pod -- ls /
kubectl exec -it my-pod -- sh
kubectl port-forward svc/web 8080:80
Copying Files to and from Containers
kubectl cp /tmp/localfile my-pod:/tmp/remote
kubectl cp my-pod:/tmp/remote /tmp/localfile
Advanced (using tar):
tar cf - . | kubectl exec -i my-pod -- tar xf - -C /tmp
Interacting With Nodes and Cluster
kubectl cordon node1
kubectl drain node1
kubectl uncordon node1
kubectl top node
kubectl top pod
kubectl cluster-info
kubectl cluster-info dump
Discovering API Resources
kubectl api-resources
kubectl api-resources --namespaced=true
kubectl api-resources -o wide
kubectl api-resources --verbs=list,get
Kubectl Output Formatting
kubectl get pods -o json
kubectl get pods -o yaml
kubectl get pods -o wide
kubectl get pods -o name
kubectl get pods -o jsonpath='{.items[*].metadata.name}'
Custom columns:
kubectl get pods -A -o=custom-columns='IMAGE:spec.containers[*].image'
Kubectl Verbosity and Debugging
- –v=0 Minimal logs
- –v=2 Recommended default
- –v=4 Debug level
- –v=6+ Full HTTP request inspection
Production-Ready Deployment YAML (Corrected)
Below is a cleaned-up and production-ready Deployment YAML based on your original example.
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
namespace: my-namespace
labels:
app: nginx
spec:
replicas: 3
revisionHistoryLimit: 5
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 1000
containers:
- name: nginx
image: nginx:1.25
ports:
- containerPort: 80
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "300m"
memory: "256Mi"
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 3
periodSeconds: 10
livenessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 10
periodSeconds: 20
securityContext:
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
Conclusion
Kubernetes Cheat Sheet
This Kubernetes cheat sheet is a comprehensive and practical reference for working with kubectl, managing kubeconfig files, deploying Kubernetes workloads, viewing and troubleshooting cluster resources, and interacting with running workloads. It also includes a corrected production-ready Deployment YAML example. Everything below is ready to copy and paste directly into your WordPress editor.
kubectl Context and Configuration
Manage which Kubernetes cluster kubectl communicates with, and configure authentication and namespace defaults.
kubectl config view # View merged kubeconfig
# Use multiple kubeconfig files simultaneously
export KUBECONFIG=~/.kube/config:~/.kube/kubconfig2
kubectl config view
# Extract a specific user's password
kubectl config view -o jsonpath='{.users[?(@.name == "e2e")].user.password}'
# List users
kubectl config view -o jsonpath='{.users[*].name}'
# Context management
kubectl config get-contexts # List contexts
kubectl config current-context # Show active context
kubectl config use-context my-cluster # Switch context
# Add a cluster entry
kubectl config set-cluster my-cluster
# Set proxy URL for cluster entry
kubectl config set-cluster my-cluster --proxy-url=my-proxy-url
# Add a user with basic authentication
kubectl config set-credentials kubeuser/foo.kubernetes.com \
--username=kubeuser --password=kubepassword
# Set default namespace for current context
kubectl config set-context --current --namespace=production
# Set a new context with specific namespace and user
kubectl config set-context gce --user=cluster-admin --namespace=foo \
&& kubectl config use-context gce
# Delete a user
kubectl config unset users.foo
Helpful aliases:
# Quickly switch or show context
alias kx='f() { [ "$1" ] && kubectl config use-context $1 || kubectl config current-context ; } ; f'
# Quickly switch or show namespace
alias kn='f() { [ "$1" ] && kubectl config set-context --current --namespace $1 \
|| kubectl config view --minify | grep namespace | cut -d" " -f6 ; } ; f'
kubectl apply (Declarative Management)
kubectl apply is the recommended method for managing resources in production. It creates or updates resources by applying a desired state.
kubectl apply -f ./app.yaml # Apply single file
kubectl apply -f ./manifests/ # Apply directory
kubectl apply -f https://example.com/app.yaml # Apply from URL
kubectl create deployment nginx --image=nginx # Quick one-shot deployment
Create multiple manifests via stdin:
cat <
Create a secret:
cat <
Viewing and Finding Resources
kubectl get pods # Pods in namespace
kubectl get pods -A # All namespaces
kubectl get pods -o wide # Pod node placement
kubectl get deployments # Deployments
kubectl get svc # Services
kubectl describe pod my-pod # Detailed pod info
kubectl describe node my-node # Node details
Sorting:
kubectl get pods --sort-by='.status.containerStatuses[0].restartCount'
kubectl get pv --sort-by=.spec.capacity.storage
Field and label selectors:
kubectl get pods --field-selector=status.phase=Running
kubectl get pods -l app=web
kubectl get nodes --selector='!node-role.kubernetes.io/control-plane'
Retrieve specific fields:
kubectl get configmap myconfig -o jsonpath='{.data.ca\.crt}'
kubectl get secret my-secret -o jsonpath='{.data.username}' | base64 --decode
Updating Resources and Rolling Updates
kubectl set image deployment/web web=nginx:1.25 # Update image
kubectl rollout history deployment/web # View history
kubectl rollout undo deployment/web # Roll back
kubectl rollout restart deployment/web # Rolling restart
kubectl rollout status deployment/web # Watch rollout
Patching Resources
kubectl patch node node1 -p '{"spec": {"unschedulable": true}}'
# Strategic merge patch
kubectl patch pod app-pod -p '{
"spec": {"containers":[{"name":"app","image":"new-image"}]}
}'
# JSON patch
kubectl patch pod app-pod --type=json -p='[
{"op":"replace","path":"/spec/containers/0/image","value":"new-image"}
]'
Editing Resources
kubectl edit svc/web-service
KUBE_EDITOR="nano" kubectl edit deployment/web
Scaling Resources
kubectl scale deployment/web --replicas=5
kubectl scale -f deployment.yaml --replicas=4
Deleting Resources
kubectl delete -f ./app.yaml
kubectl delete pod my-pod --now
kubectl delete pods,svc -l app=web
kubectl delete pod,svc --all -n test
Interacting With Running Pods
kubectl logs my-pod
kubectl logs -f my-pod
kubectl exec my-pod -- ls /
kubectl exec -it my-pod -- sh
kubectl port-forward svc/web 8080:80
Copying Files to and from Containers
kubectl cp /tmp/localfile my-pod:/tmp/remote
kubectl cp my-pod:/tmp/remote /tmp/localfile
Advanced (using tar):
tar cf - . | kubectl exec -i my-pod -- tar xf - -C /tmp
Interacting With Nodes and Cluster
kubectl cordon node1
kubectl drain node1
kubectl uncordon node1
kubectl top node
kubectl top pod
kubectl cluster-info
kubectl cluster-info dump
Discovering API Resources
kubectl api-resources
kubectl api-resources --namespaced=true
kubectl api-resources -o wide
kubectl api-resources --verbs=list,get
Kubectl Output Formatting
kubectl get pods -o json
kubectl get pods -o yaml
kubectl get pods -o wide
kubectl get pods -o name
kubectl get pods -o jsonpath='{.items[*].metadata.name}'
Custom columns:
kubectl get pods -A -o=custom-columns='IMAGE:spec.containers[*].image'
Kubectl Verbosity and Debugging
- –v=0 Minimal logs
- –v=2 Recommended default
- –v=4 Debug level
- –v=6+ Full HTTP request inspection
Production-Ready Deployment YAML (Corrected)
Below is a cleaned-up and production-ready Deployment YAML based on your original example.
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
namespace: my-namespace
labels:
app: nginx
spec:
replicas: 3
revisionHistoryLimit: 5
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 1000
containers:
- name: nginx
image: nginx:1.25
ports:
- containerPort: 80
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "300m"
memory: "256Mi"
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 3
periodSeconds: 10
livenessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 10
periodSeconds: 20
securityContext:
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
Conclusion
This Kubernetes cheat sheet provides a complete quick-reference for daily cluster operations, including context switching, applying manifests, rolling updates, patching, scaling, and debugging. With the included production-ready Deployment YAML and working examples, you can confidently operate Kubernetes clusters and deploy applications using the recommended declarative approach.
How to Create a Docker Image for Kubernetes to Deploy
1. What Is a Docker Image?
A Docker image is a read-only, portable template that contains everything required to run your application:
- Application code
- Dependencies and libraries
- Operating system base layer
- Runtime (Node, Python, Go, etc.)
- Startup command
Kubernetes uses these images to create containers inside Pods. Therefore, creating a clean, reliable Docker image is the foundation of a successful deployment.
2. Creating a Dockerfile
The first step is writing a Dockerfile. This defines how your application is built. Below is a simple example for a Node.js web application:
# Dockerfile
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install --omit=dev
COPY . .
EXPOSE 3000
CMD ["npm", "start"]
Explanation:
FROM node:18-alpinechooses a lightweight base image.WORKDIR /appsets the working directory.COPYandnpm installinstall dependencies.EXPOSE 3000informs Kubernetes which port the app listens on.CMDruns the application.
3. Building a Docker Image
Once your Dockerfile is ready, build the image:
docker build -t my-app:1.0.0 .
This creates an image called my-app with the version 1.0.0. It is good practice to use semantic versioning or Git commit hashes as tags.
4. Tagging the Image for Your Registry
To deploy to Kubernetes, your image must be stored in a registry such as:
- Docker Hub
- GitHub Container Registry
- Amazon ECR
- Google Artifact Registry
- Azure Container Registry
Tag the image with your registry path:
docker tag my-app:1.0.0 myregistry/my-app:1.0.0
5. Pushing the Image to the Registry
Push the image so Kubernetes can pull it:
docker push myregistry/my-app:1.0.0
At this point, your container image is globally accessible to your Kubernetes cluster.
6. Creating a Kubernetes Deployment
Once the image is in the registry, create a Kubernetes Deployment manifest to run the image. Below is a production-ready example:
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
namespace: production
labels:
app: my-app
spec:
replicas: 3
revisionHistoryLimit: 5
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 1000
containers:
- name: my-app
image: myregistry/my-app:1.0.0
ports:
- containerPort: 3000
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "300m"
memory: "256Mi"
readinessProbe:
httpGet:
path: /
port: 3000
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet:
path: /
port: 3000
initialDelaySeconds: 10
periodSeconds: 20
securityContext:
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
7. Applying the Deployment
To deploy your application:
kubectl apply -f deployment.yaml
Kubernetes will:
- Pull the image from the registry
- Create Pods
- Perform a rolling update if an older version exists
- Attach readiness and liveness probes
- Maintain the desired replica count
8. Verifying the Deployment
kubectl get deploy my-app -n production
kubectl get pods -n production -o wide
kubectl describe deploy my-app -n production
These commands will confirm that Kubernetes successfully rolled out your new Docker image.
