Hyper 源码分析------Hyper client创建与执行

一、Hyper总体架构

Hyper是典型的C/S架构.首先需要启动守护进程hyperd,用于接收来自client的请求,从而进行真正的hyper虚拟机创建工作以及对其的一系列操作.而作为client端的hyper命令则更像是一个命令解析器.它的作用仅仅只是对用户的hyper指令进行解析和封装,然后将其通过HTTP请求的形式传递给hyperd进程.下面就通过一条具体的hyper虚拟机启动命令:hyper run ubuntu:14.04 /bin/bash 对hyper client端的运作流程进行分析.


二、Hyper client创建

当用户输入如上所示的一条命令时,程序首先会进入./hyper.go文件中的main函数开始执行.第一步:通过cli := client.NewHyperClient(proto, addr, nil) 创建一个HyperClient的实例.其中两个参数protoaddr分别代表了hyper clienthyper server之间通信所采用的协议以及通信地址,默认的是使用unix socket domain,proto = "unix",addr="/var/run/hyper.sock".第三个参数代表是否使用https安全传输,默认为nil,即只使用http传输协议.这里需要注意的是,用户每次输入hyper命令都会重新创建一个hyper client实例,每个实例的生命周期只有接受用户命令,解析,发送给hyper daemon并接受hyper daemon的反馈这么长而已.

NewHyperClient函数中主要操作就是通过net.DialTimeout(proto, addr, timeout)函数与hyperd建立连接,然后再填充HyperClient这个数据结构.由于HyperClient主要的字段就是proto,addr以及传输协议scheme,并且在接下来的用处不是很大,因此这里就不再详细展开了.

 

三、flag参数解析

接着我们再次回到./hyper.gomain函数中.当用户输入hyper命令时,其实会输入两类参数,一类为hyper实际发送给hyper daemon的参数,例如上文中的run ubuntu:14.04等等.另一类则为flag参数,Go语言还专门提供了一个flag包用于解析此类参数.

对于flag参数,我们需要事先进行定义,比如:flHelp := flag.Bool("help", false, "Help Message") 我们就定义了一个名叫flHelpflag参数,且类型为bool,默认值为false.当接下调用flag.Parse(),若输入的命令为:hyper help ,flHelp就被解析为true,并且会转入执行输出帮助文档.hyper中其实只定义了两个flag参数,一个为flHelp,另一个为flVersion,分别用于输出帮助文档和版本信息.因为我们输入的是创建虚拟机的命令,以上条件都不满足,最后则会进入cli.Cmd(flag.Args()...)执行.其中flag.Args()返回的是一个字符串数组,就是那些非flag类型的参数.后面的省略号则是Go语言中代表不定参数的一种方法。

其实上述内容的核心意思就是过滤出flag参数,若命令中有version或者help字段就输出版本或帮助信息,然后将剩余的参数传输到hyper daemon去执行,仅此而已。

 

四、命令分发

在过滤完flag参数之后,接下来要做的就是需要将命令分发到具体的函数,来发送不同的http请求了,这个任务就是由前文所述的cli.Cmd(flag.Args()...)来完成的。在该函数中我们首先执行如下代码:

method, exists := cli.getMethod(args[:2]...)//Go能够返回两个参数

if exists {

return method(args[2:]...)

}

其中args[:2]代表的是截取args数组,准确的说是切片,的前两个元素传递给getMethod方法。并且返回两个参数:methodexists。其实method是一个函数类型的返回值,exists则是一个bool类型的变量,若existstrue则表示该命令合法,并执行相应的方法。

然后我们进入getMethod()函数观察它是如何由命令行参数路由到具体的方法的。首先,getMethod将传入其中的字符串参数都规范为首字母大写,其余字母小写的形式。然后通过语句methodName := "HyperCmd" + strings.Join(camelArgs, "")形成需要调用的方法的名称。事实上,hyper client执行具体命令的函数的命名方式都是统一的,都是HyperCmd+具体命令的形式。例如,对于hyper run命令则对应的执行函数的名称为HyperCmdRun。在得到了对应的函数名称之后,我们就能找到具体的函数了,主要是通过下面这条语句:

method := reflect.ValueOf(cli).MethodByName(methodName)

这里需要说明的是,此处使用的是GO语言的反射机制,reflect.ValueOf(cli).MethodByName得到的并不是一个函数指针,而是一个reflect.value的类型。然后还需要通过它,反射出一个接口,再通过一个断言,才能找到具体的函数,具体操作如下:

method.Interface().(func(...string) error)//这里使用了Go的高级语法,可能比较难理解

那么现在我们可以知道,如果我们执行hyper run命令时,上述代码片段中的method就是一个指向HyperCmdRun的函数指针。下面我们就对这个函数进行分析。

 

五、HyperCmdRun 函数分析

其实HyperCmdRun主要用于在虚拟机中启动Pod以及container的配置。在HyperCmdRun函数中首先定义了一个叫opts的变量:

var opts struct {

PodFile       string   `short:"p" long:"podfile" value-name:"\"\"" description:"Create and Run a pod  based on the pod file"`

….....

RestartPolicy string   `long:"restart" default:"never" value-name:"\"\"" default-mask:"-"  description:"Restart policy to apply when a container exits (never, onFailure, always)"`

}

其中包含的内容主要是输入hyper run命令时的一些可选选项,例如cpu数目,内核大小等等。接下来则会调用parser = gflag.NewParser(&opts, gflag.Default|gflag.IgnoreUnknown)函数将输入的参数进行解析到opts变量中。然后,会判断optsPodFileK8s字段是否为空,若不为空则从相应的pod filepod的配置文件)进行启动。和之前的flag参数类似,剩下的非可选参数则通过args, err := parser.Parse()导入args变量中。

因为我们并没有Pod file,所以自然需要手动进行配置了。首先定义几个变量:

var (

image   = args[1]

command = []string{}

env     = []pod.UserEnvironmentVar{}

)

其中image指的是启动的docker镜像的名称,对于我们的命令,显然image就是ubuntu:14.04

command则用于保存在虚拟机的容器中执行的命令,同于对于我们的命令,command就应该是/bin/bashenv则保存pod启动的环境变量,其实也就是上文opts中的那些变量。接下来,根据上文中获取的各类信息对UserContainerUserPod两个数据结构进行配置,其中包含了要启动的podcontainer的信息,然后将其转换成json格式,调用:

podId, err := cli.RunPod(string(jsonString))将相应的命令转换为http请求传送给hyper daemon

 

六、RunPod函数分析

RunPod函数中首先定义一个变量v := url.Values{},其中url.Values是一个map[string][]string类型,即通过一个string类型的键值能够获取一个字符串的数组。接下来通过v.Set("podArgs", podstring)将之前解析出来的pod配置和”podArgs”合成一个键值对。然后在hyper daemon中就能通过v[“podArgs”]解析出相应的命令行参数。最后,通过函数body, _, err := readBody(cli.call("POST", "/pod/run?"+v.Encode(), nil, nil))url.Values进行转码,打包生成一个http请求发往hyper daemon。由此hyper client的任务就基本完成了。

 

七、总结

其实总的来说,hyper client的任务非常简单,就只是解析用户输入的hyper命令,然后根据不同的命令,进行相应的配置,最后将需要执行的操作打包发送到hyper daemon中即可。

你可能感兴趣的:(Hyper源码分析系列,虚拟机,hyper,源码,架构,cs)