Docker 让应用的部署变得标准化和可移植。无论在本地开发环境还是生产服务器,容器里的应用行为都是一致的。对于 Go 应用来说,Docker 化更是简单——一个静态编译的二进制文件,几乎不需要什么依赖。
最简单的 Dockerfile:
FROM golang:1.21
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN go build -o myapp
EXPOSE 8080
CMD ["./myapp"]
这个 Dockerfile 可以工作,但有几个问题:
推荐使用多阶段构建,分离编译环境和运行环境:
# 构建阶段
FROM golang:1.21-alpine AS builder
WORKDIR /app
# 安装依赖
RUN apk add --no-cache git
# 复制 go.mod 和 go.sum,利用缓存
COPY go.mod go.sum ./
RUN go mod download
# 复制源代码
COPY . .
# 编译
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o myapp
# 运行阶段
FROM alpine:latest
WORKDIR /app
# 安装 ca-certificates(HTTPS 需要)
RUN apk --no-cache add ca-certificates tzdata
# 从构建阶段复制二进制文件
COPY --from=builder /app/myapp .
# 设置时区
ENV TZ=Asia/Shanghai
EXPOSE 8080
CMD ["./myapp"]
这样构建出来的镜像只有 10-20MB。
如果不需要时区数据和 CA 证书,可以用更小的 scratch 镜像:
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o myapp
FROM scratch
COPY --from=builder /app/myapp /myapp
EXPOSE 8080
ENTRYPOINT ["/myapp"]
scratch 是空镜像,只有你放进去的文件,大小通常在 10MB 以内。
开发环境用 Docker Compose 编排多个服务:
docker-compose.yml:
version: '3.8'
services:
app:
build: .
ports:
- "8080:8080"
environment:
- GIN_MODE=debug
- DATABASE_URL=postgres://postgres:postgres@db:5432/myapp?sslmode=disable
- REDIS_URL=redis:6379
depends_on:
- db
- redis
volumes:
- ./uploads:/app/uploads
db:
image: postgres:15-alpine
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
- POSTGRES_DB=myapp
volumes:
- postgres_data:/var/lib/postgresql/data
ports:
- "5432:5432"
redis:
image: redis:7-alpine
ports:
- "6379:6379"
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./certs:/etc/nginx/certs:ro
depends_on:
- app
volumes:
postgres_data:
启动:
docker-compose up -d
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o myapp
FROM alpine:latest
RUN apk --no-cache add ca-certificates curl
WORKDIR /app
COPY --from=builder /app/myapp .
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8080/health || exit 1
EXPOSE 8080
CMD ["./myapp"]
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o myapp
FROM alpine:latest
RUN apk --no-cache add ca-certificates && \
addgroup -g 1000 -S appgroup && \
adduser -u 1000 -S appuser -G appgroup
WORKDIR /app
COPY --from=builder --chown=appuser:appgroup /app/myapp .
USER appuser
EXPOSE 8080
CMD ["./myapp"]
FROM alpine:latest
WORKDIR /app
# 默认环境变量
ENV GIN_MODE=release \
PORT=8080
COPY --from=builder /app/myapp .
EXPOSE 8080
CMD ["./myapp"]
运行时覆盖:
docker run -e GIN_MODE=debug -e PORT=3000 -p 3000:3000 myapp
把不常变化的层放前面:
# 好的做法
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN go build
# 不好的做法
COPY . .
RUN go mod download
RUN go build
# 不好的做法
RUN apk add --no-cache git
RUN apk add --no-cache curl
RUN apk add --no-cache tzdata
# 好的做法
RUN apk add --no-cache git curl tzdata
创建 .dockerignore 文件:
.git
.gitignore
.env
.env.*
*.md
Makefile
docker-compose*.yml
Dockerfile*
.dockerignore
tmp/
uploads/
*.test
*.out
deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
labels:
app: myapp
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: myapp:latest
ports:
- containerPort: 8080
env:
- name: GIN_MODE
value: "release"
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: myapp-secrets
key: database-url
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
service.yaml:
apiVersion: v1
kind: Service
metadata:
name: myapp
spec:
selector:
app: myapp
ports:
- port: 80
targetPort: 8080
type: ClusterIP
ingress.yaml:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
tls:
- hosts:
- example.com
secretName: myapp-tls
rules:
- host: example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: myapp
port:
number: 80
# 登录
docker login
# 打标签
docker tag myapp username/myapp:v1.0.0
# 推送
docker push username/myapp:v1.0.0
# 推送到私有仓库
docker tag myapp registry.example.com/myapp:v1.0.0
docker push registry.example.com/myapp:v1.0.0
Docker 容器化部署的关键点:
容器化让部署变得标准化,也便于扩展和迁移。掌握 Docker 是现代后端开发的必备技能。