本文目录导读:

我来介绍几种同步配置文件的脚本方案,从简单到复杂:
基础版:使用 rsync 同步
简单同步脚本
#!/bin/bash
# sync_config.sh - 基础配置文件同步
# 配置来源和目的地
SOURCE_DIR="/etc/nginx/"
DEST_DIR="/backup/nginx-config/"
LOG_FILE="/var/log/config-sync.log"
# 创建日志函数
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$LOG_FILE"
}
# 执行同步
log "开始同步配置文件..."
rsync -avz --delete "$SOURCE_DIR" "$DEST_DIR"
if [ $? -eq 0 ]; then
log "同步成功完成"
else
log "同步失败!"
exit 1
fi
进阶版:带备份和校验的同步
#!/bin/bash
# sync_with_backup.sh - 带备份的配置文件同步
set -e # 出错立即退出
# 配置变量
CONFIG_FILES=(
"/etc/nginx/nginx.conf"
"/etc/nginx/sites-available/"
"/etc/php/8.1/fpm/php.ini"
"/etc/mysql/my.cnf"
)
BACKUP_ROOT="/var/backups/configs"
BACKUP_DIR="$BACKUP_ROOT/$(date +%Y%m%d_%H%M%S)"
REMOTE_HOST="user@remote-server"
REMOTE_PATH="/backup/configs/"
# 创建备份目录
mkdir -p "$BACKUP_DIR"
# 校验函数
verify_files() {
for file in "${CONFIG_FILES[@]}"; do
if [ ! -e "$file" ]; then
echo "警告: $file 不存在!"
return 1
fi
done
}
# 备份当前配置
backup_configs() {
echo "开始备份..."
for file in "${CONFIG_FILES[@]}"; do
cp -r "$file" "$BACKUP_DIR/"
done
tar -czf "$BACKUP_ROOT/configs_$(date +%Y%m%d).tar.gz" "$BACKUP_DIR"
echo "本地备份完成: $BACKUP_DIR"
}
# 同步到远程
sync_to_remote() {
echo "同步到远程服务器..."
rsync -avz --progress \
--exclude='*.log' \
--exclude='cache/' \
-e ssh "$BACKUP_DIR/" "$REMOTE_HOST:$REMOTE_PATH"
}
# 主流程
main() {
verify_files || exit 1
backup_configs
sync_to_remote
# 保存最近5份备份,清理旧的
cd "$BACKUP_ROOT" && ls -t *.tar.gz | tail -n +6 | xargs -r rm -f
echo "同步完成!"
}
main
高级版:Git 版本控制同步
#!/usr/bin/env python3
# sync_config.py - 使用Git进行配置文件版本控制
import os
import subprocess
import shutil
import yaml
from datetime import datetime
from pathlib import Path
class ConfigSync:
def __init__(self, config_file='sync_config.yaml'):
self.load_config(config_file)
def load_config(self, config_file):
"""加载配置文件"""
with open(config_file, 'r') as f:
self.config = yaml.safe_load(f)
self.git_repo = self.config.get('git_repo', '/etc/config-repo')
self.apps = self.config.get('apps', [])
def setup_git(self):
"""初始化Git仓库"""
Path(self.git_repo).mkdir(parents=True, exist_ok=True)
os.chdir(self.git_repo)
if not os.path.exists('.git'):
subprocess.run(['git', 'init'], check=True)
subprocess.run(['git', 'config', 'user.name', 'Config Sync'], check=True)
subprocess.run(['git', 'config', 'user.email', 'sync@localhost'], check=True)
def collect_configs(self):
"""收集所有配置文件到Git仓库"""
for app in self.apps:
src = app['source']
dest = os.path.join(self.git_repo, app['name'])
# 确保目标目录存在
Path(dest).mkdir(parents=True, exist_ok=True)
# 复制配置文件
if os.path.isdir(src):
shutil.copytree(src, dest, dirs_exist_ok=True)
elif os.path.isfile(src):
shutil.copy2(src, os.path.join(dest, os.path.basename(src)))
print(f"收集配置: {app['name']} <- {src}")
def commit_changes(self):
"""提交变更"""
os.chdir(self.git_repo)
# 检查是否有变更
result = subprocess.run(['git', 'status', '--porcelain'],
capture_output=True, text=True)
if result.stdout.strip():
# 添加所有变更
subprocess.run(['git', 'add', '.'], check=True)
# 提交
commit_msg = f"更新配置 - {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
subprocess.run(['git', 'commit', '-m', commit_msg], check=True)
print(f"提交成功: {commit_msg}")
return True
else:
print("没有配置变更")
return False
def push_to_remote(self):
"""推送到远程仓库"""
remote_url = self.config.get('remote_url')
if remote_url:
os.chdir(self.git_repo)
subprocess.run(['git', 'push', 'origin', 'main'], check=True)
print("已推送到远程仓库")
def restore_config(self, commit_hash=None):
"""从Git恢复配置"""
os.chdir(self.git_repo)
if commit_hash:
subprocess.run(['git', 'checkout', commit_hash, '--', '.'], check=True)
print(f"已恢复配置到提交 {commit_hash}")
else:
subprocess.run(['git', 'checkout', 'HEAD', '--', '.'], check=True)
print("已恢复到最新配置")
def run(self, action='sync'):
"""主执行入口"""
self.setup_git()
if action == 'sync':
self.collect_configs()
if self.commit_changes():
if self.config.get('auto_push', False):
self.push_to_remote()
elif action == 'restore':
commit_hash = input("输入要恢复的提交哈希(留空为最新): ").strip() or None
self.restore_config(commit_hash)
elif action == 'history':
os.chdir(self.git_repo)
result = subprocess.run(['git', 'log', '--oneline', '-10'],
capture_output=True, text=True)
print("最近的提交:")
print(result.stdout)
# 示例配置文件 sync_config.yaml
"""
git_repo: /etc/config-repo
remote_url: git@github.com:user/server-configs.git
auto_push: true
apps:
- name: nginx
source: /etc/nginx/conf.d/
- name: php
source: /etc/php/8.1/fpm/php.ini
- name: mysql
source: /etc/mysql/my.cnf
"""
# 命令行入口
if __name__ == '__main__':
import sys
sync = ConfigSync()
if len(sys.argv) > 1:
action = sys.argv[1]
else:
action = 'sync'
sync.run(action)
守护进程版:实时监控同步
#!/usr/bin/env python3
# file_sync_daemon.py - 文件监控实时同步
import time
import hashlib
import os
import shutil
import json
from pathlib import Path
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
class ConfigSyncHandler(FileSystemEventHandler):
"""文件系统事件处理器"""
def __init__(self, mappings, backup_dir):
self.mappings = mappings # [{src: dest}]
self.backup_dir = backup_dir
self.pending_changes = {}
self.last_sync = time.time()
def on_modified(self, event):
"""文件修改事件"""
if event.is_directory:
return
self.handle_change(event.src_path, 'modified')
def on_created(self, event):
"""文件创建事件"""
if event.is_directory:
return
self.handle_change(event.src_path, 'created')
def on_deleted(self, event):
"""文件删除事件"""
if event.is_directory:
return
self.handle_change(event.src_path, 'deleted')
def handle_change(self, filepath, action):
"""处理文件变更"""
# 检查是否在监控列表中
for mapping in self.mappings:
src_dir = mapping['source']
if filepath.startswith(src_dir):
# 计算相对路径
rel_path = os.path.relpath(filepath, src_dir)
dest_path = os.path.join(mapping['dest'], rel_path)
# 记录变更
self.pending_changes[filepath] = {
'action': action,
'dest': dest_path
}
# 如果有待处理的变更且超过收集时间,执行同步
current_time = time.time()
if current_time - self.last_sync > 5: # 5秒延迟
self.sync_pending()
break
def sync_pending(self):
"""执行待处理的同步"""
if not self.pending_changes:
return
print(f"同步 {len(self.pending_changes)} 个文件变更...")
for src, change in self.pending_changes.items():
try:
dest = change['dest']
action = change['action']
# 创建备份
self.create_backup(src, dest)
# 执行同步
if action == 'deleted':
if os.path.exists(dest):
os.remove(dest)
print(f"删除: {dest}")
else:
# 确保目录存在
os.makedirs(os.path.dirname(dest), exist_ok=True)
shutil.copy2(src, dest)
print(f"同步: {src} -> {dest}")
except Exception as e:
print(f"同步失败 {src}: {e}")
self.log_error(src, str(e))
self.pending_changes = {}
self.last_sync = time.time()
def create_backup(self, src, dest):
"""创建文件备份"""
timestamp = time.strftime('%Y%m%d_%H%M%S')
backup_path = os.path.join(self.backup_dir,
f"backup_{timestamp}_{os.path.basename(dest)}")
if os.path.exists(dest):
shutil.copy2(dest, backup_path)
def log_error(self, src, error_msg):
"""记录错误"""
log_file = os.path.join(self.backup_dir, 'sync_errors.log')
with open(log_file, 'a') as f:
f.write(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] {src}: {error_msg}\n")
class ConfigDaemon:
"""配置同步守护进程"""
def __init__(self, config_file):
self.load_config(config_file)
def load_config(self, config_file):
"""加载配置"""
with open(config_file, 'r') as f:
self.config = json.load(f)
self.mappings = self.config.get('mappings', [])
self.backup_dir = self.config.get('backup_dir', '/var/backups/configs')
self.log_dir = self.config.get('log_dir', '/var/log/config-sync')
# 确保目录存在
Path(self.backup_dir).mkdir(parents=True, exist_ok=True)
Path(self.log_dir).mkdir(parents=True, exist_ok=True)
def start(self):
"""启动监控"""
event_handler = ConfigSyncHandler(self.mappings, self.backup_dir)
observers = []
# 为每个源目录创建观察者
for mapping in self.mappings:
src_dir = mapping['source']
observer = Observer()
observer.schedule(event_handler, src_dir, recursive=True)
observer.start()
observers.append(observer)
print(f"开始监控: {src_dir}")
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
for observer in observers:
observer.stop()
print("\n停止监控")
for observer in observers:
observer.join()
def run_once(self):
"""执行一次性同步"""
print("执行一次性同步...")
for mapping in self.mappings:
src = mapping['source']
dest = mapping['dest']
if os.path.isdir(src):
shutil.copytree(src, dest, dirs_exist_ok=True)
elif os.path.isfile(src):
os.makedirs(os.path.dirname(dest), exist_ok=True)
shutil.copy2(src, dest)
print(f"同步: {src} -> {dest}")
# 配置示例 config_sync.json
"""
{
"mappings": [
{
"source": "/etc/nginx/",
"dest": "/backup/nginx/"
},
{
"source": "/etc/php/8.1/fpm/",
"dest": "/backup/php-fpm/"
}
],
"backup_dir": "/var/backups/configs",
"log_dir": "/var/log/config-sync"
}
"""
if __name__ == '__main__':
import sys
daemon = ConfigDaemon('config_sync.json')
if len(sys.argv) > 1 and sys.argv[1] == 'run':
daemon.run_once()
else:
daemon.start()
使用方法
安装依赖
# 对于Python脚本 pip install watchdog pyyaml # 对于Shell脚本 sudo apt-get install rsync # Ubuntu/Debian sudo yum install rsync # CentOS/RHEL
创建系统服务(可选)
# /etc/systemd/system/config-sync.service [Unit] Description=Config File Sync Service After=network.target [Service] Type=simple User=root ExecStart=/usr/local/bin/config-sync.py Restart=always RestartSec=10 [Install] WantedBy=multi-user.target
定时任务(cron)
# 每天凌晨2点同步配置 0 2 * * * /usr/local/bin/sync_config.sh # 每小时同步并记录日志 0 * * * * /usr/local/bin/sync_config.sh >> /var/log/config-sync.log 2>&1
推荐使用场景
- 简单场景:使用 rsync 脚本
- 需要版本控制:使用 Git 版本控制方案
- 需要实时同步:使用守护进程监控方案
- 多服务器部署:可以使用集群配置管理工具如 Ansible
这些脚本可以根据实际需求进行调整和组合,记得定期测试备份恢复!