背景
阿裡雲雲存儲網關支援以傳統檔案通路協定(NFS/SMB)來通路阿裡雲對象存儲服務,網關通過暴露一個檔案共享來和後端的OSS Bucket映射。使用者操作對應的檔案共享也就相當于在相應的OSS Bucket上進行操作,大大的便利了習慣于傳統檔案協定的使用者。
NFS網絡檔案系統是在Linux機器上通用的一種檔案系統,允許使用者能夠以本地檔案系統類似的方式通過網絡來通路遠端伺服器上的檔案。NFS自推出以來,曆經了NFSv2、NFSv3、NFSv4幾個版本,現在的Linux機器上基本都已經用的NFSv4的版本,NFSv3在一些稍微老些版本的機器上還有使用,NFSv2已經幾乎沒有人使用了。
雲存儲網關是支援以NFSv3和NFSv4兩個版本的協定來進行通路的,為了使這兩個版本都能支援(主要是為了兼顧NFSv3的支援),在實作方面實際上有些權衡,導緻預設情況下資料上傳到OSS Bucket的效率對NFSv4協定來說其實不是最優的。這也就是為什麼引入“NFS V4優化“這個選項的原因。
原了解析
使用過雲存儲網關的使用者應該都知道,在使用者寫入一個檔案到雲存儲網關的NFS共享裡面之後,雲存儲網關會在背景将這個檔案上傳到該共享所對應的OSS Bucket裡面。但是網關在什麼時候開始檔案的上傳呢?是這個檔案完全寫入完成才開始上傳呢?還是說檔案沒有完全寫完的時候就開始上傳了,邊寫邊傳呢?
OSS更新某個檔案或者說是對象的時候,是原子性的,要麼通過一次PutObject來完成,要麼通過調用Multipart Upload相關的API接口來完成,即使隻修改檔案的很少一部分,也是如此。是以對一個檔案如果寫入了10次,實際上最後調用一次API就好了。這樣比較理想的做法是在檔案做了關閉動作表明當次修改完成之後,再根據使用者是否對檔案做了修改來決定是不是需要上傳。聽起來很完美,對不對?對NFSv4協定來說,這确實是比較完美的。但是考慮到NFSv3協定的話,事情就有了一點點變化,因為NFSv3協定原生并沒有Close語義!!!使用者在用戶端關閉了某個檔案之後,從伺服器的角度來說,并不知道使用者關閉了檔案這件事情,是以對NFSv3而言我們并沒有辦法采用這邊提到的完美的上傳解決方案。
是以為了對NFSv3和NFSv4能夠提供一種通用的解決方案,之前檔案網關提供了“同步延遲”這個選項來控制檔案上傳的真正時機。這個選項的效果基本可以做到在絕大多數情況下,隻在檔案關閉的時候将這個檔案上傳到OSS Bucket。但是實際上它是通過檢測在一定時間段内是否有寫入來偵測是否需要上傳的,假設同步延遲的值是10秒,也就是說對某個檔案如果10秒内沒有新的寫入,就會嘗試将該檔案上傳,否則就會延遲上傳的時間。考慮到絕大數情況下使用者基本都是打開檔案然後持續的寫入最後關閉檔案,是以對這些情況基本是能夠做到在檔案關閉的時候才上傳這種效果的。
但是如果使用者打開檔案之後,并不是持續的寫入,而是間隔的寫入,并且寫入的間隔大于“同步延遲”的話(或者使用者想盡早看到檔案出現在OSS Bucket裡面而幹脆将同步延遲設定成了零)就可能會造成一些中間沒有意義的上傳。如何優化這種情況呢?
介紹到在這,相信您已經明白了新的“NFS V4優化“選項該出場了。考慮到現在絕大多數的Linux機器基本都已經支援NFSv4版本了,而且對絕大多數使用者來說并沒有強烈的需求需要指定使用NFSv3版本。是以“NFS V4優化”選項實作了前文提到的那種完美的方案,隻有在使用者在用戶端關閉了檔案的時候,才嘗試上傳檔案到OSS Bucket。在這個選項打開時,嘗試用NFSv3協定挂載共享就會失敗。
配置
在更新到最新版本的雲存儲網關之後就可以體驗“NFS V4優化”選項了,可以建立NFS共享的時候勾上這個選項,也可以對一個已有的共享打開或者關閉這個選項。如果您本地已經挂載了NFS共享,您可以使用mount指令看到目前挂載的NFS的版本。在筆者的CentOS 7.6上,目前挂載的NFS版本是4.1。
[root@iZbp116y3gfxxlyq58wu9iZ ~]# mount
...
172.16.159.155:/nfsv4optimize on /mnt/v4optimize type nfs4 (rw,relatime,vers=4.1,rsize=1048576,wsize=1048576,namlen=255,hard,proto=tcp,timeo=600,retrans=2,sec=sys,clientaddr=172.16.159.131,local_lock=none,addr=172.16.159.155)
這就說明你的機器預設已經支援了NFSv4,如果所有的用戶端上的輸出都具有“vers=4.x”這種輸出,那就可以很安全的打開“NFS V4優化”選項了。

打開該選項之後,嘗試用NFSv3的方式挂載的話會出錯。
[root@iZbp116y3gfxxlyq58wu9iZ ~]# mount 172.16.159.155:/nfsv4optimize /mnt/v4optimize/ -overs=3
mount.nfs: access denied by server while mounting 172.16.159.155:/nfsv4optimize
如果因為某些無法避免的原因必須要使用NFSv3的方法的挂載的話,可以動态關閉這個選項之後再嘗試挂載。
[root@iZbp116y3gfxxlyq58wu9iZ ~]# mount 172.16.159.155:/nfsv4optimize /mnt/v4optimize/ -overs=3
[root@iZbp116y3gfxxlyq58wu9iZ ~]# mount
...
172.16.159.155:/nfsv4optimize on /mnt/v4optimize type nfs (rw,relatime,vers=3,rsize=1048576,wsize=1048576,namlen=255,hard,proto=tcp,timeo=600,retrans=2,sec=sys,mountaddr=172.16.159.155,mountvers=3,mountport=32888,mountproto=udp,local_lock=none,addr=172.16.159.155)
驗證
接下來我們通過一個簡單的例子來看這個選項的效果。我們在用戶端寫了一個簡單的程式,它打開一個共享裡面的檔案,每一分鐘寫入一些内容,我們可以通過觀察OSS Bucket對應的這個檔案的修複時間來判斷到底上傳了幾次。
import time
with open("/mnt/v4optimize/v3test/test_file", "w") as f:
for i in range(1,5):
f.write("hello world!")
f.flush()
time.sleep(60)
為了性能預設的NFS挂載參數一般是以async的模式來挂載共享的,也就是說用戶端作業系統會緩存一部分資料,然後再一起寫到NFS共享裡面。為了消除這一部分的影響讓測試結果更精确,我們需要以sync的方式挂載該NFS共享來測試。
[root@iZbp116y3gfxxlyq58wu9iZ /]# mount 172.16.159.155:/nfsv4optimize /mnt/v4optimize/ -osync
[root@iZbp116y3gfxxlyq58wu9iZ /]# mount
...
172.16.159.155:/nfsv4optimize on /mnt/v4optimize type nfs4 (rw,relatime,sync,vers=4.1,rsize=1048576,wsize=1048576,namlen=255,hard,proto=tcp,timeo=600,retrans=2,sec=sys,clientaddr=172.16.159.131,local_lock=none,addr=172.16.159.155)
之後我們可以通過OSS控制台觀察這個檔案的修改時間來判斷到底發生了幾次上傳。
目前共享的同步延遲選項設定的是0秒。根據前面的判斷,不打開“NFS V4優化”選項,我們應該看到多次上傳。通過觀察會發現OSS Bucket裡面的檔案修改時間确實每一分鐘會修改一次,符合我們的預期。如果打開該優化選項,隻會看到檔案的修改時間變了一次。感興趣的使用者也可以自己做一下這個測試。
總結
本文主要介紹了雲存儲網關的“NFS V4優化”選項的工作原理,如果您并沒有需求必須使用NFSv3的方式挂載網關的NFS共享,建議您都以NFSv4的方式挂載并打開這個選項,進而獲得更理想的檔案上傳效率。