文章目录
overlayfs
就是联合文件系统,将多个文件联合在一起成为一个统一的视图。
overlayfs 一般分为 lower、upper、merged 和 work 4个目录。
- lower 只读层,该层数据不会被修改
- upper 可读写层,所有修改都发生在这一层,即使是修改的 - lower 中的数据。
- merged 视图层,可以看到 lower、upper 中的所有内容
- work 则是 overlayfs 内部使用
当在容器中新建文件
修改当前upper层,但lower层没有改变
exec.Command("tar", "-xvf", busyboxTarURL, "-C", busyboxURL).CombinedOutput()
exec.Command("tar", "-xvf", busyboxTarURL, "-C", busyboxURL).CombinedOutput()
这行代码会启动一个新的进程来执行tar
命令,并且它会自动执行。
CombinedOutput
调用这个方法包括执行命令并收集标准输出(stdout)和标准错误(stderr)的组合输出。如果命令执行成功,它会返回一个字节数组包含输出内容;如果执行过程中有错误,这个错误(一般命令执行失败)会被返回。
exec.Command
exec.Command
函数在Go语言中用于执行外部命令,其基本格式为:
cmd := exec.Command(command, arg1, arg2, ..., argN)
这里,command
是您想要执行的外部命令的名称(例如ls
、curl
等),而arg1
到argN
是传递给该命令的参数。参数之间以逗号分隔,并且每个参数都是一个单独的字符串,即使是那些在命令行中看起来像是一个整体的参数(例如带有空格的文件路径),也需要作为一个整体字符串传递。
格式差异
-
命令与参数: 执行不同命令时,主要的格式差异在于命令本身的名称以及它需要的参数。每个命令根据其功能会有不同的参数需求和格式。例如,
ping
命令可能需要一个主机名作为参数,而cp
命令则需要源文件路径和目标文件路径。 -
传递选项与参数: 一些命令支持长选项(如
--help
)和短选项(如-h
),这些选项的使用和它们后面的参数(如果有的话)也是exec.Command
参数的一部分。例如,exec.Command("grep", "-r", "pattern", "directory")
。 -
环境变量与工作目录: 除了命令和参数,
exec.Command
还允许通过Cmd.Env
和Cmd.Dir
字段设置环境变量和工作目录。这不改变基本的命令格式,但会影响命令执行的环境。 -
输入输出重定向: 如你之前代码所示,可以使用
Cmd.Stdin
、Cmd.Stdout
、Cmd.Stderr
来重定向命令的标准输入、输出和错误流。这不影响命令的格式,但影响其交互方式。
挂载mount
- 准备 busybox 目录作为只读层lower
- 准备可读写层upper
- 准备merged层,将可读写层和只读层挂载到merged层(以上操作都是在容器没启动之前运行的)
- 容器(子进程)运行时pivotRoot 切换目录(在子进程切换到命令行之前)
package contain
import(
log "github.com/sirupsen/logrus"
"os/exec"
"os"
)
func prepare_overlays(rooturl string){
create_lower(rooturl)
create_upper(rooturl)
create_merged(rooturl)
create_work(rooturl)
mount_overlays(rooturl)
log.Infof("prepare_overlays suceess")
}
func create_lower(rooturl string){
busyboxurl:=rooturl+"/busybox"
busytarurl:=rooturl+"/busybox.tar"
_, err := os.Stat(busyboxurl)
if err != nil {
if os.IsNotExist(err) {
log.Infof("文件不存在")
if err:=os.Mkdir(busyboxurl,0777);err!=nil{
log.Infof("mkdir error")
}
if _,err=exec.Command("tar", "-xvf", busytarurl, "-C", busyboxurl).CombinedOutput(); err!=nil{
log.Infof("tar busybox.tar error")
}
} else {
log.Infof("无法获取文件信息: %v\n", err)
}
}
}
func create_upper(rooturl string){
upperurl:=rooturl+"/upper"
if err:=os.Mkdir(upperurl,0777);err!=nil{
log.Infof("mkedir upper error")
}
}
func create_merged(rooturl string){
mergedurl:=rooturl+"/merged"
if err:=os.Mkdir(mergedurl,0777);err!=nil{
log.Infof("mkedir merged error")
}
}
func create_work(rooturl string){
workurl:=rooturl+"/work"
if err:=os.Mkdir(workurl,0777);err!=nil{
log.Infof("mkedir merged error")
}
}
func mount_overlays(rooturl string){
mnturl:=rooturl+"/merged"
dirs:="lowerdir="+rooturl+"/busybox"+",upperdir="+rooturl+"/upper"+",workdir="+rooturl+"/work"
cmd:=exec.Command("mount","-t","overlay","overlay","-o",dirs,mnturl)
if err:=cmd.Run();err!=nil{
log.Infof("mount overlay error")
log.Error(err)
}
}
卸载unmount
- 在容器运行退出后(子进程退出后程序环境改变,此时根目录也改变,所以此时可以卸载并删除之前的根目录)卸载unmount挂载点
- 删除upper merged work层,lower不变
package contain
import(
log "github.com/sirupsen/logrus"
"os/exec"
"os"
)
func end_overlays(rooturl string){
unmount_overlays(rooturl)
delete_upper_work_merged(rooturl)
}
func unmount_overlays(rooturl string){
mntrul:=rooturl+"/merged"
cmd:=exec.Command("umount",mntrul)
if err:=cmd.Run();err!=nil{
log.Infof("unmount merged error")
}
}
func delete_upper_work_merged(rooturl string){
upperurl:=rooturl+"/upper"
mergedurl:=rooturl+"/merged"
workurl:=rooturl+"/work"
if err:=os.RemoveAll(upperurl);err!=nil{
log.Infof("delete upper error")
}
if err:=os.RemoveAll(mergedurl);err!=nil{
log.Infof("delete merged error")
}
if err:=os.RemoveAll(workurl);err!=nil{
log.Infof("delete work error")
}
}
代码地址
https://github.com/FULLK/llkdocker
结果演示
- sudo运行后upper层是没有任何东西的,但只要运行命令就会产生一个root文件夹,因为是以root用户执行。可能名字对文件夹有修改影响,因为merged的root文件夹里没有任何东西
- 新建文件,修改在upper中出现,merged中也出现,但lower没有出现
- 查看文件内容
- exit后