- 新增 CHANGELOG.md 文件,记录项目所有重要变更 - 按功能类型分类(新增功能、改进优化、问题修复、文档更新) - 包含未发布版本和 2025-11-29 开发阶段的变更记录 - 删除 PROJECT_SUMMARY.pdf 和 README.pdf 文件 |
||
|---|---|---|
| config | ||
| docker | ||
| sql | ||
| src | ||
| test | ||
| .air.toml | ||
| .dockerignore | ||
| .gitignore | ||
| CHANGELOG.md | ||
| Dockerfile | ||
| go.mod | ||
| go.sum | ||
| localhost.session.sql | ||
| Makefile | ||
| PROJECT_SUMMARY.md | ||
| README.md | ||
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 (可选)
本地开发
- 克隆项目
git clone <repository-url>
cd yinli-api
- 安装依赖
make deps
- 配置数据库
# 创建数据库
mysql -u root -p < sql/init.sql
- 启动 Redis
redis-server
- 启动开发环境
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 文件:
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
常用国内镜像源(按推荐顺序):
- 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),无需额外配置
开发环境部署
- 生成 Docker Compose 文件(可选,文件已存在)
make docker-compose-dev
- 启动开发环境服务
make docker-up-dev
- 查看日志
make docker-logs
- 停止服务
make docker-down-dev
预发布环境部署
- 启动预发布环境服务
make docker-up-stage
- 查看日志
make docker-logs-stage
- 停止服务
make docker-down-stage
注意: 预发布环境使用不同的端口映射(MySQL: 3307, Redis: 6380),避免与开发环境冲突。
生产环境部署
- 设置环境变量(重要!)
export MYSQL_ROOT_PASSWORD=your_secure_password
export REDIS_PASSWORD=your_redis_password
- 启动生产环境服务
make docker-up-prod
- 查看日志
make docker-logs-prod
- 停止服务
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
影响范围:
-
配置文件中的
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时,同样会更新配置文件中的端口
- 执行
-
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
-
端口占用检查:
make dev、make stage、make prod启动前会检查SERVER_PORT指定的端口是否被占用- 如果端口被占用,会显示警告信息并提供终止进程的建议
-
端口进程管理命令:
make kill-$(SERVER_PORT):终止占用SERVER_PORT端口的进程(默认端口为 1234)make kill-port PORT=$(SERVER_PORT):终止指定端口的进程
修改方法:
-
修改 Makefile:
# 在 Makefile 第 20 行修改 SERVER_PORT := 1234 # 改为你想要的端口 -
注意事项:
- 修改
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 测试
- 用户注册
curl -X POST http://localhost:1234/api/auth/register \
-H "Content-Type: application/json" \
-d '{
"name": "testuser",
"password": "password123"
}'
- 用户登录
curl -X POST http://localhost:1234/api/auth/login \
-H "Content-Type: application/json" \
-d '{
"name": "testuser",
"password": "password123"
}'
- 获取用户资料
curl -X GET http://localhost:1234/api/user/profile \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
使用 Postman
- 导入 Postman 集合文件(如果有)
- 设置环境变量
base_url为http://localhost:1234 - 在认证接口获取 JWT token
- 在需要认证的接口中添加
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,可在浏览器中查看详细的覆盖率信息。
🚀 部署指南
生产环境部署
- 构建生产版本
make build
- 配置生产环境
# 修改 config/prod.yaml
# 设置正确的数据库和 Redis 连接信息
# 更改 JWT 密钥
- 使用 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
📚 主要依赖库
核心依赖
- Gin Web Framework - HTTP Web 框架
- GORM - ORM 库
- Viper - 配置管理
- JWT-Go - JWT 实现
- Go-Redis - Redis 客户端
中间件
- Gin-CORS - CORS 中间件
- Gin-Swagger - Swagger 文档
测试工具
开发工具
- Air - 热重载工具
- GolangCI-Lint - 代码检查工具
🤝 贡献指南
- Fork 项目
- 创建特性分支 (
git checkout -b feature/AmazingFeature) - 提交更改 (
git commit -m 'Add some AmazingFeature') - 推送到分支 (
git push origin feature/AmazingFeature) - 开启 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:
- 设置环境变量:
export MYSQL_ROOT_PASSWORD=your_secure_password export REDIS_PASSWORD=your_redis_password - 修改
config/prod.yaml中的敏感配置(JWT密钥等) - 启动生产环境:
make docker-up-prod - 查看日志:
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: 如果在中国大陆遇到镜像拉取超时或失败,可以:
-
配置 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"如果看到镜像源列表,说明配置成功。
-
Rootless Docker 配置:
- 如果使用 rootless Docker(
docker version显示Context: rootless),配置文件位置为~/.config/docker/daemon.json - 配置后重启:
systemctl --user restart docker - 验证:
docker info | grep -A 10 "Registry Mirrors"
- 如果使用 rootless Docker(
-
使用官方镜像名称:项目使用官方镜像名称(如
mysql:8.0、redis:7-alpine),通过配置的 Docker 镜像加速器自动加速拉取 -
Go 模块下载失败:
- Dockerfile 中已配置使用国内 Go 代理(
GOPROXY=https://goproxy.cn,direct) - 如果仍然失败,可以在 Dockerfile 中修改 GOPROXY 环境变量:
ENV GOPROXY=https://goproxy.cn,https://goproxy.io,direct - 常用 Go 代理:
https://goproxy.cn、https://goproxy.io、https://mirrors.aliyun.com/goproxy/
- Dockerfile 中已配置使用国内 Go 代理(
-
检查网络连接:确保能够访问镜像仓库
- 测试镜像源:
curl -I https://docker.m.daocloud.io/v2/ - 如果镜像加速器配置已生效但仍失败,可能是网络问题,可以稍后重试
- 测试镜像源:
-
如果镜像加速器仍未生效:
- 检查配置文件格式是否正确(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 错误,可能是以下原因:
-
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:3306或127.0.0.1:6379,说明只监听本地接口,需要修改配置。 -
修改 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 -
修改 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 -
修改 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 -
rootless Docker 的 host-gateway 问题:
如果使用 rootless Docker,
host-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 -
验证连接:
# 从容器内测试 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" -
检查防火墙规则:
# 检查 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
完整排查步骤:
- 检查 MySQL/Redis 是否监听在
0.0.0.0(而不是127.0.0.1) - 检查 MySQL 用户权限(允许从
%连接) - 检查 Redis
protected-mode是否已禁用 - 检查 Docker Compose 的
extra_hosts配置是否正确(rootless Docker 需要使用宿主机实际 IP) - 检查防火墙规则
- 查看容器日志:
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 容器的问题,已通过以下方式解决:
-
Docker Compose 配置优化(已应用):
- 为 MySQL 服务添加
stop_grace_period: 60s(增加优雅关闭时间) - 添加
stop_signal: SIGTERM(使用 SIGTERM 信号优雅停止) - 添加
init: true(使用 init 进程管理子进程,避免僵尸进程)
- 为 MySQL 服务添加
-
Makefile 自动重试机制(已实现):
- 所有
docker-down-*命令已添加自动检测权限错误 - 如果检测到权限错误,会自动重启 rootless Docker 服务并重试
- 显示完整的 Docker Compose 进度信息(
[+] Running和✔符号)
- 所有
-
如果仍然无法停止:
# 手动重启 rootless Docker 服务 systemctl --user restart docker # 然后再次尝试 make docker-down-stage
Q: Docker 权限错误(permission denied)怎么办?
A: 如果遇到 Error response from daemon: cannot stop container: permission denied 错误,可能的原因和解决方法:
-
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上下文。 -
临时解决方案(使用 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 -
永久解决方案(推荐):将用户添加到
docker组# 将当前用户添加到 docker 组 sudo usermod -aG docker $USER # 重新登录或执行以下命令使组权限生效 newgrp docker # 验证是否成功 groups | grep docker注意:添加到 docker 组后,需要:
- 重新登录系统,或
- 执行
newgrp docker命令,或 - 重新打开终端
之后就可以不使用
sudo直接操作 Docker 了。 -
检查 Docker socket 权限:
ls -la /var/run/docker.sock应该显示类似:
srw-rw---- 1 root docker,表示docker组有读写权限。 -
如果使用 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 ...