2.7 Docker 快速体验
概述
项目根目录已内置完整 Docker 化文件(docker/ 目录 + 根目录 .dockerignore),一条命令拉起 后端 + MySQL + Redis 三容器全栈。生产部署细节见 11.6 Docker 生产部署。
文件布局
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(多阶段,关键设计):
# 阶段一: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(三服务编排,关键设计):
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"]三步启动
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_PORT | 8000 | 后端宿主端口 |
| JWT_SECRET | please-change-... | 令牌签名密钥,生产必改 |
| MYSQL_HOST | mysql | 连接串内主机:自带服务用服务名;连外部库填 IP/域名 |
| MYSQL_PORT | 3306 | 数据库监听端口(进连接串;连外部库填外部库端口) |
| MYSQL_USER / MYSQL_PASSWORD | root / root | 数据库账号与密码 |
| MYSQL_DATABASE | easygoadmin.goframe.elevue | 库名(与定稿 SQL 一致,勿随意改) |
| REDIS_HOST | redis | 同 MYSQL_HOST 规则 |
| REDIS_PORT / REDIS_PASSWORD | 6379 / 123456 | Redis 监听端口(进连接串)与密码 |
自带 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 服务时,在项目根目录执行:
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常见问题
容器内注意
file.uploadDir配置默认是 Windows 绝对路径,容器内必须用GF_FILE_UPLOADDIR覆盖并挂持久卷,否则重建丢文件。- 跨卷上传"复制后删源"逻辑依赖同一文件系统,upload 目录必须是独立卷。
- 定稿 SQL 挂载到
docker-entrypoint-initdb.d仅在 MySQL 数据卷为空时执行一次;改表结构后需手动执行或docker volume rm清卷重建。 - 代码生成器(tool 域)在容器内可用(
templates/已打入镜像),但生成产物写在容器文件系统里,生产容器不建议使用,开发期仍在宿主机跑go run ./cmd/generator。 - 连接串/Redis 地址在 bridge 网络下用服务名(
mysql、redis)互访;连宿主机服务用host.docker.internal;容器内 127.0.0.1 指向容器自身,填了连不上(详见上方变量表警示)。
其他运行形态
| 形态 | 说明 |
|---|---|
| 二进制 + systemd | go build 产物 + systemd unit(见 11.4 后端部署) |
| 前端 Nginx 容器 | ui/ 独立构建 dist 后由 Nginx 托管并反代 /api(见 11.6) |
| k8s | 按拓扑拆分 Deployment/Service,上传目录用 PV |