kk Blog —— 通用基础


date [-d @int|str] [+%s|"+%F %T"]
netstat -ltunp
sar -n DEV 1

内存分配的原理--molloc/brk/mmap

http://blog.163.com/xychenbaihu@yeah/blog/static/132229655201210975312473/

内存分配的原理__进程分配内存有两种方式,分别由两个系统调用完成:brk和mmap(不考虑共享内存)。

如何查看进程发生缺页中断的次数?

ps -o majflt,minflt -C program命令查看。

majflt代表major fault,中文名叫大错误,minflt代表minor fault,中文名叫小错误。这两个数值表示一个进程自启动以来所发生的缺页中断的次数。

发生缺页中断后,执行了那些操作?

当一个进程发生缺页中断的时候,进程会陷入内核态,执行以下操作:

1、检查要访问的虚拟地址是否合法

2、查找/分配一个物理页

3、填充物理页内容(读取磁盘,或者直接置0,或者啥也不干)

4、建立映射关系(虚拟地址到物理地址)

重新执行发生缺页中断的那条指令

如果第3步,需要读取磁盘,那么这次缺页中断就是majflt,否则就是minflt。

内存分配的原理

从操作系统角度来看,进程分配内存有两种方式,分别由两个系统调用完成:brk和mmap(不考虑共享内存)。

1、brk是将数据段(.data)的最高地址指针_edata往高地址推;

2、mmap是在进程的虚拟地址空间中(堆和栈中间,称为文件映射区域的地方)找一块空闲的虚拟内存。

这两种方式分配的都是虚拟内存,没有分配物理内存。在第一次访问已分配的虚拟地址空间的时候,发生缺页中断,操作系统负责分配物理内存,然后建立虚拟内存和物理内存之间的映射关系。

在标准C库中,提供了malloc/free函数分配释放内存,这两个函数底层是由brk,mmap,munmap这些系统调用实现的。

下面以一个例子来说明内存分配的原理:

情况一、malloc小于128k的内存,使用brk分配内存,将_edata往高地址推(只分配虚拟空间,不对应物理内存(因此没有初始化),第一次读/写数据时,引起内核缺页中断,内核才分配对应的物理内存,然后虚拟地址空间建立映射关系),如下图:

1、进程启动的时候,其(虚拟)内存空间的初始布局如图1-(1)所示。

其中,mmap内存映射文件是在堆和栈的中间(例如libc-2.2.93.so,其它数据文件等),为了简单起见,省略了内存映射文件。

_edata指针(glibc里面定义)指向数据段的最高地址。

2、进程调用A=malloc(30K)以后,内存空间如图1-(2):

malloc函数会调用brk系统调用,将_edata指针往高地址推30K,就完成虚拟内存分配。

你可能会问:只要把_edata+30K就完成内存分配了?

事实是这样的,_edata+30K只是完成虚拟地址的分配,A这块内存现在还是没有物理页与之对应的,等到进程第一次读写A这块内存的时候,发生缺页中断,这个时候,内核才分配A这块内存对应的物理页。也就是说,如果用malloc分配了A这块内容,然后从来不访问它,那么,A对应的物理页是不会被分配的。

3、进程调用B=malloc(40K)以后,内存空间如图1-(3)。

情况二、malloc大于128k的内存,使用mmap分配内存,在堆和栈之间找一块空闲内存分配(对应独立内存,而且初始化为0),如下图:

4、进程调用C=malloc(200K)以后,内存空间如图2-(4):

默认情况下,malloc函数分配内存,如果请求内存大于128K(可由M_MMAP_THRESHOLD选项调节),那就不是去推_edata指针了,而是利用mmap系统调用,从堆和栈的中间分配一块虚拟内存。

这样子做主要是因为::
brk分配的内存需要等到高地址内存释放以后才能释放(例如,在B释放之前,A是不可能释放的,这就是内存碎片产生的原因,什么时候紧缩看下面),而mmap分配的内存可以单独释放。
当然,还有其它的好处,也有坏处,再具体下去,有兴趣的同学可以去看glibc里面malloc的代码了。

5、进程调用D=malloc(100K)以后,内存空间如图2-(5);

6、进程调用free(C)以后,C对应的虚拟内存和物理内存一起释放如图2-(6)。

7、进程调用free(B)以后,如图3-(7)所示:

B对应的虚拟内存和物理内存都没有释放,因为只有一个_edata指针,如果往回推,那么D这块内存怎么办呢?

当然,B这块内存,是可以重用的,如果这个时候再来一个40K的请求,那么malloc很可能就把B这块内存返回回去了。

8、进程调用free(D)以后,如图3-(8)所示:

B和D连接起来,变成一块140K的空闲内存。

9、默认情况下:

当最高地址空间的空闲内存超过128K(可由M_TRIM_THRESHOLD选项调节)时,执行内存紧缩操作(trim)。在上一个步骤free的时候,发现最高地址空闲内存超过128K,于是内存紧缩,变成图3-(9)所示。


mmap 样例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include<sys/mman.h>
#include<sys/types.h>
#include<fcntl.h>
#include<stdio.h>
#include<unistd.h>

int main()
{
	int i,j,k,l;
	char *mp;
	mp = (char*)mmap(NULL, 1000000, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
	*mp = 'A';
	munmap(mp, 1000000);
	return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include<sys/mman.h>
#include<sys/types.h>
#include<fcntl.h>
#include<stdio.h>
#include<unistd.h>

int main()
{
	int i,j,k,l;
	int fd;
	char *mp;

	fd = open("/tmp/mmap", O_CREAT|O_RDWR, 00777);
	lseek(fd, 100, SEEK_SET);
	write(fd, "", 1);
	mp = malloc(1000000);
	mp = (char*)mmap(NULL, 100, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
	i = *mp;
	*mp = 'A';
	munmap(mp, 100);
	return 0;
}

CentOS 6 使用 docker

docker bridge 设置

1
2
docker network create --subnet=192.168.3.0/24 --gateway=192.168.3.1 nett
docker run -i -t --net nett --ip 192.168.3.2 49f7960eb7e4 /bin/bash

http://www.linuxidc.com/Linux/2014-01/95513.htm

一、禁用selinux

由于Selinux和LXC有冲突,所以需要禁用selinux。编辑/etc/selinux/config,设置两个关键变量。

1
2
SELINUX=disabled
SELINUXTYPE=targeted

二、配置Fedora EPEL源

1
sudo yum install http://ftp.riken.jp/Linux/fedora/epel/6/x86_64/epel-release-6-8.noarch.rpm

三、添加hop5.repo源

1
2
cd /etc/yum.repos.d
sudo wget http://www.hop5.in/yum/el6/hop5.repo

四、安装Docker

1
sudo yum install docker-io

http://www.server110.com/docker/201411/11105.html

启动docker服务

1
2
3
[root@localhost /]# service docker start
Starting cgconfig service:                                 [  OK  ]
Starting docker:                                           [  OK  ]

基本信息查看

docker version:查看docker的版本号,包括客户端、服务端、依赖的Go等

1
2
3
4
5
6
7
8
9
[root@localhost /]# docker version
Client version: 1.0.0
Client API version: 1.12
Go version (client): go1.2.2
Git commit (client): 63fe64c/1.0.0
Server version: 1.0.0
Server API version: 1.12
Go version (server): go1.2.2
Git commit (server): 63fe64c/1.0.0

docker info :查看系统(docker)层面信息,包括管理的images, containers数等

1
2
3
4
5
6
7
8
9
10
11
12
13
[root@localhost /]# docker info
Containers: 16
Images: 40
Storage Driver: devicemapper
 Pool Name: docker-253:0-1183580-pool
 Data file: /var/lib/docker/devicemapper/devicemapper/data
 Metadata file: /var/lib/docker/devicemapper/devicemapper/metadata
 Data Space Used: 2180.4 Mb
 Data Space Total: 102400.0 Mb
 Metadata Space Used: 3.4 Mb
 Metadata Space Total: 2048.0 Mb
Execution Driver: lxc-0.9.0
Kernel Version: 2.6.32-431.el6.x86_64

5 镜像的获取与容器的使用

镜像可以看作是包含有某些软件的容器系统,比如ubuntu就是一个官方的基础镜像,很多镜像都是基于这个镜像“衍生”,该镜像包含基本的ubuntu系统。再比如,hipache是一个官方的镜像容器,运行后可以支持http和websocket的代理服务,而这个镜像本身又基于ubuntu。

搜索镜像

1
docker search <image>:在docker index中搜索image
1
2
3
4
5
6
7
[root@localhost /]# docker search ubuntu12.10
NAME                        DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
mirolin/ubuntu12.10                                                         0
marcgibbons/ubuntu12.10                                                     0
mirolin/ubuntu12.10_redis                                                   0
chug/ubuntu12.10x32         Ubuntu Quantal Quetzal 12.10 32bit  base i...   0
chug/ubuntu12.10x64         Ubuntu Quantal Quetzal 12.10 64bit  base i...   0

下载镜像

1
docker pull <image> :从docker registry server 中下拉image
1
[root@localhost /]# docker pull chug/ubuntu12.10x64

查看镜像

1
2
3
4
docker images: 列出images
docker images -a :列出所有的images(包含历史)
docker images --tree :显示镜像的所有层(layer)
docker rmi  <image ID>: 删除一个或多个image

使用镜像创建容器

1
2
[root@localhost /]# docker run chug/ubuntu12.10x64  /bin/echo hello world
hello world

交互式运行

1
2
[root@localhost /]# docker run -i -t chug/ubuntu12.10x64  /bin/bash
root@2161509ff65e:/#

运行Container

1
2
3
$ docker run --name shell -i -t chug/ubuntu12.10x64 /bin/bash 

$ docker run -t -i efd1e7457182 /bin/bash 

两个参数,-t表示给容器tty终端,-i表示可以interactive,可以交互。

查看容器

1
2
3
4
docker ps :列出当前所有正在运行的container
docker ps -l :列出最近一次启动的container
docker ps -a :列出所有的container(包含历史,即运行过的container)
docker ps -q :列出最近一次运行的container ID

再次启动容器

1
2
3
4
5
6
7
8
docker start/stop/restart <container> :开启/停止/重启container
docker start [container_id] :再次运行某个container (包括历史container)
docker attach [container_id] :连接一个正在运行的container实例(即实例必须为start状态,可以多个窗口同时attach 一个container实例)
docker start -i <container> :启动一个container并进入交互模式(相当于先start,在attach)

docker run -i -t <image> /bin/bash :使用image创建container并进入交互模式, login shell是/bin/bash
docker run -i -t -p <host_port:contain_port> :映射 HOST 端口到容器,方便外部访问容器内服务,host_port 可以省略,省略表示把 container_port 映射到一个动态端口。
注:使用start是启动已经创建过得container,使用run则通过image开启一个新的container。

删除容器

1
2
3
docker rm <container...> :删除一个或多个container
docker rm `docker ps -a -q` :删除所有的container
docker ps -a -q | xargs docker rm :同上, 删除所有的container

6 持久化容器与镜像

6.1 通过容器生成新的镜像

运行中的镜像称为容器。你可以修改容器(比如删除一个文件),但这些修改不会影响到镜像。不过,你使用docker commit 命令可以把一个正在运行的容器变成一个新的镜像。

1
docker commit <container> [repo:tag] 将一个container固化为一个新的image,后面的repo:tag可选。
1
2
3
4
5
6
7
8
9
[root@localhost /]# docker images
REPOSITORY            TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
chug/ubuntu12.10x64   latest              0b96c14dafcd        4 months ago        270.3 MB
[root@localhost /]# docker commit d0fd23b8d3ac chug/ubuntu12.10x64_2
daa11948e23d970c18ad89c9e5d8972157fb6f0733f4742db04219b9bb6d063b
[root@localhost /]# docker images
REPOSITORY              TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
chug/ubuntu12.10x64_2   latest              daa11948e23d        6 seconds ago       270.3 MB
chug/ubuntu12.10x64     latest              0b96c14dafcd        4 months ago        270.3 MB
6.2 持久化容器

export命令用于持久化容器

1
docker export <CONTAINER ID> > /tmp/export.tar
6.3 持久化镜像

Save命令用于持久化镜像

1
docker save 镜像ID > /tmp/save.tar
6.4 导入持久化container

删除container 2161509ff65e

导入export.tar文件

1
2
3
4
5
6
7
[root@localhost /]# cat /tmp/export.tar | docker import - export:latest
af19a55ff0745fb0a68655392d6d7653c29460d22d916814208bbb9626183aaa
[root@localhost /]# docker images
REPOSITORY              TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
export                  latest              af19a55ff074        34 seconds ago      270.3 MB
chug/ubuntu12.10x64_2   latest              daa11948e23d        20 minutes ago      270.3 MB
chug/ubuntu12.10x64     latest              0b96c14dafcd        4 months ago        270.3 MB
6.5 导入持久化image

删除image daa11948e23d

导入save.tar文件

1
[root@localhost /]# docker load < /tmp/save.tar

对image打tag

1
[root@localhost /]# docker tag daa11948e23d load:tag
6.6 export-import与save-load的区别

导出后再导入(export-import)的镜像会丢失所有的历史,而保存后再加载(save-load)的镜像没有丢失历史和层(layer)。这意味着使用导出后再导入的方式,你将无法回滚到之前的层(layer),同时,使用保存后再加载的方式持久化整个镜像,就可以做到层回滚。(可以执行docker tag 来回滚之前的层)。

6.7 一些其它命令
1
2
3
4
5
6
7
docker logs $CONTAINER_ID #查看docker实例运行日志,确保正常运行
docker inspect $CONTAINER_ID #docker inspect <image|container> 查看image或container的底层信息

docker build <path> 寻找path路径下名为的Dockerfile的配置文件,使用此配置生成新的image
docker build -t repo[:tag] 同上,可以指定repo和可选的tag
docker build - < <dockerfile> 使用指定的dockerfile配置文件,docker以stdin方式获取内容,使用此配置生成新的image
docker port <container> <container port> 查看本地哪个端口映射到container的指定端口,其实用docker ps 也可以看到

7 一些使用技巧

7.1 docker文件存放目录

Docker实际上把所有东西都放到/var/lib/docker路径下了。

1
2
[root@localhost docker]# ls -F
containers/  devicemapper/  execdriver/  graph/  init/  linkgraph.db  repositories-devicemapper  volumes/

containers目录当然就是存放容器(container)了,graph目录存放镜像,文件层(file system layer)存放在graph/imageid/layer路径下,这样我们就可以看看文件层里到底有哪些东西,利用这种层级结构可以清楚的看到文件层是如何一层一层叠加起来的。

7.2 查看root密码

docker容器启动时的root用户的密码是随机分配的。所以,通过这种方式就可以得到容器的root用户的密码了。

1
docker logs 5817938c3f6e 2>&1 | grep 'User: ' | tail -n1

http://www.tuicool.com/articles/7V7vYn

Docker常用命令

1. 查看docker信息(version、info)

1
2
3
4
5
# 查看docker版本
$docker version

# 显示docker系统的信息
$docker info

2. 对image的操作(search、pull、images、rmi、history)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 检索image
$docker search image_name

# 下载image
$docker pull image_name

# 列出镜像列表; -a, --all=false Show all images; --no-trunc=false Don't truncate output; -q, --quiet=false Only show numeric IDs
$docker images

# 删除一个或者多个镜像; -f, --force=false Force; --no-prune=false Do not delete untagged parents
$docker rmi image_name

# 显示一个镜像的历史; --no-trunc=false Don't truncate output; -q, --quiet=false Only show numeric IDs
$docker history image_name

3. 启动容器(run)

docker容器可以理解为在沙盒中运行的进程。这个沙盒包含了该进程运行所必须的资源,包括文件系统、系统类库、shell 环境等等。但这个沙盒默认是不会运行任何程序的。你需要在沙盒中运行一个进程来启动某一个容器。这个进程是该容器的唯一进程,所以当该进程结束的时候,容器也会完全的停止。

1
2
3
4
5
6
7
8
9
# 在容器中运行"echo"命令,输出"hello word"
$docker run image_name echo "hello word"

# 交互式进入容器中
$docker run -i -t image_name /bin/bash


# 在容器中安装新的程序
$docker run image_name apt-get install -y app_name

Note: 在执行apt-get 命令的时候,要带上-y参数。如果不指定-y参数的话,apt-get命令会进入交互模式,需要用户输入命令来进行确认,但在docker环境中是无法响应这种交互的。apt-get 命令执行完毕之后,容器就会停止,但对容器的改动不会丢失。

4. 查看容器(ps)

1
2
3
4
5
6
# 列出当前所有正在运行的container
$docker ps
# 列出所有的container
$docker ps -a
# 列出最近一次启动的container
$docker ps -l

5. 保存对容器的修改(commit)

当你对某一个容器做了修改之后(通过在容器中运行某一个命令),可以把对容器的修改保存下来,这样下次可以从保存后的最新状态运行该容器。

1
2
# 保存对容器的修改; -a, --author="" Author; -m, --message="" Commit message
$docker commit ID new_image_name

Note: image相当于类,container相当于实例,不过可以动态给实例安装新软件,然后把这个container用commit命令固化成一个image。

6. 对容器的操作(rm、stop、start、kill、logs、diff、top、cp、restart、attach)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 删除所有容器
$docker rm `docker ps -a -q`

# 删除单个容器; -f, --force=false; -l, --link=false Remove the specified link and not the underlying container; -v, --volumes=false Remove the volumes associated to the container
$docker rm Name/ID

# 停止、启动、杀死一个容器
$docker stop Name/ID
$docker start Name/ID
$docker kill Name/ID

# 从一个容器中取日志; -f, --follow=false Follow log output; -t, --timestamps=false Show timestamps
$docker logs Name/ID

# 列出一个容器里面被改变的文件或者目录,list列表会显示出三种事件,A 增加的,D 删除的,C 被改变的
$docker diff Name/ID

# 显示一个运行的容器里面的进程信息
$docker top Name/ID

# 从容器里面拷贝文件/目录到本地一个路径
$docker cp Name:/container_path to_path
$docker cp ID:/container_path to_path

# 重启一个正在运行的容器; -t, --time=10 Number of seconds to try to stop for before killing the container, Default=10
$docker restart Name/ID

# 附加到一个运行的容器上面; --no-stdin=false Do not attach stdin; --sig-proxy=true Proxify all received signal to the process
$docker attach ID

Note: attach命令允许你查看或者影响一个运行的容器。你可以在同一时间attach同一个容器。你也可以从一个容器中脱离出来,是从CTRL-C。

7. 保存和加载镜像(save、load)

当需要把一台机器上的镜像迁移到另一台机器的时候,需要保存镜像与加载镜像。

1
2
3
4
5
6
7
8
9
# 保存镜像到一个tar包; -o, --output="" Write to an file
$docker save image_name -o file_path
# 加载一个tar包格式的镜像; -i, --input="" Read from a tar archive file
$docker load -i file_path

# 机器a
$docker save image_name > /home/save.tar
# 使用scp将save.tar拷到机器b上,然后:
$docker load < /home/save.tar

8、 登录registry server(login)

1
2
# 登陆registry server; -e, --email="" Email; -p, --password="" Password; -u, --username="" Username
$docker login

9. 发布image(push)

1
2
# 发布docker镜像
$docker push new_image_name

10. 根据Dockerfile 构建出一个容器

1
2
3
4
5
6
#build
	  --no-cache=false Do not use cache when building the image
	  -q, --quiet=false Suppress the verbose output generated by the containers
	  --rm=true Remove intermediate containers after a successful build
	  -t, --tag="" Repository name (and optionally a tag) to be applied to the resulting image in case of success
$docker build -t image_name Dockerfile_path

CentOS 6 使用 LXC

http://purplegrape.blog.51cto.com/1330104/1343766/

LXC 自kernel 2.6.27 加入linux 内核,依赖Linux 内核的cgroup和namespace功能而实现,非常轻量级,设计用于操作系统内部应用级别的隔离。

不同于vmware,kvm等虚拟化技术,它是一种类似chroot的容器技术,非常的轻量级。

与传统的硬件虚拟化技术相比有以下优势:

a、更小的虚拟化开销。Linux内核本身是一个很好的硬件资源调度器,LXC的诸多特性基本由内核提供,而内核实现这些特性只有极少的花费,CPU,内存,硬盘都是直接使用。

b、更快的启动速度。lxc容器技术将操作系统抽象到了一个新的高度。直接从init启动,省去了硬件自检、grub引导、加载内核、加载驱动等传统启动项目,因此启动飞速。

c、更快速的部署。lxc与带cow特性的后端文件系统相结合,一旦建好了模板,利用快照功能,半秒钟即可实现克隆一台lxc虚拟机。LXC虚拟机本质上只是宿主机上的一个目录,这也为备份和迁移提供了极大便利。

d、更高内存使用效率。普通虚拟机一般会独占一段内存,即使闲置,其他虚拟机也无法使用,例如KVM。而容器可以只有一个内存上限,没有下限。如果它只使用1MB内存,那么它只占用宿主机1MB内存。宿主机可以将富余内存作为他用。

LXC 目前已经比较成熟,官方在2014年2月推出1.0版本后就开始了长期维护,目前最新版本已经是1.07,CentOS 从6.5 开始支持LXC技术。

将LXC投入生产环境完全没有问题,因为LXC并不是什么新技术,而是重新聚合了已经成熟了的技术。

环境CentOS 6.5 x64

1、安装LXC

1
2
3
yum install libcgroup lxc lxc-templates --enablerepo=epel
/etc/init.d/cgconfig start
/etc/init.d/lxc start

2、检查环境

1
lxc-checkconfig

输出如下即是OK

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Kernel configuration not found at /proc/config.gz; searching...
Kernel configuration found at /boot/config-2.6.32-431.1.2.0.1.el6.x86_64
--- Namespaces ---
Namespaces: enabled
Utsname namespace: enabled
Ipc namespace: enabled
Pid namespace: enabled
User namespace: enabled
Network namespace: enabled
Multiple /dev/ptsinstances: enabled
--- Control groups---
Cgroup: enabled
Cgroup namespace: enabled
Cgroup device: enabled
Cgroup sched: enabled
Cgroup cpu account: enabled
Cgroup memory controller: enabled
Cgroup cpuset: enabled
--- Misc ---
Veth pair device: enabled
Macvlan: enabled
Vlan: enabled
File capabilities: enabled
Note : Before booting a new kernel, you can check its configuration
usage : CONFIG=/path/to/config/usr/bin/lxc-checkconfig

/usr/share/lxc/templates/ 自带了常用的模板可供选择,debian/ubuntu,centos/redhat 都有。

3、使用模板安装一个centos 6 虚拟机

1
lxc-create -n vm01 -t centos

或者

1
lxc-create -n vm01 -t download -- -d centos -r 6 -a amd64

安装后,虚拟机默认位于/var/lib/lxc/vm01/rootfs,配置文件为/var/lib/lxc/vm01/config

a、如果你系统里恰好有个lvm VG 名字叫做lxc,那么lxc会识别到,加上一个参数 -B lvm,创建的虚拟机配置文件依然是/var/lib/lxc/vm01/config,但是lxc镜像会在/dev/lxc/vm01 这个LV 上 (默认500M大小);

示例:

1
lxc-create -n vm01 -t centos -B lvm --thinpool --fssize 250G --fstype xfs

上面的命令将会在lvm上创建一个lv,名为vm01,最大容量250G(因为加了thinpool参数,用多少占多少),文件系统是xfs。

b、如果你的/var 单独分区,恰好使用的是btrfs文件系统,lxc也会识别,创建lxc容器时自动创建子卷,并将容器镜像放在里面;

4、lxc容器

打开lxc容器并进入开机console,

1
lxc-start -n vm01

在后台运行虚拟机,并通过console连接过去 (使用ctrl+a+q退出console)

1
2
lxc-start -n vm01 -d
lxc-console -n vm01

直接连上虚拟机,不需要密码,连上后passwd设置root密码

1
lxc-attach -n vm01

查看lxc容器相关信息(名称、是否在运行、PID,CPU使用、IO使用、内存使用、IP地址、网络吞吐量)

1
lxc-info -n vm01

监视lxc容器的资源使用

1
lxc-top

5、配置虚拟机网络,

新版lxc自带一个桥接lxcbr0 (10.0.3.1),物理网卡通过NAT桥接到lxcbr0 ,网段为10.0.3.0/24。

如果上面新创建的虚拟机启动失败,很可能是lxcbr0 没有启动。

编辑文件/var/lib/lxc/vm01/config,确保文件包含一下内容

1
2
3
4
5
6
lxc.network.type= veth
lxc.network.link = lxcbr0
lxc.network.flags = up
lxc.network.name = eth0
lxc.network.ipv4 = 10.0.3.2/24
lxc.network.ipv4.gateway = 10.0.3.1

如果需要第二块网卡,则继续在/var/lib/lxc/vm01/config添加一组配置

1
2
3
4
5
lxc.network.type = veth
lxc.network.link = lxcbr0
lxc.network.flags = up
lxc.network.name = eth1
lxc.network.ipv4 = 10.0.3.3/24

虚拟机网络默认由dnsmasq分配,如果没有在lxc中指定,则由虚拟机内部dhcp获得。

veth依赖网卡桥接,且可以与任何机器(宿主机,其他虚拟机,局域网其他机器)通讯。

在网络层,可以采取下面的方式加固安全:

如果要隔绝虚拟机与宿主机的通讯(虚拟机之间可以通信,与局域网其他机器也可以通信),网卡可选择macvlan中的bridge模式

1
2
3
4
lxc.network.type = macvlan
lxc.network.macvlan.mode = bridge
lxc.network.flags = up
lxc.network.link = eth0

如果要进一步隔离同一宿主机上不同虚拟机之间的通讯(仅可与局域网其他机器通信),网卡还要选择macvlan中的vepa模式

1
2
3
4
lxc.network.type = macvlan
lxc.network.macvlan.mode = vepa
lxc.network.flags = up
lxc.network.link = eth0

下面是三种特殊的网络

1
lxc.network.type = none

none表示停用网络空间的namespace,复用宿主机的网络。

据说关闭容器也会关闭宿主机,ubuntu phone通过lxc里的安卓容器,使用网络复用达到兼容安卓应用的目的。(个人没有测试通过)

1
lxc.network.type = empty

empty表示容器没有网卡,仅有一个回环lo,无法通过网络层与外部通信。用于某些特殊的场合。比如将宿主机的某个图片目录挂载到容器里,容器利用有限的资源对图片进行处理,如果放在宿主机上处理,图片处理占用的资源可能不好控制,影响整体性能。

1
lxc.network.type = vlan

这种模式需要上联的物理交换机支持,用不同的vlan id 隔离容器与宿主机之间的通信。

6、控制虚拟机的资源

虚拟机默认与宿主机共享硬件资源,CPU,内存,IO等,也可以用cgroup实现资源隔离。

1
2
3
4
5
6
7
8
#设置虚拟机只使用0,1两个CPU核心
lxc-cgroup -n centos cpuset.cpus 0,1
#设置虚拟机可用内存为512M
lxc-cgroup -n centos memory.limit_in_bytes 536870912
#设置虚拟机消耗的CPU时间
 lxc-cgroup -n centos cpu.shares 256
#设置虚拟机消耗的IO权重
 lxc-cgroup -n centos blkio.weight 500

另一种限制资源的方法是将具体的限制写入虚拟机的配置文件,可选的参数如下:

1
2
3
4
5
6
7
8
9
10
#设置虚拟机只使用0,1两个CPU核心
lxc.cgroup.cpuset.cpus  = 0,1
#设置虚拟机消耗的CPU时间
lxc.cgroup.cpu.shares  = 256
#设置虚拟机可用内存为512M
lxc.cgroup.memory.limit_in_bytes = 512M
#限制虚拟机可用的内存和swap空间一共1G
lxc.cgroup.memory.memsw.limit_in_bytes = 1G
#设置虚拟机可使用的IO权重
lxc.cgroup.blkio.weight=500

7、安装ubuntu 12.04

LXC强大到有点变态,在centos上运行ubuntu?没错,因为内核对于LInux发行版来说是通用的。

1
lxc-create -n ubuntu -t ubuntu -- -r precise

或者加上MIRROR参数(仅适用于ubuntu,用于选择较近的软件源)

1
MIRROR="http://cn.archive.ubuntu.com/ubuntu"  lxc-create -n ubuntu-test -t ubuntu -- -r precise

点到为止,不深入。

8、容器克隆

你可以创建一个标准化的lxc容器作为模板,然后对它进行克隆,避免重新安装,实现横向扩展和环境的标准化。下面以基于lvm卷的容器为例

1
lxc-clone vm01 webserver01 -B lvm

克隆后的容器,是一个独立的lvm逻辑卷,默认与原来的大小一致(也可以指定大小),仅仅会改变mac地址和主机名。

如果你想节约空间,克隆时带上 -s (–snapshot) 参数,可以创建一个源容器的可读写快照,它几乎不占用空间,使得在一个机器上运行成百上千个容器成为可能,仅支持lvm和btrfs,因为它们都有cow功能 。-L 参数可以指定快照的大小。更多参数详见 man lxc-clone 。

1
lxc-clone vm01 webserver01 -s -B lvm

9、lxc容器的系统安全

lxc容器里的系统完全可以不需要用到root密码和ssh,可以设置空密码或者超级长的密码,openssh服务可以不必启动甚至不必安装。因为从宿主机运行下面的命令可以直接获得root shell,相当于chroot

1
lxc-attach -n webserver01

如果是应用容器,则更简单,因为容器里只有应用进程,比如httpd,连init 都木有。具体实现参考模板lxc-sshd 。

lxc 1.0还支持非特权容器,利用uidmap映射技术,将容器里的root映射为宿主机上的普通用户,允许以普通用户身份运行LXC容器,大大提高了宿主机的安全性。

使用方法省略,见我的另一篇文章。《ubuntu 14.04 体验LXC非特权容器》

http://purplegrape.blog.51cto.com/1330104/1528503