Lewati ke konten
Kembali ke Blog

Hugo dan Docker: Containerization untuk Deployment Konsisten

· · 7 menit baca

Docker telah menjadi standar industri untuk application containerization, memungkinkan development dan deployment yang konsisten across environments. Untuk Hugo sites, Docker dapat digunakan untuk create reproducible build environments, simplify deployment, dan enable modern DevOps practices. Panduan ini akan membahas cara mengintegrasikan Hugo dengan Docker untuk workflow development dan deployment yang efficient.

Docker memberikan konsistensi antara development dan production environments, eliminate “works on my machine” issues, dan simplify deployment ke berbagai platforms termasuk Kubernetes.

Docker Setup untuk Hugo

Dockerfile Dasar

# Dockerfile
FROM golang:1.21-alpine AS builder

Install Hugo Extended

ARG HUGO_VERSION=0.139.2 RUN wget -O /tmp/hugo.tar.gz \ https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_Linux-64bit.tar.gz \ && tar -xzf /tmp/hugo.tar.gz \ && mv hugo /usr/local/bin/ \ && rm /tmp/hugo.tar.gz

Set work directory

WORKDIR /app

Copy source files

COPY . .

Build Hugo site

RUN hugo --minify --environment production

Use nginx for serving

FROM nginx:alpine

Copy built files

COPY --from=builder /app/public /usr/share/nginx/html

Copy nginx config

COPY nginx.conf /etc/nginx/nginx.conf

Expose port

EXPOSE 80

Health check

HEALTHCHECK --interval=30s --timeout=3s \ CMD wget --no-verbose --tries=1 --spider http://localhost/ || exit 1

CMD ["nginx", "-g", "daemon off;"]

Nginx Configuration untuk Docker

# nginx.conf
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;

events { worker_connections 1024; }

http { include /etc/nginx/mime.types; default_type application/octet-stream;

log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                '$status $body_bytes_sent "$http_referer" '
                '"$http_user_agent" "$http_x_forwarded_for"';

access_log /var/log/nginx/access.log main;

sendfile on;
keepalive_timeout 65;
gzip on;

# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;

server {
    listen 80;
    server_name localhost;
    root /usr/share/nginx/html;
    index index.html;

    location / {
        try_files $uri $uri/ =404;
    }

    # Cache static assets
    location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
    }
}

}

Multi-Stage Builds

Optimized Multi-Stage Dockerfile

# syntax=docker/dockerfile:1
# ============
# Build Stage
# ============
FROM golang:1.21-alpine AS builder

Install Hugo

ARG HUGO_VERSION=0.139.2 ENV HUGO_VERSION=${HUGO_VERSION} RUN wget -q -O /tmp/hugo.tar.gz \ https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_Linux-64bit.tar.gz \ && tar -xf /tmp/hugo.tar.gz -C /tmp \ && mv /tmp/hugo /usr/local/bin/hugo \ && rm /tmp/hugo.tar.gz

Install git untuk submodule

RUN apk add --no-cache git

WORKDIR /src

Copy only necessary files first for better caching

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

Copy source

COPY . .

Build dengan environment production

ENV HUGO_ENVIRONMENT=production ENV HUGO_MINIFY=true

RUN hugo --gc --minify

============

Production Stage

============

FROM nginx:alpine AS production

Install security packages

RUN apk add --no-cache nginx-mod-http-geoip nginx-mod-http-image-filter \ && apk add --no-cache curl

Copy nginx configuration

COPY nginx/nginx.conf /etc/nginx/nginx.conf COPY nginx/conf.d /etc/nginx/conf.d

Copy built Hugo site

COPY --from=builder /src/public /usr/share/nginx/html

Create non-root user

RUN addgroup -g 1000 -S appgroup && \ adduser -u 1000 -S appuser -G appgroup && \ chown -R appuser:appgroup /usr/share/nginx/html && \ chown -R appuser:appgroup /var/cache/nginx && \ chown -R appuser:appgroup /var/log/nginx && \ touch /var/run/nginx.pid && \ chown -R appuser:appgroup /var/run/nginx.pid

USER appuser

EXPOSE 80

HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ CMD curl -f http://localhost/health || exit 1

CMD ["nginx", "-g", "daemon off;"]

Development Environment dengan Docker

Docker Compose untuk Development

# docker-compose.yml
version: '3.8'

services: hugo: build: context: . dockerfile: Dockerfile.dev container_name: hugo-dev command: hugo server --bind 0.0.0.0 --port 1313 -D ports:

  • "1313:1313"
volumes:
  - .:/src:cached
  - hugo_cache:/src/.hugo_cache
environment:
  - HUGO_ENVIRONMENT=development
networks:
  - hugo-network

Optional: Live reload dengan air

hugo-air:
build:
context: .
dockerfile: Dockerfile.air
container_name: hugo-air
ports:

  • "1314:1314"
volumes:
  - .:/src:cached
environment:
  - HUGO_ENV=development
networks:
  - hugo-network

volumes:
hugo_cache:

networks:
hugo-network:
driver: bridge

Dockerfile Development

# Dockerfile.dev
FROM golang:1.21-alpine

Install Hugo

ARG HUGO_VERSION=0.139.2 RUN wget -O /tmp/hugo.tar.gz \ https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_Linux-64bit.tar.gz \ && tar -xzf /tmp/hugo.tar.gz -C /tmp \ && mv /tmp/hugo /usr/local/bin/hugo \ && rm /tmp/hugo.tar.gz

Install git untuk submodule

RUN apk add --no-cache git

WORKDIR /src

Expose port

EXPOSE 1313

CMD ["sh"]

Docker dengan GitHub Actions

Build dan Push Docker Image

# .github/workflows/docker.yml
name: Docker Build and Push

on: push: branches: [main] tags: ['v*'] pull_request: branches: [main]

env: REGISTRY: ghcr.io IMAGE_NAME: ${{ github.repository }}

jobs: build: runs-on: ubuntu-latest permissions: contents: read packages: write

steps:
  - name: Checkout
    uses: actions/checkout@v4

  - name: Set up Docker Buildx
    uses: docker/setup-buildx-action@v3

  - name: Log in to Container Registry
    uses: docker/login-action@v3
    with:
      registry: ${{ env.REGISTRY }}
      username: ${{ github.actor }}
      password: ${{ secrets.GITHUB_TOKEN }}

  - name: Extract metadata
    id: meta
    uses: docker/metadata-action@v5
    with:
      images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
      tags: |
        type=ref,event=branch
        type=ref,event=tag
        type=raw,value=latest,enable={{is_default_branch}}

  - name: Build and push Docker image
    uses: docker/build-push-action@v5
    with:
      context: .
      push: ${{ github.event_name != 'pull_request' }}
      tags: ${{ steps.meta.outputs.tags }}
      labels: ${{ steps.meta.outputs.labels }}
      cache-from: type=gha
      cache-to: type=gha,mode=max

Docker untuk Multi-Platform Build

Build ARM dan AMD64

# docker-compose.ci.yml
services:
  builder:
    build:
      context: .
      dockerfile: Dockerfile
      platforms:
        - linux/amd64
        - linux/arm64
    image: hugo-builder:${TAG:-latest}

Kubernetes Deployment

Kubernetes Manifests

# k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hugo-site
  labels:
    app: hugo-site
spec:
  replicas: 3
  selector:
    matchLabels:
      app: hugo-site
  template:
    metadata:
      labels:
        app: hugo-site
    spec:
      containers:
        - name: hugo
          image: ghcr.io/username/hugo-site:latest
          ports:
            - containerPort: 80
          resources:
            requests:
              memory: "64Mi"
              cpu: "100m"
            limits:
              memory: "128Mi"
              cpu: "200m"
          livenessProbe:
            httpGet:
              path: /health
              port: 80
            initialDelaySeconds: 10
            periodSeconds: 30
          readinessProbe:
            httpGet:
              path: /health
              port: 80
            initialDelaySeconds: 5
            periodSeconds: 10

apiVersion: v1 kind: Service metadata: name: hugo-service spec: selector: app: hugo-site ports:

  • protocol: TCP port: 80 targetPort: 80

type: ClusterIP


apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: hugo-ingress annotations: kubernetes.io/tls-acme: "true" nginx.ingress.kubernetes.io/proxy-body-size: "10m" spec: tls:

  • hosts:
    • example.com

    secretName: hugo-tls

rules:

  • host: example.com http: paths:
    • path: / pathType: Prefix backend: service: name: hugo-service port: number: 80

Docker Best Practices

Image Size Optimization

# Multi-stage build untuk minimize image size
FROM golang:1.21-alpine AS builder
# Build commands...

FROM nginx:alpine AS production

Copy dari builder...

Final image kecil dan efficient

Security Best Practices

# Use specific version, not 'latest'
FROM golang:1.21-alpine

Create non-root user

RUN addgroup -g 1000 -S appgroup && \ adduser -u 1000 -S appuser -G appgroup

USER appuser

Don't run as root

Caching Strategy

# Copy go.mod pertama untuk leverage Docker cache
COPY go.mod go.sum* ./
RUN go mod download && go mod verify

Copy source setelah dependencies

COPY . .

Troubleshooting Docker

Common Issues dan Solutions

# Check container logs
docker logs hugo-container

Exec into container

docker exec -it hugo-container sh

Check container resources

docker stats hugo-container

Debug build issues

docker build --no-cache -t hugo-debug .

Kesimpulan

Docker containerization untuk Hugo sites memberikan konsistensi, portability, dan scalability. Dengan setup yang tepat, Anda dapat achieve reproducible builds dan simplified deployments across different environments.

Ditulis oleh

Hendra Wijaya

Tinggalkan Komentar

Email tidak akan ditampilkan.