Go to file
Table b1ddecc45e docs: 添加 CHANGELOG.md 升级日志并清理 PDF 文件
- 新增 CHANGELOG.md 文件,记录项目所有重要变更
- 按功能类型分类(新增功能、改进优化、问题修复、文档更新)
- 包含未发布版本和 2025-11-29 开发阶段的变更记录
- 删除 PROJECT_SUMMARY.pdf 和 README.pdf 文件
2025-11-29 19:27:42 +08:00
config fix: 修复 Docker 容器连接宿主机 MySQL/Redis 问题并更新文档 2025-11-29 06:55:50 +08:00
docker fix: 修复 Docker 容器连接宿主机 MySQL/Redis 问题并更新文档 2025-11-29 06:55:50 +08:00
sql init repo 2025-11-29 03:27:19 +08:00
src fix: 修复 Docker 模式下 SERVER_PORT 未生效的问题 2025-11-29 08:10:47 +08:00
test 将 @internal 目录重命名为 src 目录并做以下变更: 2025-11-29 04:58:41 +08:00
.air.toml init repo 2025-11-29 03:27:19 +08:00
.dockerignore init repo 2025-11-29 03:27:19 +08:00
.gitignore init repo 2025-11-29 03:27:19 +08:00
CHANGELOG.md docs: 添加 CHANGELOG.md 升级日志并清理 PDF 文件 2025-11-29 19:27:42 +08:00
Dockerfile fix: 修复 MySQL 容器在 rootless Docker 环境下无法正常停止的问题 2025-11-29 06:18:17 +08:00
go.mod 将 @internal 目录重命名为 src 目录并做以下变更: 2025-11-29 04:58:41 +08:00
go.sum init repo 2025-11-29 03:27:19 +08:00
localhost.session.sql init repo 2025-11-29 03:27:19 +08:00
Makefile fix: 修复 Docker 模式下 SERVER_PORT 未生效的问题 2025-11-29 08:10:47 +08:00
PROJECT_SUMMARY.md fix: 修复 MySQL 容器在 rootless Docker 环境下无法正常停止的问题 2025-11-29 06:18:17 +08:00
README.md docs: 在 README 中添加 SERVER_PORT 配置说明 2025-11-29 08:16:01 +08:00

Yinli API

基于 Golang Gin 框架构建的高性能 RESTful API 服务,集成了 JWT 认证、Redis 缓存、频率限制、CORS 等安全特性。

🚀 技术架构

核心技术栈

  • Web 框架: Gin - 高性能的 HTTP Web 框架
  • 数据库: MySQL 8.0 - 关系型数据库
  • 缓存: Redis - 内存数据库,用于缓存和频率限制
  • ORM: GORM - Go 语言 ORM 库
  • 配置管理: Viper - 配置文件管理
  • 认证: JWT - JSON Web Token 认证
  • 文档: Swagger - API 文档自动生成
  • 测试: Testify - 测试框架
  • 容器化: Docker - 容器化部署

安全特性

  • 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. 克隆项目
git clone <repository-url>
cd yinli-api
  1. 安装依赖
make deps
  1. 配置数据库
# 创建数据库
mysql -u root -p < sql/init.sql
  1. 启动 Redis
redis-server
  1. 启动开发环境
make dev

Docker 部署

项目支持三种环境的 Docker Compose 部署dev开发、stage预发布、prod生产

Docker 镜像配置(中国大陆用户)

如果在中国大陆使用 Docker建议配置镜像加速器以提高镜像拉取速度。项目使用官方镜像名称mysql:8.0redis:7-alpine),通过配置的 Docker 镜像加速器自动加速拉取。

方法1配置 Docker 镜像加速器(推荐)

根据 Docker 运行模式选择配置位置:

标准 Docker需要 root 权限):

编辑或创建 /etc/docker/daemon.json 文件:

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 文件:

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重要:配置后必须重启才能生效

# 方法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

验证配置:

docker info | grep -A 10 "Registry Mirrors"

如果看到配置的镜像源列表,说明配置成功。

方法2使用环境变量临时

# 设置 Docker 镜像代理
export DOCKER_REGISTRY_MIRROR=https://registry.cn-hangzhou.aliyuncs.com

常用国内镜像源(按推荐顺序):

  • DaoCloudhttps://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.0redis:7-alpine),通过配置的 Docker 镜像加速器自动加速
  • 如果镜像加速器配置正确Docker 会自动从配置的镜像源拉取镜像
  • 如果遇到镜像拉取失败,请确保已重启 Docker 服务使配置生效:sudo systemctl restart docker
  • Go 模块下载Dockerfile 中已配置使用国内 Go 代理(GOPROXY=https://goproxy.cn,direct),无需额外配置

开发环境部署

  1. 生成 Docker Compose 文件(可选,文件已存在)
make docker-compose-dev
  1. 启动开发环境服务
make docker-up-dev
  1. 查看日志
make docker-logs
  1. 停止服务
make docker-down-dev

预发布环境部署

  1. 启动预发布环境服务
make docker-up-stage
  1. 查看日志
make docker-logs-stage
  1. 停止服务
make docker-down-stage

注意: 预发布环境使用不同的端口映射MySQL: 3307, Redis: 6380避免与开发环境冲突。

生产环境部署

  1. 设置环境变量(重要!)
export MYSQL_ROOT_PASSWORD=your_secure_password
export REDIS_PASSWORD=your_redis_password
  1. 启动生产环境服务
make docker-up-prod
  1. 查看日志
make docker-logs-prod
  1. 停止服务
make docker-down-prod

注意:

  • 生产环境使用不同的端口映射MySQL: 3308, Redis: 6381
  • 生产环境配置了健康检查和自动重启策略
  • 请务必在生产环境部署前修改配置文件中的敏感信息JWT密钥、数据库密码等

📋 可用命令

开发命令

make dev          # 启动开发环境
make stage        # 启动预发布环境
make prod         # 启动生产环境

构建命令

make build        # 构建 Linux 版本
make build-all    # 构建所有平台版本
make clean        # 清理构建文件

测试命令

make test         # 运行测试
make test-coverage # 运行测试并生成覆盖率报告
make benchmark    # 运行基准测试

代码质量

make fmt          # 格式化代码
make vet          # 静态检查
make lint         # 代码规范检查
make check        # 执行所有检查

文档生成

make docs         # 生成 API 文档
make docs-serve   # 启动文档服务器

Docker 操作

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           # 查看开发环境容器日志
make docker-logs-stage     # 查看预发布环境容器日志
make docker-logs-prod      # 查看生产环境容器日志
make docker-ps             # 查看所有容器状态

🔧 配置说明

环境配置

项目支持三种环境配置:

  • dev - 开发环境
  • stage - 预发布环境
  • prod - 生产环境

配置文件位于 config/ 目录下,可通过环境变量 APP_ENV 或命令行参数 -env 指定。

主要配置项

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 第 20 行
SERVER_PORT := 1234

影响范围

  1. 配置文件中的 server.port

    • 执行 make devmake stagemake prod 时,会自动将对应环境配置文件(config/dev.yamlconfig/stage.yamlconfig/prod.yaml)中的 server.port 更新为 SERVER_PORT 的值
    • 执行 make docker-up-devmake docker-up-stagemake 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 devmake stagemake prod 启动前会检查 SERVER_PORT 指定的端口是否被占用
    • 如果端口被占用,会显示警告信息并提供终止进程的建议
  4. 端口进程管理命令

    • make kill-$(SERVER_PORT):终止占用 SERVER_PORT 端口的进程(默认端口为 1234
    • make kill-port PORT=$(SERVER_PORT):终止指定端口的进程

修改方法

  1. 修改 Makefile

    # 在 Makefile 第 20 行修改
    SERVER_PORT := 1234  # 改为你想要的端口
    
  2. 注意事项

    • 修改 SERVER_PORT 后,需要重新执行 make docker-compose-dev 以更新 Docker Compose 文件
    • 如果使用 Docker 部署,还需要手动更新 Dockerfile 中的 EXPOSE 指令和健康检查 URL
      EXPOSE 1234  # 改为与 SERVER_PORT 一致
      HEALTHCHECK ... CMD wget ... http://localhost:1234/health ...
      
    • 确保修改后的端口未被其他服务占用
    • 如果修改了端口API 访问地址也需要相应更新(如 http://localhost:1234

示例

# 将 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

📡 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 文档

🧪 接口测试

使用 curl 测试

  1. 用户注册
curl -X POST http://localhost:1234/api/auth/register \
  -H "Content-Type: application/json" \
  -d '{
    "name": "testuser",
    "password": "password123"
  }'
  1. 用户登录
curl -X POST http://localhost:1234/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "name": "testuser",
    "password": "password123"
  }'
  1. 获取用户资料
curl -X GET http://localhost:1234/api/user/profile \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"

使用 Postman

  1. 导入 Postman 集合文件(如果有)
  2. 设置环境变量 base_urlhttp://localhost:1234
  3. 在认证接口获取 JWT token
  4. 在需要认证的接口中添加 Authorization: Bearer {token}

🔍 开发调试

日志查看

# 查看应用日志
tail -f logs/app.log

# 查看 Docker 容器日志
make docker-logs

数据库调试

# 连接数据库
mysql -h localhost -u root -p yinli

# 查看用户表
SELECT * FROM user;

Redis 调试

# 连接 Redis
redis-cli

# 查看所有键
KEYS *

# 查看频率限制
KEYS rate_limit:*

性能监控

# 查看应用性能
go tool pprof http://localhost:1234/debug/pprof/profile

# 内存使用情况
go tool pprof http://localhost:1234/debug/pprof/heap

端口进程管理

在 Ubuntu 环境中,如果端口被占用,可以使用以下命令查找并终止进程:

# 方法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

示例:

# 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

📊 测试报告

运行测试并生成报告:

make test-coverage

测试报告将生成在 build/coverage.html,可在浏览器中查看详细的覆盖率信息。

🚀 部署指南

生产环境部署

  1. 构建生产版本
make build
  1. 配置生产环境
# 修改 config/prod.yaml
# 设置正确的数据库和 Redis 连接信息
# 更改 JWT 密钥
  1. 使用 Docker 部署
# 设置环境变量
export MYSQL_ROOT_PASSWORD=your_secure_password
export REDIS_PASSWORD=your_redis_password

# 启动生产环境
make docker-up-prod

# 查看日志
make docker-logs-prod

环境变量

生产环境建议使用环境变量覆盖敏感配置:

export YINLI_DATABASE_PASSWORD=your_db_password
export YINLI_JWT_SECRET=your_jwt_secret
export YINLI_REDIS_PASSWORD=your_redis_password

📚 主要依赖库

核心依赖

中间件

测试工具

开发工具

🤝 贡献指南

  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 文件了解详情。

🆘 常见问题

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. 设置环境变量:
    export MYSQL_ROOT_PASSWORD=your_secure_password
    export REDIS_PASSWORD=your_redis_password
    
  2. 修改 config/prod.yaml 中的敏感配置JWT密钥等
  3. 启动生产环境:
    make docker-up-prod
    
  4. 查看日志:
    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
    extra_hosts:
      - "host.docker.internal:192.168.1.11"  # 替换为实际宿主机 IP
    

Q: Docker 镜像拉取失败怎么办?

A: 如果在中国大陆遇到镜像拉取超时或失败,可以:

  1. 配置 Docker 镜像加速器(推荐):

    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 服务才能生效!

    验证配置:

    docker info | grep -A 10 "Registry Mirrors"
    

    如果看到镜像源列表,说明配置成功。

  2. Rootless Docker 配置

    • 如果使用 rootless Dockerdocker version 显示 Context: rootless),配置文件位置为 ~/.config/docker/daemon.json
    • 配置后重启:systemctl --user restart docker
    • 验证:docker info | grep -A 10 "Registry Mirrors"
  3. 使用官方镜像名称:项目使用官方镜像名称(如 mysql:8.0redis:7-alpine),通过配置的 Docker 镜像加速器自动加速拉取

  4. Go 模块下载失败

    • Dockerfile 中已配置使用国内 Go 代理(GOPROXY=https://goproxy.cn,direct
    • 如果仍然失败,可以在 Dockerfile 中修改 GOPROXY 环境变量:
      ENV GOPROXY=https://goproxy.cn,https://goproxy.io,direct
      
    • 常用 Go 代理:https://goproxy.cnhttps://goproxy.iohttps://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 dockersystemctl --user status docker

Q: Docker 容器无法连接到宿主机上的 MySQL 和 Redis

A: 如果遇到 dial tcp: connection refusedlookup mysql on 127.0.0.11:53: server misbehaving 错误,可能是以下原因:

  1. MySQL/Redis 只监听本地接口127.0.0.1

    检查服务监听状态:

    # 检查 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:3306127.0.0.1:6379,说明只监听本地接口,需要修改配置。

  2. 修改 MySQL 配置允许外部连接

    # 编辑 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 用户权限

    # 允许 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
    

    或者创建新的用户:

    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 配置允许外部连接

    # 方法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 Dockerhost-gateway 可能指向错误的 IP。需要手动指定宿主机 IP

    # docker-compose.yml
    services:
      yinli-api:
        extra_hosts:
          - "host.docker.internal:192.168.1.11"  # 替换为实际宿主机 IP
    

    获取宿主机 IP

    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. 验证连接

    # 从容器内测试 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. 检查防火墙规则

    # 检查 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. 如果仍然无法停止

    # 手动重启 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容器可能由不同的上下文创建。

    # 查看当前 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

    # 停止并删除容器
    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

    # 将当前用户添加到 docker 组
    sudo usermod -aG docker $USER
    
    # 重新登录或执行以下命令使组权限生效
    newgrp docker
    
    # 验证是否成功
    groups | grep docker
    

    注意:添加到 docker 组后,需要:

    • 重新登录系统,或
    • 执行 newgrp docker 命令,或
    • 重新打开终端

    之后就可以不使用 sudo 直接操作 Docker 了。

  4. 检查 Docker socket 权限

    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 创建的容器是隔离的,不能互相管理
    • 如果遇到权限错误,可以尝试
      # 方法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 ...