Linux 容器在 v2.6.29版本之后就加入到内核之中了, 之前虽然也听说过, 但一直没有太留心, 一直使用 KVM 来创建虚拟机.直至最近 Docker 大出风头, 才开始关注. 想了解一下 Linux 容器究竟是什么? 与现有虚拟机技术(Xen, KVM等)有什么区别?
Linux 容器技术出现的很早, 其实也是一直虚拟化技术, 但似乎一直没有 Xen, KVM 这些来的出名.同时, 在实现原理上, 和Xen, KVM之类的也是有很大区别的.下面简单说明下目前4类虚拟技术的区别: (下面说明中, VM:虚拟机, HOST:主机, 即安装虚拟机的机器)
传统的虚拟化技术 (VirtualBox, VMware) 通过在Linux上安装虚拟化软件, 然后通过虚拟化软件来安装虚拟机系统, 大致结构如下:
VM1VM2VM3...... |
VirtualBoxorVMWareor... |
LinuxKernel |
硬件 |
VM是由虚拟化软件(VirtualBox, VMWare…)来管理的, Linux Kernel不能直接管理到各个VM.
Xen (半虚拟化) Xen是Linux上历史比较长的虚拟化技术, 它的虚拟化结构大致如下:
LinuxKernelVM1VM2VM3...... |
Xen |
硬件 |
Xen的虚拟化原理是在 Linux Kernel和硬件之间加入一层 Xen代码, 有Xen来管理Linux Kernel和其它的VM.
KVM (最新的虚拟化技术) 相比其它的虚拟化技术, KVM是比较新的, 它需要CPU的支持. 它的虚拟化结构大致如下:
VM1VM2VM3...... |
KVM(由内核管理) |
LinuxKernel |
硬件 |
这个结构和传统的虚拟化技术很类似, 有一点不同的是, KVM和Linux Kernel是紧密结合的,所以Linux Kernel能够更好的管理 VMs, VM的性能会比传统的虚拟化技术更好.
Linux 容器 (LXC - linux container) LXC 是非常轻量级的, 它将 VM 的进程也伪装成 HOST 的进程. 大致的结构如下:
p1(HOST),p2(VM),p3(VM),p4(HOST)...... |
LinuxKernel |
硬件 |
那么, 对于某些系统进程, PID是固定的, 比如 init进程的PID=1, VM中的 init进程的PID是如何处理的呢?原来, VM的 init进程的PID在 HOST的进程表中会显示成其它PID(>1).
从上面可以看出, LXC这种虚拟化, VM的进程就像HOST的进程一样运行, 管理, 所以创建和销毁都是非常快速的.
注: 参考 http://veck.logdown.com/posts/200566-compare-of-kvm-and-lxc
Linux 容器相关的2个重要概念Linux容器功能是基于 cgroups 和 Namespace 来实现的. 所以要了解 Linux 容器必须先了解 cgroup 和 Namespace.
cgroupscgroups 是将任意进程进行分组化管理的 Linux 内核功能.通过cgroups可以有效的隔离各类进程, 同时还可以控制进程的资源占用(CPU, 内存等等)情况.使用示例: (debian v7.6 x86_64)
mount -t tmpfs cgroup_root /sys/fs/cgroupmkdir /sys/fs/cgroup/testmount -t cgroup -ocpuset test /sys/fs/cgroup/test
此时, test目录就是一个 cgroup, 这里 -o 指定了 cpuset, cpuset是Linux中既定的一种cgroup, 后面有时间重新写博客详细介绍.test 目录有cgroup必须的各个文件
cd /sys/fs/cgroup/testls -ltotal 0-rw-r--r-- 1 root root 0 Aug 14 14:34 cgroup.clone_children--w--w--w- 1 root root 0 Aug 14 14:34 cgroup.event_control-rw-r--r-- 1 root root 0 Aug 14 14:34 cgroup.PRocs-rw-r--r-- 1 root root 0 Aug 14 14:34 cpuset.cpu_exclusive-rw-r--r-- 1 root root 0 Aug 14 14:34 cpuset.cpus-rw-r--r-- 1 root root 0 Aug 14 14:34 cpuset.mem_exclusive-rw-r--r-- 1 root root 0 Aug 14 14:34 cpuset.mem_hardwall-rw-r--r-- 1 root root 0 Aug 14 14:34 cpuset.memory_migrate-r--r--r-- 1 root root 0 Aug 14 14:34 cpuset.memory_pressure-rw-r--r-- 1 root root 0 Aug 14 14:34 cpuset.memory_pressure_enabled-rw-r--r-- 1 root root 0 Aug 14 14:34 cpuset.memory_spread_page-rw-r--r-- 1 root root 0 Aug 14 14:34 cpuset.memory_spread_slab-rw-r--r-- 1 root root 0 Aug 14 14:34 cpuset.mems-rw-r--r-- 1 root root 0 Aug 14 14:34 cpuset.sched_load_balance-rw-r--r-- 1 root root 0 Aug 14 14:34 cpuset.sched_relax_domain_level-rw-r--r-- 1 root root 0 Aug 14 14:34 notify_on_release-rw-r--r-- 1 root root 0 Aug 14 14:34 release_agent-rw-r--r-- 1 root root 0 Aug 14 14:34 tasks
其中部分文件介绍.
文件名 | R/W | 用途 |
---|---|---|
release_agent | RW | 删除分组时执行的命令. 这个文件只存在于根分组 |
notify_on_release | RW | 设置是否执行 release/_agent. 为1时执行 |
tasks | RW | 属于分组的线程 TID 列表 |
cgroup.procs | R | 属于分组的进程 PID 列表. 仅包括多线程进程的线程leader的TID, 这点与 tasks 不同 |
cgroup.event_control | RW | 监视状态变化的分组删除事件的配置文件 |
在cgroup中还可以建立子cgroup, 建立的方法很简单, 只要创建文件夹即可.
cd /sys/fs/cgroup/testmkdir test-childls -l test-child # 创建了文件夹之后, 自动生成cgroup需要的文件total 0-rw-r--r-- 1 root root 0 Aug 14 15:10 cgroup.clone_children--w--w--w- 1 root root 0 Aug 14 15:10 cgroup.event_control-rw-r--r-- 1 root root 0 Aug 14 15:10 cgroup.procs-rw-r--r-- 1 root root 0 Aug 14 15:10 cpuset.cpu_exclusive-rw-r--r-- 1 root root 0 Aug 14 15:10 cpuset.cpus-rw-r--r-- 1 root root 0 Aug 14 15:10 cpuset.mem_exclusive-rw-r--r-- 1 root root 0 Aug 14 15:10 cpuset.mem_hardwall-rw-r--r-- 1 root root 0 Aug 14 15:10 cpuset.memory_migrate-r--r--r-- 1 root root 0 Aug 14 15:10 cpuset.memory_pressure-rw-r--r-- 1 root root 0 Aug 14 15:10 cpuset.memory_spread_page-rw-r--r-- 1 root root 0 Aug 14 15:10 cpuset.memory_spread_slab-rw-r--r-- 1 root root 0 Aug 14 15:10 cpuset.mems-rw-r--r-- 1 root root 0 Aug 14 15:10 cpuset.sched_load_balance-rw-r--r-- 1 root root 0 Aug 14 15:10 cpuset.sched_relax_domain_level-rw-r--r-- 1 root root 0 Aug 14 15:10 notify_on_release-rw-r--r-- 1 root root 0 Aug 14 15:10 tasks
注意, 删除子cgroup的时候, 要用 rmdir 来删除文件夹, 用 rm -rf 的方法无法删除
cd /sys/fs/cgroup/testrmdir test-child
注: 参考内核文档 Documentation/cgroups/cgroups.txt
Namespace (命名空间)使用Namespace, 可以让每个进程组有独立的PID, ipC和网络空间.Namespace的生效主要是通过 clone系统调用来实现的.clone系统调用的第3个参数flags就是通过设置Namespace来划分资源的.参数种类如下:
名称 | 说明 |
---|---|
CLONE_NEWIPC | 划分IPC(进程间通信)命名空间, 信号量(semaphore), 共享内存, 消息队列等进程间通信用的资源 |
CLONE_NEWNET | 划分网络命名空间. 分配网络接口 |
CLONE_NEWNS | 划分挂载的命名空间. 与chroot同样分配新的根文件系统 |
CLONE_NEWPID | 划分 PID 命名空间. 分配新的进程ID空间 |
CLONE_NEWUTS | 划分 UTS(Universal Time sharing System)命名空间. 分配新的 UTS 空间 |
安装 LXC
apt-get install lxclxc-checkconfig # 安装完成后, 用这个命令检查系统是否可以使用 lxc# 我的debian系统上有个 missingCgroup namespace: CONFIG_CGROUP_NSmissing# 对于这个missing, 可能是由于系统中没有挂载cgroup导致的, 挂载一个cgroup即可mount -t cgroup cgroup /mnt/cgroup
创建容器 从现有模板创建容器, 比较慢, 需要下载
# 创建一个 debian 系统lxc-create -n test -t debian
这样创建的容器默认在 /var/lib/lxc/test 中, 为了将容器创建在我们指定的位置, 可以写个简单的配置文件lxc.conf, 里面只需要一句
lxc.rootfs = /home/lxc/test
然后,
lxc-create -n test -t debian -f /path/to/lxc.conf
这样, 就把容器创建在了 /home/lxc/test 中了, /var/lib/lxc/test 中只有一个 config文件(这个config文件可以作为 lxc-create 命令 -f 参数对应配置文件的参考)
启动容器 启动后就进行入了虚拟机的控制台了. (果然像传说一样, 几秒就启动完成了 ^_^)
lxc-start -n test
停止容器 在主机中输入停止的命令.
lxc-stop -n test
销毁容器 销毁之前, 可以通过 lxc-ls 来查看有几个容器
lxc-ls testlxc-destroy -n testlxc-ls
注: 参考URL - http://obdnmagazine.blogspot.com/2013/07/tested-lxc-080-rc1-debian-wheezyax3a6.html
容器示例 - 配置python uliweb 开发环境尝试在容器配置一次开发环境, 然后通过复制容器, 形成多个虚拟机.
# 主机中root@debian-113:~# uliweb # 主机中没有安装uliweb 软件包-bash: uliweb: command not foundroot@debian-113:~# lxc-start -n test# 虚拟机登录界面, 输入用户名和密码# 虚拟机中root@test:~# apt-get install pythonroot@test:~# apt-get install python-piproot@test:~# pip install Uliwebroot@test:~# uliweb --versionUliweb version is 0.3.1
主机中设置网桥, 虚拟机用桥接方式上网, 确保每个虚拟机有独立的IP
# 主机中root@debian-113:~# lxc-stop -n testroot@debian-113:~# apt-cache search bridge-utilsroot@debian-113:~# brctl addbr br0# 配置主机的网桥root@debian-113:/var/lib/lxc/test# cat /etc/network/interfaces # This file describes the network interfaces available on your system# and how to activate them. For more information, see interfaces(5).# The loopback network interfaceauto lo#auto eth0iface lo inet loopback# 追加的网桥配置 auto br0iface br0 inet staticaddress 192.168.1.113netmask 255.255.255.0gateway 192.168.1.1 bridge_ports eth0 bridge_stp on bridge_fd 0root@debian-113:/var/lib/lxc/test# /etc/init.d/networking restart
配置容器的网络(也是在主机中修改容器的配置文件)
root@debian-113:/var/lib/lxc/test# cat /var/lib/lxc/test/config... ... (很多默认生成的配置)# network <-- 这个 network 相关的是要追加的lxc.network.type = vethlxc.network.flags = uplxc.network.link = br0lxc.network.name = eth0
启动Linux容器, 进入虚拟机
root@debian-113:/var/lib/lxc/test# lxc-start -n test# 登录进入虚拟机, 确认虚拟机的IProot@test:~# cat /etc/network/interfaces <-- 默认是自动获取IPauto loiface lo inet loopbackauto eth0iface eth0 inet dhcproot@test:~# ifconfig <-- 我的机器自动分配的 192.168.1.167# 创建一个简单的uliweb工程root@test:~# cd /home/root@test:/home# mkdir CM-webroot@test:/home# cd CM-web/root@test:/home/CM-web# uliweb makeproject testroot@test:/home/CM-web# cd test/root@test:/home/CM-web/test# uliweb makeapp first_approot@test:/home/CM-web/test# uliweb runserver -h 0.0.0.0
启动Web服务后, 就可以在主机的浏览器中 通过 http://192.168.1.167:8000/ 来访问虚拟机中的web服务了.
最后, 复制一个新的容器, 也就是再重新生成一个上面的 python uliweb 开发环境
# 在主机中root@debian-113:~# cd /var/lib/lxcroot@debian-113:/var/lib/lxc# cp -r test test2# 修改 test2/config 如下lxc.utsname = test2 <-- 修改名称xc.rootfs = /home/lxc/test2 <-- 修改 rootfs位置... ... <-- 其它部分不用修改, 和 test 一样就行root@debian-113:/var/lib/lxc# cd /home/lxc/root@debian-113:/home/lxc# cp -r test test2 <-- 重新复制一份 rootfsroot@debian-113:/home/lxc# lxc-start -n test2 <-- 启动 test2 虚拟机, 其中环境和 test一样, IP会不一样, 自动获取的# 进入 test2 虚拟机中, 可以直接启动之前的 uliweb 测试工程, 也可以从主机中访问其web服务.
新闻热点
疑难解答