Closed helios741 closed 5 years ago
容器就是一个进程,已经在宿主机上对容器进行了隔离,对容器资源进行限制,那么容器的文件系统呢?
容器的文件系统也应该和network/PID一样和宿主机进行隔离,这个就要用到了Mount Namespace了。
Mount Namespace
看一下在DOCKER基础技术:LINUX NAMESPACE(上)的一个程序:
#define _GNU_SOURCE #include <sys/types.h> #include <sys/wait.h> #include <stdio.h> #include <sched.h> #include <signal.h> #include <unistd.h> /* 定义一个给 clone 用的栈,栈大小1M */ #define STACK_SIZE (1024 * 1024) static char container_stack[STACK_SIZE]; char* const container_args[] = { "/bin/bash", NULL }; int container_main(void* arg) { printf("Container - inside the container!\n"); /* 直接执行一个shell,以便我们观察这个进程空间里的资源是否被隔离了 */ execv(container_args[0], container_args); printf("Something's wrong!\n"); return 1; } int main() { printf("Parent - start a container!\n"); /* 调用clone函数,其中传出一个函数,还有一个栈空间的(为什么传尾指针,因为栈是反着的) */ int container_pid = clone(container_main, container_stack+STACK_SIZE, CLONE_NEWNS | SIGCHLD, NULL); /* 等待子进程结束 */ waitpid(container_pid, NULL, 0); printf("Parent - container stopped!\n"); return 0; }
我们通过clone系统调用创建了一个新的子进程container_main,并且声明要为它启动Mount Namespace(CLONE_NEWNS)。
container_main
小知识🈯️:
为什么Mount Namespace对应的宏叫做CLONE_NEWNS而不是CLONE_NEWMOUNT的呢,这是因为Mount Namespace是Linux中的第一个namespace,当时以为就这一个了,所以也就这样了。
CLONE_NEWNS
CLONE_NEWMOUNT
编译:
# gcc -o ns ns.c # ./ns Parent - start a container! Container - inside the container!
这时候我们的shell就在container_main这个进程里面,我们可以通过ps看到只有这个namespace的进程。但是我们通过ls /或者ls /tmp发现文件系统并没有做隔离,还都是宿主机的文件系统。
这个是因为容器进程会默认继承父进程的文件系统,所以创建新进程的时候还有显示去声明那些目录要重新mount,比如我们要重新挂在/tmp这个目录,所以子进程的代码可以改为:
int container_main(void* arg) { printf("Container - inside the container!\n"); mount("none", "/tmp", "tmpfs", 0, ""); // 告诉容器以tmpfs(内存盘)的格式重新挂在/tmp目录 /* 直接执行一个shell,以便我们观察这个进程空间里的资源是否被隔离了 */ execv(container_args[0], container_args); printf("Something's wrong!\n"); return 1; }
编译之后我们通过ls /tmp查看这个目录下面就没有什么文件了。
ls /tmp
我们每次启动容器,大多时候不是想继承父进程的文件系统,我们首先想到的就是直接把根目录(/)重新挂在了不就完了么。
在Linux中的chroot就能帮助搞定这个事情,chroot的用法比较简单,
通过chroot就能把ubuntu/centos的文件系统挂在到容器的根目录。这个就是rootfs了。
由于rootfs打包的不仅仅是应用程序,而是将整个操作系统的文件和目录一起打包进去,这也就意味着应用和它的依赖,这也就解决了云端和本地的配置不一样的问题了。
分为三层:只读层(readonly + whiteout),init层(ro + wh),可读写层(rw)
只读层(readonly + whiteout)
init层(ro + wh)
可读写层(rw)
ubuntu和centos用得不一样
docker的volume在这里面说一说
rootfs
容器就是一个进程,已经在宿主机上对容器进行了隔离,对容器资源进行限制,那么容器的文件系统呢?
容器的文件系统也应该和network/PID一样和宿主机进行隔离,这个就要用到了
Mount Namespace
了。看一下在DOCKER基础技术:LINUX NAMESPACE(上)的一个程序:
我们通过clone系统调用创建了一个新的子进程
container_main
,并且声明要为它启动Mount Namespace(CLONE_NEWNS)。小知识🈯️:
为什么Mount Namespace对应的宏叫做
CLONE_NEWNS
而不是CLONE_NEWMOUNT
的呢,这是因为Mount Namespace是Linux中的第一个namespace,当时以为就这一个了,所以也就这样了。编译:
这时候我们的shell就在container_main这个进程里面,我们可以通过ps看到只有这个namespace的进程。但是我们通过ls /或者ls /tmp发现文件系统并没有做隔离,还都是宿主机的文件系统。
这个是因为容器进程会默认继承父进程的文件系统,所以创建新进程的时候还有显示去声明那些目录要重新mount,比如我们要重新挂在/tmp这个目录,所以子进程的代码可以改为:
编译之后我们通过
ls /tmp
查看这个目录下面就没有什么文件了。我们每次启动容器,大多时候不是想继承父进程的文件系统,我们首先想到的就是直接把根目录(/)重新挂在了不就完了么。
在Linux中的chroot就能帮助搞定这个事情,chroot的用法比较简单,
通过chroot就能把ubuntu/centos的文件系统挂在到容器的根目录。这个就是rootfs了。
由于rootfs打包的不仅仅是应用程序,而是将整个操作系统的文件和目录一起打包进去,这也就意味着应用和它的依赖,这也就解决了云端和本地的配置不一样的问题了。
rootfs的分层
分为三层:
只读层(readonly + whiteout)
,init层(ro + wh)
,可读写层(rw)
联合文件系统(UnionFS)
ubuntu和centos用得不一样
docker的volume在这里面说一说
参考