Hugo dan Docker: Containerization untuk Deployment Konsisten
Hugo dan Docker: Containerization untuk Deployment Konsisten
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.
Artikel Terkait
Link Postingan: https://www.tirinfo.com/hugo-docker/