rsync基本使用

既然是文件同步,在同步過程中必然會涉及到源和目標兩文件之間版本控制的問題,例如是否要刪除源主機上沒有但目標上多出來的文件,目標文件比源文件更新(newer than source)時是否仍要保持同步,遇到軟鏈接時是拷貝軟鏈接本身還是拷貝軟鏈接所指向的文件,目標文件已存在時是否要先對其做個備份等等。

rsync同步過程中由兩部分模式組成:決定哪些文件需要同步的檢查模式以及文件同步時的同步模式。

(1).檢查模式是指按照指定規則來檢查哪些文件需要被同步,例如哪些文件是明確被排除不傳輸的。默認情況下,rsync使用"quick check"算法快速檢查源文件和目標文件的大小、mtime(修改時間)是否一致,如果不一致則需要傳輸。當然,也可以通過在rsync命令行中指定某些選項來改變quick check的檢查模式,比如"--size-only"選項表示"quick check"將僅檢查文件大小不同的文件作爲待傳輸文件。rsync支持非常多的選項,其中檢查模式的自定義性是非常有彈性的。

(2).同步模式是指在文件確定要被同步後,在同步過程發生之前要做哪些額外工作。例如上文所說的是否要先刪除源主機上沒有但目標主機上有的文件,是否要先備份已存在的目標文件,是否要追蹤鏈接文件等額外操作。rsync也提供非常多的選項使得同步模式變得更具彈性。

相對來說,爲rsync手動指定同步模式的選項更常見一些,只有在有特殊需求時才指定檢查模式,因爲大多數檢查模式選項都可能會影響rsync的性能。

2.3 rsync三種工作方式

以下是rsync的語法:

Local:  rsync [OPTION...] SRC... [DEST]
 
Access via remote shell:
  Pull: rsync [OPTION...] [[email protected]]HOST:SRC... [DEST]
  Push: rsync [OPTION...] SRC... [[email protected]]HOST:DEST
 
Access via rsync daemon:
  Pull: rsync [OPTION...] [[email protected]]HOST::SRC... [DEST]
        rsync [OPTION...] rsync://[[email protected]]HOST[:PORT]/SRC... [DEST]
  Push: rsync [OPTION...] SRC... [[email protected]]HOST::DEST
        rsync [OPTION...] SRC... rsync://[[email protected]]HOST[:PORT]/DEST

由此語法可知,rsync有三種工作方式:

(1).本地文件系統上實現同步。命令行語法格式爲上述"Local"段的格式。

(2).本地主機使用遠程shell和遠程主機通信。命令行語法格式爲上述"Access via remote shell"段的格式。

(3).本地主機通過網絡套接字連接遠程主機上的rsync daemon。命令行語法格式爲上述"Access via rsync daemon"段的格式。

前兩者的本質是通過管道通信,即使是遠程shell。而方式(3)則是讓遠程主機上運行rsync服務,使其監聽在一個端口上,等待客戶端的連接。

但是,還有第四種工作方式:通過遠程shell也能臨時啓動一個rsync daemon,這不同於方式(3),它不要求遠程主機上事先啓動rsync服務,而是臨時派生出rsync daemon,它是單用途的一次性daemon,僅用於臨時讀取daemon的配置文件,當此次rsync同步完成,遠程shell啓動的rsync daemon進程也會自動消逝。此通信方式的命令行語法格式同"Access via rsync daemon",但要求options部分必須明確指定"--rsh"選項或其短選項"-e"。

以下是對rsync語法的簡單說明,由於rsync支持一百多個選項,所以此處只介紹幾個常用選項。完整的選項說明以及rsync的使用方法見"man rsync"。

Local:  rsync [OPTION...] SRC... [DEST]
 
Access via remote shell:
  Pull: rsync [OPTION...] [[email protected]]HOST:SRC... [DEST]
  Push: rsync [OPTION...] SRC... [[email protected]]HOST:DEST
 
Access via rsync daemon:
  Pull: rsync [OPTION...] [[email protected]]HOST::SRC... [DEST]
        rsync [OPTION...] rsync://[[email protected]]HOST[:PORT]/SRC... [DEST]
  Push: rsync [OPTION...] SRC... [[email protected]]HOST::DEST
        rsync [OPTION...] SRC... rsync://[[email protected]]HOST[:PORT]/DEST

其中,第一個路徑參數一定是源文件路徑,即作爲同步基準的一方,可以同時指定多個源文件路徑。最後一個路徑參數則是目標文件路徑,也就是待同步方。路徑的格式可以是本地路徑,也可以是使用[email protected]:path或[email protected]::path的遠程路徑,如果主機和path路徑之間使用單個冒號隔開,表示使用的是遠程shell通信方式,而使用雙冒號隔開的則表示的是連接rsync daemon。另外,連接rsync daemon時,還提供了URL格式的路徑表述方式rsync://[email protected]/path。

如果僅有一個SRC或DEST參數,則將以類似於"ls -l"的方式列出源文件列表(只有一個路徑參數,總會認爲是源文件),而不是複製文件。

如果對rsync不熟悉,可暫先只瞭解本地以及遠程shell格式的[email protected]:path路徑格式。例如:

[[email protected] ~]# rsync /etc/fstab /tmp                # 在本地同步
[[email protected] ~]# rsync -r /etc 172.16.10.5:/tmp       # 將本地/etc目錄拷貝到遠程主機的/tmp下,以保證遠程/tmp目錄和本地/etc保持同步
[[email protected] ~]# rsync -r 172.16.10.5:/etc /tmp       # 將遠程主機的/etc目錄拷貝到本地/tmp下,以保證本地/tmp目錄和遠程/etc保持同步
[[email protected] ~]# rsync /etc/                          # 列出本地/etc/目錄下的文件列表
[[email protected] ~]# rsync 172.16.10.5:/tmp/              # 列出遠程主機上/tmp/目錄下的文件列表

另外,使用rsync一定要注意的一點是,源路徑如果是一個目錄的話,帶上尾隨斜線和不帶尾隨斜線是不一樣的,不帶尾隨斜線表示的是整個目錄包括目錄本身,帶上尾隨斜線表示的是目錄中的文件,不包括目錄本身。例如:

[[email protected] ~]# rsync -a /etc /tmp
[[email protected] ~]# rsync -a /etc/ /tmp

第一個命令會在/tmp目錄下創建etc目錄,而第二個命令不會在/tmp目錄下創建etc目錄,源路徑/etc/中的所有文件都直接放在/tmp目錄下。

2.4 選項說明和示例

接下來是rsync的選項說明。

-v:顯示rsync過程中詳細信息。可以使用"-vvvv"獲取更詳細信息。
-P:顯示文件傳輸的進度信息。(實際上"-P"="--partial --progress",其中的"--progress"纔是顯示進度信息的)。
-n --dry-run  :僅測試傳輸,而不實際傳輸。常和"-vvvv"配合使用來查看rsync是如何工作的。
-a --archive  :歸檔模式,表示遞歸傳輸並保持文件屬性。等同於"-rtopgDl"。
-r --recursive:遞歸到目錄中去。
-t --times:保持mtime屬性。強烈建議任何時候都加上"-t",否則目標文件mtime會設置爲系統時間,導致下次更新
          :檢查出mtime不同從而導致增量傳輸無效。
-o --owner:保持owner屬性(屬主)。
-g --group:保持group屬性(屬組)。
-p --perms:保持perms屬性(權限,不包括特殊權限)。
-D        :是"--device --specials"選項的組合,即也拷貝設備文件和特殊文件。
-l --links:如果文件是軟鏈接文件,則拷貝軟鏈接本身而非軟鏈接所指向的對象。
-z        :傳輸時進行壓縮提高效率。
-R --relative:使用相對路徑。意味着將命令行中指定的全路徑而非路徑最尾部的文件名發送給服務端,包括它們的屬性。用法見下文示例。
--size-only :默認算法是檢查文件大小和mtime不同的文件,使用此選項將只檢查文件大小。
-u --update :僅在源mtime比目標已存在文件的mtime新時才拷貝。注意,該選項是接收端判斷的,不會影響刪除行爲。
-d --dirs   :以不遞歸的方式拷貝目錄本身。默認遞歸時,如果源爲"dir1/file1",則不會拷貝dir1目錄,使用該選項將拷貝dir1但不拷貝file1。
--max-size  :限制rsync傳輸的最大文件大小。可以使用單位後綴,還可以是一個小數值(例如:"--max-size=1.5m")
--min-size  :限制rsync傳輸的最小文件大小。這可以用於禁止傳輸小文件或那些垃圾文件。
--exclude   :指定排除規則來排除不需要傳輸的文件。
--delete    :以SRC爲主,對DEST進行同步。多則刪之,少則補之。注意"--delete"是在接收端執行的,所以它是在
            :exclude/include規則生效之後才執行的。
-b --backup :對目標上已存在的文件做一個備份,備份的文件名後默認使用"~"做後綴。
--backup-dir:指定備份文件的保存路徑。不指定時默認和待備份文件保存在同一目錄下。
-e          :指定所要使用的遠程shell程序,默認爲ssh。
--port      :連接daemon時使用的端口號,默認爲873端口。
--password-file:daemon模式時的密碼文件,可以從中讀取密碼實現非交互式。注意,這不是遠程shell認證的密碼,而是rsync模塊認證的密碼。
-W --whole-file:rsync將不再使用增量傳輸,而是全量傳輸。在網絡帶寬高於磁盤帶寬時,該選項比增量傳輸更高效。
--existing  :要求只更新目標端已存在的文件,目標端還不存在的文件不傳輸。注意,使用相對路徑時如果上層目錄不存在也不會傳輸。
--ignore-existing:要求只更新目標端不存在的文件。和"--existing"結合使用有特殊功能,見下文示例。
--remove-source-files:要求刪除源端已經成功傳輸的文件。

rsync的選項非常多,能夠實現非常具有彈性的功能,以上選項僅僅只是很小一部分常用的選項,關於更完整更詳細的選項說明,見我的rsync man手冊翻譯

雖然選項非常多,但最常用的選項組合是"avz",即壓縮和顯示部分信息,並以歸檔模式傳輸。

2.4.1 基礎示例

以下是幾個本地同步示例和通過遠程shell實現的同步示例,示例中沒有使用"-a"選項,目的是爲了更清晰地說明各選項的作用。

(1).將/etc/fstab拷貝到/tmp目錄下。

[[email protected] ~]# rsync /etc/fstab /tmp

(2).將/etc/cron.d目錄拷貝到/tmp下。

[[email protected] ~]# rsync -r /etc/cron.d /tmp

該命令會在目標主機上創建/tmp/cron.d目錄,並將/etc/cron.d/中的文件放入到/tmp/cron.d/目錄中,也就是說默認情況下,是不會在目錄路徑下創建上層目錄/etc的。

(3).將/etc/cron.d目錄拷貝到/tmp下,但要求在/tmp下也生成etc子目錄。

[[email protected] ~]# rsync -R -r /etc/cron.d /tmp

其中"-R"選項表示使用相對路徑,此相對路徑是以目標目錄爲根的。對於上面的示例,表示在目標上的/tmp下創建etc/cron.d目錄,即/tmp/etc/cron.d,etc/cron.d的根"/"代表的就是目標/tmp。

如果要拷貝的源路徑較長,但只想在目標主機上保留一部分目錄結構,例如要拷貝/var/log/anaconda/*到/tmp下,但只想在/tmp下保留從log開始的目錄,如何操作?使用一個點代表相對路徑的起始位置即可,也就是將長目錄進行劃分。

[[email protected] ~]# rsync -R -r /var/./log/anaconda /tmp

這樣,從點開始的目錄都是相對路徑,其相對根目錄爲目標路徑。所以對於上面的示例,將在目標上創建/tmp/log/anaconda/*。

(4).對遠程目錄下已存在文件做一個備份。

[[email protected] ~]# rsync -R -r --backup /var/./log/anaconda /tmp

這樣在目標目錄下,已存在的文件就被做一個備份,備份文件默認使用"~"做後綴,可以使用"--suffix"指定備份後綴。

[[email protected] tmp]# ll log/anaconda/
total 3112
-rw------- 1 root root    6668 Jul 14 12:45 anaconda.log
-rw------- 1 root root    6668 Jul 14 11:44 anaconda.log~
-rw------- 1 root root    3826 Jul 14 12:45 ifcfg.log
-rw------- 1 root root    3826 Jul 14 11:44 ifcfg.log~
-rw------- 1 root root 1102699 Jul 14 12:45 journal.log
-rw------- 1 root root 1102699 Jul 14 11:44 journal.log~
-rw------- 1 root root       0 Jul 14 12:45 ks-script-1uLekR.log
-rw------- 1 root root       0 Jul 14 11:44 ks-script-1uLekR.log~
-rw------- 1 root root       0 Jul 14 12:45 ks-script-iGpl4q.log
-rw------- 1 root root       0 Jul 14 11:44 ks-script-iGpl4q.log~
-rw------- 1 root root  160420 Jul 14 12:45 packaging.log
-rw------- 1 root root  160420 Jul 14 11:44 packaging.log~
-rw------- 1 root root   27906 Jul 14 12:45 program.log
-rw------- 1 root root   27906 Jul 14 11:44 program.log~
-rw------- 1 root root   78001 Jul 14 12:45 storage.log
-rw------- 1 root root   78001 Jul 14 11:44 storage.log~
-rw------- 1 root root  197961 Jul 14 12:45 syslog
-rw------- 1 root root  197961 Jul 14 11:44 syslog~

可以使用"--backup-dir"指定備份文件保存路徑,但要求保存路徑必須存在。

[[email protected] ~]# mkdir /tmp/log_back

[[email protected] ~]# rsync -R -r --backup --backup-dir=/tmp/log_back /var/./log/anaconda /tmp

指定備份路徑後,默認將不會加備份後綴,除非使用"--suffix"顯式指定後綴,如"--suffix=~"。

[[email protected] tmp]# tree /tmp/log_back/
/tmp/log_back/
└── log
    └── anaconda
        ├── anaconda.log
        ├── ifcfg.log
        ├── journal.log
        ├── ks-script-1uLekR.log
        ├── ks-script-iGpl4q.log
        ├── packaging.log
        ├── program.log
        ├── storage.log
        └── syslog

(5).指定ssh連接參數,如端口、連接的用戶、ssh選項等。

[[email protected] tmp]# >~/.ssh/known_hosts   # 先清空host key以便下面的測試

[[email protected] tmp]# rsync -e "ssh -p 22 -o StrictHostKeyChecking=no" /etc/fstab 172.16.10.5:/tmp
Warning: Permanently added '172.16.10.5' (RSA) to the list of known hosts.
[email protected]'s password:

可見直接指定ssh參數是生效的。

(6)."--existing"和"--ignore-existing"

"--existing"是隻更新目標端已存在的文件。

目前/tmp/{a,b}目錄中內容如下,bashrc在a目錄中,crontab在b目錄中,且a目錄中多了一個c子目錄。

[[email protected] ~]# tree /tmp/{a,b}
/tmp/a
├── bashrc
├── c
│   └── find
├── fstab
├── profile
└── rc.local
/tmp/b
├── crontab
├── fstab
├── profile
└── rc.local
 
1 directory, 9 files

使用"--existing"選項使得只更新目標端已存在的文件。

[[email protected] ~]# rsync -r -v --existing /tmp/a/ /tmp/b           
sending incremental file list
fstab
profile
rc.local
 
sent 2972 bytes  received 70 bytes  6084.00 bytes/sec
total size is 204755  speedup is 67.31

結果只有3個目標上已存在的文件被更新了,由於目標上沒有c目錄,所以c目錄中的文件也沒有進行傳輸。

而"--ignore-existing"是更新目標端不存在的文件。

[[email protected] ~]# rsync -r -v --ignore-existing /tmp/a/ /tmp/b
sending incremental file list
bashrc
c/
c/find
 
sent 202271 bytes  received 54 bytes  404650.00 bytes/sec
total size is 204755  speedup is 1.01

"--existing"和"--ignore-existing"結合使用時,有個特殊功效,當它們結合"--delete"使用的時候,文件不會傳輸,但會刪除receiver端額外多出的文件。

$ mkdir a b
$ touch a/{1..4}.txt
$ touch b/a.log

$ rsync -nrv --delete a/ b/
sending incremental file list
deleting a.log
1.txt
2.txt
3.txt
4.txt
 
sent 118 bytes  received 33 bytes  302.00 bytes/sec
total size is 0  speedup is 0.00 (DRY RUN)

$ rsync -nrv --existing --ignore-existing --delete a/ b/
sending incremental file list
deleting a.log
 
sent 106 bytes  received 21 bytes  254.00 bytes/sec
total size is 0  speedup is 0.00 (DRY RUN)

實際上,"--existing"和"--ingore-existing"是傳輸規則,只會影響receiver要求讓sender傳輸的文件列表,在receiver決定哪些文件需要傳輸之前的過程,是這兩個選項無法掌控的,所以各種規則、"--delete"等操作都不會被這兩個選項影響。

(7)."--remove-source-files"刪除源端文件。

使用該選項後,源端已經更新成功的文件都會被刪除,源端所有未傳輸或未傳輸成功的文件都不會被移除。未傳輸成功的原因有多種,如exclude排除了,"quick check"未選項該文件,傳輸中斷等等。

總之,顯示在"rsync -v"被傳輸列表中的文件都會被移除。如下:

[[email protected] ~]# rsync -r -v --remove-source-files /tmp/a/anaconda /tmp/a/audit /tmp       
sending incremental file list
anaconda/anaconda.log
anaconda/ifcfg.log
anaconda/journal.log
anaconda/ks-script-1uLekR.log
anaconda/ks-script-iGpl4q.log
anaconda/packaging.log
anaconda/program.log
anaconda/storage.log
anaconda/syslog
audit/audit.log
 
sent 4806915 bytes  received 204 bytes  9614238.00 bytes/sec
total size is 4805676  speedup is 1.00

上述顯示出來的文件在源端全部被刪除。

2.4.2 "--exclude"排除規則

使用"--exclude"選項指定排除規則,排除那些不需要傳輸的文件。

[[email protected] tmp]# rsync -r -v --exclude="anaconda/*.log" /var/log/anaconda /var/log/audit /tmp
sending incremental file list
anaconda/
anaconda/syslog
audit/
audit/audit.log
 
sent 3365629 bytes  received 58 bytes  6731374.00 bytes/sec
total size is 3365016  speedup is 1.00

上例中只排除了anaconda目錄中的log文件,但是audit目錄中的log文件是正常傳輸的。

注意,一個"--exclude"只能指定一條規則,要指定多條排除規則,需要使用多個"--exclude"選項,或者將排除規則寫入到文件中,然後使用"--exclude-from"選項讀取該規則文件。

另外,除了"--exclude"排除規則,還有"--include"包含規則,顧名思義,它就是篩選出要進行傳輸的文件,所以include規則也稱爲傳輸規則。它的使用方法和"--exclude"一樣。如果一個文件即能匹配排除規則,又能匹配包含規則,則先匹配到的立即生效,生效後就不再進行任何匹配。

最後,關於規則,最重要的一點是它的作用時間。當發送端敲出rsync命令後,rsync將立即掃描命令行中給定的文件和目錄(掃描過程中還會按照目錄進行排序,將同一個目錄的文件放在相鄰的位置),這稱爲拷貝樹(copy tree),掃描完成後將待傳輸的文件或目錄記錄到文件列表中,然後將文件列表傳輸給接收端。而篩選規則的作用時刻是在掃描拷貝樹時,所以會根據規則來匹配並決定文件是否記錄到文件列表中(嚴格地說是會記錄到文件列表中的,只不過排除的文件會被標記爲hide隱藏起來),只有記錄到了文件列表中的文件或目錄纔是真正需要傳輸的內容。換句話說,篩選規則的生效時間在rsync整個同步過程中是非常靠前的,它會影響很多選項的操作對象,最典型的如"--delete"。也許,你看完這一整篇文章都沒感覺到這一點的重要性,但如果你閱讀rsync的man文檔或者學習rsync的原理,你一定會深有體會。

實際上,排除規則和包含規則都只是"--filter"篩選規則的兩種特殊規則。"--filter"比較複雜,它有自己的規則語法和匹配模式,由於篇幅有限,以及考慮到本文的難度定位,"--filter"規則不便在此多做解釋,僅簡單說明下規則類,幫助理解下文的"--delete"。

以下是rsync中的規則種類,不解之處請結合下文的"--delete"分析:

(1).exclude規則:即排除規則,只作用於發送端,被排除的文件不會進入文件列表(實際上是加上隱藏規則進行隱藏)。

(2).include規則:即包含規則,也稱爲傳輸規則,只作用於發送端,被包含的文件將明確記錄到文件列表中。

(3).hide規則:即隱藏規則,只作用於發送端,隱藏後的文件對於接收端來說是看不見的,也就是說接收端會認爲它不存在於源端。

(4).show規則:即顯示規則,只作用於發送端,是隱藏規則的反向規則。

(5).protect規則:即保護規則,該規則只作用於接收端,被保護的文件不會被刪除掉。

(6).risk規則:即取消保護規則。是protect的反向規則。

除此之外,還有一種規則是"clear規則",作用是刪除include/exclude規則列表。

2.4.2.1 如何一次寫對exclude規則

很多人寫不來規則,總髮現寫出來後沒有生效,要寫成功一次規則得要不斷地進行調試、調試、調試,令人無比心煩。

其實很多工具的規則寫法是類似的,比如tar也一樣。規則寫好後不生效的原因一般有兩種:絕對路徑和相對路徑的問題、尾隨斜線的問題。

一般來說,操作的路徑是絕對路徑,那麼規則裏必須也寫絕對路徑,而且必須寫完整的絕對路徑。操作的路徑是相對路徑,那麼規則裏必須寫相對路徑,從哪裏開始相對可能和工具有關。比如要操作路徑"/abc/def",想要篩選def下的所有txt文件,必須寫成"/abc/def/*.txt",如果操作路徑是"abc/def",那麼要篩選這個目錄下的所有txt文件,可能需要寫成"abc/def/*.txt",也有可能寫成"*.txt",甚至寫成"def/*.txt",寫成哪種形式依賴於這個軟件如何解析相對路徑。

對於rsync來說,要更復雜一些,因爲除了bash的相對路徑外,它自身還提供了一個相對路徑的解析規則,而且尾隨斜線也會影響規則的寫法。這就是爲什麼寫rsync規則非常麻煩的原因。

我這裏提供一個判斷規則寫法的方式,純屬我個人的經驗總結:使用"-n"選項是dry run模式,也就是隻測試不傳輸,"-i"選項是輸出要傳輸文件的路徑。"-i"只是一個便捷性選項,可以替換成其它選項來自定義輸出格式,有時候通過這些信息來做一些判斷是非常有用的,具體的可以翻man手冊。

root:~$ rsync -nr -i a b/
cd+++++++++ a/
>f+++++++++ a/1.txt
>f+++++++++ a/2.txt
>f+++++++++ a/3.txt
>f+++++++++ a/4.txt

這裏已經顯示了傳輸文件的路徑"a/*",也就是說包括了目錄a,且是相對路徑的。所以要寫規則時,需要加上這個a路徑,比如下面的排除規則。

root:~$ rsync -nr -i --exclude="a/2.txt"  a b/
cd+++++++++ a/
>f+++++++++ a/1.txt
>f+++++++++ a/3.txt
>f+++++++++ a/4.txt

root:~$ rsync -nr -i --exclude="a/*.txt"  a b/
cd+++++++++ a/

如果上面的傳輸路徑a加上尾隨斜線,再看-i的輸出路徑信息,發現已經改變了:

root:~$ rsync -nr -i a/ b/
>f+++++++++ 1.txt
>f+++++++++ 2.txt
>f+++++++++ 3.txt
>f+++++++++ 4.txt

所以這時的排除規則中不應該包含a目錄前綴:

root:~$ rsync -nr -i --exclude="2.txt" ./a/ b/
>f+++++++++ 1.txt
>f+++++++++ 3.txt
>f+++++++++ 4.txt

2.4.3 "--delete"解釋

使用"--delete"選項後,接收端的rsync會先刪除目標目錄下已經存在,但源端目錄不存在的文件。也就是"多則刪之,少則補之"。

例如,先實現一次同步,再向目標目錄中拷貝一個新文件,這樣目標目錄中就比源目錄多出一個文件。

[[email protected] ~]# rsync -r /etc/cron.d /tmp/

[[email protected] ~]# cp /etc/fstab /tmp/cron.d/

[[email protected] ~]# ls /tmp/cron.d/
0hourly  fstab  raid-check  sysstat

再使用"--delete"選項,這時會將目標端多出的文件給刪除掉,然後進行同步。

[[email protected] ~]# rsync -r -v /etc/cron.d /tmp --delete
sending incremental file list
deleting cron.d/fstab
cron.d/0hourly
cron.d/raid-check
cron.d/sysstat
 
sent 704 bytes  received 70 bytes  1548.00 bytes/sec
total size is 471  speedup is 0.61

這樣的行爲實現了遠程刪除的功能,對於作用於本地的rsync,也就實現了rm的本地刪除功能。而且,如果使用空目錄作爲源目錄,則它的作用是清空目錄上的整個目錄。

如果將"--delete"選項和"--exclude"選項一起使用,則被排除的文件不會被刪除。例如:

[[email protected] ~]# rsync -r /var/log/anaconda /var/log/audit /tmp  # 先進行一次同步以便測試

[[email protected] ~]# cp /etc/fstab /tmp/anaconda/                    # 拷貝一個新文件到目標目錄以便測試

[[email protected] ~]# rsync -r -v --exclude="anaconda/*.log" /var/log/anaconda /var/log/audit /tmp --delete
sending incremental file list
deleting anaconda/fstab
anaconda/syslog
audit/audit.log
 
sent 3406190 bytes  received 52 bytes  6812484.00 bytes/sec
total size is 3405579  speedup is 1.00

結果發現只刪除了"anaconda/fstab"文件,被"--exclude"規則匹配的anaconda/*.log文件都沒有被刪除。也就是網上所說的言論:exclude排除的文件不會被刪除。

結論是沒錯的,但我想很多人不知道爲何會如此,也可能從來沒想過爲何會如此,所以我簡單地做個說明。

在發送端將文件列表發送給接收端後,接收端的generator(要是不知道,你認爲是某個就好了)進程會掃描每個文件列表中的信息,然後對列表中的每個信息條目都計算數據塊校驗碼,最後將數據庫校驗碼發給發送端,發送端通過校驗碼來匹配哪些數據塊是需要傳輸的,這樣就實現了增量傳輸的功能——只傳輸改變的部分,不會傳輸整個文件。而delete刪除的時間點是generator進程處理每個文件列表時、生成校驗碼之前進行的,先將目標上存在但源上不存在的多餘文件刪除,這樣就無需爲多餘的文件生成校驗碼。

所以,delete動作是比"--exclude"規則更晚執行的,被"--exclude"規則排除的文件不會進入文件列表中,在執行了delete時會認爲該文件不存在於源端,從而導致目標端將這些文件刪除。但這是想當然的,儘管理論上確實是這樣的,但是rsync爲了防止衆多誤刪除情況,提供了兩種規則:保護規則(protect)和取消保護規則(risk)。默認情況下,"--delete"和"--exclude"一起使用時,雖然發送端的exclude規則將文件標記爲隱藏,使得接收端認爲這些被排除文件在源端不存在,但rsync會將這些隱藏文件標記爲保護文件,使得它們不受delete行爲的影響,這樣delete就刪除不了這些被排除的文件。如果還是想要強行刪除被exclude排除的文件,可以使用"--delete-excluded"選項強制取消保護,這樣即使被排除的文件也會被刪除。

那麼現在,是否理解了網上的言論"exclude排除的文件不會被刪除"?

除了"--delete",相關的選項還有"--delete-before"、"--delete-during"、"--delete-delay"等,它們都隱含了"--delete"選項,它們分別表示generator處理各個文件列表之前一次性全部刪除待刪除文件、處理文件列表時處理到哪個文件列表就刪除該文件列表中的待刪除文件,以及同步完所有數據後一次性刪除所有待刪除文件。

舉個例子,假如源端要傳輸3個目錄a、b、c,在目標端a目錄中有a1、a2、a3共3個文件需要被刪除,b目錄中有b1、b2、b3需要刪除,同理c目錄也一樣c1、c2、c3需要被刪除。

如果是"--delete-before",則在目標端rsync剛啓動時,就會把a1-a3、b1-b3、c1-c3一次性刪除,然後纔會處理文件列表中的a目錄,處理完a後處理b,再是c。

如果是"--delete-during",則在目標端rsync剛啓動時,先處理文件列表中的a目錄,處理a目錄時發現此目錄中有待刪除文件a1-a3,順手就刪除它們,然後完成a目錄的相關操作,再處理文件列表中的b目錄,發現也有待刪除文件b1-b3,順手刪除它們,同理c1-c3也如此。

如果是"--delete-delay",則同步完文件列表中的a/b/c目錄後,最後一次性刪除a1-a3、b1-b3、c1-c3。

其實"--delete"選項大多數情況下默認採用的就是"--delete-during"。

2.5 rsync daemon模式

2.5.1 簡單介紹

既然rsync通過遠程shell就能實現兩端主機上的文件同步,還要使用rsync的服務幹啥?試想下,你有的機器上有一堆文件需要時不時地同步到衆多機器上去,比如目錄a、b、c是專門傳輸到web服務器上的,d/e、f、g/h是專門傳輸到ftp服務器上的,還要對這些目錄中的某些文件進行排除,如果通過遠程shell連接方式,無論是使用排除規則還是包含規則,甚至一條一條rsync命令地傳輸,這都沒問題,但太過繁瑣且每次都要輸入同樣的命令顯得太死板。使用rsync daemon就可以解決這種死板問題。而且,rsync daemon是向外提供服務的,這樣只要告訴了別人rsync的url路徑,外人就能向ftp服務器一樣獲取文件列表並進行選擇性地下載,所以,你所制定的列表,你的同事也可以獲取到並使用。

舉個簡單的例子,Linux內核官網www.kernel.org提供rsync的下載方式,官方給出的地址是rsync://rsync.kernel.org/pub,可以根據這個地址找出你想下載的內核版本。例如要找出linux-3.0.15版本的內核相關文件。

[[email protected] ~]# rsync --no-motd -r -v -f "+ */" -f "+ linux-3.0.15*" -f "- *" -m rsync://rsync.kernel.org/pub/
receiving file list ... done
drwxr-xr-x         124 2017/07/14 20:27:22 .
drwxr-xr-x         178 2014/11/12 05:50:10 linux
drwxr-xr-x        4096 2017/06/27 05:46:27 linux/kernel
drwxr-xr-x      237568 2017/07/05 20:49:33 linux/kernel/v3.x
-rw-r--r--    76803806 2012/01/04 03:00:31 linux/kernel/v3.x/linux-3.0.15.tar.bz2
-rw-r--r--    96726195 2012/01/04 03:00:31 linux/kernel/v3.x/linux-3.0.15.tar.gz
-rw-r--r--         836 2012/01/04 03:00:31 linux/kernel/v3.x/linux-3.0.15.tar.sign
-rw-r--r--    63812604 2012/01/04 03:00:31 linux/kernel/v3.x/linux-3.0.15.tar.xz
 
sent 59 bytes  received 80.19K bytes  12.35K bytes/sec
total size is 237.34M  speedup is 2957.66

你無需關注上面的規則代表什麼意思,需要關注的重點是通過rsync可以向外提供文件列表並提供相應的下載。

同樣,你還可以根據路徑,將rsync daemon上的文件拉取到本地實現下載的功能。

[[email protected] ~]# rsync --no-motd -avzP rsync://rsync.kernel.org/pub/linux/kernel/v3.x/linux-3.0.15.tar.bz2 /tmp
receiving incremental file list
linux-3.0.15.tar.bz2
     2834426   3%   300.51kB/s    0:40:22

下面就來介紹下rsync daemon。

rsync daemon是"rsync --daemon"或再加上其他一些選項啓動的,它會讀取配置文件,默認是/etc/rsyncd.conf,並默認監聽在873端口上,當外界有客戶端對此端口發起連接請求,通過這個網絡套接字就可以完成連接,以後與該客戶端通信的所有數據都通過該網絡套接字傳輸。

rsync daemon的通信方式和傳輸通道與遠程shell不同。遠程shell連接的兩端是通過管道完成通信和數據傳輸的,即使連接的一端是遠程主機,當連接到目標端時,將在目標端上根據遠程shell進程fork出rsync進程使其成爲rsync server。而rsync daemon是事先在server端上運行好的rsync後臺進程(根據啓動選項,也可以設置爲非後臺進程),它監聽套接字等待client端的連接,連接建立後所有通信方式都是通過套接字完成的。

注意,rsync中的server的概念從來就不代表是rsync daemon,server在rsync中只是一種通用稱呼,只要不是發起rsync請求的client端,就是server端,你可以認爲rsync daemon是一種特殊的server,其實daemon更應該稱之爲service。(之所以解釋這一點,是避免各位初學的朋友在閱讀man rsync過程中產生誤解)

以下是rsync client連接rsync daemon時的命令語法:

Pull: rsync [OPTION...] [[email protected]]HOST::SRC... [DEST]
      rsync [OPTION...] rsync://[[email protected]]HOST[:PORT]/SRC... [DEST]
Push: rsync [OPTION...] SRC... [[email protected]]HOST::DEST
      rsync [OPTION...] SRC... rsync://[[email protected]]HOST[:PORT]/DEST

連接命令有兩種類型,一種是rsync風格使用雙冒號的"rsync [email protected]::src dest",一種是url風格的"rsync://[email protected]:port/src dest"。對於rsync風格的連接命令,如果想要指定端口號,則需要使用選項"--port"。

上述語法中,其中daemon端的路徑,如[email protected]::src,它的src代表的是模塊名,而不是真的文件系統中的路徑。關於rsync中的模塊,相信見了下面的配置文件就會知道是什麼意思。

2.5.2 daemon配置文件rsyncd.conf

默認"rsync --daemon"讀取的配置文件爲/etc/rsyncd.conf,有些版本的系統上可能該文件默認不存在。rsyncd.conf的配置見man rsyncd.conf。以下是部分內容:

[[email protected] ~]# cat /etc/rsyncd.conf
# /etc/rsyncd: configuration file for rsync daemon mode
 
# See rsyncd.conf man page for more options.
 
# configuration example:
 
# uid = nobody
# gid = nobody
# use chroot = yes
# max connections = 4
# pid file = /var/run/rsyncd.pid
# exclude = lost+found/
# transfer logging = yes
# timeout = 900
# ignore nonreadable = yes
# dont compress   = *.gz *.tgz *.zip *.z *.Z *.rpm *.deb *.bz2
 
# [ftp1]
#        path = /home/ftp
#        comment = ftp export area

在上述示例配置文件中,先定義了一些全局選項,然後定義了[ftp1],這個用中括號包圍的"[ftp1]"就是rsync中所謂的模塊,ftp1爲模塊ID,必須保證唯一,每個模塊中必須定義一項"path",path定義的是該模塊代表的路徑,例如此示例文件中,如果想請求ftp1模塊,則在客戶端使用"rsync [email protected]::ftp1",這表示訪問[email protected]上的/home/ftp目錄,如果要訪問/home/ftp目錄下的子目錄www,則"rsync [email protected]::ftp1/www"。

以下是常見的配置項,也算是一個配置示例:

######### 全局配置參數 ##########
port=888    # 指定rsync端口。默認873
uid = rsync # rsync服務的運行用戶,默認是nobody,文件傳輸成功後屬主將是這個uid
gid = rsync # rsync服務的運行組,默認是nobody,文件傳輸成功後屬組將是這個gid
use chroot = no # rsync daemon在傳輸前是否切換到指定的path目錄下,並將其監禁在內
max connections = 200 # 指定最大連接數量,0表示沒有限制
timeout = 300         # 確保rsync服務器不會永遠等待一個崩潰的客戶端,0表示永遠等待
motd file = /var/rsyncd/rsync.motd   # 客戶端連接過來顯示的消息
pid file = /var/run/rsyncd.pid       # 指定rsync daemon的pid文件
lock file = /var/run/rsync.lock      # 指定鎖文件
log file = /var/log/rsyncd.log       # 指定rsync的日誌文件,而不把日誌發送給syslog
dont compress = *.gz *.tgz *.zip *.z *.Z *.rpm *.deb *.bz2  # 指定哪些文件不用進行壓縮傳輸
 
###########下面指定模塊,並設定模塊配置參數,可以創建多個模塊###########
[longshuai]        # 模塊ID
path = /longshuai/ # 指定該模塊的路徑,該參數必須指定。啓動rsync服務前該目錄必須存在。rsync請求訪問模塊本質就是訪問該路徑。
ignore errors      # 忽略某些IO錯誤信息
read only = false  # 指定該模塊是否可讀寫,即能否上傳文件,false表示可讀寫,true表示可讀不可寫。所有模塊默認不可上傳
write only = false # 指定該模式是否支持下載,設置爲true表示客戶端不能下載。所有模塊默認可下載
list = false       # 客戶端請求顯示模塊列表時,該模塊是否顯示出來,設置爲false則該模塊爲隱藏模塊。默認true
hosts allow = 10.0.0.0/24 # 指定允許連接到該模塊的機器,多個ip用空格隔開或者設置區間
hosts deny = 0.0.0.0/32   # 指定不允許連接到該模塊的機器
auth users = rsync_backup # 指定連接到該模塊的用戶列表,只有列表裏的用戶才能連接到模塊,用戶名和對應密碼保存在secrts file中,
                          # 這裏使用的不是系統用戶,而是虛擬用戶。不設置時,默認所有用戶都能連接,但使用的是匿名連接
secrets file = /etc/rsyncd.passwd # 保存auth users用戶列表的用戶名和密碼,每行包含一個username:passwd。由於"strict modes"
                                  # 默認爲true,所以此文件要求非rsync daemon用戶不可讀寫。只有啓用了auth users該選項纔有效。
[xiaofang]    # 以下定義的是第二個模塊
path=/xiaofang/
read only = false
ignore errors
comment = anyone can access

注意:

(1).客戶端推到服務端時,文件的屬主和屬組是配置文件中指定的uid和gid。但是客戶端從服務端拉的時候,文件的屬主和屬組是客戶端正在操作rsync的用戶身份,因爲執行rsync程序的用戶爲當前用戶。

(2).auth users和secrets file這兩行不是一定需要的,省略它們時將默認使用匿名連接。但是如果使用了它們,則secrets file的權限必須是600。客戶端的密碼文件也必須是600。

(3).關於secrets file的權限,實際上並非一定是600,只要滿足除了運行rsync daemon的用戶可讀即可。是否檢查權限的設定是通過選項strict mode設置的,如果設置爲false,則無需關注文件的權限。但默認是yes,即需要設置權限。

配置完後,再就是提供模塊相關目錄、身份驗證文件等。

[[email protected] ~]# useradd -r -s /sbin/nologin rsync

[[email protected] ~]# mkdir /{longshuai,xiaofang}

[[email protected] ~]# chown -R rsync.rsync /{longshuai,xiaofang}

提供模塊longshuai身份驗證文件,由於rsync daemon是以root身份運行的,所以要求身份驗證文件對非root用戶不可讀寫,所以設置爲600權限。

[[email protected] ~]# echo "rsync_backup:123456" >> /etc/rsyncd.passwd

[[email protected] ~]# chmod 600 /etc/rsyncd.passwd 

然後啓動rsync daemon,啓動方式很簡單。

[[email protected] ~]# rsync --daemon

如果是CentOS 7,則自帶啓動腳本。

[[email protected] ~]# systemctl start rsyncd

看看該腳本的內容。

[[email protected] ~]# cat /usr/lib/systemd/system/rsyncd.service
[Unit]
Description=fast remote file copy program daemon
ConditionPathExists=/etc/rsyncd.conf
 
[Service]
EnvironmentFile=/etc/sysconfig/rsyncd
ExecStart=/usr/bin/rsync --daemon --no-detach "$OPTIONS"
 
[Install]
WantedBy=multi-user.target

可以看到啓動方法也僅僅只是多了一個"--no-detach",該選項表示rsync不將自己從終端上剝離。

總之,啓動好rysnc daemon後,它就監聽在指定的端口上,等待客戶端的連接。

由於上述示例中的模塊longshuai配置了身份驗證功能,所以客戶端連接時會詢問密碼。如果不想手動輸入密碼,則可以使用"--password-file"選項提供密碼文件,密碼文件中只有第一行纔是傳遞的密碼,其餘所有的行都會被自動忽略。

例如在客戶端上:

[[email protected] ~]# echo "123456" > /tmp/rsync_passwd

然後使用該"--password-file"連接需要身份驗證的longshuai模塊。

[[email protected] ~]# echo "123456" > /tmp/rsync_passwd

如果需要訪問模塊中的某個文件,則:

[[email protected] ~]# rsync --list-only --port 888 [email protected]::longshuai/a/b --password-file=/tmp/rsync_passwd

還可以使用url格式語法:

[[email protected] ~]# rsync --list-only rsync://[email protected]:888/longshuai/a/b --password-file=/tmp/rsync_passwd

2.6 遠程shell方式連接使用daemon

在前文說了rsync有三種工作方式:本地同步模式、遠程shell模式和rsync daemon模式。前兩者是使用管道進行通信和傳輸數據的,後者是通過網絡套接字進行通信和傳輸數據的,且rsync daemon要求在server端必須已經運行好rsync且監聽在指定端口上。

但rsync支持第4種工作方式:通過遠程shell方式連接rsync daemon。也就是將第二種和第三種方式結合起來。雖然這種方式用的不多,但還是有必要稍微解釋下,爲你閱讀rsync的man文檔提供一些幫助。

爲了下面稱呼的方便,暫且將通過遠程shell連接使用daemon的方式成爲"遠程shell daemon",當然,官方並沒有這樣的術語,僅僅只是本人在此爲了方便而如此稱呼。

遠程shell daemon的方式嚴格地說是"遠程shell通信方式+使用rsync daemon的功能"。所以它的通信方式和遠程shell是一樣的,在客戶端發起遠程shell連接,在server端fork遠程shell進程以啓動rsync進程,但這個rsync進程是臨時的rsync daemon,它只讀取配置文件中client所請求的模塊部分,且只讀取模塊部分中的path和身份認證相關內容,(也就是說不會將全局配置項和其它模塊項加載到內存,該模塊下的其他配置也不會生效),當rsync操作完成,該rsync daemon就消逝並從內存中被清理。而且,遠程shell daemon啓動的臨時daemon不會和已經在server端運行的rsync daemon衝突,它們可以並存。由於遠程shell連接的最終目標是rsync模塊,所以它只能使用rsync daemon語法。

以下是語法格式:爲了簡潔,沒有指定src還是dest,且以ssh這個遠程shell爲例。

rsync [options] --rsh=ssh [email protected]::module

rsync [options] --rsh="ssh -l ssh_user" [email protected]::module

rsync [options] -e "ssh -l ssh_user" [email protected]::module

rsync [options] -e "ssh -l ssh_user" rsync://[email protected]/module

涉及了兩個用戶ssh_user和auth_user,由於使用的是遠程shell通信方式,所以client要和server端建立ssh連接,ssh_user就是ssh連接server的用戶。auth_user則是模塊中的身份認證用戶。如果不指定"ssh_user",則默認將使用auth_user,但很多時候auth_user都只是一個虛擬用戶,這樣就建立不了ssh連接導致失敗,所以建議明確指定ssh_user和auth_user。

舉個例子就能說明上面的一切。以下是server端配置文件/etc/rsyncd.conf中的一個模塊配置,稍後將從client端使用遠程shell方式請求該模塊。

[tmpdir]
path=/tmp
auth users=lisi
secrets file=/tmp/lisi_passwd

當前server端是沒有rsync daemon在運行的。

[[email protected] ~]# netstat -tnl
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State     
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN    
tcp        0      0 127.0.0.1:25            0.0.0.0:*               LISTEN    
tcp6       0      0 :::22                   :::*                    LISTEN    
tcp6       0      0 ::1:25                  :::*                    LISTEN

在客戶端上使用以下命令:

[[email protected] ~]# rsync --list-only -e "ssh -l root" [email protected]::tmpdir
[email protected]'s password:
 
Password:

可以看到要求輸入兩次密碼,第一次密碼是[email protected]的密碼,即建立ssh連接使用的密碼,只有建立了ssh連接,才能在server上啓動臨時rsync daemon。第二次輸入的密碼Password是"auth users=lisi"對應的密碼。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章