zoukankan      html  css  js  c++  java
  • 容器基础(二): 使用Namespace进行边界隔离

    Linux Namespace

    容器技术可以认为是一种沙盒(sandbox), 为了实现沙盒/容器/应用间的隔离,就需要一种技术来对容器界定边界,从而让容器不至于互相干扰。当前使用的技术就是Namespace。Namespace定义如下:

    Namespace是Linux 内核用来隔离内核资源的方式, 是对全局系统资源的一种封装隔离,使得处于不同namespace的进程拥有独立的全局系统资源,改变一个namespace中的系统资源只会影响当前namespace里的进程,对其他namespace中的进程没有影响。

    目前, Linux内核里面实现了7种不同类型的namespace:

    1 名称        宏定义             隔离内容
    2 Cgroup      CLONE_NEWCGROUP   Cgroup root directory (since Linux 4.6)
    3 IPC         CLONE_NEWIPC      System V IPC, POSIX message queues (since Linux 2.6.19)
    4 Network     CLONE_NEWNET      Network devices, stacks, ports, etc. (since Linux 2.6.24)
    5 Mount       CLONE_NEWNS       Mount points (since Linux 2.4.19)
    6 PID         CLONE_NEWPID      Process IDs (since Linux 2.6.24)
    7 User        CLONE_NEWUSER     User and group IDs (started in Linux 2.6.23 and completed in Linux 3.8)
    8 UTS         CLONE_NEWUTS      Hostname and NIS domain name (since Linux 2.6.19)

    和namespace相关的函数有三个:

    clone: 创建一个新的进程并把他放到新的namespace中
    int clone(int (*child_func)(void *), void *child_stack
                , int flags, void *arg);
    
    flags: 
        指定一个或者多个上面的CLONE_NEW*(当然也可以包含跟namespace无关的flags), 
        这样就会创建一个或多个新的不同类型的namespace, 并把新创建的子进程加入新创建的这些namespace中。
    setns: 将当前进程加入到已有的namespace中
    int setns(int fd, int nstype);
    
    fd: 
        指向/proc/[pid]/ns/目录里相应namespace对应的文件,
        表示要加入哪个namespace
    
    nstype:
        指定namespace的类型(上面的任意一个CLONE_NEW*):
        1. 如果当前进程不能根据fd得到它的类型,如fd由其他进程创建,并通过UNIX domain socket传给当前进程,那么就需要通过nstype来指定fd指向的namespace的类型
        2. 如果进程能根据fd得到namespace类型,比如这个fd是由当前进程打开的,那么nstype设置为0即可
    unshare: 使当前进程退出指定类型的namespace,并加入到新创建的namespace(相当于创建并加入新的namespace)
    int unshare(int flags);
    
    flags:
        指定一个或者多个上面的CLONE_NEW*, 这样当前进程就退出了当前指定类型的namespace并加入到新创建的namespace
    clone和unshare的功能都是创建并加入新的namespace, 他们的区别是:
      > unshare是使当前进程加入新的namespace
      > clone是创建一个新的子进程,然后让子进程加入新的namespace,而当前进程保持不变

    简单的示例程序如下,clone时指定CLONE_NEWIPC/CLONE_NEWPID等参数, 由于没有自定义文件系统, 这个示例程序不使用chroot/pivot_root:

    ➜  cat namespace.c 
    #define _GNU_SOURCE
    #include <sched.h>
    #include <sys/wait.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    #include <sys/mount.h>
    
    #define CHECK_EXIST(ret_val, error_msg) 
    do {                                    
      if (ret_val == -1) {                  
        perror(error_msg);                  
        exit(-1);                           
      }                                     
    } while (0)
    
    static int child_func(void *hostname) {
      // set hostname
      CHECK_EXIST(sethostname((char *)hostname, strlen((char *)hostname)), "sethostname");
    
      // 挂载proc, 后续ps命令则只能看到自身进程
      CHECK_EXIST(mount("proc", "/proc", "proc", 0, NULL),    "mount-proc");
    
      // exec /bin/bash and wait input
      CHECK_EXIST(execlp("bash", "bash", (char *) NULL), "execlp");
    
      return 0;
    }
    
    //子进程的栈空间
    static char s_child_stack[1024 * 1024];
    
    int main(int argc, char *argv[]) {
      pid_t child_pid;
    
      if (argc < 2) {
        printf("Usage: %s <child-hostname>
    ", argv[0]);
        return -1;
      }
    
      /*
      * 创建并启动子进程并传入hostname给子进程
      *
      * flags参数如下:
      * 设置CLONE_NEWPID设置pid namespace
      * 设置CLONE_NEWUTS设置新的UTS namespace,
      * 设置子进程退出后返回给父进程的信号为SIGCHLD, 跟namespace无关
      */
      child_pid = clone(
          child_func,
          //栈是从高位向低位增长,所以这里要指向高位地址
          s_child_stack + sizeof(s_child_stack),
          CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWPID | CLONE_NEWIPC | SIGCHLD,
          argv[1]);
    
      CHECK_EXIST(child_pid, "clone");
    
      waitpid(child_pid, NULL, 0);
    
      return 0;
    }
    ➜  

     从结果来看:

    指定CLONE_NEWPID, 然后子进程挂载proc,子进程里面就只能看到自己的进程,且第一个bash的pid=1;

    指定CLONE_NEWIPC,   父子进程的IPC也被隔离了;

    ➜ ipcs -q

    --------- 消息队列 -----------
    键 msqid 拥有者 权限 已用字节数 消息

    ➜ ipcmk -Q
    消息队列 id:0
    ➜ ipcs -q

    --------- 消息队列 -----------
    键 msqid 拥有者 权限 已用字节数 消息
    0x34d432ae 0 root 644 0 0

    ➜ ./namespace sdocker
    root@sdocker:/home/jason/demo/namespace# hostname
    sdocker
    root@sdocker:/home/jason/demo/namespace# ps -ef
    UID PID PPID C STIME TTY TIME CMD
    root 1 0 0 00:39 pts/0 00:00:00 bash
    root 3 1 0 00:39 pts/0 00:00:00 ps -ef
    root@sdocker:/home/jason/demo/namespace# ipcs -q

    --------- 消息队列 -----------
    键 msqid 拥有者 权限 已用字节数 消息

    root@sdocker:/home/jason/demo/namespace# exit
    exit
    ➜ ipcs -q

    --------- 消息队列 -----------
    键 msqid 拥有者 权限 已用字节数 消息
    0x34d432ae 0 root 644 0 0

    ➜ 

    Excellence, is not an act, but a habit.
    作者:子厚.
    出处:http://www.cnblogs.com/aios/
    本文版权归作者和博客园共有,欢迎转载、交流、点赞、评论,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。

  • 相关阅读:
    小程序模板
    小程序 if else
    小程序入门小知识
    懒加载
    展示效果
    五星评价
    萤火虫效果
    下雪效果
    选项卡
    VUE组件中 data 里面的数据为什么要return 出来
  • 原文地址:https://www.cnblogs.com/aios/p/10035923.html
Copyright © 2011-2022 走看看