天天看点

Linux容器LXC学习之Namespace

Linux Containers即就是Linux容器,是一个加强版的Chroot。容器可以提供轻量级的虚拟化,一遍隔离进程和资源,而且不需要提供指令解释机制以及全虚拟化的其他复杂性。; 

LXC主要依赖Linux内核的3种隔离机制(isolation infrastructure):

  • Chroot 将应用隔离岛一个虚拟的私有root下
  • Cgroups 实现资源控制,给每个用户实例可以按需提供其计算资源
  • Namespaces 提供了隔离性,每个用户实例之间相互隔离,互不影响

Linux的3.12内核支持6 种Namespace:

  • UTS:隔离进程的hostname
  • IPC:进程间通信
  • PID:隔离进程间的PID namespace,clone新建的进程PID namespace中PID从 1 开始
  • NS:mount挂载点
  • NET:网络访问,包括接口
  • USER:将本地的虚拟user-id映射到真实的user-id

想要测试LXC的namespace隔离功能,可以使用clone函数,该函数的flag指定了要隔离的类型:

#include <sched.h>              int clone(int (*fn)(void *), void *child_stack, int flags, void *arg, ... );                  fn:子进程要执行的函数              child_stack:子进程的栈空间              flag:创建子进程的标志              arg:传给子进程的参数           

namespace中对应前五种可以使用clone函数的flag激活,如下: 

Linux容器LXC学习之Namespace

下边对每一种namespace进行实例验证。

UTS namespace

#define _GNU_SOURCE   //功能测试宏,可以在程序中定义(必须程序开头),也可以在编译时指定 gcc -D_GNU_SOURCE 程序名              #include <sys/types.h>              #include <sys/wait.h>              #include <stdio.h>              #include <stdlib.h>              #include <sched.h>              #include <signal.h>              #include <unistd.h>                  #define STACK_SIZE (1024 * 1024)                  static char child_stack[STACK_SIZE];   //定义成void *child_stack时,子进程不执行              char * const child_args[] = {              "/bin/bash",              NULL              };                  int child_main(void* arg) {              printf("Child inside Namespace\n");              sethostname("In Namespace",12);         //子进程中设置hostname,区分父子进程UTS命令空间              execv(child_args[0], child_args);              printf("execv error\n");              return -1;              }                  int main()              {              printf("Parent outside Namespace \n");              int child_pid = clone(child_main, child_stack + STACK_SIZE, CLONE_NEWUTS | SIGCHLD, NULL); //函数名代表函数的首地址,此处也可以写成&child_main              //子进程栈空间,不加STACK_SIZE时,无法执行子进程的/bin/bash              if(child_pid == -1)              perror("clone");              waitpid(child_pid, NULL, 0);              printf("child exit...\n");                  exit(EXIT_SUCCESS);              }           
[[email protected] docker]$ sudo ./a.out               Parent outside Namespace               Child inside Namespace              [[email protected] Namespace docker]# hostname              In Namespace              [[email protected] Namespace docker]# exit              exit              child exit...              [[email protected] docker]$ hostname              tiany.com              [[email protected] docker]$            

IPC

ipc namespace测试和上一个程序基本一致,只需要在clone函数中开启CLONE_NEWIPC标志即可, 在子进程中创建消息队列,其他进程中看不见,反之亦然。

测试如下所示:

inside IPC namespace :

Linux容器LXC学习之Namespace

outside IPC namespace: 

Linux容器LXC学习之Namespace

PID Namspace

在clone函数中添加CLONE_NEWPID标志,即就是开启了PID namespace; 

在子进程执行的程序中,添加 

printf("I am [%5d] child \n",getpid());

打印子进程PID,测试如下:

[[email protected] docker]$ sudo ./a.out               I am [13021] parent ?               I am [    1] child               [[email protected] Namespace docker]#            

会输出子进程PID为1,就是因为隔离了父子进程的PID namespace,在父进程时可以使用top 或 “ps exf”命令显示自己和子进程(未映射的)的PID,会发现在子进程时使用ps命令和父进程时的内容一模一样,是因为这些工具否是从真实的”/proc”文件系统中获取信息,而/proc是尚未隔离的。

NS namespace和NET namespace的测试代码地址:

https://github.com/ty92/Linux-LXC