Skip to content

2.7 Docker 快速体验 ​

概述

项目根目录已内置完整 Docker 化文件(docker/ 目录 + 根目录 .dockerignore),一条命令拉起 后端 + MySQL + Redis 三容器全栈。生产部署细节见 11.6 Docker 生产部署。

文件布局 ​

text
EasyGoAdmin_GoFrame_EleVue/
├── .dockerignore            # 构建上下文排除清单(Docker 硬规则:必须在上下文根目录)
└── docker/
    ├── Dockerfile           # 多阶段构建:golang:1.27-alpine 编译 → alpine 运行
    ├── docker-compose.yml   # 三服务编排:后端 + MySQL8 + Redis7
    └── .env.example         # 环境变量模板(复制为 .env 使用,含明文密钥不入库)

为什么 .dockerignore 不在 docker/ 里

.dockerignore 由构建上下文根目录决定,与 Dockerfile 位置无关。放 docker/ 里会完全失效,ui/node_modules(数百 MB)会被打进构建上下文,构建极慢。而 Dockerfile / docker-compose.yml 位置无所谓,构建/启动时用 -f 指定即可。

文件注释速览 ​

Dockerfile(多阶段,关键设计):

dockerfile
# 阶段一:builder —— 编译静态二进制
#   纯 Go 项目(sqlite 驱动为 modernc 系,无 CGO),CGO_ENABLED=0 静态编译
#   先拷 go.mod/go.sum 再 go mod download:源码变更但依赖未变时不重复下载
#   -trimpath 去除本地路径,-s -w 去除符号表与调试信息
FROM golang:1.27-alpine AS builder
ENV GOPROXY=https://goproxy.cn,direct CGO_ENABLED=0 GOOS=linux
WORKDIR /build
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN go build -trimpath -ldflags "-s -w" -o /build/bin/admin-server main.go

# 阶段二:runtime —— 最终业务镜像
#   运行时资产四件(缺一运行报错):
#     manifest/config  配置文件
#     manifest/data    ip2region.xdb 归属地库
#     resource         字体/swagger 静态资源
#     templates        代码生成器模板(tool 域运行时读取)
#   先于二进制 COPY,业务代码变动不破坏资产层缓存
#   非 root 运行;tzdata 供 app.timezone=Asia/Shanghai 使用
FROM alpine:3.22 AS runtime
ENV TZ=Asia/Shanghai
RUN apk add --no-cache tzdata ca-certificates \
    && addgroup -g 1000 app && adduser -u 1000 -G app -s /sbin/nologin -D app
WORKDIR /opt/easygoadmin
COPY --chown=app:app manifest/config ./manifest/config
COPY --chown=app:app manifest/data   ./manifest/data
COPY --chown=app:app resource        ./resource
COPY --chown=app:app templates       ./templates
COPY --chown=app:app --from=builder /build/bin/admin-server ./bin/admin-server
USER app
# 健康检查:/api/health 为公开白名单路径,不带 /v1
HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \
    CMD wget -qO- "http://127.0.0.1:8000/api/health" || exit 1
ENTRYPOINT ["./bin/admin-server"]

docker-compose.yml(三服务编排,关键设计):

yaml
services:
  easygoadmin:
    build:
      context: ..                       # 上下文为项目根(compose 在 docker/ 目录)
      dockerfile: docker/Dockerfile
    # 敏感/环境相关配置一律 GF_ 前缀覆盖,不改镜像内 config.yaml
    # (键名 = 配置路径大写下划线,如 file.uploadDir → GF_FILE_UPLOADDIR)
    environment:
      # 连接串由 .env 参数拼装:主机/端口/账号/密码/库名均可覆盖(连外部库改 .env 即可)
      GF_DATABASE_DEFAULT_LINK: "mysql:${MYSQL_USER}:${MYSQL_PASSWORD}@tcp(${MYSQL_HOST}:${MYSQL_PORT})/${MYSQL_DATABASE}?..."
      GF_REDIS_DEFAULT_ADDRESS: "${REDIS_HOST}:${REDIS_PORT}"
      GF_FILE_UPLOADDIR: "/data/upload"   # 配置默认是 Windows 绝对路径,容器内必须覆盖
      GF_CAPTCHA_BYPASSCODE: ""           # 生产清空万能验证码
      GF_APP_ERRORMSG: "false"            # 生产关闭详细错误信息
    volumes:
      - ../upload:/data/upload        # 宿主路径相对 compose 文件目录,需 ../ 回到项目根
      - ../log:/opt/easygoadmin/log
    depends_on:
      mysql: { condition: service_healthy }     # 等 healthcheck 通过再启动
      redis: { condition: service_healthy }

  mysql:
    image: mysql:8.0
    volumes:
      - mysql-data:/var/lib/mysql
      # 首次初始化:导入定稿 SQL(仅空数据卷时执行一次)
      - ../document/mysql/easygoadmin.goframe.elevue.sql:/docker-entrypoint-initdb.d/init.sql:ro
    command: [--character-set-server=utf8mb4, --collation-server=utf8mb4_unicode_ci, --lower_case_table_names=1]

  redis:
    image: redis:7-alpine
    command: ["redis-server", "--requirepass", "${REDIS_PASSWORD:-123456}", "--appendonly", "yes"]

三步启动 ​

sh
cd docker

# 1. 首次:生成 .env 并按需修改密钥(JWT_SECRET / 数据库 / Redis 密码)
copy .env.example .env

# 2. 构建并启动全栈(首次 MySQL 自动导入定稿 SQL 建库)
docker compose up -d --build

# 3. 验证
curl http://127.0.0.1:8000/api/health

默认账号 admin / 123456。.env 变量说明:

变量默认值说明
APP_PORT8000后端宿主端口
JWT_SECRETplease-change-...令牌签名密钥,生产必改
MYSQL_HOSTmysql连接串内主机:自带服务用服务名;连外部库填 IP/域名
MYSQL_PORT3306数据库监听端口(进连接串;连外部库填外部库端口)
MYSQL_USER / MYSQL_PASSWORDroot / root数据库账号与密码
MYSQL_DATABASEeasygoadmin.goframe.elevue库名(与定稿 SQL 一致,勿随意改)
REDIS_HOSTredis同 MYSQL_HOST 规则
REDIS_PORT / REDIS_PASSWORD6379 / 123456Redis 监听端口(进连接串)与密码

自带 mysql/redis 服务的宿主映射端口已在 compose 写死(3306/6379,仅供宿主机客户端直连调试),不在 .env 中配置;占用冲突时直接改 compose 的 ports。

连宿主机已有数据库/Redis:.env 中把 MYSQL_HOST / REDIS_HOST 改为 host.docker.internal(Windows/Mac Docker Desktop;Linux 需 compose 加 extra_hosts: host.docker.internal:host-gateway),端口填宿主机上实际监听端口。

容器内 127.0.0.1 不是宿主机

bridge 网络下每个容器有独立网络栈,MYSQL_HOST=127.0.0.1 / REDIS_HOST=127.0.0.1 指向容器自身,连接必失败(Redis 会静默降级内存缓存,JWT 黑名单、验证码共享随之失效)。连宿主机一律用 host.docker.internal 或宿主机实际 IP。

仅构建后端镜像(连宿主机已有 DB/Redis) ​

不启动 compose 里的 mysql/redis 服务时,在项目根目录执行:

sh
docker build -f docker/Dockerfile -t easygoadmin:latest .

docker run -d --name easygoadmin -p 8000:8000 \
  -e GF_DATABASE_DEFAULT_LINK="mysql:root:root@tcp(host.docker.internal:3306)/easygoadmin.goframe.elevue?charset=utf8mb4&parseTime=true&loc=Local" \
  -e GF_REDIS_DEFAULT_ADDRESS="host.docker.internal:6379" \
  -e GF_FILE_UPLOADDIR="/data/upload" \
  -v ${PWD}/upload:/data/upload easygoadmin:latest

常见问题 ​

容器内注意

  1. file.uploadDir 配置默认是 Windows 绝对路径,容器内必须用 GF_FILE_UPLOADDIR 覆盖并挂持久卷,否则重建丢文件。
  2. 跨卷上传"复制后删源"逻辑依赖同一文件系统,upload 目录必须是独立卷。
  3. 定稿 SQL 挂载到 docker-entrypoint-initdb.d 仅在 MySQL 数据卷为空时执行一次;改表结构后需手动执行或 docker volume rm 清卷重建。
  4. 代码生成器(tool 域)在容器内可用(templates/ 已打入镜像),但生成产物写在容器文件系统里,生产容器不建议使用,开发期仍在宿主机跑 go run ./cmd/generator。
  5. 连接串/Redis 地址在 bridge 网络下用服务名(mysql、redis)互访;连宿主机服务用 host.docker.internal;容器内 127.0.0.1 指向容器自身,填了连不上(详见上方变量表警示)。

其他运行形态 ​

形态说明
二进制 + systemdgo build 产物 + systemd unit(见 11.4 后端部署)
前端 Nginx 容器ui/ 独立构建 dist 后由 Nginx 托管并反代 /api(见 11.6)
k8s按拓扑拆分 Deployment/Service,上传目录用 PV

小蚂蚁云团队 · 提供技术支持