MetaNode社区
找工作面试题库领SepoliaETH

© 2025 MetaNode社区. All rights reserved.

Powered by MetaNode

VIP

尊享永久会员

解锁所有面试题解,一次性买断

当前等级普通用户
限时优惠
¥129¥399

/永久

✓解锁全部企业高频面试题及高质量题解
✓参与模拟面试,获取百套模拟面试视频
✓加入永久会员交流群,专属答疑

点击按钮联系客服获取兑换码

扫码添加老师微信

获取兑换码 · 干货不错过

微信二维码
Logo

关注我们

B站抖音小红书

golang的Docker服务

A

匿名

18 天前

🐹 Golang 后端服务 Dockerfile 编写完整教程

从入门到精通:构建高效、安全的 Go 应用容器镜像


📑 目录

  • 基础入门
  • 多阶段构建
  • 生产级优化
  • 最佳实践
  • 实战案例
  • 常见问题

🎯 基础入门

最简单的 Dockerfile

DOCKERFILE
1FROM golang:1.21 2 3WORKDIR /app 4 5COPY . . 6 7RUN go build -o main . 8 9CMD ["./main"]

问题:

  • ❌ 镜像体积过大(~1GB+)
  • ❌ 包含完整的构建工具链
  • ❌ 存在安全风险

🚀 多阶段构建(推荐)

基础多阶段构建

DOCKERFILE
1# 构建阶段 2FROM golang:1.21-alpine AS builder 3 4WORKDIR /app 5 6# 复制依赖文件 7COPY go.mod go.sum ./ 8 9# 下载依赖 10RUN go mod download 11 12# 复制源代码 13COPY . . 14 15# 编译应用 16RUN go build -o main . 17 18# 运行阶段 19FROM alpine:latest 20 21WORKDIR /app 22 23# 从构建阶段复制可执行文件 24COPY --from=builder /app/main . 25 26# 暴露端口 27EXPOSE 8080 28 29# 运行应用 30CMD ["./main"]

优势:

  • ✅ 镜像体积缩小到 ~20MB
  • ✅ 只包含运行时必需的文件
  • ✅ 提高安全性

🏆 生产级优化

完整优化版本

DOCKERFILE
1# ============================================ 2# 构建阶段 3# ============================================ 4FROM golang:1.21-alpine AS builder 5 6# 安装构建依赖 7RUN apk add --no-cache git ca-certificates tzdata 8 9# 设置工作目录 10WORKDIR /app 11 12# 优化:先复制依赖文件,利用 Docker 缓存 13COPY go.mod go.sum ./ 14 15# 下载依赖(这层会被缓存) 16RUN go mod download && go mod verify 17 18# 复制源代码 19COPY . . 20 21# 编译优化参数 22# -ldflags="-w -s" : 去除调试信息,减小体积 23# -trimpath : 去除文件路径信息 24# CGO_ENABLED=0 : 禁用 CGO,生成静态链接二进制 25RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \ 26 -ldflags="-w -s" \ 27 -trimpath \ 28 -o main . 29 30# ============================================ 31# 运行阶段 32# ============================================ 33FROM alpine:latest 34 35# 安装运行时依赖 36RUN apk add --no-cache ca-certificates tzdata 37 38# 创建非 root 用户 39RUN addgroup -g 1001 -S appuser && \ 40 adduser -S appuser -u 1001 -G appuser 41 42WORKDIR /app 43 44# 复制时区数据 45COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo 46 47# 复制可执行文件 48COPY --from=builder /app/main . 49 50# 复制配置文件(如果有) 51# COPY --from=builder /app/config ./config 52 53# 更改文件所有者 54RUN chown -R appuser:appuser /app 55 56# 切换到非 root 用户 57USER appuser 58 59# 暴露端口 60EXPOSE 8080 61 62# 健康检查 63HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ 64 CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1 65 66# 运行应用 67CMD ["./main"]

📦 不同场景的 Dockerfile

1. 使用 Distroless(最小化镜像)

DOCKERFILE
1# 构建阶段 2FROM golang:1.21-alpine AS builder 3 4WORKDIR /app 5 6COPY go.mod go.sum ./ 7RUN go mod download 8 9COPY . . 10 11# 静态编译 12RUN CGO_ENABLED=0 GOOS=linux go build \ 13 -ldflags="-w -s" \ 14 -a -installsuffix cgo \ 15 -o main . 16 17# 运行阶段 - 使用 distroless 18FROM gcr.io/distroless/static-debian11 19 20WORKDIR /app 21 22COPY --from=builder /app/main . 23 24EXPOSE 8080 25 26USER nonroot:nonroot 27 28CMD ["./main"]

特点:

  • 镜像体积 ~5-10MB
  • 只包含应用和运行时库
  • 无 shell,更安全

2. 使用 Scratch(极致精简)

DOCKERFILE
1FROM golang:1.21-alpine AS builder 2 3WORKDIR /app 4 5COPY go.mod go.sum ./ 6RUN go mod download 7 8COPY . . 9 10# 完全静态编译 11RUN CGO_ENABLED=0 GOOS=linux go build \ 12 -ldflags="-w -s -extldflags '-static'" \ 13 -a \ 14 -o main . 15 16# 从空白镜像开始 17FROM scratch 18 19# 复制 CA 证书(用于 HTTPS 请求) 20COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ 21 22# 复制时区数据 23COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo 24 25WORKDIR /app 26 27COPY --from=builder /app/main . 28 29EXPOSE 8080 30 31CMD ["./main"]

特点:

  • 镜像体积 ~5MB
  • 绝对最小化
  • 仅适用于完全静态编译的应用

3. 包含 CGO 依赖

DOCKERFILE
1# 构建阶段 2FROM golang:1.21-alpine AS builder 3 4# 安装 CGO 依赖 5RUN apk add --no-cache gcc musl-dev 6 7WORKDIR /app 8 9COPY go.mod go.sum ./ 10RUN go mod download 11 12COPY . . 13 14# 启用 CGO 15RUN CGO_ENABLED=1 GOOS=linux go build \ 16 -ldflags="-w -s" \ 17 -o main . 18 19# 运行阶段 20FROM alpine:latest 21 22# 安装运行时库 23RUN apk add --no-cache libc6-compat 24 25WORKDIR /app 26 27COPY --from=builder /app/main . 28 29EXPOSE 8080 30 31CMD ["./main"]

4. 包含静态资源和配置

DOCKERFILE
1FROM golang:1.21-alpine AS builder 2 3WORKDIR /app 4 5COPY go.mod go.sum ./ 6RUN go mod download 7 8COPY . . 9 10RUN CGO_ENABLED=0 go build -ldflags="-w -s" -o main . 11 12# 运行阶段 13FROM alpine:latest 14 15WORKDIR /app 16 17# 复制二进制文件 18COPY --from=builder /app/main . 19 20# 复制静态资源 21COPY --from=builder /app/static ./static 22COPY --from=builder /app/templates ./templates 23 24# 复制配置文件 25COPY --from=builder /app/config.yaml . 26 27EXPOSE 8080 28 29CMD ["./main"]

🎨 最佳实践

1. 利用构建缓存

DOCKERFILE
1FROM golang:1.21-alpine AS builder 2 3WORKDIR /app 4 5# ✅ 先复制依赖文件 6COPY go.mod go.sum ./ 7RUN go mod download 8 9# ✅ 后复制源代码(源代码变更频繁) 10COPY . . 11 12RUN go build -o main .

2. 使用 .dockerignore

创建 .dockerignore 文件:

GITIGNORE
1# Git 2.git 3.gitignore 4 5# IDE 6.vscode 7.idea 8*.swp 9*.swo 10 11# 构建产物 12*.exe 13*.test 14*.out 15main 16 17# 依赖 18vendor/ 19 20# 文档 21*.md 22LICENSE 23 24# CI/CD 25.github 26.gitlab-ci.yml 27 28# 测试 29*_test.go 30testdata/ 31 32# 临时文件 33*.tmp 34*.log 35.DS_Store

3. 优化编译参数

DOCKERFILE
1# 完整的编译优化 2RUN CGO_ENABLED=0 \ 3 GOOS=linux \ 4 GOARCH=amd64 \ 5 go build \ 6 -ldflags="-w -s -X main.version=${VERSION} -X main.buildTime=${BUILD_TIME}" \ 7 -trimpath \ 8 -o main \ 9 ./cmd/server

参数说明:

  • -ldflags="-w -s" - 去除调试信息和符号表
  • -trimpath - 去除文件系统路径
  • -X - 设置编译时变量
  • CGO_ENABLED=0 - 禁用 CGO,生成静态二进制

4. 多架构支持

DOCKERFILE
1FROM --platform=$BUILDPLATFORM golang:1.21-alpine AS builder 2 3ARG TARGETPLATFORM 4ARG BUILDPLATFORM 5ARG TARGETOS 6ARG TARGETARCH 7 8WORKDIR /app 9 10COPY go.mod go.sum ./ 11RUN go mod download 12 13COPY . . 14 15RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} \ 16 go build -ldflags="-w -s" -o main . 17 18FROM alpine:latest 19 20WORKDIR /app 21 22COPY --from=builder /app/main . 23 24CMD ["./main"]

构建多架构镜像:

Bash
1docker buildx build --platform linux/amd64,linux/arm64 -t myapp:latest .

5. 添加健康检查

DOCKERFILE
1# 方式 1: 使用内置健康检查端点 2HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ 3 CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1 4 5# 方式 2: 使用 curl 6HEALTHCHECK --interval=30s --timeout=3s \ 7 CMD curl -f http://localhost:8080/health || exit 1 8 9# 方式 3: 使用自定义脚本 10COPY healthcheck.sh / 11HEALTHCHECK CMD ["/healthcheck.sh"]

6. 环境变量管理

DOCKERFILE
1FROM alpine:latest 2 3WORKDIR /app 4 5COPY main . 6 7# 设置默认环境变量 8ENV APP_ENV=production \ 9 APP_PORT=8080 \ 10 LOG_LEVEL=info 11 12# 可在运行时覆盖 13EXPOSE ${APP_PORT} 14 15CMD ["./main"]

运行时覆盖:

Bash
1docker run -e APP_ENV=development -e APP_PORT=3000 myapp

🔧 实战案例

案例 1: Web API 服务

项目结构:

TEXT
1myapp/ 2├── cmd/ 3│ └── server/ 4│ └── main.go 5├── internal/ 6│ ├── handler/ 7│ ├── service/ 8│ └── repository/ 9├── config/ 10│ └── config.yaml 11├── go.mod 12├── go.sum 13├── Dockerfile 14└── .dockerignore

完整 Dockerfile:

DOCKERFILE
1# ============================================ 2# 构建阶段 3# ============================================ 4FROM golang:1.21-alpine AS builder 5 6# 构建参数 7ARG VERSION=dev 8ARG BUILD_TIME 9 10# 安装依赖 11RUN apk add --no-cache git ca-certificates tzdata 12 13WORKDIR /app 14 15# 缓存依赖 16COPY go.mod go.sum ./ 17RUN go mod download && go mod verify 18 19# 复制源代码 20COPY . . 21 22# 编译 23RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \ 24 -ldflags="-w -s \ 25 -X main.version=${VERSION} \ 26 -X main.buildTime=${BUILD_TIME}" \ 27 -trimpath \ 28 -o server \ 29 ./cmd/server 30 31# ============================================ 32# 运行阶段 33# ============================================ 34FROM alpine:latest 35 36# 元数据 37LABEL maintainer="your-email@example.com" \ 38 version="${VERSION}" \ 39 description="My Web API Service" 40 41# 安装运行时依赖 42RUN apk add --no-cache ca-certificates tzdata && \ 43 addgroup -g 1001 -S appuser && \ 44 adduser -S appuser -u 1001 -G appuser 45 46WORKDIR /app 47 48# 复制文件 49COPY --from=builder /app/server . 50COPY --from=builder /app/config ./config 51 52# 权限设置 53RUN chown -R appuser:appuser /app 54 55USER appuser 56 57# 环境变量 58ENV TZ=Asia/Shanghai \ 59 GIN_MODE=release 60 61EXPOSE 8080 62 63# 健康检查 64HEALTHCHECK --interval=30s --timeout=3s --start-period=5s \ 65 CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1 66 67CMD ["./server"]

案例 2: gRPC 服务

DOCKERFILE
1FROM golang:1.21-alpine AS builder 2 3# 安装 protoc 和相关工具 4RUN apk add --no-cache git protobuf-dev && \ 5 go install google.golang.org/protobuf/cmd/protoc-gen-go@latest && \ 6 go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest 7 8WORKDIR /app 9 10COPY go.mod go.sum ./ 11RUN go mod download 12 13COPY . . 14 15# 生成 protobuf 代码(如果需要) 16# RUN protoc --go_out=. --go-grpc_out=. proto/*.proto 17 18# 编译 19RUN CGO_ENABLED=0 go build -ldflags="-w -s" -o grpc-server ./cmd/server 20 21# 运行阶段 22FROM alpine:latest 23 24RUN apk add --no-cache ca-certificates 25 26WORKDIR /app 27 28COPY --from=builder /app/grpc-server . 29 30# gRPC 端口 31EXPOSE 50051 32 33CMD ["./grpc-server"]

案例 3: 定时任务服务

DOCKERFILE
1FROM golang:1.21-alpine AS builder 2 3WORKDIR /app 4 5COPY go.mod go.sum ./ 6RUN go mod download 7 8COPY . . 9 10RUN CGO_ENABLED=0 go build -ldflags="-w -s" -o cronjob ./cmd/cronjob 11 12FROM alpine:latest 13 14# 安装 cronie(如果需要系统级 cron) 15RUN apk add --no-cache ca-certificates tzdata 16 17WORKDIR /app 18 19COPY --from=builder /app/cronjob . 20COPY --from=builder /app/config ./config 21 22# 设置时区 23ENV TZ=Asia/Shanghai 24 25# 定时任务不需要暴露端口 26# 但可能需要健康检查文件 27HEALTHCHECK --interval=60s --timeout=3s \ 28 CMD test -f /tmp/healthy || exit 1 29 30CMD ["./cronjob"]

案例 4: 微服务(包含数据库迁移)

DOCKERFILE
1# ============================================ 2# 构建阶段 3# ============================================ 4FROM golang:1.21-alpine AS builder 5 6RUN apk add --no-cache git ca-certificates 7 8WORKDIR /app 9 10COPY go.mod go.sum ./ 11RUN go mod download 12 13COPY . . 14 15# 编译主服务 16RUN CGO_ENABLED=0 go build -ldflags="-w -s" -o api ./cmd/api 17 18# 编译迁移工具 19RUN CGO_ENABLED=0 go build -ldflags="-w -s" -o migrate ./cmd/migrate 20 21# ============================================ 22# 运行阶段 23# ============================================ 24FROM alpine:latest 25 26RUN apk add --no-cache ca-certificates tzdata postgresql-client 27 28WORKDIR /app 29 30# 复制二进制文件 31COPY --from=builder /app/api . 32COPY --from=builder /app/migrate . 33 34# 复制迁移脚本 35COPY --from=builder /app/migrations ./migrations 36 37# 复制启动脚本 38COPY docker-entrypoint.sh /usr/local/bin/ 39RUN chmod +x /usr/local/bin/docker-entrypoint.sh 40 41EXPOSE 8080 42 43ENTRYPOINT ["docker-entrypoint.sh"] 44CMD ["./api"]

启动脚本 docker-entrypoint.sh:

Bash
1#!/bin/sh 2set -e 3 4# 等待数据库就绪 5echo "Waiting for database..." 6until pg_isready -h $DB_HOST -p $DB_PORT -U $DB_USER 7do 8 echo "Database is unavailable - sleeping" 9 sleep 1 10done 11 12echo "Database is up - executing migrations" 13./migrate up 14 15echo "Starting application" 16exec "$@"

🛠️ 构建和运行

基本构建

Bash
1# 构建镜像 2docker build -t myapp:latest . 3 4# 查看镜像大小 5docker images myapp 6 7# 运行容器 8docker run -d -p 8080:8080 --name myapp myapp:latest

带构建参数

Bash
1# 传递构建参数 2docker build \ 3 --build-arg VERSION=1.0.0 \ 4 --build-arg BUILD_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ") \ 5 -t myapp:1.0.0 .

使用 docker-compose

YAML
1version: '3.8' 2 3services: 4 app: 5 build: 6 context: . 7 dockerfile: Dockerfile 8 args: 9 VERSION: "1.0.0" 10 ports: 11 - "8080:8080" 12 environment: 13 - DB_HOST=postgres 14 - DB_PORT=5432 15 - DB_USER=admin 16 - DB_PASSWORD=secret 17 depends_on: 18 - postgres 19 restart: unless-stopped 20 21 postgres: 22 image: postgres:15-alpine 23 environment: 24 - POSTGRES_USER=admin 25 - POSTGRES_PASSWORD=secret 26 - POSTGRES_DB=myapp 27 volumes: 28 - postgres_data:/var/lib/postgresql/data 29 restart: unless-stopped 30 31volumes: 32 postgres_data:

运行:

Bash
1docker-compose up -d 2docker-compose logs -f app 3docker-compose down

🔍 镜像优化技巧

1. 查看镜像层

Bash
1# 查看镜像历史 2docker history myapp:latest 3 4# 使用 dive 深度分析 5docker run --rm -it \ 6 -v /var/run/docker.sock:/var/run/docker.sock \ 7 wagoodman/dive:latest myapp:latest

2. 镜像体积对比

Bash
1# Golang 官方镜像 2FROM golang:1.21 # ~900MB 3 4# Alpine 版本 5FROM golang:1.21-alpine # ~300MB 6 7# 多阶段 + Alpine 8# 最终镜像 # ~20MB 9 10# Distroless 11# 最终镜像 # ~10MB 12 13# Scratch 14# 最终镜像 # ~5MB

3. 构建缓存优化

DOCKERFILE
1# ❌ 不好的做法 2COPY . . 3RUN go mod download 4RUN go build -o main . 5 6# ✅ 好的做法 7COPY go.mod go.sum ./ 8RUN go mod download 9COPY . . 10RUN go build -o main .

❓ 常见问题

Q1: 时区问题

DOCKERFILE
1# 方法 1: 复制时区数据 2FROM alpine:latest 3RUN apk add --no-cache tzdata 4ENV TZ=Asia/Shanghai 5 6# 方法 2: 从构建阶段复制 7COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo 8ENV TZ=Asia/Shanghai

Q2: CA 证书问题(HTTPS 请求失败)

DOCKERFILE
1# 方法 1: 安装 ca-certificates 2FROM alpine:latest 3RUN apk add --no-cache ca-certificates 4 5# 方法 2: 从构建阶段复制 6COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/

Q3: 静态文件路径问题

Go
1// 使用 embed 嵌入静态文件 2package main 3 4import ( 5 "embed" 6 "net/http" 7) 8 9//go:embed static/* 10var staticFiles embed.FS 11 12func main() { 13 http.Handle("/static/", http.FileServer(http.FS(staticFiles))) 14 http.ListenAndServe(":8080", nil) 15}

Q4: 数据库连接问题

Go
1// 在应用启动时等待数据库 2import ( 3 "database/sql" 4 "time" 5) 6 7func waitForDB(db *sql.DB, maxRetries int) error { 8 for i := 0; i < maxRetries; i++ { 9 if err := db.Ping(); err == nil { 10 return nil 11 } 12 time.Sleep(2 * time.Second) 13 } 14 return fmt.Errorf("database not ready after %d retries", maxRetries) 15}

Q5: 热重载(开发环境)

开发环境 Dockerfile:

DOCKERFILE
1FROM golang:1.21-alpine 2 3# 安装 air(热重载工具) 4RUN go install github.com/cosmtrek/air@latest 5 6WORKDIR /app 7 8COPY go.mod go.sum ./ 9RUN go mod download 10 11COPY . . 12 13CMD ["air", "-c", ".air.toml"]

.air.toml 配置:

TOML
1root = "." 2testdata_dir = "testdata" 3tmp_dir = "tmp" 4 5[build] 6 args_bin = [] 7 bin = "./tmp/main" 8 cmd = "go build -o ./tmp/main ." 9 delay = 1000 10 exclude_dir = ["assets", "tmp", "vendor", "testdata"] 11 exclude_file = [] 12 exclude_regex = ["_test.go"] 13 exclude_unchanged = false 14 follow_symlink = false 15 full_bin = "" 16 include_dir = [] 17 include_ext = ["go", "tpl", "tmpl", "html"] 18 kill_delay = "0s" 19 log = "build-errors.log" 20 send_interrupt = false 21 stop_on_error = true 22 23[color] 24 app = "" 25 build = "yellow" 26 main = "magenta" 27 runner = "green" 28 watcher = "cyan" 29 30[log] 31 time = false 32 33[misc] 34 clean_on_exit = false

📊 性能基准测试

不同基础镜像的性能对比

基础镜像镜像大小构建时间启动时间内存占用
golang:1.21~900MB2m 30s1.5s50MB
golang:1.21-alpine~300MB2m 00s1.2s40MB
多阶段 + alpine~20MB2m 10s0.8s35MB
多阶段 + distroless~10MB2m 15s0.7s30MB
多阶段 + scratch~5MB2m 20s0.5s25MB

🎓 进阶话题

1. 使用 BuildKit 特性

DOCKERFILE
1# syntax=docker/dockerfile:1.4 2 3FROM golang:1.21-alpine AS builder 4 5# 使用缓存挂载加速依赖下载 6RUN --mount=type=cache,target=/go/pkg/mod \ 7 go mod download 8 9# 使用缓存挂载加速构建 10RUN --mount=type=cache,target=/root/.cache/go-build \ 11 go build -o main .

构建:

Bash
1DOCKER_BUILDKIT=1 docker build -t myapp .

2. 安全扫描

Bash
1# 使用 Trivy 扫描漏洞 2docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \ 3 aquasec/trivy image myapp:latest 4 5# 使用 Snyk 6snyk container test myapp:latest

3. 签名和验证

Bash
1# 启用 Docker Content Trust 2export DOCKER_CONTENT_TRUST=1 3 4# 推送签名镜像 5docker push myapp:latest 6 7# 拉取时自动验证 8docker pull myapp:latest

📚 参考资源

  • 🔗 Go 官方文档
  • 🔗 Dockerfile 最佳实践
  • 🔗 Distroless 镜像
  • 🔗 Docker BuildKit
  • 🔗 Dive - 镜像分析工具

🎉 总结

快速检查清单

  • ✅ 使用多阶段构建
  • ✅ 禁用 CGO(如果可能)
  • ✅ 使用 Alpine 或 Distroless 基础镜像
  • ✅ 添加 .dockerignore 文件
  • ✅ 优化层缓存顺序
  • ✅ 使用非 root 用户运行
  • ✅ 添加健康检查
  • ✅ 设置合适的编译参数
  • ✅ 复制必要的运行时文件(CA 证书、时区数据)
  • ✅ 使用构建参数管理版本信息

推荐配置

DOCKERFILE
1# 生产环境推荐配置 2FROM golang:1.21-alpine AS builder 3RUN apk add --no-cache git ca-certificates tzdata 4WORKDIR /app 5COPY go.mod go.sum ./ 6RUN go mod download 7COPY . . 8RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-w -s" -o main . 9 10FROM alpine:latest 11RUN apk add --no-cache ca-certificates tzdata && \ 12 adduser -D -u 1001 appuser 13WORKDIR /app 14COPY --from=builder /app/main . 15USER appuser 16EXPOSE 8080 17HEALTHCHECK --interval=30s CMD wget -q --spider http://localhost:8080/health || exit 1 18CMD ["./main"]

🚀 现在你已经掌握了编写高效 Golang Dockerfile 的所有技能!

评论 (0)

登录 后发表评论

暂无评论,快来抢沙发吧!