# 使用 rsync 和 inotify 實現即時備份 [toc] ![aaa](https://hackmd.io/_uploads/Hk1MrnVBA.gif) --- ## 實時同步的原理 是透過`監測`文件狀態變化 再 更新被動地更動 `監測`可以使用`inotify-tools`命令 ![image](https://hackmd.io/_uploads/SJr49PEBC.png) inotify 是kernal function之一 以下軟體可以呼叫inotify功能 > **inotify-tools** 內建 > sersync 第三方 > lrsyncd 第三方 inotify 監控 rsync 同步 再使用腳本自動化操作 ## inotify 監控 安裝inotify-tools在機器1(被備份端) > sudo yum install inotify-tools -y > 安裝完後可以使用rpm -ql inotify-tools查看相關工具 > ![image](https://hackmd.io/_uploads/S1PqMdVS0.png) ### inotify-tools主要工具 inotifywait : 監測檔案或資料夾發生的事件的次數(包括 開啟 關閉 刪除等) inotifywatch : 統計發生的事件的次數 進階設置 > inotify kernal參數 主要有 max_queued_events、max_user_instances、max_user_watches,分別的作用是: max_queued_events: 作用:這個參數定義了 inotify 事件隊列中可以排隊的最大事件數量。當事件數量達到這個限制時,新的事件會被丟棄並且內核會發出一個溢出(overflow)事件來通知用戶空間程序。 用途:限制事件隊列的大小以防止過多的事件佔用內存,從而保護系統穩定性。 max_user_instances: 作用:這個參數定義了每個用戶可以創建的 inotify 實例的最大數量。一個 inotify 實例是由調用 inotify_init() 系統調用創建的。 用途:限制每個用戶創建的 inotify 實例數量,以防止資源耗盡攻擊(resource exhaustion attacks)。 max_user_watches: 作用:這個參數定義了每個用戶可以監視的文件或目錄的最大數量。每個 inotify 實例可以監視多個文件或目錄,每個監視都佔用一個 watch descriptor。 用途:限制每個用戶能監視的文件或目錄數量,防止單個用戶佔用過多資源,從而影響系統的其他部分或其他用戶。 基本上只需要確保max_queued_events(預設值16384 較小)足夠大以應付同時發生的大量事件和max_user_instances(預設值8192 較小) >可以透過 `cat /proc/sys/fs/inotify/參數名稱` 查看 設定值 並使用 vim /etc/sysctl.fonf 加上 `fs.inotify.參數名稱=數值` 即可設定 例如 `fs.inotify.max_user_instances=10000` ### inotifywait 命令 用法 inotifywait 檔案或資料夾 > ex:`inotify /data/www/` ![image](https://hackmd.io/_uploads/BypaQOEBR.png) 可以監控到 操作`touch a.txt`,但是命令執行完畢後便結束任務 不會持續監控 可以透過 -m 設置成持續監控 ![image](https://hackmd.io/_uploads/rJmDvu4S0.png) > 圖中呈現了幾種基本操作監控到的事件 `inotifywait` 常用參數的介紹: - **`-m` (monitor)**: 監控目標路徑的變化,持續運行,直到手動終止。 - **`-d` (daemonize)**: 以守護進程方式運行,適合於長期運行的監控任務。 - **`-r` (recursive)**: 遞歸監控目標路徑下的所有子目錄。 - **`-q` (quiet)**: 靜默模式,只輸出變化事件,不顯示啟動和結束信息。 - **`--exclude`**: 使用正則表達式排除特定文件或目錄(大小寫敏感)。 - **`--excludei`**: 使用正則表達式排除特定文件或目錄(大小寫不敏感)。 - **`-o` (outfile)**: 將輸出寫入指定文件,而不是標準輸出。 - **`-s` (syslog)**: 將輸出寫入系統日誌,而不是標準輸出。 - **`--timefmt`**: 指定時間戳格式,配合 `--format` 使用。 - **`--format`**: 自定義輸出格式,可以包括時間、事件類型、文件名等。 - **`-e` (event)**: 指定監控的事件類型(如 create、delete、modify 等)。 其中 --timefmt 的時間格式 與 --format 的格式如下 --timefmt (時間格式使用 strftime 標準): - %Y:四位數的年份,例如:2023 - %y:兩位數的年份,例如:23 - %m:兩位數的月份(01-12) - %d:兩位數的日期(01-31) - %H:兩位數的小時(00-23) - %M:兩位數的分鐘(00-59) - %S:兩位數的秒數(00-60) --format : - %T:使用 --timefmt 格式指定的時間戳 - %w:監控的目錄名稱 - %f:發生變化的文件名稱 - %e:事件發生的類型(例如:IN_MODIFY、IN_DELETE 等) - %Xe:特定事件 e 的擴展格式,其中 X 是擴展選項 範例: `inotifywait -mr --timefmt "%Y-%m-%d %H:%M:%S" --format "%T%w%f event: %;e" /data/www` ![1718026016368](https://hackmd.io/_uploads/HJi51tNBR.gif) 但是,並不是所有的事件觸發都需要同步,例如:查看文件 會觸發OPEN但這種事件不需要執行同步(或是說 不需要做任何對應的處理) 所以我們只需要關注以下這些事件即可: - `create`:當一個文件或目錄被創建時觸發。 - `delete`:當一個文件或目錄被刪除時觸發。 - `moved_to`:當一個文件或目錄被移動到監控的目錄中時觸發。 - `close_write`:當一個打開用於寫入的文件被關閉時觸發。 - `attrib`:當一個文件或目錄的屬性被修改時觸發。屬性包括權限、擁有者、時間戳等。 範例: `inotifywait -mrq /data/www --timefmt "%F %H:%M:%S" --format "%T %W%f event: %;e" -e create,delete,moved_to,close_write,attrib` > ![cacheMessagep126363](https://hackmd.io/_uploads/r1mB2FEBA.gif) ## rsync 同步 如果想要機器上的資料備份到另一台機器 可以使用rsync 因為 rsync可以進行 增量複製 ### 設置 機器1(左) 機器名稱 data (資料伺服器) 機器2(右) 機器名稱 backup (備份伺服器) 皆已使用ssh無密碼登入 目的是將 機器1 /data/www 的內容 備份到 機器2 /data/backup [user@backup backup]$ `rsync -ac data:/data/www/ .` ![image](https://hackmd.io/_uploads/ryo6WvNB0.png) > 但是目前存在一些問題 > 需要備份時都需要執行一次rsync命令 (使用contab) > 但是`刪除檔案`的動作不會同步 需要加上 `--delete` 參數 > 可以使用crontab週期性更新 那要如何做到實時同步? > [優化] `* * * * * rsync -ac --delete data:/data/www/ /data/backup &> /dev/null` 使用排程(crond)設置每分鐘執行一次rsync命令 `crontab -e`輸入`* * * * * rsync -ac data:/data/www/ /data/backup ```md ### 排程(crond) yum install cronie *: don’t care 分 時 日 月 周 1 * * * * 每小時的第1分鐘 */1 * * * * 每1分鐘執行一次 */2 * * * * 每2分鐘執行一次 1 * * * 每小時的第0分鐘 */1 * * * 每1小時執行一次 /2: 代表每2時間(分、時、日…)執行一次 29 9 15 8 * (8/15 09:29 執行一次) 0 17 10 * * (每個月10日 下午5點整) 0 4 * * 6 (每個星期六 零晨4點整) 1,31 17 10 * * (每個17號 17點 1分、31分都執行一次) 1-10 17 10 * * (每個17號 17點 1分、2分…、10分都執行一次) 0 * * * * (每小時第0分鐘) 0 23-1/2,8 * * * 23,4,3,5,7,8 */20 6-12 * 12 * 在12月時,6~12小時間 每20分鐘 執行一次 ``` crontab -l: 列出当前用户的定时任务 crontab -r: 删除当前用户的所有定时任务。 ### rsync的運作模式 1. 使用ssh存取 用法類似 scp scp [參數] [來源] [目的] rsync [參數] [來源] [目的] ex: push:`rsync /home/user/a.txt user@10.0.0.1:/home/user/a.txt` pull: `rsync user@10.0.0.1:/home/user/a.txt /home/user/a.txt` 2. 使用rsync獨立服務rsync-daemon(port 873/TCP)存取 rsync [參數] [來源] [目的] ex: push: `rsync /home/user/a.txt user@10.0.0.1::/home/user/a.txt` `rsync /home/user/a.txt rsync://user@10.0.0.1:873 //home/user/a.txt` `rsync /home/user/a.txt rsync://user@10.0.0.1 //home/user/a.txt` pull: `rsync user@10.0.0.1::/home/user/a.txt /home/user/a.txt` `rsync rsync://user@10.0.0.1:873//home/user/a.txt /home/user/a.txt` ![wait4del](https://hackmd.io/_uploads/B1nEZ5VBA.png) pull: rsync [OPTION] [USER@]HOST::SRC... [DST] rsync [OPTION] rsync://[USER@]HOST[:POST]/SRC... [DST] push: rsync [OPTION] SRC... [USER@]HOST::DST rsync [OPTION] SRC... rsync://[USER@]HOST[:POST]/DST ---- ![image](https://hackmd.io/_uploads/SyM_X54BC.png) rsync獨立服務 情境: **備份伺服器**做rsync伺服器端 **資料伺服器**做rsync客戶端 設置rsync伺服器之前 只需要執行`rsync --daemon`就可以成為rsync伺服器端 但需要確保配置檔/etc/rsyncd.conf存在 ![image](https://hackmd.io/_uploads/HJPMj9VSR.png) #### rsync配置檔 設置 ```conf [共享名稱-自訂] path=共享位置 ``` 範例 ```conf [backup] path=/data/backup ``` 配置檔寫好後 rsync客戶端 可以透過`rsync rsync://HOST`查看共享的位置 >![image](https://hackmd.io/_uploads/SyZhbsNB0.png) > 在配置檔設定後便會出現`共享名稱` > ![image](https://hackmd.io/_uploads/B1_FMjNBA.png) 也可以使用另一種格式 或設置/etc/hosts ---- 為了方便操作(使用systemctl)可以安裝rsync-daemon > yum install rsync-daemon 安裝套件同時也會建立配置檔/etc/rsyncd.conf與service文件 ![image](https://hackmd.io/_uploads/H1krJoNS0.png) 也可以設置成開機啟動`systemctl enable --now rsyncd` 但內部其其實也是使用rsync --daemon cat /usr/lib/systemd/system/rsyncd.service ![image](https://hackmd.io/_uploads/B14pesNHC.png) --- 但目前仍然無法存取,因為rsync需要設置共享系統的權限 與 文件系統的權限 ![image](https://hackmd.io/_uploads/r11LXj4SR.png) > 前面的backup是HOST 後面的是共享名稱 需要在配置檔中加入`read only = no`,才能`寫入` > 預設 read only是 yes > 修改完要 重啟服務 systemctl restart rsyncd 接著需要設置文件系統的權限,否則報錯如下圖: > ![image](https://hackmd.io/_uploads/H1KpNs4rA.png) rsync預設是透過nobody用戶進行存取 所以需要讓**rsync伺服器端**設置對應權限 > `setfacl -m u:nobody:rwx /data/backup/` 測試 rsync client(左): `rsync /etc/passwd backup:backup` ![image](https://hackmd.io/_uploads/Byb4LjEBA.png) 可以正常存取並且擁有者為nobody 如果以user的身分傳檔案`rsync /etc/passed user@192.169.245.149::backup`擁有者也會是nobody 但使用rsync-daemod傳輸資料時預設不需要驗證會有安全性的問題 > 使用ssh時會需要密碼或預先設置免密碼登入 所以需要啟用驗證功能 (在配置檔新增以下內容) ```conf uid = root # 指定身份驗證通過時共享目錄,將之指定為生成的文件所有者,默認為nobody gid = root # 提示身份驗證通過時,生成文件的group,默認為nobody port = 873 # 指定啟動rsync服務時,監聽從哪個端口,默認為873/tcp #use chroot = no # 默認為yes max connections = 0 ignore errors exclude = lost+found/ log file = /var/log/rsyncd.log pid file = /var/run/rsyncd.pid lock file = /var/run/rsyncd.lock reverse lookup = no #hosts allow = 10.0.0.0/24 [backup] # 每個模塊名對應一個不同的path目錄,如果同名後面模塊生效 path = /data/backup/ comment = backup dir read only = no # 默認為yes,即只讀 auth users = rsyncuser # 默認為匿名用戶可以訪問rsync服務器 secrets file = /etc/rsync.pas ``` 移除註解 ```conf uid = root gid = root port = 873 max connections = 0 ignore errors exclude = lost+found/ log file = /var/log/rsyncd.log pid file = /var/run/rsyncd.pid lock file = /var/run/rsyncd.lock reverse lookup = no [backup] path = /data/backup/ comment = backup dir read only = no auth users = rsyncuser secrets file = /etc/rsync.pas ``` 接著,將使用者與密碼`user:password`寫入到 /etc/rsync.pas `echo "rsyncuser:0000" > /etc/rsync.pas` > 為了提高安全性 可將存放密碼的檔案權限設置為600 > chmod 600 /etc/rsync.pas 如此一來 登入時就會要求輸入密碼 但為了方便撰寫腳本,可以在client端設定一個密碼檔並以`--password-file=密碼檔`避免交互 > ![image](https://hackmd.io/_uploads/S1jNCs4rC.png) --- [root@data-centos8 ~]# vim inotify_rsync.sh ``` sh #!/bin/bash SRC='/data/www/' # 注意最後的/ DEST='rsyncuser@rsync服務器IP::backup' rpm -q rsync &> /dev/null || yum -y install rsync inotifywait -mrq --exclude=".*\.swp" --timefmt '%Y-%m-%d %H:%M:%S' --format '%T %w %f' -e create,delete,moved_to,close_write,attrib ${SRC} | while read DATE TIME DIR FILE; do FILEPATH=${DIR}${FILE} rsync -az --delete --password-file=/etc/rsync.pas $SRC $DEST && echo "At ${TIME} on ${DATE}, file ${FILEPATH} was backed up via rsync" >> /var/log/changelist.log done ``` 執行腳本後 bash aa.sh rsync server端執行`watch -n0.1 ls -l /data/backup`每0.1秒執行一次ls -l /data/backup ![aaa](https://hackmd.io/_uploads/Hk1MrnVBA.gif)