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-sa in the prod-app namespace.
  • 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:

  • Role app-read-config:
    • Allows reading (get, list) ConfigMaps and Secrets in this namespace.
  • RoleBinding:
    • Attaches that Role to the app-sa ServiceAccount.
    • Any pod running as app-sa can now read ConfigMaps and Secrets in prod-app.

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-sa ServiceAccount, inheriting its RBAC permissions.
  • Injects configuration from ConfigMap and Secret.
  • 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.com to app-service on port 80.
  • Uses app-tls-secret for 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-deployment Deployment.
  • 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-alpine chooses a lightweight base image.
  • WORKDIR /app sets the working directory.
  • COPY and npm install install dependencies.
  • EXPOSE 3000 informs Kubernetes which port the app listens on.
  • CMD runs 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.

0