Linux 容器的使用
Linux 容器在 v2.6.29版本之后就加入到内核之中了, 之前虽然也听说过, 但一直没有太留心, 一直使用 KVM 来创建虚拟机.
直至最近 Docker 大出风头, 才开始关注. 想了解一下 Linux 容器究竟是什么? 与现有虚拟机技术(Xen, KVM等)有什么区别?
Linux 容器技术出现的很早, 其实也是一直虚拟化技术, 但似乎一直没有 Xen, KVM 这些来的出名.
同时, 在实现原理上, 和Xen, KVM之类的也是有很大区别的.
下面简单说明下目前4类虚拟技术的区别: (下面说明中, VM:虚拟机, HOST:主机, 即安装虚拟机的机器)
-
传统的虚拟化技术 (VirtualBox, VMware)
通过在Linux上安装虚拟化软件, 然后通过虚拟化软件来安装虚拟机系统, 大致结构如下:VM1 VM2 VM3 ... ... VirtualBox or VMWare or ... Linux Kernel 硬件 VM是由虚拟化软件(VirtualBox, VMWare…)来管理的, Linux Kernel不能直接管理到各个VM.
-
Xen (半虚拟化)
Xen是Linux上历史比较长的虚拟化技术, 它的虚拟化结构大致如下:Linux Kernel VM1 VM2 VM3 ... ... Xen 硬件 Xen的虚拟化原理是在 Linux Kernel和硬件之间加入一层 Xen代码, 有Xen来管理Linux Kernel和其它的VM.
-
KVM (最新的虚拟化技术)
相比其它的虚拟化技术, KVM是比较新的, 它需要CPU的支持. 它的虚拟化结构大致如下: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).从上面可以看出, LXC这种虚拟化, VM的进程就像HOST的进程一样运行, 管理, 所以创建和销毁都是非常快速的.
注: 参考 http://veck.logdown.com/posts/200566-compare-of-kvm-and-lxc
Linux 容器相关的2个重要概念
Linux容器功能是基于 cgroups 和 Namespace 来实现的. 所以要了解 Linux 容器必须先了解 cgroup 和 Namespace.
cgroups
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 (命名空间)
使用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 空间 |
Linux 容器的使用方法 (以下命令基于 debian v7.5)
-
安装 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
容器示例 - 配置python uliweb 开发环境
尝试在容器配置一次开发环境, 然后通过复制容器, 形成多个虚拟机.
# 主机中
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服务.