Web3 / AI 笔记已发布·公开可见·
golang的Docker服务
🐹 Golang 后端服务 Dockerfile 编写完整教程
▾代码块DOCKERFILE自动换行123456789FROM golang:1.21 WORKDIR /app COPY . . RUN go build -o main . CMD ["./main"]
问题:
▾代码块DOCKERFILE自动换行123456789101112131415161718192021222324252627282930# 构建阶段 FROM golang:1.21-alpine AS builder WORKDIR /app # 复制依赖文件 COPY go.mod go.sum ./ # 下载依赖 RUN go mod download # 复制源代码 COPY . . # 编译应用 RUN go build -o main . # 运行阶段 FROM alpine:latest WORKDIR /app # 从构建阶段复制可执行文件 COPY /app/main . # 暴露端口 EXPOSE 8080 # 运行应用 CMD ["./main"]
优势:
▾代码块DOCKERFILE自动换行12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667# ============================================ # 构建阶段 # ============================================ FROM golang:1.21-alpine AS builder # 安装构建依赖 RUN apk add --no-cache git ca-certificates tzdata # 设置工作目录 WORKDIR /app # 优化:先复制依赖文件,利用 Docker 缓存 COPY go.mod go.sum ./ # 下载依赖(这层会被缓存) RUN go mod download && go mod verify # 复制源代码 COPY . . # 编译优化参数 # -ldflags="-w -s" : 去除调试信息,减小体积 # -trimpath : 去除文件路径信息 # CGO_ENABLED=0 : 禁用 CGO,生成静态链接二进制 RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \ -ldflags="-w -s" \ -trimpath \ -o main . # ============================================ # 运行阶段 # ============================================ FROM alpine:latest # 安装运行时依赖 RUN apk add --no-cache ca-certificates tzdata # 创建非 root 用户 RUN addgroup -g 1001 -S appuser && \ adduser -S appuser -u 1001 -G appuser WORKDIR /app # 复制时区数据 COPY /usr/share/zoneinfo /usr/share/zoneinfo # 复制可执行文件 COPY /app/main . # 复制配置文件(如果有) # COPY --from=builder /app/config ./config # 更改文件所有者 RUN chown -R appuser:appuser /app # 切换到非 root 用户 USER appuser # 暴露端口 EXPOSE 8080 # 健康检查 HEALTHCHECK \ CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1 # 运行应用 CMD ["./main"]
▾代码块DOCKERFILE自动换行12345678910111213141516171819202122232425262728# 构建阶段 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="-w -s" \ -a -installsuffix cgo \ -o main . # 运行阶段 - 使用 distroless FROM gcr.io/distroless/static-debian11 WORKDIR /app COPY /app/main . EXPOSE 8080 USER nonroot:nonroot CMD ["./main"]
特点:
▾代码块DOCKERFILE自动换行12345678910111213141516171819202122232425262728293031FROM 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="-w -s -extldflags '-static'" \ -a \ -o main . # 从空白镜像开始 FROM scratch # 复制 CA 证书(用于 HTTPS 请求) COPY /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ # 复制时区数据 COPY /usr/share/zoneinfo /usr/share/zoneinfo WORKDIR /app COPY /app/main . EXPOSE 8080 CMD ["./main"]
特点:
▾代码块DOCKERFILE自动换行12345678910111213141516171819202122232425262728293031# 构建阶段 FROM golang:1.21-alpine AS builder # 安装 CGO 依赖 RUN apk add --no-cache gcc musl-dev WORKDIR /app COPY go.mod go.sum ./ RUN go mod download COPY . . # 启用 CGO RUN CGO_ENABLED=1 GOOS=linux go build \ -ldflags="-w -s" \ -o main . # 运行阶段 FROM alpine:latest # 安装运行时库 RUN apk add --no-cache libc6-compat WORKDIR /app COPY /app/main . EXPOSE 8080 CMD ["./main"]
▾代码块DOCKERFILE自动换行1234567891011121314151617181920212223242526272829FROM golang:1.21-alpine AS builder WORKDIR /app COPY go.mod go.sum ./ RUN go mod download COPY . . RUN CGO_ENABLED=0 go build -ldflags="-w -s" -o main . # 运行阶段 FROM alpine:latest WORKDIR /app # 复制二进制文件 COPY /app/main . # 复制静态资源 COPY /app/static ./static COPY /app/templates ./templates # 复制配置文件 COPY /app/config.yaml . EXPOSE 8080 CMD ["./main"]
▾代码块DOCKERFILE自动换行123456789101112FROM golang:1.21-alpine AS builder WORKDIR /app # ✅ 先复制依赖文件 COPY go.mod go.sum ./ RUN go mod download # ✅ 后复制源代码(源代码变更频繁) COPY . . RUN go build -o main .
创建 .dockerignore 文件:
▾代码块GITIGNORE自动换行1234567891011121314151617181920212223242526272829303132333435# Git .git .gitignore # IDE .vscode .idea *.swp *.swo # 构建产物 *.exe *.test *.out main # 依赖 vendor/ # 文档 *.md LICENSE # CI/CD .github .gitlab-ci.yml # 测试 *_test.go testdata/ # 临时文件 *.tmp *.log .DS_Store
▾代码块DOCKERFILE自动换行123456789# 完整的编译优化 RUN CGO_ENABLED=0 \ GOOS=linux \ GOARCH=amd64 \ go build \ -ldflags="-w -s -X main.version=${VERSION} -X main.buildTime=${BUILD_TIME}" \ -trimpath \ -o main \ ./cmd/server
参数说明:
-ldflags="-w -s" - 去除调试信息和符号表-trimpath - 去除文件系统路径-X - 设置编译时变量CGO_ENABLED=0 - 禁用 CGO,生成静态二进制▾代码块DOCKERFILE自动换行123456789101112131415161718192021222324FROM golang:1.21-alpine AS builder ARG TARGETPLATFORM ARG BUILDPLATFORM ARG TARGETOS ARG TARGETARCH WORKDIR /app COPY go.mod go.sum ./ RUN go mod download COPY . . RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} \ go build -ldflags="-w -s" -o main . FROM alpine:latest WORKDIR /app COPY /app/main . CMD ["./main"]
构建多架构镜像:
▾代码块Bash自动换行1docker buildx build --platform linux/amd64,linux/arm64 -t myapp:latest .
▾代码块DOCKERFILE自动换行1234567891011# 方式 1: 使用内置健康检查端点 HEALTHCHECK \ CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1 # 方式 2: 使用 curl HEALTHCHECK \ CMD curl -f http://localhost:8080/health || exit 1 # 方式 3: 使用自定义脚本 COPY healthcheck.sh / HEALTHCHECK CMD ["/healthcheck.sh"]
▾代码块DOCKERFILE自动换行123456789101112131415FROM alpine:latest WORKDIR /app COPY main . # 设置默认环境变量 ENV APP_ENV=production \ APP_PORT=8080 \ LOG_LEVEL=info # 可在运行时覆盖 EXPOSE ${APP_PORT} CMD ["./main"]
运行时覆盖:
▾代码块Bash自动换行1docker run -e APP_ENV=development -e APP_PORT=3000 myapp
项目结构:
▾代码块Plain Text自动换行1234567891011121314myapp/ ├── cmd/ │ └── server/ │ └── main.go ├── internal/ │ ├── handler/ │ ├── service/ │ └── repository/ ├── config/ │ └── config.yaml ├── go.mod ├── go.sum ├── Dockerfile └── .dockerignore
完整 Dockerfile:
▾代码块DOCKERFILE自动换行12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667# ============================================ # 构建阶段 # ============================================ FROM golang:1.21-alpine AS builder # 构建参数 ARG VERSION=dev ARG BUILD_TIME # 安装依赖 RUN apk add --no-cache git ca-certificates tzdata WORKDIR /app # 缓存依赖 COPY go.mod go.sum ./ RUN go mod download && go mod verify # 复制源代码 COPY . . # 编译 RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \ -ldflags="-w -s \ -X main.version=${VERSION} \ -X main.buildTime=${BUILD_TIME}" \ -trimpath \ -o server \ ./cmd/server # ============================================ # 运行阶段 # ============================================ FROM alpine:latest # 元数据 LABEL maintainer="your-email@example.com" \ version="${VERSION}" \ description="My Web API Service" # 安装运行时依赖 RUN apk add --no-cache ca-certificates tzdata && \ addgroup -g 1001 -S appuser && \ adduser -S appuser -u 1001 -G appuser WORKDIR /app # 复制文件 COPY /app/server . COPY /app/config ./config # 权限设置 RUN chown -R appuser:appuser /app USER appuser # 环境变量 ENV TZ=Asia/Shanghai \ GIN_MODE=release EXPOSE 8080 # 健康检查 HEALTHCHECK \ CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1 CMD ["./server"]
▾代码块DOCKERFILE自动换行123456789101112131415161718192021222324252627282930313233FROM golang:1.21-alpine AS builder # 安装 protoc 和相关工具 RUN apk add --no-cache git protobuf-dev && \ go install google.golang.org/protobuf/cmd/protoc-gen-go@latest && \ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest WORKDIR /app COPY go.mod go.sum ./ RUN go mod download COPY . . # 生成 protobuf 代码(如果需要) # RUN protoc --go_out=. --go-grpc_out=. proto/*.proto # 编译 RUN CGO_ENABLED=0 go build -ldflags="-w -s" -o grpc-server ./cmd/server # 运行阶段 FROM alpine:latest RUN apk add --no-cache ca-certificates WORKDIR /app COPY /app/grpc-server . # gRPC 端口 EXPOSE 50051 CMD ["./grpc-server"]
▾代码块DOCKERFILE自动换行123456789101112131415161718192021222324252627282930FROM golang:1.21-alpine AS builder WORKDIR /app COPY go.mod go.sum ./ RUN go mod download COPY . . RUN CGO_ENABLED=0 go build -ldflags="-w -s" -o cronjob ./cmd/cronjob FROM alpine:latest # 安装 cronie(如果需要系统级 cron) RUN apk add --no-cache ca-certificates tzdata WORKDIR /app COPY /app/cronjob . COPY /app/config ./config # 设置时区 ENV TZ=Asia/Shanghai # 定时任务不需要暴露端口 # 但可能需要健康检查文件 HEALTHCHECK \ CMD test -f /tmp/healthy || exit 1 CMD ["./cronjob"]
▾代码块DOCKERFILE自动换行1234567891011121314151617181920212223242526272829303132333435363738394041424344# ============================================ # 构建阶段 # ============================================ FROM golang:1.21-alpine AS builder RUN apk add --no-cache git ca-certificates WORKDIR /app COPY go.mod go.sum ./ RUN go mod download COPY . . # 编译主服务 RUN CGO_ENABLED=0 go build -ldflags="-w -s" -o api ./cmd/api # 编译迁移工具 RUN CGO_ENABLED=0 go build -ldflags="-w -s" -o migrate ./cmd/migrate # ============================================ # 运行阶段 # ============================================ FROM alpine:latest RUN apk add --no-cache ca-certificates tzdata postgresql-client WORKDIR /app # 复制二进制文件 COPY /app/api . COPY /app/migrate . # 复制迁移脚本 COPY /app/migrations ./migrations # 复制启动脚本 COPY docker-entrypoint.sh /usr/local/bin/ RUN chmod +x /usr/local/bin/docker-entrypoint.sh EXPOSE 8080 ENTRYPOINT ["docker-entrypoint.sh"] CMD ["./api"]
启动脚本 docker-entrypoint.sh:
▾代码块Bash自动换行12345678910111213141516#!/bin/sh set -e # 等待数据库就绪 echo "Waiting for database..." until pg_isready -h $DB_HOST -p $DB_PORT -U $DB_USER do echo "Database is unavailable - sleeping" sleep 1 done echo "Database is up - executing migrations" ./migrate up echo "Starting application" exec "$@"
▾代码块Bash自动换行12345678# 构建镜像 docker build -t myapp:latest . # 查看镜像大小 docker images myapp # 运行容器 docker run -d -p 8080:8080 --name myapp myapp:latest
▾代码块Bash自动换行12345# 传递构建参数 docker build \ --build-arg VERSION=1.0.0 \ --build-arg BUILD_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ") \ -t myapp:1.0.0 .
▾代码块YAML自动换行1234567891011121314151617181920212223242526272829303132version: '3.8' services: app: build: context: . dockerfile: Dockerfile args: VERSION: "1.0.0" ports: - "8080:8080" environment: - DB_HOST=postgres - DB_PORT=5432 - DB_USER=admin - DB_PASSWORD=secret depends_on: - postgres restart: unless-stopped postgres: image: postgres:15-alpine environment: - POSTGRES_USER=admin - POSTGRES_PASSWORD=secret - POSTGRES_DB=myapp volumes: - postgres_data:/var/lib/postgresql/data restart: unless-stopped volumes: postgres_data:
运行:
▾代码块Bash自动换行123docker-compose up -d docker-compose logs -f app docker-compose down
▾代码块Bash自动换行1234567# 查看镜像历史 docker history myapp:latest # 使用 dive 深度分析 docker run --rm -it \ -v /var/run/docker.sock:/var/run/docker.sock \ wagoodman/dive:latest myapp:latest
▾代码块Bash自动换行1234567891011121314# Golang 官方镜像 FROM golang:1.21 # ~900MB # Alpine 版本 FROM golang:1.21-alpine # ~300MB # 多阶段 + Alpine # 最终镜像 # ~20MB # Distroless # 最终镜像 # ~10MB # Scratch # 最终镜像 # ~5MB
▾代码块DOCKERFILE自动换行12345678910# ❌ 不好的做法 COPY . . RUN go mod download RUN go build -o main . # ✅ 好的做法 COPY go.mod go.sum ./ RUN go mod download COPY . . RUN go build -o main .
▾代码块DOCKERFILE自动换行12345678# 方法 1: 复制时区数据 FROM alpine:latest RUN apk add --no-cache tzdata ENV TZ=Asia/Shanghai # 方法 2: 从构建阶段复制 COPY /usr/share/zoneinfo /usr/share/zoneinfo ENV TZ=Asia/Shanghai
▾代码块DOCKERFILE自动换行123456# 方法 1: 安装 ca-certificates FROM alpine:latest RUN apk add --no-cache ca-certificates # 方法 2: 从构建阶段复制 COPY /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
▾代码块Go自动换行123456789101112131415// 使用 embed 嵌入静态文件 package main import ( "embed" "net/http" ) //go:embed static/* var staticFiles embed.FS func main() { http.Handle("/static/", http.FileServer(http.FS(staticFiles))) http.ListenAndServe(":8080", nil) }
▾代码块Go自动换行123456789101112131415// 在应用启动时等待数据库 import ( "database/sql" "time" ) func waitForDB(db *sql.DB, maxRetries int) error { for i := 0; i < maxRetries; i++ { if err := db.Ping(); err == nil { return nil } time.Sleep(2 * time.Second) } return fmt.Errorf("database not ready after %d retries", maxRetries) }
开发环境 Dockerfile:
▾代码块DOCKERFILE自动换行12345678910111213FROM golang:1.21-alpine # 安装 air(热重载工具) RUN go install github.com/cosmtrek/air@latest WORKDIR /app COPY go.mod go.sum ./ RUN go mod download COPY . . CMD ["air", "-c", ".air.toml"]
.air.toml 配置:
▾代码块TOML自动换行12345678910111213141516171819202122232425262728293031323334root = "." testdata_dir = "testdata" tmp_dir = "tmp" [build] args_bin = [] bin = "./tmp/main" cmd = "go build -o ./tmp/main ." delay = 1000 exclude_dir = ["assets", "tmp", "vendor", "testdata"] exclude_file = [] exclude_regex = ["_test.go"] exclude_unchanged = false follow_symlink = false full_bin = "" include_dir = [] include_ext = ["go", "tpl", "tmpl", "html"] kill_delay = "0s" log = "build-errors.log" send_interrupt = false stop_on_error = true [color] app = "" build = "yellow" main = "magenta" runner = "green" watcher = "cyan" [log] time = false [misc] clean_on_exit = false
| 基础镜像 | 镜像大小 | 构建时间 | 启动时间 | 内存占用 |
|---|---|---|---|---|
| golang:1.21 | ~900MB | 2m 30s | 1.5s | 50MB |
| golang:1.21-alpine | ~300MB | 2m 00s | 1.2s | 40MB |
| 多阶段 + alpine | ~20MB | 2m 10s | 0.8s | 35MB |
| 多阶段 + distroless | ~10MB | 2m 15s | 0.7s | 30MB |
| 多阶段 + scratch | ~5MB | 2m 20s | 0.5s | 25MB |
▾代码块DOCKERFILE自动换行1234567891011# syntax=docker/dockerfile:1.4 FROM golang:1.21-alpine AS builder # 使用缓存挂载加速依赖下载 RUN \ go mod download # 使用缓存挂载加速构建 RUN \ go build -o main .
构建:
▾代码块Bash自动换行1DOCKER_BUILDKIT=1 docker build -t myapp .
▾代码块Bash自动换行123456# 使用 Trivy 扫描漏洞 docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \ aquasec/trivy image myapp:latest # 使用 Snyk snyk container test myapp:latest
▾代码块Bash自动换行12345678# 启用 Docker Content Trust export DOCKER_CONTENT_TRUST=1 # 推送签名镜像 docker push myapp:latest # 拉取时自动验证 docker pull myapp:latest
▾代码块DOCKERFILE自动换行123456789101112131415161718# 生产环境推荐配置 FROM golang:1.21-alpine AS builder RUN apk add --no-cache git ca-certificates tzdata WORKDIR /app COPY go.mod go.sum ./ RUN go mod download COPY . . RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-w -s" -o main . FROM alpine:latest RUN apk add --no-cache ca-certificates tzdata && \ adduser -D -u 1001 appuser WORKDIR /app COPY /app/main . USER appuser EXPOSE 8080 HEALTHCHECK CMD wget -q --spider http://localhost:8080/health || exit 1 CMD ["./main"]
🚀 现在你已经掌握了编写高效 Golang Dockerfile 的所有技能!
Web3 用户 - Ethereum