用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")
}
谢谢阅读。
下一篇: PHP位运算 详细说明