您的位置:首页 > 运维架构 > Docker

Kubernetes编排工具-helm源码分析(Tiller中status命令处理流程)

2017-04-29 11:40 513 查看
应用编排一直是Docker生态中,大家极力去解决的问题,作为docker生态中,发展最快的调度和编排引擎Kubernetes,其对应用的部署能力离大家的预期还有比较大的提升空间。

     helm作为Kubernetes一个包管理引擎,基于chart的概念,有效的对Kubernetes上应用的部署进行了优化。Chart通过模板引擎,下方对接Kubernetes中services模型,上端打造包管理仓库。最后的使得Kubernetes中,对应用的部署能够达到像使用apt-get和yum一样简单易用。

     本文将对helm中,status命令的处理流程进行分析,helm版本拉取master上代码,时间为2017年04月29号,当前最新版本为v2.3.1,拉取的commit Id 为 “045bf78f80024e42ddf055c8ad1d49cf2e6b91e0”.

1、tiller中status命令的入口
在tiller中,所有的命令真正执行的开始都在release_server.go中,我们查看release_server的所有函数,找到函数GetReleaseStatus.



查看status函数的源代码:

// GetReleaseStatus gets the status information for a named release.
func (s *ReleaseServer) GetReleaseStatus(c ctx.Context, req *services.GetReleaseStatusRequest) (*services.GetReleaseStatusResponse, error) {
if !ValidName.MatchString(req.Name) {
return nil, errMissingRelease
}

log.Printf("warning:GetReleaseStatus is called: %v", req.Name)

var rel *release.Release

if req.Version <= 0 {
var err error
rel, err = s.env.Releases.Last(req.Name)
if err != nil {
return nil, fmt.Errorf("getting deployed release %q: %s", req.Name, err)
}
} else {
var err error
if rel,err = s.env.Releases.Get(req.Name, req.Version); err != nil {
return nil, fmt.Errorf("getting release '%s' (v%d): %s", req.Name, req.Version, err)
}
}

if rel.Info == nil {
return nil, errors.New("release info is missing")
}
if rel.Chart == nil {
return nil, errors.New("release chart is missing")
}

log.Printf("warning:GetReleaseStatus is called:  rel.Info %v", rel.Info)

sc := rel.Info.Status.Code
statusResp := &services.GetReleaseStatusResponse{
Name:      rel.Name,
Namespace: rel.Namespace,
Info:      rel.Info,
}

log.Printf("warning:GetReleaseStatus is called:  rel.Manifest %v", rel.Manifest)

// Ok, we got the status of the release as we had jotted down, now we need to match the
// manifest we stashed away with reality from the cluster.
kubeCli := s.env.KubeClient
resp, err := kubeCli.Get(rel.Namespace, bytes.NewBufferString(rel.Manifest))
if sc == release.Status_DELETED || sc == release.Status_FAILED {
// Skip errors if this is already deleted or failed.
return statusResp, nil
} else if err != nil {
log.Printf("warning: Get for %s failed: %v", rel.Name, err)
return nil, err
}
rel.Info.Status.Resources = resp
return statusResp, nil
}


2、GetReleaseStatus 函数的处理流程
Step1:  MatchString 判断名称是否匹配,其中req.Name

if !ValidName.MatchString(req.Name) {
return nil, errMissingRelease
}


Step2: 拉取版本的Release信息

err = s.env.Releases.Get(req.Name, req.Version);


拉取到的Release结构体如下:

type Release struct {
// Name is the name of the release
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
// Info provides information about a release
Info *Info `protobuf:"bytes,2,opt,name=info" json:"info,omitempty"`
// Chart is the chart that was released.
Chart *hapi_chart3.Chart `protobuf:"bytes,3,opt,name=chart" json:"chart,omitempty"`
// Config is the set of extra Values added to the chart.
// These values override the default values inside of the chart.
Config *hapi_chart.Config `protobuf:"bytes,4,opt,name=config" json:"config,omitempty"`
// Manifest is the string representation of the rendered template.
Manifest string `protobuf:"bytes,5,opt,name=manifest" json:"manifest,omitempty"`
// Hooks are all of the hooks declared for this release.
Hooks []*Hook `protobuf:"bytes,6,rep,name=hooks" json:"hooks,omitempty"`
// Version is an int32 which represents the version of the release.
Version int32 `protobuf:"varint,7,opt,name=version" json:"version,omitempty"`
// Namespace is the kubernetes namespace of the release.
Namespace string `protobuf:"bytes,8,opt,name=namespace" json:"namespace,omitempty"`
}


其中Info中存储着结构体信息,Mainfest中存储着yaml文件信息(包含release所部署的所有k8s资源),模板文件Chart在Chart变量中

可以说Release的结构体中,存储了这个版本的所有信息。
中间有输入版本信息,--revision int32       if set, display the status of the named release with revision

Step3: 根据mainfest的信息通过kubeclient的get接口,获取当前资源的状态

resp, err := kubeCli.Get(rel.Namespace, bytes.NewBufferString(rel.Manifest))
if sc == release.Status_DELETED || sc == release.Status_FAILED {
// Skip errors if this is already deleted or failed.
return statusResp, nil
} else if err != nil {
log.Printf("warning: Get for %s failed: %v", rel.Name, err)
return nil, err
}
rel.Info.Status.Resources = resp


3、在kubeclient中Get接口的处理,源代码如下:

func (c *Client) Get(namespace string, reader io.Reader) (string, error) {
// Since we don't know what order the objects come in, let's group them by the types, so
// that when we print them, they come looking good (headers apply to subgroups, etc.)
objs := make(map[string][]runtime.Object)
infos, err := c.BuildUnstructured(namespace, reader)
if err != nil {
return "", err
}
missing := []string{}
err = perform(infos, func(info *resource.Info) error {
log.Printf("Doing get for %s: %q", info.Mapping.GroupVersionKind.Kind, info.Name)
log.Printf("Doing(00) get for %+v", info.Object)
if err := info.Get(); err != nil {
log.Printf("WARNING: Failed Get for resource %q: %s", info.Name, err)
missing = append(missing, fmt.Sprintf("%v\t\t%s", info.Mapping.Resource, info.Name))
return nil
}

// Use APIVersion/Kind as grouping mechanism. I'm not sure if you can have multiple
// versions per cluster, but this certainly won't hurt anything, so let's be safe.
gvk := info.ResourceMapping().GroupVersionKind
vk := gvk.Version + "/" + gvk.Kind
objs[vk] = append(objs[vk], info.Object)
return nil
})
if err != nil {
return "", err
}

// Ok, now we have all the objects grouped by types (say, by v1/Pod, v1/Service, etc.), so
// spin through them and print them. Printer is cool since it prints the header only when
// an object type changes, so we can just rely on that. Problem is it doesn't seem to keep
// track of tab widths
buf := new(bytes.Buffer)
p, _ := c.Printer(nil, printers.PrintOptions{})
for t, ot := range objs {
if _, err = buf.WriteString("==> " + t + "\n"); err != nil {
return "", err
}
for _, o := range ot {
if err := p.PrintObj(o, buf); err != nil {
log.Printf("failed to print object type %s, object: %q :\n %v", t, o, err)
return "", err
}
}
if _, err := buf.WriteString("\n"); err != nil {
return "", err
}
}
if len(missing) > 0 {
buf.WriteString("==> MISSING\nKIND\t\tNAME\n")
for _, s := range missing {
fmt.Fprintln(buf, s)
}
}
return buf.String(), nil
}


具体步骤为:

Step1、将YAML文件进行解析,得到解析后的结构体
Step2、根据结构体中的资源信息,分资源获得资源的状态
Step3、将得到的信息,放到有个map中

// Use APIVersion/Kind as grouping mechanism. I'm not sure if you can have multiple
// versions per cluster, but this certainly won't hurt anything, so let's be safe.
gvk := info.ResourceMapping().GroupVersionKind
vk := gvk.Version + "/" + gvk.Kind
objs[vk] = append(objs[vk], info.Object)


Step4、根据需要展示的字段,拼接成需要展示的信息

buf := new(bytes.Buffer)
p, _ := c.Printer(nil, printers.PrintOptions{})
for t, ot := range objs {
if _, err = buf.WriteString("==> " + t + "\n"); err != nil {
return "", err
}
for _, o := range ot {
if err := p.PrintObj(o, buf); err != nil {
log.Printf("failed to print object type %s, object: %q :\n %v", t, o, err)
return "", err
}
}
if _, err := buf.WriteString("\n"); err != nil {
return "", err
}
}


然后返回展示信息,返回的信息为一个字符串(包括多行)

备注:


grpc库使用https://github.com/grpc/grpc-go库中的commitId:
c5b7fccd204277076155f10851dad72b76a49317
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息