From 7e5572344be8cad04f908cf594fafeaf797ada51 Mon Sep 17 00:00:00 2001
From: Table
Date: Sat, 29 Nov 2025 06:18:17 +0800
Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=20MySQL=20=E5=AE=B9?=
=?UTF-8?q?=E5=99=A8=E5=9C=A8=20rootless=20Docker=20=E7=8E=AF=E5=A2=83?=
=?UTF-8?q?=E4=B8=8B=E6=97=A0=E6=B3=95=E6=AD=A3=E5=B8=B8=E5=81=9C=E6=AD=A2?=
=?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
主要改动:
- Docker Compose 配置优化:
* 为所有环境的 MySQL 服务添加 stop_grace_period: 60s(增加优雅关闭时间)
* 添加 stop_signal: SIGTERM(使用 SIGTERM 信号优雅停止)
* 添加 init: true(使用 init 进程管理子进程,避免僵尸进程)
- Makefile 改进:
* 所有 docker-down-* 命令添加自动重试机制
* 自动检测权限错误并重启 rootless Docker 服务
* 显示完整的 Docker Compose 进度信息(包括容器状态变化)
- README.md 更新:
* 添加 Docker 权限问题的详细解决方案
* 包括 rootless Docker 的特殊处理方法和自动重试机制说明
问题原因:
MySQL 容器在 rootless Docker 环境下停止时遇到权限问题,需要更长的优雅关闭时间来处理 InnoDB 数据文件。
解决方案:
1. 增加 stop_grace_period 到 60 秒,给 MySQL 足够时间优雅关闭
2. 使用 init 进程管理子进程,避免权限问题
3. 在 Makefile 中添加自动检测和重试机制,无需手动重启 Docker 服务
---
Dockerfile | 12 +-
Makefile | 106 +++++++++-
PROJECT_SUMMARY.md | 48 ++++-
README.md | 335 +++++++++++++++++++++++++++++++-
config/prod.yaml | 4 +-
config/stage.yaml | 4 +-
docker/docker-compose.dev.yml | 7 +-
docker/docker-compose.prod.yml | 71 +++++++
docker/docker-compose.stage.yml | 55 ++++++
9 files changed, 609 insertions(+), 33 deletions(-)
create mode 100644 docker/docker-compose.prod.yml
create mode 100644 docker/docker-compose.stage.yml
diff --git a/Dockerfile b/Dockerfile
index 0404521..e7cd630 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,5 +1,10 @@
# 多阶段构建
-FROM golang:1.21-alpine AS builder
+# 使用阿里云镜像加速(如需使用官方镜像,请配置 Docker 镜像加速器)
+FROM golang:1.24-alpine AS builder
+
+# 设置 Go 代理(中国大陆用户)
+ENV GOPROXY=https://goproxy.cn,direct
+ENV GOSUMDB=sum.golang.google.cn
# 设置工作目录
WORKDIR /app
@@ -20,6 +25,7 @@ COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-w -s" -o main src/main.go
# 最终镜像
+# 使用阿里云镜像加速(如需使用官方镜像,请配置 Docker 镜像加速器)
FROM alpine:latest
# 安装必要的包
@@ -47,11 +53,11 @@ RUN chown -R appuser:appgroup /app
USER appuser
# 暴露端口
-EXPOSE 8080
+EXPOSE 1234
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
- CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1
+ CMD wget --no-verbose --tries=1 --spider http://localhost:1234/health || exit 1
# 启动应用
CMD ["./main"]
diff --git a/Makefile b/Makefile
index bd44e73..12e46ed 100644
--- a/Makefile
+++ b/Makefile
@@ -14,7 +14,7 @@ GOVET := go vet
# Docker 相关变量
DOCKER := docker
-DOCKER_COMPOSE := docker-compose
+DOCKER_COMPOSE := $(DOCKER) compose
# 默认目标
.PHONY: help
@@ -133,15 +133,13 @@ docker-build: ## 构建Docker镜像
docker-compose-dev: ## 生成开发环境Docker Compose文件
@echo "生成开发环境Docker Compose文件..."
@mkdir -p $(DOCKER_DIR)
- @echo "version: '3.8'" > $(DOCKER_DIR)/docker-compose.dev.yml
- @echo "" >> $(DOCKER_DIR)/docker-compose.dev.yml
- @echo "services:" >> $(DOCKER_DIR)/docker-compose.dev.yml
+ @echo "services:" > $(DOCKER_DIR)/docker-compose.dev.yml
@echo " yinli-api:" >> $(DOCKER_DIR)/docker-compose.dev.yml
@echo " build:" >> $(DOCKER_DIR)/docker-compose.dev.yml
@echo " context: .." >> $(DOCKER_DIR)/docker-compose.dev.yml
@echo " dockerfile: Dockerfile" >> $(DOCKER_DIR)/docker-compose.dev.yml
@echo " ports:" >> $(DOCKER_DIR)/docker-compose.dev.yml
- @echo " - \"8080:8080\"" >> $(DOCKER_DIR)/docker-compose.dev.yml
+ @echo " - \"1234:1234\"" >> $(DOCKER_DIR)/docker-compose.dev.yml
@echo " environment:" >> $(DOCKER_DIR)/docker-compose.dev.yml
@echo " - APP_ENV=dev" >> $(DOCKER_DIR)/docker-compose.dev.yml
@echo " depends_on:" >> $(DOCKER_DIR)/docker-compose.dev.yml
@@ -188,15 +186,103 @@ docker-up-dev: docker-compose-dev ## 启动开发环境Docker容器
@echo "启动开发环境Docker容器..."
cd $(DOCKER_DIR) && $(DOCKER_COMPOSE) -f docker-compose.dev.yml up -d
+.PHONY: docker-up-stage
+docker-up-stage: ## 启动预发布环境Docker容器
+ @echo "启动预发布环境Docker容器..."
+ cd $(DOCKER_DIR) && $(DOCKER_COMPOSE) -f docker-compose.stage.yml up -d
+
+.PHONY: docker-up-prod
+docker-up-prod: ## 启动生产环境Docker容器
+ @echo "启动生产环境Docker容器..."
+ @echo "警告: 请确保已设置 MYSQL_ROOT_PASSWORD 和 REDIS_PASSWORD 环境变量"
+ cd $(DOCKER_DIR) && $(DOCKER_COMPOSE) -f docker-compose.prod.yml up -d
+
.PHONY: docker-down
-docker-down: ## 停止并移除Docker容器
+docker-down: ## 停止并移除所有Docker容器
@echo "停止并移除Docker容器..."
- cd $(DOCKER_DIR) && $(DOCKER_COMPOSE) down
+ @cd $(DOCKER_DIR) && \
+ OUTPUT=$$($(DOCKER_COMPOSE) -f docker-compose.dev.yml -f docker-compose.stage.yml -f docker-compose.prod.yml down --remove-orphans 2>&1); \
+ echo "$$OUTPUT"; \
+ if echo "$$OUTPUT" | grep -q "permission denied"; then \
+ echo ""; \
+ echo "⚠️ 检测到权限错误,自动重启 rootless Docker 服务并重试..."; \
+ systemctl --user restart docker >/dev/null 2>&1 || true; \
+ sleep 3; \
+ echo "重试停止容器..."; \
+ $(DOCKER_COMPOSE) -f docker-compose.dev.yml -f docker-compose.stage.yml -f docker-compose.prod.yml down --remove-orphans 2>&1 || { \
+ echo ""; \
+ echo "❌ 仍然失败,请手动执行: systemctl --user restart docker && make docker-down"; \
+ exit 0; \
+ }; \
+ fi
+
+.PHONY: docker-down-dev
+docker-down-dev: ## 停止并移除开发环境Docker容器
+ @echo "停止并移除开发环境Docker容器..."
+ @cd $(DOCKER_DIR) && $(DOCKER_COMPOSE) -f docker-compose.dev.yml down --remove-orphans 2>&1 || { \
+ echo ""; \
+ echo "⚠️ 如果遇到权限错误(permission denied),容器可能由 root 用户创建。"; \
+ echo " 请手动执行: sudo docker compose -f docker/docker-compose.dev.yml down"; \
+ exit 0; \
+ }
+
+.PHONY: docker-down-stage
+docker-down-stage: ## 停止并移除预发布环境Docker容器
+ @echo "停止并移除预发布环境Docker容器..."
+ @cd $(DOCKER_DIR) && \
+ OUTPUT=$$($(DOCKER_COMPOSE) -f docker-compose.stage.yml down --remove-orphans 2>&1); \
+ echo "$$OUTPUT"; \
+ if echo "$$OUTPUT" | grep -q "permission denied"; then \
+ echo ""; \
+ echo "⚠️ 检测到权限错误,自动重启 rootless Docker 服务并重试..."; \
+ systemctl --user restart docker >/dev/null 2>&1 || true; \
+ sleep 3; \
+ echo "重试停止容器..."; \
+ $(DOCKER_COMPOSE) -f docker-compose.stage.yml down --remove-orphans 2>&1 || { \
+ echo ""; \
+ echo "❌ 仍然失败,请手动执行: systemctl --user restart docker && make docker-down-stage"; \
+ exit 0; \
+ }; \
+ fi
+
+.PHONY: docker-down-prod
+docker-down-prod: ## 停止并移除生产环境Docker容器
+ @echo "停止并移除生产环境Docker容器..."
+ @cd $(DOCKER_DIR) && \
+ if ! $(DOCKER_COMPOSE) -f docker-compose.prod.yml down --remove-orphans 2>&1 | tee /dev/stderr | grep -q "permission denied"; then \
+ :; \
+ else \
+ echo ""; \
+ echo "⚠️ 检测到权限错误,自动重启 rootless Docker 服务并重试..."; \
+ systemctl --user restart docker >/dev/null 2>&1 || true; \
+ sleep 3; \
+ echo "重试停止容器..."; \
+ $(DOCKER_COMPOSE) -f docker-compose.prod.yml down --remove-orphans 2>&1 || { \
+ echo ""; \
+ echo "❌ 仍然失败,请手动执行: systemctl --user restart docker && make docker-down-prod"; \
+ exit 0; \
+ }; \
+ fi
.PHONY: docker-logs
-docker-logs: ## 查看Docker容器日志
- @echo "查看Docker容器日志..."
- cd $(DOCKER_DIR) && $(DOCKER_COMPOSE) logs -f
+docker-logs: ## 查看开发环境Docker容器日志
+ @echo "查看开发环境Docker容器日志..."
+ cd $(DOCKER_DIR) && $(DOCKER_COMPOSE) -f docker-compose.dev.yml logs -f
+
+.PHONY: docker-logs-stage
+docker-logs-stage: ## 查看预发布环境Docker容器日志
+ @echo "查看预发布环境Docker容器日志..."
+ cd $(DOCKER_DIR) && $(DOCKER_COMPOSE) -f docker-compose.stage.yml logs -f
+
+.PHONY: docker-logs-prod
+docker-logs-prod: ## 查看生产环境Docker容器日志
+ @echo "查看生产环境Docker容器日志..."
+ cd $(DOCKER_DIR) && $(DOCKER_COMPOSE) -f docker-compose.prod.yml logs -f
+
+.PHONY: docker-ps
+docker-ps: ## 查看所有Docker容器状态
+ @echo "查看所有Docker容器状态..."
+ cd $(DOCKER_DIR) && $(DOCKER_COMPOSE) -f docker-compose.dev.yml -f docker-compose.stage.yml -f docker-compose.prod.yml ps
# 清理
.PHONY: clean
diff --git a/PROJECT_SUMMARY.md b/PROJECT_SUMMARY.md
index be8ba19..6631844 100644
--- a/PROJECT_SUMMARY.md
+++ b/PROJECT_SUMMARY.md
@@ -81,12 +81,48 @@ make dev
```
### 2. Docker 启动
+
+#### 开发环境
```bash
-# 生成 Docker Compose 文件
+# 生成 Docker Compose 文件(可选,文件已存在)
make docker-compose-dev
-# 启动 Docker 容器
+# 启动开发环境容器
make docker-up-dev
+
+# 查看日志
+make docker-logs
+
+# 停止服务
+make docker-down-dev
+```
+
+#### 预发布环境
+```bash
+# 启动预发布环境容器
+make docker-up-stage
+
+# 查看日志
+make docker-logs-stage
+
+# 停止服务
+make docker-down-stage
+```
+
+#### 生产环境
+```bash
+# 设置环境变量(重要!)
+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
```
### 3. 运行测试
@@ -204,9 +240,15 @@ make dev
### 生产环境
```bash
+# 设置环境变量
+export MYSQL_ROOT_PASSWORD=your_secure_password
+export REDIS_PASSWORD=your_redis_password
+
# 使用 Docker 部署
-make docker-compose-prod
make docker-up-prod
+
+# 查看日志
+make docker-logs-prod
```
## 📝 下一步建议
diff --git a/README.md b/README.md
index b61a87b..69c9e71 100644
--- a/README.md
+++ b/README.md
@@ -94,16 +94,164 @@ make dev
### Docker 部署
-1. **生成 Docker Compose 文件**
+项目支持三种环境的 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. **启动服务**
+2. **启动开发环境服务**
```bash
make docker-up-dev
```
+3. **查看日志**
+```bash
+make docker-logs
+```
+
+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密钥、数据库密码等)
+
## 📋 可用命令
### 开发命令
@@ -149,12 +297,19 @@ make docs-serve # 启动文档服务器
### Docker 操作
```bash
-make docker-build # 构建 Docker 镜像
-make docker-up-dev # 启动开发环境容器
-make docker-up-stage # 启动预发布环境容器
-make docker-up-prod # 启动生产环境容器
-make docker-down # 停止容器
-make docker-logs # 查看容器日志
+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 # 查看所有容器状态
```
## 🔧 配置说明
@@ -386,8 +541,15 @@ make build
3. **使用 Docker 部署**
```bash
-make docker-compose-prod
+# 设置环境变量
+export MYSQL_ROOT_PASSWORD=your_secure_password
+export REDIS_PASSWORD=your_redis_password
+
+# 启动生产环境
make docker-up-prod
+
+# 查看日志
+make docker-logs-prod
```
### 环境变量
@@ -451,4 +613,157 @@ A: 1. 在 `src/handler` 中添加处理函数
A: 在 `src/middleware` 目录下创建新的中间件文件,参考现有中间件的实现。
### Q: 如何部署到生产环境?
-A: 使用 `make docker-compose-prod` 生成生产环境配置,然后使用 `make docker-up-prod` 部署。
+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**: 开发环境,端口映射 MySQL:3306, Redis:6379
+- **stage**: 预发布环境,端口映射 MySQL:3307, Redis:6380
+- **prod**: 生产环境,端口映射 MySQL:3308, Redis:6381,包含健康检查和自动重启
+
+### 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 权限错误(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 ...
+ ```
diff --git a/config/prod.yaml b/config/prod.yaml
index 71864a9..01f698d 100644
--- a/config/prod.yaml
+++ b/config/prod.yaml
@@ -3,7 +3,7 @@ server:
mode: release
database:
- host: localhost
+ host: mysql # Docker环境使用服务名,本地环境使用localhost
port: 3306
username: root
password: sasasasa
@@ -15,7 +15,7 @@ database:
maxOpenConns: 500
redis:
- host: localhost
+ host: redis # Docker环境使用服务名,本地环境使用localhost
port: 6379
password: ""
db: 2
diff --git a/config/stage.yaml b/config/stage.yaml
index 21077c2..eb143cd 100644
--- a/config/stage.yaml
+++ b/config/stage.yaml
@@ -3,7 +3,7 @@ server:
mode: release
database:
- host: localhost
+ host: mysql # Docker环境使用服务名,本地环境使用localhost
port: 3306
username: root
password: sasasasa
@@ -15,7 +15,7 @@ database:
maxOpenConns: 200
redis:
- host: localhost
+ host: redis # Docker环境使用服务名,本地环境使用localhost
port: 6379
password: ""
db: 1
diff --git a/docker/docker-compose.dev.yml b/docker/docker-compose.dev.yml
index d09b3bb..d336594 100644
--- a/docker/docker-compose.dev.yml
+++ b/docker/docker-compose.dev.yml
@@ -1,12 +1,10 @@
-version: '3.8'
-
services:
yinli-api:
build:
context: ..
dockerfile: Dockerfile
ports:
- - "8080:8080"
+ - "1234:1234"
environment:
- APP_ENV=dev
depends_on:
@@ -29,6 +27,9 @@ services:
- ../sql:/docker-entrypoint-initdb.d:ro
networks:
- yinli-network
+ stop_grace_period: 60s
+ stop_signal: SIGTERM
+ init: true
redis:
image: redis:7-alpine
diff --git a/docker/docker-compose.prod.yml b/docker/docker-compose.prod.yml
new file mode 100644
index 0000000..156075c
--- /dev/null
+++ b/docker/docker-compose.prod.yml
@@ -0,0 +1,71 @@
+services:
+ yinli-api:
+ build:
+ context: ..
+ dockerfile: Dockerfile
+ ports:
+ - "1234:1234"
+ environment:
+ - APP_ENV=prod
+ depends_on:
+ - mysql
+ - redis
+ volumes:
+ - ../config:/app/config:ro
+ networks:
+ - yinli-network
+ restart: always
+ healthcheck:
+ test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:1234/health"]
+ interval: 30s
+ timeout: 10s
+ retries: 3
+ start_period: 40s
+
+ mysql:
+ image: mysql:8.0
+ environment:
+ MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
+ MYSQL_DATABASE: yinli
+ ports:
+ - "3308:3306"
+ volumes:
+ - mysql_prod_data:/var/lib/mysql
+ - ../sql:/docker-entrypoint-initdb.d:ro
+ networks:
+ - yinli-network
+ restart: always
+ stop_grace_period: 60s
+ stop_signal: SIGTERM
+ init: true
+ command: --default-authentication-plugin=mysql_native_password
+ healthcheck:
+ test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
+ interval: 10s
+ timeout: 5s
+ retries: 5
+
+ redis:
+ image: redis:7-alpine
+ ports:
+ - "6381:6379"
+ volumes:
+ - redis_prod_data:/data
+ networks:
+ - yinli-network
+ restart: always
+ command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD:-}
+ healthcheck:
+ test: ["CMD", "redis-cli", "ping"]
+ interval: 10s
+ timeout: 5s
+ retries: 5
+
+volumes:
+ mysql_prod_data:
+ redis_prod_data:
+
+networks:
+ yinli-network:
+ driver: bridge
+
diff --git a/docker/docker-compose.stage.yml b/docker/docker-compose.stage.yml
new file mode 100644
index 0000000..a04e20c
--- /dev/null
+++ b/docker/docker-compose.stage.yml
@@ -0,0 +1,55 @@
+services:
+ yinli-api:
+ build:
+ context: ..
+ dockerfile: Dockerfile
+ ports:
+ - "1234:1234"
+ environment:
+ - APP_ENV=stage
+ depends_on:
+ - mysql
+ - redis
+ volumes:
+ - ../config:/app/config:ro
+ networks:
+ - yinli-network
+ restart: unless-stopped
+
+ mysql:
+ image: mysql:8.0
+ environment:
+ MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-sasasasa}
+ MYSQL_DATABASE: yinli
+ ports:
+ - "3307:3306"
+ volumes:
+ - mysql_stage_data:/var/lib/mysql
+ - ../sql:/docker-entrypoint-initdb.d:ro
+ networks:
+ - yinli-network
+ restart: unless-stopped
+ stop_grace_period: 60s
+ stop_signal: SIGTERM
+ init: true
+ command: --default-authentication-plugin=mysql_native_password
+
+ redis:
+ image: redis:7-alpine
+ ports:
+ - "6380:6379"
+ volumes:
+ - redis_stage_data:/data
+ networks:
+ - yinli-network
+ restart: unless-stopped
+ command: redis-server --appendonly yes
+
+volumes:
+ mysql_stage_data:
+ redis_stage_data:
+
+networks:
+ yinli-network:
+ driver: bridge
+