LXC使用指南

[b]目录

目录1

1lxc简介1

2lxc安装1

2.1系统及内核版本1

2.2安装lxc1

3lxc工具集2

3.1创建和销毁容器2

3.1.1lxc-create2

3.1.2lxc-destroy2

3.2在容器中运行与结束应用2

3.2.1lxc-execute和lxc-start2

3.2.2lxc-stop3

3.3lxc-console3

3.4冻结和解冻容器3

3.4.1lxc-freeze3

3.4.2lxc-unfreeze3

3.5获取容器的信息4

3.5.1lxc-ls4

3.5.2lxc-info4

3.5.3lxc-ps4

3.5.4lxc-netstat4

3.5.5lxc-cgroup4

3.6监控容器4

3.6.1lxc-monitor4

3.6.2lxc-wait5

3.6.3lxc-kill5

4lxc配置5

4.1配置主机名5

4.2配置容器使用的虚拟化网络6

4.3容器伪终端和系统控制台6

4.4挂载点7

4.5根文件系统7

4.6控制组配置7

4.6.1控制组CPU配置7

4.6.2块设备配置8

4.6.3CUP计数信息(非配置信息)8

4.6.4CPU集合配置8

4.6.5设备白名单10

4.6.6内存配置10

4.6.7冷冻配置11

4.6.8网络通信配置11

4.6.9权限配置11

4.6.10其他配置项12

5连接容器实例12

6网络流量配置15

7附录和参考17

7.1brctl命令简介17

7.2tc命令简介17

7.3超级用户权限18

1lxc简介

容器有效地将由单个操作系统管理的资源划分到孤立的组中,以更好地在孤立的组之间平衡有冲突的资源使用需求。与虚拟化相比,这样既不需要指令级模拟,也不需要即时编译。容器可以在核心CPU本地运行指令,而不需要任何专门的解释机制。此外,也避免了准虚拟化(paravirtualization)和系统调用替换中的复杂性。

2lxc安装

2.1系统及内核版本

Fedora13,2.6.33内核

2.2安装lxc

1)安装lxc工具包

#yuminstalllxc

2)检查内核是否支持lxc容器

#lxc-checkconfig

如果输出所有项均为enbale状态,则表明内核支持lxc容器,否则需重新编译内核打开相应功能项。

3)通过shell在容器中运行一个程序(超级用户下)

a)在当前目录下编译配置文件conf.txt,其内容为:

#Simplestlxcconfiguration

lxc.utsname=name

b)挂载cgroup

#mkdir/cgroup

#mount–tcgroupcgroup/cgroup

c)输入如下shell命令

#lxc-execute-ntest-fconf.txt/bin/echo“helloworld”

d)输出结果为

helloworld

4)编辑/etc/fstab,使得在系统启动时自动挂载cgroup

#/bin/echo“cgroup/cgroupcgroupdefaults00”>>/etc/fstab

3lxc工具集

3.1创建和销毁容器

3.1.1lxc-create

lxc-create-nname[-fconfig_file][-ttemplate]

创建一个系统对象来存储配置和用户信息,该对象保存/usr/local/var/lib/lxc(注意该目录为版本相关的,不同版本的lxc可能不同)目录下,name为其标示符。如果配置文件未指定,则系统会支持默认的资源隔离:PID,sysvIPC和挂载点。

例如:

#lxc-create-ntest

创建容器test,可在/usr/local/var/lib/lxc目录下发现创建了目录test,该文件中包含了默认的配置文件config

3.1.2lxc-destroy

lxc-destroy-nname

销毁一个名称为name的容器,主要删除在/usr/local/var/lib/lxc中相应的文件,注意一定要在容器处于STOPPED状态时才能销毁容器.

例如:

#lxc-destroy-ntest

销毁容器test.

3.2在容器中运行与结束应用

3.2.1lxc-execute和lxc-start

a)lxc-execute-nname[-fconfig_file][-sEKY=VAL]command

在指定的容器name中运行由command指定的命令。如果容器name未创建,则lxc-execute使用指定的配置文件创建容器name。该命令通过中间进程lxc-init来启动需要运行的命令,并且lxc-init在创建命令后会等待该命令执行结束。因此lxc-init进程的pid为1,运行的命令的pid为2。

b)lxc-start-nname[-fconfig_file][-sKEY=VAL]command

lxc-start在指定容器name中运行command指定的命令。注意lxc-start不支持孤儿进程组和守护进程,且如果command未指定,则会运行/sbin/init命令。

例如:

#lxc-create-ntest–fconf.txt/bin/bash#运行bash

#lxc-start-ntest/bin/bash#运行bash

注意,lxc-create用于在容器中运行一个应用,而lxc-start则用于在容器中运行一个系统。

3.2.2lxc-stop

lxc-stop-nname

终止容器name的运行。

例如:

#lxc-stop-ntest#终止容器test的运行

3.3lxc-console

如果创建容器时候配置了tty,可通过如下命令连接到tty

lxc-console-ntest-t3#连接到容器test的3号tty上。

3.4冻结和解冻容器

3.4.1lxc-freeze

lxc-freeze-nname

冻结所有在容器name中运行的程序,此处冻结是指挂起所有在指定容器中运行的程序。必须显式的使用lxc-ufreeze命令解除冻结。

#lxc-freeze-nfoo#冻结容器foo中运行的所有程序

3.4.2lxc-unfreeze

lxc-unfreeze-nname

解除容器name的冻结状态.

#lxc-ufreeze-nfoo#将foo中冻结的程序恢复运行。

注意:该功能需要内核打开相应的功能!

3.5获取容器的信息

3.5.1lxc-ls

列出系统中存在的所有容器,选项和ls命令相同。

3.5.2lxc-info

lxc-info-nname

列出容器name的运行信息:输出格式如下:

nameisstopped

nameisrunning

3.5.3lxc-ps

显示特定容器的pids,选项和ps相同,为内置的ps

3.5.4lxc-netstat

显示指定容器的网络状态,该命令建立在命令netstat之上,因此接受于netstat相同的参数。

3.5.5lxc-cgroup

lxc-cgroup-nnamesubsystem[value]

显示或者设置与控制组相关联的子系统的值,如果value未指定则显示其值,否则设定相应子系统的值。

例如:

lxc-cgroup-nfoodevices.list#显示允许容器foo使用的设备列表

lxc-cgroup-nfoocpuset.cpus“0,3”#将处理器0和3分配给容器foo

3.6监控容器

3.6.1lxc-monitor

lxc-monitor-nname

监控指定容器的状态,其中名称为正则表达式。

例如:

lxc-monitor-n‘foo|bar’#监控容器foo和bar

lxc-monitor-n‘.*’#监控所有容器

lxc-monitor-n‘[f|b].*’#监控所有名称以f或b开始的容器

3.6.2lxc-wait

lxc-wait-nname-sstates

等待容器到达某一状态,然后退出,其中states可为若干状态.

例如:

lxc-wait-nfoo‘RUNNING’#等待容器到RUNNING状态

lxc-wait-nfoo‘RUNNING|STOPPED’#等待容器到达RUNNING或者STOPPED状态

注意:容器的状态分为:STOPPED,STARTING,RUNNING,STOPPING,ABORTING.

3.6.3lxc-kill

lxc-kill-nnameSIGNUM

lxc-kill将信号SIGNUM发送给容器中的第一个进程。

例如:

lxc-kill-nfoo2#向foo中第一个进程发送SIGINT信号

注意:如果使用lxc-execute运行的命令,信号首先发送给lxc-init,然后lxc-init将该信号传递给运行的第二个进程。如果发送的信号使得lxc-init进程终止,那么该信号不会传递给运行的第二个进程,并且导致容器中得应用结束运行。

4lxc配置

每个lxc容器都对应于一个配置文件,该文件可在使用lxc-create、lxc-execute和lxc-start命令时指定,如果通过lxc-create创建的容器,则会在/var/lib/lxc/container-name/config中保存该配置。配置信息包括:主机名、网络、cgroup信息等。lxc配置文件中每一行采用key=val的形式,每行保存一个配置项。注释以#开头。

4.1配置主机名

lxc.utsname=foo#配置主机名为foo

4.2配置容器使用的虚拟化网络

lxc.network.type:指定容器使用的虚拟化网络类型,该域一般为新的网络配置的开始,其可能值为:

值类型

empty创建仅有回路接口的网络

veth创建一个对等网络,该网络的一端分配给容器,另一端与lxc.network.link指定的网桥连接。如果未指定网桥,端设备会被创建但是不会连接到任何网桥上。在默认情况下lxc会对容器外的网络设备选择一个名字,但是可以通过lxc.network.veth.pair选项来指定。

vlan创建一个由lxc.network.link指定的虚拟局域网接口(网卡)分配给容器,vlan的标识符可由lxc.network.vlan.id指定。

phys将由lxc.network.link指定的已经存在的接口(网卡)分配给容器。

macvlan创建一个macvlan接口,该接口和由lxc.network.link指定的接口相连接。

lxc.network.flags:指定网络将要执行的动作,可选值为:

on:激活接口

down:关闭接口

lxc.network.link:指定用于进行真实网络通信的接口(网卡)。

lxc.network.name:指定网络接口的名称,在默认情况下,接口名称是动态分配的。

lxc.network.hwaddr:指定虚拟网络接口(虚拟网卡)的MAC地址,在默认情况下,该值自动分配。

lxc.network.ipv4:指定分配给虚拟网卡的ipv4地址。

lxc.network.ipv6:指定分配给虚拟网卡的ipv6地址。

4.3容器伪终端和系统控制台

lxc.pts:如果该项被设置,容器将要拥有一个新的伪终端,并且该终端为容器所私有。该值指定了该伪终端可拥有的最大实例数。

lxc.console:指定控制台输出文件的路径

lxc.tty:指定容器可得到的tty数量,注意在inittab中getty的数量不能超过指定的值。

4.4挂载点

lxc.mount:指定包含挂载信息的文件,该文件内容采用fstab格式。fstab格式如下:

specfiletypeoptionsdumppass

spec:所需加载文件系统所在的设备或远程文件系统。

file:文件系统的挂载点。

type:文件系统类型.

options:加载文件系统的参数。

dump:文件系统转储频率,若不需转储则为0.

pass:启动时扫描文件系统的顺序。根文件系统为1,其他为2,若启动时无需扫描则为0.

如下是一个典型的fstab文件:

none/lxc/rootfs.fedora/dev/ptsdevptsdefaults00

none/lxc/rootfs.fedora/procprocdefaults00

none/lxc/rootfs.fedora/syssysfsdefaults00

lxc.mount.entry:指定一个挂载点,该行采用fstab格式。

4.5根文件系统

lxc.rootfs:指定容器的根文件系统,若该项未指定,则采用和主机相同的根文件系统。

例如:

lxc.rootfs=/lxc/test/rootfs

lxc.rootfs.mount:指定调用pivot_root前lxc.rootfs绑定点,一般情况下使用默认值即可。

备注:pivot_root改变根文件系统,格式为:pivot_rootnew_rootput_old。pivot_root将当前进程的根文件系统移动到目录put_old下,并使得new_root作为新的根文件系统。

lxc.pivotdir:指定原始根文件系统在lxc.rootfs下的支点(pivot)。

4.6控制组配置

4.6.1控制组CPU配置

lxc.cgroup.cpu.shares:该组的CPU时间份额,默认情况下该值为1024。通过将该值修改为较大值,则该组会获得更多的CPU时间,否则,该组获得的CPU时间会相对较少。比如系统中仅存在控制组A和B,这两个组中cpu.shares的值分别为1024和2048,则A获得1/3的CPU时间,B获得2/3的时间。注意,子控制组获得CPU时间份额不能超过其父控制组的CPU时间份额。

lxc.cgroup.cpu.rt_period_us:该项指定了控制组CUP资源分配的周期,以防止某些任务一直占用CPU资源。该项以微秒为单位。

lxc.cgroup.cpu.rt_runtime_us:定义了在CUP资源分配周期内控制组中连续占用的CPU资源的时间长度,时间以微秒为单位。比如CUP资源分配周期为5s,控制组连续占用最长时间为4s,则rt_period_us=5000000,rt_runtime_us=4000000。

4.6.2块设备配置

lxc.cgroup.blkio.weight:指定当前组块设备优先级权重,权重允许的范围为100~1000.

在控制组中另外只读的项:

blkio.time:控制组中指定使用块设备的时间(毫秒),格式为:major:minortimes,其中前两个域为主从设备号,第三个域为时间。

blkio.sectors:组传输的扇区数,格式为:major:minorsectors,其中前两个域为主从设备号,第三个域为传输的扇区数。

4.6.3CUP计数信息(非配置信息)

在控制组中cpu时间统计信息项:

cpuacct.stat:显示CPU时间信息,显示信息包括两个部分:user和system,分别为用户态和内核态运行的时间。

cpuacct.usage:显示控制组中进程消耗的cpu时间统计。可通过向该文件中写入0来清空时间计数。

cpuacct.usage_percpu:显示控制组中每个cpu时间统计。

4.6.4CPU集合配置

lxc.cgroup.cpuset.cpus:在cpuset中得cpu列表。列表中cpu从0开始编号,如果系统中存在100个cpu那么cpu的编号为0~99.

例如:

lxc.cgroup.cpuset.cpus=0-9#将0到9号CPU分配给该组

lxc.cgroup.cpuset.cpus=0,3,4-6#将0,3,4,5,6号CPU分配给该组

注意:该项指定的CPU集合必须为父cpu集合的子集。

lxc.cgroup.cpuset.mems:分配给该cpuset的内存节点列表,例如:

lxc.cgroup.cpuset.mems=0,1#将内存节点0和1分配给该组

lxc.cgroup.cpuset.memory_migrate:如果该项设置为真,当将一个任务连接到新的cpuset时,任务所在前cpuset中分配给该任务的页也被迁移到新的cpuset中,并且尝试确保页所在节点列表的相对位置不变。比如,分配给任务的某一页在前cpuset内存节点中的第二个节点中,则迁移时会尝试将其放到新的cpuset内存节点列表中第二个内存节点中。此外,如果设定了该项,当cpuset中的内存节点列表(cpuset.mems)发生变化时,分配给任务的页被迁移到新的内存节点列表的节点中。

lxc.cgroup.cpuset.cpu_exclusive:cpuset中cpu独占标记,只有在父cpuset设定该项后,子cpuset才可设置该项。当设定该项后,除了祖先和后代cpuset外,lxc.cgroup.cpuset.cpus指定的cpu不允许出现在任何其他cpuset的cpu列表中。

lxc.cgroup.cpuset.mem_exclusive:cpuset中内存节点独占标记。配置规则和lxc.cgroup.cpuset.cpu_exclusive相同。

lxc.cgroup.cpuset.memory_hadwall:该项指定内核分配内存页和缓存数据时是否仅限于从分配给cpuset的内存节点中分配。在默认情况下,该项是关闭的,内核分配的内存页和缓存由多个用户所共享。

lxc.cgroup.cpuset.memory_spread_page和lxc.cgroup.cpuset.memory_spread_slab:这两项控制内核应该从什么地方分配文件系统和相关内核数据结构所需的页。如果lxc.cgroup.cpuset.memory_spread_page项为真,那么内核倾向于在发生缺页的任务允许使用的所有内存节点中均匀的分配内存;否则,会倾向于在任务运行的节点上分配内存。在默认情况下,这两项是关闭的。

例如:

lxc.cgroup.cpuset.memory_spread_page=1#打开spreadpagecache功能

lxc.cgroup.cpuset.memory_spread_slab=0#关闭spreadslabcache功能

lxc.cgroup.cpuset.sched_load_balance:如果设定该项为真,内核会对整个cpuset中得所有CPU进行负载平衡调度。

注意:如果两个重叠的cpuset均打开了该项,那么它们必须在同一个调度域中。

lxc.cgroup.cpuset.sched_relax_domain_level:请求内核改变进行负载平衡时的CPU搜索范围,如果lxc.cgroup.cpuset.sched_load_balance为假,则该项不起作用。其可选等级如下:

-1:norequest.usesystemdefaultorfollowrequestofothers.

0:nosearch.

1:searchsiblings(hyperthreadsinacore).

2:searchcoresinapackage.

3:searchcpusinanode[=systemwideonnon-NUMAsystem]

(4:searchnodesinachunkofnode[onNUMAsystem])

(5:searchsystemwide[onNUMAsystem])

4.6.5设备白名单

lxc.cgroup.devices.allow:将设备添加到白名单中。

lxc.cgroup.devices.deny:将设备从白名单中删除。

当前控制组中的白名单列表可从devices.list中得到,白名单列表的每个入口包含四个域,其形式如下:

typemajor:minoraccess

其中type可选值为:a,c,b,a表示所有设备,c为字符设备,b为块设备。

major和minor为设备的主从设备号,用整数或者*表示所有设备。

access为访问权限的结合r(读),w(写)和m(mknod)。

例如:

c1:3mr#允许对/dev/null进行读和mknode操作。

a*:*rwm#允许对所有设备进行读写和mknode操作。

4.6.6内存配置

lxc.cgroup.memory.limit_in_bytes:当前组的内存使用的最大数。

例如:

lxc.cgroup.memory.limit_in_bytes=4096#设定内存限制为4094字节

lxc.cgroup.memory.limit_in_bytes=4K#设定内存限制为4K

lxc.cgroup.memory.limit_in_bytes=256M#设定内存限制为256M

lxc.cgroup.memory.limit_in_bytes=1G#设定内存限制为1G字节

lxc.cgroup.memory.limit_in_bytes=-1#设定内存为无限制

lxc.cgroup.memory.memsw.limit_in_bytes:设定当前组中内存+对换空间的限制,设定方式和lxc.cgroup.memory.limit_in_bytes相同,但需保证lxc.cgroup.memory.limit_in_bytes<=lxc.cgroup.memory.memsw.limit_in_bytes.

lxc.cgroup.memory.soft_limit_in_bytes:设定内存软限制,这使得系统更大程度上的共享内存,当系统检测到内存争用和短缺时,控制组的内存限制被退回到软限制,防止导致其他控制组饥饿的发生。设定该值时一般要小于硬限制,否则该值会被硬限制所覆盖。

lxc.cgroup.memory.use_hierarchy:打开内存的层次记账功能,注意如果父控制组已经打开该功能,则子控制组对该项的设置将不起作用。

lxc.cgroup.memory.swappiness:设定控制组的对换区的大小,在默认情况下为60。可选值为0~100。

内存子系统其他报告信息项:

memory.stat:显示内存使用的统计信息。

memory.usage_in_bytes:报告当前容器中应用使用的内存量(字节)。

memory.memsw.usage_in_bytes:报告当前容器使用的内存+对换空间的总量。

memory.max_usage_in_bytes:报告容器中内存使用量的最大值。

memory.memsw.max_usage_in_bytes:报告内存+对换空间的最大使用量。

memory.failcnt:应用使用内存数量达到memory.limit_in_bytes的次数。

memory.memsw_failcnt:应用使用内存+对换空间数量达到memory.memsw.limit_in_bytes的次数。

4.6.7冷冻配置

lxc.cgroup.freezer.state:挂起或者恢复控制组中的任务,其可选值如下:

FROZEN:挂起控制组中得任务

THAWED:恢复控制组中任务的运行。

在配置文件中配置该项为FROZEN可使得应用在容器开始运行的状态为挂起状态。在默认情况下该值设定为THAWED。注意,无法向处于冷冻状态的控制组中添加任务。

4.6.8网络通信配置

net_cls子系统通过对网络包进行标记从而使得linux流量控制器(tc)识别网络报来自于那个特定的控制组,从而获得该网络包的优先级。

lxc.cgroup.net_cls.classid:该值指示了当前控制组的类别句柄(handle),该值采用十六进制表示。句柄的格式如下:0xAAAABBBB其中AAAA是十六进制主ID号,BBBB是十六进制从ID号,比如0x100001表示10:1(iproute2工具集使用十六进制表示句柄ID)。此处设定的为linux流量控制器中的类标识符。具体可参考TC相关信息。

4.6.9权限配置

如果容器作为超级用户运行,指定其可放弃的权限。

lxc.cap.drop:指定容器放弃的权限。该行指定了一些用空格分隔的权限。权限的格式为相应权限定义除去CAP_前缀后的小写形式,比如CAP_SYS_MODULE使用sys_module指定,通过指定sys_chroot可剥夺容器中所有进程进行chroot的权限,附录7.3中显示容器放弃的执行权限,注意,被剥夺的权限不能被恢复,即使root也没有权限来恢复被剥夺的权限,只能重启系统或者重新运行容器并将相应配置选项移除。

例:

lxc.cap.drop=sys_modulemknodsetuidnet_raw

lxc.cap.drop=mac_override

lxc.cap.drop=sys_chroot

4.6.10其他配置项

notify_on_release:当该项被打开时,在控制组最后一个程序结束时,内核执行由release_agent指定的程序。在默认情况下该项是关闭的。

release_agent:保存控制组中最后一个程序结束运行时执行的程序的路径,该项只在根控制组中出现。

5连接容器实例

5.1运行Fedora基本安装虚拟机

1)安装febootstrap工具

yuminstallfebootstrap

2)生成fedora基本安装系统

mkdir/lxc

cd/lxc

febootstrapfedora-13rootfs.fedora

在rootfs.fedora目录下生成了基本安装的fedora13系统。

3)基本安装文件不能直接在linux容器使用,需做如下修改:

1、由于udev不在lxc容器中运行,首先需要人工创建需要的设备,编写脚本如下:

#!/bin/bash

ROOT=$(pwd)

DEV=${ROOT}/dev

if[$ROOT='/'];then

printf"\033[22;35m\nDONOTRUNONTHEHOSTNODE\n\n"

tputsgr0

exit1

fi

if[!-d$DEV];then

printf"\033[01;33m\nRunthisscriptinrootfs\n\n"

tputsgr0

exit1

fi

rm-rf${DEV}

mkdir${DEV}

mknod-m666${DEV}/nullc13

mknod-m666${DEV}/zeroc15

mknod-m666${DEV}/randomc18

mknod-m666${DEV}/urandomc19

mkdir-m755${DEV}/pts

mkdir-m1777${DEV}/shm

mknod-m666${DEV}/ttyc50

mknod-m666${DEV}/tty0c40

mknod-m666${DEV}/tty1c41

mknod-m666${DEV}/tty2c42

mknod-m666${DEV}/tty3c43

mknod-m666${DEV}/tty4c44

mknod-m600${DEV}/consolec51

mknod-m666${DEV}/fullc17

mknod-m600${DEV}/initctlp

mknod-m666${DEV}/ptmxc52

exit0

保存到/usr/local/bin/lxc-config中,并添加可执行权限。

chmodu+x/usr/local/bin/lxc-config

切换到rootfs.fedora下运行该脚本:

cd/lxc/rootfs.fedora

/usr/local/bin/lxc-config

2、配置rootfs.fedora

cp/etc/resolv.conf/lxc/rootfs.fedora/etc

chroot/lxc/rootfs.fedora/bin/bash

#挂载proc,sys,/dev/pts

mount-tprocnone/proc

mount-tsysfsnone/sys

mount-tdevptsnone/dev/pts

#添加一些应用

yumupdate

yum-yreinstallglibc-common

yuminstallhttpdphp-mysqlmysql-servernanoopenssh-clientsvim

#在/下添加一些必要的文件

touch/etc/fstab

rm/etc/mtab

ln-s/proc/mounts/etc/mtab

#卸载proc,sys和/dev/pts

umount-tprocnone/proc

umount-tsysfsnone/sys

umount-tdevptsnone/dev/pts

#设定根密码

passwd

3、根文件系统配置

编辑/lxc/rootfs.feodar/etc/sysconfig/init文件,将最后一行改为:

ACTIVE_CONSOLES=/dev/tty[1-4]

编辑/lxc/rootfs.fedora/etc/rc.sysinit将下行注释掉:

/sbin/start_udev

编辑/lxc/rootfs.fedora/etc/sysconfig/network添加如下两行:

NETWORKING=yes

HOSTNAME=fedora

4、创建lxc配置文件/lxc/config.fedora

lxc.utsname=fedora

lxc.tty=4

lxc.network.type=veth

lxc.network.flags=up

lxc.network.link=br0

lxc.network.name=eth0

lxc.network.mtu=1500

lxc.network.ipv4=192.168.0.65/24

lxc.rootfs=/lxc/rootfs.fedora

lxc.mount=/lxc/fstab.fedora

lxc.cgroup.devices.deny=a

#/dev/nullandzero

lxc.cgroup.devices.allow=c1:3rwm

lxc.cgroup.devices.allow=c1:5rwm

#consoles

lxc.cgroup.devices.allow=c5:1rwm

lxc.cgroup.devices.allow=c5:0rwm

lxc.cgroup.devices.allow=c4:0rwm

lxc.cgroup.devices.allow=c4:1rwm

#/dev/{,u}random

lxc.cgroup.devices.allow=c1:9rwm

lxc.cgroup.devices.allow=c1:8rwm

#/dev/pts/*-ptsnamespacesare"comingsoon"

lxc.cgroup.devices.allow=c136:*rwm

lxc.cgroup.devices.allow=c5:2rwm

#rtc

lxc.cgroup.devices.allow=c254:0rwm

5、创建fstab文件/lxc/fstab.fedora

none/lxc/rootfs.fedora/dev/ptsdevptsdefaults00

none/lxc/rootfs.fedora/procprocdefaults00

none/lxc/rootfs.fedora/syssysfsdefaults00

/etc/resolv.conf/lxc/rootfs.fedora/etc/resolv.confnonebind00

6、删除不必要的init脚本

删除/lxc/rootfs.fedora/etc/init目录下,除下述文件外的所有文件:

rc.conf

start-ttys.conf

tty.conf

将rc.conf和start-ttys.conf第一行修改为

startonstartup

将rc.conf中最后一行的

exec/etc/rc.d/rc$RUNLEVEL

修改为:

exec/etc/rc.d/rc.fedora

创建/lxc/rootfs.fedora/etc/rc.d/rc.fedora

#!/bin/bash

routeadddefaultgw192.168.1.1

/etc/init.d/rsyslogstart&

/etc/init.d/iptablesstart&

/etc/init.d/sshdstart&

/etc/init.d/mysqldstart&

/etc/init.d/httpdstart&

将其属性设置为可执行:

chmodu+x/lxc/rootfs.fedora/etc/rc.d/rc.fedora

删除/lxc/rootfs.fedora/etc/init.d目录下除下述文件外的所有文件:

httpd

iptables

mysqld

rsyslog

sshd

4)创建linux容器

创建网桥:

brctladdbr0

brctlsetfdbr00

brctladdifbr0eth0

ifconfigbr0192.168.74.130up#此处应为本机IP地址

ifconfigeth00.0.0.0up

routeadd-netdefaultgw192.168.74.2br0#设置网关

创建容器

lxc-create-f/lxc/config.fedora-nfedora

运行容器

lxc-start-nfedora#lxc-start在未指定运行的命令的情况下默认执行init程序

连接到该容器,你会看到登陆界面:

lxc-console-nfedora

注意:在登陆后需用根用户添加网关才能联网:

routeadd-netdefaultgw192.168.74.2eth0

6网络流量配置

6.1lxc运行虚拟机网络流量配置

linux容器通过内核流量控制子系统(trafficcontrolsubsystem)对流量进行限制,配置文件中lxc.net_cls.classid指示了流量控制器中类的标示。现在采用基于容器IP地址的方法,使用如下脚本利用TC工具创建HTB队列规则,tc命令的使用可参考附录和参考部分:

#!/bin/bash

#remove all classful qdisc first
tc qdisc del dev eth0 root

#create root htb qdisc, specify all unclassified packet into subclass 4
tc qdisc add dev eth0 root handle 1:0 htb default 4

#create subclasses
tc class add dev eth0 parent 1:0 classid 1:2 htb rate 256kbps ceil 512kbps prio 0
tc class add dev eth0 parent 1:0 classid 1:3 htb rate 128kbps ceil 256kbps prio 1
tc class add dev eth0 parent 1:0 classid 1:4 htb rate 64kbps  ceil 128kbps prio 2

#attach a sfq qdisc for each leaf class
tc qdisc add dev eth0 parent 1:2 handle 2: sfq perturb 5
tc qdisc add dev eth0 parent 1:3 handle 3: sfq perturb 5
tc qdisc add dev eth0 parent 1:4 handle 4: sfq perturb 5

#attach filters to root htb qdisc
tc filter add dev eth0 protocol ip parent 1:0 u32 match ip src 192.168.74.10  flowid 1:2
tc filter add dev eth0 protocol ip parent 1:0 u32 match ip src 192.168.74.30  flowid 1:3
tc filter add dev eth0 protocol ip parent 1:0 u32 match ip src 192.168.74.130 flowid 1:4

注意,此处根据IP地址将数据包放置到不同的class中,也可采用其他的filter来实现不同的分类规则。u32过滤器可根据数据包的信息对数据包进行归类。此外,本章中容器通过配置桥接来进行网络通信,对于其他的链接网络的形式尚未进行测试。

6.2利用classid进行流量控制

在linux控制组中net_cls子系统通过对网络包打标记classid来表示属于不同控制组的网络包。linux系统中流量控制系统可根据该标记来对控制组的网络包进行控制。注意,net_cls子系统并不是通过修改网络包来打标记的,当网络包通过协议栈的过程中,通过当前的进程上下文来的到控制组信息,进而获得当前网络包的classid,因此,如果网络包被包过滤器处理之前被重新调度,那么就无法获得该包的classid!因此,对于采用6.1中网桥方式进行网络通信的方式,运行fedora虚拟机时无法采用classid对网络包进行分类管理。下面给出了一个简单的利用classid进行网络流量限制的配置脚本,使用的cgroup过滤器,根据网络包进程上下文中得到的classid对网络包进行分类。

#!/bin/bash

dev=eth0
tc qdisc del dev $dev root
# set qdisc as htb

tc qdisc add dev $dev parent root handle 10: htb 

# create some claess for root qdisc

tc class add dev $dev parent 10: classid 10:1 htb rate 1mbps ceil 1mbps
tc class add dev $dev parent 10:1 classid 10:2 htb rate 256kbps ceil 256kbps 
tc class add dev $dev parent 10:1 classid 10:3 htb rate 256kbps ceil 256kbps 
tc class add dev $dev parent 10:1 classid 10:4 htb rate 512kbps ceil 1mbps

# add sfq qdisc for every leaf class
tc qdisc add dev $dev parent 10:2 handle 2:0 pfifo
tc qdisc add dev $dev parent 10:3 handle 3:0 pfifo
tc qdisc add dev $dev parent 10:4 handle 4:0 pfifo

# add filter for root qdisc

tc filter add dev $dev parent 10: protocol ip prio 1 handle 10: cgroup

7附录和参考

7.1brctl命令简介

该命令用于建立、维护和检查Ethernet网桥在Linux内核中的配置,如下是该命令的简要使用:

brctladdbr<name>:添加一个名称为name的网桥。

brctldelbr<name>:删除名称为name的网桥。

brctladdif<name><device>:将接口device连接到网桥name上。

brctldelif<name><device>:将接口device从网桥name上移除。

brctlsetfd<name><time>:设定网桥name的转送延时。

更多信息可参考http://linux.die.net/man/8/brctl。

7.2tc命令简介

该命令用于linux内核的网络流量控制,在本文档中使用有类型的队列规则(classfulqdisc)。下面给出一个配置实例,更多信息可参考http://linux.die.net/man/8/tc。

1)假设由另个客户A和B通过接口eth0连接到网络,我们系统分配给用户B的带宽为60kbps,分配给用户A的带宽为40kbps。此外,希望将A用户的30kbps的带宽按照如下划分:www占用其中的30kbps,剩余的10kbps用于其他应用。系统中任何未使用的带宽均可允许A或者B使用。

a)在接口eth0上创建一个htbqdisc,其句柄为1:0,未分类数据包发送给类1:12

tcqdiscadddeveth0roothandle1:htbdefault12

b)为qdisc创建htb类以及子类

tcclassadddeveth0parent1:classid1:1htbrate100kbpsceil100kbps

tcclassadddeveth0parent1:1classid1:10htbrate30kbpsceil100kbps

tcclassadddeveth0parent1:1classid1:11htbrate10kbpsceil100kbps

tcclassadddeveth0parent1:1classid1:12htbrate60kbpsceil100kbps

c)为qdisc添加filter,假设A用户的IP地址为1.2.3.4

tcfilteradddeveth0protocolipparent1:0prio1u32matchipsrc1.2.3.4ipdprot800xFFFFflowid1:10

tcfilteradddeveth0protocolipparent1:0prio1u32matchipsrc1.2.3.4flowid1:11

上面第一行指定A的www服务的包发到类1:10,第二行指定其他包发给类1:11

d)为每个leafclass添加队列(可选)

tcqdiscadddeveth0parent1:10handle20:pfifolimit5

tcqdiscadddeveth0parent1:11handle30:pfifolimit5

tcqdiscadddeveth0parent1:12handle40:pfifolimit10

7.3超级用户权限

在linux2.2内核开始将超级用户的权限分为若干独立的子权限,每个子权限可独立的禁止或者打开,注意,一旦剥夺了根用户的某一权限,那么除非重启系统,否则无法恢复该权限。下面对这些权限进行简单的介绍:

CAP_CHOWN:允许改变文件的所有权

CAP_DAC_OVERRIDE:忽略对文件的所有DAC访问限制

CAP_DAC_READ_SEARCH:忽略所有对读、搜索操作的限制

CAP_FOWNER:如果文件属于进程的UID,就取消对文件的限制

CAP_FSETID:允许设置setuid位

CAP_KILL:允许对不属于自己的进程发送信号

CAP_SETGID:允许改变组ID

CAP_SETUID:允许改变用户ID

CAP_SETPCAP:允许向其它进程转移能力以及删除其它进程的任意能力

CAP_LINUX_IMMUTABLE:允许修改文件的不可修改(IMMUTABLE)和只添加(APPEND-ONLY)属性

CAP_NET_BIND_SERVICE:允许绑定到小于1024的端口

CAP_NET_BROADCAST:允许网络广播和多播访问

CAP_NET_ADMIN:允许执行网络管理任务:接口、防火墙和路由等。

CAP_NET_RAW:允许使用原始(raw)套接字

CAP_IPC_LOCK:允许锁定共享内存片段

CAP_IPC_OWNER:忽略IPC所有权检查

CAP_SYS_MODULE:插入和删除内核模块

CAP_SYS_RAWIO:允许对ioperm/iopl的访问

CAP_SYS_CHROOT:允许使用chroot()系统调用

CAP_SYS_PTRACE:允许跟踪任何进程

CAP_SYS_PACCT:允许配置进程记帐(processaccounting)

CAP_SYS_ADMIN:允许执行系统管理任务:加载/卸载文件系统、设置磁盘配额、开/关交换设备和文件等。

CAP_SYS_BOOT:允许重新启动系统

CAP_SYS_NICE:允许提升优先级,设置其它进程的优先级

CAP_SYS_RESOURCE:忽略资源限制

CAP_SYS_TIME:允许改变系统时钟

CAP_SYS_TTY_CONFIG:允许配置TTY设备

CAP_MKNOD:允许使用mknod()系统调用

CAP_LEASE:Allowtakingofleasesonfiles

7.4cgroup模块编写

lxc建立在cgroup的基础上,在2.6.35以上内核中支持将控制组子系统通过模块的形式加入到内核中,本节演示了如何编写一个控制组子系统模块。在当前的内核中支持的子系统的最大个数为unsignedlong类型的bit位数,当前的子系统个数为8个。编写简单过程如下:

1)定义structcgroup_subsys结构

struct cgroup_subsys  sample = {
	.create = sample_create,
	.destroy = sample_destroy,
	.populate = sample_populate,
	.module = THIS_MODULE,
};

structcgroup_subsys结构定义了控制组子系统结构,其中主要包含了一些控制组子系统接口和一些属性信息,其中create和destory接口必须定义,这两个接口实现了创建和销毁控制组子系统状态对象。

2)定义子系统状态结构,该结构中必须要包含structcgroup_subsys_state结构已便于编程处理.

struct  cgroup_hello_state {
	struct cgroup_subsys_state  css;
	int  helloid;
	/*其他相关信息*/
};

3)定义一个全局变量只是hello子系统的id,记录系统分配给子系统的编号,以便于后面编程处理.

inthello_subsys_id;

4)定义子系统在控制组目录中的控制文件接口.

struct  cftype cft[]={
	{
		.name = “hello”,
		.read_u64 = sample_read_u64,
		.write_u64 = sample_write_u64,
	},
}

structcftype结构定义了控制文件的处理句柄以及相关名称和属性信息,在这里仅仅实现文件的读写句柄.

5)实现接口。

完整的代码如下:

#include <linux/module.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/cgroup.h>
MODULE_LICENSE("Dual BSD/GPL");
/*控制组子系统结构*/
struct cgroup_hello_state{
	struct cgroup_subsys_state css;
	int helloid;
	/*其他状态信息*/
};

int  hello_subsys_id;

static struct cgroup_subsys_state *cgrp_create(struct cgroup_subsys *ss,struct cgroup *cgrp);
static void cgrp_destroy(struct cgroup_subsys *ss,struct cgroup * cgrp);
static int cgrp_populate(struct cgroup_subsys *ss,struct cgroup * cgrp);
static void cgrp_attach(struct cgroup_subsys *ss,struct cgroup * cgrp,struct cgroup *old_cgrp,struct task_struct *task);
/*定义控制组子系统结构*/
struct cgroup_subsys cgroup_hello_subsys ={
	.name = "hello",
	.create = cgrp_create,
	.destroy = cgrp_destroy,
	.populate = cgrp_populate,
	.attach = cgrp_attach,
	.module = THIS_MODULE,
};

static void cgrp_attach(struct cgroup_subsys *ss,struct cgroup * cgrp,struct cgroup *old_cgrp,struct task_struct *task){
	printk(KERN_ALERT "%s:%d\n",__func__,task->pid);
}
static inline struct cgroup_hello_state *cgrp_hello_state(struct cgroup *cgrp){
	return container_of(cgroup_subsys_state(cgrp,hello_subsys_id),struct cgroup_hello_state,css);
}

static struct cgroup_subsys_state *cgrp_create(struct cgroup_subsys *ss,struct cgroup *cgrp){
	struct cgroup_hello_state *cs;
	cs = kmalloc(sizeof(*cs),GFP_KERNEL);
	if(!cs)
		return ERR_PTR(-ENOMEM);
	printk(KERN_ALERT "%s\n",__func__);
	return &cs->css;
}

static void cgrp_destroy(struct cgroup_subsys *ss,struct cgroup * cgrp){
	kfree(cgrp_hello_state(cgrp));
}

static u64 read_helloid(struct cgroup *cgrp,struct cftype *cft);
static int write_helloid(struct cgroup *cgrp,struct cftype *cft, u64 val);
/*控制文件操作对象结构*/
struct cftype ss_files[]={
	{
		.name = "hello",
		.read_u64 = read_helloid,
		.write_u64 = write_helloid,
	},
};
/*创建本子系统的控制文件*/
static int cgrp_populate(struct cgroup_subsys *ss,struct cgroup *cgrp){
	return cgroup_add_files(cgrp,ss,ss_files,ARRAY_SIZE(ss_files));
}

static u64 read_helloid(struct cgroup *cgrp, struct cftype *cft){
	printk(KERN_ALERT "%s:%s\n",__func__,cft->name);
	return cgrp_hello_state(cgrp)->helloid;
}
static int write_helloid(struct cgroup *cgrp,struct cftype *cft,u64 val){
	printk(KERN_ALERT "%s:%s\n",__func__,cft->name);
	cgrp_hello_state(cgrp)->helloid = (int)val;
	return 0;
}

static int hello_init(void){
	int ret;
	printk(KERN_ALERT "Hello,world\n");
	ret = cgroup_load_subsys(&cgroup_hello_subsys);
	if(ret)
		goto out;
	hello_subsys_id = cgroup_hello_subsys.subsys_id;
	printk(KERN_ALERT "subsys id:%d\n",hello_subsys_id);
out:
	return ret;
}

static void hello_exit(void){
	cgroup_unload_subsys(&cgroup_hello_subsys);
	printk(KERN_ALERT "Goodbye, cruel world\n");
}
module_init(hello_init);
module_exit(hello_exit);

上述文档可能存在疏漏错误的地方,还望大家指正~~~~

[/b]

相关推荐