Linux 容器在 v2.6.29版本以后就加入到内核之中了, 以前虽然也据说过, 但一直没有太留心, 一直使用 KVM 来建立虚拟机.
直至最近 Docker 大出风头, 才开始关注. 想了解一下 Linux 容器到底是什么? 与现有虚拟机技术(Xen, KVM等)有什么区别?html
Linux 容器技术出现的很早, 其实也是一直虚拟化技术, 但彷佛一直没有 Xen, KVM 这些来的出名.
同时, 在实现原理上, 和Xen, KVM之类的也是有很大区别的.
下面简单说明下目前4类虚拟技术的区别: (下面说明中, VM:虚拟机, HOST:主机, 即安装虚拟机的机器)python
传统的虚拟化技术 (VirtualBox, VMware)
经过在Linux上安装虚拟化软件, 而后经过虚拟化软件来安装虚拟机系统, 大体结构以下:linux
VM1 VM2 VM3 ... ... |
VirtualBox or VMWare or ... |
Linux Kernel |
硬件 |
VM是由虚拟化软件(VirtualBox, VMWare…)来管理的, Linux Kernel不能直接管理到各个VM.web
Xen (半虚拟化)
Xen是Linux上历史比较长的虚拟化技术, 它的虚拟化结构大体以下:c#
Linux Kernel VM1 VM2 VM3 ... ... |
Xen |
硬件 |
Xen的虚拟化原理是在 Linux Kernel和硬件之间加入一层 Xen代码, 有Xen来管理Linux Kernel和其它的VM.浏览器
KVM (最新的虚拟化技术)
相比其它的虚拟化技术, KVM是比较新的, 它须要CPU的支持. 它的虚拟化结构大体以下:bash
VM1 VM2 VM3 ... ... |
KVM (由内核管理) |
Linux Kernel |
硬件 |
这个结构和传统的虚拟化技术很相似, 有一点不一样的是, KVM和Linux Kernel是紧密结合的,
因此Linux Kernel可以更好的管理 VMs, VM的性能会比传统的虚拟化技术更好.网络
Linux 容器 (LXC - linux container)
LXC 是很是轻量级的, 它将 VM 的进程也假装成 HOST 的进程. 大体的结构以下:多线程
p1(HOST), p2(VM), p3(VM), p4(HOST)...... |
Linux Kernel |
硬件 |
那么, 对于某些系统进程, PID是固定的, 好比 init进程的PID=1, VM中的 init进程的PID是如何处理的呢?
原来, VM的 init进程的PID在 HOST的进程表中会显示成其它PID(>1).app
从上面能够看出, LXC这种虚拟化, VM的进程就像HOST的进程同样运行, 管理, 因此建立和销毁都是很是快速的.
注: 参考 http://veck.logdown.com/posts/200566-compare-of-kvm-and-lxc
Linux容器功能是基于 cgroups 和 Namespace 来实现的. 因此要了解 Linux 容器必须先了解 cgroup 和 Namespace.
cgroups 是将任意进程进行分组化管理的 Linux 内核功能.
经过cgroups能够有效的隔离各种进程, 同时还能够控制进程的资源占用(CPU, 内存等等)状况.
使用示例: (debian v7.6 x86_64)
mount -t tmpfs cgroup_root /sys/fs/cgroup mkdir /sys/fs/cgroup/test mount -t cgroup -ocpuset test /sys/fs/cgroup/test
此时, test目录就是一个 cgroup, 这里 -o 指定了 cpuset, cpuset是Linux中既定的一种cgroup, 后面有时间从新写博客详细介绍.
test 目录有cgroup必须的各个文件
cd /sys/fs/cgroup/test ls -l total 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/test mkdir test-child ls -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/test rmdir test-child
注: 参考内核文档 Documentation/cgroups/cgroups.txt
使用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 lxc lxc-checkconfig # 安装完成后, 用这个命令检查系统是否可使用 lxc # 个人debian系统上有个 missing Cgroup 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 test lxc-destroy -n test lxc-ls
注: 参考URL - http://obdnmagazine.blogspot.com/2013/07/tested-lxc-080-rc1-debian-wheezyax3a6.html
尝试在容器配置一次开发环境, 而后经过复制容器, 造成多个虚拟机.
# 主机中 root@debian-113:~# uliweb # 主机中没有安装uliweb 软件包 -bash: uliweb: command not found root@debian-113:~# lxc-start -n test # 虚拟机登陆界面, 输入用户名和密码 # 虚拟机中 root@test:~# apt-get install python root@test:~# apt-get install python-pip root@test:~# pip install Uliweb root@test:~# uliweb --version Uliweb version is 0.3.1
主机中设置网桥, 虚拟机用桥接方式上网, 确保每一个虚拟机有独立的IP
# 主机中 root@debian-113:~# lxc-stop -n test root@debian-113:~# apt-cache search bridge-utils root@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 interface auto lo #auto eth0 iface lo inet loopback # 追加的网桥配置 auto br0 iface br0 inet static address 192.168.1.113 netmask 255.255.255.0 gateway 192.168.1.1 bridge_ports eth0 bridge_stp on bridge_fd 0 root@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 = veth lxc.network.flags = up lxc.network.link = br0 lxc.network.name = eth0
启动Linux容器, 进入虚拟机
root@debian-113:/var/lib/lxc/test# lxc-start -n test # 登陆进入虚拟机, 确认虚拟机的IP root@test:~# cat /etc/network/interfaces <-- 默认是自动获取IP auto lo iface lo inet loopback auto eth0 iface eth0 inet dhcp root@test:~# ifconfig <-- 个人机器自动分配的 192.168.1.167 # 建立一个简单的uliweb工程 root@test:~# cd /home/ root@test:/home# mkdir CM-web root@test:/home# cd CM-web/ root@test:/home/CM-web# uliweb makeproject test root@test:/home/CM-web# cd test/ root@test:/home/CM-web/test# uliweb makeapp first_app root@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/lxc root@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 <-- 从新复制一份 rootfs root@debian-113:/home/lxc# lxc-start -n test2 <-- 启动 test2 虚拟机, 其中环境和 test同样, IP会不同, 自动获取的 # 进入 test2 虚拟机中, 能够直接启动以前的 uliweb 测试工程, 也能够从主机中访问其web服务.