案例六、檢測兩臺機器上文件的差異

生產環境中,web服務器大多會做負載均衡,所以有多臺機器上跑着同樣的web程序代碼。如果嚴格按照規範流程上線,即不人爲特意更改代碼,那麼這多臺機器上的代碼一定是一樣的,並不會存在兩臺機器上同一個文件內容不同的現象。但是,本案例的需求就是要檢查兩臺機器上同一個文件的差異,畢竟我們不能確定服務器上的代碼是否有人爲改動過。

需求如下:

1)兩臺機器A和B,檢查的目標目錄爲/data/wwwroot/www.abc.com/,路徑一致。

2)需要過濾目錄uploads、tmp兩個目錄,即這兩個目錄下的文件不需要進行對比。

3)以A機器上的文件作爲標準,B機器少了文件和改了文件需要記錄,多了文件不用考慮。

4)假設A機器可以免密登錄B機器

5)把有差異和丟失的文件列表存入文件/data/change.log


知識點一:A機器免密登錄B機器

這裏所謂的免密登錄指的是,A機器可以直接ssh B機器,不用輸入密碼。

實現免密登錄的方式有兩種:一個是使用expect腳本,另外一種是通過密鑰認證,具體操作步驟如下。


首先在A機器上生成一對密鑰,其中包含公鑰和私鑰,私鑰放在A機器上,公鑰放在B機器上,私鑰用來加密,公鑰用來解密。生成密鑰對的命令如下:

# ssh-keygen     //執行後一直按回車即可
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa):   //可以在這裏更改密鑰保存的路徑,如果按回車則存放到默認路徑下。
Enter passphrase (empty for no passphrase):   //這裏需要輸入密鑰的密碼,按回車則密鑰密碼爲空。
Enter same passphrase again:   //密碼確認。
Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/id_rsa.pub.

說明:

因爲當前用戶是root,所以會在root家目錄/root/下生成一個.ssh目錄,密鑰對在.ssh目錄下。


這樣在~/.ssh/目錄下就會生成兩個文件id_rsa和id_rsa.pub,其中id_rsa爲私鑰,id_rsa_pub爲公鑰。如果機器上已經存在了這樣的一對文件,就可以直接使用它們。

也可以指定文件的名字,如下:

# ssh-keygen -f /tmp/123     //這樣會在/tmp/下生成123和123.pub兩個文件

生成密鑰對後,需要把公鑰傳到目標機器上,假設B機器的IP爲192.168.0.110,則傳輸公鑰的方法爲:

# ssh-copy-id [email protected]   //這裏的root@可以省略掉,只寫IP。這個操作,需要我們手動輸入對方機器目標用戶(root)的密碼。

上面這個命令會把~/.ssh/id_rsa.pub文件內容寫到目標機器目標用戶(如root)家目錄下的.ssh/authorized_keys文件裏,注意這裏是追加寫入。

若想指定特定的私鑰,比如,上例中那個/tmp/123,命令是:

# ssh-copy-id -i /tmp/123 [email protected]  //這裏的-i選項後指定私鑰文件路徑,它可以自動產生公鑰。

做完上面的兩步,就可以用ssh命令登錄目標機器了,而且不用輸入密碼。它默認會找~/.ssh/id_rsa這個私鑰,但如果使用的另外的私鑰文件,還需要在ssh時指定文件路徑,如下:

# ssh -i /tmp/123 192.168.0.110


知識點二:find中過濾指定目錄

先看一個簡單的需求,要求查找當前目錄下除了123目錄(./123)外,所有的文件,有兩種方法。

方法一:

# find ./ -type f |grep -v '^\./123'

說明:這裏首先find出所有文件列表,然後再針對結果用grep排除掉123目錄。對於此方法,簡單的場景是沒問題的,如果某個文件的文件名中包含了123關鍵詞,就不好處理了。

方法二:

# find ./ -path './123*' -prune -o -type f

說明:-path類似於shell中的正則匹配,指定字符串作爲尋找目錄的範本樣式。-prune,不尋找字符串作爲尋找文件或目錄的範本樣式。-prune -o組合在一起使用,會把-prune前面的匹配排除掉,如果是要排除多個目錄,可以這麼用:

# find ./ \( -path './123*' -o -path './abc*' \) -prune -o -type f

注意:-path後面跟的那個目錄名字後面不要加/,否則就不正確。-o是-or的意思。

# ls
111  123  12333  222  233  333  abc
# ls 123
ioio  lsls
# ls 233/
ai
# ls 333/
ia
# ls abc/
ei
# find ./ \( -path './123*' -o -path './abc*' \) -prune -o -type f
./123
./233/ai
./333/ia
./111
./222
./12333
./abc

說明:當前目錄下,123目錄和abc目錄下的文件沒有被列出來,其他文件都列出來了,說明123和abc目錄下的文件已經被過濾排除。


知識點三:對比兩個文件是否有差異

可以使用diff命令,也可以對比兩個文件的md5sum值。diff命令能比較出差異來,並且會把具體差異的內容顯示出來,在本例中並不需要知道具體差異的內容,而且我們沒有辦法用diff命令對比本機和遠程機器上的兩個文件,所以只能使用md5sum。先看一下diff命令的用法:

# echo -e '123\n234' > 2.txt
# echo -e '123\nabc' > 1.txt
# cat 1.txt 
123
abc
# cat 2.txt 
123
234
# diff 1.txt 2.txt 
2c2
< abc
---
> 234

說明:echo -e可以識別換行\n,diff出來的結果,2c2指的是第二行有差異,<表示左邊的文件,>表示右邊的文件。

而md5sum命令這樣使用:

# md5sum 2.txt
0941216c25fdb3bcf7876f97bb79c865  2.txt

前面這個字符串,就是2.txt文件的md5值,如果修改一下2.txt,則md5值會發生改變:

# echo '123' >> 2.txt 
# md5sum 2.txt 
48ac7d30e9f2c450f2b9d9198577d0e2  2.txt

要想判斷兩個文件是不是一樣,只要對比兩個文件的md5值即可。


知識點四:嵌入文檔(Here Document)

嵌入文檔,英文名叫Here Document,大家可能見過,但恐怕並不知道這個專業術語,先看一段shell代碼:

#!/bin/bash
cat > 1.txt <<EOF
Hello
My
name
is aming.
This is a test text.
EOF

運行這個腳本後,會產生1.txt文檔,內容爲:

Hello
My
name
is aming.
This is a test text.

這個用法就是嵌入文檔。這裏的EOF叫做標識符,還可以換成其他字符串,比如寫成ABC、123等等,隨便自定義,但要保證前後對應,也不要造成和其他字符串混淆。通常大家習慣寫EOF,這樣也容易讓別人識別。就跟shell腳本名字以.sh結尾同樣的道理。


嵌入文檔需要注意一點,最後面那個標識符EOF必須要頂格寫,不一定非得cat << EOF,也可以換成其他命令,比如:

#!/bin/bash
wc -l <<EOF
1
2
3
EOF


知識點五:while循環遍歷文件

在shell中,用for循環遍歷一個文件內容時,也就是說按行來遍歷時會有一些問題。因爲for循環遍歷對象以空白字符或者換行作爲分隔符,例如下面文本用for循環則無法實現預期:

# vim test.txt
ab 1
123
234
# for s in `cat test.txt`;do echo $s;done
ab
1
123
234

而使用while循環,效果是這樣的:

# cat test.txt |while read s ;do echo $s;done
ab 1
123
234

所以,要想遍歷一個文件的所有行時,可以用while read循環來實現。


本案例參考腳本

結合以上五個知識點,得出最終參考腳本,內容如下:

#!/bin/bash
##對比兩臺機器上的文件差異
##作者:
##日期:
##版本:v0.2

#假設B機器IP爲192.168.0.110
B_ip=192.168.0.110
dir=/data/wwwroot/www.abc.com
#首先檢查/tmp/md5.list文件是否存在,若存在就刪掉,避免影響後續操作
[ -f /tmp/md5.list ] && rm -f /tmp/md5.list

#把除了uploads以及tmp目錄外的其他文件全部列出來
cd $dir
find . \( -path "./uploads*" -o -path "./tmp*" \) -prune -o -type f > /tmp/file.list

#用while循環,求出所有文件的md5值,並寫入一個文件裏
cat /tmp/file.list|while read line
do
  md5sum $line
done > /tmp/md5.list

#將md5.list拷貝到B機器
scp /tmp/md5.list $B_ip:/tmp/

#判斷/tmp/check_md5.sh文件是否存在
[ -f /tmp/check_md5.sh ] && rm -f /tmp/check_md5.sh

#用Here Document編寫check_md5.sh腳本內容
cat >/tmp/check_md5.sh << EOF
#!/bin/bash
dir=/data/wwwroot/www.abc.com
##注意,這裏涉及到的特殊字符都需要脫義,比如反引號和$
cd \$dir
n=\`wc -l /tmp/md5.list|awk '{print \$1}'\`
for i in \`seq 1 \$n\`
do
  file_name=\`sed -n "\$i"p /tmp/md5.list |awk '{print \$2}'\`
  md5=\`sed -n "\$i"p /tmp/md5.list |awk '{print \$1}'\`
  if [ -f \$file_name ]
  then
      md5_b=\`md5sum \$file_name|awk '{print \$1}'\`
  if [ \$md5_b != \$md5 ]
  then
     echo "\$file_name changed."
  fi
  else
     echo "\$file_name lose."
  fi
done > /data/change.log
EOF
scp /tmp/check_md5.sh $B_ip:/tmp/
ssh $B_ip "/bin/bash /tmp/check_md5.sh"

說明:

此腳本的思路是:

1)在A機器上遍歷所有文件,對每一個文件計算md5值存入一個臨時文件裏

2)把臨時文件拷貝到B機器,留着備用

3)編寫在B機器上要執行的對比md5值的腳本

4)將腳本傳到B機器

5)執行腳本


而在B機器上執行的腳本,思路是:

1)根據md5值臨時文件裏的文件列表,逐一計算B機器上文件的md5值

2)對比B機器上文件的md5值是否和A機器上的一樣,同時判斷該文件是否存在

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