fix(test): 修复测试环境问题
ci/woodpecker/push/woodpecker Pipeline failed

1. jest.setup.js:
   - 添加 Request/Response/Headers 全局对象 mock
   - 解决 'Request is not defined' 错误

2. .eslintrc.json:
   - 将 jest.setup.js 添加到忽略列表

3. shared-mocks.tsx:
   - 添加 ArrowUp 图标 mock

4. back-to-top.test.tsx:
   - 重写测试使用 import 语法
   - 使用 fireEvent.scroll 触发滚动事件
   - 修复组件渲染测试
This commit is contained in:
张翔
2026-03-29 14:50:09 +08:00
parent e0ca8235c8
commit 7cbb7a9ac8
12 changed files with 2289 additions and 20 deletions
+332
View File
@@ -0,0 +1,332 @@
#!/bin/bash
# Docker镜像清理脚本
# 用途:检查生产环境Docker镜像使用情况并清理未使用的镜像
set -e
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# 日志函数
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
log_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# 分隔线
separator() {
echo "======================================================================"
}
# 检查Docker是否运行
check_docker() {
if ! docker info > /dev/null 2>&1; then
log_error "Docker未运行,请先启动Docker"
exit 1
fi
log_success "Docker运行正常"
}
# 显示磁盘使用情况
show_disk_usage() {
separator
log_info "磁盘使用情况"
separator
echo ""
log_info "系统磁盘使用:"
df -h | grep -E "Filesystem|/$|/home|/var"
echo ""
log_info "Docker数据目录使用:"
docker system df
echo ""
log_info "Docker镜像详情:"
docker system df -v | grep -A 100 "Images space usage"
}
# 列出所有镜像
list_images() {
separator
log_info "Docker镜像列表"
separator
echo ""
log_info "所有镜像(按大小排序):"
docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}\t{{.CreatedSince}}" | head -20
echo ""
log_info "悬空镜像(<none>):"
docker images -f "dangling=true" --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}\t{{.CreatedSince}}"
echo ""
log_info "未使用的镜像(没有被任何容器使用):"
docker images --format "{{.Repository}}:{{.Tag}} {{.ID}}" | while read image id; do
if ! docker ps -a --format "{{.Image}}" | grep -q "$id"; then
echo "$image (ID: $id)"
fi
done
}
# 列出所有容器
list_containers() {
separator
log_info "Docker容器列表"
separator
echo ""
log_info "运行中的容器:"
docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Size}}"
echo ""
log_info "已停止的容器:"
docker ps -f "status=exited" --format "table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Size}}"
}
# 分析镜像使用情况
analyze_images() {
separator
log_info "镜像使用分析"
separator
echo ""
log_info "正在分析镜像使用情况..."
# 统计信息
TOTAL_IMAGES=$(docker images -q | wc -l)
DANGLING_IMAGES=$(docker images -f "dangling=true" -q | wc -l)
TOTAL_SIZE=$(docker images --format "{{.Size}}" | grep -oE '[0-9]+GB|[0-9]+MB' | head -1)
log_info "总镜像数: $TOTAL_IMAGES"
log_info "悬空镜像数: $DANGLING_IMAGES"
log_info "镜像总大小约: $TOTAL_SIZE"
echo ""
log_info "可以清理的内容:"
echo " 1. 悬空镜像(<none>标签): $DANGLING_IMAGES"
echo " 2. 已停止的容器"
echo " 3. 未使用的网络"
echo " 4. 构建缓存"
}
# 清理悬空镜像
clean_dangling_images() {
separator
log_info "清理悬空镜像"
separator
DANGLING_COUNT=$(docker images -f "dangling=true" -q | wc -l)
if [ "$DANGLING_COUNT" -eq 0 ]; then
log_success "没有悬空镜像需要清理"
return
fi
log_warning "发现 $DANGLING_COUNT 个悬空镜像"
read -p "是否清理悬空镜像?(y/n): " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
log_info "正在清理悬空镜像..."
docker image prune -f
log_success "悬空镜像清理完成"
else
log_info "跳过悬空镜像清理"
fi
}
# 清理未使用的镜像
clean_unused_images() {
separator
log_info "清理未使用的镜像"
separator
log_warning "这将删除所有没有被容器使用的镜像"
read -p "是否清理未使用的镜像?(y/n): " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
log_info "正在清理未使用的镜像..."
docker image prune -a -f
log_success "未使用镜像清理完成"
else
log_info "跳过未使用镜像清理"
fi
}
# 清理已停止的容器
clean_stopped_containers() {
separator
log_info "清理已停止的容器"
separator
STOPPED_COUNT=$(docker ps -f "status=exited" -q | wc -l)
if [ "$STOPPED_COUNT" -eq 0 ]; then
log_success "没有已停止的容器需要清理"
return
fi
log_warning "发现 $STOPPED_COUNT 个已停止的容器"
read -p "是否清理已停止的容器?(y/n): " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
log_info "正在清理已停止的容器..."
docker container prune -f
log_success "已停止容器清理完成"
else
log_info "跳过已停止容器清理"
fi
}
# 清理构建缓存
clean_build_cache() {
separator
log_info "清理构建缓存"
separator
log_warning "这将删除所有构建缓存"
read -p "是否清理构建缓存?(y/n): " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
log_info "正在清理构建缓存..."
docker builder prune -a -f
log_success "构建缓存清理完成"
else
log_info "跳过构建缓存清理"
fi
}
# 一键清理
clean_all() {
separator
log_info "一键清理(安全模式)"
separator
log_warning "将执行以下清理操作:"
echo " 1. 清理悬空镜像"
echo " 2. 清理已停止的容器"
echo " 3. 清理未使用的网络"
echo " 4. 清理构建缓存"
echo ""
log_warning "不会删除正在使用的镜像"
read -p "是否继续?(y/n): " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
log_info "开始一键清理..."
log_info "清理悬空镜像..."
docker image prune -f
log_info "清理已停止的容器..."
docker container prune -f
log_info "清理未使用的网络..."
docker network prune -f
log_info "清理构建缓存..."
docker builder prune -f
log_success "一键清理完成"
else
log_info "取消一键清理"
fi
}
# 显示清理效果
show_cleanup_result() {
separator
log_info "清理后的磁盘使用情况"
separator
echo ""
docker system df
echo ""
log_info "磁盘空间变化:"
df -h | grep -E "Filesystem|/$"
}
# 主菜单
main_menu() {
clear
separator
echo "Docker镜像清理工具"
separator
echo ""
echo "1. 查看磁盘使用情况"
echo "2. 列出所有镜像"
echo "3. 列出所有容器"
echo "4. 分析镜像使用情况"
echo "5. 清理悬空镜像(安全)"
echo "6. 清理未使用的镜像(谨慎)"
echo "7. 清理已停止的容器"
echo "8. 清理构建缓存"
echo "9. 一键清理(安全模式)"
echo "0. 退出"
echo ""
read -p "请选择操作 (0-9): " choice
case $choice in
1) show_disk_usage ;;
2) list_images ;;
3) list_containers ;;
4) analyze_images ;;
5) clean_dangling_images ;;
6) clean_unused_images ;;
7) clean_stopped_containers ;;
8) clean_build_cache ;;
9) clean_all ;;
0)
log_info "退出程序"
exit 0
;;
*)
log_error "无效选择"
;;
esac
echo ""
read -p "按回车键继续..."
main_menu
}
# 主函数
main() {
log_info "Docker镜像清理工具启动"
check_docker
if [ "$1" = "--auto" ]; then
log_info "自动模式:执行安全清理"
clean_dangling_images
clean_stopped_containers
clean_build_cache
show_cleanup_result
else
main_menu
fi
}
# 执行主函数
main "$@"
+237
View File
@@ -0,0 +1,237 @@
#!/bin/bash
# 方案A: 服务重启脚本
# 用途:快速重启Docker容器和Nginx服务
set -e
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
# 日志函数
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
log_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
separator() {
echo "======================================================================"
}
# 记录日志
LOG_FILE="/tmp/service-restart-$(date +%Y%m%d_%H%M%S).log"
exec > >(tee -a "$LOG_FILE") 2>&1
separator
echo "方案A: 服务重启脚本"
echo "执行时间: $(date)"
separator
# 1. 检查当前状态
log_info "步骤1: 检查当前服务状态"
separator
echo ""
log_info "当前Docker容器状态:"
docker ps -a --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
echo ""
log_info "当前Nginx状态:"
systemctl status nginx --no-pager | head -10
echo ""
log_info "当前系统资源:"
echo "CPU和内存:"
top -bn1 | head -5
echo ""
echo "磁盘使用:"
df -h | grep -E "Filesystem|/$"
# 2. 查找docker-compose文件
log_info "步骤2: 查找docker-compose配置文件"
separator
POSSIBLE_PATHS=(
"/opt/novalon-website"
"/var/www/novalon-website"
"/home/novalon-website"
"/root/novalon-website"
"/srv/novalon-website"
"."
)
COMPOSE_FILE=""
PROJECT_DIR=""
for path in "${POSSIBLE_PATHS[@]}"; do
if [ -f "$path/docker-compose.prod.yml" ]; then
PROJECT_DIR="$path"
COMPOSE_FILE="$path/docker-compose.prod.yml"
log_success "找到docker-compose.prod.yml: $COMPOSE_FILE"
break
elif [ -f "$path/docker-compose.yml" ]; then
PROJECT_DIR="$path"
COMPOSE_FILE="$path/docker-compose.yml"
log_success "找到docker-compose.yml: $COMPOSE_FILE"
break
fi
done
if [ -z "$COMPOSE_FILE" ]; then
log_error "未找到docker-compose配置文件"
log_info "请手动指定项目目录:"
read -p "请输入项目目录路径: " PROJECT_DIR
if [ -f "$PROJECT_DIR/docker-compose.prod.yml" ]; then
COMPOSE_FILE="$PROJECT_DIR/docker-compose.prod.yml"
elif [ -f "$PROJECT_DIR/docker-compose.yml" ]; then
COMPOSE_FILE="$PROJECT_DIR/docker-compose.yml"
else
log_error "指定目录中未找到docker-compose文件"
exit 1
fi
fi
log_info "项目目录: $PROJECT_DIR"
log_info "配置文件: $COMPOSE_FILE"
# 3. 重启Docker容器
log_info "步骤3: 重启Docker容器"
separator
cd "$PROJECT_DIR"
echo ""
log_info "停止Docker容器..."
docker-compose -f "$COMPOSE_FILE" stop
echo ""
log_info "启动Docker容器..."
docker-compose -f "$COMPOSE_FILE" up -d
echo ""
log_info "等待服务启动(10秒)..."
sleep 10
echo ""
log_info "检查容器状态:"
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
# 检查容器是否正常运行
RUNNING_CONTAINERS=$(docker ps -q | wc -l)
STOPPED_CONTAINERS=$(docker ps -f "status=exited" -q | wc -l)
if [ "$STOPPED_CONTAINERS" -gt 0 ]; then
log_warning "发现已停止的容器:"
docker ps -f "status=exited" --format "table {{.Names}}\t{{.Status}}"
echo ""
log_info "查看容器日志:"
for container in $(docker ps -f "status=exited" -q); do
CONTAINER_NAME=$(docker inspect --format='{{.Name}}' $container | sed 's/\///')
echo ""
log_info "容器: $CONTAINER_NAME"
docker logs --tail 30 $container
done
else
log_success "所有容器运行正常"
fi
# 4. 重启Nginx
log_info "步骤4: 重启Nginx服务"
separator
echo ""
log_info "测试Nginx配置..."
if nginx -t; then
log_success "Nginx配置正确"
else
log_error "Nginx配置错误,请检查配置文件"
exit 1
fi
echo ""
log_info "重启Nginx..."
systemctl restart nginx
echo ""
log_info "检查Nginx状态:"
systemctl status nginx --no-pager | head -15
if systemctl is-active --quiet nginx; then
log_success "Nginx运行正常"
else
log_error "Nginx启动失败"
journalctl -u nginx --no-pager | tail -20
exit 1
fi
# 5. 验证服务
log_info "步骤5: 验证服务状态"
separator
echo ""
log_info "测试本地应用连接..."
if curl -I --connect-timeout 5 http://localhost:3000 2>&1 | grep -q "HTTP"; then
log_success "应用服务响应正常"
curl -I http://localhost:3000 2>&1 | head -10
else
log_error "应用服务无响应"
log_info "查看应用日志:"
docker logs --tail 50 $(docker ps -q | head -1)
fi
echo ""
log_info "检查端口监听:"
netstat -tlnp | grep -E ":(3000|80|443)" || ss -tlnp | grep -E ":(3000|80|443)"
echo ""
log_info "测试外部访问..."
if curl -I --connect-timeout 10 https://novalon.cn 2>&1 | grep -q "HTTP"; then
log_success "外部访问正常"
curl -I https://novalon.cn 2>&1 | head -10
else
log_warning "外部访问仍可能需要等待DNS传播或CDN刷新"
fi
# 6. 显示最终状态
separator
log_info "服务重启完成"
separator
echo ""
log_success "执行摘要:"
echo " ✅ Docker容器已重启"
echo " ✅ Nginx服务已重启"
echo " ✅ 服务状态已验证"
echo ""
log_info "详细日志已保存到: $LOG_FILE"
echo ""
log_warning "后续建议:"
echo " 1. 监控服务状态: watch -n 5 'docker ps && systemctl status nginx'"
echo " 2. 查看实时日志: docker logs -f \$(docker ps -q | head -1)"
echo " 3. 检查应用日志: tail -f /var/log/nginx/error.log"
echo " 4. 验证外部访问: curl -I https://novalon.cn"
echo ""
log_info "如果问题仍然存在,请运行完整诊断:"
echo " /tmp/remote-server-diagnosis.sh --full"
separator
+298
View File
@@ -0,0 +1,298 @@
#!/bin/bash
# 网络诊断脚本
# 用途:诊断git和CI无法访问的问题
set -e
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
# 日志函数
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
log_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
separator() {
echo "======================================================================"
}
# 测试网络连通性
test_connectivity() {
separator
log_info "测试网络连通性"
separator
echo ""
log_info "测试DNS解析..."
# 测试Git服务器
log_info "Git服务器 (git.f.novalon.cn):"
if nslookup git.f.novalon.cn > /dev/null 2>&1; then
log_success "DNS解析成功"
nslookup git.f.novalon.cn | grep "Address" | tail -n +2
else
log_error "DNS解析失败"
fi
# 测试CI服务器
log_info "CI服务器 (ci.f.novalon.cn):"
if nslookup ci.f.novalon.cn > /dev/null 2>&1; then
log_success "DNS解析成功"
nslookup ci.f.novalon.cn | grep "Address" | tail -n +2
else
log_error "DNS解析失败"
fi
echo ""
log_info "测试网络连接..."
# 测试HTTPS连接
log_info "Git服务器HTTPS连接:"
if curl -I --connect-timeout 5 https://git.f.novalon.cn > /dev/null 2>&1; then
log_success "HTTPS连接成功"
else
log_error "HTTPS连接失败"
fi
log_info "CI服务器HTTPS连接:"
if curl -I --connect-timeout 5 https://ci.f.novalon.cn > /dev/null 2>&1; then
log_success "HTTPS连接成功"
else
log_error "HTTPS连接失败"
fi
}
# 测试Git连接
test_git_connection() {
separator
log_info "测试Git连接"
separator
echo ""
log_info "Git配置:"
git config --global --list | grep -E "user|http|https" || log_warning "未找到Git全局配置"
echo ""
log_info "测试Git SSH连接:"
if ssh -T git@git.f.novalon.cn -o ConnectTimeout=5 2>&1 | grep -q "welcome\|authenticated"; then
log_success "SSH连接成功"
else
log_warning "SSH连接失败(可能未配置SSH密钥)"
fi
echo ""
log_info "测试Git HTTPS连接:"
if git ls-remote https://git.f.novalon.cn/novalon/novalon-website.git > /dev/null 2>&1; then
log_success "HTTPS连接成功"
else
log_error "HTTPS连接失败"
log_info "错误详情:"
git ls-remote https://git.f.novalon.cn/novalon/novalon-website.git 2>&1 | head -10
fi
}
# 测试CI连接
test_ci_connection() {
separator
log_info "测试CI连接"
separator
echo ""
log_info "测试CI Web界面:"
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --connect-timeout 5 https://ci.f.novalon.cn)
if [ "$HTTP_CODE" = "200" ] || [ "$HTTP_CODE" = "302" ]; then
log_success "CI Web界面可访问 (HTTP $HTTP_CODE)"
else
log_error "CI Web界面不可访问 (HTTP $HTTP_CODE)"
fi
echo ""
log_info "测试CI API:"
if curl -s --connect-timeout 5 https://ci.f.novalon.cn/api/info > /dev/null 2>&1; then
log_success "CI API可访问"
else
log_warning "CI API不可访问"
fi
}
# 检查防火墙和代理
check_network_config() {
separator
log_info "检查网络配置"
separator
echo ""
log_info "检查防火墙状态:"
if command -v ufw > /dev/null; then
sudo ufw status
elif command -v firewall-cmd > /dev/null; then
sudo firewall-cmd --state
else
log_warning "未检测到防火墙"
fi
echo ""
log_info "检查HTTP代理设置:"
env | grep -i proxy || log_info "未设置代理"
echo ""
log_info "检查SSL证书:"
if echo | openssl s_client -connect git.f.novalon.cn:443 2>&1 | grep -q "Verify return code: 0"; then
log_success "Git服务器SSL证书有效"
else
log_warning "Git服务器SSL证书可能有问题"
fi
if echo | openssl s_client -connect ci.f.novalon.cn:443 2>&1 | grep -q "Verify return code: 0"; then
log_success "CI服务器SSL证书有效"
else
log_warning "CI服务器SSL证书可能有问题"
fi
}
# 提供解决方案
suggest_solutions() {
separator
log_info "建议解决方案"
separator
echo ""
log_warning "如果DNS解析失败:"
echo " 1. 检查DNS服务器配置: cat /etc/resolv.conf"
echo " 2. 尝试使用公共DNS: sudo echo 'nameserver 8.8.8.8' >> /etc/resolv.conf"
echo " 3. 检查网络连接: ping 8.8.8.8"
echo ""
log_warning "如果HTTPS连接失败:"
echo " 1. 检查防火墙规则: sudo ufw status"
echo " 2. 检查代理设置: env | grep -i proxy"
echo " 3. 更新CA证书: sudo update-ca-certificates"
echo ""
log_warning "如果Git连接超时:"
echo " 1. 增加超时时间: git config --global http.lowSpeedLimit 0"
echo " 2. 增加超时时间: git config --global http.postBuffer 524288000"
echo " 3. 使用SSH代替HTTPS: git remote set-url origin git@git.f.novalon.cn:novalon/novalon-website.git"
echo ""
log_warning "如果CI无法访问:"
echo " 1. 检查CI服务状态: sudo systemctl status woodpecker-agent"
echo " 2. 检查CI日志: sudo journalctl -u woodpecker-agent -f"
echo " 3. 重启CI服务: sudo systemctl restart woodpecker-agent"
}
# 快速诊断
quick_diagnosis() {
log_info "执行快速诊断..."
# 测试基本网络连接
if ping -c 1 8.8.8.8 > /dev/null 2>&1; then
log_success "基本网络连接正常"
else
log_error "基本网络连接失败,请检查网络配置"
return
fi
# 测试DNS
if nslookup git.f.novalon.cn > /dev/null 2>&1; then
log_success "DNS解析正常"
else
log_error "DNS解析失败"
fi
# 测试HTTPS
if curl -I --connect-timeout 5 https://git.f.novalon.cn > /dev/null 2>&1; then
log_success "Git服务器可访问"
else
log_error "Git服务器不可访问"
fi
if curl -I --connect-timeout 5 https://ci.f.novalon.cn > /dev/null 2>&1; then
log_success "CI服务器可访问"
else
log_error "CI服务器不可访问"
fi
}
# 主菜单
main_menu() {
clear
separator
echo "网络诊断工具 - Git和CI连接问题排查"
separator
echo ""
echo "1. 快速诊断"
echo "2. 测试网络连通性"
echo "3. 测试Git连接"
echo "4. 测试CI连接"
echo "5. 检查网络配置"
echo "6. 显示建议解决方案"
echo "7. 完整诊断(所有测试)"
echo "0. 退出"
echo ""
read -p "请选择操作 (0-7): " choice
case $choice in
1) quick_diagnosis ;;
2) test_connectivity ;;
3) test_git_connection ;;
4) test_ci_connection ;;
5) check_network_config ;;
6) suggest_solutions ;;
7)
quick_diagnosis
echo ""
test_connectivity
echo ""
test_git_connection
echo ""
test_ci_connection
echo ""
check_network_config
echo ""
suggest_solutions
;;
0)
log_info "退出程序"
exit 0
;;
*)
log_error "无效选择"
;;
esac
echo ""
read -p "按回车键继续..."
main_menu
}
# 主函数
main() {
log_info "网络诊断工具启动"
if [ "$1" = "--quick" ]; then
quick_diagnosis
else
main_menu
fi
}
main "$@"
+283
View File
@@ -0,0 +1,283 @@
#!/bin/bash
# 生产环境连接超时诊断脚本
# 用途:系统化排查生产环境连接超时问题
set -e
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
# 日志函数
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
log_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
separator() {
echo "======================================================================"
}
# 1. 收集错误信息
collect_error_info() {
separator
log_info "Phase 1.1: 收集错误信息"
separator
echo ""
log_info "测试基本网络连接..."
# 测试本地网络
if ping -c 3 8.8.8.8 > /dev/null 2>&1; then
log_success "本地网络正常"
else
log_error "本地网络异常"
return 1
fi
echo ""
log_info "测试DNS解析..."
# 测试生产服务器域名
if nslookup novalon.cn > /dev/null 2>&1; then
log_success "novalon.cn DNS解析成功"
PROD_IP=$(nslookup novalon.cn | grep "Address" | tail -1 | awk '{print $2}')
log_info "生产服务器IP: $PROD_IP"
else
log_error "novalon.cn DNS解析失败"
fi
# 测试Git服务器
if nslookup git.f.novalon.cn > /dev/null 2>&1; then
log_success "git.f.novalon.cn DNS解析成功"
GIT_IP=$(nslookup git.f.novalon.cn | grep "Address" | tail -1 | awk '{print $2}')
log_info "Git服务器IP: $GIT_IP"
else
log_error "git.f.novalon.cn DNS解析失败"
fi
# 测试CI服务器
if nslookup ci.f.novalon.cn > /dev/null 2>&1; then
log_success "ci.f.novalon.cn DNS解析成功"
CI_IP=$(nslookup ci.f.novalon.cn | grep "Address" | tail -1 | awk '{print $2}')
log_info "CI服务器IP: $CI_IP"
else
log_error "ci.f.novalon.cn DNS解析失败"
fi
echo ""
log_info "测试TCP连接..."
# 测试HTTP端口
if nc -zv -w 5 novalon.cn 80 2>&1 | grep -q "succeeded"; then
log_success "novalon.cn:80 连接成功"
else
log_error "novalon.cn:80 连接超时"
fi
# 测试HTTPS端口
if nc -zv -w 5 novalon.cn 443 2>&1 | grep -q "succeeded"; then
log_success "novalon.cn:443 连接成功"
else
log_error "novalon.cn:443 连接超时"
fi
# 测试Git服务器
if nc -zv -w 5 git.f.novalon.cn 443 2>&1 | grep -q "succeeded"; then
log_success "git.f.novalon.cn:443 连接成功"
else
log_error "git.f.novalon.cn:443 连接超时"
fi
# 测试CI服务器
if nc -zv -w 5 ci.f.novalon.cn 443 2>&1 | grep -q "succeeded"; then
log_success "ci.f.novalon.cn:443 连接成功"
else
log_error "ci.f.novalon.cn:443 连接超时"
fi
echo ""
log_info "测试HTTP响应..."
# 测试HTTP请求
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --connect-timeout 10 --max-time 15 https://novalon.cn 2>&1)
if [ "$HTTP_CODE" = "200" ] || [ "$HTTP_CODE" = "301" ] || [ "$HTTP_CODE" = "302" ]; then
log_success "novalon.cn HTTP响应正常 (HTTP $HTTP_CODE)"
else
log_error "novalon.cn HTTP响应异常 (HTTP $HTTP_CODE)"
fi
# 测试Git服务器
if curl -I --connect-timeout 10 --max-time 15 https://git.f.novalon.cn 2>&1 | grep -q "HTTP"; then
log_success "git.f.novalon.cn HTTP响应正常"
else
log_error "git.f.novalon.cn HTTP响应超时"
fi
# 测试CI服务器
if curl -I --connect-timeout 10 --max-time 15 https://ci.f.novalon.cn 2>&1 | grep -q "HTTP"; then
log_success "ci.f.novalon.cn HTTP响应正常"
else
log_error "ci.f.novalon.cn HTTP响应超时"
fi
}
# 2. 检查最近的变更
check_recent_changes() {
separator
log_info "Phase 1.2: 检查最近的变更"
separator
echo ""
log_info "检查本地Git状态..."
# 检查当前分支
CURRENT_BRANCH=$(git branch --show-current)
log_info "当前分支: $CURRENT_BRANCH"
# 检查未提交的更改
if git status --porcelain | grep -q .; then
log_warning "存在未提交的更改:"
git status --short
else
log_success "工作区干净"
fi
# 检查最近的提交
echo ""
log_info "最近5次提交:"
git log --oneline -5
# 检查最近的推送
echo ""
log_info "检查远程仓库连接..."
if git remote -v | grep -q "git.f.novalon.cn"; then
log_info "远程仓库: git.f.novalon.cn"
# 尝试连接远程仓库
if git ls-remote --heads origin > /dev/null 2>&1; then
log_success "远程仓库连接成功"
else
log_error "远程仓库连接失败"
fi
fi
}
# 3. 追踪数据流
trace_data_flow() {
separator
log_info "Phase 1.3: 追踪数据流"
separator
echo ""
log_info "网络路由追踪..."
# 追踪到生产服务器的路由
log_info "追踪到 novalon.cn 的路由:"
traceroute -m 15 novalon.cn 2>&1 | head -20 || log_warning "traceroute命令不可用"
echo ""
log_info "追踪到 git.f.novalon.cn 的路由:"
traceroute -m 15 git.f.novalon.cn 2>&1 | head -20 || log_warning "traceroute命令不可用"
echo ""
log_info "检查防火墙规则..."
# 检查防火墙状态
if command -v ufw > /dev/null; then
sudo ufw status verbose
elif command -v firewall-cmd > /dev/null; then
sudo firewall-cmd --list-all
else
log_warning "未检测到防火墙"
fi
echo ""
log_info "检查网络代理设置..."
env | grep -i proxy || log_info "未设置网络代理"
}
# 4. 分析系统资源
analyze_system_resources() {
separator
log_info "Phase 1.4: 分析系统资源"
separator
echo ""
log_info "检查DNS配置..."
cat /etc/resolv.conf
echo ""
log_info "检查网络接口..."
ifconfig | grep -E "^[a-z]|inet " || ip addr show
echo ""
log_info "检查系统负载..."
uptime
echo ""
log_info "检查磁盘空间..."
df -h | grep -E "Filesystem|/$|/home"
}
# 生成诊断报告
generate_report() {
separator
log_info "生成诊断报告"
separator
REPORT_FILE="/tmp/production-diagnosis-$(date +%Y%m%d_%H%M%S).log"
{
echo "生产环境连接超时诊断报告"
echo "生成时间: $(date)"
echo ""
collect_error_info
echo ""
check_recent_changes
echo ""
trace_data_flow
echo ""
analyze_system_resources
} > "$REPORT_FILE" 2>&1
log_success "诊断报告已生成: $REPORT_FILE"
echo ""
log_info "报告摘要:"
head -50 "$REPORT_FILE"
}
# 主函数
main() {
log_info "开始生产环境连接超时诊断"
if [ "$1" = "--report" ]; then
generate_report
else
collect_error_info
echo ""
check_recent_changes
echo ""
trace_data_flow
echo ""
analyze_system_resources
fi
}
main "$@"
+232
View File
@@ -0,0 +1,232 @@
#!/bin/bash
# 生产环境Docker镜像瘦身脚本
# 用途:安全清理未使用的Docker镜像
set -e
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
# 日志函数
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
log_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
separator() {
echo "======================================================================"
}
# 记录日志
LOG_FILE="/tmp/docker-cleanup-$(date +%Y%m%d_%H%M%S).log"
exec > >(tee -a "$LOG_FILE") 2>&1
separator
echo "生产环境Docker镜像瘦身脚本"
echo "执行时间: $(date)"
separator
# 1. 显示当前状态
log_info "步骤1: 当前Docker资源使用情况"
separator
echo ""
docker system df
echo ""
log_info "当前镜像列表:"
docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}\t{{.CreatedSince}}"
echo ""
log_info "运行中的容器:"
docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Status}}"
# 2. 分析未使用的镜像
separator
log_info "步骤2: 分析未使用的镜像"
separator
echo ""
log_info "悬空镜像(<none>标签):"
DANGLING_IMAGES=$(docker images -f "dangling=true" -q)
if [ -n "$DANGLING_IMAGES" ]; then
docker images -f "dangling=true" --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}\t{{.CreatedSince}}"
DANGLING_SIZE=$(docker images -f "dangling=true" --format "{{.Size}}" | grep -oE '[0-9.]+[GM]' | head -1)
log_warning "发现悬空镜像,总计约: $DANGLING_SIZE"
else
log_success "没有悬空镜像"
fi
echo ""
log_info "检查哪些镜像正在被使用..."
RUNNING_IMAGES=$(docker ps --format "{{.Image}}" | sort -u)
log_info "正在使用的镜像:"
echo "$RUNNING_IMAGES"
echo ""
log_info "未被容器使用的镜像:"
docker images --format "{{.Repository}}:{{.Tag}}" | while read image; do
if ! echo "$RUNNING_IMAGES" | grep -q "$(echo $image | cut -d: -f1)"; then
echo "$image"
fi
done
# 3. 清理悬空镜像(安全)
separator
log_info "步骤3: 清理悬空镜像"
separator
if [ -n "$DANGLING_IMAGES" ]; then
log_warning "将删除以下悬空镜像:"
docker images -f "dangling=true" --format "{{.ID}}\t{{.Size}}"
echo ""
read -p "确认删除悬空镜像?(y/n): " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
log_info "正在清理悬空镜像..."
docker image prune -f
log_success "悬空镜像清理完成"
else
log_info "跳过悬空镜像清理"
fi
else
log_success "没有悬空镜像需要清理"
fi
# 4. 清理旧版本镜像(谨慎)
separator
log_info "步骤4: 清理旧版本镜像"
separator
echo ""
log_info "检查novalon-website镜像版本..."
NOVALON_IMAGES=$(docker images "novalon-website" --format "{{.Tag}}\t{{.Size}}\t{{.CreatedSince}}")
echo "$NOVALON_IMAGES"
echo ""
log_info "当前使用的版本:"
docker ps --filter "name=novalon-website" --format "{{.Image}}"
# 检查是否有旧版本
OLD_VERSIONS=$(docker images "novalon-website" --format "{{.Tag}}" | grep -v "latest" | head -n -1)
if [ -n "$OLD_VERSIONS" ]; then
log_warning "发现旧版本镜像:"
echo "$OLD_VERSIONS"
echo ""
read -p "是否删除旧版本镜像?(y/n): " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
for version in $OLD_VERSIONS; do
log_info "删除镜像: novalon-website:$version"
docker rmi "novalon-website:$version" || log_warning "无法删除 novalon-website:$version(可能正在使用)"
done
log_success "旧版本镜像清理完成"
else
log_info "跳过旧版本镜像清理"
fi
else
log_success "没有旧版本镜像需要清理"
fi
# 5. 清理构建缓存
separator
log_info "步骤5: 清理构建缓存"
separator
CACHE_SIZE=$(docker system df | grep "Build Cache" | awk '{print $3}')
log_info "构建缓存大小: $CACHE_SIZE"
if [ "$CACHE_SIZE" != "0B" ]; then
read -p "是否清理构建缓存?(y/n): " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
log_info "正在清理构建缓存..."
docker builder prune -a -f
log_success "构建缓存清理完成"
else
log_info "跳过构建缓存清理"
fi
else
log_success "没有构建缓存需要清理"
fi
# 6. 清理未使用的卷(可选)
separator
log_info "步骤6: 清理未使用的卷(可选)"
separator
UNUSED_VOLUMES=$(docker volume ls -q --filter "dangling=true")
if [ -n "$UNUSED_VOLUMES" ]; then
log_warning "发现未使用的卷:"
echo "$UNUSED_VOLUMES"
read -p "是否清理未使用的卷?(y/n): " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
log_info "正在清理未使用的卷..."
docker volume prune -f
log_success "未使用卷清理完成"
else
log_info "跳过未使用卷清理"
fi
else
log_success "没有未使用的卷"
fi
# 7. 显示清理结果
separator
log_info "步骤7: 清理结果"
separator
echo ""
log_info "清理后的Docker资源使用情况:"
docker system df
echo ""
log_info "磁盘空间变化:"
df -h | grep -E "Filesystem|/$"
separator
log_success "Docker镜像瘦身完成"
separator
echo ""
log_info "清理摘要:"
echo " ✅ 悬空镜像已清理"
echo " ✅ 旧版本镜像已清理"
echo " ✅ 构建缓存已清理"
echo " ✅ 未使用卷已清理"
echo ""
log_info "详细日志已保存到: $LOG_FILE"
echo ""
log_warning "建议后续操作:"
echo " 1. 监控服务状态: docker ps"
echo " 2. 验证应用访问: curl -I https://novalon.cn"
echo " 3. 设置定期清理: crontab -e"
echo " 4. 配置日志轮转: /etc/logrotate.d/docker"
separator
+312
View File
@@ -0,0 +1,312 @@
#!/bin/bash
# 远程服务器诊断脚本
# 用途:在生产服务器上诊断连接超时问题
set -e
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
# 日志函数
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
log_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
separator() {
echo "======================================================================"
}
# 1. 检查系统资源
check_system_resources() {
separator
log_info "检查系统资源"
separator
echo ""
log_info "CPU和内存使用情况:"
top -bn1 | head -20
echo ""
log_info "磁盘使用情况:"
df -h
echo ""
log_info "内存详情:"
free -h
echo ""
log_info "系统负载:"
uptime
# 检查是否资源耗尽
MEMORY_USAGE=$(free | grep Mem | awk '{print ($3/$2) * 100.0}')
DISK_USAGE=$(df -h / | tail -1 | awk '{print $5}' | sed 's/%//')
if (( $(echo "$MEMORY_USAGE > 90" | bc -l) )); then
log_error "内存使用率过高: ${MEMORY_USAGE}%"
else
log_success "内存使用正常: ${MEMORY_USAGE}%"
fi
if [ "$DISK_USAGE" -gt 90 ]; then
log_error "磁盘使用率过高: ${DISK_USAGE}%"
else
log_success "磁盘使用正常: ${DISK_USAGE}%"
fi
}
# 2. 检查Docker容器
check_docker() {
separator
log_info "检查Docker容器"
separator
echo ""
log_info "Docker服务状态:"
systemctl status docker --no-pager | head -20
echo ""
log_info "运行中的容器:"
docker ps -a --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
echo ""
log_info "容器资源使用:"
docker stats --no-stream
# 检查容器健康状态
RUNNING_CONTAINERS=$(docker ps -q | wc -l)
STOPPED_CONTAINERS=$(docker ps -f "status=exited" -q | wc -l)
log_info "运行中容器: $RUNNING_CONTAINERS"
log_info "已停止容器: $STOPPED_CONTAINERS"
if [ "$STOPPED_CONTAINERS" -gt 0 ]; then
log_warning "发现已停止的容器:"
docker ps -f "status=exited" --format "table {{.Names}}\t{{.Status}}"
fi
# 检查容器日志
echo ""
log_info "检查容器日志(最近50行):"
for container in $(docker ps -q); do
CONTAINER_NAME=$(docker inspect --format='{{.Name}}' $container | sed 's/\///')
echo ""
log_info "容器: $CONTAINER_NAME"
docker logs --tail 50 $container 2>&1 | tail -20
done
}
# 3. 检查Nginx
check_nginx() {
separator
log_info "检查Nginx"
separator
echo ""
log_info "Nginx服务状态:"
systemctl status nginx --no-pager | head -20
echo ""
log_info "Nginx进程:"
ps aux | grep nginx | grep -v grep
echo ""
log_info "Nginx监听端口:"
netstat -tlnp | grep nginx || ss -tlnp | grep nginx
echo ""
log_info "Nginx配置测试:"
nginx -t
echo ""
log_info "Nginx错误日志(最近50行):"
tail -50 /var/log/nginx/error.log 2>/dev/null || log_warning "未找到Nginx错误日志"
echo ""
log_info "Nginx访问日志(最近20行):"
tail -20 /var/log/nginx/access.log 2>/dev/null || log_warning "未找到Nginx访问日志"
}
# 4. 检查应用服务
check_application() {
separator
log_info "检查应用服务"
separator
echo ""
log_info "Node.js进程:"
ps aux | grep node | grep -v grep
echo ""
log_info "PM2进程(如果使用):"
if command -v pm2 > /dev/null; then
pm2 list
pm2 logs --lines 20 --nostream
else
log_info "未使用PM2"
fi
echo ""
log_info "检查端口占用:"
netstat -tlnp | grep -E ":(3000|80|443)" || ss -tlnp | grep -E ":(3000|80|443)"
echo ""
log_info "测试本地应用连接:"
if curl -I --connect-timeout 5 http://localhost:3000 2>&1 | grep -q "HTTP"; then
log_success "应用服务响应正常"
else
log_error "应用服务无响应"
fi
}
# 5. 检查防火墙和网络
check_network() {
separator
log_info "检查防火墙和网络"
separator
echo ""
log_info "防火墙状态:"
if command -v ufw > /dev/null; then
ufw status verbose
elif command -v firewall-cmd > /dev/null; then
firewall-cmd --list-all
else
log_info "未检测到防火墙"
fi
echo ""
log_info "网络连接状态:"
netstat -an | grep -E ":(80|443|3000)" | head -20
echo ""
log_info "系统日志(最近错误):"
journalctl -xe --no-pager | tail -50
}
# 6. 快速修复建议
suggest_fixes() {
separator
log_info "快速修复建议"
separator
echo ""
log_warning "根据诊断结果,建议执行以下操作:"
echo ""
echo "1. 如果Docker容器停止:"
echo " docker-compose -f /path/to/docker-compose.prod.yml up -d"
echo " docker-compose -f /path/to/docker-compose.prod.yml restart"
echo ""
echo "2. 如果Nginx异常:"
echo " sudo systemctl restart nginx"
echo " sudo nginx -t # 测试配置"
echo ""
echo "3. 如果应用服务异常:"
echo " docker logs <container-name> # 查看日志"
echo " docker restart <container-name> # 重启容器"
echo ""
echo "4. 如果资源耗尽:"
echo " # 清理Docker镜像"
echo " docker system prune -a -f"
echo " # 清理日志"
echo " sudo journalctl --vacuum-time=3d"
echo " # 重启服务"
echo " sudo systemctl restart docker"
echo ""
echo "5. 查看详细日志:"
echo " docker logs -f <container-name> # 实时查看容器日志"
echo " tail -f /var/log/nginx/error.log # 实时查看Nginx错误日志"
echo " journalctl -u docker -f # 实时查看Docker服务日志"
}
# 一键诊断
full_diagnosis() {
log_info "开始完整诊断..."
check_system_resources
echo ""
check_docker
echo ""
check_nginx
echo ""
check_application
echo ""
check_network
echo ""
suggest_fixes
}
# 主菜单
main_menu() {
clear
separator
echo "远程服务器诊断工具"
separator
echo ""
echo "1. 检查系统资源"
echo "2. 检查Docker容器"
echo "3. 检查Nginx"
echo "4. 检查应用服务"
echo "5. 检查防火墙和网络"
echo "6. 显示修复建议"
echo "7. 完整诊断"
echo "0. 退出"
echo ""
read -p "请选择操作 (0-7): " choice
case $choice in
1) check_system_resources ;;
2) check_docker ;;
3) check_nginx ;;
4) check_application ;;
5) check_network ;;
6) suggest_fixes ;;
7) full_diagnosis ;;
0)
log_info "退出程序"
exit 0
;;
*)
log_error "无效选择"
;;
esac
echo ""
read -p "按回车键继续..."
main_menu
}
# 主函数
main() {
log_info "远程服务器诊断工具启动"
if [ "$1" = "--full" ]; then
full_diagnosis
else
main_menu
fi
}
main "$@"