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 builderInstall 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-networkOptional: 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-networkvolumes:
hugo_cache:networks:
hugo-network:
driver: bridgeDockerfile Development
# Dockerfile.dev FROM golang:1.21-alpineInstall 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 Pushon: 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=maxDocker 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-alpineCreate 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 verifyCopy source setelah dependencies
COPY . .
Troubleshooting Docker
Common Issues dan Solutions
# Check container logs docker logs hugo-containerExec 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