# 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 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 # 如果进程无法正常终止,可以使用强制终止 kill -9 # 或者使用一行命令直接终止占用端口的进程 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 ... ```