高阶k8s二次开发教程 -- 通过阅读Istio源码习得

这篇具有很好参考价值的文章主要介绍了高阶k8s二次开发教程 -- 通过阅读Istio源码习得。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

本篇文章全网几乎找不到,在做深层次的k8s二次开发时非常管用。那就是使用Client-go去访问自定义CRD资源。

我们先使用kubebuilder生成一个CRD,论生成CRD这些,还是kubebuilder更加方便。

创建CRD

apiVersion: "apiextensions.k8s.io/v1beta1"
kind: "CustomResourceDefinition"
metadata:
  name: "projects.example.sealyun.com"
spec:
  group: "example.sealyun.com"
  version: "v1alpha1"
  scope: "Namespaced"
  names:
    plural: "projects"
    singular: "project"
    kind: "Project"
  validation:
    openAPIV3Schema:
      required: ["spec"]
      properties:
        spec:
          required: ["replicas"]
          properties:
            replicas:
              type: "integer"
              minimum: 1

创建Golang客户端

package v1alpha1

import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

type ProjectSpec struct {
    Replicas int `json:"replicas"`
}

type Project struct {
  metav1.TypeMeta   `json:",inline"`
  metav1.ObjectMeta `json:"metadata,omitempty"`
  Spec ProjectSpec `json:"spec"`
}

type ProjectList struct {
  metav1.TypeMeta `json:",inline"`
  metav1.ListMeta `json:"metadata,omitempty"`
  Items []Project `json:"items"`
}

定义DeepCopy方法

Kubernetes API(在本例中为Project和ProjectList)提供的每种类型都需要实现该k8s.io/apimachinery/pkg/runtime.Object接口。该接口定义了两种方法GetObjectKind()和DeepCopyObject()。第一种方法已经由嵌入式metav1.TypeMeta结构提供; 第二个你必须自己实现。

如果你不实现DeepCopy方法,那么资源是无法注册进入后续的资源列表中的。

该DeepCopyObject方法旨在生成对象的深层副本。由于这涉及许多样板代码,因此很多工具通常会自动生成这些方法。为了本文的目的,我们将手动完成。继续向deepcopy.go同一个包添加第二个文件:

package v1alpha1

import "k8s.io/apimachinery/pkg/runtime"

// DeepCopyInto copies all properties of this object into another object of the
// same type that is provided as a pointer.
func (in *Project) DeepCopyInto(out *Project) {
    out.TypeMeta = in.TypeMeta
    out.ObjectMeta = in.ObjectMeta
    out.Spec = ProjectSpec{
        Replicas: in.Spec.Replicas,
    }
}

// DeepCopyObject returns a generically typed copy of an object
func (in *Project) DeepCopyObject() runtime.Object {
    out := Project{}
    in.DeepCopyInto(&out)
    return &out
}

// DeepCopyObject returns a generically typed copy of an object
func (in *ProjectList) DeepCopyObject() runtime.Object {
    out := ProjectList{}
    out.TypeMeta = in.TypeMeta
    out.ListMeta = in.ListMeta

    if in.Items != nil {
        out.Items = make([]Project, len(in.Items))
        for i := range in.Items {
            in.Items[i].DeepCopyInto(&out.Items[i])
        }
    }
    return &out
}

注册类型

需要让客户端知道新类型。允许客户端在与API服务器通信的时候自动处理新类型,为此,register.go在包中添加一个新文件。

注意这里仅仅是指客户端而已,k8s服务器在apply crd资源的时候就自动会帮我们去注册。

package v1alpha1

import (
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    "k8s.io/apimachinery/pkg/runtime"
    "k8s.io/apimachinery/pkg/runtime/schema"
)

const GroupName = "example.sealyun.com"
const GroupVersion = "v1alpha1"

var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: GroupVersion}

var (
    SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
    AddToScheme   = SchemeBuilder.AddToScheme
)

func addKnownTypes(scheme *runtime.Scheme) error {
    scheme.AddKnownTypes(SchemeGroupVersion,
        &Project{},
        &ProjectList{},
    )

    metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
    return nil
}

此代码实际上并没有做任何事情(除了创建新runtime.SchemeBuilder实例)。重要的部分是AddToScheme函数,它是runtime.SchemeBuilder中创建的类型。一旦Kubernetes客户端初始化为注册您的类型,您可以稍后从客户端代码的任何部分调用此函数。

构建HTTP客户端

在定义类型并添加方法以在全局方案构建器中注册它们之后,您现在可以创建能够加载自定义资源的HTTP客户端。

package main

import (
    "flag"
    "log"
    "time"
    "k8s.io/apimachinery/pkg/runtime/schema"
    "k8s.io/apimachinery/pkg/runtime/serializer"
    "github.com/martin-helmich/kubernetes-crd-example/api/types/v1alpha1"
    "k8s.io/client-go/kubernetes/scheme"
    "k8s.io/client-go/rest"
    "k8s.io/client-go/tools/clientcmd"
)

var kubeconfig string

func init() {
    flag.StringVar(&kubeconfig, "kubeconfig", "", "path to Kubernetes config file")
    flag.Parse()
}

func main() {
    var config *rest.Config
    var err error
    if kubeconfig == "" {
        log.Printf("using in-cluster configuration")
        config, err = rest.InClusterConfig()
    } else {
        log.Printf("using configuration from '%s'", kubeconfig)
        config, err = clientcmd.BuildConfigFromFlags("", kubeconfig)
    }
    if err != nil {
        panic(err)
    }
    v1alpha1.AddToScheme(scheme.Scheme)
    crdConfig := *config
    crdConfig.ContentConfig.GroupVersion = &schema.GroupVersion{Group: v1alpha1.GroupName, Version: v1alpha1.GroupVersion}
    crdConfig.APIPath = "/apis"
    crdConfig.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs}
    crdConfig.UserAgent = rest.DefaultKubernetesUserAgent()
    exampleRestClient, err := rest.UnversionedRESTClientFor(&crdConfig)
    if err != nil {
        panic(err)
    }
}

您现在可以使用第exampleRestClient中创建的内容来查询example.sealyun.com/v1alpha1API组中的所有自定义资源。示例可能如下所示:

result := v1alpha1.ProjectList{}
err := exampleRestClient.
    Get().
    Resource("projects").
    Do().
    Into(&result)

没错,刚才注册的逻辑的唯一目的就是让这个地方的Into能够认识的了ProjectList方法。

为了以更加类型安全的方式使用您的API,通常最好将这些操作包装在您自己的客户端集中。为此,创建一个新的子包clientset/v1alpha1。首先,实现一个定义API组类型的接口,并将配置设置从您的main方法移动到该clientset的构造函数中(NewForConfig在下面的示例中):

package v1alpha1

import (
    "github.com/martin-helmich/kubernetes-crd-example/api/types/v1alpha1"
    "k8s.io/apimachinery/pkg/runtime/schema"
    "k8s.io/apimachinery/pkg/runtime/serializer"
    "k8s.io/client-go/kubernetes/scheme"
    "k8s.io/client-go/rest"
)

type ExampleV1Alpha1Interface interface {
    Projects(namespace string) ProjectInterface
}

type ExampleV1Alpha1Client struct {
    restClient rest.Interface
}

func NewForConfig(c *rest.Config) (*ExampleV1Alpha1Client, error) {
    config := *c
    config.ContentConfig.GroupVersion = &schema.GroupVersion{Group: v1alpha1.GroupName, Version: v1alpha1.GroupVersion}
    config.APIPath = "/apis"
    config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs}
    config.UserAgent = rest.DefaultKubernetesUserAgent()
    client, err := rest.RESTClientFor(&config)
    if err != nil {
        return nil, err
    }
    return &ExampleV1Alpha1Client{restClient: client}, nil
}

func (c *ExampleV1Alpha1Client) Projects(namespace string) ProjectInterface {
    return &projectClient{
        restClient: c.restClient,
        ns: namespace,
    }
}

以上是对client的封装

接下来,您需要实现一个特定的Project客户端集来访问自定义资源(请注意,上面的示例已经使用了我们仍需要提供的ProjectInterface和projectClient类型)。projects.go在同一个包中创建第二个文件:

package v1alpha1

import (
    "github.com/martin-helmich/kubernetes-crd-example/api/types/v1alpha1"
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    "k8s.io/apimachinery/pkg/watch"
    "k8s.io/client-go/kubernetes/scheme"
    "k8s.io/client-go/rest"
)

type ProjectInterface interface {
    List(opts metav1.ListOptions) (*v1alpha1.ProjectList, error)
    Get(name string, options metav1.GetOptions) (*v1alpha1.Project, error)
    Create(*v1alpha1.Project) (*v1alpha1.Project, error)
    Watch(opts metav1.ListOptions) (watch.Interface, error)
    // ...
}

type projectClient struct {
    restClient rest.Interface
    ns         string
}

func (c *projectClient) List(opts metav1.ListOptions) (*v1alpha1.ProjectList, error) {
    result := v1alpha1.ProjectList{}
    err := c.restClient.
        Get().
        Namespace(c.ns).
        Resource("projects").
        VersionedParams(&opts, scheme.ParameterCodec).
        Do().
        Into(&result)
    return &result, err
}

func (c *projectClient) Get(name string, opts metav1.GetOptions) (*v1alpha1.Project, error) {
    result := v1alpha1.Project{}
    err := c.restClient.
        Get().
        Namespace(c.ns).
        Resource("projects").
        Name(name).
        VersionedParams(&opts, scheme.ParameterCodec).
        Do().
        Into(&result)
    return &result, err
}

func (c *projectClient) Create(project *v1alpha1.Project) (*v1alpha1.Project, error) {
    result := v1alpha1.Project{}
    err := c.restClient.
        Post().
        Namespace(c.ns).
        Resource("projects").
        Body(project).
        Do().
        Into(&result)
    return &result, err
}

func (c *projectClient) Watch(opts metav1.ListOptions) (watch.Interface, error) {
    opts.Watch = true
    return c.restClient.
        Get().
        Namespace(c.ns).
        Resource("projects").
        VersionedParams(&opts, scheme.ParameterCodec).
        Watch()
}

上面的Delete和Update方法和这个差不多,没有什么区别。

创建informer

构建Kubernetes operator时,通常希望能够对新创建或更新的事件进行监听。理论上,可以定期调用该List()方法并检查是否添加了新资源。

大多数情况通过使用初始List()初始加载资源的所有相关实例,然后使用Watch()订阅相关事件进行处理。然后,使用从informer接收的初始对象列表和更新来构建本地缓存,该缓存允许快速访问任何自定义资源,而无需每次都访问API服务器。

这种模式非常普遍,以至于client-go库为此提供了一个cache包:来自包的Informerk8s.io/client-go/tools/cache。您可以为自定义资源构建新的Informer,如下所示:

package main

import (
    "time"
    "github.com/martin-helmich/kubernetes-crd-example/api/types/v1alpha1"
    client_v1alpha1 "github.com/martin-helmich/kubernetes-crd-example/clientset/v1alpha1"
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    "k8s.io/apimachinery/pkg/runtime"
    "k8s.io/apimachinery/pkg/util/wait"
    "k8s.io/apimachinery/pkg/watch"
    "k8s.io/client-go/tools/cache"
)

func WatchResources(clientSet client_v1alpha1.ExampleV1Alpha1Interface) cache.Store {
    projectStore, projectController := cache.NewInformer(
        &cache.ListWatch{
            ListFunc: func(lo metav1.ListOptions) (result runtime.Object, err error) {
                return clientSet.Projects("some-namespace").List(lo)
            },
            WatchFunc: func(lo metav1.ListOptions) (watch.Interface, error) {
                return clientSet.Projects("some-namespace").Watch(lo)
            },
        },
        &v1alpha1.Project{},
        1*time.Minute,
        cache.ResourceEventHandlerFuncs{},
    )
    go projectController.Run(wait.NeverStop)
    return projectStore
}

该NewInformer方法返回两个对象:第二个返回值,控制器控制List()和Watch()调用并填充第一个返回值,Store缓存监听到的一些信息。您现在可以使用store轻松访问CRD,列出所有CRD或通过名称访问它们。store函数返回interface{}类型,因此您必须将它们强制转换回CRD类型:

store := WatchResource(clientSet)
project := store.GetByKey("some-namespace/some-project").(*v1alpha1.Project)

如此很多情况下就不需要再去调用apiserver了,给apiserver减轻压力.

总结

虽然现在很多工具给我们写CRD controller带来了极大的便捷,但是对于client-go这些基本的使用还是非常必要的,而官方client-go的开发文档和事例真的是少之又少,基本仅包含非常基本的操作。

还有一个dynamic client的方式也可以用来访问自定义CRD,但是文中的方式会更优雅更清晰更适合工程化。文章来源地址https://www.toymoban.com/news/detail-611501.html

到了这里,关于高阶k8s二次开发教程 -- 通过阅读Istio源码习得的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • K8S之istio流量控制管理(十七)

    一,istio介绍 1、istio架构   结合上图我们来理解Istio的各组件的功能及相互之间的协作方式。 1. 自动注入:在创建应用程序时自动注入 Sidecar代理Envoy程序。在 Kubernetes中创建 Pod时,Kube-apiserver调用控制面组件的 Sidecar-Injector服务,自动修改应用程序的描述信息并注入Sidecar。在

    2024年02月09日
    浏览(46)
  • 为什么K8s需要服务网格Istio?

    Kubernetes服务网格是一种工具,用于在平台级别而非应用级别为应用程序注入可观测性、可靠性和安全性功能。Kubernetes和微服务的兴起推动了人们对这项技术的兴趣,许多组织都采用了Kubernetes服务网格解决方案。 微服务架构高度依赖于网络,而服务网格可以管理应用程序服务

    2024年04月13日
    浏览(50)
  • 体验istio(二):kubeadm安装k8s集群

    在这一篇中我们将使用kubeadm基于ubuntu22.04部署一个控制、工作节点分离的双节点集群用于测试,没有高可用加入,使用kubeadm的原因首先是它支持生产级部署,稳定性上没问题,而这里的测试环境也没有基础设施自动化相关的需求。 注意,在部署k8s方面,官方文档已经非常详

    2024年01月23日
    浏览(49)
  • 使用kind在mac本地搭建k8s及istio

    之前使用multipass装ubuntu,然后再用microk8s搭建k8s,这会直接用orbstack及kind在本地搭建k8s及istio 通过orbstack这个地址下载,主要是开销低,用来替代docker desktop 添加国内源 ~/.orbstack/config/docker.json 重启orbstack 或者下载https://github.com/istio/istio/releases/download/1.18.2/istio-1.18.2-osx-arm64.ta

    2024年02月14日
    浏览(48)
  • Installing and configuring Istio components on K8s

    Here\\\'s a step-by-step guide to installing and configuring Istio components, setting up basic routing, and implementing server-side authentication on Kubernetes: Install Istio: Download the latest release of Istio from the official Istio website. Extract the files from the downloaded package. Assuming you have a Kubernetes cluster, install Istio by running th

    2024年02月11日
    浏览(43)
  • 37.云原生之springcloud+k8s+GitOps+istio+安全实践

    云原生专栏大纲 安装gitlab,将https://gitee.com/zhouwei1996/spring-cloud-bookinfo.git迁移至gitlab gitlab中创建全局变量,如镜像仓库账号密码,保证gitlab-ci.yaml中内容安全 共享runner创建,获取token如下:glrt-wfzAecJmszsZb3GorS8J 安装gitlab-runner,参考:22.云原生之GitLab CICD实战及解析 修改ConfigM

    2024年03月22日
    浏览(75)
  • k8s整合istio配置gateway入口、配置集群内部服务调用管理

    curl -HHost:tomcat.shenshuxin.cn “http://node101:32318” 端口号是ingressgateway服务的nodeport 查看方式: kubectl get service -n istio-system | grep istio-ingressgateway 找到80端口对应的nodeport即可 注意部署的两个deployment需要指定一下版本标签 version: ?? 注意这里的hosts名称(demo-tomcat-for-istio-name)要和上面

    2024年02月12日
    浏览(38)
  • 云原生之深入解析K8S Istio Gateway服务的架构分析与实战操作

    Istio 提供一种简单的方式来为已部署的服务建立网络,该网络具有负载均衡、服务间认证、监控、网关等功能,而不需要对服务的代码做任何改动。 istio 适用于容器或虚拟机环境(特别是 k8s),兼容异构架构; istio 使用 sidecar(边车模式)代理服务的网络,不需要对业务代

    2024年02月13日
    浏览(59)
  • Online Boutique在k8s中部署,启用istio,配置Kiali、Jaeger、Prometheus、Grafana

    实验内容主要包括: (1)安装kubernetes集群环境,并安装部署dashboard,以可视化方式管理集群中的pod、service、delpoyment。 (2)将基于微服务架构的Online Boutique应用部署在上述kubernetes环境中。 (3)针对Online Boutique在熔断、限流、监控、认证、授权、安全、负载等方面的不足,

    2024年02月03日
    浏览(45)
  • k8s源码阅读环境配置

      k8s代码的阅读可以让我们更加深刻的理解k8s各组件的工作原理,同时提升我们Go编程能力。   IDE使用Goland,代码阅读环境需要进行如下配置: 从github上下载代码:https://github.com/kubernetes/kubernetes 在GOPATH目录下新建文件夹:$GOPATH/src/k8s.io/kubernetes 将下载的zip包解压后,将kub

    2024年01月21日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包