【已解答】宝塔面板定制docker升级镜像后一键修复运行环境
之前一直在群晖Docker使用宝塔,主要是省掉额外搭建服务器,一般用来测试测试网站什么的还挺好的,但是问题是,每次升级宝塔之后都会出现应用和配置文件丢失,于是,需要将官方的文件目录映射代码从-v ~/website_data:/www/wwwroot -v ~/mysql_data:/www/server/data -v ~/vhost:/www/server/panel/vhost
改成
- v ./www:/www也就是将整个www目录做持久化,因为宝塔的一些配置文件、备份文件、日志文件全都是在www目录下的。
这样修改后,升级宝塔之后,配置文件都还在,安装的插件也都在,但是均无法运行,看了一下是因为init的启动脚本和配置脚本在etc目录下,升级宝塔后丢失,于是我让AI写了段代码,可以完美解决这个问题。
代码如下(代码由GeminiPro优化)
#!/bin/bash
# ===========================================================================
# 宝塔 Docker 面板环境深度修复脚本 V34.0 (V21基准 + MySQL 终极修复)
# ===========================================================================
# 严正声明:
# 本脚本以用户提供的 V21 版本为不可变基准。
# 1. [保持] Nginx/Apache/PHP/Redis/基础依赖 逻辑与 V21 完全一致,绝无修改。
# 2. [修复] 仅针对 "第四阶段:MySQL" 进行替换,集成 my.cnf 模板修复与 ibdata1 尺寸自适应。
# ===========================================================================
# 设置环境变量,确保命令可用
export PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export DEBIAN_FRONTEND=noninteractive
# --- 1. 配置区域 (依赖包下载地址) ---
# 使用 Ubuntu 官方源
URL_JEMALLOC="http://archive.ubuntu.com/ubuntu/pool/universe/j/jemalloc/libjemalloc2_5.2.1-1ubuntu1_amd64.deb"
URL_LUAJIT="http://archive.ubuntu.com/ubuntu/pool/universe/l/luajit/libluajit-5.1-2_2.1.0~beta3+dfsg-5.1_amd64.deb"
URL_SSL="http://nz2.archive.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.1f-1ubuntu2_amd64.deb"
URL_ICU="http://security.ubuntu.com/ubuntu/pool/main/i/icu/libicu60_60.2-3ubuntu3.2_amd64.deb"
# --- 2. 颜色与日志定义 ---
GREEN='\033[0;32m'
RED='\033[0;31m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
GREY='\033[0;37m'
NC='\033[0m' # No Color
log_info() { echo -e "${CYAN}[运行] $1${NC}"; }
log_success() { echo -e "${GREEN}[成功] $1${NC}"; }
log_fail() { echo -e "${RED}[错误] $1${NC}"; }
log_skip() { echo -e "${GREY}[跳过] $1${NC}"; }
log_step() { echo -e "${YELLOW} -> $1${NC}"; }
log_line() { echo -e "--------------------------------------------------------"; }
# --- 3. 核心工具函数 ---
# 下载并安装 .deb 包
install_deb() {
local url="$1"
local filename="$2"
log_step "正在下载: $(basename "$filename")..."
if wget --timeout=15 --tries=3 -O "$filename" "$url" >/dev/null 2>&1; then
log_step "下载完成,正在通过 dpkg 安装..."
dpkg -i "$filename" >/dev/null 2>&1
rm -f "$filename"
return 0
else
log_fail "下载失败: $url (请检查网络或更换源)"
return 1
fi
}
# --- 脚本开始 ---
clear
echo -e "========================================================"
echo -e " 宝塔 Docker 服务全自动修复脚本 V34.0 (基准修正版) "
echo -e "========================================================"
# 检查是否为 Debian/Ubuntu 系统
if [ ! -f "/usr/bin/apt-get" ]; then
log_fail "检测到非 Debian/Ubuntu 系统,本脚本仅支持 apt 包管理!"
exit 1
fi
# ---------------------------------------------------------------------------
# 第一阶段:基础用户与环境重建 (V21 原版)
# ---------------------------------------------------------------------------
log_line
log_info "1. 检查并重建系统用户 (User/Group)"
if [ ! -f "/usr/bin/wget" ]; then
log_step "未检测到 wget,正在安装..."
apt-get update -y >/dev/null 2>&1
apt-get install -y wget >/dev/null 2>&1
fi
# 补充 Apache 可能需要的通用库 (libexpat, libnghttp2)
log_step "检查基础运行库..."
if ! dpkg -s libexpat1 >/dev/null 2>&1; then
apt-get install -y libexpat1 libnghttp2-14 >/dev/null 2>&1
fi
for user in www mysql redis; do
if ! id -u $user >/dev/null 2>&1; then
log_step "用户 [$user] 不存在,正在创建..."
groupadd $user >/dev/null 2>&1
useradd -g $user -s /sbin/nologin $user >/dev/null 2>&1
log_success "用户 [$user] 创建成功"
else
log_skip "用户 [$user] 已存在"
fi
done
# ---------------------------------------------------------------------------
# 第二阶段:核心依赖库修复 (V21 原版)
# ---------------------------------------------------------------------------
log_line
log_info "2. 检查系统核心依赖库 (.so files)"
# >>> 修复 Jemalloc <<<
if [ -f "/usr/lib/libjemalloc.so.2" ]; then
log_skip "依赖 libjemalloc.so.2 已存在"
else
log_step "依赖 libjemalloc 缺失,准备安装..."
if install_deb "$URL_JEMALLOC" "/tmp/libjemalloc2.deb"; then
rm -f /usr/lib/libjemalloc.so.2 /usr/lib64/libjemalloc.so.2
real_jemalloc=$(find /usr -name "libjemalloc.so.2" | grep -v "/usr/lib/libjemalloc.so.2" | head -n 1)
if [ -n "$real_jemalloc" ]; then
ln -sf "$real_jemalloc" /usr/lib/libjemalloc.so.2
ln -sf "$real_jemalloc" /usr/lib64/libjemalloc.so.2
log_success "libjemalloc 修复完成"
fi
fi
fi
# >>> 修复 LuaJIT <<<
if [ -f "/usr/lib/libluajit-5.1.so.2" ]; then
log_skip "依赖 libluajit-5.1.so.2 已存在"
else
log_step "依赖 libluajit-5.1 缺失,准备安装..."
if install_deb "$URL_LUAJIT" "/tmp/libluajit.deb"; then
rm -f /usr/lib/libluajit-5.1.so.2 /usr/lib64/libluajit-5.1.so.2
real_luajit=$(find /usr -name "libluajit-5.1.so.2" | grep -v "/usr/lib/libluajit-5.1.so.2" | head -n 1)
if [ -n "$real_luajit" ]; then
ln -sf "$real_luajit" /usr/lib/libluajit-5.1.so.2
ln -sf "$real_luajit" /usr/lib64/libluajit-5.1.so.2
ln -sf "$real_luajit" /usr/lib/x86_64-linux-gnu/libluajit-5.1.so.2 2>/dev/null
log_success "libluajit 修复完成"
fi
fi
fi
# >>> 修复 OpenSSL 1.1 / ICU <<<
if [ ! -f "/usr/lib/x86_64-linux-gnu/libssl.so.1.1" ]; then
install_deb "$URL_SSL" "/tmp/libssl1.1.deb"
fi
if [ ! -f "/usr/lib/x86_64-linux-gnu/libicuio.so.60" ]; then
install_deb "$URL_ICU" "/tmp/libicu60.deb"
fi
ldconfig >/dev/null 2>&1
# ---------------------------------------------------------------------------
# 第三阶段:PHP 服务修复 (V21 原版)
# ---------------------------------------------------------------------------
log_line
log_info "3. 检查 PHP 服务状态"
if [ ! -d "/www/server/php" ]; then
log_skip "未检测到 PHP 安装目录"
else
for php_dir in /www/server/php/*; do
if [ -d "$php_dir" ]; then
version=$(basename "$php_dir")
if [[ ! $version =~ ^+$ ]]; then continue; fi
if pgrep -f "$php_dir/" >/dev/null; then
log_success "PHP-$version 正在运行中 (跳过)"
continue
fi
log_step "正在启动 PHP-$version..."
init_script="/etc/init.d/php-fpm-$version"
src_script="$php_dir/src/sapi/fpm/init.d.php-fpm"
if [ ! -f "$init_script" ]; then
if [ -f "$src_script" ]; then
cp "$src_script" "$init_script"
else
cat > "$init_script" <<EOF
#!/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
export PATH
mkdir -p $php_dir/var/run/
case "\$1" in
start) $php_dir/sbin/php-fpm --fpm-config $php_dir/etc/php-fpm.conf ;;
stop) pkill -F $php_dir/var/run/php-fpm.pid ;;
reload) pkill -USR2 -F $php_dir/var/run/php-fpm.pid ;;
esac
EOF
fi
chmod +x "$init_script"
fi
service "php-fpm-$version" start >/dev/null 2>&1
if pgrep -f "$php_dir/" >/dev/null; then log_success "PHP-$version 启动成功"; else log_fail "PHP-$version 启动失败"; fi
fi
done
fi
# ---------------------------------------------------------------------------
# 第四阶段:MySQL 服务修复 (唯一修改部分:使用 V32 验证成功的逻辑)
# ---------------------------------------------------------------------------
log_line
log_info "4. 检查 MySQL 服务状态 (深度修复)"
MYSQL_BIN="/www/server/mysql/bin/mysql"
MY_CNF="/etc/my.cnf"
if [ ! -f "$MYSQL_BIN" ]; then
log_skip "未检测到 MySQL 安装文件"
else
# 1. 确定数据目录
DATA_DIR="/www/server/data"
if [ ! -d "$DATA_DIR" ] && [ -d "/www/server/mysql/data" ]; then
DATA_DIR="/www/server/mysql/data"
fi
log_step "锁定 MySQL 数据目录: $DATA_DIR"
# 2. 配置文件重建 (使用用户提供的可用模板)
if [ -f "$MY_CNF" ]; then
cp "$MY_CNF" "${MY_CNF}.bak_v34"
log_step "已备份旧配置到 ${MY_CNF}.bak_v34"
fi
log_step "正在写入标准配置 (修复日志文件大小匹配问题)..."
cat > "$MY_CNF" <<EOF
#password = your_password
port = 3306
socket = /tmp/mysql.sock
port = 3306
socket = /tmp/mysql.sock
datadir = $DATA_DIR
default_storage_engine = InnoDB
skip-external-locking
key_buffer_size = 256M
max_allowed_packet = 100G
table_open_cache = 1024
sort_buffer_size = 4M
net_buffer_length = 4K
read_buffer_size = 4M
read_rnd_buffer_size = 256K
myisam_sort_buffer_size = 64M
thread_cache_size = 128
tmp_table_size = 128M
lower_case_table_names = 1
default_authentication_plugin = mysql_native_password
sql-mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES
explicit_defaults_for_timestamp = true
#skip-name-resolve
max_connections = 500
max_connect_errors = 100
open_files_limit = 65535
log-bin=mysql-bin
binlog_format=mixed
server-id = 1
binlog_expire_logs_seconds = 600000
slow_query_log=1
slow-query-log-file=$DATA_DIR/mysql-slow.log
long_query_time=3
#log_queries_not_using_indexes=on
early-plugin-load = ""
log-error = $DATA_DIR/mysql-error.log
innodb_data_home_dir = $DATA_DIR
innodb_data_file_path = ibdata1:10M:autoextend
innodb_log_group_home_dir = $DATA_DIR
innodb_buffer_pool_size = 1024M
innodb_log_file_size = 512M
innodb_log_buffer_size = 128M
innodb_flush_log_at_trx_commit = 1
innodb_lock_wait_timeout = 50
innodb_max_dirty_pages_pct = 90
innodb_read_io_threads = 4
innodb_write_io_threads = 4
quick
max_allowed_packet = 500M
no-auto-rehash
key_buffer_size = 256M
sort_buffer_size = 4M
read_buffer = 2M
write_buffer = 2M
interactive-timeout
EOF
log_success "配置文件 /etc/my.cnf 重建完成"
# 3. 解决 ibdata1 大小不匹配问题
IBDATA_FILE="$DATA_DIR/ibdata1"
if [ -f "$IBDATA_FILE" ]; then
FILE_SIZE_BYTE=$(ls -l "$IBDATA_FILE" | awk '{print $5}')
FILE_SIZE_MB=$((FILE_SIZE_BYTE / 1024 / 1024))
# 如果不是 10M,修正它
if [ "$FILE_SIZE_MB" -ne 10 ]; then
log_step "检测到 ibdata1 为 ${FILE_SIZE_MB}M,修正配置..."
sed -i "s|ibdata1:10M:autoextend|ibdata1:${FILE_SIZE_MB}M:autoextend|g" "$MY_CNF"
fi
fi
# 4. 修正权限
log_step "修正 MySQL 目录权限..."
chown root:root "$MY_CNF"
chmod 644 "$MY_CNF"
chown -R mysql:mysql /www/server/mysql
chown -R mysql:mysql "$DATA_DIR"
chmod 755 "$DATA_DIR"
mkdir -p /var/run/mysqld && chown mysql:mysql /var/run/mysqld
# 5. 恢复启动脚本
if [ -f "/www/server/mysql/support-files/mysql.server" ]; then
cp /www/server/mysql/support-files/mysql.server /etc/init.d/mysqld
chmod +x /etc/init.d/mysqld
fi
# 6. 重启流程
log_step "正在重启 MySQL..."
service mysqld stop >/dev/null 2>&1
for k in {1..5}; do
if ! pgrep -x "mysqld" >/dev/null; then break; fi
sleep 1
done
if pgrep -x "mysqld" >/dev/null; then
pkill -9 mysqld >/dev/null 2>&1
rm -f "$DATA_DIR/mysql.pid"
fi
# 清理残留锁
rm -f "$DATA_DIR"/*.lock /tmp/mysql.sock /tmp/mysql.sock.lock
service mysqld start >/dev/null 2>&1
for i in {1..10}; do
if pgrep -x "mysqld" >/dev/null; then break; fi; sleep 1;
done
if pgrep -x "mysqld" >/dev/null; then
log_success "MySQL 启动成功"
else
log_fail "MySQL 启动失败,尝试救砖模式..."
sudo -u mysql /www/server/mysql/bin/mysqld_safe --defaults-file="$MY_CNF" >/dev/null 2>&1 &
sleep 5
if pgrep -x "mysqld" >/dev/null; then
log_success "MySQL (救砖模式) 启动成功"
else
log_fail "MySQL 救砖失败,请检查日志: $DATA_DIR/mysql-error.log"
tail -n 10 "$DATA_DIR/mysql-error.log" 2>/dev/null
fi
fi
fi
# ---------------------------------------------------------------------------
# 第五阶段:Redis 服务修复 (V21 原版)
# ---------------------------------------------------------------------------
log_line
log_info "5. 检查 Redis 服务状态"
if [ ! -d "/www/server/redis" ]; then
log_skip "未检测到 Redis 安装目录"
else
if [ -f "/www/server/redis/scripts/redis_init_script" ]; then
cp /www/server/redis/scripts/redis_init_script /etc/init.d/redis
chmod +x /etc/init.d/redis
fi
log_step "修正 Redis 目录权限..."
chown -R redis:redis /www/server/redis
log_step "正在关闭旧的 Redis 进程..."
service redis stop >/dev/null 2>&1
pkill redis-server >/dev/null 2>&1
# --- 关键:循环等待直到进程完全消失 ---
for k in {1..10}; do
if ! pgrep -x "redis-server" >/dev/null; then break; fi
echo -n "."
sleep 1
done
echo ""
if pgrep -x "redis-server" >/dev/null; then
log_step "Redis 进程卡死,执行强制清理..."
pkill -9 redis-server >/dev/null 2>&1
rm -f /var/run/redis_6379.pid
fi
log_step "尝试启动 Redis..."
service redis start >/dev/null 2>&1
sleep 2
if pgrep -x "redis-server" >/dev/null; then
log_success "Redis 重启成功"
else
log_step "Service 启动失败,尝试通过配置文件直接启动..."
pkill -9 redis-server >/dev/null 2>&1
sudo -u redis /www/server/redis/src/redis-server /www/server/redis/redis.conf &
sleep 2
if pgrep -x "redis-server" >/dev/null; then
log_success "Redis (直接模式) 启动成功"
else
log_fail "Redis 启动失败,请检查配置文件权限"
fi
fi
fi
# ---------------------------------------------------------------------------
# 第六阶段:Nginx 服务修复 (V21 原版)
# ---------------------------------------------------------------------------
log_line
log_info "6. 检查 Nginx 服务状态"
if [ ! -d "/www/server/nginx" ]; then
log_skip "未检测到 Nginx 安装目录"
else
if [ ! -f "/etc/init.d/nginx" ]; then
cat > /etc/init.d/nginx << 'EOF'
#!/bin/bash
# chkconfig: 2345 55 25
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
program=/www/server/nginx/sbin/nginx
config=/www/server/nginx/conf/nginx.conf
case "$1" in
start) $program -c $config ;;
stop) $program -c $config -s stop ;;
reload) $program -c $config -s reload ;;
esac
EOF
chmod +x /etc/init.d/nginx
fi
if [ -d "/www/wwwlogs" ]; then chown -R www:www /www/wwwlogs; fi
log_step "正在重启 Nginx..."
service nginx stop >/dev/null 2>&1
pkill nginx >/dev/null 2>&1
sleep 1
service nginx start >/dev/null 2>&1
sleep 1
if pgrep -x "nginx" >/dev/null; then log_success "Nginx 重启成功"; else log_fail "Nginx 启动失败"; fi
fi
# ---------------------------------------------------------------------------
# 第七阶段:Apache (httpd) 服务修复 (V21 原版)
# ---------------------------------------------------------------------------
log_line
log_info "7. 检查 Apache 服务状态"
if [ ! -d "/www/server/apache" ]; then
log_skip "未检测到 Apache 安装目录"
else
# 1. 恢复启动脚本 (Apachectl)
# 宝塔通常将 apachectl 放在 bin 下,需要链接到 init.d
if [ ! -f "/etc/init.d/httpd" ]; then
log_step "重建 Apache 启动脚本..."
if [ -f "/www/server/apache/bin/apachectl" ]; then
cp /www/server/apache/bin/apachectl /etc/init.d/httpd
chmod +x /etc/init.d/httpd
# 替换脚本中的路径(防止旧配置路径不对)
sed -i 's|/www/server/apache|/www/server/apache|g' /etc/init.d/httpd
else
log_fail "Apache bin 目录下未找到 apachectl,无法恢复启动脚本"
fi
fi
# 2. 关键权限修复
# Apache 对日志目录权限极其敏感,如果不可写会直接启动失败且无回显
log_step "修正 Apache 及日志目录权限..."
chown -R www:www /www/server/apache
if [ -d "/www/wwwlogs" ]; then
chown -R www:www /www/wwwlogs
chmod 755 /www/wwwlogs
fi
# 3. 稳健重启逻辑 (参考 Redis)
log_step "正在关闭旧的 Apache 进程..."
service httpd stop >/dev/null 2>&1
pkill httpd >/dev/null 2>&1
# 等待进程退出
for k in {1..10}; do
if ! pgrep -x "httpd" >/dev/null; then break; fi
echo -n "."
sleep 1
done
echo ""
if pgrep -x "httpd" >/dev/null; then
log_step "Apache 进程卡死,强制清理..."
pkill -9 httpd >/dev/null 2>&1
rm -f /www/server/apache/logs/httpd.pid
fi
# 4. 启动
log_step "尝试启动 Apache..."
service httpd start >/dev/null 2>&1
sleep 2
# 5. 验证
if pgrep -x "httpd" >/dev/null; then
log_success "Apache 重启成功"
else
log_fail "Apache 启动失败"
log_step "尝试手动获取错误信息:"
/www/server/apache/bin/httpd -k start
fi
fi
# ---------------------------------------------------------------------------
# 结束
# ---------------------------------------------------------------------------
log_line
echo -e "${GREEN}修复脚本运行结束。${NC}"
echo -e "如果服务仍有问题,请复制上方红色错误信息进行排查。"
echo -e "========================================================"使用时可以保存为sh,在宝塔的面板里面手动运行,也可以像我这样加入计划任务,定时运行,都是可以的。
实测可以修复升级宝塔后出现的ngnix、mysql、redis、php、apache运行错误的问题,而无需卸载重新安装。
PS:宝塔的计划任务触发条件没有开机或重启选项,如果有开机或重启选项,每次重启宝塔面板自动运行这一段代码,基本就完美了,希望官方能够跟进一下。
您好,非常感谢您的反馈,已经将此帖发给对应的维护者,感谢您提供的脚本,奖励宝塔币100
页:
[1]