实时同步工具-lsyncd
2024-08-05 11:35:25 阿炯

Lsyncd (Live Syncing Daemon: Mirror) 轻量级本地与远程实时同步解决方案,采用C语言开发并使用Lua做为其配置文件,采用GPLv2协议授权使用。

Lsyncd watches a local directory trees event monitor interface (inotify or fsevents). It aggregates and combines events for a few seconds and then spawns one (or more) process(es) to synchronize the changes. By default this is rsync. Lsyncd is thus a light-weight live mirror solution that is comparatively easy to install not requiring new filesystems or block devices and does not hamper local filesystem performance.


Lsyncd 使用文件系统事件接口(inotify 或 fsevents)来监视本地文件和目录的更改,并在短时内汇总合并事件,然后启动一个或多个进程将更改同步到远程文件系统,默认同步方式是 rsync 模式。

其安装相对简单,对系统本身没有太多要求,也不会影响本地文件系统的性能。作为 rsync 的替代方案,lsyncd 可以通过 rsync+ssh 模式提高同步效率,例如,重命名文件/目录或者移动位置(普通 rsync 通过删除旧文件然后重新传输整个文件来执行移动)。v2.2.1版本之后需要rsync v3.1之上的版本。

优点:
实时同步(单向):通过监控文件系统事件(inotify 或 fsevents),可以在文件变化时触发同步;
基于 rsync:使用 rsync 进行实际的文件传输,继承 rsync 高效性和灵活性;
自动化:精心配置过的 lsyncd 可以自动监控和同步文件变化;
灵活性:通过 Lua 脚本配置,定制同步行为;

缺点:
复杂性:配置文件高级用法可能对刚接触用户来说有些复杂;
资源消耗:实时监控和同步可能会消耗一定的系统资源,特别是在有大量文件变化的情况下;
延迟问题:尽管是实时同步,但对于非常频繁的变化,可能会有轻微的延迟;

使用场景:
日志文件同步:在需要实时同步日志文件到远程服务器进行分析时,lsyncd 非常有用;
实时备份:适用于需要实时备份重要数据的场景;
Web 服务器文件同步:在多台 Web 服务器之间同步静态文件,确保所有服务器上的文件保持一致;
    
安装

Lsyncd 可在 Fedora、Debian 和 Ubuntu 的官方软件仓库中找到,例如:在 Debian 及其衍生版本上进行安装,可以使用 apt:
apt install lsyncd

要在 Fedora 上安装 lsyncd,可以使用:
dnf install lsyncd

lsyncd 不是 RHEL/CentOS 7 默认仓库的一部分,因此必须使用 EPEL 仓库安装它,可以使用以下命令安装 epel-repo:
# yum -y install epel-release

安装 lsyncd:
# yum -y install lsyncd

配置

lsyncd 使用 Lua 配置文件,通常位于 /etc/lsyncd/lsyncd.conf.lua,也可以通过以下方式查看路径:
查看:systemctl status lsyncd

# systemctl status lsyncd
● lsyncd.service - LSB: lsyncd daemon init script
 Loaded: loaded (/etc/init.d/lsyncd; generated)
 Active: active (exited) since Tue 2024-07-30 00:31:05 CST; 23h ago
... ...     

查看:/etc/init.d/lsyncd
... ...
CONFIG=/etc/lsyncd/lsyncd.conf.lua
... ...

默认配置路径:/etc/lsyncd/lsyncd.conf.lua,如果不存在则新建,或者从 /usr/share/doc/lsyncd*/examples 提供的示例中拷贝一份进行修改。

官方示例:
# ls /usr/share/doc/lsyncd*/examples
lalarm.lua  lbash.lua  lecho.lua  lftp.lua  lgforce.lua  limagemagic.lua  lpostcmd.lua  lrsync.lua  lrsyncssh.lua  ls3.lua.gz  lsayirc.lua

例如下面配置:
settings {
   logfile    = "/var/log/lsyncd/lsyncd.log", -- 指定日志文件路径
   statusFile = "/var/log/lsyncd-status.log", -- 指定状态文件路径
   statusInterval = 20, -- 每 20 秒写入一次状态文件
   nodaemon   = true  -- 守护进程模式运行
}

lsyncd 的配置文件是用 Lua 脚本编写的,支持高度的自定义。以下是配置文件中的主要选项:

(1) settings 表
settings 表用于定义全局设置。

settings {
   logfile    = "/var/log/lsyncd/lsyncd.log", -- 指定日志文件路径
   statusFile = "/var/log/lsyncd-status.log", -- 指定状态文件路径
   inotifyMode = "CloseWrite or Modify", -- 指定 inotify 事件模式
   statusInterval = 20, -- 每 20 秒写入一次状态文件
   nodaemon = true  -- 守护进程模式运行
}

logfile: 日志文件路径,用于记录 lsyncd 的运行日志;
logfile = "/var/log/lsyncd/lsyncd.log",

statusFile: 状态文件路径,用于记录 lsyncd 的状态信息;
statusFile = "/var/log/lsyncd/lsyncd.status",

statusInterval: 设置写入状态文件的间隔时间,以秒为单位,默认 10s;
statusInterval = 20,

nodaemon:设置 lsyncd 以守护进程模式运行 true/false;
nodaemon = true,

inotifyMode: 指定 inotify 事件模式;
inotifyMode = "CloseWrite or Modify"

maxProcesses:限制并发的进程数;
maxProcesses = 1

delay:设置同步的延迟时间(秒);
delay = 15

(2) sync 表
sync 表用于定义同步任务。
default(必需)
指定要使用的同步模式,常用的同步模式有 default.rsync 和 default.direct。
default.rsync
default.rsync: 使用 rsync 进行同步;
default.direct: 直接复制文件,不使用 rsync;

source(必需)
定义源目录的路径。
source = "/path/to/source"

target(必需)
定义目标目录的路径,可以是本地路径或远程路径。
target = "/path/to/target"

delay
设置同步延迟时间(以秒为单位),表示在检测到文件更改后等待多长时间再进行同步。
delay = 10

exclude
指定要排除的文件或目录的模式列表。
exclude = { "*.tmp", "*.log", "cache/", "temp/" }

excludeFrom
指定一个包含要排除文件和目录列表的文件路径。
excludeFrom = "/path/to/exclude-list.txt"

rsync
指定 rsync 相关的配置参数。

rsync = {
binary = "/usr/bin/rsync",  -- rsync 可执行文件路径
archive = true, -- 归档模式(保留权限、时间戳等)
compress = false, -- 不压缩数据
delete = true, -- 删除目标目录中不在源目录中的文件
verbose = true -- 显示详细输出
}

init
初始化同步时执行的脚本。
init = function(event)
os.execute("sh /path/to/scripts/init_script.sh")
return false  -- 阻止默认的 init 同步
end

collect
在收集完所有变化但尚未开始同步操作时执行的脚本。
collect = function(event)
os.execute("sh /path/to/scripts/collect_sync.sh")
end

action
定义在执行同步操作时的自定义行为。
action = function(inlet)
local result = Rsync(inlet, true)  -- 执行 rsync 操作
os.execute("sh /path/to/scripts/post_sync.sh")
return result
end

完整示例

settings {
logfile    = "/var/log/lsyncd/lsyncd.log",
statusFile = "/var/log/lsyncd/lsyncd.status",
statusInterval = 10
}

sync {
default.rsync,
source = "/path/to/source",
target = "/path/to/target",
delay = 10,
exclude = { "*.tmp", "*.log", "cache/", "temp/" },
excludeFrom = "/path/to/exclude-list.txt",
rsync = {
binary = "/usr/bin/rsync",  -- rsync 可执行文件路径
archive = true,             -- 归档模式(保留权限、时间戳等)
compress = false,           -- 不压缩数据
delete = true,              -- 删除目标目录中不在源目录中的文件
verbose = true              -- 显示详细输出
},
init = function(event)
os.execute("sh /path/to/scripts/init_script.sh")
return false  -- 阻止默认的 init 同步
end,
collect = function(event)
os.execute("sh /path/to/scripts/collect_sync.sh")
end,
action = function(inlet)
local result = Rsync(inlet, true)  -- 执行 rsync 操作
os.execute("sh /path/to/scripts/post_sync.sh")
return result
end
}

更多设置,请查看 lysyncd 官方页面。

启动和管理 lsyncd 服务

启动 lsyncd:
systemctl start lsyncd

开机自启:
systemctl enable lsyncd

检查状态:
systemctl status lsyncd

查看日志:
配置文件配置,默认 Linux 在 syslog 打印日志;
tail -f /var/log/lsyncd/lsyncd.log

lsyncd 示例

1、命令行:实时同步目录&文件

1.1、运行时参数说明
-help: 显示帮助信息
lsyncd -help

-version: 显示 lsyncd 版本信息
lsyncd -version

-nodaemon: 以非守护进程模式运行 lsyncd,便于调试
lsyncd -nodaemon /path/to/config.lua

-log Exec, -log Minimal: 设置日志级别
lsyncd -log Exec /path/to/config.lua
lsyncd -log Minimal /path/to/config.lua

-rsync [source] [target]: 直接从命令行启动 lsyncd,使用 rsync 模式同步源和目标目录

lsyncd -rsync /source/ /target/

1.2、用例说明
示例环境:
# lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 20.04.6 LTS
Release:        20.04
Codename:       focal

使用 lsyncd rsync 同步 localhost 上的目录:
-rsync [source] [target]: 直接从命令行启动 lsyncd,并使用 rsync 模式同步源和目标目录。

# ls
source  target

# lsyncd -rsync /home/workspace/linux/source/ /home/workspace/linux/target/
11:20:35 Normal: --- Startup, daemonizing ---

# ps -aux | grep lsyncd
root      903394  0.0  0.0   4508  2152 ?        Ss   16:57   0:00 lsyncd -rsync /home/workspace/linux/source/ /home/workspace/linux/target

# echo 'Hello world!!!' > ./source/freeoa.txt
# echo '========' >> ./source/freeoa.txt

注意:
source & target 需要配置绝对路径;

默认通过 syslog 输出日志;

lsyncd 进程进入后台,作为守护进程运行,也可以 添加 -nodaemon 参数,以非守护进程模式运行 lsyncd,便于调试。

# lsyncd -nodaemon -rsync /home/workspace/linux/source/ /home/workspace/linux/target
17:46:25 Normal: --- Startup ---
17:46:25 Normal: recursive startup rsync: /home/workspace/linux/source/ -> /home/workspace/linux/target/
17:46:25 Normal: Startup of /home/workspace/linux/source/ -> /home/workspace/linux/target/ finished.
17:48:16 Normal: Calling rsync with filter-list of new/modified files/dirs
/freeoa.txt
/
17:48:16 Normal: Finished a list after exitcode: 0

source 内容变更:

# echo 'edit content~~' >> ./source/freeoa.txt
# cat target/freeoa.txt
Hello world!!!
========
edit content~~

要跨主机同步文件和目录

(1)使用: rsync 模式
# lsyncd -nodaemon -rsync  /home/workspace/linux/source/ linux-dev:/home/tmp/target
17:52:10 Normal: --- Startup ---
17:52:10 Normal: recursive startup rsync: /home/workspace/linux/source/ -> linux-dev:/home/tmp/target/
17:52:11 Normal: Startup of /home/workspace/linux/source/ -> linux-dev:/home/tmp/target/ finished.
17:53:09 Normal: Calling rsync with filter-list of new/modified files/dirs
/freeoa.txt
/
17:53:10 Normal: Finished a list after exitcode: 0

source 内容变更:
# echo 'To sync files and directories across hosts' >> ./source/freeoa.txt

Linux-dev:
# cat freeoa.txt
Hello world!!!
========
edit content~~
To sync files and directories across hosts

注意:
/etc/host: 添加域名映射;

host1(源) ->host2(目标):无密访问;
(2)使用:rsyncssh 模式

# lsyncd -nodaemon -rsyncssh /home/workspace/linux/source/ linux-dev /home/tmp/target
17:58:53 Normal: --- Startup ---
17:58:53 Normal: recursive startup rsync: /home/workspace/linux/source/ -> linux-dev:/home/tmp/target/
17:58:54 Normal: Startup of "/home/workspace/linux/source/" finished: 0

2、使用 default.direct 同步本地目录和文件

lsyncd 的 default.direct 模式使用的是 cp、rm 和 mv 命令进行文件的复制、删除和移动操作,这与 rsync 模式的不同之处在于其不依赖于 rsync 工具。这种模式适用于在本地文件系统之间进行简单同步,或者在不需要使用 rsync 提供的高级选项和功能的情况下使用,不适用于跨网络同步,因为 cp、rm 和 mv 都是本地文件操作命令。以下是一个使用 default.direct 模式的 lsyncd 配置文件示例:
/etc/lsyncd/lsyncd.conf.lua:

settings {
logfile    = "/var/log/lsyncd/lsyncd.log", -- 指定日志文件路径
statusFile = "/var/log/lsyncd/lsyncd.status", -- 指定状态文件路径
statusInterval = 10, -- 每 10 秒写入一次状态文件
inotifyMode = "CloseWrite or Modify" -- 设置 inotify 事件模式
}

sync {
default.direct,  -- 使用 default.direct 模式进行同步
source = "/home/workspace/linux/source/", -- 本地源目录路径
target = "/home/workspace/linux/target", -- 本地目标目录路径
delay = 5,  -- 设置文件系统事件到实际同步操作之间的延迟时间
excludeFrom = "/home/workspace/linux/exclude-list.txt",  -- 排除的文件或目录列表
maxProcesses = 1              -- 限制同时运行的同步进程数
}

重启 lsyncd:
systemctl restart lsyncd

查看日志:
# tail -f /var/log/lsyncd/lsyncd.log

Tue Jul 30 23:56:55 2024 Normal: --- Startup, daemonizing ---
Tue Jul 30 23:56:55 2024 Normal: recursive startup rsync: /home/workspace/linux/source/ -> linux-dev:/home/tmp/target/
Tue Jul 30 23:56:56 2024 Normal: Startup of /home/workspace/linux/source/ -> linux-dev:/home/tmp/target/ finished.

3、使用 default.rsync 将本地主机上的源文件和目录同步到另一台目标主机上

default.rsync 模式使用 rsync 工具进行同步,可以在本地或通过网络同步目录。rsync 提供了高级选项,如差异同步、压缩和删除目标中不在源中的文件。

修改默认配置:/etc/lsyncd/lsyncd.conf.lua

# cat /etc/lsyncd/lsyncd.conf.lua
settings {
logfile    = "/var/log/lsyncd/lsyncd.log",
statusFile = "/var/log/lsyncd/lsyncd.status",
statusInterval = 10,
inotifyMode = "CloseWrite or Modify",
}

sync {
default.rsync,
source = "/home/workspace/linux/source/",
target = "linux-dev:/home/tmp/target",
delay = 5,
maxProcesses = 1
}

重启 rsync:systemctl restart lsyncd

4、使用 default.rsyncssh 将本地主机上的源目录和文件同步到不同的目标主机上

lsyncd 还可以配置使用 ssh 命令在目标主机移动文件或目录,这样可以避免重复传输相同的文件。

原理:当文件发生变化时,lsyncd 会生成一个 rsync 进程,通过 ssh 命令连接到目标主机。在目标主机上,文件或目录会被本地移动(使用 mv 命令),而不是被删除并重新传输。这种方法可以有效减少网络传输量,提高同步效率。

# cat /etc/lsyncd/lsyncd.conf.lua
settings {
logfile    = "/var/log/lsyncd/lsyncd.log",
statusFile = "/var/log/lsyncd/lsyncd.status",
statusInterval = 10,
inotifyMode = "CloseWrite or Modify",
}

sync {
default.rsyncssh,
source = "/home/workspace/linux/source/",
target = "linux-dev:/home/tmp/target",
delay = 5,
maxProcesses = 1
}

5、使用 lsyncd 为目录启用压缩和递归功能

同步文件时启用压缩和递归功能。

archive = true:递归同步并保留文件属性;

compress = false:不压缩数据,减少 CPU 负担(可以根据需要设置为 true);

# cat /etc/lsyncd/lsyncd.conf.lua
settings {
logfile    = "/var/log/lsyncd/lsyncd.log",
statusFile = "/var/log/lsyncd/lsyncd.status",
statusInterval = 10,
inotifyMode = "CloseWrite or Modify",
}

sync {
default.rsync,
source = "/home/workspace/linux/source/",
target = "linux-dev:/home/tmp/target",
delay = 5,
maxProcesses = 1,
rsync  = {
 archive  = true,
 compress = true
   }
}

6、同步到多个目标目录

在 Linux 中使用 lsyncd 从一个源目录同步到多个目标目录,可以通过两种主要方法实现:创建单独的同步任务,或者使用 for 循环动态生成同步任务。

以下是两种方法的详细说明和示例配置。

方法一:创建单独的同步任务
通过在配置文件中为每个目标目录创建单独的同步任务,可以明确地定义每个同步任务的参数和行为。这种方法适用于需要对每个同步任务进行特定配置的情况。

# cat /etc/lsyncd/lsyncd.conf.lua
settings {
logfile    = "/var/log/lsyncd/lsyncd.log",
statusFile = "/var/log/lsyncd/lsyncd.status",
statusInterval = 10,
inotifyMode = "CloseWrite or Modify",
}

sync {
default.rsync,
source = "/home/workspace/linux/source/",
target = "/home/workspace/linux/target",
delay = 5,
}

sync {
default.rsync,
source = "/home/workspace/linux/source/",
target = "/home/workspace/linux/target2",
delay = 5,
}

sync {
default.rsync,
source = "/home/workspace/linux/source/",
target = "linux-dev:/home/tmp/target",
delay = 5,
}

方法二:使用 for 循环动态生成同步任务
通过使用 Lua 语言中的 for 循环,可以动态生成多个同步任务。这种方法适用于目标目录较多且配置相似的情况,简化配置文件的编写和维护。

# cat /etc/lsyncd/lsyncd.conf.lua
settings {
logfile    = "/var/log/lsyncd/lsyncd.log",
statusFile = "/var/log/lsyncd/lsyncd.status",
statusInterval = 10,
inotifyMode = "CloseWrite or Modify",
}

targets = {
"/home/workspace/linux/target",
"/home/workspace/linux/target2",
"linux-dev:/home/tmp/target",
}

for _, target in ipairs( targets )
do
sync{ default.rsync, source='/home/workspace/linux/source/', target=target }
end

重启: systemctl restart lsyncd日志输出: less /var/log/lsyncd/lsyncd.log

... ...
Tue Jul 30 23:59:07 2024 Normal: Finished a list after exitcode: 0
Wed Jul 31 10:46:04 2024 Normal: --- TERM signal, fading ---
Wed Jul 31 10:46:04 2024 Normal: --- Startup, daemonizing ---
Wed Jul 31 10:46:04 2024 Normal: recursive startup rsync: /home/workspace/linux/source/ -> /home/workspace/linux/target/
Wed Jul 31 10:46:04 2024 Normal: recursive startup rsync: /home/workspace/linux/source/ -> /home/workspace/linux/target2/
Wed Jul 31 10:46:04 2024 Normal: recursive startup rsync: /home/workspace/linux/source/ -> linux-dev:/home/tmp/target/
Wed Jul 31 10:46:04 2024 Normal: Startup of /home/workspace/linux/source/ -> /home/workspace/linux/target2/ finished.
Wed Jul 31 10:46:04 2024 Normal: Startup of /home/workspace/linux/source/ -> /home/workspace/linux/target/ finished.
Wed Jul 31 10:46:05 2024 Normal: Startup of /home/workspace/linux/source/ -> linux-dev:/home/tmp/target/ finished.

7、在 rsync 操作前后执行脚本

在 lsyncd 中可以配置在 rsync 操作前后执行脚本。

示例:源和目标同步完成,执行 “target1_status.sh” 脚本,打印 “Completed sync in target”,可用于检查目录同步退出状态。

# cat /etc/lsyncd/lsyncd.conf.lua
settings {
logfile    = "/var/log/lsyncd/lsyncd.log",
statusFile = "/var/log/lsyncd/lsyncd.status",
statusInterval = 10,
inotifyMode = "CloseWrite or Modify",
}

sync {
default.rsync,
source = "/home/workspace/linux/source/",
target = "/home/workspace/linux/target",
delay = 5,
rsync = {
binary = "/home/workspace/linux/lsyncd/target1_status.sh",
},
}

target1_status.sh:

# cat lsyncd/target1_status.sh
#!/bin/bash
/usr/bin/rsync "$@"
result=$?
(
  if [ $result -eq 0 ]; then
 echo "`date`: Completed sync in target" >>  /var/log/lsyncd/custom_scripts.log
  fi
) >/dev/null 2>/dev/null </dev/null

exit $result

8、排除文件和目录

在 lsyncd 中,可以通过配置 exclude 和 excludeFrom 参数来排除特定的文件和目录。

exclude:用于排除指定的文件和目录,可以使用通配符来匹配文件和目录名称:

exclude = { "*.tmp", "*.log", "cache/", "temp/" }

以上配置将排除所有以 .tmp 和 .log 结尾的文件,以及名为 cache/ 和 temp/ 的目录。

excludeFrom: 参数指定一个包含要排除文件和目录列表的文件路径,这个文件中的每一行都代表一个排除模式:

创建 exclude-list.txt 文,添加要排除的文件和目录模式:

# /path/to/exclude-list.txt
*.tmp
*.log
cache/
temp/

然后在配置文件中引用这个文件:excludeFrom = "/path/to/exclude-list.txt"

9、同步目录删除文件

默认情况下,Lsyncd 会删除目标目录中那些在源目录中已经不存在的文件,以保持目标目录与源目录的同步。这是确保同步完整性的基本操作。然而,用户可能会出于各种原因需要排除某些情况,因此 delete 选项在默认配置中通常是作为附加参数使用。

delete 选项的有效值包括:
delete = true:默认值,Lsyncd 会在目标目录中删除源目录中没有的内容,无论是在启动时还是正常运行时;
delete = false:不会删除目标目录中的任何文件,无论是启动时还是正常运行时(但可以重写);
delete = 'startup':Lsyncd 在启动时会删除目标目录中的文件,但在正常运行时不会删除;
delete = 'running':Lsyncd 在启动时不会删除目标目录中的文件,但在正常运行时会删除;

sync {
default.direct,
source = "/source",
target = "/target1",
delay = 10,
delete = false
}


配置确实有些复杂,再来看看来自sunsky303的lsyncd配置参考,感谢原作者。

调用

作为大多数Unix工具,Lsyncd将在用-help调用时打印其命令行选项的摘要。
lsyncd --help
lsyncd -help

Lsyncd中的两个连字符是冗余的。它没有短的一个字母选项,并且一个连字符总是与指定两个相同。

也像大多数Unix工具一样,--version或者-version让Lsyncd打印它的版本号。
lsyncd -version

Lsyncd 2.1被设计为主要通过配置文件进行配置(见下文)。配置文件因此可以是唯一的命令行选项。

lsyncd CONFIGFILE
尽管对于标准使用或快速测试,它可以通过命令行选项进行光标配置。以下内容将使用rsync保持本地源和目标目录同步:

lsyncd -rsync /home/USER/src /home/USER/dst
目标可以是Rsync可以识别的任何东西。

lsyncd -rsync /home/USER/src remotehost:dst
通过调用两次(或多次)-rsync配置两个(或更多)目标。

lsyncd -rsync /home/USER/src remotehost1:dst -rsync /home/USER/src remotehost2:dst
Rsync同步的一个缺点是,通常目录和文件移动会导致移动源的删除和导线移动目标的重新传输。但Lsyncd 2可以使用ssh命令在目标上本地移动目录和文件。要-rsyncssh在本地源目录,远程主机和目标目录之后使用此用法。REMOTEHOST可以包含一个用户me@remotehost.com。

lsyncd -rsyncssh /home/USER/src REMOTEHOST TARGETDIR
测试Lsyncd配置时-nodaemon是一个非常方便的标志。使用此选项,Lsyncd不会分离,并且不会成为守护进程。所有日志消息都是在控制台上打印的配置日志记录工具(stdout和stderr)之外的。

lsyncd -nodaemon CONFIGFILE
使用-nodaemon运行时的行为有所不同。Lsyncd不会/像它在成为守护程序时那样将其工作目录更改为。因此,相对目标./target会起作用,-nodaemon但必须指定绝对路径才能在守护进程模式下工作。源目录也将由Lsyncd转换为绝对路径。源代码不解析为绝对路径的原因是因为Lsyncd本身并不关心目标说明符的格式,该目标说明符也可以是远程主机,rsyncd模块等。它不透明地传递给rsync。它关心观察到的目录。

所有日志消息按类别排序。默认情况下,Lsyncd缺少日志消息。通过指定可以将Lsyncd转换为motormouth -log all。

lsyncd -log all CONFIGFILE
这可能很容易变得太多。一个特别有用的类别是“Exec”,它将记录Lsyncd产生的所有进程的命令行。

lsyncd -log Exec CONFIGFILE
当默认初始启动同步失败时,Lsyncd将以错误消息终止。它是这样设计的,所以配置故障可见地报告给可能的初始用户。但是,在生产过程中可能会完成远程目标,但是您希望Lsyncd始终启动并不断尝试同步到远程目标,直到它启动。

lsyncd -insist -rsync /home/USER/src remotehost:dst
在生产模式下,建议坚持。它也可以在配置文件中的settings {}命令中指定。

配置文件

Lsyncd配置文件是有效的Lua语法。它的设计简单而有力。虽然丰富的配置和简单性本身并不是对立的,但一些折衷是不可避免的。为了尽可能实现这两个目标,Lsyncd配置可以在不同的层完成。较低的层次增加了适应性,而界面变得更有吸引力。

设置
对于所有图层的脚本,settings可以使用该调用来更改守护程序范围的配置。

例如,以下代码将指示Lsyncd登录/tmp/lsyncd.log,定期/tmp/lsyncd.status使用其状态更新该文件,并且不会将其作为守护程序分离。

settings {
   logfile    = "/tmp/lsyncd.log",
   statusFile = "/tmp/lsyncd.status",
   nodaemon   = true,
}
警告 如果你从2.0.x升级,请注意它settings是一个变量的函数,所以你必须删除settings和之间的等号'=' {。

有效的设置键是:

logfile=FILENAMElogs into this file
pidfile=FILENAMElogs PID into this file
nodaemon=BOOLdoes not detach
statusFile=FILENAMEperiodically writes a status report to this file
statusInterval=NUMBERwrites the status file at shortest after this number of seconds has passed (default: 10)
logfacility=STRINGsyslog facility, default "user"
logident=STRINGsyslog identification (tag), default "lsyncd"
insist=BOOLkeep running at startup although one or more targets failed due to not being reachable.
inotifyMode=STRINGSpecifies on inotify systems what kind of changes to listen to. Can be "Modify", "CloseWrite" (default) or "CloseWrite or Modify".
maxProcesses=NUMBERLysncd will not spawn more than these number of processes. This adds across all sync{}s.

此外,还可以配置一些参数,这些参数由所有Syncs继承(请参阅第3层)

maxDelays    =    数    当这个延迟事件的数量排队时,动作将被产生,甚至低于延迟计时器。
 
配置第4层:默认配置

可以简单地从三个默认实现中选择:rsync,rsyncssh和direct。

要使用默认的rsync行为来同步本地目录,只需将其添加到配置文件即可:
sync {
   default.rsync,
   source = "DIRNAME",
   target = "DIRNAME"
}
论点的顺序并不重要。如果target是本地目录,请注意它是绝对路径名。您可以通过这种方式添加多个同步。源目录可能相同或不同,没有问题。source是每个同步必须给出的通用参数。所有其他sync参数可能因所选行为而异。或者,您可以覆盖默认或设置值maxDelays或maxProcesses每次同步。

也可以通过将默认init功能设置为false来跳过初始rsync过程:
sync {
    default.rsync,
    source = "DIRNAME",
    target = "DIRNAME",
    init   = false
}
这是一个危险的优化; 因此,请仅在确定源和目标在Lsyncd启动时同步的情况下使用它。

可以选择的默认行为如下:

default.rsync
默认的rsync配置会将事件聚合到delay秒或1000个独立的不可折叠事件,这些事件首先发生。然后它会产生一个Rsync,并带有所有已更改文件的过滤器。过滤器列表通过管道传输到Rsync。从Lsyncd到Rsync的调用将如下所示:
/usr/bin/rsync -ltsd --delete --include-from=- --exclude=* SOURCE TARGET

可以更改调用Rsync的选项以及与rsync参数调用的Rsync二进制文件。

例:
sync {
    default.rsync,
    source    = "/home/user/src/",
    target    = "foohost.com:~/trg/",
    delay     = 15,
    rsync     = {
        binary   = "/usr/local/bin/rsync",
        archive  = true,
        compress = true
    }
}

以下是rsync参数选项的表格。请查看Rsync文档以获得深入的解释。

parameter=TYPEdefault valuecomment
acls=BOOLfalse 
append=BOOLfalse(Lsyncd >= 2.2.0)
append-verify=BOOLfalse(Lsyncd >= 2.2.0)
archive=BOOLfalse 
backup=BOOLfalse(Lsyncd >= 2.2.0)
backup_dir=DIRfalse(Lsyncd >= 2.2.0)
binary=FILENAME"/usr/bin/rsync"Lsyncd calls this binary as rsync
checksum=BOOLfalse 
chmod=STRING (Lsyncd >= 2.2.0)
chown=USER:GROUP (Lsyncd >= 2.2.0)
compress=BOOLfalse 
copy_dirlinks=BOOLfalse(Lsyncd >= 2.2.0)
copy_links=BOOLfalse 
cvs_exclude=BOOL 
dry_run=BOOLfalse 
exclude=PATTERN TABLE of PATTERNs also allowed
excludeFrom=FILENAME  
executability=BOOLfalse 
existing=BOOLfalse(Lsyncd >= 2.2.0)
group=BOOLfalse 
groupmap=STRING (Lsyncd >= 2.2.0)
hard_links=BOOLfalse 
ignore_times=BOOLfalse 
inplace=BOOLfalse(Lsyncd >= 2.1.6)
ipv4=BOOLfalse 
ipv6=BOOLfalse 
links=BOOLtrue 
one_file_system=BOOLfalse 
owner=BOOLfalse 
password_file=FILENAME (Lsyncd >= 2.1.2)
perms=BOOLfalse 
protect_args=BOOLtrue 
prune_empty_dirs=BOOLfalse 
quiet=BOOLfalse 
rsh=COMMAND  
rsync_path=PATH (path to rsync on remote host)
sparse=BOOLfalse 
suffix=SUFFIX (Lsyncd >= 2.2.0)
temp_dir=DIR  
times=BOOLtrue 
update=BOOLfalse 
usermap=STRING (Lsyncd >= 2.2.0)
verbose=BOOLfalse 
whole_file=BOOLfalse 
xattrs=BOOLfalse 
_extra=TABLE of STRINGS. 如果绝对需要,可以将其他参数指定为STRINGS表(例如:{“--omit-dir-times”,“--omit-link-times”})。请注意,下划线将此解释为解决方法。如果您需要上述选项未涵盖的内容,请通过项目网站上的功能请求申请。最值得注意的是,不要添加-r递归或-a这意味着递归,因为Lsyncd会自己处理它。另外,不要为相对添加-R,这会破坏Lsyncd < - > Rsync通信。

如果绝对需要,可以将其他参数指定为STRINGS表(例如:{“--omit-dir-times”,“--omit-link-times”})。请注意,下划线将此解释为解决方法。如果您需要上述选项未涵盖的内容,请通过项目网站上的功能请求申请。最值得注意的是,不要添加-r递归或-a这意味着递归,因为Lsyncd会自己处理它。另外,不要为相对添加-R,这会破坏Lsyncd < - > Rsync通信。

default.rsyncssh
此配置不同于标准rsync配置,因为它使用ssh命令在目标主机本地移动文件或目录,而不是再次删除和传输。这种配置产生了类似于default.rsync的Rsync进程,但是会产生/usr/bin/ssh HOST mv ORIGIN DESTINATION命令。

与default.rsync不同,它不需要统一的target参数,但需要host并targetdir分开。Rsync的选项可以通过rsync上面所描述的default.rsync中的参数进行更改。

除此之外,可以通过ssh参数配置ssh 。
binary=FILENAMELsyncd calls this binary as ssh (default: /usr/bin/ssh)
identityFile=FILEUses this file to identify for public key authentication.
options=TABLEA table of addition extended options to pass to ssh's -o option.
port=PORTAdds --port=PORT to the ssh call.
_extra=STRING TABLESimilar to rsync._extra this can be used as quick workaround if absolutely needed.

例:

settings {
    logfile = "/var/log/lsyncd.log",
    statusFile = "/var/log/lsyncd-status.log",
    statusInterval = 20
}

sync {
   default.rsyncssh,
   source="/srcdir",
   host="remotehost",
   excludeFrom="/etc/lsyncd.exclude",
   targetdir="/dstdir",
   rsync = {
     archive = true,
     compress = false,
     whole_file = false
   },
   ssh = {
     port = 1234
   }
}
请注意rsync参数集和ssh参数集之间的逗号。

警告 如果你从2.0.x升级,请注意它settings是一个变量的函数,所以你必须删除settings和之间的等号'=' {。

Lsyncd将调用xargs远程主机来处理单个连接中的多个任务。Xargs选项可以由xargs参数指定。

binary=FILENAMELsyncd calls this binary as xargs on the remote host (default: /usr/bin/xargs)
delimiter=DELIMITERdelimiting character to separate filenames. By default the 0 character is used. Very old holds may need newline instead.
_extra=STRING TABLEBy default { '-0', 'rm -rf' }. Remove the -0 if you chose newline delimiter instead. Otherwise leave it as is.

sync例:{
    default.rsyncssh,
    source    = "/home/user/src/",
    host      = "foohost.com",
    targetdir = "~/trg/",
}

default.direct
Default.direct可以用来保持两个本地目录同步,比使用default.rsync更好的性能。Default.direct在启动时使用(就像default.rsync一样)rsync来初始化目标目录与源目录的同步。但是,在正常操作期间,default.direct使用/ bin / cp,/ bin / rm和/ bin / mv来保持同步。所有参数就像default.rsync一样。

例:
sync {
    default.direct,
    source  = "/home/user/src/",
    target  = "/home/user/trg/"
}

排除
可以指定两个附加参数来同步{}:
excludeFrom=FILENAMEloads exclusion rules from this file, on rule per line
exclude=LISTloads exclusion rules from this list of strings

排除规则在rsync的排除模式之后建模,但稍微简单一些。Lsyncd支持这些功能:

通常,如果某个事件的路径名的任何部分(参见第3层以下)与文本匹配,则将其排除。例如文件“/ bin / foo / bar”匹配规则“foo”。
如果规则以斜杠开始,则只会在路径名的开头匹配
如果规则以斜线结尾,则只会在路径名的末尾匹配
?匹配不是斜杠的任何字符。
* 匹配零个或多个不是斜线的字符
** 匹配零个或多个字符,这可以是斜杠。

例:
sync {
    default.rsync,
    source    = "/home/user/src/",
    targetdir = "/home/user/dst/",
    exclude = { '*.bak' , '*.tmp' }
}

缺失
默认情况下,Lsyncd将删除目标上不存在的文件,因为这是保持目标与源同步的基本部分。但是,由于各种原因,许多用户请求例外,因此所有默认实现都delete作为附加参数。

有效值为delete:

delete=trueDefault. Lsyncd will delete on the target whatever is not in the source. At startup and what's being deleted during normal operation.
delete=falseLsyncd will not delete any files on the target. Not on startup nor on normal operation. (Overwrites are possible though)
delete='startup'Lsyncd will delete files on the target when it starts up but not on normal operation.
delete='running'Lsyncd will not delete files on the target when it starts up but will delete those that are removed during normal operation.

配置第3层:简单的onAction

简单的行动
在这一层中,可以创建自定义配置。这个例子将使用bash命令来保持本地目录同步。

bash = {
    delay = 5,
    maxProcesses = 3,
    onCreate = "cp -r ^sourcePathname ^targetPathname",
    onModify = "cp -r ^sourcePathname ^targetPathname",
    onDelete = "rm -rf ^targetPathname",
    onMove   = "mv ^o.targetPathname ^d.targetPathname",
    onStartup = '[[ if [ "$(ls -A ^source)" ]; then cp -r ^source* ^target; fi]]',
}

这个例子一步一步解释。从技术上讲,任何Lsyncd配置都是一个填充了一组键的Lua表。因此,它首先创建一个名为变量的变量bash并为其分配一个= {...}的表格。

bash = {
  ...
}
现在表格中充满了条目。每个条目都有一个等号的左键和它的右值。如果没有指定延迟,这意味着对Lsyncd的即时操作。这个例子想要将更改汇总5秒,因此下一个条目是:

    delay = 5,
自从标记条目结束以来,需要逗号。

操作
操作由6个键指定:

onAttribcalled when only attributes changed
onCreatecalled on a new file or directory
onModifycalled when a file has changed
onDeletecalled when a file or directory has been deleted
onMovecalled when a file or directory has been moved within the observed directory tree
onStartupcalled on the start of Lsyncd

要采取的动作被指定为Lua字符串。因此,动作可以用Lua允许的任何内容进行分隔,这些是onStartup上面示例中使用的'TEXT','TEXT'或'[[TEXT]] 。当没有onMove或移动进出观察的目录树时,它被分成onDelete移动起点和onCreate移动目的地的一个。那就是如果任一个在观察目录树内。onStartup将始终阻止此Sync的所有其他操作直至完成。

任何以“/”开始的动作都会指示Lsyncd在开始时直接调用二进制文件,而不是产生额外的shell。例如
onCreate = "/usr/bin/zip /usr/var/all.zip ^sourcePath"
onModify = "/usr/bin/zip /usr/var/all.zip ^sourcePath"

将使用绝对路径名将所有新创建和修改的文件添加到/usr/var/all.zip。任何不以“/”开始的操作都会导致Lsyncd产生一个shell来执行命令。

变量
变量参数用插入符号^指定。它已经被选择超过$或其他符号,与标准的shell约定冲突较少。

请注意,变量将始终用双引号隐式引用,因此如果您希望它们成为另一个双引号字符串的一部分,则必须更深入一层,例如
onCreate   = '[[ su user -c "/usr/bin/zip /usr/var/all.zip ^o.sourcePath " ]],

将扩大到su user -c "/usr/bin/zip /usr/var/all.zip "source""哪个是不正确的,并将打破。你必须将上面的叙述更深一层地重写
onCreate = function(event)
    spawnShell('[[ su user -c "/usr/bin/zip /usr/var/all.zip \"$1\"" ]], event.sourcePath)
end

所有可能的变量
^sourcethe absolute path of the observed source directory
^targetthe "target" attribute of the config
^paththe relative path of the file or directory to the observed directory; directories have a slash at the end.
^pathnamethe relative path of the file or directory to the observed directory; directories have no slash at the end.
^sourcePaththe absolute path of the observed source directory and the relative path of the file or directory; this equals the absolute local path of the file or directory. Directories have a slash at the end.
^sourcePathnamesame as ^sourcePath, but directories have no slash at the end.
^targetPathThe "target" attributed of the config appended by the relative path of the file or directory. Directories have a slash at the end.
^targetPathnamesame as ^targetPath, but directories have no slash at the end.

从上面的示例中,它将移动目标目录中的文件或目录。对于onMoves一个。和或者d。可以预置为path,pathname,sourcePath sourcePathname,targetPath和targetPathname以指定移动起始位置或目标位置。没有任何变量指的是移动的起源。

onMove   = "mv ^o.targetPathname ^d.targetPathname",

执行控制(退出代码)
关于示例启动的几句话。它看起来有点复杂,但它只是一些bash脚本,没有什么特别的Lsyncd。它只是将源的递归副本做到目标,但首先测试源文件中是否有任何东西。否则,该命令会返回一个非零的错误代码。

onStartup = '[[if [ "$(ls -A ^source)" ]; then cp -r ^source* ^target; fi]],
默认情况下,Lsyncd忽略除了onStartup之外的所有退出代码,它必须返回0才能继续。您可以通过添加一个exitcodes表来改变这种行为。

exitcodes = {[0] = "ok", [1] = "again", [2] = "die"}
这些键为退出代码指定了所需操作的字符串。

againrespawns the action after seconds, or 1 second if delay is immediate
dielets Lsyncd terminate.

所有其他值让Lsyncd正常继续。


配置第2层:高级onAction

虽然第4层和第3层感觉像正常的配置文件,但第2层和第1层进入编码领域。因此,假设您在使用第2层或第1层时有一些编码知识。

而不是像第3层那样将动作指定为字符串Lua函数可以用于在Lsyncd中执行一些小脚本。

这个例子会将任何带有在目录中创建的“.ps”后缀的文件转换为PDF。

autopdf = {
    onCreate = function(event)
        log("Normal", "got an onCreate Event")
        if string.ends(event.pathname, ".ps") then
            spawn(event, "/usr/bin/ps2pdf", event.sourcePath)
        end
    end
}
该函数可以使用任何有效的Lua代码。

Lsyncd提供了一组用于用户脚本的函数。

日志(类别,...)
将一条消息记录到文件/ stdout / syslog中。第一个参数是日志记录类别,其他都是要记录的字符串。日志记录类别必须以大写字母开头。“正常”和“错误”是日志消息的标准类别。所有其他都是用于调试的类别。

产卵(事件,二进制,...)
产生与事件(或事件列表,参见下文)相关的新进程作为第一个参数。第二个参数指定要调用的二进制文件。所有其他人都是二进制文件的参数。

如果第三个参数是“<”,那么第四个参数将不会作为参数传递给二进制文件。第四个参数是一个字符串,它将通过stdin传递给二进制文件。

不要使用Lua os.execute,而要使用Lsyncd,spawn()它会阻塞整个Lsyncd守护进程,直到命令完成。另一方面,Lsyncd spawn在子进程运行时立即返回。

spawnShell(Event,Command,...)
和spawn()一样,只有它会调用一个shell。任何参数在命令中都被称为

 2,$ 3等等。

顺便说一句,这是spawnShell的简单实现:

function spawnShell(agent, command, ...)
    return spawn(agent, "/bin/sh", "-c", command, "/bin/sh", ...)
end
终止(退出码)
让Lsyncd终止exitcode。

事件
这些行为的变量由事件字段给出。它有以下领域。

领域含义
event.config使用sync {}调用的配置
event.inlet请参阅第1层关于入口
event.etype事件类型。可以是'ATTRIB','CREATE','MODIFY','DELETE','MOVE'
event.status事件的状态。当它准备好产生时'等待',如果有与该事件相关的进程正在运行,则'等待'
event.isdir如果事件与目录相关,则为true
event.name文件名,目录以斜杠结尾
event.basename文件名,目录不以斜杠结尾
event.path请参阅第3层的 ^路径
event.pathname请参阅第3层的 ^路径名
event.source请参阅第3层的 ^源
event.sourcePath请参阅第3层的 ^ sourcePath
event.sourcePathname请参阅第3层的 ^ sourcePathname
event.target请参阅第3层的 ^目标
event.targetPath请参阅第3层的 ^ targetPath
event.targetPathname请参阅第3层的 ^ targetPathname

这个例子会在观察到的目录树中讨论所有的移动。

tattleMove = {
    onMove = function(oEvent, dEvent)
        log("Normal", "A moved happened from ",
            oEvent.pathname, " to ",  dEvent.pathname)
    end,
}

行动功能必须简短而快速。他们正在Lsyncd的唯一主线程中运行。如果您不得不再花费更多时间计算spawn {}子进程。

只能有一个与事件关联的子进程。

第3层就是Lsyncd在初始化时自动为您写入第2层功能。-log FWrite使用第3层配置启动Lsyncd 以查看它为您动态写入和加载的功能。因此第3层和第2层也可以随意混合。


配置第1层:入口

第2层允许您为每个事件创建一个进程。但是,与默认的rsync行为一样,您可能需要为多个事件调用一个进程。这可以通过入口完成。当任何事件准备就绪时,Lsyncd action使用inletas参数调用条目。该inlet可以用来准备抢单的事件或事件列表。

例如,这是default.rsync使用的操作:

action = function( inlet )
   local elist = inlet.getEvents( )
   local config = inlet.getConfig( )
   local paths = elist.getPaths( )
   log( "Normal", "rsyncing list\n", table.concat( paths, '\n' ) )
   spawn(elist, '/usr/bin/rsync',
       '<', table.concat( paths, '\000' ),
       '--delete',
       config.rsync._computed,
       '--from0',
       '--include-from=-',
       '--exclude=*',
       config.source,
       config.target
   )
end

入口功能是:

功能描述
inlet.getEvent()event在第2层配置中检索下一个多次调用getEvent()将返回相同的事件,除非它已经产生了一个动作。
inlet.getEvents(测试)返回已准备好的所有事件的列表。test对于每个事件都要调用的函数来测试它是否应该包含在列表中是可选的。它有一个参数,event如果应该包含事件,则返回true。如果没有任何准备好的活动将被列入清单
inlet.discardEvent()丢弃一个事件。下一次调用getEvent将会收到另一个事件,即使这个事件没有产生任何动作
inlet.getConfig()返回相同event.config同步{}的配置
inlet.addExclude()向此同步添加排除模式(请参阅排除)
inlet.rmExclude()从此同步中移除排除模式
inlet.createBlanketEvent()放在event延迟FIFO的顶部,阻止所有事件并被所有事件阻塞。这用于onStartup。

getEvents返回的列表可以作为代理传递给spawn {} 以及单数事件。列表具有以下功能

功能描述
elist.getPaths(分隔符)返回路径的字符串(如在event.path由分离delimiter。缺省情况下\ n被用作分隔符。
elist.getSourcePaths(分隔符)返回sourcePaths的字符串(如在event.sourcePath由分离delimiter。缺省情况下\ n被用作分隔符。

如果用户脚本本身没有提供,那么第2层功能就是在默认情况下加载的第1层操作。

-----
-- Default action calls user scripts on**** functions.
--
action = function( inlet )
    -- in case of moves getEvent returns the origin and destination of the move
    local event, event2 = inlet.getEvent( )
    local config = inlet.getConfig( )
    local func = config[ 'on'.. event.etype ]
    if func
    then
        func( event, event2 )
    end
    -- if function didnt change the wait status its not interested
    -- in this event -> drop it.
    if event.status == "wait"
    then
        inlet.discardEvent( event )
    end
end,

如果在配置中找不到“onMove”字段,Lsyncd将自动将Move事件拆分为Create和Delete事件。在处理第1层action函数中的移动时,只需将“onMove”设置为“true”即可。

除了初始化时的每个同步的actionLsyncd调用init。这是在用户脚本没有的情况下加载的默认init函数。它为第2层和第3层提供onStartup()功能。

-----
-- called on (re)initalizing of lsyncd.
--
init = function( inlet )
    local config = inlet.getConfig( )
    -- calls a startup if provided by user script.
    if type( config.onStartup ) == "function"
    then
        local event = inlet.createBlanketEvent( )
        config.onStartup( event )
        if event.status == 'wait'
    then
            -- user script did not spawn anything
            -- thus the blanket event is deleted again.
            inlet.discardEvent( event )
        end
    end
end,

作为另一个例子,这是init的init default.rsync。作为专业,它改变了配置,因为它已经添加了一个斜线。

-----
-- Spawns the recursive startup sync
--
init = function( inlet )
    local config = inlet.getConfig( )
    local event = inlet.createBlanketEvent( )
    if string.sub(config.target, -1) ~= "/"
    then
        config.target = config.target .. "/"
    end
    
    log("Normal", "recursive startup rsync: ", config.source,
        " -> ", config.target)
    
    spawn(event,
        "/usr/bin/rsync",
        "--delete",
        config.rsync._computed .. "r",
        config.source,
        config.target
    )
end,

当子进程完成并收集其僵尸进程时,Lsyncd调用该collect入口的函数。当收回“再次”时,代理(事件或事件列表)的状态将再次设置为“等待”,并且将在delay数秒内(或者如果更小,则为1秒)准备就绪。

默认收集函数在exitcodes []表中查找退出代码的条目。否则,大多数不幸的更长的代码不会做任何事情,但会做出好的日志消息

-----
-- Called when collecting a finished child process
--
collect = function(agent, exitcode)
    local config = agent.config

    if not agent.isList and agent.etype == "Blanket" then
        if exitcode == 0
        then
            log("Normal", "Startup of '",agent.source,"' finished.")
        elseif config.exitcodes and
               config.exitcodes[exitcode] == "again"
        then
            log("Normal",
                "Retrying startup of '",agent.source,"'.")
            return "again"
        else
            log("Error", "Failure on startup of '",agent.source,"'.")
            terminate(-1) -- ERRNO
        end
        return
    end

    local rc = config.exitcodes and config.exitcodes[exitcode]
    if rc == "die"
    then
        return rc
    end

    if agent.isList
    then
        if rc == "again"
        then
            log("Normal", "Retrying a list on exitcode = ",exitcode)
        else
            log("Normal", "Finished a list = ",exitcode)
        end
    else
        if rc == "again"
        then
            log("Normal", "Retrying ",agent.etype,
                " on ",agent.sourcePath," = ",exitcode)
        else
            log("Normal", "Finished ",agent.etype,
                " on ",agent.sourcePath," = ",exitcode)
        end
    end
    return rc
end,


最新版本:2.3


项目主页:
https://lsyncd.github.io/lsyncd/

https://github.com/lsyncd/lsyncd