GoTest/Makefile
Table 62482b371c fix: 修复 Docker 容器连接宿主机 MySQL/Redis 问题并更新文档
主要改动:

1. Docker Compose 配置优化(dev/stage/prod):
   - 移除 mysql 和 redis 服务定义,改为使用宿主机服务
   - 更新 extra_hosts 使用宿主机实际 IP (192.168.1.11) 替代 host-gateway
   - 解决 rootless Docker 网络连接问题

2. Config 配置文件更新(dev/stage/prod):
   - 更新 database.host 和 redis.host 为 host.docker.internal
   - 支持容器通过 host.docker.internal 访问宿主机服务

3. README.md 文档完善:
   - 添加 Docker 容器连接宿主机 MySQL/Redis 的完整排查指南
   - 添加 MySQL 容器无法正常停止的解决方案
   - 更新 Docker Compose 环境说明
   - 添加常见错误信息和排查步骤

问题修复:
- 修复容器无法连接宿主机 MySQL(bind-address 和用户权限问题)
- 修复容器无法连接宿主机 Redis(bind 和 protected-mode 问题)
- 修复 rootless Docker 的 host-gateway 问题(使用宿主机实际 IP)

相关配置要求:
- MySQL: bind-address = 0.0.0.0, root@'%' 用户权限
- Redis: bind = 0.0.0.0, protected-mode = no
2025-11-29 06:55:50 +08:00

290 lines
9.7 KiB
Makefile
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.

# Makefile for Yinli API
# 变量定义
APP_NAME := yinli-api
VERSION := $(shell git tag --sort=-version:refname | head -n 1 2>/dev/null || echo "1.0.0")
BUILD_DIR := build
DOCKER_DIR := docker
DOC_DIR := doc
# Go 相关变量
GO := go
GOFMT := gofmt
GOVET := go vet
# Docker 相关变量
DOCKER := docker
DOCKER_COMPOSE := $(DOCKER) compose
# 默认目标
.PHONY: help
help: ## 显示帮助信息
@echo "可用的命令:"
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
# 开发环境
.PHONY: dev
dev: ## 启动开发环境
@echo "启动开发环境..."
$(GO) run src/main.go -env=dev
.PHONY: stage
stage: ## 启动预发布环境
@echo "启动预发布环境..."
$(GO) run src/main.go -env=stage
.PHONY: prod
prod: ## 启动生产环境
@echo "启动生产环境..."
$(GO) run src/main.go -env=prod
# 构建相关
.PHONY: build
build: ## 构建应用程序
@echo "构建应用程序..."
@mkdir -p $(BUILD_DIR)
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 $(GO) build -ldflags="-w -s" -o $(BUILD_DIR)/$(APP_NAME) src/main.go
.PHONY: build-windows
build-windows: ## 构建Windows版本
@echo "构建Windows版本..."
@mkdir -p $(BUILD_DIR)
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 $(GO) build -ldflags="-w -s" -o $(BUILD_DIR)/$(APP_NAME).exe src/main.go
.PHONY: build-mac
build-mac: ## 构建macOS版本
@echo "构建macOS版本..."
@mkdir -p $(BUILD_DIR)
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 $(GO) build -ldflags="-w -s" -o $(BUILD_DIR)/$(APP_NAME)-mac src/main.go
.PHONY: build-all
build-all: build build-windows build-mac ## 构建所有平台版本
# 依赖管理
.PHONY: deps
deps: ## 下载依赖
@echo "下载依赖..."
$(GO) mod download
.PHONY: deps-update
deps-update: ## 更新依赖
@echo "更新依赖..."
$(GO) mod tidy
$(GO) get -u ./...
# 代码质量
.PHONY: fmt
fmt: ## 格式化代码
@echo "格式化代码..."
$(GOFMT) -s -w .
.PHONY: vet
vet: ## 代码静态检查
@echo "代码静态检查..."
$(GOVET) ./...
.PHONY: check
check: fmt vet ## 执行所有代码检查
# 测试相关
.PHONY: test
test: ## 运行测试
@echo "运行测试..."
$(GO) test -v ./...
.PHONY: test-coverage
test-coverage: ## 运行测试并生成覆盖率报告
@echo "运行测试并生成覆盖率报告..."
@mkdir -p $(BUILD_DIR)
$(GO) test -v -coverprofile=$(BUILD_DIR)/coverage.out ./...
$(GO) tool cover -html=$(BUILD_DIR)/coverage.out -o $(BUILD_DIR)/coverage.html
@echo "覆盖率报告已生成: $(BUILD_DIR)/coverage.html"
.PHONY: test-race
test-race: ## 运行竞态检测测试
@echo "运行竞态检测测试..."
$(GO) test -race -v ./...
.PHONY: benchmark
benchmark: ## 运行基准测试
@echo "运行基准测试..."
$(GO) test -bench=. -benchmem ./...
# 文档生成
.PHONY: docs
docs: ## 生成API文档
@echo "生成API文档..."
@mkdir -p $(DOC_DIR)/dev $(DOC_DIR)/stage $(DOC_DIR)/prod
@echo "请先安装 swag: go install github.com/swaggo/swag/cmd/swag@latest"
@echo "然后运行: swag init -g src/main.go -o doc/dev --parseDependency --parseInternal"
.PHONY: docs-serve
docs-serve: docs ## 启动文档服务器
@echo "启动文档服务器..."
@cd $(DOC_DIR) && python3 -m http.server 8081
# Docker 相关
.PHONY: docker-build
docker-build: ## 构建Docker镜像
@echo "构建Docker镜像..."
$(DOCKER) build -t $(APP_NAME):$(VERSION) -t $(APP_NAME):latest .
.PHONY: docker-compose-dev
docker-compose-dev: ## 生成开发环境Docker Compose文件
@echo "生成开发环境Docker Compose文件..."
@mkdir -p $(DOCKER_DIR)
@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 " - \"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 " volumes:" >> $(DOCKER_DIR)/docker-compose.dev.yml
@echo " - ../config:/app/config:ro" >> $(DOCKER_DIR)/docker-compose.dev.yml
@echo " network_mode: host" >> $(DOCKER_DIR)/docker-compose.dev.yml
@echo "开发环境Docker Compose文件已生成: $(DOCKER_DIR)/docker-compose.dev.yml"
@echo "注意: MySQL 和 Redis 需要单独部署,容器使用 network_mode: host 直接访问宿主机上的服务"
.PHONY: docker-up-dev
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容器
@echo "停止并移除Docker容器..."
@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) -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
clean: ## 清理构建文件
@echo "清理构建文件..."
rm -rf $(BUILD_DIR)
rm -rf $(DOC_DIR)
$(GO) clean
.PHONY: clean-docker
clean-docker: ## 清理Docker资源
@echo "清理Docker资源..."
$(DOCKER) system prune -f
$(DOCKER) volume prune -f
# 安装工具
.PHONY: install-tools
install-tools: ## 安装开发工具
@echo "安装开发工具..."
$(GO) install github.com/swaggo/swag/cmd/swag@latest
$(GO) install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
# 全流程
.PHONY: all
all: clean deps check test build docs ## 执行完整的构建流程
.PHONY: ci
ci: deps check test-coverage ## CI流程
# 版本管理
.PHONY: version
version: ## 显示版本信息
@echo "应用名称: $(APP_NAME)"
@echo "版本: $(VERSION)"
@echo "Go版本: $(shell $(GO) version)"
# 默认目标
.DEFAULT_GOAL := help