FTP同步和rsync同步是两种不同的文件传输/同步机制,核心区别在于传输策略和差异检测方式:
| 维度 | FTP 同步 | rsync 同步 |
|---|---|---|
| 差异检测 | 无内置差异检测,通常需配合外部工具 | 内置强大的块级差异算法 |
| 扫描方式 | 取决于具体工具,但普遍需要全量扫描 | 首次全量,后续增量(基于校验和) |
| 传输内容 | 通常传输整个文件 | 只传输变化的部分(delta) |
| 方向性 | 单向推送/拉取 | 双向同步支持 |
| 压缩 | 无原生支持 | 传输过程中实时压缩 |
| 断点续传 | 部分客户端支持 | 原生支持 |
| 权限/属性 | 通常不保留 | 完整保留(时间戳、权限、软链接等) |
FTP 是否每次都全部扫描?
传统 FTP 客户端/脚本:是的,通常需要全量扫描。
标准 FTP 协议本身没有"增量同步"概念
常见做法(如 lftp 的 mirror、Python ftplib 脚本)需要:
遍历远程目录列表
对比本地文件(时间戳或大小)
决定是否重新传输
这意味着每次都要读取远程完整文件列表,对大目录开销明显
指定的目录文件与日俱增,rsync没法用只能用ftp,那么有没有办法让它每个月自动换目录挂载,比如现在是7月1日自动创建并挂载7月份的目录,到了8月1日就自动创建和挂载8月份的目录。
按照这个需求可以通过 Docker 的动态挂载切换目录,目的是通过按月分目录来减少每次扫描的文件数量。
方案是修改docker-compose.yml配置指定env参数加定时脚本来实现。
services:
vsftpd:
image: fauria/vsftpd
container_name: vsftpd
restart: unless-stopped
#network_mode: host
ports:
# - "20:20"
- "2121:21" # 控制端口
- "65500-65509:65500-65509" # PASV 端口范围
volumes:
# === 统一时区卷挂载 ===
- /etc/localtime:/etc/localtime:ro
- /usr/share/zoneinfo:/usr/share/zoneinfo:ro
# === 数据存储挂载 ===
- /data/vdb/docker/compose/vsftpd/logs/vsftpd.log:/var/log/vsftpd.log #日志
#- /data/vdb/docker/compose/web/nginx/html/zssjapi/uploads/files:/home/vsftpd/xj_zssj_sync/zssj
# === 动态挂载 ===
- ${CURRENT_MONTH_DIR}:/home/vsftpd/xj_zssj_sync/${CURRENT_YEAR}/${CURRENT_MONTH}:rw
environment:
TZ: Asia/Shanghai
FTP_USER: ${FTP_USER}
FTP_PASS: ${FTP_PASS}
# PASV_ADDRESS: 10.28.4.172 # 宿主机IP
PASV_ADDRESS: 10.10.11.11 # 宿主机能访问到的
PASV_MIN_PORT: 65500
PASV_MAX_PORT: 65509
USER_ID: 1000
GROUP_ID: 1000#!/bin/bash
# /opt/1panel/docker/compose/vsftpd/scripts/ftp-monthly-rotate.sh
set -e
# 配置
COMPOSE_DIR="/opt/1panel/docker/compose/vsftpd"
ENV_FILE="${COMPOSE_DIR}/.env"
FTP_USER="xj_zssj_sync"
FTP_PASS="Xj@zssj123"
BASE_DIR="/data/vdb/docker/compose/web/nginx/html/zssjapi/uploads/files"
# 获取当前小时和分钟(测试用)
# CURRENT_HOUR=$(date +%H)
# CURRENT_MINUTE=$(date +%M)
# CURRENT_MONTH_DIR="${BASE_DIR}/test_${CURRENT_HOUR}/test_${CURRENT_MINUTE}"
# 获取当前年月(月份不带前导零)用于生产
CURRENT_YEAR=$(date +%Y)
CURRENT_MONTH=$(date +%-m | sed 's/^0//')
CURRENT_MONTH_DIR="${BASE_DIR}/${CURRENT_YEAR}/${CURRENT_MONTH}"
# 创建新目录
mkdir -p "${CURRENT_MONTH_DIR}"
chmod 755 "${CURRENT_MONTH_DIR}"
# 创建测试文件
# echo "Created at: $(date '+%Y-%m-%d %H:%M:%S')" > "${CURRENT_MONTH_DIR}/test_${TIMESTAMP}.txt"
# echo "Directory: ${CURRENT_MONTH_DIR}" >> "${CURRENT_MONTH_DIR}/test_${TIMESTAMP}.txt"
# 更新 .env 文件
cat > ${ENV_FILE} << EOF
FTP_USER=${FTP_USER}
FTP_PASS=${FTP_PASS}
CURRENT_YEAR=${CURRENT_YEAR}
CURRENT_MONTH=${CURRENT_MONTH}
CURRENT_MONTH_DIR=${CURRENT_MONTH_DIR}
EOF
# 重启服务
cd ${COMPOSE_DIR}
docker-compose down
docker-compose up -d
echo "$(date '+%Y-%m-%d %H:%M:%S'): FTP 已切换至 ${CURRENT_MONTH_DIR}" >> /data/vdb/docker/compose/vsftpd/logs/ftp-rotate.log
# 1. 备份原配置
cp /opt/1panel/docker/compose/vsftpd/docker-compose.yml /opt/1panel/docker/compose/vsftpd/docker-compose.yml.bak
# 2. 修改 docker-compose.yml(按上面内容修改)
# 3. 创建脚本目录和文件
mkdir -p /opt/1panel/docker/compose/vsftpd/scripts
vi /opt/1panel/docker/compose/vsftpd/scripts/ftp-monthly-rotate.sh
# 粘贴上面脚本内容
# 4. 赋予执行权限
chmod +x /opt/1panel/docker/compose/vsftpd/scripts/ftp-monthly-rotate.sh
# 5. 执行测试
/opt/1panel/docker/compose/vsftpd/scripts/ftp-monthly-rotate.sh
# 6. 验证
docker compose ps
docker inspect vsftpd | grep -A 5 "Mounts"
cat /opt/1panel/docker/compose/vsftpd/.envcrontab -e
# 添加
0 * * * * /opt/1panel/docker/compose/vsftpd/scripts/ftp-monthly-rotate.sh
# 稳定跑后改成每月
# 修改定时任务为每月执行
crontab -e
# 改为
0 2 1 * * /opt/1panel/docker/compose/vsftpd/scripts/ftp-monthly-rotate.sh# 编辑脚本,注释掉测试部分,恢复年月部分
vi /opt/1panel/docker/compose/vsftpd/scripts/ftp-monthly-rotate.sh
# 获取当前年月(月份不带前导零)
CURRENT_YEAR=$(date +%Y)
CURRENT_MONTH=$(date +%-m | sed 's/^0//')
CURRENT_MONTH_DIR="${BASE_DIR}/${CURRENT_YEAR}/${CURRENT_MONTH}"
# 注释掉小时分钟
# CURRENT_HOUR=$(date +%H)
# CURRENT_MINUTE=$(date +%M)
# CURRENT_MONTH_DIR="${BASE_DIR}/test_${CURRENT_HOUR}${CURRENT_MINUTE}"不同机器只需要修改这两处:
volumes:
# === 动态挂载(由.env控制) ===
- ${CURRENT_MONTH_DIR}:/home/vsftpd/xj_tzsj_sync/tzsj:rw
environment:
FTP_USER: ${FTP_USER}
FTP_PASS: ${FTP_PASS}其它都在 .env 里动态变化
脚本每次执行会重新生成 .env:
cat > ${ENV_FILE} << EOF
FTP_USER=${FTP_USER}
FTP_PASS=${FTP_PASS}
CURRENT_HOUR=${CURRENT_HOUR}
CURRENT_MINUTE=${CURRENT_MINUTE}
CURRENT_MONTH_DIR=${CURRENT_MONTH_DIR}
EOF# 获取时间戳(用于文件名)
TIMESTAMP=$(date '+%Y%m%d_%H%M%S')
# 创建新目录
mkdir -p "${CURRENT_MONTH_DIR}"
chmod 755 "${CURRENT_MONTH_DIR}"
# 创建测试文件
echo "Created at: $(date '+%Y-%m-%d %H:%M:%S')" > "${CURRENT_MONTH_DIR}/test_${TIMESTAMP}.txt"
echo "Directory: ${CURRENT_MONTH_DIR}" >> "${CURRENT_MONTH_DIR}/test_${TIMESTAMP}.txt"执行后,目录里会有这样的文件:
/data/.../uploads/files/test_1430/
└── test_20260716_143000.txt#!/bin/bash
# /usr/local/bin/ftp-test-hourly.sh
set -e
HOURLY_BASE="/data/vdb/tmp/hourly_dir"
LINK_PATH="/data/vdb/docker/compose/vsftpd/test"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
NEW_DIR="${HOURLY_BASE}/${TIMESTAMP}"
# 创建新目录
mkdir -p "${NEW_DIR}"
chmod 755 "${NEW_DIR}"
# 创建测试文件
echo "Created at: $(date '+%Y-%m-%d %H:%M:%S')" > "${NEW_DIR}/test_${TIMESTAMP}.txt"
echo "Directory: ${NEW_DIR}" >> "${NEW_DIR}/test_${TIMESTAMP}.txt"
# 更新符号链接
if [ -e "${LINK_PATH}" ] && [ ! -L "${LINK_PATH}" ]; then
BACKUP="${LINK_PATH}.backup.$(date +%s)"
mv "${LINK_PATH}" "${BACKUP}"
echo "已备份旧目录到 ${BACKUP}"
fi
ln -sfn "${NEW_DIR}" "${LINK_PATH}"
# 重启容器使新挂载生效
docker restart vsftpd
# 验证容器内内容
echo "=== 容器内验证 ==="
docker exec vsftpd ls -la /home/vsftpd/xj_tzsj_sync/test 2>/dev/null || echo "容器验证失败"
# 记录日志
echo "$(date '+%Y-%m-%d %H:%M:%S'): 链接已指向 ${NEW_DIR},容器已重启" >> /data/vdb/tmp/ftp-test.log
# 输出状态
echo "=== 宿主机状态 ==="
ls -la "$(dirname ${LINK_PATH})" | grep test
echo "链接指向: $(readlink ${LINK_PATH})"
ls -la "${LINK_PATH}/"{/collapse-item}
评论 (0)