首页
登录 | 注册

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

应用编排一直是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.

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


查看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信息


拉取到的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




2020 jeepxie.net webmaster#jeepxie.net
10 q. 0.008 s.
京ICP备10005923号