这份 Debian Docker 全栈部署脚本 V5.1 是一个高度自动化的运维工具,旨在为你在本地或内网服务器上搭建一套工业级、可公网访问的网站托管环境。
它的核心逻辑是将 内网穿透 (FRP)、反向代理 (NPM) 和 应用容器化 (WordPress) 完美结合。以下是该脚本的功能与作用详细说明:
一、 核心功能概览
1. 环境自动化初始化 (Step 1-2)
- 清理冲突:自动检测并强行释放 80、443、81 等端口,停止系统自带的 Apache 或 Nginx,确保 Docker 容器能顺利接管网络。
- 兼容性修复:针对最新的 Debian 13 (Trixie) 做了平滑处理(若无原生仓库则降级使用 Bookworm 源),确保 Docker 引擎安装成功。
- 国内加速:配置了阿里云镜像源和 Docker Hub 镜像加速器,大幅提升国内环境下的安装和拉取镜像速度。
2. 内网穿透集成 (Step 3-4, 6)
- FRP 客户端部署:自动部署
frpc容器。通过你在脚本运行中粘贴的配置,将本地服务隧道化。 - 作用:让没有公网 IP 的家庭服务器或公司内网机器,也能通过一台公网 VPS 被全世界访问。
3. 流量网关与 SSL 管理 (Step 6)
- Nginx Proxy Manager (NPM):部署了一个可视化的反向代理管理器。
- 作用:
- 流量分发:根据域名将流量分发给不同的 WordPress 容器。
- SSL 证书:支持一键申请和自动续期 Let’s Encrypt 证书,让网站强制 HTTPS。
- 图形化:无需手动改 Nginx 配置文件,在 81 端口网页后台即可操作。
4. 智能建站系统 (Step 5.1)
- 站点隔离:每执行一次
add_site.sh,都会创建一个独立的目录,包含专属的数据库 (MariaDB) 和缓存 (Redis)。 - Redis 自动增强:脚本会自动从官方源下载
redis-cache插件并预装到 WordPress 中,提升网站访问速度。 - 容器自动联网:这是 V5.1 的核心修复点。它确保新创建的 WordPress 容器能自动加入
web_network虚拟网络,使得反向代理网关 (NPM) 能够通过“容器名”直接找到网站,避免了繁琐的 IP 手动配置。
二、 脚本产生的目录结构与作用
脚本运行后,会在 /data/docker_sites 生成规范化的文件结构:
| 路径 | 作用说明 |
/proxy/ | 存放 Nginx Proxy Manager 的数据和证书。 |
/frpc/ | 存放 FRP 客户端配置,负责连接公网服务器。 |
/scripts/ | 存放功能脚本,如 add_site.sh(用于新建网站)。 |
/cache/ | 存放下载好的 Redis 插件包,避免重复下载。 |
/[站点ID]/ | 重点:每个新网站的独立家目录,包含网站代码和数据库数据。 |
manage.sh | 管理入口:你日常唯一需要操作的文件,用于建站或重启服务。 |
三、 它的实际作业流程 (Workflow)
- 流量进入:外部用户访问
http://kaixinit.cn。 - 隧道传输:流量到达公网 FRPS,通过隧道传送到你这台机器的
frpc容器。 - 网关分发:
frpc将流量转交给proxy-app-1(NPM)。 - 内网寻址:NPM 根据你设置的转发规则,在 Docker 内部网络寻找
kaixinit_wp_app。 - 响应请求:WordPress 容器处理请求,配合 Redis 缓存和 MariaDB 数据库返回网页内容。
1. 进入 Root 模式
sudo -i2. 创建并写入部署脚本
nano /root/setup_ultimate.sh#如果清空内容在编辑
truncate -s 0 /root/setup_ultimate.sh & nano /root/setup_ultimate.sh3. 运行初始化
chmod +x setup_ultimate.sh
./setup_ultimate.shDebian Docker 全栈部署终极版 (V5.1)脚本
#!/bin/bash
# =================================================================
# 脚本名称: Debian Docker 全栈部署终极版 (V5.1)
# 适用系统: Debian 12 / 13 (Trixie)
# 核心特性: 国内全加速 | 源码粘贴录入 | 插件官方源下载 | 容器自动联网
# =================================================================
# 0. 权限与环境变量
if [ "$EUID" -ne 0 ]; then echo -e "\033[31m❌ 请使用 root 用户运行\033[0m"; exit 1; fi
export DOCKER_CLI_HINTS=false
BASE_DIR="/data/docker_sites"
clear
echo -e "\033[32m====================================================\033[0m"
echo -e "\033[32m 🚀 全栈环境部署 V5.1:最终完美修复版 \033[0m"
echo -e "\033[32m====================================================\033[0m"
# =======================
# 1. 环境清理与目录准备
# =======================
echo -e "\n\033[33m>>> [1/7] 初始化环境与端口清理...\033[0m"
# 安装基础依赖
apt-get update && apt-get install -y psmisc curl unzip jq ca-certificates gnupg lsb-release 2>/dev/null
# 强力释放端口并停止冲突服务
fuser -k 80/tcp 81/tcp 443/tcp 2>/dev/null
systemctl stop apache2 nginx 2>/dev/null
# 备份旧目录并重建
[[ -d "$BASE_DIR" ]] && mv "$BASE_DIR" "${BASE_DIR}_bak_$(date +%Y%m%d_%H%M%S)"
mkdir -p "$BASE_DIR/proxy" "$BASE_DIR/frpc" "$BASE_DIR/scripts" "$BASE_DIR/cache" "$BASE_DIR/backups"
chmod 700 "$BASE_DIR"
# =======================
# 2. 安装 Docker (国内阿里云加速)
# =======================
echo -e "\n\033[33m>>> [2/7] 检查并安装 Docker 引擎 (阿里云源)...\033[0m"
if ! command -v docker &> /dev/null; then
mkdir -p /etc/apt/keyrings
curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/debian/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg --yes
# 获取系统代号并处理 Debian 13 (Trixie) 兼容
CODENAME=$(lsb_release -cs)
[[ "$CODENAME" == "trixie" ]] && CODENAME="bookworm"
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://mirrors.aliyun.com/docker-ce/linux/debian $CODENAME stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
apt-get update && apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
fi
# 配置镜像加速器
mkdir -p /etc/docker
cat > /etc/docker/daemon.json <<EOF
{
"registry-mirrors": ["https://docker.m.daocloud.io", "https://dockerproxy.com"],
"log-driver": "json-file",
"log-opts": { "max-size": "10m", "max-file": "3" }
}
EOF
systemctl restart docker
# =======================
# 3. 配置文件块粘贴录入
# =======================
echo -e "\n\033[33m>>> [3/7] 请粘贴您的 FRP 配置文件内容...\033[0m"
echo -e "\033[31m⚠️ 警告:规则名称 (name) 必须唯一,避免路由冲突!\033[0m"
echo -e "\033[36m------------------------------------------------------------"
echo "💡 操作说明:直接粘贴内容,最后一行输入 'n' 并回车结束"
echo -e "------------------------------------------------------------\033[0m"
FRPC_CONFIG="$BASE_DIR/frpc/frpc.toml"
rm -rf "$FRPC_CONFIG" && touch "$FRPC_CONFIG"
while IFS= read -r line; do
[[ "$line" == "n" ]] && break
echo "$line" >> "$FRPC_CONFIG"
done
# =======================
# 4. 配置核对预览
# =======================
clear
echo -e "\033[33m🔍 请核对 frpc.toml 配置文件原文:\033[0m"
echo "----------------------------------------------------"
cat "$FRPC_CONFIG"
echo "----------------------------------------------------"
read -p "⚠️ 核对无误按 [回车] 开始部署,如有误 Ctrl+C 退出。" CONFIRM_KEY
# =======================
# 5. 生成管理子脚本 (内置最新修复逻辑)
# =======================
echo -e "\n\033[33m>>> [5/7] 正在生成管理系统与服务配置...\033[0m"
# 5.1 生成 add_site.sh (WP官方源 + 自动联网)
cat > "$BASE_DIR/scripts/add_site.sh" <<'EOF'
#!/bin/bash
BASE_DIR="/data/docker_sites"
CACHE_DIR="$BASE_DIR/cache"
REDIS_ZIP="$CACHE_DIR/redis-cache.zip"
mkdir -p "$CACHE_DIR"
# 官方稳定源
MIRRORS=(
"https://downloads.wordpress.org/plugin/redis-cache.2.5.4.zip"
"https://ghproxy.net/https://github.com/wp-plugins/redis-cache/archive/refs/tags/2.5.4.zip"
)
download_plugin() {
echo "🔍 正在检查/下载 Redis 插件..."
for url in "${MIRRORS[@]}"; do
curl -L -m 20 -o "$REDIS_ZIP" "$url" 2>/dev/null
if [ -f "$REDIS_ZIP" ] && [ $(stat -c%s "$REDIS_ZIP") -gt 102400 ] && unzip -t "$REDIS_ZIP" >/dev/null 2>&1; then
echo "✅ 插件下载成功且校验通过!"
return 0
fi
rm -f "$REDIS_ZIP"
done
return 1
}
read -p "🔹 网站标识 (如 kaixinit): " SITE_ID
[[ -z "$SITE_ID" ]] && exit 1
SITE_DIR="$BASE_DIR/$SITE_ID"
mkdir -p "$SITE_DIR/db_data" "$SITE_DIR/wp-content/plugins"
[[ ! -f "$REDIS_ZIP" ]] && download_plugin
if [ -f "$REDIS_ZIP" ]; then
unzip -oq "$REDIS_ZIP" -d "$SITE_DIR/wp-content/plugins/"
mv "$SITE_DIR/wp-content/plugins/redis-cache"* "$SITE_DIR/wp-content/plugins/redis-cache-tmp" 2>/dev/null
mv "$SITE_DIR/wp-content/plugins/redis-cache-tmp" "$SITE_DIR/wp-content/plugins/redis-cache" 2>/dev/null
echo "📦 Redis 插件已安装。"
fi
chown -R 33:33 "$SITE_DIR/wp-content"
DB_PWD=$(openssl rand -hex 12)
cat > "$SITE_DIR/docker-compose.yml" <<YML
services:
db:
image: mariadb:10.6
restart: always
environment: { MYSQL_ROOT_PASSWORD: $DB_PWD, MYSQL_DATABASE: wordpress, MYSQL_USER: $SITE_ID, MYSQL_PASSWORD: $DB_PWD }
volumes: [ ./db_data:/var/lib/mysql ]
networks: [ internal_net ]
redis: { image: redis:alpine, restart: always, networks: [ internal_net ] }
wordpress:
image: wordpress:latest
container_name: ${SITE_ID}_wp_app
restart: always
depends_on: [db, redis]
environment: { WORDPRESS_DB_HOST: db:3306, WORDPRESS_DB_USER: $SITE_ID, WORDPRESS_DB_PASSWORD: $DB_PWD, WORDPRESS_DB_NAME: wordpress, WORDPRESS_CONFIG_EXTRA: "define('WP_REDIS_HOST', 'redis');" }
volumes: [ ./wp-content:/var/www/html/wp-content ]
networks: [ internal_net, web_network ]
networks:
web_network: { external: true }
internal_net: { driver: bridge }
YML
cd "$SITE_DIR" && docker compose up -d
docker network connect web_network ${SITE_ID}_wp_app 2>/dev/null
echo -e "\n\033[32m✅ 站点 ${SITE_ID} 启动成功!NPM转发主机名: \033[1m${SITE_ID}_wp_app\033[0m"
EOF
# 5.2 生成 manage.sh
cat > "$BASE_DIR/manage.sh" <<'EOF'
#!/bin/bash
BASE_DIR="/data/docker_sites"
clear
echo -e "\033[36m1. 新建站点\n2. 重启核心服务\nq. 退出\033[0m"
read -p "指令: " OPT
case $OPT in
1) $BASE_DIR/scripts/add_site.sh ;;
2) docker restart proxy-app-1 frpc-frpc-1 ;;
q) exit 0 ;;
esac
EOF
# =======================
# 6. 生成核心服务并启动 (静默模式)
# =======================
echo -e "\n\033[33m>>> [6/7] 正在启动核心服务 (NPM & FRPC)...\033[0m"
docker network create web_network 2>/dev/null
cat > "$BASE_DIR/proxy/docker-compose.yml" <<EOF
services:
app:
image: 'jc21/nginx-proxy-manager:latest'
container_name: proxy-app-1
restart: unless-stopped
ports: ['80:80', '81:81', '443:443']
volumes: ['./data:/data', './letsencrypt:/etc/letsencrypt']
networks: ['web_network']
networks:
web_network: { external: true }
EOF
cat > "$BASE_DIR/frpc/docker-compose.yml" <<EOF
services:
frpc:
image: snowdreamtech/frpc:latest
container_name: frpc-frpc-1
restart: always
volumes: ['./frpc.toml:/etc/frp/frpc.toml']
networks: ['web_network']
networks:
web_network: { external: true }
EOF
cd "$BASE_DIR/proxy" && docker compose up -d >/dev/null 2>&1
cd "$BASE_DIR/frpc" && docker compose up -d >/dev/null 2>&1
# =======================
# 7. 权限与完成
# =======================
chmod -R 700 "$BASE_DIR"
chmod +x "$BASE_DIR/scripts"/*.sh
chmod +x "$BASE_DIR/manage.sh"
echo -e "\n\033[32m✅ 最终全能版 V5.1 部署成功!\033[0m"
echo -e "💡 请通过以下命令管理您的站点:\033[1m/data/docker_sites/manage.sh\033[0m"第二阶段:部署网站容器
一、 全量备份脚本 (backup_sites.sh):你的“后悔药”
这个脚本的作用是为你的所有网站提供快照式保护。它不是简单的文件复制,而是“数据库+文件”的深度打包。
核心功能:
- 数据库静默导出:脚本会自动进入每一个 WordPress 站点的目录,利用
docker compose ps找到正在运行的数据库容器名,并执行mysqldump。这保证了导出的数据是实时且完整的。 - 全量打包:它会将网站的
wp-content(包含你所有的图片、插件、主题)、docker-compose.yml(环境配置)以及刚刚导出的db.sql全部压缩成一个.tar.gz文件。 - 路径显式回显(新增):备份完成后,它会用醒目的颜色告诉你备份存放在
/data/docker_sites/backups。这样你就不需要满世界去找文件,方便你通过 FTP 或 SCP 下载到本地保存。
作用:
- 防灾:如果服务器硬盘损坏或误删了数据,你可以通过这个备份包在几分钟内恢复整个网站。
- 迁移:如果你想换一台服务器,只需把这个压缩包拷贝过去,解压并
docker compose up -d即可实现秒级搬家。
二、 主管理菜单 (manage.sh):你的“指挥中心”
这是你日常运维的“驾驶舱”,它将原本复杂的 Docker 命令简化成了简单的数字选项。
核心功能:
- 实时状态监控:
- 每次打开菜单,它都会自动检查 NPM(网关) 和 FRP(隧道) 是否在线。
- 作用:让你一眼看出网站无法访问是因为本地断网了,还是代理服务挂了。绿色的“● 运行中”让你感到安心。
- 流程化新建站点(选项 1):
- 调用
add_site.sh。 - 作用:标准化建站过程,自动分配数据库密码,自动连接到公共网络。
- 调用
- 可视化站点清单(选项 2):
- 作用:解决“好记性不如烂笔头”的问题。随时查看每个站点的数据库账号和那串复杂的随机密码。
- 安全彻底的销毁(选项 5):
- 作用:不同于
rm -rf,它会先执行docker compose down -v。这会干净地停止容器、移除虚拟网络和挂载卷,防止系统残留“僵尸容器”或占用端口。
- 作用:不同于
三、 整体系统协作图
| 组件 | 角色 | 作用 |
| manage.sh | 指挥官 | 接收你的指令,调用其他子脚本。 |
| backup_sites.sh | 后勤部 | 负责定期打包数据,确保资产安全。 |
| add_site.sh | 建设队 | 负责快速开辟新领地(新站点)。 |
| backups/ 文件夹 | 仓库 | 存放所有站点在不同时间点的状态包。 |
站点超级管理台 V5.9
/data/docker_sites/manage.sh#如果清空内容在编辑
truncate -s 0 /data/docker_sites/manage.sh & nano /data/docker_sites/manage.sh# 进入缓存目录
cd /data/docker_sites/cache/
# 直接从官方下载 (这是 6.9 版本的官方中文路径)
sudo wget https://cn.wordpress.org/latest-zh_CN.zip
sudo wget https://github.com/typecho/typecho/releases/latest/download/typecho.zip
# ls 查看如下cat > /data/docker_sites/manage.sh <<'EOF'
#!/bin/bash
# =================================================================
# Docker 运营旗舰版 V15.8 (备用机一键上线·智能清场·深度急救)
# =================================================================
BASE_DIR="/data/docker_sites"
BACKUP_DIR="$BASE_DIR/backups"
CACHE_DIR="$BASE_DIR/cache"
EXCLUDE_DIRS="^(proxy|frpc|cache|backups|scripts)$"
# --- 基础工具 ---
get_site_list() {
( cd "$BASE_DIR" 2>/dev/null || exit; ls -d */ 2>/dev/null | sed 's/\///' | grep -vE "$EXCLUDE_DIRS" | sort )
}
check_status() { [ -z "$(docker ps -q -f name=proxy)" ] && echo "OFF" || echo "ON"; }
check_frp() { [ -z "$(docker ps -q -f name=frpc)" ] && echo "OFF" || echo "ON"; }
# --- 12. 备用机一键上线 (新增) ---
func_backup_online() {
clear
echo "----------------------------------------------------"
echo "🚀 备用机一键上线程序 (Disaster Recovery)"
echo "----------------------------------------------------"
echo "此功能将执行以下操作:"
echo "1. 清理端口占用 (杀掉 Apache/Nginx)"
echo "2. 启动目录下所有站点的容器"
echo "3. 自动挂载 Docker 网络"
echo "4. 强制刷新网关与穿透连接"
echo "5. 锁定开机自启"
echo "----------------------------------------------------"
read -p "确认执行上线操作? (y/n): " choice
[ "$choice" != "y" ] && return
echo ""
echo "🧹 [1/6] 清理端口占用..."
systemctl stop apache2 2>/dev/null; systemctl disable apache2 2>/dev/null
systemctl stop nginx 2>/dev/null; systemctl disable nginx 2>/dev/null
killall apache2 2>/dev/null; killall nginx 2>/dev/null
echo "🌐 [2/6] 创建网络环境..."
docker network create web_network 2>/dev/null
echo "🚀 [3/6] 正在启动所有容器 (请耐心等待)..."
# 查找并启动所有 docker-compose.yml
find "$BASE_DIR" -maxdepth 2 -name "docker-compose.yml" -not -path "*/scripts/*" -exec docker compose -f {} up -d \;
echo "⏳ [4/6] 等待服务初始化 (5秒)..."
sleep 5
echo "🔗 [5/6] 建立网络连接..."
# 连接核心组件
docker network connect web_network frpc-frpc-1 2>/dev/null
docker network connect web_network proxy-app-1 2>/dev/null
# 连接所有站点
get_site_list | while read id; do
docker network connect web_network "${id}_nginx" 2>/dev/null
docker network connect web_network "${id}_wp_app" 2>/dev/null
done
echo "🔄 [6/6] 刷新服务与锁定自启..."
echo " - 重载网关..." && docker restart proxy-app-1
echo " - 重连穿透..." && sleep 2 && docker restart frpc-frpc-1
echo " - 锁定自启..." && docker update --restart=always $(docker ps -a -q) 2>/dev/null
echo ""
echo "✅ 备用机上线完成!请检查访问是否正常。"
read -p "回车返回..." k
}
# --- 11. 无法访问修复 ---
func_deep_fix() {
clear
echo "----------------------------------------------------"
echo "🚑 深度网络修复 & 自启锁定程序"
echo "----------------------------------------------------"
read -p "确认执行修复? (y/n): " choice
[ "$choice" != "y" ] && return
echo "🧹 [0/4] 清理端口..."
systemctl stop apache2 2>/dev/null; systemctl disable apache2 2>/dev/null
systemctl stop nginx 2>/dev/null; systemctl disable nginx 2>/dev/null
killall apache2 2>/dev/null; killall nginx 2>/dev/null
echo "🔧 [1/4] 检查网络..."
docker network create web_network 2>/dev/null
docker network connect web_network frpc-frpc-1 2>/dev/null
docker network connect web_network proxy-app-1 2>/dev/null
get_site_list | while read id; do
docker network connect web_network "${id}_nginx" 2>/dev/null
docker network connect web_network "${id}_wp_app" 2>/dev/null
done
echo "🔄 [2/4] 刷新网关..." && docker restart proxy-app-1
echo "📡 [3/4] 重连公网..." && sleep 3 && docker restart frpc-frpc-1
echo "🔒 [4/4] 锁定自启..." && docker update --restart=always $(docker ps -a -q) 2>/dev/null
echo "✅ 修复完成!"
read -p "回车返回..." k
}
# --- 10. 下载资源 ---
func_download_cache() {
mkdir -p "$CACHE_DIR"
clear
echo "----------------------------------------------------"
echo "📦 安装包缓存管理"
if [ -z "$(ls -A "$CACHE_DIR" 2>/dev/null)" ]; then echo " (暂无缓存)"; else ls -lh "$CACHE_DIR" | grep -v '^total' | awk '{print " 📄 " $9 " (" $5 ")"}' ; fi
echo "----------------------------------------------------"
read -p "下载新包? (y/n): " c
if [[ "$c" == "y" ]]; then
read -p "URL: " u; [ -z "$u" ] && return
def=$(basename "$u"); read -p "文件名 (默认 $def): " n; [ -z "$n" ] && n="$def"
echo "⬇️ 下载中..." && curl -L -# -o "$CACHE_DIR/$n" "$u" && echo "✅ 完成"
fi
read -p "回车返回..." k
}
# --- 9. 修复权限 ---
func_fix_perms() {
IS_SILENT=$1
[ "$IS_SILENT" != "silent" ] && echo "--- 校准权限 (PHP-82/Nginx-101) ---"
get_site_list | while read id; do
dir="$BASE_DIR/$id"
if [ -d "$dir/www_root" ]; then
mkdir -p "$dir/www_root/wp-content/upgrade" "$dir/nginx_cache"
chown -R 82:82 "$dir/www_root" 2>/dev/null
chown -R 101:101 "$dir/nginx_cache" 2>/dev/null
find "$dir/www_root" -type d -exec chmod 755 {} \; 2>/dev/null
find "$dir/www_root" -type f -exec chmod 644 {} \; 2>/dev/null
if [ -f "$dir/www_root/wp-config.php" ]; then
sed -i "/WP_REDIS_HOST/d" "$dir/www_root/wp-config.php"
sed -i "/WP_REDIS_PORT/d" "$dir/www_root/wp-config.php"
sed -i "/wp-settings.php/i define('WP_REDIS_HOST', 'redis');" "$dir/www_root/wp-config.php"
sed -i "/wp-settings.php/i define('WP_REDIS_PORT', 6379);" "$dir/www_root/wp-config.php"
grep -q "FS_METHOD" "$dir/www_root/wp-config.php" || echo "define('FS_METHOD', 'direct');" >> "$dir/www_root/wp-config.php"
fi
fi
done
[ "$IS_SILENT" != "silent" ] && { echo "✅ 完成。"; read -p "回车..." k; }
}
# --- 1. 新建站点 ---
func_add_site() {
echo "--- 新建站点 ---"
read -p "站点ID: " SITE_ID; [ -z "$SITE_ID" ] && return
SITE_DIR="$BASE_DIR/$SITE_ID"
mkdir -p "$SITE_DIR"/{db_data,redis_data,www_root,nginx_conf,nginx_cache}
mkdir -p "$CACHE_DIR"
if [ -n "$(ls -A "$CACHE_DIR" 2>/dev/null)" ]; then echo "可用缓存:"; ls -1 "$CACHE_DIR" | awk '{print " - " $0}'; fi
read -p "输入文件名 (回车下载最新版): " ZIP_NAME
if [ -z "$ZIP_NAME" ]; then
TARGET_ZIP="$CACHE_DIR/latest-zh_CN.zip"
[ ! -f "$TARGET_ZIP" ] && { echo "拉取官方包..."; curl -L -o "$TARGET_ZIP" "https://cn.wordpress.org/latest-zh_CN.zip"; }
else
TARGET_ZIP="$CACHE_DIR/$ZIP_NAME"
if [ ! -f "$TARGET_ZIP" ]; then echo "❌ 文件不存在"; read -p "返回..." k; return; fi
fi
echo "1. 解压..."
rm -rf "$SITE_DIR/www_root/"*
unzip -oq "$TARGET_ZIP" -d "$SITE_DIR/temp"
[ -d "$SITE_DIR/temp/wordpress" ] && mv "$SITE_DIR/temp/wordpress/"* "$SITE_DIR/www_root/" || mv "$SITE_DIR/temp/"* "$SITE_DIR/www_root/"
rm -rf "$SITE_DIR/temp"
DB_PWD=$(openssl rand -hex 12)
cat > "$SITE_DIR/uploads.ini" <<INI
upload_max_filesize = 20M
post_max_size = 20M
memory_limit = 256M
INI
cat > "$SITE_DIR/nginx_conf/nginx.conf" <<EOF_NG
user nginx;
worker_processes auto;
pid /var/run/nginx.pid;
events { worker_connections 4096; multi_accept on; }
http {
fastcgi_cache_key "\$scheme\$request_method\$host\$request_uri";
sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 65; keepalive_requests 1000;
open_file_cache max=10000 inactive=60s; open_file_cache_valid 120s; open_file_cache_min_uses 2; open_file_cache_errors on;
gzip on; gzip_comp_level 5; gzip_min_length 1024; gzip_vary on; gzip_types text/plain text/css application/javascript application/json application/xml image/svg+xml;
fastcgi_cache_path /var/cache/nginx/wordpress levels=1:2 keys_zone=WP:200m inactive=30m max_size=5g use_temp_path=off;
fastcgi_cache_lock on; fastcgi_cache_lock_timeout 10s; fastcgi_cache_use_stale error timeout updating http_500; fastcgi_cache_background_update on;
limit_req_zone \$binary_remote_addr zone=logins:10m rate=10r/m;
map \$http_cookie \$skip_cache { default 0; ~*wordpress_logged_in 1; ~*comment_author 1; ~*wp-postpass 1; ~*woocommerce_items_in_cart 1; ~*woocommerce_cart_hash 1; }
include /etc/nginx/mime.types; default_type application/octet-stream; client_max_body_size 20M; include /etc/nginx/conf.d/*.conf;
}
EOF_NG
cat > "$SITE_DIR/nginx_conf/default.conf" <<EOF_SD
server {
listen 80; root /var/www/html; index index.php;
location / { try_files \$uri \$uri/ /index.php?\$args; }
location ~ \.php$ {
fastcgi_pass php_fpm:9000; include fastcgi_params; fastcgi_param SCRIPT_FILENAME \$document_root\$fastcgi_script_name;
fastcgi_cache WP; fastcgi_cache_valid 200 1h; fastcgi_cache_bypass \$skip_cache; fastcgi_no_cache \$skip_cache; add_header X-FastCGI-Cache \$upstream_cache_status;
}
}
EOF_SD
cat > "$SITE_DIR/docker-compose.yml" <<YML
services:
db:
image: mariadb:10.6
restart: always
environment:
MYSQL_ROOT_PASSWORD: "${DB_PWD}"
MYSQL_DATABASE: "wordpress"
MYSQL_USER: "${SITE_ID}"
MYSQL_PASSWORD: "${DB_PWD}"
volumes:
- ./db_data:/var/lib/mysql
networks:
- internal_net
redis:
image: redis:alpine
restart: always
networks:
- internal_net
php_fpm:
image: wordpress:fpm-alpine
restart: always
depends_on:
- db
- redis
environment:
WORDPRESS_DB_HOST: "db:3306"
WORDPRESS_DB_USER: "${SITE_ID}"
WORDPRESS_DB_PASSWORD: "${DB_PWD}"
WORDPRESS_DB_NAME: "wordpress"
volumes:
- ./www_root:/var/www/html
- ./uploads.ini:/usr/local/etc/php/conf.d/uploads.ini
networks:
- internal_net
nginx:
image: nginx:alpine
container_name: "${SITE_ID}_nginx"
restart: always
volumes:
- ./www_root:/var/www/html
- ./nginx_conf/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx_conf/default.conf:/etc/nginx/conf.d/default.conf:ro
- ./nginx_cache:/var/cache/nginx/wordpress
networks:
- internal_net
- web_network
networks:
web_network:
external: true
internal_net:
driver: bridge
YML
echo "2. 启动容器..."
cd "$SITE_DIR" && docker compose up -d
docker network connect web_network "${SITE_ID}_nginx" 2>/dev/null
CONF="$SITE_DIR/www_root/wp-config.php"
[ -f "$SITE_DIR/www_root/wp-config-sample.php" ] && cp "$SITE_DIR/www_root/wp-config-sample.php" "$CONF"
if [ -f "$CONF" ]; then
sed -i "s/database_name_here/wordpress/" "$CONF"
sed -i "s/username_here/$SITE_ID/" "$CONF"
sed -i "s/password_here/$DB_PWD/" "$CONF"
sed -i "s/localhost/db/" "$CONF"
fi
func_fix_perms "silent"
echo "🔒 锁定开机自启..."
docker update --restart=always $(docker ps -a -q) 2>/dev/null
echo "----------------------------------------------------"
echo "✅ 站点 $SITE_ID 部署成功!"
echo "数据库密码: $DB_PWD"
echo "----------------------------------------------------"
read -p "回车返回..." k
}
# --- 功能清单 ---
func_list() { printf "%-18s | %-25s\n" "ID" "DB密码"; get_site_list | while read id; do yml="$BASE_DIR/$id/docker-compose.yml"; [ -f "$yml" ] && printf "%-18s | %-25s\n" "$id" "$(grep 'MYSQL_ROOT_PASSWORD' "$yml" | head -n 1 | sed 's/.*: //; s/\"//g')"; done; read -p "继续..." k; }
func_backup() { echo "备份中..."; DATE=$(date +%Y%m%d_%H%M); mkdir -p "$BACKUP_DIR"; get_site_list | while read id; do cd "$BASE_DIR/$id"; db_con=$(docker compose ps --format '{{.Name}}' db 2>/dev/null); [ -n "$db_con" ] && { root_pw=$(grep 'MYSQL_ROOT_PASSWORD' "docker-compose.yml" | head -n 1 | sed 's/.*: //; s/\"//g'); docker exec "$db_con" mysqldump -uroot -p"$root_pw" wordpress > db.sql 2>/dev/null; [ -s db.sql ] && tar -czf "$BACKUP_DIR/${id}_$DATE.tar.gz" -C "$BASE_DIR" "$id"; rm db.sql && echo "✅ $id"; }; done; read -p "完成..." k; }
func_restart_core() { docker restart proxy-app-1 frpc; read -p "已重启..." k; }
func_delete() { get_site_list; read -p "删除ID: " did; [ -n "$did" ] && [ -d "$BASE_DIR/$did" ] && { cd "$BASE_DIR/$did" && docker compose down -v; cd "$BASE_DIR" && rm -rf "$BASE_DIR/$did"; echo "✅ 删除"; }; read -p "继续..." k; }
func_restore() { ls -1 "$BACKUP_DIR" 2>/dev/null; read -p "文件名: " FN; [ -f "$BACKUP_DIR/$FN" ] && { SID=$(echo "$FN" | cut -d'_' -f1); tar -xzf "$BACKUP_DIR/$FN" -C "$BASE_DIR"; cd "$BASE_DIR/$SID" && docker compose up -d; echo "✅ 恢复"; }; read -p "继续..." k; }
func_update() { get_site_list | while read id; do cd "$BASE_DIR/$id" && docker compose pull && docker compose up -d; done; docker image prune -f; read -p "完成..." k; }
func_monitor() { printf "CPU: %s%% 内存: %s\n" "$(top -bn1 | grep 'Cpu(s)' | awk '{print 100-$8}')" "$(free -m | awk 'NR==2{printf "%.2f%%", $3*100/$2}')"; docker stats --no-stream --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}"; read -p "回车..." k; }
# --- 主循环 ---
while true; do
clear
echo "----------------------------------------------------"
echo "Docker 运营旗舰版 V15.8 (一键备机上线·急救·下载)"
echo "网关: $(check_status) 穿透: $(check_frp) 上传: 20M"
echo "----------------------------------------------------"
echo "1. 新建站点 2. 站点清单 3. 全站备份"
echo "4. 重启核心 5. 删除站点 6. 一键恢复"
echo "7. 一键更新 8. 性能监控 9. 修复权限"
echo "10.下载资源 11.访问修复(急救) 12.备机上线(一键)"
echo "q. 退出管理"
echo "----------------------------------------------------"
read -p "指令: " opt
case $opt in
1) func_add_site ;; 2) func_list ;; 3) func_backup ;;
4) func_restart_core ;; 5) func_delete ;; 6) func_restore ;;
7) func_update ;; 8) func_monitor ;; 9) func_fix_perms ;;
10) func_download_cache ;; 11) func_deep_fix ;; 12) func_backup_online ;;
q) exit 0 ;; *) read -p "无效..." k ;;
esac
done
EOF
chmod +x /data/docker_sites/manage.sh第四阶段:运维与故障恢复 (Cheat Sheet)
1. 查看数据库密码
如果你需要登录 WordPress 后台或数据库:
cat /data/docker_sites/kaixinit/docker-compose.yml
# 寻找 MYSQL_PASSWORD 字段
2. “核弹级” 重启所有服务
如果环境乱了,或者端口卡死,执行以下全套复活命令:
Bash
# === 1. 清理现场 ===
docker stop $(docker ps -aq)
docker rm $(docker ps -aq)
docker network prune -f
# === 2. 杀灭宿主机残留进程 (防止80端口被占) ===
killall -9 nginx apache2 httpd frpc 2>/dev/null
# === 3. 按正确顺序启动 ===
# 先启动网关
cd /data/docker_sites/proxy && docker compose up -d
# 再启动所有网站
find /data/docker_sites -maxdepth 2 -name "docker-compose.yml" -not -path "*/proxy/*" -not -path "*/frpc/*" -exec docker compose -f {} up -d \;
# 最后启动穿透 (等网关就绪)
sleep 5
cd /data/docker_sites/frpc && docker compose up -d
# === 4. 检查 ===
docker ps
3. 解决 FRPC 无法启动 (端口被占用)
如果 Docker 里的 frpc 报错,通常是因为宿主机有一个“幽灵” frpc 进程:
Bash
pkill -9 frpc
killall -9 frpc
# 然后重启容器
cd /data/docker_sites/frpc && docker compose up -d
原生安装模式,安装完wordpress,运行选项 9 ➡️ 注入 Redis 代码+ 校准文件权限。
cat > /data/docker_sites/manage.sh <<'EOF'
#!/bin/bash
# =================================================================
# Docker 运营旗舰版 V16.3 (真·原生纯净安装版)
# =================================================================
BASE_DIR="/data/docker_sites"
BACKUP_DIR="$BASE_DIR/backups"
CACHE_DIR="$BASE_DIR/cache"
EXCLUDE_DIRS="^(proxy|frpc|cache|backups|scripts)$"
# --- 基础工具 ---
get_site_list() {
( cd "$BASE_DIR" 2>/dev/null || exit; ls -d */ 2>/dev/null | sed 's/\///' | grep -vE "$EXCLUDE_DIRS" | sort )
}
check_status() { [ -z "$(docker ps -q -f name=proxy)" ] && echo "OFF" || echo "ON"; }
check_frp() { [ -z "$(docker ps -q -f name=frpc)" ] && echo "OFF" || echo "ON"; }
# --- 13. 查看数据库配置 ---
func_view_db_config() {
clear
echo "----------------------------------------------------"
echo "📊 查看站点数据库配置信息"
echo "----------------------------------------------------"
get_site_list | awk '{print " - " $0}'
echo "----------------------------------------------------"
read -p "请输入要查看的站点ID: " SITE_ID
YML_FILE="$BASE_DIR/$SITE_ID/docker-compose.yml"
if [ -z "$SITE_ID" ] || [ ! -f "$YML_FILE" ]; then echo "❌ 找不到配置"; read -p "回车..."; return; fi
# 提取数据库容器的配置
DB_NAME=$(grep "MYSQL_DATABASE" "$YML_FILE" | head -n 1 | awk -F': ' '{print $2}' | tr -d '"')
DB_USER=$(grep "MYSQL_USER" "$YML_FILE" | head -n 1 | awk -F': ' '{print $2}' | tr -d '"')
DB_PASS=$(grep "MYSQL_PASSWORD" "$YML_FILE" | head -n 1 | awk -F': ' '{print $2}' | tr -d '"')
echo ""
echo "📝 [站点: $SITE_ID] 安装填空指南"
echo "----------------------------------------------------"
echo "1. 数据库名 (Database Name) : $DB_NAME"
echo "2. 用户名 (Username) : $DB_USER"
echo "3. 密码 (Password) : $DB_PASS"
echo "4. 数据库主机 (Database Host): db <--- ⚠️ 必须填这个,不能填localhost"
echo "5. 表前缀 (Table Prefix) : wp_ (默认即可)"
echo "----------------------------------------------------"
read -p "回车返回..." k
}
# --- 12. 备用机一键上线 ---
func_backup_online() {
clear
echo "🚀 备用机一键上线"; read -p "确认? (y/n): " c; [ "$c" != "y" ] && return
systemctl stop apache2 2>/dev/null; systemctl disable apache2 2>/dev/null
systemctl stop nginx 2>/dev/null; systemctl disable nginx 2>/dev/null
killall apache2 2>/dev/null; killall nginx 2>/dev/null
docker network create web_network 2>/dev/null
find "$BASE_DIR" -maxdepth 2 -name "docker-compose.yml" -not -path "*/scripts/*" -exec docker compose -f {} up -d \;
sleep 5
docker network connect web_network frpc-frpc-1 2>/dev/null
docker network connect web_network proxy-app-1 2>/dev/null
get_site_list | while read id; do
docker network connect web_network "${id}_nginx" 2>/dev/null
docker network connect web_network "${id}_wp_app" 2>/dev/null
done
docker restart proxy-app-1
sleep 2 && docker restart frpc-frpc-1
docker update --restart=always $(docker ps -a -q) 2>/dev/null
echo "✅ 上线完成"; read -p "回车..." k
}
# --- 11. 无法访问修复 ---
func_deep_fix() {
clear
echo "🚑 深度网络修复"; read -p "确认? (y/n): " c; [ "$c" != "y" ] && return
systemctl stop apache2 2>/dev/null; systemctl disable apache2 2>/dev/null
systemctl stop nginx 2>/dev/null; systemctl disable nginx 2>/dev/null
killall apache2 2>/dev/null; killall nginx 2>/dev/null
docker network create web_network 2>/dev/null
docker network connect web_network frpc-frpc-1 2>/dev/null
docker network connect web_network proxy-app-1 2>/dev/null
get_site_list | while read id; do
docker network connect web_network "${id}_nginx" 2>/dev/null
docker network connect web_network "${id}_wp_app" 2>/dev/null
done
docker restart proxy-app-1
sleep 2 && docker restart frpc-frpc-1
docker update --restart=always $(docker ps -a -q) 2>/dev/null
echo "✅ 完成"; read -p "回车..." k
}
# --- 10. 下载资源 ---
func_download_cache() {
mkdir -p "$CACHE_DIR"; clear; echo "📦 缓存管理"; ls -lh "$CACHE_DIR" 2>/dev/null | grep -v '^total' | awk '{print "📄 " $9 " (" $5 ")"}'
read -p "下载新包? (y/n): " c
if [[ "$c" == "y" ]]; then
read -p "URL: " u; [ -n "$u" ] && { d=$(basename "$u"); read -p "文件名 ($d): " n; [ -z "$n" ] && n="$d"; curl -L -# -o "$CACHE_DIR/$n" "$u"; }
fi
read -p "回车..." k
}
# --- 9. 修复权限 (注入Redis优化) ---
func_fix_perms() {
IS_SILENT=$1
[ "$IS_SILENT" != "silent" ] && echo "--- 校准权限 ---"
get_site_list | while read id; do
dir="$BASE_DIR/$id"
if [ -d "$dir/www_root" ]; then
mkdir -p "$dir/www_root/wp-content/upgrade" "$dir/nginx_cache"
chown -R 82:82 "$dir/www_root" 2>/dev/null
chown -R 101:101 "$dir/nginx_cache" 2>/dev/null
find "$dir/www_root" -type d -exec chmod 755 {} \; 2>/dev/null
find "$dir/www_root" -type f -exec chmod 644 {} \; 2>/dev/null
# 当且仅当用户已经手动安装完 WP (存在wp-config.php) 时,才注入Redis配置
if [ -f "$dir/www_root/wp-settings.php" ] && [ -f "$dir/www_root/wp-config.php" ]; then
# 防止重复注入
grep -q "WP_REDIS_HOST" "$dir/www_root/wp-config.php"
if [ $? -ne 0 ]; then
[ "$IS_SILENT" != "silent" ] && echo "💉 正在为 $id 注入 Redis 优化代码..."
sed -i "/wp-settings.php/i define('WP_REDIS_HOST', 'redis');" "$dir/www_root/wp-config.php"
sed -i "/wp-settings.php/i define('WP_REDIS_PORT', 6379);" "$dir/www_root/wp-config.php"
grep -q "FS_METHOD" "$dir/www_root/wp-config.php" || echo "define('FS_METHOD', 'direct');" >> "$dir/www_root/wp-config.php"
fi
fi
fi
done
[ "$IS_SILENT" != "silent" ] && { echo "✅ 完成"; read -p "回车..." k; }
}
# --- 1. 新建站点 (真·原生模式) ---
func_add_site() {
echo "--- 新建站点 (原生模式) ---"
read -p "站点ID: " SITE_ID; [ -z "$SITE_ID" ] && return
SITE_DIR="$BASE_DIR/$SITE_ID"
mkdir -p "$SITE_DIR"/{db_data,redis_data,www_root,nginx_conf,nginx_cache}
mkdir -p "$CACHE_DIR"
if [ -n "$(ls -A "$CACHE_DIR" 2>/dev/null)" ]; then echo "可用缓存:"; ls -1 "$CACHE_DIR" | awk '{print " - " $0}'; fi
read -p "输入文件名 (回车下载WP): " ZIP_NAME
if [ -z "$ZIP_NAME" ]; then
TARGET_ZIP="$CACHE_DIR/latest-zh_CN.zip"
[ ! -f "$TARGET_ZIP" ] && { echo "下载官方WP..."; curl -L -o "$TARGET_ZIP" "https://cn.wordpress.org/latest-zh_CN.zip"; }
else
TARGET_ZIP="$CACHE_DIR/$ZIP_NAME"
if [ ! -f "$TARGET_ZIP" ]; then echo "❌ 文件不存在"; read -p "返回..." k; return; fi
fi
echo "1. 解压文件..."
rm -rf "$SITE_DIR/www_root/"*
unzip -oq "$TARGET_ZIP" -d "$SITE_DIR/temp"
if [ -d "$SITE_DIR/temp/wordpress" ]; then
mv "$SITE_DIR/temp/wordpress/"* "$SITE_DIR/www_root/"
else
mv "$SITE_DIR/temp/"* "$SITE_DIR/www_root/"
fi
rm -rf "$SITE_DIR/temp"
# 智能识别
IS_WP=0
if [ -f "$SITE_DIR/www_root/wp-settings.php" ]; then
IS_WP=1
echo "✅ 识别为 WordPress,准备环境..."
else
echo "ℹ️ 识别为 通用PHP"
fi
DB_PWD=$(openssl rand -hex 12)
cat > "$SITE_DIR/uploads.ini" <<INI
upload_max_filesize = 50M
post_max_size = 50M
memory_limit = 256M
INI
# Nginx 配置
if [ "$IS_WP" -eq 1 ]; then
# WP Nginx
cat > "$SITE_DIR/nginx_conf/nginx.conf" <<EOF_WP_NG
user nginx;
worker_processes auto;
pid /var/run/nginx.pid;
events { worker_connections 4096; multi_accept on; }
http {
fastcgi_cache_key "\$scheme\$request_method\$host\$request_uri";
sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 65;
gzip on; gzip_types text/plain text/css application/javascript application/json image/svg+xml;
fastcgi_cache_path /var/cache/nginx/wordpress levels=1:2 keys_zone=WP:200m inactive=30m max_size=5g use_temp_path=off;
fastcgi_cache_lock on;
map \$http_cookie \$skip_cache { default 0; ~*wordpress_logged_in 1; ~*comment_author 1; ~*wp-postpass 1; ~*woocommerce_items_in_cart 1; }
include /etc/nginx/mime.types; default_type application/octet-stream; client_max_body_size 50M; include /etc/nginx/conf.d/*.conf;
}
EOF_WP_NG
cat > "$SITE_DIR/nginx_conf/default.conf" <<EOF_WP_SD
server {
listen 80; root /var/www/html; index index.php index.html;
location / { try_files \$uri \$uri/ /index.php?\$args; }
location ~ \.php$ {
fastcgi_pass php_fpm:9000; include fastcgi_params; fastcgi_param SCRIPT_FILENAME \$document_root\$fastcgi_script_name;
fastcgi_cache WP; fastcgi_cache_valid 200 1h; fastcgi_cache_bypass \$skip_cache; fastcgi_no_cache \$skip_cache; add_header X-FastCGI-Cache \$upstream_cache_status;
}
}
EOF_WP_SD
else
# 通用 Nginx
cat > "$SITE_DIR/nginx_conf/nginx.conf" <<EOF_GEN_NG
user nginx;
worker_processes auto;
pid /var/run/nginx.pid;
events { worker_connections 4096; }
http {
sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 65;
gzip on; gzip_types text/plain text/css application/javascript application/json image/svg+xml;
include /etc/nginx/mime.types; default_type application/octet-stream; client_max_body_size 50M; include /etc/nginx/conf.d/*.conf;
}
EOF_GEN_NG
cat > "$SITE_DIR/nginx_conf/default.conf" <<EOF_GEN_SD
server {
listen 80; root /var/www/html; index index.php index.html;
location / { try_files \$uri \$uri/ /index.php?\$args; }
location ~ \.php$ {
fastcgi_pass php_fpm:9000; include fastcgi_params; fastcgi_param SCRIPT_FILENAME \$document_root\$fastcgi_script_name;
}
}
EOF_GEN_SD
fi
cat > "$SITE_DIR/docker-compose.yml" <<YML
services:
db:
image: mariadb:10.6
restart: always
environment:
MYSQL_ROOT_PASSWORD: "${DB_PWD}"
MYSQL_DATABASE: "${SITE_ID}"
MYSQL_USER: "${SITE_ID}"
MYSQL_PASSWORD: "${DB_PWD}"
volumes:
- ./db_data:/var/lib/mysql
networks:
- internal_net
redis:
image: redis:alpine
restart: always
networks:
- internal_net
php_fpm:
image: wordpress:fpm-alpine
restart: always
depends_on:
- db
- redis
# ⚠️ 关键修改:删除了 WORDPRESS_DB_... 环境变量
# 这样 WordPress 容器启动时就不会自动生成 wp-config.php
# 用户访问网页时,就会看到“语言选择”和“数据库配置”的原生安装界面
volumes:
- ./www_root:/var/www/html
- ./uploads.ini:/usr/local/etc/php/conf.d/uploads.ini
networks:
- internal_net
nginx:
image: nginx:alpine
container_name: "${SITE_ID}_nginx"
restart: always
volumes:
- ./www_root:/var/www/html
- ./nginx_conf/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx_conf/default.conf:/etc/nginx/conf.d/default.conf:ro
- ./nginx_cache:/var/cache/nginx/wordpress
networks:
- internal_net
- web_network
networks:
web_network:
external: true
internal_net:
driver: bridge
YML
echo "2. 启动容器..."
cd "$SITE_DIR" && docker compose up -d
docker network connect web_network "${SITE_ID}_nginx" 2>/dev/null
# 移除之前的自动配置逻辑,只设置权限
chown -R 82:82 "$SITE_DIR/www_root" 2>/dev/null
find "$SITE_DIR/www_root" -type d -exec chmod 755 {} \; 2>/dev/null
find "$SITE_DIR/www_root" -type f -exec chmod 644 {} \; 2>/dev/null
echo "🔒 锁定自启..."
docker update --restart=always $(docker ps -a -q) 2>/dev/null
echo "----------------------------------------------------"
echo "✅ 站点 $SITE_ID 环境部署成功!(原生安装模式)"
echo "请在浏览器访问您的域名,按提示完成安装。"
echo "----------------------------------------------------"
echo "📝 [安装填空小抄] (请复制)"
echo "数据库名 (DB Name) : $SITE_ID"
echo "用户名 (Username) : $SITE_ID"
echo "密码 (Password) : $DB_PWD"
echo "数据库主机 (Host) : db"
echo "----------------------------------------------------"
read -p "回车返回..." k
}
# --- 功能清单 ---
func_list() { printf "%-18s | %-25s\n" "ID" "DB密码"; get_site_list | while read id; do yml="$BASE_DIR/$id/docker-compose.yml"; [ -f "$yml" ] && printf "%-18s | %-25s\n" "$id" "$(grep 'MYSQL_ROOT_PASSWORD' "$yml" | head -n 1 | sed 's/.*: //; s/\"//g')"; done; read -p "继续..." k; }
func_backup() {
echo "备份中..."
DATE=$(date +%Y%m%d_%H%M)
mkdir -p "$BACKUP_DIR"
get_site_list | while read id; do
cd "$BASE_DIR/$id"
db_con=$(docker compose ps --format '{{.Name}}' db 2>/dev/null)
if [ -n "$db_con" ]; then
root_pw=$(grep 'MYSQL_ROOT_PASSWORD' "docker-compose.yml" | head -n 1 | sed 's/.*: //; s/\"//g')
db_name=$(grep 'MYSQL_DATABASE' "docker-compose.yml" | head -n 1 | sed 's/.*: //; s/\"//g')
[ -z "$db_name" ] && db_name="wordpress"
docker exec "$db_con" mysqldump -uroot -p"$root_pw" "$db_name" > db.sql 2>/dev/null
[ -s db.sql ] && tar -czf "$BACKUP_DIR/${id}_$DATE.tar.gz" -C "$BASE_DIR" "$id"
rm db.sql
echo "✅ $id 备份成功 (DB: $db_name)"
fi
done
read -p "完成..." k
}
func_restart_core() { docker restart proxy-app-1 frpc; read -p "已重启..." k; }
func_delete() { get_site_list; read -p "删除ID: " did; [ -n "$did" ] && [ -d "$BASE_DIR/$did" ] && { cd "$BASE_DIR/$did" && docker compose down -v; cd "$BASE_DIR" && rm -rf "$BASE_DIR/$did"; echo "✅ 删除"; }; read -p "继续..." k; }
func_restore() { ls -1 "$BACKUP_DIR" 2>/dev/null; read -p "文件名: " FN; [ -f "$BACKUP_DIR/$FN" ] && { SID=$(echo "$FN" | cut -d'_' -f1); tar -xzf "$BACKUP_DIR/$FN" -C "$BASE_DIR"; cd "$BASE_DIR/$SID" && docker compose up -d; echo "✅ 恢复"; }; read -p "继续..." k; }
func_update() { get_site_list | while read id; do cd "$BASE_DIR/$id" && docker compose pull && docker compose up -d; done; docker image prune -f; read -p "完成..." k; }
func_monitor() { printf "CPU: %s%% 内存: %s\n" "$(top -bn1 | grep 'Cpu(s)' | awk '{print 100-$8}')" "$(free -m | awk 'NR==2{printf "%.2f%%", $3*100/$2}')"; docker stats --no-stream --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}"; read -p "回车..." k; }
while true; do
clear
echo "----------------------------------------------------"
echo "Docker 运营旗舰版 V16.3 (原生安装·自动清场)"
echo "网关: $(check_status) 穿透: $(check_frp) 上传: 50M"
echo "----------------------------------------------------"
echo "1. 新建站点 2. 站点清单 3. 全站备份"
echo "4. 重启核心 5. 删除站点 6. 一键恢复"
echo "7. 一键更新 8. 性能监控 9. 修复权限"
echo "10.下载资源 11.访问修复 12.备机上线"
echo "13.查看配置 (数据库)"
echo "q. 退出"
echo "----------------------------------------------------"
read -p "指令: " opt
case $opt in
1) func_add_site ;; 2) func_list ;; 3) func_backup ;;
4) func_restart_core ;; 5) func_delete ;; 6) func_restore ;;
7) func_update ;; 8) func_monitor ;; 9) func_fix_perms ;;
10) func_download_cache ;; 11) func_deep_fix ;; 12) func_backup_online ;;
13) func_view_db_config ;;
q) exit 0 ;; *) read -p "无效..." k ;;
esac
done
EOF
chmod +x /data/docker_sites/manage.sh原创文章,作者:开心电脑网,如若转载,请注明出处。