VOOZH about

URL: https://dev.to/altradits/the-complete-docker-go-guide-from-beginner-to-kubernetes-expert-5epg

⇱ The Complete Docker + Go Guide: From Beginner to Kubernetes Expert - DEV Community


Click To Jump to your Favorite Topic Bellow:


Beginner: Why Docker + Go?

Why Docker + Go?

  • Tiny images (even 5–10 MB)
  • Fast startup (microseconds)
  • Predictable memory usage
  • No "works on my machine" bugs

Install Docker

Skip if Docker is already installed.

Mac / Windows

Docker Desktop

Linux

sudo apt install docker.io

Verify installation:

docker --version

Your First Go Container

2.1 Simple Go Program

main.go

package main

import (
 "fmt"
 "net/http"
)

func main() {
 http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
 fmt.Fprintf(w, "Hello, Docker!")
 })

 http.ListenAndServe(":8080", nil)
}

2.2 Write a Dockerfile

Dockerfile

FROM golang:1.22

WORKDIR /app

COPY go.mod go.sum* ./
RUN go mod download

COPY . .

RUN go build -o myapp .

EXPOSE 8080

CMD ["./myapp"]

2.3 Build & Run

docker build -t go-web .

docker run -p 8080:8080 go-web

Visit:

http://localhost:8080

2.4 Basic Commands

docker ps
docker stop <container>
docker rm <container>
docker images
docker rmi go-web

Intermediate: Optimizing Builds & Caching

3.1 Leverage Docker Layer Caching

Dependencies change less frequently than application code.

✅ Good

COPY go.mod go.sum ./
RUN go mod download

COPY . .

❌ Bad

COPY . .
RUN go mod download

3.2 Use .dockerignore

.git
*.log
tmp/
*.test
coverage.out
Dockerfile
README.md

3.3 Build with Tags & Versions

docker build -t myapp:1.0.0 .

docker tag myapp:1.0.0 myapp:latest

Multi-Stage Builds (The Right Way)

Go binaries don't need the Go compiler at runtime.

4.1 Two-Stage Dockerfile

## Stage 1: Build
FROMgolang:1.22ASbuilder

WORKDIR /build

COPY go.mod go.sum ./
RUN go mod download

COPY . .

RUN CGO_ENABLED=0 GOOS=linux \
 go build -ldflags="-s -w" \
 -o myapp .

## Stage 2: Run
FROM alpine:latest

RUN apk --no-cache add ca-certificates

WORKDIR /root/

COPY --from=builder /build/myapp .

EXPOSE 8080

CMD ["./myapp"]

Build Flags Explained

  • CGO_ENABLED=0 → Pure Go binary
  • GOOS=linux → Linux target
  • -ldflags="-s -w" → Strip debug symbols

Size Comparison

Image Approx Size
golang:1.22 ~800 MB
Alpine + Binary ~12 MB

4.2 Even Smaller: Scratch

FROM scratch

COPY --from=builder /build/myapp /myapp

EXPOSE 8080

ENTRYPOINT ["/myapp"]

Works when using CGO_ENABLED=0.


Distroless & Scratch (Minimal Images)

5.1 Google Distroless

FROM gcr.io/distroless/static-debian12

COPY --from=builder /build/myapp /myapp

ENTRYPOINT ["/myapp"]

Benefits

  • Includes CA certificates
  • Includes timezone data
  • No shell
  • No package manager
  • Reduced attack surface

5.2 Adding TLS to Scratch

FROM scratch

COPY --from=builder \
 /etc/ssl/certs/ca-certificates.crt \
 /etc/ssl/certs/

COPY --from=builder /build/myapp /myapp

ENTRYPOINT ["/myapp"]

Advanced: Debugging, Profiling, & Signal Handling

6.1 Graceful Shutdown (SIGTERM)

func main() {
 srv := &http.Server{Addr: ":8080"}

 go func() {
 if err := srv.ListenAndServe(); err != nil &&
 err != http.ErrServerClosed {
 log.Fatal(err)
 }
 }()

 quit := make(chan os.Signal, 1)

 signal.Notify(
 quit,
 syscall.SIGINT,
 syscall.SIGTERM,
 )

 <-quit

 ctx, cancel := context.WithTimeout(
 context.Background(),
 5*time.Second,
 )

 defer cancel()

 if err := srv.Shutdown(ctx); err != nil {
 log.Fatal(err)
 }
}

6.2 Debugging with Delve

FROM golang:1.22

RUN go install github.com/go-delve/delve/cmd/dlv@latest

CMD [
 "dlv",
 "debug",
 "--headless",
 "--listen=:40000",
 "--api-version=2",
 "--accept-multiclient"
]

Run:

docker run \
 -p 40000:40000 \
 -p 8080:8080 \
 debug-image

6.3 Profiling with pprof

import _ "net/http/pprof"
go tool pprof \
http://localhost:6060/debug/pprof/heap

Expert: Go Modules, Vendoring, & CI/CD Pipelines

7.1 Vendoring

go mod vendor
COPY vendor ./vendor

RUN go build -mod=vendor -o myapp .

7.2 GitHub Actions

name: Build and Push

on: push

jobs:
 build:
 runs-on: ubuntu-latest

 steps:
 - uses: actions/checkout@v4

 - uses: docker/setup-buildx-action@v3

 - uses: docker/login-action@v3
 with:
 username: ${{ secrets.DOCKER_USER }}
 password: ${{ secrets.DOCKER_TOKEN }}

 - uses: docker/build-push-action@v5
 with:
 push: true
 tags: user/app:latest
 cache-from: type=gha
 cache-to: type=gha,mode=max

7.3 Build Args

ARG VERSION=dev

RUN go build \
 -ldflags="-X main.version=$VERSION" \
 -o myapp .
docker build \
 --build-arg VERSION=1.2.3 \
 -t myapp .

Expert: Docker Compose for Go Microservices

version: "3.8"

services:
 api:
 build: .

 ports:
 - "8080:8080"

 environment:
 - DB_HOST=postgres
 - REDIS_ADDR=redis:6379

 depends_on:
 - postgres
 - redis

 restart: unless-stopped

 postgres:
 image: postgres:16-alpine

 environment:
 POSTGRES_PASSWORD: secret

 volumes:
 - pgdata:/var/lib/postgresql/data

 redis:
 image: redis:7-alpine

 ports:
 - "6379"

volumes:
 pgdata:

Run:

docker compose up -d

Expert to God: Kubernetes, Init Containers, & Sidecars

9.1 Kubernetes Deployment

apiVersion: apps/v1
kind: Deployment

metadata:
 name: go-app

spec:
 replicas: 3

 selector:
 matchLabels:
 app: go-app

 template:
 metadata:
 labels:
 app: go-app

 spec:
 containers:
 - name: app
 image: myapp:latest

 ports:
 - containerPort: 8080

9.2 Init Containers

initContainers:
 - name: migrate
 image: migrate/migrate

9.3 Sidecar Pattern

containers:
 - name: go-app
 image: myapp

 - name: log-shipper
 image: fluent/fluent-bit

Security & Best Practices Checklist

Area Recommendation
Base Image Use scratch, distroless, or alpine
User USER 10001
Read-only Root readOnlyRootFilesystem: true
Secrets Use env vars or secret managers
Scanning trivy image myapp
Labels OCI labels
Health Checks Add HEALTHCHECK
Timeouts Configure stop timeout
Resources Limit CPU and memory

Final Expert Wisdom

  • Use CGO_ENABLED=0 unless you require native C libraries.
  • Prefer scratch when possible.
  • Never store persistent data inside container filesystems.
  • Drop unnecessary Linux capabilities.
  • Use health checks everywhere.
  • Keep images small.
  • Treat containers as immutable.

You now know everything from docker run to orchestrating 10,000 Go containers on Kubernetes.

Go forth and containerize.