用go寫一個docker(7)-linux的AUFS檔案系統
namespace和cgroup解決的是容器的資源隔離和限制問題。容器的另一個特點是映象分層,我們可以在基礎映象上加自己定製的東西。今天我們就以AUFS為例,來看看docker的檔案系統。
什麼是AUFS
AUFS 的全稱是 Advanced Multi-layered unification filesytem,它的主要功能是:把多個目錄結合成一個目錄,對外使用。以下面為例,我們先直觀地體驗一下。
1.準備工作
準備三個目錄,分別命名為:base,mnt,top。在base目錄下建立名為base.txt 和 common.txt檔案,在top目錄下建立名為common.txt和top.txt檔案,mnt目錄為空。
[email protected]:~/aufs2# ls [email protected]:~/aufs2# mkdir base mnt top [email protected]:~/aufs2# touch base/base.txt base/common.txt [email protected]:~/aufs2# touch top/common.txt top/top.txt [email protected]:~/aufs2# tree . ├── base │ ├── base.txt │ └── common.txt ├── mnt └── top ├── common.txt └── top.txt 3 directories, 4 files
寫點內容到這些檔案中:
[email protected]:~/aufs2# echo 'base' > base/base.txt
[email protected]:~/aufs2# echo 'base' > base/common.txt
[email protected]:~/aufs2# echo 'top' > top/common.txt
[email protected]:~/aufs2# echo 'top' > top/top.txt
2.使用AUFS掛載
用aufs把base和top 掛載到mnt:
mount -t aufs -o br=./top:./base none ./mnt
稍微說下這裡的mount命令引數:
-t aufs:mount的檔案型別,這裡使用aufs
-o:傳遞個 aufs 的選項,每個檔案型別的選項不同
none:這個本來是裝置的名字,但是我們並沒有用到任何裝置,只會用到資料夾,因此這裡為 none
./mnt:掛載點,也就是我們要把目錄最終掛到哪個目錄
執行該命令後,在mnt目錄下會看到有了三個檔案:
[email protected]:~/aufs2# ls mnt/
base.txt common.txt top.txt
我們看看這三個檔案的內容:
[email protected]:~/aufs2# cat mnt/base.txt
base
[email protected]:~/aufs2# cat mnt/common.txt
top
[email protected]:~/aufs2# cat mnt/top.txt
top
可以看到common.txt檔案裡的內容是top/common.txt的。
3.體驗
我們修改一下mnt目錄下的base.txt的內容,再看看base目錄下的bast.txt檔案有無變化:
[email protected]:~/aufs2# echo 'mnt' > mnt/base.txt
[email protected]:~/aufs2# cat mnt/base.txt
mnt
[email protected]:~/aufs2# cat base/base.txt
base
可以看到base/base.txt並無變化。此時看下top目錄,會發現top下多了一個base.txt,並且內容是mnt:
[email protected]:~/aufs2# ls top/
base.txt common.txt top.txt
[email protected]:~/aufs2# cat top/base.txt
mnt
是不是有點神奇,這個讀寫步驟是怎麼樣的呢?
AUFS讀寫步驟
預設情況下,最上層的目錄為讀寫層,且只有一個(比如top就是最上層)
下層可以有一個或多個只讀層(比如base和top)
讀檔案時,從最上層開始逐層往下找,讀取第一個找到的檔案
寫檔案時,如果最上層有該檔案,則直接寫該檔案;否則從上往下逐層找,找到檔案後把檔案複製到最
上層,寫這個複製的檔案;如果都沒有找到該檔案,則在最上層建立一個。
刪除檔案時,會在最上層建立一個以.wh開頭加上檔名的檔案,比如在mnt下執行rm base.txt命令後,會在top目錄下建立一個.wh.base.txt的檔案,標記該檔案已刪除,但不會刪除base目錄下的base.txt
在go上測試使用
例項程式碼如下:
package main
import (
"os"
"os/exec"
"log"
)
const path = "/opt/aufs/"
func writeFile(filename string, content string){
f, err := os.OpenFile(filename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
log.Fatal(err)
}
_, err = f.Write([]byte(content))
if err != nil {
log.Fatal(err)
}
defer f.Close()
}
func main(){
// 定義目錄
baseDir := path + "base"
topDir := path + "top"
mntDir := path + "mnt"
// 建立目錄
if err := os.MkdirAll(baseDir, 0777); err != nil {
log.Fatal(err)
}
if err := os.MkdirAll(topDir, 0777); err != nil {
log.Fatal(err)
}
if err := os.MkdirAll(mntDir, 0777); err != nil {
log.Fatal(err)
}
// 建立檔案
writeFile(baseDir + "/" + "common.txt","base\n")
writeFile(baseDir + "/" + "base.txt","base\n")
writeFile(topDir + "/" + "common.txt","top\n")
writeFile(topDir + "/" + "top.txt","top\n")
// 掛載
dirs := "br=" + topDir + ":" + baseDir
cmd := exec.Command("mount", "-t", "aufs", "-o", dirs, "none",mntDir)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
log.Fatal(err)
}
// 寫一個檔案測試
writeFile(mntDir + "/" + "test.txt","test\n")
}
謝謝閱讀。