Anasayfa / Software / Automate Deployments with Docker and Kubernetes: An Advanced Step‑by‑Step Guide

Automate Deployments with Docker and Kubernetes: An Advanced Step‑by‑Step Guide

Docker Kubernetes automation

In today’s fast‑moving cloud era, manual deployments are a liability. By combining Docker’s lightweight containers with Kubernetes’ orchestration power, you can create a repeatable, zero‑touch pipeline that ships code from commit to production in minutes. This guide walks you through every piece of the puzzle—Dockerfiles, Helm charts, GitHub Actions, and common pitfalls—so you can build a rock‑solid, production‑grade automation workflow.

What You’ll Need

  • A Linux or macOS workstation with Docker Engine ≥ 20.10 installed.
  • Kubectl configured to talk to a Kubernetes cluster (minikube, kind, or a cloud‑hosted cluster).
  • Helm ≥ 3.10 for package management.
  • A Git repository (GitHub, GitLab, or Bitbucket) with push access.
  • An image registry (Docker Hub, GitHub Container Registry, or a private registry) and credentials for push/pull.

Step 1: Containerize the Application

Start by writing a minimal Dockerfile that captures all runtime dependencies. For a Node.js service, a typical file looks like this:

FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build

FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
EXPOSE 3000
CMD ["node", "dist/index.js"]

Key points:

  • Use multi‑stage builds to keep the final image small.
  • Never copy node_modules from the host; let the container install its own dependencies.
  • Expose only the ports your app actually uses.

Run docker build -t yourrepo/yourapp:dev . to verify the image builds locally.

Step 2: Build and Push the Docker Image

Automate the build with a CI workflow. Below is a GitHub Actions snippet that builds the image, logs into Docker Hub, and pushes the tag:

name: CI
on:
  push:
    branches: [ main ]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v2
      - name: Log in to Docker Hub
        uses: docker/login-action@v2
        with:
          username: ${{ secrets.DOCKERHUB_USER }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}
      - name: Build and push
        uses: docker/build-push-action@v4
        with:
          context: .
          push: true
          tags: yourrepo/yourapp:${{ github.sha }}

This step ensures every commit produces an immutable image identified by the commit SHA, which later stages will reference.

Step 3: Define Kubernetes Manifests

Next, describe how the container runs in the cluster. A minimal deployment.yaml and service.yaml might look like:

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: yourapp
  labels:
    app: yourapp
spec:
  replicas: 3
  selector:
    matchLabels:
      app: yourapp
  template:
    metadata:
      labels:
        app: yourapp
    spec:
      containers:
      - name: yourapp
        image: yourrepo/yourapp:${IMAGE_TAG}
        ports:
        - containerPort: 3000
        env:
        - name: NODE_ENV
          value: "production"
---
# service.yaml
apiVersion: v1
kind: Service
metadata:
  name: yourapp-svc
spec:
  selector:
    app: yourapp
  ports:
  - protocol: TCP
    port: 80
    targetPort: 3000
  type: LoadBalancer

Notice the ${IMAGE_TAG} placeholder. We’ll replace it dynamically in the CI pipeline so the deployment always points at the freshly built image.

Step 4: Package with Helm

Hard‑coding the image tag in raw manifests quickly becomes unmanageable. Helm charts let you templatize values and version the whole stack. Create the following structure:

yourapp/
  Chart.yaml
  values.yaml
  templates/
    deployment.yaml
    service.yaml

Chart.yaml:

apiVersion: v2
name: yourapp
description: A Helm chart for deploying YourApp
version: 0.1.0
appVersion: "1.0"

values.yaml (default values):

replicaCount: 3
image:
  repository: yourrepo/yourapp
  tag: "latest"
  pullPolicy: IfNotPresent
service:
  type: LoadBalancer
  port: 80

Move the raw manifests into templates/ and replace hard‑coded fields with Helm template syntax, e.g., {{ .Values.image.repository }}:{{ .Values.image.tag }}. Then, from your CI job, run:

helm upgrade --install yourapp ./yourapp 
  --set image.tag=${{ github.sha }} 
  --namespace production --create-namespace

This single command creates or updates the release, ensuring the new image is rolled out without manual kubectl apply.

Step 5: Set Up a Full CI/CD Pipeline

Combine the previous pieces into an end‑to‑end pipeline. Extend the GitHub Actions workflow:

      - name: Deploy to Kubernetes
        uses: azure/k8s-deploy@v4
        with:
          manifests: |
            ./k8s/*.yaml
          images: |
            yourrepo/yourapp:${{ github.sha }}
          helm-release: yourapp
          helm-chart: ./yourapp
          namespace: production
          token: ${{ secrets.KUBE_TOKEN }}
          kubeconfig: ${{ secrets.KUBE_CONFIG }}

Key settings:

  • images: passes the exact SHA‑tag to Helm.
  • token/kubeconfig: store cluster credentials as encrypted GitHub secrets.
  • –create-namespace: guarantees the namespace exists on first run.

When the workflow finishes, Kubernetes performs a rolling update, preserving zero‑downtime if you’ve configured readiness probes (add them to the deployment template).

Step 6: Verify and Monitor the Release

Automation is only as good as its observability. After deployment, run:

kubectl get pods -n production -l app=yourapp -w
kubectl describe deployment yourapp -n production

Set up kubectl port-forward or expose the service via an Ingress controller to run integration tests against the live endpoint. For production monitoring, integrate Prometheus metrics and Grafana dashboards; Helm charts for both are readily available.

Common Mistakes to Avoid

Even seasoned engineers stumble over a few recurring issues:

  • Hard‑coding image tags. Forgetting to replace ${IMAGE_TAG} leads to stale pods that never update.
  • Missing health probes. Without readinessProbe and livenessProbe, Kubernetes may consider a failing pod healthy, causing traffic loss during rollouts.
  • Running containers as root. This is a security risk; always specify a non‑root user in the Dockerfile.
  • Over‑committing resources. Setting CPU/memory limits too low triggers OOM kills; too high wastes cluster capacity.
  • Storing secrets in plain text. Use Kubernetes Secrets or external secret managers (e.g., HashiCorp Vault) instead of embedding passwords in values.yaml.

Address these early, and your pipeline will be far more reliable.

Tips and Tricks

Here are a few pro‑level shortcuts that smooth the workflow:

  • Use Docker BuildKit cache. Add --cache-from=type=registry,ref=yourrepo/yourapp:cache to speed up incremental builds.
  • Leverage Helm hooks. A pre-install hook can create a ConfigMap with environment‑specific values before the main chart deploys.
  • Implement GitOps. Tools like Argo CD or Flux watch a Git branch and automatically apply Helm releases, decoupling CI from CD.
  • Tag images with both SHA and semantic version. Push yourrepo/yourapp:${{ github.sha }} and yourrepo/yourapp:1.2.3 for easier rollback.
  • Enable Helm diff plugin. Run helm diff upgrade in CI to see exactly what changes will be applied before they happen.

Frequently Asked Questions

Do I need a separate Docker registry for each environment?

No. A single registry can host multiple tags—use naming conventions like dev‑, staging‑, and prod‑ to separate environments while sharing the same repository.

Can I roll back a failed deployment automatically?

Yes. Helm stores a history of releases. Running helm rollback yourapp 2 reverts to the previous successful revision. You can also add a GitHub Action step that triggers a rollback if health checks fail.

What’s the difference between kubectl apply and Helm upgrades?

kubectl apply works on raw manifests and lacks versioning, while Helm treats each release as a versioned package, handling rollbacks, templating, and dependency management out of the box.

Conclusion

Automating Docker and Kubernetes deployments transforms a chaotic, error‑prone process into a predictable, repeatable pipeline. By containerizing your app, pushing immutable images, templating with Helm, and wiring everything together in a CI/CD system, you gain rapid feedback, consistent environments, and the confidence to ship features at scale. Keep an eye on health probes, secret management, and resource limits, and you’ll enjoy smooth rollouts that keep your users happy and your ops team stress‑free.

Photo by Rubaitul Azad on Unsplash

Etiketlendi: