如何在Ubuntu上创建及管理LXC容器
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何在Ubuntu上创建及管理LXC容器相关的知识,希望对你有一定的参考价值。
参考技术A 虽然早在十多年前就引入了容器这个概念,用来安全地管理共享式主机托管环境(比如FreeBSD监狱),但LXC或Docker之类的Linux只是最近因日益需要为云计算部署应用程序而进入主流。虽然这阵子Docker备受媒体的关注,并且得到了各大云服务提供商(比如亚马逊AWS和微软Azure)以及发行版提供商(比如红帽和Ubuntu)的大力支持,但LXC实际上却是针对Linux平台开发的早期容器技术之一。如果你是普普通通的Linux用户,Docker/LXC可以带来什么样的好处呢?嗯,容器实际上是一种在诸发行版之间几乎即时切换的一种好方法。假设你当前的桌面系统是Debian,你需要Debian的稳定性。与此同时,你又想玩最新的Ubuntu游戏。然后,用不着很麻烦地通过双启动进入到Ubuntu分区,或者启动占用大量资源的Ubuntu虚拟机,只要立即启用一个Ubuntu容器即可,一切都搞定了。即便没有Docker的种种好处,我喜欢LXC容器的地方在于,LXC可以由libvirt接口来管理,Docker却不是这样。如果你之前一直使用基于libvirt的管理工具(比如virt-manager或virsh),就可以使用同样那些工具来管理LXC容器。我在本教程中着重介绍标准LXC容器工具的命令行用法,并且演示如何在Ubuntu上从命令行创建及管理LXC容器。将LXC安装到Ubuntu上想在Ubuntu上使用LXC,就要安装LXC用户空间工具,如下所示。$ sudo apt-get install lxc安装完毕之后,运行lxc-checkconifg工具,检查当前Linux内核支持LXC的情况。要是一切都已被启用,内核对LXC的支持已准备就绪。$ lxc-checkconfig安装LXC工具后,你会发现,LXC的默认网桥接口(lxcbr0)已自动创建(已在/etc/lxc/default.conf中加以配置)。创建LXC容器后,窗口的接口就会自动连接到该网桥,那样容器就能与外界进行联系了。创建LXC容器为了能够创建某个特定目标环境(比如Debian Wheezy 64位)的LXC容器,你就需要一个相应的LXC模板。幸运的是,Ubuntu上的LXC用户空间工具随带一系列预先准备好的LXC模板。你可以在/usr/share/lxc/templates目录下找到可用的LXC模板。$ ls /usr/share/lxc/templatesLXC模板其实就是一段脚本而已,用来为某个特定的Linux环境创建容器。你在创建LXC容器时,需要用到这其中一个模板。比如说,为了创建Ubuntu容器,使用下面这个命令行:$ sudo lxc-create -n -t ubuntu默认情况下,它会创建与本地主机同一版本号和同一架构的最小Ubuntu安装系统,这种情况下是Saucy Salamander(13.10)64位。如果你希望,可以创建任何一种版本的Ubuntu容器,只要传递release参数。比如说,想创建Ubuntu 14.10容器:$ sudo lxc-create -n -t ubuntu -- --release utopic它会下载并验证目标容器环境需要的所有程序包。整个过程可能需要几分钟或更长时间,具体取决于容器类型。所以请耐心点。经过一系列的程序包下载和验证后,LXC容器映像最终创建完毕,你会看到默认的登录资料可供使用。容器存储在/var/lib/lxc/<container-name>,根文件系统则位于/var/lib/lxc/<container-name>/rootfs。LXC创建过程中下载的所有程序包则缓存在/var/cache/lxc里面,那样使用同样的LXC模板创建额外的容器就不用花时间了。现在不妨看一下主机上的LXC容器列表:$ sudo lxc-ls –fancyNAME STATE IPV4 IPV6 AUTOSTART------------------------------------test-lxc STOPPED - - NO想启动一个容器,请使用下面这个命令。“-d”选项将容器作为守护程序来启动。要是没有这个选项,你在启动容器后,会直接被连接到控制台。$ sudo lxc-start -n -d启动容器后,不妨再次检查容器状态:$ sudo lxc-ls –fancyNAME STATE IPV4 IPV6 AUTOSTART-----------------------------------------lxc RUNNING 10.0.3.55 - NO你会看到,容器处于“RUNNING”(运行)状态,已被赋予了一个IP地址。你还可以证实,容器的接口(比如vethJ06SFL)自动连接到LXC的内部网桥(lxcbr0),如下所示。$ brctl show lxcbr0管理LXC容器既然我们已知道了如何创建及启动LXC容器,现在不妨看看我们可以如何处理运行中的容器。首先,我们想要访问容器的控制台。为此,键入这个命令:$ sudo lxc-console -n 键入<Ctrl+a q>组合键,退出控制台。想停止和销毁容器:$ sudo lxc-stop -n $ sudo lxc-destroy -n 想把现有容器克隆成另一个容器,使用这些命令:$ sudo lxc-stop -n $ sudo lxc-clone -o -n 故障排查如果你遇到了LXC方面的错误,下面是故障排查方面的几个要点。1. 你无法创建LXC容器,出现下列错误。$ sudo lxc-create -n test-lxc -t ubuntulxc-create: symbol lookup error: /usr/lib/x86_64-linux-gnu/liblxc.so.1: undefined symbol: cgmanager_get_pid_cgroup_abs_sync这意味着你运行最新的LXC,却使用较旧的libcgmanager(libcg管理器)。想解决这个问题,你就需要更新libcg管理器。$ sudo apt-get install libcgmanager0Linux Container(LXC)容器隔离实现机制
一、LXC概述
LXC(LinuxContainer)是来自于Sourceforge网站上的开源项目,LXC给Linux用户提供了用户空间的工具集,用户可以通过LXC创建和管理容器,在容器中创建运行操作系统就可以有效的隔离多个操作系统,实现操作系统级的虚拟化。最初的Docker容器技术基于LXC进行构建,后来Docker在自己的内核中刨除了LXC。
二、LXC命令介绍
从Linux内核2.6.27版本开始已经支持LXC,只需要在Linux用户态安装相应的用户态工具liblxc即可。下表3-1是LXC的常用命令接口:
三、LXC容器隔离实现机制
从前面的介绍中我们可以了解到,LXC能够创建容器用于Linux系统的虚拟化,而LXC作为用户层管理工具主要提供了管理容器的接口,对实现容器的机制进行了封装隐藏,本文将对LXC容器的实现机制进行分析。
LXC内部采用了Linux内核Namespace和Cgroup两个特性,下面我们将对这两种机制进行介绍。
3.1、Namespaee命名空间实现机制
Namespace命名空间机制给虚拟化提供了轻量级形式,即操作系统级虚拟化。该机制和FreeBSD的jail机制和OpenVZ类似。传统上,Linux系统中所有进程通过PID进行标识,内核只需要管理一个PID列表,而且用户通过uname系统调用获取的系统相关信息也全部相同。在Linux系统中,用户的管理方式通过UID编号,即通过全局唯一的UID列表进行标识。全局ID能够使内核很好的管理系统,选择允许或拒绝某些特权。如UID为0的root用户能够允许做任何事情,但是其他UID的用户就会受到限制;对于用户X无法杀死另一个用户Y的进程,但是用户X可以看到用户Y的活动状态,而这种状态并不适用于一些场景,比如对于隐私性要求较高的服务。
为了解决上述类似的问题,Namespace机制为进程ID,用户ID等系列资源的隔离提供了一种占用资源极少的解决方案,而其它虚拟化方案一般需要一台物理机运行多个内核进行上述资源的隔离,命名空间可以在一台物理机上运行一个内核,通过命名空间机制将上述全局资源进行抽象。它可以使一组进程放置于容器中,而各个容器间彼此隔离,也可以设置容器之间进行共享。从用户的角度看,命名空间将全局资源控制进行了分割放置到相应的容器中,在容器中进程只能看到本容器中的成员而无法看到其他容器的成员。
命名空间层次关系
在上图中,我们可以看到三个命名空间,父命名空间记录管理了6个PID值,而两个子命名空间父命名空间知道子命名空间的存在,而两个子命名空间不知道对方的存在,对于进程各个子命名空间中属性的改变无法将影响传播到其它命名空间(包括父命名空间),这样在两个子命名空间中运行Linux内核就可以很好的隔离两个系统。
命名空间在Linux内核中的表示
命名空间分别对UTS,进程间通信(IPC),文件系统视图,进程PID,UID和网络六个属性进行封装。
在每个Task任务结构中包含了一个nsproxy结构,这样对于每个创建的进程都有对应的命名空间,通过该结构可以建立进程与命名空间之间的关系,
对于命名空间的支持,需要在Linux内核编译的时候进行启动设置。如果没有设置,会使用系统默认的命名空间即整个内核就一个命名空间,全局可见。
对于每个具体的子命名空间实现形式,在此不再具体展开。
命名空间在用户空间的表示
命名空间机制在用户层通过调用系统调用clone实现,clone系统调用与fIork系统调用的最大区别就是通过传入众多的参数选择性的继承父进程的资源,而flork系统调用就会复制父进程的资源。
通过设置flags参数就可以创建新的命名空间,选择性的继承父进程的资源。
flags设定为CLONE NEWPID时会创建一个新的PID命名空间,即该新的命名空间为进程提供了一个新的独立的P1D环境,调用clone系统的进程的ID号为1,就成为该命名空间的第一个进程,相当于Linux系统的lnit进程,由于是起始PID命名空间中的祖先进程,如果该进程结束在这个命名空间中的所有进程都会被结束。PID的命名空间具有层次性,在父命名空间中的进程可以创建出子命名空间,而子命名空间对父命名空间可见。
flags设定为CLONE—NEWIPC时会创建一个新的IPC命名空间,如果该标记没有设定,进程会根据父进程的参数进程设置。针对system V对象和POSIX信号队列的进程间通信机制,IPC命名空间可以进行隔离,即当前进程的通信对象或队列在本命名空间中对其它进程可见,而在不同命名空间中的进程进行通信类似于不同Linux系统间的通信。
flags设定为CLONE NEWUTS时会创建一个新的UTS命名空间,初始结构会以调用进程的UTS进行初始化,通过调用setdomainname函数和sethostname函数可以分别设置域名和主机名,调用uname函数可以获得UTS信息。
flags设定为CLONE NEWNS时会创建新的mount挂载点命名空间,建立的挂载点命名空间就是进程可见的文件结构视图,所以通过挂载命名空间可以很好的进行文件系统的隔离。
flags设定为CLONE NEWNET时会创建一个新的网络命名空间,网络命名空间对网络栈进行了视图上的隔离,包括IPV4,IPV6,栈协议,ip路由表,防火墙规则等。一个物理网络设备只能一次对应一个网络命名空间,但通过创建虚拟网络设备创建的隧道可以与实际的物理网络设备进行通信,这样就可以实现多个网络设备的通信。
以上所述的flags标识可以组合使用,LXC调用该接口可以很方便的创建独立的运行环境,设定相应的参数,完成操作系统级的虚拟化隔离。
3.2、Cgroup实现机制
LXC通过命名空间机制实现了资源的隔离,而对于物理资源的限制则通过cgroup(control group)机制实现。
cgroup系统定义了以下的概念:
1)控制群(control group):控制群就是一组进程组,是cgroup系统中的控制的基本单位。
2)子系统:子系统是一类资源控制系统,比如CPU,是对c铲oup中进程的控制具体方法,通过子系统可以针对每个cgroup进行限制。目前cgroup机制支持的子系统如下表所示:
3)层级:层级是各个控制群以树形的方式进行排列,子节点的控制权继承父节点的树形。linux系统中一个进程在其中的一个控制群中,同时也在其中的一个层级下。一个层级通过cgroup系统的虚拟文件系统相对应。
它们之间的层级关系如下图所示:
cgroup层级关系
在Linux系统中第一个被用户建立的cgroup是根cgroup,包含了系统中所有的进程,位于第一层级。在上图中根cgroup又被分为两个子的cgroup系统,即cgroup0和cgroupl,位于cgroup系统的第二层。一个子系统只能在一个层级上,即在第一层级的cpuset子系统不同加入第二层级中的cgroup0和cgroupl,因为前面已经说过层级中子节点需要继承父节点的属性,如果两个层级中都有则会出现重复。每个层级可以有多个子系统,如第二层级有cpu和memo巧子系统。对于进程来说,可以位于不同层级的cgroup中。
cgroup在linux内核层的表示
cgroup相关的数据结构和命名空间类似,也是从进程管理结构开始。如果需要使用cgroup机制需要在Linux内核编译的时候开启CONFIG_CGROUPS宏。在css_set结构中存储了与进程相关cgroups信息,在cg_list将在同一个css_set的进程组链接起来。Css_set结构如下
css_set结构中最重要成员就是cgroup_subsys_state指针数组,在进程和cgroup之间没有直接的联系,需要通过cgroup_subsys_state间接指向cgroup结构,这样做主要是因为有的时候读取子系统状态会比较频繁而对cgroup赋值等操作较少,struct cgroup中有一个成员cgroupfsroot结构,就是用于我们前面所说的层级关系的具体实现。
对于子系统的管理,则通过cgroup_subsys数据结构进行管理。在该结构中定义了一组操作的接口函数指针,相当于c++的基类,给出了接口函数具体的行为需要针对每个子系统的特点进行自行定义,比如cgroup subsys_state接口每个子系统返回的信息是不同的,需要不同的实现,这样的设计就可以很好的完成多种类型的兼容。
cgroup在Linux用户层的表示
cgroup在linux用户层通过cgroup文件系统方式表示出来。比如在用户空间可以通过执行如下命令:mount -t cgroup -o cpu,memory cpu_mem/mycgroup/cpu_mem就可以创建名字为cpu_mem的层级,这个层级中有cpu和memory两个子系统,这两个系统可以是挂载至lJ/mycgroup/cpu_mem文件下。这一个创建cgroup层级的过程就如同创建新的文件夹的过程,只是创建的是cgroup特殊文件系统。如果在cpu_mem系统中想要创建一个新的cgroup,只要进入cpu_mem文件目录下,执行命令mkdir newcgroup就可以创建一个叫newcgroup的控制群,这样也表示新创建的cgroup是属于cpu_mem这一个层级。进入newcgroup文件目录后,会有相关子系统的控制文件,可以读取或者修改这些控制文件,比如向的tasks文件中写入当前系统中某个进程的pid号就相当于将进程加入了新创建的cgroup控制群中。
本文出自 “我拿流年乱了浮生” 博客,请务必保留此出处http://tasnrh.blog.51cto.com/4141731/1760061
以上是关于如何在Ubuntu上创建及管理LXC容器的主要内容,如果未能解决你的问题,请参考以下文章