http://www.tuicool.com/articles/7v2ai22
【编者的话】Docker核心解决的问题是利用LXC来实现类似VM的功能,从而利用更加节省的硬件资源提供给用户更多的计算资源。而 LXC所实现的隔离性主要是来自内核的命名空间, 其中pid、net、ipc、mnt、uts 等命名空间将容器的进程、网络、消息、文件系统和hostname 隔离开。本文是Linux命名空间系列教程的第一篇,通过一个简单的例子介绍了Linux容器以及UTS命名空间。DockerOne在 撸代码 的基础上进行了校对和整理。
我在 OVH 工作的时候,曾经为一款“即将发布”的产品添加安全机制,而这项工作需要用到Linux的命名空间(Linux namespace)。在学习过程中我发现,Linux的命名空间很强大,但是这方面的文档却非常少。
大多数人都应该听说过 LXC——LinuX Containers ,它是一个加强版的Chroot。简单的说,LXC就是将不同的应用隔离开来,这其有点类似于chroot,chroot是将应用隔离到一个虚拟的私有root下,而LXC在这之上更进了一步。LXC内部依赖Linux内核的3种隔离机制(isolation infrastructure):
我本可以将这个系列命名为《如何构建你自己的LXC》,从而赢得更高的Google排名,但这样做可能会显得我太狂妄。事实上,LXC所做的事情远不止隔离,它还包含模板管理、冻结以及其它更多的功能。这个系列将为为你解开LXC的神秘面纱。
在这个系列中,我们将使用最简的C程序启动/bin/bash ,并逐步为它加入各种隔离机制。
好了,现在我们开始吧。
有意思的是,Linux的容器工具容器并没有提供一个类似黑盒般神秘的容器解决方案,而是提供单独隔离构件(isolation building block),统称为Namespaces。每一个新版本都会有新的构件发布出来。这样让你就可以跟你特定应用的情况,选择所需要的构件。
Linux的3.12内核支持6种Namespace:
如下是一个程序的完整的骨架,用于从子进程启动/bin/bash程序(为了保持例子简单,所以去掉了错误检查):
main-0-template.c
static char child_stack[STACK_SIZE];
char* const child_args[] = {
"/bin/bash",
NULL
};
int child_main(void* arg) {
printf(" - World !\n");
execv(child_args[0], child_args);
printf("Ooops\n");
return 1;
}
int main() {
printf(" - Hello ?\n");
int child_pid = clone(child_main, child_stack + STACK_SIZE, SIGCHLD, NULL);
waitpid(child_pid, NULL, 0);
return 0;
}
请注意,在这里我们使用的是 clone
而不是 fork
系统调用。这正是魔法(即将)发生的地方。
jean-tiare@jeantiare-Ubuntu:~/blog$ gcc -Wall main.c -o ns && ./ns
- Hello ?
- World !
jean-tiare@jeantiare-Ubuntu:~/blog$ # inside the container
jean-tiare@jeantiare-Ubuntu:~/blog$ exit
jean-tiare@jeantiare-Ubuntu:~/blog$ # outside the container
OK,帅呆了。不过假如没有注释,你很难注意到我们在子进程的/bin/bash中。事实上,当我在写这篇文章的时候,好几次不小心退出父进程的shell。
现在做一些修改,如直接修改主机名(the hostname with 0% env vars tricks),是不是会很牛逼?就单单用Namespace?很简单,我们只要:
clone
系统调调用中使用 CLONE_NEWUTS
的标志sethostname
main-1-uts.c
// (needs root privileges (or appropriate capabilities)) //[...] int child_main(void* arg) { printf(" - World !\n"); sethostname("In Namespace", 12); execv(child_args[0], child_args); printf("Ooops\n"); return 1; } int main() { printf(" - Hello ?\n"); int child_pid = clone(child_main, child_stack+STACK_SIZE, CLONE_NEWUTS | SIGCHLD, NULL); waitpid(child_pid, NULL, 0); return 0; }
运行一下:
jean-tiare@jeantiare-Ubuntu:~/blog$ gcc -Wall main.c -o ns && sudo ./ns
- Hello ?
- World !
root@In Namespace:~/blog$ # inside the container
root@In Namespace:~/blog$ exit
jean-tiare@jeantiare-Ubuntu:~/blog$ # outside the container
事情就是这个样子(至少这篇文章中是这样)!namespace真是太TM容易上手了:clone
,设置特定的 CLONE_NEW*
标志,设置新的env,搞定!
你希望更加深入?有兴趣可以阅读一下 excellent LWN article series on namespaces 。
原文链接: https://blog.jtlebi.fr/2013/12 ... -uts/