GoTest/README.md
Table 4f6f308d23 docs: 更新 README 和 PROJECT_SUMMARY 文档
- README.md 更新:
  * 文档生成部分:添加详细的命令说明和使用方法
  * 新增 SWAGGER_PORT 配置说明章节,包括变量定义、影响范围、修改方法
  * 更新系统接口表格中的 Swagger 描述
  * 说明应用内置 Swagger UI 的使用方法

- PROJECT_SUMMARY.md 更新:
  * 添加项目概述描述
  * 更新 API 文档部分,反映已实现的功能
  * 更新构建和部署部分,添加端口配置和进程管理功能
  * 详细分类 Make 命令,添加所有新命令说明
  * 更新系统接口说明,添加 Swagger UI 访问地址
  * 更新下一步建议,使用新的文档生成命令
  * 新增"最新优化功能"章节,详细说明端口配置和文档优化
2025-11-29 20:13:00 +08:00

1073 lines
31 KiB
Markdown
Raw 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.

# Yinli API
基于 Golang Gin 框架构建的高性能 RESTful API 服务,集成了 JWT 认证、Redis 缓存、频率限制、CORS 等安全特性。
## 🚀 技术架构
### 核心技术栈
- **Web 框架**: [Gin](https://gin-gonic.com/) - 高性能的 HTTP Web 框架
- **数据库**: [MySQL 8.0](https://dev.mysql.com/doc/) - 关系型数据库
- **缓存**: [Redis](https://redis.io/) - 内存数据库,用于缓存和频率限制
- **ORM**: [GORM](https://gorm.io/) - Go 语言 ORM 库
- **配置管理**: [Viper](https://github.com/spf13/viper) - 配置文件管理
- **认证**: [JWT](https://jwt.io/) - JSON Web Token 认证
- **文档**: [Swagger](https://swagger.io/) - API 文档自动生成
- **测试**: [Testify](https://github.com/stretchr/testify) - 测试框架
- **容器化**: [Docker](https://www.docker.com/) - 容器化部署
### 安全特性
- **JWT 认证**: 基于 JSON Web Token 的用户认证
- **CORS 支持**: 跨域资源共享配置
- **频率限制**: 基于 Redis 的 API 频率限制
- **密码加密**: 使用 bcrypt 加密用户密码
- **中间件保护**: 多层中间件安全防护
### 项目结构
```
yinli-api/
├── src/ # 源代码目录
│ ├── main.go # 应用程序入口
│ ├── handler/ # HTTP 处理器
│ ├── middleware/ # 中间件
│ ├── model/ # 数据模型
│ ├── repository/ # 数据访问层
│ ├── service/ # 业务逻辑层
│ └── pkg/ # 可复用的包
│ ├── auth/ # 认证相关
│ ├── cache/ # 缓存操作
│ ├── config/ # 配置管理
│ └── database/ # 数据库连接
├── config/ # 配置文件
│ ├── dev.yaml # 开发环境配置
│ ├── stage.yaml # 预发布环境配置
│ └── prod.yaml # 生产环境配置
├── docker/ # Docker 相关文件
├── doc/ # API 文档
├── sql/ # 数据库脚本
├── test/ # 测试文件
├── build/ # 构建输出
├── Dockerfile # Docker 镜像构建文件
├── Makefile # 构建脚本
└── README.md # 项目说明
```
## 🛠️ 快速开始
### 环境要求
- Go 1.21+
- MySQL 8.0+
- Redis 6.0+
- Docker & Docker Compose (可选)
### 本地开发
1. **克隆项目**
```bash
git clone <repository-url>
cd yinli-api
```
2. **安装依赖**
```bash
make deps
```
3. **配置数据库**
```bash
# 创建数据库
mysql -u root -p < sql/init.sql
```
4. **启动 Redis**
```bash
redis-server
```
5. **启动开发环境**
```bash
make dev
```
### Docker 部署
项目支持三种环境的 Docker Compose 部署dev开发、stage预发布、prod生产
#### Docker 镜像配置(中国大陆用户)
如果在中国大陆使用 Docker建议配置镜像加速器以提高镜像拉取速度。项目使用官方镜像名称`mysql:8.0`、`redis:7-alpine`),通过配置的 Docker 镜像加速器自动加速拉取。
**方法1配置 Docker 镜像加速器(推荐)**
根据 Docker 运行模式选择配置位置:
**标准 Docker需要 root 权限):**
编辑或创建 `/etc/docker/daemon.json` 文件:
```bash
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": [
"https://docker.m.daocloud.io",
"https://dockerproxy.com",
"https://docker.nju.edu.cn"
]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
```
**Rootless Docker无需 root 权限):**
编辑或创建 `~/.config/docker/daemon.json` 文件:
```bash
mkdir -p ~/.config/docker
cat > ~/.config/docker/daemon.json <<-'EOF'
{
"registry-mirrors": [
"https://docker.m.daocloud.io",
"https://dockerproxy.com",
"https://docker.nju.edu.cn"
]
}
EOF
```
然后重启 rootless Docker**重要:配置后必须重启才能生效**
```bash
# 方法1重启 rootless Docker 服务
systemctl --user restart docker
# 方法2如果使用 dockerd-rootless需要重启用户服务
pkill -HUP dockerd
# 或者重启整个 rootless Docker
systemctl --user stop docker.socket
systemctl --user start docker.socket
```
验证配置:
```bash
docker info | grep -A 10 "Registry Mirrors"
```
如果看到配置的镜像源列表,说明配置成功。
**方法2使用环境变量临时**
```bash
# 设置 Docker 镜像代理
export DOCKER_REGISTRY_MIRROR=https://registry.cn-hangzhou.aliyuncs.com
```
**常用国内镜像源(按推荐顺序):**
- DaoCloud`https://docker.m.daocloud.io` ⭐ 推荐
- Docker 代理:`https://dockerproxy.com`
- 南京大学:`https://docker.nju.edu.cn`
- 上海交大:`https://docker.mirrors.sjtug.sjtu.edu.cn`
- 阿里云:`https://registry.cn-hangzhou.aliyuncs.com`(需要登录)
- 中科大:`https://docker.mirrors.ustc.edu.cn`(可能不稳定)
- 网易:`https://hub-mirror.c.163.com`(可能不稳定)
**重要提示:**
- 项目使用官方镜像名称(如 `mysql:8.0`、`redis:7-alpine`),通过配置的 Docker 镜像加速器自动加速
- 如果镜像加速器配置正确Docker 会自动从配置的镜像源拉取镜像
- 如果遇到镜像拉取失败,请确保已重启 Docker 服务使配置生效:`sudo systemctl restart docker`
- **Go 模块下载**Dockerfile 中已配置使用国内 Go 代理(`GOPROXY=https://goproxy.cn,direct`),无需额外配置
#### 开发环境部署
1. **生成 Docker Compose 文件(可选,文件已存在)**
```bash
make docker-compose-dev
```
2. **启动开发环境服务**
```bash
make docker-up-dev
```
3. **查看日志**
```bash
make docker-logs-dev
```
4. **停止服务**
```bash
make docker-down-dev
```
#### 预发布环境部署
1. **启动预发布环境服务**
```bash
make docker-up-stage
```
2. **查看日志**
```bash
make docker-logs-stage
```
3. **停止服务**
```bash
make docker-down-stage
```
**注意:** 预发布环境使用不同的端口映射MySQL: 3307, Redis: 6380避免与开发环境冲突。
#### 生产环境部署
1. **设置环境变量(重要!)**
```bash
export MYSQL_ROOT_PASSWORD=your_secure_password
export REDIS_PASSWORD=your_redis_password
```
2. **启动生产环境服务**
```bash
make docker-up-prod
```
3. **查看日志**
```bash
make docker-logs-prod
```
4. **停止服务**
```bash
make docker-down-prod
```
**注意:**
- 生产环境使用不同的端口映射MySQL: 3308, Redis: 6381
- 生产环境配置了健康检查和自动重启策略
- 请务必在生产环境部署前修改配置文件中的敏感信息JWT密钥、数据库密码等
## 📋 可用命令
### 开发命令
```bash
make dev # 启动开发环境
make stage # 启动预发布环境
make prod # 启动生产环境
```
### 构建命令
```bash
make build # 构建 Linux 版本
make build-all # 构建所有平台版本
make clean # 清理构建文件
```
### 测试命令
```bash
make test # 运行测试
make test-coverage # 运行测试并生成覆盖率报告
make benchmark # 运行基准测试
```
### 代码质量
```bash
make fmt # 格式化代码
make vet # 静态检查
make lint # 代码规范检查
make check # 执行所有检查
```
### 文档生成
```bash
make docs # 生成 API 文档(自动安装 swag 工具)
make docs-serve # 启动 Swagger UI 文档服务器(使用 Docker
make docs-stop # 停止 Swagger UI 文档服务器
```
**文档生成说明**
1. **生成 API 文档**
```bash
make docs
```
- 自动检查并安装 `swag` 工具(如果未安装)
- 自动过滤生成过程中的警告信息
- 自动修复生成的 `docs.go` 文件中的兼容性问题
- 文档生成到 `doc/dev/` 目录
2. **启动 Swagger UI 服务器**
```bash
make docs-serve
```
- 使用 Docker 运行 Swagger UI 容器
- 提供完整的 Swagger UI 界面(非简单的文件列表)
- 默认端口:`8081`(可通过 `SWAGGER_PORT` 变量修改)
- 访问地址:`http://localhost:8081`
3. **停止 Swagger UI 服务器**
```bash
make docs-stop
```
- 停止并移除 Swagger UI 容器
4. **应用内置 Swagger UI**
- 启动应用后,访问:`http://localhost:$(SERVER_PORT)/swagger/index.html`
- 例如:`http://localhost:1234/swagger/index.html`
- 注意:需要先执行 `make docs` 生成文档,并在 `main.go` 中导入 docs 包
### Docker 操作
```bash
make docker-build # 构建 Docker 镜像
make docker-compose-dev # 生成开发环境 Docker Compose 文件
make docker-up-dev # 启动开发环境容器
make docker-up-stage # 启动预发布环境容器
make docker-up-prod # 启动生产环境容器
make docker-down # 停止所有容器
make docker-down-dev # 停止开发环境容器
make docker-down-stage # 停止预发布环境容器
make docker-down-prod # 停止生产环境容器
make docker-logs-dev # 查看开发环境容器日志
make docker-logs-stage # 查看预发布环境容器日志
make docker-logs-prod # 查看生产环境容器日志
make docker-ps # 查看所有容器状态
```
## 🔧 配置说明
### 环境配置
项目支持三种环境配置:
- `dev` - 开发环境
- `stage` - 预发布环境
- `prod` - 生产环境
配置文件位于 `config/` 目录下,可通过环境变量 `APP_ENV` 或命令行参数 `-env` 指定。
### 主要配置项
```yaml
server:
port: 1234 # 服务端口
mode: debug # 运行模式 (debug/release)
database:
host: localhost # 数据库地址
port: 3306 # 数据库端口
username: root # 数据库用户名
password: sasasasa # 数据库密码
dbname: yinli # 数据库名称
redis:
host: localhost # Redis 地址
port: 6379 # Redis 端口
password: "" # Redis 密码
db: 0 # Redis 数据库
jwt:
secret: your-secret-key # JWT 密钥
expireHours: 24 # 令牌过期时间(小时)
rateLimit:
enabled: true # 是否启用频率限制
requests: 100 # 每个时间窗口的请求数
window: 60 # 时间窗口(秒)
```
### SERVER_PORT 配置
`SERVER_PORT` 是定义在 `Makefile` 中的变量,用于统一管理应用服务器的端口配置。修改此变量会影响以下内容:
**变量定义位置**
```makefile
# Makefile 第 20 行
SERVER_PORT := 1234
```
**影响范围**
1. **配置文件中的 `server.port`**
- 执行 `make dev`、`make stage`、`make prod` 时,会自动将对应环境配置文件(`config/dev.yaml`、`config/stage.yaml`、`config/prod.yaml`)中的 `server.port` 更新为 `SERVER_PORT` 的值
- 执行 `make docker-up-dev`、`make docker-up-stage`、`make docker-up-prod` 时,同样会更新配置文件中的端口
2. **Docker Compose 端口映射**
- `make docker-compose-dev`:生成 `docker/docker-compose.dev.yml` 时,设置端口映射为 `$(SERVER_PORT):$(SERVER_PORT)`
- `make docker-up-stage`:动态更新 `docker/docker-compose.stage.yml` 中的端口映射
- `make docker-up-prod`:动态更新 `docker/docker-compose.prod.yml` 中的端口映射和健康检查 URL
3. **端口占用检查**
- `make dev`、`make stage`、`make prod` 启动前会检查 `SERVER_PORT` 指定的端口是否被占用
- 如果端口被占用,会显示警告信息并提供终止进程的建议
4. **端口进程管理命令**
- `make kill-$(SERVER_PORT)`:终止占用 `SERVER_PORT` 端口的进程(默认端口为 1234
- `make kill-port PORT=$(SERVER_PORT)`:终止指定端口的进程
**修改方法**
1. **修改 Makefile**
```makefile
# 在 Makefile 第 20 行修改
SERVER_PORT := 1234 # 改为你想要的端口
```
2. **注意事项**
- 修改 `SERVER_PORT` 后,需要重新执行 `make docker-compose-dev` 以更新 Docker Compose 文件
- 如果使用 Docker 部署,还需要手动更新 `Dockerfile` 中的 `EXPOSE` 指令和健康检查 URL
```dockerfile
EXPOSE 1234 # 改为与 SERVER_PORT 一致
HEALTHCHECK ... CMD wget ... http://localhost:1234/health ...
```
- 确保修改后的端口未被其他服务占用
- 如果修改了端口API 访问地址也需要相应更新(如 `http://localhost:1234`
**示例**
```bash
# 将 SERVER_PORT 改为 1234
# 1. 编辑 Makefile修改 SERVER_PORT := 1234
# 2. 重新生成 Docker Compose 文件
make docker-compose-dev
# 3. 启动服务
make dev
# 4. 访问 API
curl http://localhost:1234/health
```
### SWAGGER_PORT 配置
`SWAGGER_PORT` 是定义在 `Makefile` 中的变量,用于统一管理 Swagger UI 文档服务器的端口配置。
**变量定义位置**
```makefile
# Makefile 第 23 行
SWAGGER_PORT := 8081
```
**影响范围**
1. **Docker Swagger UI 容器端口映射**
- `make docs-serve` 启动 Swagger UI 容器时,使用 `-p $(SWAGGER_PORT):8080` 进行端口映射
- 容器内部端口固定为 8080外部访问端口为 `SWAGGER_PORT` 的值
2. **静态文件服务器端口**
- 当 Docker 不可用时,`make docs-serve` 会使用 Python HTTP 服务器
- 服务器监听在 `SWAGGER_PORT` 指定的端口
3. **访问地址**
- Swagger UI 访问地址:`http://localhost:$(SWAGGER_PORT)`
- 默认访问地址:`http://localhost:8081`
**修改方法**
1. **修改 Makefile**
```makefile
# 在 Makefile 第 23 行修改
SWAGGER_PORT := 8081 # 改为你想要的端口
```
2. **注意事项**
- 确保修改后的端口未被其他服务占用
- 如果修改了端口,访问地址也需要相应更新
- 修改后需要重新执行 `make docs-serve` 才能生效
**示例**
```bash
# 将 SWAGGER_PORT 改为 9090
# 1. 编辑 Makefile修改 SWAGGER_PORT := 9090
# 2. 启动 Swagger UI 服务器
make docs-serve
# 3. 访问 Swagger UI
# 浏览器打开: http://localhost:9090
```
## 📡 API 接口
### 认证接口
| 方法 | 路径 | 描述 | 认证 |
|------|------|------|------|
| POST | `/api/auth/register` | 用户注册 | ❌ |
| POST | `/api/auth/login` | 用户登录 | ❌ |
### 用户接口
| 方法 | 路径 | 描述 | 认证 |
|------|------|------|------|
| GET | `/api/user/profile` | 获取用户资料 | ✅ |
| PUT | `/api/user/profile` | 更新用户资料 | ✅ |
| PUT | `/api/user/password` | 修改密码 | ✅ |
### 管理员接口
| 方法 | 路径 | 描述 | 认证 |
|------|------|------|------|
| GET | `/api/admin/users` | 获取用户列表 | ✅ (管理员) |
| DELETE | `/api/admin/users/{id}` | 删除用户 | ✅ (管理员) |
| PUT | `/api/admin/users/{id}/status` | 更新用户状态 | ✅ (管理员) |
### 系统接口
| 方法 | 路径 | 描述 | 认证 |
|------|------|------|------|
| GET | `/health` | 健康检查 | ❌ |
| GET | `/swagger/*` | API 文档Swagger UI | ❌ |
## 🧪 接口测试
### 使用 curl 测试
1. **用户注册**
```bash
curl -X POST http://localhost:1234/api/auth/register \
-H "Content-Type: application/json" \
-d '{
"name": "testuser",
"password": "password123"
}'
```
2. **用户登录**
```bash
curl -X POST http://localhost:1234/api/auth/login \
-H "Content-Type: application/json" \
-d '{
"name": "testuser",
"password": "password123"
}'
```
3. **获取用户资料**
```bash
curl -X GET http://localhost:1234/api/user/profile \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
```
### 使用 Postman
1. 导入 Postman 集合文件(如果有)
2. 设置环境变量 `base_url``http://localhost:1234`
3. 在认证接口获取 JWT token
4. 在需要认证的接口中添加 `Authorization: Bearer {token}`
## 🔍 开发调试
### 日志查看
```bash
# 查看应用日志
tail -f logs/app.log
# 查看 Docker 容器日志
make docker-logs-dev
```
### 数据库调试
```bash
# 连接数据库
mysql -h localhost -u root -p yinli
# 查看用户表
SELECT * FROM user;
```
### Redis 调试
```bash
# 连接 Redis
redis-cli
# 查看所有键
KEYS *
# 查看频率限制
KEYS rate_limit:*
```
### 性能监控
```bash
# 查看应用性能
go tool pprof http://localhost:1234/debug/pprof/profile
# 内存使用情况
go tool pprof http://localhost:1234/debug/pprof/heap
```
### 端口进程管理
在 Ubuntu 环境中,如果端口被占用,可以使用以下命令查找并终止进程:
```bash
# 方法1: 使用 netstat 查找端口对应的进程
netstat -tuln | grep :1234
# 方法2: 使用 lsof 查找端口对应的进程(需要安装: sudo apt install lsof
lsof -i :1234
# 方法3: 使用 ss 查找端口对应的进程
ss -tulnp | grep :1234
# 获取进程 ID (PID) 后,使用以下命令终止进程
kill <PID>
# 如果进程无法正常终止,可以使用强制终止
kill -9 <PID>
# 或者使用一行命令直接终止占用端口的进程
kill $(lsof -t -i:1234)
# 如果 lsof 不可用,可以使用 fuser需要安装: sudo apt install psmisc
sudo fuser -k 1234/tcp
```
**示例:**
```bash
# 1. 查找端口 1234 对应的进程
$ netstat -tulnp | grep :1234
tcp6 0 0 :::1234 :::* LISTEN 449736/main
# 2. 或者使用 lsof 获取更详细信息
$ lsof -i :1234
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
main 449736 table 9u IPv6 903989 0t0 TCP *:1234 (LISTEN)
# 3. 终止进程PID 为 449736
$ kill 449736
# 4. 如果进程无法正常终止,使用强制终止
$ kill -9 449736
```
## 📊 测试报告
运行测试并生成报告:
```bash
make test-coverage
```
测试报告将生成在 `build/coverage.html`,可在浏览器中查看详细的覆盖率信息。
## 🚀 部署指南
### 生产环境部署
1. **构建生产版本**
```bash
make build
```
2. **配置生产环境**
```bash
# 修改 config/prod.yaml
# 设置正确的数据库和 Redis 连接信息
# 更改 JWT 密钥
```
3. **使用 Docker 部署**
```bash
# 设置环境变量
export MYSQL_ROOT_PASSWORD=your_secure_password
export REDIS_PASSWORD=your_redis_password
# 启动生产环境
make docker-up-prod
# 查看日志
make docker-logs-prod
```
### 环境变量
生产环境建议使用环境变量覆盖敏感配置:
```bash
export YINLI_DATABASE_PASSWORD=your_db_password
export YINLI_JWT_SECRET=your_jwt_secret
export YINLI_REDIS_PASSWORD=your_redis_password
```
## 📚 主要依赖库
### 核心依赖
- [Gin Web Framework](https://github.com/gin-gonic/gin) - HTTP Web 框架
- [GORM](https://github.com/go-gorm/gorm) - ORM 库
- [Viper](https://github.com/spf13/viper) - 配置管理
- [JWT-Go](https://github.com/golang-jwt/jwt) - JWT 实现
- [Go-Redis](https://github.com/go-redis/redis) - Redis 客户端
### 中间件
- [Gin-CORS](https://github.com/gin-contrib/cors) - CORS 中间件
- [Gin-Swagger](https://github.com/swaggo/gin-swagger) - Swagger 文档
### 测试工具
- [Testify](https://github.com/stretchr/testify) - 测试断言库
- [HTTP Test](https://golang.org/pkg/net/http/httptest/) - HTTP 测试工具
### 开发工具
- [Air](https://github.com/cosmtrek/air) - 热重载工具
- [GolangCI-Lint](https://github.com/golangci/golangci-lint) - 代码检查工具
## 🤝 贡献指南
1. Fork 项目
2. 创建特性分支 (`git checkout -b feature/AmazingFeature`)
3. 提交更改 (`git commit -m 'Add some AmazingFeature'`)
4. 推送到分支 (`git push origin feature/AmazingFeature`)
5. 开启 Pull Request
## 📄 许可证
本项目采用 MIT 许可证 - 查看 [LICENSE](LICENSE) 文件了解详情。
## 🆘 常见问题
### Q: 如何修改数据库连接?
A: 修改 `config/{env}.yaml` 文件中的 `database` 配置项。
### Q: 如何添加新的 API 接口?
A: 1. 在 `src/handler` 中添加处理函数
2.`src/handler/router.go` 中注册路由
3. 添加相应的测试用例
### Q: 如何自定义中间件?
A: 在 `src/middleware` 目录下创建新的中间件文件,参考现有中间件的实现。
### Q: 如何部署到生产环境?
A:
1. 设置环境变量:
```bash
export MYSQL_ROOT_PASSWORD=your_secure_password
export REDIS_PASSWORD=your_redis_password
```
2. 修改 `config/prod.yaml` 中的敏感配置JWT密钥等
3. 启动生产环境:
```bash
make docker-up-prod
```
4. 查看日志:
```bash
make docker-logs-prod
```
### Q: Docker Compose 支持哪些环境?
A: 项目支持三种环境的 Docker Compose 部署:
- **dev**: 开发环境,应用端口 1234
- **stage**: 预发布环境,应用端口 1234包含自动重启
- **prod**: 生产环境,应用端口 1234包含健康检查和自动重启
**重要说明**
- 项目使用宿主机上的 MySQL 和 Redis 服务,不在 Docker Compose 中部署
- 容器通过 `host.docker.internal` 访问宿主机服务
- 需要确保宿主机上的 MySQL 和 Redis 已正确配置(监听在 0.0.0.0,允许外部连接)
- 如果使用 rootless Docker需要在 `docker-compose.yml` 中手动指定宿主机 IP
```yaml
extra_hosts:
- "host.docker.internal:192.168.1.11" # 替换为实际宿主机 IP
```
### Q: Docker 镜像拉取失败怎么办?
A: 如果在中国大陆遇到镜像拉取超时或失败,可以:
1. **配置 Docker 镜像加速器**(推荐):
```bash
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": [
"https://docker.m.daocloud.io",
"https://dockerproxy.com",
"https://docker.nju.edu.cn"
]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
```
**重要:配置后必须重启 Docker 服务才能生效!**
验证配置
```bash
docker info | grep -A 10 "Registry Mirrors"
```
如果看到镜像源列表说明配置成功
2. **Rootless Docker 配置**
- 如果使用 rootless Docker`docker version` 显示 `Context: rootless`配置文件位置为 `~/.config/docker/daemon.json`
- 配置后重启`systemctl --user restart docker`
- 验证`docker info | grep -A 10 "Registry Mirrors"`
3. **使用官方镜像名称**项目使用官方镜像名称 `mysql:8.0`、`redis:7-alpine`通过配置的 Docker 镜像加速器自动加速拉取
4. **Go 模块下载失败**
- Dockerfile 中已配置使用国内 Go 代理`GOPROXY=https://goproxy.cn,direct`
- 如果仍然失败可以在 Dockerfile 中修改 GOPROXY 环境变量
```dockerfile
ENV GOPROXY=https://goproxy.cn,https://goproxy.io,direct
```
- 常用 Go 代理`https://goproxy.cn`、`https://goproxy.io`、`https://mirrors.aliyun.com/goproxy/`
5. **检查网络连接**确保能够访问镜像仓库
- 测试镜像源`curl -I https://docker.m.daocloud.io/v2/`
- 如果镜像加速器配置已生效但仍失败可能是网络问题可以稍后重试
6. **如果镜像加速器仍未生效**
- 检查配置文件格式是否正确JSON 格式
- 标准 Docker检查 `/etc/docker/daemon.json`
- Rootless Docker检查 `~/.config/docker/daemon.json`
- 确认已重启 Docker 服务
- 检查 Docker 服务状态`sudo systemctl status docker` `systemctl --user status docker`
### Q: Docker 容器无法连接到宿主机上的 MySQL 和 Redis
A: 如果遇到 `dial tcp: connection refused` `lookup mysql on 127.0.0.11:53: server misbehaving` 错误可能是以下原因
1. **MySQL/Redis 只监听本地接口127.0.0.1**
检查服务监听状态
```bash
# 检查 MySQL
sudo netstat -tlnp | grep 3306
#
sudo ss -tlnp | grep 3306
# 检查 Redis
sudo netstat -tlnp | grep 6379
#
sudo ss -tlnp | grep 6379
```
如果显示 `127.0.0.1:3306` `127.0.0.1:6379`说明只监听本地接口需要修改配置
2. **修改 MySQL 配置允许外部连接**
```bash
# 编辑 MySQL 配置文件
sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf
# 找到 bind-address修改为
bind-address = 0.0.0.0
# 重启 MySQL
sudo systemctl restart mysql
# 验证配置
mysql -uroot -p -e "SHOW VARIABLES LIKE 'bind_address';"
# 应该显示: bind_address | 0.0.0.0
```
3. **修改 MySQL 用户权限**
```bash
# 允许 root 用户从任何主机连接
mysql -uroot -p << 'EOF'
UPDATE mysql.user SET host='%' WHERE user='root' AND host='localhost';
FLUSH PRIVILEGES;
SELECT user, host FROM mysql.user WHERE user='root';
EOF
```
或者创建新的用户
```bash
mysql -uroot -p << 'EOF'
CREATE USER 'root'@'%' IDENTIFIED BY 'your_password';
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION;
FLUSH PRIVILEGES;
EOF
```
4. **修改 Redis 配置允许外部连接**
```bash
# 方法1通过 Redis CLI 临时修改重启后失效
redis-cli CONFIG SET bind "0.0.0.0"
redis-cli CONFIG SET protected-mode no
redis-cli CONFIG REWRITE # 持久化配置
# 方法2修改 Redis 配置文件推荐
# 找到 Redis 配置文件通常在 /etc/redis/redis.conf /usr/local/etc/redis.conf
sudo nano /etc/redis/redis.conf
# 修改以下配置
bind 0.0.0.0
protected-mode no
# 重启 Redis
sudo systemctl restart redis
#
sudo service redis-server restart
```
5. **rootless Docker 的 host-gateway 问题**
如果使用 rootless Docker`host-gateway` 可能指向错误的 IP需要手动指定宿主机 IP
```yaml
# docker-compose.yml
services:
yinli-api:
extra_hosts:
- "host.docker.internal:192.168.1.11" # 替换为实际宿主机 IP
```
获取宿主机 IP
```bash
hostname -I | awk '{print $1}'
#
ip addr show | grep "inet " | grep -v "127.0.0.1" | head -1 | awk '{print $2}' | cut -d/ -f1
```
6. **验证连接**
```bash
# 从容器内测试 MySQL 连接
docker exec docker-yinli-api-1 sh -c "nc -zv host.docker.internal 3306"
# 从容器内测试 Redis 连接
docker exec docker-yinli-api-1 sh -c "nc -zv host.docker.internal 6379"
```
7. **检查防火墙规则**
```bash
# 检查 iptables 规则
sudo iptables -L -n | grep -E "3306|6379"
# 如果需要允许 Docker 网络访问
sudo iptables -A INPUT -p tcp --dport 3306 -s 172.17.0.0/16 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 6379 -s 172.17.0.0/16 -j ACCEPT
```
**完整排查步骤**
1. 检查 MySQL/Redis 是否监听在 `0.0.0.0`而不是 `127.0.0.1`
2. 检查 MySQL 用户权限允许从 `%` 连接
3. 检查 Redis `protected-mode` 是否已禁用
4. 检查 Docker Compose `extra_hosts` 配置是否正确rootless Docker 需要使用宿主机实际 IP
5. 检查防火墙规则
6. 查看容器日志`docker logs docker-yinli-api-1`
**常见错误信息**
- `dial tcp 172.17.0.1:3306: connect: connection refused` - MySQL 未监听在 0.0.0.0 或用户权限不足
- `dial tcp: lookup mysql on 127.0.0.11:53: server misbehaving` - config 文件中使用了 `mysql` 作为 host Docker Compose 中没有 mysql 服务
- `DENIED Redis is running in protected mode` - Redis protected-mode 未禁用
- `Host 'xxx' is not allowed to connect to this MySQL server` - MySQL 用户权限问题
### Q: MySQL 容器无法正常停止怎么办?
A: 如果遇到 `make docker-down-stage` 无法正常关闭 MySQL 容器的问题已通过以下方式解决
1. **Docker Compose 配置优化**已应用
- MySQL 服务添加 `stop_grace_period: 60s`增加优雅关闭时间
- 添加 `stop_signal: SIGTERM`使用 SIGTERM 信号优雅停止
- 添加 `init: true`使用 init 进程管理子进程避免僵尸进程
2. **Makefile 自动重试机制**已实现
- 所有 `docker-down-*` 命令已添加自动检测权限错误
- 如果检测到权限错误会自动重启 rootless Docker 服务并重试
- 显示完整的 Docker Compose 进度信息`[+] Running` `✔` 符号
3. **如果仍然无法停止**
```bash
# 手动重启 rootless Docker 服务
systemctl --user restart docker
# 然后再次尝试
make docker-down-stage
```
### Q: Docker 权限错误permission denied怎么办
A: 如果遇到 `Error response from daemon: cannot stop container: permission denied` 错误可能的原因和解决方法
1. **Docker 上下文不匹配**常见原因
如果系统同时安装了标准 Docker rootless Docker容器可能由不同的上下文创建
```bash
# 查看当前 Docker 上下文
docker context show
# 查看所有可用的上下文
docker context ls
# 如果容器是由标准 Docker 创建的切换到 default 上下文
docker context use default
# 然后尝试停止容器
docker stop docker-mysql-1
# 如果需要切换回 rootless
docker context use rootless
```
**提示**如果容器是通过 `sudo docker compose` 创建的通常需要使用 `default` 上下文如果通过普通用户创建的可能使用 `rootless` 上下文
2. **临时解决方案(使用 sudo**
```bash
# 停止并删除容器
sudo docker stop docker-mysql-1
sudo docker rm docker-mysql-1
# 或使用 docker compose
sudo docker compose -f docker/docker-compose.stage.yml down
```
3. **永久解决方案(推荐)**将用户添加到 `docker`
```bash
# 将当前用户添加到 docker
sudo usermod -aG docker $USER
# 重新登录或执行以下命令使组权限生效
newgrp docker
# 验证是否成功
groups | grep docker
```
**注意**添加到 docker 组后需要
- 重新登录系统
- 执行 `newgrp docker` 命令
- 重新打开终端
之后就可以不使用 `sudo` 直接操作 Docker
4. **检查 Docker socket 权限**
```bash
ls -la /var/run/docker.sock
```
应该显示类似`srw-rw---- 1 root docker`表示 `docker` 组有读写权限
5. **如果使用 rootless Docker**
- Rootless Docker 不需要 sudo但需要确保 Docker 服务正在运行
- 检查服务状态`systemctl --user status docker`
- 启动服务`systemctl --user start docker`
- 注意rootless Docker 和标准 Docker 创建的容器是隔离的不能互相管理
- **如果遇到权限错误可以尝试**
```bash
# 方法1重启 rootless Docker 服务推荐
systemctl --user restart docker
# 等待几秒后再尝试停止容器
docker stop docker-mysql-1
# 方法2使用 docker kill 强制停止
docker kill docker-mysql-1
docker rm docker-mysql-1
# 方法3检查并修复 rootless Docker socket 权限
ls -la /run/user/$(id -u)/docker.sock
# 应该显示类似srw-rw---T 1 table ...
```