在云化的时代大背景下,docker的出现带火了容器技术。Kernel的namespace功能特性的出现, 使容器的实现更为简单。
这里是一个最简单的例子。
源代码:simple_container
在开始之前, 需要先了解namespace。
namespace是linux kernerl的特性,由五个namespace组成:mount namespace、ust namespace、ipc namespace、network namespace、user namespace。
每一个namespace代表一类资源的隔离,这些namespace是通过linux的系统调用clone()
创建的:
child_pid = clone(startContainer, ct->stack + ct->stacksize, \
CLONE_NEWIPC | CLONE_NEWNET | CLONE_NEWNS | CLONE_NEWPID \
CLONE_NEWUTS|SIGCHLD, ct);
通过clone()创建的子进程将在新建的namespace中运行,被从宿主机中隔离出来。
容器的根文件系统,也就是容器的image,立马需要包含容器运行时需要的文件。
这里做一个最简单根文件系统,里面只包含一个bash,基于centos系统。
执行下面的脚本,将相应的文件写入到指定目录中:
#!/bin/bash
if [[ ! $# -eq 1 ]]
then
echo "usage: $0 directory"
exit
fi
LOCPATH=`pwd`
DstDir="${LOCPATH}/$1"
echo "The Dst Path is: %DstDir [any key continue]"
read
yum install --installroot=$DstDir -y rootfiles
yum install --installroot=$DstDir -y bash
社区提出了一个App Container Spec。对容器的镜像文件格式做了约定。
这里只阐述容器的原理,就不在镜像格式上做深入探讨了。
代码托管在github上:源代码
里面就做了两件事情:在父进程中通过系统调用clone
创建一个子进程,并分配namespace;在子进程中加载/proc,切换rootfs。
运行结果如下:
[root@localhost bin]# ./Liker ../rootfs/ # rootfs是我们准备的images
[root@localhost /]# alias # 这时候已经处于我们自己的容器中了!注意当前路径已经切换到了/
alias cp='cp -i'
alias mv='mv -i'
alias rm='rm -i'
[root@localhost /]# exit # 退出容器
exit
Finished!
[root@localhost bin]#
这虽然只是一个简单的namespace的使用, 但是却可以使容器技术不再显得那么神秘。
通过namespace达到了隔离容器的目录, 在资源使用上,容器和宿主系统上的其它进程享有同样的待遇。如果一个容器申请了大量的资源(比如内存), 必然会影响到其它进程的运行。 所以我们还需要限制一个容器能够使用的资源总量。使用cgroup可以达到此目的。
可以通过cgroup限制或保障以下资源:
1. CPU, 容器可以使用的CPU核心与CPU时间,Cpusets、CPU Accounting Controller。
2. 内存, 容器可以使用的内存, Memory Resource Controller。
容器可以使用的HugePage, HugeTLB Controller。
3. 磁盘, 容器对磁盘IO的占用, Block IO Controller。
4. 网络, 容器对带宽的占用,Network Classifier、Network Priority。
5. 权限, 容器可以操作的设备, Device Whitelist Controller。