飞牛NFS挂载磁盘完成不同系统PT下载

60

如题,为什么需要这样操作?

MoviePilot+Emby+qbittorrent组合完成全自动下载协同工作

在我的环境里面, MoviePilot和Emby需要科学网络环境,而多数PT站点会将梯子环境内的下载器识别为盒子,而盒子是无法享受某些福利的

所以我们既要qbittorrent正常保种/下载,又要保证MP和EmbyServer的正常使用.

可以在MP启动时增加代理参数,解决MP的科学需求. 但EmbyServer容器增加代理参数无法正常使用,或者说能实现但很麻烦.

所以如果你的是群晖或者飞牛, 把三个容器都部署在同一台机器上面,对网络的分流设置可能比较麻烦.那可以选择用双系统组合运行的方式来完成配置:

飞牛或者其他任意系统运行qbittorrent, 挂载NFS文件夹.主力存储设备或者主程序宿主设备挂梯子.

具体步骤:
飞牛OS:

#####创建脚本:
nano /usr/local/bin/mount_qb_nfs.sh


######################脚本内容###########

#!/bin/bash

# NFS自动挂载脚本 - 改进版
# 功能:支持多个挂载点、服务检查、内容验证、日志记录

# 日志文件
LOG_FILE="/var/log/mount-nfs-retry.log"

# 挂载配置项:NFS服务器 远程路径 本地挂载点 验证文件夹(可选)
declare -a NFS_ENTRIES=(
    "10.2.0.252 /volume3/Media /vol02/Media"
    "10.2.0.252 /volume4/QB_temp /vol02/qb_temp"
    # "10.3.0.252 /volume1/Media /vol02/media Movies"
)

# 日志函数
log() {
    echo "$(date '+%Y-%m-%d %H:%M:%S'): $1" | tee -a "$LOG_FILE"
}

# 检查NFS服务是否可用
check_nfs_server() {
    local server=$1
    if showmount -e "$server" &>/dev/null; then
        return 0
    else
        return 1
    fi
}

# 验证挂载内容
verify_mount() {
    local mount_point=$1
    local check_folder=$2
    
    # 如果没有指定验证文件夹,跳过验证
    [ -z "$check_folder" ] && return 0
    
    if [ -d "$mount_point/$check_folder" ]; then
        log "验证成功!找到 $check_folder 文件夹"
        return 0
    else
        log "警告!未找到 $check_folder 文件夹,挂载可能异常"
        return 1
    fi
}

# 尝试挂载单个NFS路径
mount_nfs_entry() {
    local entry=$1
    read -r server remote_path mount_point check_folder <<< "$entry"
    
    # 如果验证文件夹为0,则视为未指定
    [ "$check_folder" = "0" ] && check_folder=""
    
    log "处理挂载项: $server:$remote_path -> $mount_point"
    
    # 检查NFS服务可用性
    if ! check_nfs_server "$server"; then
        log "NFS服务 [$server] 未就绪,跳过"
        return 1
    fi
    
    # 创建挂载点目录
    mkdir -p "$mount_point"
    
    # 如果已经挂载,检查是否正常
    if mountpoint -q "$mount_point"; then
        log "[$mount_point] 已挂载,验证挂载内容..."
        if verify_mount "$mount_point" "$check_folder"; then
            log "[$mount_point] 挂载正常,验证通过"
            return 0
        else
            log "[$mount_point] 挂载异常,强制卸载..."
            umount -f "$mount_point" 2>/dev/null
            sleep 2
        fi
    fi
    
    # 如果已有挂载记录但实际未挂载,先清理
    mount | grep -q "$mount_point" && {
        log "[$mount_point] 清理残留挂载记录..."
        umount -f "$mount_point" 2>/dev/null
        sleep 2
    }
    
    # 尝试挂载
    log "尝试挂载 $server:$remote_path 到 $mount_point"
    if mount -t nfs4 "${server}:${remote_path}" "$mount_point" &>> "$LOG_FILE"; then
        sleep 2
        if mountpoint -q "$mount_point"; then
            log "挂载成功 [$mount_point]"
            if verify_mount "$mount_point" "$check_folder"; then
                log "[$mount_point] 验证通过,挂载正常"
                return 0
            else
                log "[$mount_point] 挂载成功但验证失败,卸载重试..."
                umount -f "$mount_point" 2>/dev/null
                return 1
            fi
        else
            log "挂载失败 [$mount_point] - mountpoint检查未通过"
            return 1
        fi
    else
        log "挂载失败 [$mount_point] - mount命令失败"
        return 1
    fi
}

# 主函数
main() {
    log "========== NFS自动挂载开始 =========="
    log "配置的挂载项数量: ${#NFS_ENTRIES[@]}"
    
    local retry_interval=10
    local max_retries=3
    
    for entry in "${NFS_ENTRIES[@]}"; do
        # 跳过空行和注释行
        [[ "$entry" =~ ^[[:space:]]*$ ]] && continue
        [[ "$entry" =~ ^[[:space:]]*# ]] && continue
        
        local attempt=1
        local success=false
        
        while [ $attempt -le $max_retries ]; do
            log "========== 尝试 $attempt/$max_retries =========="
            
            if mount_nfs_entry "$entry"; then
                success=true
                break
            fi
            
            if [ $attempt -lt $max_retries ]; then
                log "等待 ${retry_interval} 秒后重试..."
                sleep $retry_interval
            fi
            
            ((attempt++))
        done
        
        if [ "$success" = true ]; then
            log "挂载项处理成功 ✓"
        else
            log "挂载项处理失败 ✗ - 已达到最大重试次数"
        fi
        
        sleep 1
    done
    
    log "========== NFS自动挂载结束 =========="
}

# 运行主函数
main

exit 0


##########授权执行:
chmod +x /usr/local/bin/mount_qb_nfs.sh

创建服务:


nano /etc/systemd/system/mount-qb-nfs.service

##########

[Unit]
Description=Mount NFS from 10.0.0.252:/volume1/QBdownload
After=network-online.target
Wants=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/local/bin/mount_qb_nfs.sh
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target
########持久化/开机自动运行

systemctl daemon-reload

systemctl enable mount-qb-nfs.service

systemctl start mount-qb-nfs.service

检查运行状态:

systemctl status mount-qb-nfs.service

创建docker自启脚本:

nano /usr/local/bin/wait-nfs-and-start-qbit.sh

########自启docker容器脚本:

#!/bin/bash

# NFS挂载验证并启动 qBittorrent 容器脚本
# 功能:等待NFS挂载成功且验证文件夹存在,容器内也能访问后启动容器

# 配置参数
INITIAL_WAIT=10                    # 初始等待时间(秒)
RETRY_INTERVAL=10                  # 重试间隔(秒)
MAX_RETRIES=60                     # 最大重试次数(0表示无限重试)
MOUNT_POINT="/vol02/Media"          # NFS挂载点
REQUIRED_DIR="$MOUNT_POINT/Movies"  # 需要验证的文件夹
QBIT_CONTAINER_NAME="qbittorrent"   # qBittorrent 容器名称
WORKDIR="/vol1/1000/docker/docker_compose/qBittorrent"  # docker compose 工作目录

# 日志文件
LOG_FILE="/var/log/nfs-qbit-startup.log"

# 日志函数
log() {
    local message="$(date '+%Y-%m-%d %H:%M:%S'): $1"
    echo "$message"
    echo "$message" >> "$LOG_FILE"
}

log "========== 开始 NFS 挂载验证和容器启动流程 =========="

# 等待 NFS 挂载服务完成
log "等待 ${INITIAL_WAIT} 秒,让 NFS 挂载服务开始工作..."
sleep "$INITIAL_WAIT"

# 检查挂载点目录是否存在
if [ ! -d "$MOUNT_POINT" ]; then
    log "警告:挂载点目录 $MOUNT_POINT 不存在,创建中..."
    mkdir -p "$MOUNT_POINT" || {
        log "错误:无法创建挂载点目录"
        exit 1
    }
fi

log "开始检测 NFS 挂载点: $MOUNT_POINT"
log "需要验证的文件夹: $REQUIRED_DIR"

# 重试计数器
attempt=0

# 主循环:等待挂载和验证
while [ $MAX_RETRIES -eq 0 ] || [ $attempt -lt $MAX_RETRIES ]; do
    ((attempt++))
    
    # 1. 检查宿主机挂载点
    if mountpoint -q "$MOUNT_POINT" 2>/dev/null; then
        log "[宿主] 挂载点已挂载 (尝试 $attempt)"
        
        # 2. 检查 Movies 文件夹是否存在
        if [ -d "$REQUIRED_DIR" ]; then
            log "[宿主] $REQUIRED_DIR 文件夹存在"
            
            # 3. 检查容器的运行状态
            if [ "$(docker ps -q -f name=^/${QBIT_CONTAINER_NAME}$ 2>/dev/null)" ]; then
                log "容器 $QBIT_CONTAINER_NAME 已在运行"
                
                # 4. 用临时容器检测容器内能否看到 Movies 文件夹
                log "使用临时容器验证容器内 NFS 可见性..."
                if docker run --rm \
                    -v "$MOUNT_POINT:/media" \
                    --entrypoint /bin/sh \
                    busybox:latest -c "[ -d /media/Movies ] && exit 0 || exit 1" 2>> "$LOG_FILE"; then
                    
                    log "验证成功!容器内可以访问 NFS 内容"
                    log "========== NFS 验证完成,容器已就绪 =========="
                    exit 0
                else
                    log "警告:容器内无法访问到 NFS 内容,${RETRY_INTERVAL}秒后重试..."
                    sleep "$RETRY_INTERVAL"
                    continue
                fi
            else
                log "容器未运行,准备启动..."
                
                # 尝试启动容器
                if [ -d "$WORKDIR" ] && [ -f "$WORKDIR/docker-compose.yml" ]; then
                    log "使用 docker-compose 启动: $WORKDIR"
                    cd "$WORKDIR" && \
                    docker compose up -d &>> "$LOG_FILE" && \
                    log "Docker Compose 启动命令已执行" || log "Docker Compose 启动失败"
                    
                    # 等待容器启动
                    sleep 5
                else
                    log "警告:未找到 docker-compose.yml,尝试启动已有容器"
                    if docker start "$QBIT_CONTAINER_NAME" &>> "$LOG_FILE"; then
                        log "容器 $QBIT_CONTAINER_NAME 启动成功"
                        sleep 5
                    else
                        log "容器 $QBIT_CONTAINER_NAME 启动失败"
                        sleep "$RETRY_INTERVAL"
                        continue
                    fi
                fi
                
                # 验证容器是否真的启动了
                if ! [ "$(docker ps -q -f name=^/${QBIT_CONTAINER_NAME}$ 2>/dev/null)" ]; then
                    log "容器启动失败,${RETRY_INTERVAL}秒后重试..."
                    sleep "$RETRY_INTERVAL"
                fi
            fi
        else
            log "[宿主] $REQUIRED_DIR 文件夹不存在 (尝试 $attempt),${RETRY_INTERVAL}秒后重试..."
            sleep "$RETRY_INTERVAL"
        fi
    else
        log "[宿主] 挂载点未挂载 (尝试 $attempt),${RETRY_INTERVAL}秒后重试..."
        sleep "$RETRY_INTERVAL"
    fi
done

log "错误:达到最大重试次数 ($MAX_RETRIES),退出"
exit 1


##########授权执行:
chmod +x /usr/local/bin/wait-nfs-and-start-qbit.sh

创建服务:

nano /etc/systemd/system/start-qbit-after-nfs.service
#######

[Unit]
Description=Wait for NFS Mount and Start qBittorrent Container
After=mount-qb-nfs.service network.target docker.service
Requires=mount-qb-nfs.service
Wants=docker.service
PartOf=docker.service

[Service]
Type=oneshot
ExecStart=/usr/local/bin/wait-nfs-and-start-qbit.sh
RemainAfterExit=yes
StandardOutput=journal
StandardError=journal
SyslogIdentifier=qbit-nfs-starter

Environment="PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"

TimeoutStartSec=600
TimeoutStopSec=120

[Install]
WantedBy=multi-user.target





systemctl daemon-reload

systemctl enable start-qbit-after-nfs.service

systemctl start start-qbit-after-nfs.service

检查运行状态:

systemctl status start-qbit-after-nfs.service