文章目录
umount <挂载点>
在Linux系统中,如果你使用 mount -o bind
命令创建了一个绑定挂载,你可以使用 umount
命令来卸载(解除挂载)这个挂载点。umount
命令用于卸载文件系统或挂载点。
下面是卸载绑定挂载的基本语法:
umount <挂载点>
umount
:这是命令本身,用于执行卸载操作。注意,umount
的拼写中没有字母n
,尽管在某些系统上也可以使用unmount
作为别名。<挂载点>
:这是要卸载的挂载点的路径。在绑定挂载的情况下,这是你之前使用mount -o bind
命令挂载的目录。
例如,如果你之前创建了一个绑定挂载:
mount -o bind /host/directory/ /container/directory/
你可以使用以下命令来卸载这个挂载点:
umount /container/directory/
执行这个命令后,/container/directory/
将不再是一个挂载点,容器将无法访问宿主机上的 /host/directory/
目录。
在某些情况下,如果你尝试卸载一个正在被使用的挂载点,umount
命令可能会返回一个错误,表示设备正忙。为了解决这个问题,你可以使用 fuser
命令来查找并终止使用该挂载点的进程,或者使用 lsof
命令来列出打开的文件和挂载点。
在Go语言中,如果你需要卸载一个挂载点,你可以使用 syscall
包中的 Syscall
函数来调用底层的 umount
系统调用,但这通常不是推荐的做法,因为它涉及到直接的系统调用,可能会导致代码的可移植性降低。通常,卸载挂载点的操作是在命令行中手动执行的,或者在容器化环境中通过容器运行时的命令来实现。
os.Mkdir
在Go语言中,os.Mkdir
函数用于创建一个新的目录。如果你尝试使用 os.Mkdir
创建一个已经存在的目录,函数会返回一个错误。这个错误通常是 syscall.EEXIST
,它是 os
包中的一个常量,表示文件已存在。
下面是你的代码片段的详细解释:
if err := os.Mkdir(containervolume_inhost, 0777); err != nil {
log.Infof("mkdir hostvolume error: %v", err)
}
os.Mkdir(containervolume_inhost, 0777)
:这行代码尝试创建一个名为containervolume_inhost
的新目录,并设置其权限为0777
,这是一个非常开放的权限设置,允许所有用户读、写和执行。err != nil
:这是一个条件判断,检查os.Mkdir
函数是否返回了非空的错误值。如果返回了错误,那么err
将不会是nil
。log.Infof("mkdir hostvolume error: %v", err)
:如果os.Mkdir
返回了错误,这段代码会记录一个信息级别的日志,包含错误信息。%v
是一个格式化动词,用于打印错误信息的默认格式。
如果 containervolume_inhost
目录已经存在,os.Mkdir
会返回一个错误,你的代码会捕获这个错误并记录日志。然而,这段代码没有提供任何处理已存在目录的逻辑,它只是记录了错误。
为了更好地处理这种情况,你可以添加额外的逻辑来检查目录是否已经存在,而不是仅仅依赖于错误处理。例如:
if _, err := os.Stat(containervolume_inhost); os.IsNotExist(err) {
// 目录不存在,创建目录
if err := os.Mkdir(containervolume_inhost, 0777); err != nil {
log.Infof("mkdir hostvolume error: %v", err)
}
} else {
// 目录已经存在
log.Infof("Directory %s already exists", containervolume_inhost)
}
在这个修改后的代码中,我们首先使用 os.Stat
函数来检查目录是否存在。如果目录不存在,我们再尝试创建它。如果目录已经存在,我们记录一个信息,说明目录已经存在,而不尝试创建它。这样可以避免不必要的错误日志。
mount -o bind <源目录> <目标目录>
在Linux系统中,mount
命令用于挂载文件系统,而 -o bind
选项用于创建绑定挂载。下面是这个命令的语法解释:
mount -o bind <源目录> <目标目录>
mount
:这是命令本身,用于执行挂载操作。-o
:这是mount
命令的一个选项,用于指定挂载选项。-o
后面跟着的是逗号分隔的挂载选项列表。bind
:这是-o
选项后面的一个挂载选项,表示要创建一个绑定挂载。绑定挂载允许你将一个目录的内容挂载到另一个位置,而不改变原始目录的内容。<源目录>
:这是要挂载的源目录的路径。在绑定挂载中,这个目录的内容将被复制到目标目录。<目标目录>
:这是挂载点的路径,即源目录的内容将被挂载到这个位置。
例如,命令 mount -o bind /host/directory/ /container/directory/
的解释如下:
/host/directory/
:这是宿主机上的源目录路径,它包含了你想要在容器内部访问的文件和目录。/container/directory/
:这是容器内部的目标目录路径,宿主机上的源目录将被挂载到这里。
执行这个命令后,容器内的/container/directory/
目录将直接映射到宿主机的/host/directory/
目录。这意味着在容器内部对/container/directory/
目录的任何更改都会反映到宿主机的/host/directory/
目录,反之亦然。
绑定挂载在容器化环境中非常有用,因为它允许容器访问宿主机上的文件系统,这对于共享配置文件、日志文件或数据文件等非常有用。然而,需要注意的是,绑定挂载可能会带来安全风险,因为容器可以访问宿主机上的文件系统,所以应该谨慎使用,并确保只有授权的容器可以访问敏感的宿主机目录。
在Docker中,绑定挂载可以通过-v
或--volume
选项来实现,例如:
docker run -v /host/directory/:/container/directory/ ...
这个命令与mount -o bind
命令的效果类似,但它是在Docker容器启动时自动执行的。
卸载overlayfs
时里含有绑定挂载点
当您尝试卸载一个OverlayFS文件系统,而该文件系统中包含通过mount --bind创建的绑定挂载点时,可能会遇到无法卸载成功的问题。这主要是因为Linux内核为了防止数据损坏或一致性问题,不允许直接卸载一个包含活动挂载点的文件系统。
修改merged层中的绑定挂载点对upper层
对/merged/tmp(实际上是访问的/volume的内容,因为它被bind挂载到这里)的修改理论上不会直接影响到OverlayFS定义的upper层。这是因为bind挂载创建了一个新的、独立的访问点到原始的/volume目录,它在文件系统层次上“遮盖”了OverlayFS原本的lower和upper结构。
fmt.Errorf
fmt.Errorf
函数返回的是一个 error
类型的值。在Go语言中,error
是一个内置的接口类型,定义如下:
type error interface {
Error() string
}
这个接口有一个方法 Error()
,它返回一个字符串,通常是描述错误的文本。fmt.Errorf
函数使用类似于 fmt.Printf
的格式化字符串来创建一个新的错误值,并返回这个值。
在你的例子中:
err := fmt.Errorf("invalid volume [%s], must split by `:`", volume)
这里创建了一个新的错误值,并将其赋值给变量 err
。这个错误值描述了一个无效的卷,指出卷应该通过冒号 :
进行分割。如果这个错误值被传递给一个函数或方法,并且该函数或方法期望接收一个 error
类型的参数,那么这个错误值就可以被传递进去。
例如,你可以在函数中返回这个错误值:
func validateVolume(volume string) error {
parts := strings.Split(volume, ":")
if len(parts) != 2 {
return fmt.Errorf("invalid volume [%s], must split by `:`", volume)
}
// 继续处理...
return nil
}
在这个例子中,validateVolume
函数检查传入的卷是否正确地通过冒号分割成两部分。如果分割后的切片长度不是2,函数就会返回一个错误值。如果一切正常,函数返回 nil
表示没有错误。
实现目标
- 启动容器时通过-v参数指定volume 实现指定的位置的文件数据在容器结束后的变化保留在主机上
- 将需要保留变化的文件通过bind mount使得主机和容器内的文件保持同步变化
流程
- 实现-v命令
- 这里的 “volume” 指的是在容器化技术(如Docker)中使用的卷(volume)的概念。
- -v命令会进行bind mount操作使得文件同步
- 在Docker中,-v 或 --volume 选项用于将宿主机的目录挂载到容器中,或者创建一个命名的卷。
- 退出时候先卸载同步的文件挂载点然后再卸载overlayfs,最后才删除,删除依旧是删除overlayfs,因为overlayfs包括挂载点
代码
https://github.com/FULLK/llkdocker/tree/main/volume_docker
-
实现挂载操作是在子进程(容器)运行起来之前
-
先挂载overlayfs,然后绑定挂载数据卷,这样主机上的volume就同步到了merged的容器的数据卷的位置
-
此时如果在容器中对容器的与主机绑定的数据卷建立文件
-
注意由于先挂载overlayfs再绑定挂载,此时绑定挂载点已经不属于overlayfs了,所以在绑定里面的修改将不会有overlayfs的机制(绑定挂载点如果在lower,但此时由于是绑定挂载,相当于并没有挂载到overlayfs,所以overlayfs机制对该挂载点不会生效,相当于lower层绑定挂载点变化,但upper层不会变化,merged绑定挂载点变化)
-
实现卸载操作是在子进程结束之后
-
此时先卸载绑定挂载点,因为绑定挂载点在overlayfs里面,如果先卸载overlayfs就会发现里面还有一个挂载点,此时卸载就会失败,
-
退出后在宿主机查看挂载点,发现成功卸载
-
绑定挂载内容也在主机上保存了
-
重新启动容器,此时由于绑定挂载,容器内的数据卷内容保存生效