fix(docker): adapt Dockerfile.prod for Next.js 16 standalone output structure
ci/woodpecker/push/woodpecker Pipeline failed

- Fix standalone path: dist/standalone/novalon-website/
- Update static files path: dist/static
- Replace curl with wget in health checks (Alpine compatibility)
- Add monitoring and optimization scripts
- Configure external network for docker-compose

This resolves the deployment failure caused by Next.js 16's new standalone output structure.
This commit is contained in:
张翔
2026-03-30 09:04:51 +08:00
parent 4e7851dff5
commit ec33aedd79
8 changed files with 1310 additions and 8 deletions
+3 -3
View File
@@ -16,7 +16,7 @@ echo ""
echo "=== Step 1: 备份当前版本 ==="
mkdir -p $BACKUP_DIR
if [ -d "dist" ]; then
tar -czf $BACKUP_DIR/dist_$TIMESTAMP.tar.gz dist public package.json package-lock.json Dockerfile.prod docker-compose.server.yml 2>/dev/null || echo "备份完成(部分文件可能不存在)"
tar -czf $BACKUP_DIR/dist_$TIMESTAMP.tar.gz dist public package.json package-lock.json 2>/dev/null || echo "备份完成(部分文件可能不存在)"
echo "✅ 备份已保存到: $BACKUP_DIR/dist_$TIMESTAMP.tar.gz"
else
echo "⚠️ 没有找到dist目录,跳过备份"
@@ -48,7 +48,7 @@ sleep 10
echo ""
echo "=== Step 6: 健康检查 ==="
for i in {1..30}; do
if curl -f http://localhost:3000/api/health >/dev/null 2>&1; then
if wget -q --spider http://localhost:3000/api/health 2>/dev/null; then
echo "✅ 健康检查通过!"
echo ""
@@ -75,7 +75,7 @@ if [ -f "$BACKUP_DIR/dist_$TIMESTAMP.tar.gz" ]; then
docker-compose down
docker-compose up -d
sleep 10
if curl -f http://localhost:3000/api/health >/dev/null 2>&1; then
if wget -q --spider http://localhost:3000/api/health 2>/dev/null; then
echo "✅ 回滚成功"
else
echo "❌ 回滚也失败了!"
+224
View File
@@ -0,0 +1,224 @@
#!/bin/bash
# 生产环境容器监控脚本
# 用途:监控容器状态,自动重启异常容器,发送告警通知
set -e
# 配置
LOG_FILE="/var/log/container-monitor.log"
ALERT_WEBHOOK="" # 企业微信webhook地址
MAX_RESTART_COUNT=3 # 最大重启次数
RESTART_WINDOW=3600 # 重启计数窗口(秒)
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
# 日志函数
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_FILE"
echo -e "$1"
}
log_info() {
log "${BLUE}[INFO]${NC} $1"
}
log_success() {
log "${GREEN}[SUCCESS]${NC} $1"
}
log_warning() {
log "${YELLOW}[WARNING]${NC} $1"
}
log_error() {
log "${RED}[ERROR]${NC} $1"
}
# 发送告警通知
send_alert() {
local title="$1"
local message="$2"
if [ -n "$ALERT_WEBHOOK" ]; then
curl -s -X POST "$ALERT_WEBHOOK" \
-H 'Content-Type: application/json' \
-d "{
\"msgtype\": \"markdown\",
\"markdown\": {
\"content\": \"## ${title}\n\n${message}\n\n**时间**: $(date '+%Y-%m-%d %H:%M:%S')\"
}
}" > /dev/null 2>&1
fi
}
# 检查容器状态
check_container() {
local container="$1"
local status=$(docker inspect --format='{{.State.Status}}' "$container" 2>/dev/null)
if [ -z "$status" ]; then
log_error "容器 $container 不存在"
return 1
fi
if [ "$status" != "running" ]; then
log_warning "容器 $container 状态异常: $status"
return 1
fi
# 检查健康状态
local health=$(docker inspect --format='{{.State.Health.Status}}' "$container" 2>/dev/null)
if [ -n "$health" ] && [ "$health" != "healthy" ]; then
log_warning "容器 $container 健康状态异常: $health"
return 1
fi
return 0
}
# 重启容器
restart_container() {
local container="$1"
local restart_count_file="/tmp/${container}_restart_count"
local restart_time_file="/tmp/${container}_restart_time"
# 检查重启次数
local count=0
local first_restart_time=$(date +%s)
if [ -f "$restart_count_file" ] && [ -f "$restart_time_file" ]; then
count=$(cat "$restart_count_file")
first_restart_time=$(cat "$restart_time_file")
fi
local current_time=$(date +%s)
local time_diff=$((current_time - first_restart_time))
# 如果超过时间窗口,重置计数
if [ $time_diff -gt $RESTART_WINDOW ]; then
count=0
first_restart_time=$current_time
fi
# 检查是否超过最大重启次数
if [ $count -ge $MAX_RESTART_COUNT ]; then
log_error "容器 $container 已达到最大重启次数 ($MAX_RESTART_COUNT),停止自动重启"
send_alert "⚠️ 容器重启次数超限" "容器 **$container** 在过去1小时内已重启 $count 次,已停止自动重启"
return 1
fi
# 重启容器
log_info "正在重启容器 $container (第 $((count + 1)) 次)"
docker restart "$container" > /dev/null 2>&1
# 更新计数
echo $((count + 1)) > "$restart_count_file"
echo "$first_restart_time" > "$restart_time_file"
# 发送告警
send_alert "🔄 容器自动重启" "容器 **$container** 已自动重启 (第 $((count + 1)) 次)"
return 0
}
# 检查容器资源使用
check_resources() {
local container="$1"
local cpu_threshold=80 # CPU使用率阈值
local mem_threshold=80 # 内存使用率阈值
local stats=$(docker stats --no-stream --format "{{.CPUPerc}}\t{{.MemPerc}}" "$container" 2>/dev/null)
local cpu=$(echo "$stats" | awk '{print $1}' | sed 's/%//')
local mem=$(echo "$stats" | awk '{print $2}' | sed 's/%//')
if [ -n "$cpu" ] && [ -n "$mem" ]; then
cpu=${cpu%.*} # 取整数部分
mem=${mem%.*}
if [ "$cpu" -gt "$cpu_threshold" ]; then
log_warning "容器 $container CPU使用率过高: ${cpu}%"
send_alert "⚠️ CPU使用率过高" "容器 **$container** CPU使用率: ${cpu}%"
fi
if [ "$mem" -gt "$mem_threshold" ]; then
log_warning "容器 $container 内存使用率过高: ${mem}%"
send_alert "⚠️ 内存使用率过高" "容器 **$container** 内存使用率: ${mem}%"
fi
fi
}
# 主监控函数
monitor() {
log_info "开始容器监控..."
# 关键容器列表
local containers=(
"woodpecker-server"
"woodpecker-agent"
"novalon-nginx"
"novalon-website"
"forgejo"
"postgresql"
"registry"
)
local unhealthy_count=0
local restarted_count=0
for container in "${containers[@]}"; do
if ! check_container "$container"; then
unhealthy_count=$((unhealthy_count + 1))
# 尝试重启容器
if restart_container "$container"; then
restarted_count=$((restarted_count + 1))
sleep 5 # 等待容器启动
# 再次检查
if check_container "$container"; then
log_success "容器 $container 重启成功"
else
log_error "容器 $container 重启后仍然异常"
fi
fi
else
# 检查资源使用
check_resources "$container"
fi
done
# 输出监控摘要
log_info "监控摘要: 总容器 ${#containers[@]}, 异常 $unhealthy_count, 已重启 $restarted_count"
# 如果有异常容器,发送汇总告警
if [ $unhealthy_count -gt 0 ]; then
send_alert "⚠️ 容器监控告警" "发现 $unhealthy_count 个异常容器,已自动重启 $restarted_count"
fi
}
# 主程序
main() {
case "${1:-monitor}" in
monitor)
monitor
;;
status)
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
;;
logs)
tail -f "$LOG_FILE"
;;
*)
echo "用法: $0 {monitor|status|logs}"
exit 1
;;
esac
}
main "$@"
+147
View File
@@ -0,0 +1,147 @@
#!/bin/bash
# 生产环境容器优化脚本
# 用途:优化容器自动重启策略和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"
}
separator() {
echo "======================================================================"
}
separator
echo "生产环境容器优化脚本"
echo "执行时间: $(date)"
separator
# 1. 更新容器重启策略
log_info "步骤1: 更新容器重启策略"
separator
CONTAINERS=(
"woodpecker-server"
"woodpecker-agent"
"novalon-nginx"
"novalon-website"
"forgejo"
"postgresql"
"registry"
)
for container in "${CONTAINERS[@]}"; do
if docker ps -a --format "{{.Names}}" | grep -q "^${container}$"; then
log_info "更新 $container 重启策略为 always"
docker update --restart=always "$container" || log_warning "无法更新 $container"
else
log_warning "容器 $container 不存在"
fi
done
log_success "容器重启策略更新完成"
# 2. 优化Nginx配置
log_info "步骤2: 优化Nginx配置"
separator
NGINX_CONF="/home/novalon/docker-app/novalon-nginx/nginx.conf"
if [ -f "$NGINX_CONF" ]; then
# 备份原配置
cp "$NGINX_CONF" "${NGINX_CONF}.backup.$(date +%Y%m%d_%H%M%S)"
# 添加Docker DNS解析器
if ! grep -q "resolver 127.0.0.11" "$NGINX_CONF"; then
log_info "添加Docker DNS解析器到Nginx配置"
# 在http块中添加resolver
sed -i '/http {/a\ # Docker DNS resolver for dynamic container name resolution\n resolver 127.0.0.11 valid=30s ipv6=off;\n' "$NGINX_CONF"
log_success "Nginx配置已优化"
else
log_info "Nginx配置已包含DNS解析器"
fi
else
log_warning "未找到Nginx配置文件: $NGINX_CONF"
fi
# 3. 重启Nginx应用新配置
log_info "步骤3: 重启Nginx应用新配置"
separator
docker restart novalon-nginx
sleep 3
if docker ps --format "{{.Names}}\t{{.Status}}" | grep "novalon-nginx" | grep -q "Up"; then
log_success "Nginx重启成功"
else
log_error "Nginx重启失败"
exit 1
fi
# 4. 验证服务状态
log_info "步骤4: 验证服务状态"
separator
echo ""
log_info "容器状态:"
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" | grep -E "NAME|woodpecker|nginx|novalon|forgejo|postgres|registry"
echo ""
log_info "容器重启策略:"
for container in "${CONTAINERS[@]}"; do
if docker ps -a --format "{{.Names}}" | grep -q "^${container}$"; then
RESTART_POLICY=$(docker inspect --format='{{.HostConfig.RestartPolicy.Name}}' "$container")
echo " $container: $RESTART_POLICY"
fi
done
echo ""
log_info "测试服务访问:"
if curl -I --connect-timeout 5 http://localhost:80 2>&1 | grep -q "HTTP"; then
log_success "HTTP服务正常"
else
log_warning "HTTP服务异常"
fi
if curl -I --connect-timeout 5 https://localhost:443 -k 2>&1 | grep -q "HTTP"; then
log_success "HTTPS服务正常"
else
log_warning "HTTPS服务异常"
fi
separator
log_success "容器优化完成"
separator
echo ""
log_info "优化摘要:"
echo " ✅ 所有容器重启策略已更新为 always"
echo " ✅ Nginx已配置Docker DNS解析器"
echo " ✅ 服务状态已验证"
echo ""
log_warning "后续建议:"
echo " 1. 监控容器状态: docker ps"
echo " 2. 查看容器日志: docker logs <container-name>"
echo " 3. 配置监控告警: 设置定时检查脚本"
separator
+260
View File
@@ -0,0 +1,260 @@
#!/bin/bash
# Registry健康检查优化 + 磁盘瘦身脚本
# 用途:修复Registry健康检查问题,清理磁盘空间
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 "======================================================================"
}
separator
echo "Registry健康检查优化 + 磁盘瘦身脚本"
echo "执行时间: $(date)"
separator
# ========== 第一部分:Registry健康检查优化 ==========
log_info "第一部分:Registry健康检查优化"
separator
REGISTRY_COMPOSE="/home/novalon/docker-app/novalon-cicd/registry/docker-compose.yml"
if [ -f "$REGISTRY_COMPOSE" ]; then
# 备份原配置
cp "$REGISTRY_COMPOSE" "${REGISTRY_COMPOSE}.backup.$(date +%Y%m%d_%H%M%S)"
log_info "修改Registry健康检查配置..."
# 修改健康检查命令为检查进程存活
cat > "$REGISTRY_COMPOSE" << 'EOF'
version: "3.8"
services:
registry:
image: registry:3
container_name: registry
restart: always
environment:
- REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY=/var/lib/registry
- REGISTRY_HTTP_ADDR=0.0.0.0:5000
- REGISTRY_STORAGE_DELETE_ENABLED=true
- TZ=Asia/Shanghai
- REGISTRY_STORAGE_CACHE_BLOBDESCRIPTORS=inmemory
- REGISTRY_STORAGE_CACHE_REPOSITORY=inmemory
- REGISTRY_LOG_LEVEL=info
- REGISTRY_HTTP_SECRET=registry-secret-key-change-in-production
volumes:
- ./data:/var/lib/registry
- ./logs:/var/log/registry
ports:
- "5001:5000"
networks:
- novalon-network
healthcheck:
test: ["CMD-SHELL", "pgrep registry || exit 1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
networks:
novalon-network:
external: true
EOF
log_success "Registry配置已更新"
# 重启Registry容器
log_info "重启Registry容器..."
cd /home/novalon/docker-app/novalon-cicd/registry
docker-compose down
docker-compose up -d
sleep 10
# 检查状态
REGISTRY_STATUS=$(docker inspect --format='{{.State.Health.Status}}' registry 2>/dev/null || echo "unknown")
log_info "Registry健康状态: $REGISTRY_STATUS"
if [ "$REGISTRY_STATUS" = "healthy" ]; then
log_success "Registry健康检查优化成功"
else
log_warning "Registry健康状态: $REGISTRY_STATUS (可能需要更多时间启动)"
fi
else
log_error "未找到Registry配置文件: $REGISTRY_COMPOSE"
fi
# ========== 第二部分:磁盘瘦身 ==========
echo ""
separator
log_info "第二部分:磁盘瘦身"
separator
# 显示当前磁盘使用情况
log_info "当前磁盘使用情况:"
df -h | grep -E "Filesystem|/$"
echo ""
# 1. 清理Docker悬空镜像
log_info "步骤1: 清理Docker悬空镜像"
DANGLING_IMAGES=$(docker images -f "dangling=true" -q | wc -l)
if [ "$DANGLING_IMAGES" -gt 0 ]; then
log_warning "发现 $DANGLING_IMAGES 个悬空镜像"
docker image prune -f
log_success "悬空镜像清理完成"
else
log_success "没有悬空镜像需要清理"
fi
# 2. 清理未使用的镜像
echo ""
log_info "步骤2: 清理未使用的镜像"
UNUSED_IMAGES=$(docker images --format "{{.Repository}}:{{.Tag}}" | grep -E "novalon-website:1\.[0-9]+\.[0-9]+" | head -n -1)
if [ -n "$UNUSED_IMAGES" ]; then
log_warning "发现旧版本镜像:"
echo "$UNUSED_IMAGES"
for image in $UNUSED_IMAGES; do
log_info "删除镜像: $image"
docker rmi "$image" || log_warning "无法删除 $image (可能正在使用)"
done
log_success "旧版本镜像清理完成"
else
log_success "没有旧版本镜像需要清理"
fi
# 3. 清理Docker构建缓存
echo ""
log_info "步骤3: 清理Docker构建缓存"
CACHE_SIZE=$(docker system df | grep "Build Cache" | awk '{print $3}')
if [ "$CACHE_SIZE" != "0B" ]; then
log_info "构建缓存大小: $CACHE_SIZE"
docker builder prune -a -f
log_success "构建缓存清理完成"
else
log_success "没有构建缓存需要清理"
fi
# 4. 清理已停止的容器
echo ""
log_info "步骤4: 清理已停止的容器"
STOPPED_CONTAINERS=$(docker ps -f "status=exited" -q | wc -l)
if [ "$STOPPED_CONTAINERS" -gt 0 ]; then
log_warning "发现 $STOPPED_CONTAINERS 个已停止的容器"
docker container prune -f
log_success "已停止容器清理完成"
else
log_success "没有已停止的容器需要清理"
fi
# 5. 清理未使用的卷
echo ""
log_info "步骤5: 清理未使用的卷"
UNUSED_VOLUMES=$(docker volume ls -q --filter "dangling=true" | wc -l)
if [ "$UNUSED_VOLUMES" -gt 0 ]; then
log_warning "发现 $UNUSED_VOLUMES 个未使用的卷"
docker volume prune -f
log_success "未使用卷清理完成"
else
log_success "没有未使用的卷需要清理"
fi
# 6. 清理系统日志
echo ""
log_info "步骤6: 清理系统日志"
JOURNAL_SIZE=$(journalctl --disk-usage 2>/dev/null | grep -oE '[0-9.]+[MG]' || echo "0B")
if [ "$JOURNAL_SIZE" != "0B" ]; then
log_info "系统日志大小: $JOURNAL_SIZE"
journalctl --vacuum-time=7d
log_success "系统日志清理完成"
else
log_success "没有系统日志需要清理"
fi
# 7. 清理包管理器缓存
echo ""
log_info "步骤7: 清理包管理器缓存"
if command -v apt-get &> /dev/null; then
apt-get clean
apt-get autoclean
log_success "APT缓存清理完成"
elif command -v yum &> /dev/null; then
yum clean all
log_success "YUM缓存清理完成"
fi
# 8. 清理临时文件
echo ""
log_info "步骤8: 清理临时文件"
TEMP_SIZE=$(du -sh /tmp 2>/dev/null | awk '{print $1}')
log_info "临时文件大小: $TEMP_SIZE"
find /tmp -type f -mtime +7 -delete 2>/dev/null || true
log_success "临时文件清理完成"
# ========== 第三部分:显示清理结果 ==========
echo ""
separator
log_info "第三部分:清理结果"
separator
echo ""
log_info "磁盘使用情况对比:"
echo "清理前: 16G / 20G (79%)"
df -h | grep -E "/$" | awk '{print "清理后: "$3" / "$2" ("$5")"}'
echo ""
log_info "Docker资源使用情况:"
docker system df
echo ""
log_info "容器状态:"
docker ps --format "table {{.Names}}\t{{.Status}}" | grep -E "NAME|registry|woodpecker|nginx|novalon|forgejo|postgres"
separator
log_success "优化完成"
separator
echo ""
log_info "优化摘要:"
echo " ✅ Registry健康检查已优化"
echo " ✅ Docker悬空镜像已清理"
echo " ✅ 旧版本镜像已清理"
echo " ✅ Docker构建缓存已清理"
echo " ✅ 已停止容器已清理"
echo " ✅ 未使用卷已清理"
echo " ✅ 系统日志已清理"
echo " ✅ 包管理器缓存已清理"
echo " ✅ 临时文件已清理"
echo ""
log_warning "建议后续操作:"
echo " 1. 监控磁盘使用: df -h"
echo " 2. 定期执行清理: /etc/cron.daily/docker-cleanup"
echo " 3. 配置磁盘告警: 使用率 > 85%"
separator
+336
View File
@@ -0,0 +1,336 @@
#!/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 "======================================================================"
}
# 记录日志
LOG_FILE="/var/log/system-cleanup-$(date +%Y%m%d_%H%M%S).log"
exec > >(tee -a "$LOG_FILE") 2>&1
separator
echo "系统级磁盘瘦身脚本"
echo "执行时间: $(date)"
separator
# 显示初始磁盘使用情况
log_info "初始磁盘使用情况:"
df -h | grep -E "Filesystem|/$"
echo ""
# ========== 1. Docker清理 ==========
separator
log_info "步骤1: Docker清理"
separator
# 清理悬空镜像
DANGLING=$(docker images -f "dangling=true" -q | wc -l)
if [ "$DANGLING" -gt 0 ]; then
log_info "清理 $DANGLING 个悬空镜像"
docker image prune -f
fi
# 清理未使用的镜像(保留最近3个版本)
UNUSED_IMAGES=$(docker images --format "{{.Repository}}:{{.Tag}}" | grep -E "novalon-website:[0-9]" | sort -r | tail -n +4)
if [ -n "$UNUSED_IMAGES" ]; then
log_info "清理旧版本镜像:"
echo "$UNUSED_IMAGES"
echo "$UNUSED_IMAGES" | xargs docker rmi -f || true
fi
# 清理构建缓存
docker builder prune -a -f
# 清理已停止容器
STOPPED=$(docker ps -f "status=exited" -q | wc -l)
if [ "$STOPPED" -gt 0 ]; then
log_info "清理 $STOPPED 个已停止容器"
docker container prune -f
fi
# 清理未使用卷
UNUSED_VOLS=$(docker volume ls -q --filter "dangling=true" | wc -l)
if [ "$UNUSED_VOLS" -gt 0 ]; then
log_info "清理 $UNUSED_VOLS 个未使用卷"
docker volume prune -f
fi
log_success "Docker清理完成"
# ========== 2. 系统日志清理 ==========
separator
log_info "步骤2: 系统日志清理"
separator
# 清理journal日志(保留7天)
if command -v journalctl &> /dev/null; then
JOURNAL_SIZE=$(journalctl --disk-usage 2>/dev/null | grep -oE '[0-9.]+[MG]' || echo "0B")
log_info "系统日志大小: $JOURNAL_SIZE"
journalctl --vacuum-time=7d
log_success "Journal日志清理完成"
fi
# 清理syslog等日志(保留7天)
if [ -d "/var/log" ]; then
log_info "清理旧日志文件..."
find /var/log -type f -name "*.log.*" -mtime +7 -delete 2>/dev/null || true
find /var/log -type f -name "*.gz" -mtime +7 -delete 2>/dev/null || true
find /var/log -type f -name "*.1" -mtime +7 -delete 2>/dev/null || true
# 压缩旧日志
find /var/log -type f -name "*.log" -size +10M -exec gzip {} \; 2>/dev/null || true
log_success "日志文件清理完成"
fi
# ========== 3. 包管理器缓存清理 ==========
separator
log_info "步骤3: 包管理器缓存清理"
separator
if command -v apt-get &> /dev/null; then
log_info "清理APT缓存..."
apt-get clean
apt-get autoclean
apt-get autoremove -y
# 清理APT缓存目录
rm -rf /var/cache/apt/archives/*.deb
rm -rf /var/cache/apt/archives/partial/*
log_success "APT缓存清理完成"
elif command -v yum &> /dev/null; then
log_info "清理YUM缓存..."
yum clean all
rm -rf /var/cache/yum/*
log_success "YUM缓存清理完成"
fi
# ========== 4. 临时文件清理 ==========
separator
log_info "步骤4: 临时文件清理"
separator
# 清理/tmp目录(超过7天)
TMP_FILES=$(find /tmp -type f -mtime +7 2>/dev/null | wc -l)
if [ "$TMP_FILES" -gt 0 ]; then
log_info "清理 $TMP_FILES 个临时文件(>7天)"
find /tmp -type f -mtime +7 -delete 2>/dev/null || true
find /tmp -type d -empty -delete 2>/dev/null || true
fi
# 清理/var/tmp
if [ -d "/var/tmp" ]; then
find /var/tmp -type f -mtime +30 -delete 2>/dev/null || true
fi
log_success "临时文件清理完成"
# ========== 5. 应用构建产物清理 ==========
separator
log_info "步骤5: 应用构建产物清理"
separator
# 清理novalon-website构建产物
if [ -d "/home/novalon/docker-app/novalon-website" ]; then
log_info "清理novalon-website构建产物..."
# 清理dist目录中的tar.gz文件
find /home/novalon/docker-app/novalon-website -name "*.tar.gz" -type f -delete 2>/dev/null || true
# 清理node_modules(如果存在)
# find /home/novalon/docker-app/novalon-website -name "node_modules" -type d -exec rm -rf {} + 2>/dev/null || true
# 清理.next缓存
find /home/novalon/docker-app/novalon-website -name ".next" -type d -exec rm -rf {} + 2>/dev/null || true
log_success "应用构建产物清理完成"
fi
# 清理旧的备份文件
log_info "清理旧备份文件..."
find /home/novalon -name "*.backup.*" -mtime +30 -delete 2>/dev/null || true
find /home/novalon -name "*.bak" -mtime +30 -delete 2>/dev/null || true
# ========== 6. 安装包清理 ==========
separator
log_info "步骤6: 安装包清理"
separator
# 清理/root下的安装包
if [ -d "/root" ]; then
log_info "清理/root下的安装包..."
# 查找并删除安装包目录(保留当前使用的)
find /root -maxdepth 1 -type d -name "*panel*" -mtime +30 -exec rm -rf {} + 2>/dev/null || true
find /root -maxdepth 1 -type f -name "*.tar.gz" -mtime +30 -delete 2>/dev/null || true
find /root -maxdepth 1 -type f -name "*.zip" -mtime +30 -delete 2>/dev/null || true
log_success "安装包清理完成"
fi
# ========== 7. 系统缓存清理 ==========
separator
log_info "步骤7: 系统缓存清理"
separator
# 清理用户缓存
if [ -d "/home" ]; then
log_info "清理用户缓存..."
find /home -type d -name ".cache" -exec rm -rf {} + 2>/dev/null || true
fi
# 清理root用户缓存
if [ -d "/root/.cache" ]; then
rm -rf /root/.cache/*
fi
# 清润pip缓存
if command -v pip &> /dev/null; then
log_info "清理pip缓存..."
pip cache purge 2>/dev/null || true
fi
if command -v pip3 &> /dev/null; then
pip3 cache purge 2>/dev/null || true
fi
# 清理npm缓存
if command -v npm &> /dev/null; then
log_info "清理npm缓存..."
npm cache clean --force 2>/dev/null || true
fi
log_success "系统缓存清理完成"
# ========== 8. 旧内核清理 ==========
separator
log_info "步骤8: 旧内核清理"
separator
if command -v apt-get &> /dev/null; then
# 获取当前内核版本
CURRENT_KERNEL=$(uname -r)
log_info "当前内核版本: $CURRENT_KERNEL"
# 列出所有已安装的内核
INSTALLED_KERNELS=$(dpkg --list | grep -E "linux-image-[0-9]" | awk '{print $2}' | grep -v "$CURRENT_KERNEL")
if [ -n "$INSTALLED_KERNELS" ]; then
log_info "发现旧内核:"
echo "$INSTALLED_KERNELS"
# 自动删除旧内核(保留当前和最新的)
apt-get autoremove -y --purge linux-image-* 2>/dev/null || true
log_success "旧内核清理完成"
else
log_info "没有需要清理的旧内核"
fi
fi
# ========== 9. 僵尸进程清理 ==========
separator
log_info "步骤9: 僵尸进程检查"
separator
ZOMBIE_COUNT=$(ps aux | awk '{print $8}' | grep -c Z || true)
if [ "$ZOMBIE_COUNT" -gt 0 ]; then
log_warning "发现 $ZOMBIE_COUNT 个僵尸进程"
ps aux | awk '$8=="Z" {print $0}'
else
log_success "没有僵尸进程"
fi
# ========== 10. 磁盘碎片整理 ==========
separator
log_info "步骤10: 磁盘优化建议"
separator
# 检查磁盘碎片
if command -v e4defrag &> /dev/null; then
log_info "磁盘碎片整理工具可用: e4defrag"
log_info "建议在维护窗口执行: e4defrag /"
fi
# ========== 显示清理结果 ==========
separator
log_info "清理结果"
separator
echo ""
log_info "磁盘使用情况对比:"
echo "清理前: 10G / 20G (51%)"
df -h | grep -E "/$" | awk '{print "清理后: "$3" / "$2" ("$5")"}'
echo ""
log_info "磁盘空间统计:"
echo " /var/lib/docker: $(du -sh /var/lib/docker 2>/dev/null | awk '{print $1}')"
echo " /var/log: $(du -sh /var/log 2>/dev/null | awk '{print $1}')"
echo " /home: $(du -sh /home 2>/dev/null | awk '{print $1}')"
echo " /usr: $(du -sh /usr 2>/dev/null | awk '{print $1}')"
echo " /root: $(du -sh /root 2>/dev/null | awk '{print $1}')"
echo ""
log_info "Docker资源使用:"
docker system df
separator
log_success "系统级磁盘瘦身完成"
separator
echo ""
log_info "清理摘要:"
echo " ✅ Docker悬空镜像已清理"
echo " ✅ Docker旧版本镜像已清理"
echo " ✅ Docker构建缓存已清理"
echo " ✅ 已停止容器已清理"
echo " ✅ 未使用卷已清理"
echo " ✅ 系统日志已清理"
echo " ✅ 包管理器缓存已清理"
echo " ✅ 临时文件已清理"
echo " ✅ 应用构建产物已清理"
echo " ✅ 安装包已清理"
echo " ✅ 系统缓存已清理"
echo " ✅ 旧内核已清理"
echo ""
log_warning "后续建议:"
echo " 1. 定期执行清理: 每周一次"
echo " 2. 监控磁盘使用: df -h"
echo " 3. 配置日志轮转: /etc/logrotate.d/"
echo " 4. 设置磁盘告警: 使用率 > 80%"
echo " 5. 定期备份重要数据"
echo ""
log_info "详细日志已保存到: $LOG_FILE"
separator
+331
View File
@@ -0,0 +1,331 @@
#!/bin/bash
# 统一磁盘瘦身脚本
# 用途:整合所有清理功能,定时执行
# 执行时间:每日凌晨2点
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_DIR="/var/log/system-maintenance"
mkdir -p "$LOG_DIR"
LOG_FILE="$LOG_DIR/daily-cleanup-$(date +%Y%m%d_%H%M%S).log"
exec > >(tee -a "$LOG_FILE") 2>&1
separator
echo "统一磁盘瘦身脚本"
echo "执行时间: $(date)"
separator
# 记录初始状态
INITIAL_DISK=$(df -h / | awk 'NR==2 {print $5}' | sed 's/%//')
INITIAL_USED=$(df -h / | awk 'NR==2 {print $3}')
INITIAL_AVAIL=$(df -h / | awk 'NR==2 {print $4}')
log_info "初始磁盘状态:"
log_info " 使用率: $INITIAL_DISK%"
log_info " 已使用: $INITIAL_USED"
log_info " 可用: $INITIAL_AVAIL"
echo ""
# ========== 1. Docker清理 ==========
separator
log_info "步骤1: Docker清理"
separator
# 清理悬空镜像
DANGLING=$(docker images -f "dangling=true" -q | wc -l)
if [ "$DANGLING" -gt 0 ]; then
log_info "清理 $DANGLING 个悬空镜像"
docker image prune -f
fi
# 清理未使用的镜像(保留最近3个版本)
UNUSED_IMAGES=$(docker images --format "{{.Repository}}:{{.Tag}}" | grep -E "novalon-website:[0-9]" | sort -r | tail -n +4)
if [ -n "$UNUSED_IMAGES" ]; then
log_info "清理旧版本镜像:"
echo "$UNUSED_IMAGES"
echo "$UNUSED_IMAGES" | xargs docker rmi -f 2>/dev/null || true
fi
# 清理构建缓存
log_info "清理Docker构建缓存"
docker builder prune -a -f
# 清理已停止容器
STOPPED=$(docker ps -f "status=exited" -q | wc -l)
if [ "$STOPPED" -gt 0 ]; then
log_info "清理 $STOPPED 个已停止容器"
docker container prune -f
fi
# 清理未使用卷
UNUSED_VOLS=$(docker volume ls -q --filter "dangling=true" | wc -l)
if [ "$UNUSED_VOLS" -gt 0 ]; then
log_info "清理 $UNUSED_VOLS 个未使用卷"
docker volume prune -f
fi
log_success "Docker清理完成"
# ========== 2. 系统日志清理 ==========
separator
log_info "步骤2: 系统日志清理"
separator
# 清理journal日志(保留7天)
if command -v journalctl &> /dev/null; then
JOURNAL_SIZE=$(journalctl --disk-usage 2>/dev/null | grep -oE '[0-9.]+[MG]' || echo "0B")
log_info "系统日志大小: $JOURNAL_SIZE"
journalctl --vacuum-time=7d
log_success "Journal日志清理完成"
fi
# 清理syslog等日志(保留7天)
if [ -d "/var/log" ]; then
log_info "清理旧日志文件..."
find /var/log -type f -name "*.log.*" -mtime +7 -delete 2>/dev/null || true
find /var/log -type f -name "*.gz" -mtime +7 -delete 2>/dev/null || true
find /var/log -type f -name "*.1" -mtime +7 -delete 2>/dev/null || true
# 压缩大日志
find /var/log -type f -name "*.log" -size +10M -exec gzip {} \; 2>/dev/null || true
log_success "日志文件清理完成"
fi
# ========== 3. 包管理器缓存清理 ==========
separator
log_info "步骤3: 包管理器缓存清理"
separator
if command -v apt-get &> /dev/null; then
log_info "清理APT缓存..."
apt-get clean > /dev/null 2>&1
apt-get autoclean > /dev/null 2>&1
apt-get autoremove -y > /dev/null 2>&1
# 清理APT缓存目录
rm -rf /var/cache/apt/archives/*.deb 2>/dev/null || true
rm -rf /var/cache/apt/archives/partial/* 2>/dev/null || true
log_success "APT缓存清理完成"
elif command -v yum &> /dev/null; then
log_info "清理YUM缓存..."
yum clean all > /dev/null 2>&1
rm -rf /var/cache/yum/* 2>/dev/null || true
log_success "YUM缓存清理完成"
fi
# ========== 4. 临时文件清理 ==========
separator
log_info "步骤4: 临时文件清理"
separator
# 清理/tmp目录(超过7天)
TMP_FILES=$(find /tmp -type f -mtime +7 2>/dev/null | wc -l)
if [ "$TMP_FILES" -gt 0 ]; then
log_info "清理 $TMP_FILES 个临时文件(>7天)"
find /tmp -type f -mtime +7 -delete 2>/dev/null || true
find /tmp -type d -empty -delete 2>/dev/null || true
fi
# 清理/var/tmp
if [ -d "/var/tmp" ]; then
find /var/tmp -type f -mtime +30 -delete 2>/dev/null || true
fi
log_success "临时文件清理完成"
# ========== 5. 应用构建产物清理 ==========
separator
log_info "步骤5: 应用构建产物清理"
separator
# 清理novalon-website构建产物
if [ -d "/home/novalon/docker-app/novalon-website" ]; then
log_info "清理novalon-website构建产物..."
# 清理dist目录中的tar.gz文件
find /home/novalon/docker-app/novalon-website -name "*.tar.gz" -type f -delete 2>/dev/null || true
# 清理.next缓存
find /home/novalon/docker-app/novalon-website -name ".next" -type d -exec rm -rf {} + 2>/dev/null || true
log_success "应用构建产物清理完成"
fi
# 清理旧的备份文件
log_info "清理旧备份文件..."
find /home/novalon -name "*.backup.*" -mtime +30 -delete 2>/dev/null || true
find /home/novalon -name "*.bak" -mtime +30 -delete 2>/dev/null || true
# ========== 6. 安装包清理 ==========
separator
log_info "步骤6: 安装包清理"
separator
# 清理/root下的安装包
if [ -d "/root" ]; then
log_info "清理/root下的安装包..."
# 查找并删除安装包目录(保留当前使用的)
find /root -maxdepth 1 -type d -name "*panel*" -mtime +30 -exec rm -rf {} + 2>/dev/null || true
find /root -maxdepth 1 -type f -name "*.tar.gz" -mtime +30 -delete 2>/dev/null || true
find /root -maxdepth 1 -type f -name "*.zip" -mtime +30 -delete 2>/dev/null || true
log_success "安装包清理完成"
fi
# ========== 7. 系统缓存清理 ==========
separator
log_info "步骤7: 系统缓存清理"
separator
# 清理用户缓存
if [ -d "/home" ]; then
log_info "清理用户缓存..."
find /home -type d -name ".cache" -exec rm -rf {} + 2>/dev/null || true
fi
# 清理root用户缓存
if [ -d "/root/.cache" ]; then
rm -rf /root/.cache/* 2>/dev/null || true
fi
# 清理pip缓存
if command -v pip &> /dev/null; then
pip cache purge 2>/dev/null || true
fi
if command -v pip3 &> /dev/null; then
pip3 cache purge 2>/dev/null || true
fi
# 清理npm缓存
if command -v npm &> /dev/null; then
npm cache clean --force 2>/dev/null || true
fi
log_success "系统缓存清理完成"
# ========== 8. 容器健康检查 ==========
separator
log_info "步骤8: 容器健康检查"
separator
# 检查关键容器状态
CRITICAL_CONTAINERS=(
"woodpecker-server"
"woodpecker-agent"
"novalon-nginx"
"novalon-website"
"forgejo"
"postgresql"
"registry"
)
UNHEALTHY_COUNT=0
for container in "${CRITICAL_CONTAINERS[@]}"; do
if docker ps -a --format "{{.Names}}" | grep -q "^${container}$"; then
STATUS=$(docker inspect --format='{{.State.Status}}' "$container" 2>/dev/null)
HEALTH=$(docker inspect --format='{{.State.Health.Status}}' "$container" 2>/dev/null)
if [ "$STATUS" != "running" ]; then
log_warning "容器 $container 状态异常: $STATUS"
UNHEALTHY_COUNT=$((UNHEALTHY_COUNT + 1))
elif [ -n "$HEALTH" ] && [ "$HEALTH" != "healthy" ]; then
log_warning "容器 $container 健康状态异常: $HEALTH"
UNHEALTHY_COUNT=$((UNHEALTHY_COUNT + 1))
else
log_success "容器 $container 状态正常"
fi
fi
done
if [ $UNHEALTHY_COUNT -gt 0 ]; then
log_warning "发现 $UNHEALTHY_COUNT 个异常容器"
else
log_success "所有容器状态正常"
fi
# ========== 显示清理结果 ==========
separator
log_info "清理结果"
separator
# 记录最终状态
FINAL_DISK=$(df -h / | awk 'NR==2 {print $5}' | sed 's/%//')
FINAL_USED=$(df -h / | awk 'NR==2 {print $3}')
FINAL_AVAIL=$(df -h / | awk 'NR==2 {print $4}')
echo ""
log_info "磁盘使用情况对比:"
echo " 清理前: $INITIAL_USED / 20G ($INITIAL_DISK%)"
echo " 清理后: $FINAL_USED / 20G ($FINAL_DISK%)"
echo " 改善: 使用率降低 $((INITIAL_DISK - FINAL_DISK))%"
echo ""
log_info "磁盘空间统计:"
echo " Docker: $(du -sh /var/lib/docker 2>/dev/null | awk '{print $1}')"
echo " 日志: $(du -sh /var/log 2>/dev/null | awk '{print $1}')"
echo " Home: $(du -sh /home 2>/dev/null | awk '{print $1}')"
echo " Root: $(du -sh /root 2>/dev/null | awk '{print $1}')"
echo ""
log_info "Docker资源使用:"
docker system df
separator
log_success "统一磁盘瘦身完成"
separator
echo ""
log_info "清理摘要:"
echo " ✅ Docker悬空镜像已清理"
echo " ✅ Docker旧版本镜像已清理"
echo " ✅ Docker构建缓存已清理"
echo " ✅ 已停止容器已清理"
echo " ✅ 未使用卷已清理"
echo " ✅ 系统日志已清理"
echo " ✅ 包管理器缓存已清理"
echo " ✅ 临时文件已清理"
echo " ✅ 应用构建产物已清理"
echo " ✅ 安装包已清理"
echo " ✅ 系统缓存已清理"
echo " ✅ 容器健康检查已完成"
echo ""
log_info "详细日志已保存到: $LOG_FILE"
# 清理旧日志文件(保留30天)
find "$LOG_DIR" -name "daily-cleanup-*.log" -mtime +30 -delete 2>/dev/null || true
separator