Files
2026-10-20 21:19:44 +08:00

168 lines
8.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 2.7 Docker 快速体验
:::tip 概述
项目根目录已内置完整 Docker 化文件(`docker/` 目录 + 根目录 `.dockerignore`),一条命令拉起 后端 + MySQL + Redis 三容器全栈。生产部署细节见 [11.6 Docker 生产部署](../deploy/docker)。
:::
## 文件布局
```text
EasyGoAdmin_GoFrame_EleVue/
├── .dockerignore # 构建上下文排除清单(Docker 硬规则:必须在上下文根目录)
└── docker/
├── Dockerfile # 多阶段构建:golang:1.27-alpine 编译 → alpine 运行
├── docker-compose.yml # 三服务编排:后端 + MySQL8 + Redis7
└── .env.example # 环境变量模板(复制为 .env 使用,含明文密钥不入库)
```
:::warning 为什么 `.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_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`),端口填宿主机上实际监听端口。
::: warning 容器内 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
```
## 常见问题
::: warning 容器内注意
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 指向容器自身**,填了连不上(详见上方变量表警示)。
:::
## 其他运行形态
| 形态 | 说明 |
|------|------|
| 二进制 + systemd | `go build` 产物 + systemd unit(见 [11.4 后端部署](../deploy/backend)) |
| 前端 Nginx 容器 | `ui/` 独立构建 dist 后由 Nginx 托管并反代 /api(见 [11.6](../deploy/docker)) |
| k8s | 按拓扑拆分 Deployment/Service,上传目录用 PV |