Contents

lsyncd 做檔案同步實作紀錄

最近看到這幾篇使用 inotify 做文件同步感到有興趣
雖然現在工作上目前還沒用到
但這種情境感覺很常遇到
希望能用不用寫程式方法

相關工具

  • inotify

inotify-tools 監聽資料夾裡檔案異動工具 | 程式狂想筆記
之前小記這篇,其實裡面沒什麼實作
需透過 bash 執行
但這樣當然不是很方便,所以有類似 sersync 和 lsyncd 工具解決這個方案

  • sersync

這篇lsyncd实时同步搭建指南——取代rsync+inotify-Linux运维日志有講到很多優點缺點備份圖
為了不浪費時間
所以我就不實作了

lsyncd

安裝

apt install rsyncd

或去 github 官網抓 axkibe/lsyncd: Lsyncd (Live Syncing Daemon) synchronizes local directories with remote targets

lua 記得要升到 5.2

Lua >= 5.2

Lsyncd depends on Lua 5.2 or greater; that is 5.2 or 5.3. For most distributions you need to install the liblua??, the liblua??-dev and the lua?? package, with ?? being the respective Lua version.
cmake >= 2.8

To configure Lsyncd to your system, cmake >= 2.8 is required
rsync >= 3.1

During runtime Lsyncd needs rsync > 3.1 installed both on source and target systems.

Lsyncd - Building

設定

一開始不會有/etc/lsyncd.conf
可以做 service lsync status 有沒有執行

vim /etc/lsyncd.conf 網路上看到是這個路徑
但是我看CONFIG=/etc/lsyncd/lsyncd.conf.lua

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# 全域設定
settings {
    logfile = "/var/log/lsyncd/lsyncd.log",
    statusFile = "/var/log/lsyncd/lsyncd.status"
    inotifyMode = "CloseWrite",
    maxProcesses = 8,
}

## 以下設定參考 https://www.centos.bz/2017/08/lsyncd-rsync-inotify/
# 可設定多組
# I. 本地目錄同步,direct:cp/rm/mv。 適用:500+萬文件,變動不大
sync {
    default.direct,
    source    = "/tmp/src",
    target    = "/tmp/dest",
    delay = 1
    maxProcesses = 1
    }

# II. 本地目錄同步,rsync模式:rsync
sync {
    default.rsync,
    source    = "/tmp/src",
    target    = "/tmp/dest1",
    excludeFrom = "/etc/rsyncd.d/rsync_exclude.lst",
    rsync     = {
        binary = "/usr/bin/rsync",
        archive = true,
        compress = true,
        bwlimit   = 2000
        } 
    }

# III. 遠程目錄同步,rsync模式 + rsyncd daemon
sync {
    default.rsync,
    source    = "/tmp/src",
    target    = "syncuser@172.29.88.223::module1",
    delete="running",  # 可以改成false 這樣就不會刪掉
    exclude = { ".*", ".tmp" },
    delay = 30,
    init = false, # 默認init功能設置為false來跳過初始rsync過程
    rsync     = {
        binary = "/usr/bin/rsync",
        archive = true,
        compress = true,
        verbose   = true,
        password_file = "/etc/rsyncd.d/rsync.pwd",
        _extra    = {"--bwlimit=200"}
        }
    }

# IV. 遠程目錄同步,rsync模式 + ssh shell
sync {
    default.rsync,
    source    = "/tmp/src",
    target    = "172.29.88.223:/tmp/dest",
    # target    = "root@172.29.88.223:/remote/dest",
    # 上面target,注意如果是普通用戶,必須擁有寫權限
    maxDelays = 5,
    delay = 30,
    # init = true,
    rsync     = {
        binary = "/usr/bin/rsync",
        archive = true,
        compress = true,
        bwlimit   = 2000
        # rsh = "/usr/bin/ssh -p 22 -o StrictHostKeyChecking=no"
        # 如果要指定其它端口,請用上面的rsh
        }
    }

# V. 遠程目錄同步,rsync模式 + rsyncssh,效果與上面相同
sync {
    default.rsyncssh,
    source    = "/tmp/src2",
    host      = "172.29.88.223",
    targetdir = "/remote/dir",
    excludeFrom = "/etc/rsyncd.d/rsync_exclude.lst",
    # maxDelays = 5,
    delay = 0,
    # init = false,
    rsync    = {
        binary = "/usr/bin/rsync",
        archive = true,
        compress = true,
        verbose   = true,
        _extra = {"--bwlimit=2000"},
        },
    ssh      = {
        port  =  1234
        }
    }

default.direct

Default.direct可以用來保持兩個本地目錄同步,比使用default.rsync更好的性能。Default.direct在啟動時使用(就像default.rsync一樣)rsync來初始化目標目錄與源目錄的同步。但是,在正常操作期間,default.direct使用/ bin / cp,/ bin / rm和/ bin / mv來保持同步。所有參數就像default.rsync一樣。

delete 也適用這個屬性

_extra 可放一些參數

想說有沒有 size-only 設定
但發現都找不到
後來看到_extra 可以設定

簡單測試

預設是 10 秒檢查資料更新
可設定 statusInterval
Lsyncd - The Configuration File

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
settings {
   logfile    = "/tmp/lsyncd.log",
   statusFile = "/tmp/lsyncd.status",
   nodaemon   = true,
}

sync {
   default.rsync,
   source = "/home/malagege/test_lsyncd/a/",
   target = "/home/malagege/test_lsyncd/b/",

default.rsync 預設是執行
/usr/bin/rsync -ltsd –delete –include-from=- –exclude=* SOURCE TARGET
所以會做刪除動作

同步完做動作

Lsyncd - FAQ: How can I call a script before or after each rsync operation?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#!/bin/bash
/usr/bin/rsync "$@"
result=$?
(
  if [ $result -eq 0 ]; then
     echo "my commands";
  fi
) >/dev/null 2>/dev/null </dev/null

exit $result

這邊注意 rsync 並不是對單一檔案做搬移
而是對整個…
echo $@ 結果是
-slt -r --delete --ignore-errors --force --from0 --include-from=- --exclude=* /home/malagege/test_lsyncd/a/ /home/malagege/test_lsyncd/a/ /home/malagege/test_lsyncd/b/

跟我想要抓的東西不一樣

可能做 Layer 2 做自訂動作

感覺這邊自由度很高
因為我對 lua 不熟
所以就沒打算做這段

其他參考

同步腳本

以下都是參考:真正的inotify+rsync实时同步 彻底告别同步慢 - 琴酒网络 - 博客园

1
2
3
4
5
6
#!/bin/bash
/usr/bin/inotifywait -mrq --format '%w%f'-e create,close_write,delete /backup |while read file
#把發生更改的文件列表都接收到file 然後循環,但有什麼鬼用呢?下面的命令都沒有引用這個$file 下面做的是全量rsync
do
    cd /backup && rsync -az --delete /backup/ rsync_backup@192.168.24.101::backup/--password-file=/etc/rsync.password
done

優化

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#!/bin/bash
src=/data/                           # 需要同步的源路徑
des=data                             # 目標服務器上 rsync --daemon 發佈的名稱,rsync --daemon這裡就不做介紹了,網上搜一下,比較簡單。
rsync_passwd_file=/etc/rsyncd.passwd            # rsync驗證的密碼文件
ip1=192.168.0.18                 # 目標服務器1
ip2=192.168.0.19                 # 目標服務器2
user=root                            # rsync --daemon定義的驗證用戶名
cd ${src}                            
# 此方法中,由於rsync同步的特性,這裡必須要先cd到源目錄,inotify再監聽 ./ 才能rsync同步後目錄結構一致,有興趣的同學可以進行各種嘗試觀看其效果
/usr/local/bin/inotifywait -mrq --format  '%Xe %w%f' -e modify,create,delete,attrib,close_write,move ./ | while read file
# 把監控到有發生更改的"文件路徑列表"循環
do
        INO_EVENT=$(echo $file | awk '{print $1}')      # 把inotify輸出切割 把事件類型部分賦值給INO_EVENT
        INO_FILE=$(echo $file | awk '{print $2}')       # 把inotify輸出切割 把文件路徑部分賦值給INO_FILE
        echo "-------------------------------$(date)------------------------------------"
        echo $file
        #增加、修改、寫入完成、移動進事件
        #增、改放在同一個判斷,因為他們都肯定是針對文件的操作,即使是新建目錄,要同步的也只是一個空目錄,不會影響速度。
        if [[ $INO_EVENT =~ 'CREATE' ]] || [[ $INO_EVENT =~ 'MODIFY' ]] || [[ $INO_EVENT =~ 'CLOSE_WRITE' ]] || [[ $INO_EVENT =~ 'MOVED_TO' ]]         # 判斷事件類型
        then
                echo 'CREATE or MODIFY or CLOSE_WRITE or MOVED_TO'
                rsync -avzcR --password-file=${rsync_passwd_file} $(dirname ${INO_FILE}) ${user}@${ip1}::${des} &&
# INO_FILE變量代表路徑哦  -c校驗文件內容
                rsync -avzcR --password-file=${rsync_passwd_file} $(dirname ${INO_FILE}) ${user}@${ip2}::${des}
#仔細看 上面的rsync同步命令 源是用了$(dirname ${INO_FILE})變量 即每次只針對性的同步發生改變的文件的目錄(只同步目標文件的方法在生產環境的某些極端
#環境下會漏文件 現在可以在不漏文件下也有不錯的速度 做到平衡)
#然後用-R參數把源的目錄結構遞歸到目標後面 保證目錄結構一致性
        fi
        #刪除、移動出事件
        if [[ $INO_EVENT =~ 'DELETE' ]] || [[ $INO_EVENT =~ 'MOVED_FROM' ]]
        then
                echo 'DELETE or MOVED_FROM'
                rsync -avzR --delete --password-file=${rsync_passwd_file} $(dirname ${INO_FILE}) ${user}@${ip1}::${des} &&
                rsync -avzR --delete --password-file=${rsync_passwd_file} $(dirname ${INO_FILE}) ${user}@${ip2}::${des}
#看rsync命令 如果直接同步已刪除的路徑${INO_FILE}會報no such or directory錯誤 所以這裡同步的源是被刪文件或目錄的上一級路徑
#並加上--delete來刪除目標上有而源中沒有的文件,這裡不能做到指定文件刪除,如果刪除的路徑越靠近根,則同步的目錄月多,同步刪除的操作就越花時間。
#這裡有更好方法的同學,歡迎交流。
        fi
        #修改屬性事件 指 touch chgrp chmod chown等操作
        if [[ $INO_EVENT =~ 'ATTRIB' ]]
        then
                echo 'ATTRIB'
                if [ ! -d "$INO_FILE" ]
# 如果修改屬性的是目錄 則不同步,因為同步目錄會發生遞歸掃瞄,等此目錄下的文件發生同步時,rsync會順帶更新此目錄。
                then
                        rsync -avzcR --password-file=${rsync_passwd_file} $(dirname ${INO_FILE}) ${user}@${ip1}::${des} &&         
                        rsync -avzcR --password-file=${rsync_passwd_file} $(dirname ${INO_FILE}) ${user}@${ip2}::${des}
                fi
        fi
done

簡單來看就是對檔案做 dirname 備份,減少做多次遞迴
是不是能確認來源是資料夾或檔案,單純 scp 或 rsync 過去是不是比較快?

相關同步數量問題

以下參考:等很久資訊室: Lsyncd - Live syncing Daemon 資料夾同步

檢查可監測數量設定值:
lsyncd 是依靠 kernel 內建的檔案監測功能 inotify,來回報記錄檔案狀態,而 inotify 預設有設定了一個可監測資料夾數量的限定值。

cat /proc/sys/fs/inotify/max_user_watches

在 CentOS 6.8 內,它預設值是 8192,也就是它只能監測最多 8192 個資料夾。
而我們要同步的資料夾有多少,會不會超過這個數值呢?必竟,若是超過了,它就無法同步到了。

cat /var/run/lsyncd/lsyncd.status

在狀態檔中找到一行如下:
Inotify watching 1440 directories
那數字便是目前 lsyncd 所監測的資料夾數量。
如果您看到的數字很靠近 8192 或甚至超過,那就必須放大這個限定值。

echo ‘10000’ > /proc/sys/fs/inotify/max_user_watches

這樣可以即時把限定值改成 10000,但重開機後它又會變成原本的 8192。
下面方法可以改變開機後的預設值:

把上述即時修改的指令放到 rc.local 之類的開機啟動檔內。
或是
在 /etc/sysctl.conf 內加上一行
fs.inotify.max_user_watches = 10000

這樣它重開機後,一樣會放大成我們設定的數值。