• hyperstart 容器创建流程分析


    hyperstart中运行的pod的核心数据结构如下所示:

    struct hyper_pod {
    	struct hyper_interface	*iface;
    	struct hyper_route	*rt;
    	struct portmapping_white_list	*portmap_white_lists;
    	char			**dns;
    	struct list_head	containers;
    	struct list_head	exec_head;
    	char			*hostname;
    	char			*share_tag;
    	int			init_pid;
    	uint32_t		i_num;
    	uint32_t		r_num;
    	uint32_t		d_num;
    	/* how many containers are running */
    	uint32_t		remains;
    	int			req_destroy;
    	int			efd;
    };
    

      

    1、static int hyper_start_pod(char *json, int length):

      该函数首先调用hyper_parse_pod(pod, json, length),将从runv传入的json数据解析用来填充pod,pod为指向全局变量global_pod的指针。之后,再调用hyper_setup_pod(pod)进行容器创建之前的准备工作,最后调用hyper_start_containers(pod)进行容器的创建和启动的工作。

    2、static int hyper_setup_pod(struct hyper_pod *pod):

      该函数首先创建了一个目录"/tmp/hyper/proc",之后再依次调用hyper_setup_network(pod),hyper_setup_dns(pod),hyper_setup_shared(pod),hyper_setup_portmapping(pod),hyper_setup_pod_init(pod)进行pod的初始化工作。其中我们先主要关注hyper_setup_shared和hyper_setup_pod_init的工作。

    3、static int hyper_setup_shared(struct hyper_pod *pod):

      该函数的作用就是将容器的镜像挂载到SHARE_DIR(/tmp/hyper/shared)。当hypervisor为QEMU时,该函数的操作简化的来看,就是将宿主机的/var/run/hyper/vm-ID/share_dir目录挂载到SHARE_DIR下。此时,rootfs在SHARE_DIR/container-ID/之下。

    4、static int hyper_setup_pod_init(struct hyper_pod *pod):

      该函数的主要作用是调用clone创建一个pod进程,其中的主要逻辑如下:

      int flag = CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWIPC | CLONE_NEWUTS

      struct hyper_pod_arg arg = {.pod = NULL, .ctl_pipe = {-1, -1}, .pod = pod}

      pod->init_pid = clone(hyper_pod_init, stack + stacksize , flags, &arg)

    5、static int hyper_pod_init(void *data):

      该函数的工作主要用于为SIGCHLD设置信号处理函数,重新挂载/proc目录,最后重新设置pod的hostname,最后无限等待用于处理SIGCHLD。

    hyperstart中container的核心数据结构如下:

    struct hyper_container {
    	struct list_head	list;
    	struct hyper_exec	exec;
    	int			ns;
    	uint32_t		code;
    
    	// configs
    	char			*id;
    	char			*rootfs;
    	char			*image;
    	char			*scsiaddr;
    	char			*fstype;
    	struct volume		*vols;
    	struct fsmap		*maps;
    	struct sysctl		*sys;
    	struct port		*ports;
    	int			vols_num;
    	int			maps_num;
    	int			sys_num;
    	int			ports_num;
    	int			initialize;
    };
    

      

    6、static int hyper_start_containers(struct hyper_pod *pod):

      该函数的作用就是根据pod内container的配置信息创建container,并且在其中运行process。主要的内容为遍历pod中的container,执行hyper_setup_container(c, pod)和hyper_run_process(&c->exec)

    7、int hyper_setup_container(struct hyper_container *container, struct hyper_pod *pod):

      该函数首先通过hyper_setup_container_portmapping(container, pod)和hyper_setup_pty(container),为container设置port mapping和pty,之后再调用pid = clone(hyper_setup_container_rootfs, stack + stacksize, flags, &arg) (注:flag = CLONE_NEWNS | SIGCHLD)创建容器进程。最后获取容器的mount namespace fd:

      sprintf(path, "/proc/%s/ns/mnt", pid);

      container->ns = open(path, O_RDONLY | O_CLOEXEC);

    8、static int hyper_setup_container_rootfs(void *data):

    (1)、该函数首先等待父进程打开容器进程的container->ns,之后调用hyper_enter_sandbox(arg->pod, -1),利用setns进入pod->init_pid的PID,UTS,IPC namespace,之后再fork一个子进程。(对pod->init_pid的操作不应该是在pod的PID namespace之外么?但现在是在pod 的 pid namespace中,并且已经重新挂载了/proc)

    (2)、之后,两个mount命令,将根目录的模式设置为MS_PRIVATE和MS_SLAVE。接着设置root目录为"/tmp/hyper/container-id/root/",然后将SHARE_DIR挂载到root目录,此时容器的根文件系统挂载完成。之后,将当前目录切换到容器的根目录

    (3)、调用函数container_setup_init_layer(container, setup_dns),对/etc, /etc/resolv.conf,/etc/hosts,/etc/hostname进行重新创建,并且将/proc/mounts重新链接到/etc/mtab。相当于创建了docker中的init-layer。之后再调用container_setup_modules,container_setup_volumes,container_setup_dns进行rootfs的初始化工作

    (4)、调用chroot(".")和chdir("/")

    (5)、调用container_setup_sysctl(container)和container_setup_workdir(container),根据配置文件队/proc/sys/进行配置,然后进入容器的workdir

    hyperstart中exec的核心数据结构如下所示:

    struct hyper_exec {
    
      struct list_head    list;
      struct hyper_pod    *pod;
      struct hyper_event    stdinev;
      struct hyper_event    stdoutev;
      struct hyper_event    stderrev;
      int          pid;
      int          ptyno;
    
      int          init;
      int          ptyfd;
      uint8_t        close_stdin_request;
      uint8_t        code;
      uint8_t        exit;
      uint8_t        ref;
      char         *container_id;
      char         *user;
      char         *group;
      char         **additional_groups;
      int          nr_additional_groups;
      struct env        *envs;
      int          envs_num;
      char         **argv;
      int          argc;
      int          tty;
      uint64_t         seq;
      uint64_t         errseq;
      char          *workdir;
    } 
    

      

    9、int hyper_run_process(struct hyper_exec *exec)

      ...

      struct stdio_config io = {-1, -1,-1, -1,-1, -1}

      ...

      hyper_setup_stdio(exec, &io)

      pipe2(pipe, O_CLOEXEC)

      pid = fork()

      if (pid == 0) {

        hyper_do_exec_cmd(exec, pipe[1], &io)
      }

      hyper_get_type(pipe[0], &type)

      hyper_setup_stdio_events(exec, &io)  // 和容器建立IO

      exec->pid = type

      list_add_tail(&exec->list, &exec->pod->exec_head)

      exec->ref++

      ....

    10、static int hyper_do_exec_cmd(struct hyper_exec *exec, int pipe, struct stdio_config *io)

      hyper_enter_sandbox(exec->pod, pipe) -> enter pidns of pod init

      c = hyper_find_container(exec->pod, exec->container_id)

      setns(c->ns, CLONE_NEWNS)

      chdir("/")

      hyper_setup_env(c->exec.envs, c->exec.envs_num)

      setenv("HOME", "/root", 1)

      setenv("HOSTNAME", exec->pod->hostname, 1)

      if (exec->tty) {

        setenv("TERM", "xterm", 1)
      } else {

        unsetenv("TERM")

      }

      hyper_exec_process(exec, io)

    11、static void hyper_exec_process(struct hyper_exec *exec, struct stdio_config *io)

      sigprocmask(SIG_SETMASK, &orig_mask, NULL)

      exec->workdir && chdir(exec->workdir)

      hyper_setup_exec_user(exec)

      hyper_setup_exec_env(exec->envs, exec->envs)

      setsid()

      hyper_install_process_stdio(exec, io)

      execvp(exec->argv[0], exec->argv)

  • 相关阅读:
    解决mysqldump: Got error: 1044: Access denied for user
    Ubuntu技巧之 is not in the sudoers file解决方法
    mysql日志详细解析
    linux利用grep查看打印匹配的下几行或前后几行的命令
    Linux无法使用userdel删除用户和组的解决办法
    ubuntu快速清理磁盘垃圾
    smarty中math函数的用法
    metinfo首页内容简介
    linux命令别名的使用
    mysql 导出表结构和表数据 mysqldump用法
  • 原文地址:https://www.cnblogs.com/YaoDD/p/5949893.html
Copyright © 2020-2023  润新知