client-goを使用して、単一のYAMLファイル内の複数のタイプでKubernetes APIに対して直接「kubectl apply」を実行する


10

私はhttps://github.com/kubernetes/client-goを使用しており、すべて正常に動作します。

公式のKubernetesダッシュボードのマニフェスト(YAML)があります:https : //raw.githubusercontent.com/kubernetes/dashboard/v2.0.0-beta4/aio/deploy/recommended.yaml

kubectl applyclient-goを使用して、Goコードでこのマニフェストを模倣したいと思います。

YAMLバイトをいくつかの(アン)マーシャリングして、パッケージで定義されている正しいAPIタイプに変換する必要があることを理解しています:https : //github.com/kubernetes/api

Createクラスターに単一のAPIタイプを正常に編集しましたが、同じではないタイプのリストを含むマニフェストでこれを行うにはどうすればよいですか?kind: List*これらのさまざまなタイプをサポートするリソースはありますか?

現在の回避策はcsplit、区切り記号として---を使用してYAMLファイルを分割することです

csplit /path/to/recommended.yaml /---/ '{*}' --prefix='dashboard.' --suffix-format='%03d.yaml'

次に、作成された新しい(14)パーツをループし、それらのバイトを読み取り、UniversalDeserializerのデコーダーによって返されたオブジェクトのタイプを切り替え、k8sクライアントセットを使用して正しいAPIメソッドを呼び出します。

これをプログラムで実行して、ダッシュボードの新しいバージョンをクラスターに更新します。また、Metrics Serverや他の多くのリソースに対してもこれを行う必要があります。代替の(おそらくより簡単な)メソッドは、コンテナーイメージにインストールされたkubectlを使用してコードを出荷し、直接呼び出すことkubectl apply -f -です。しかし、それは、kubectlがそれを使用できるように、kube構成をディスクに書き込むか、インラインで渡す必要があることも意味します。

この問題が役立つことがわかりました:https : //github.com/kubernetes/client-go/issues/193 デコーダーはここにあります:https : //github.com/kubernetes/apimachinery/tree/master/pkg/runtime/シリアライザ

ここでclient-goに公開されています:https : //github.com/kubernetes/client-go/blob/master/kubernetes/scheme/register.go#L69

私はまた、kubectlで使用されているRunConvert方法を見て撮影した:https://github.com/kubernetes/kubernetes/blob/master/pkg/kubectl/cmd/convert/convert.go#L139をし、そのIを想定します独自のgenericclioptions.IOStreamsを提供して出力を取得できますか?

RunConvertは廃止予定のパスにあるようです

[client-go]とタグ付けされた他の質問も調べましたが、ほとんどの場合、古い例を使用するか、単一のkind定義が含まれているYAMLファイルを使用しています。その後、APIが変更されました。

編集:複数のクラスターに対してこれを行う必要があり、プログラムでクラスターを作成しているため(AWS EKS API + CloudFormation / eksctl)、ServiceAccount多くのAWSアカウントにわたって、多くのクラスターコンテキストにわたってsを作成するオーバーヘッドを最小限に抑えたいと思います。理想的には、クライアントセットの作成に関連する唯一の認証手順は、aws-iam-authenticatorを使用して、クラスターデータ(名前、地域、CA証明書など)を使用してトークンを取得することです。aws-iam-authenticatorはしばらくリリースされていませんが、その内容でmasterは、サードパーティの役割のクロスアカウントの役割と外部IDを使用して渡すことができます。IMO、これはServiceAccount(およびIRSA)アプリケーション(アドオンを作成してこれらのクラスターに適用するバックエンドAPI)が対話する必要がある他のAWSサービスがあるため。

編集:私は最近https://github.com/ericchiang/k8sを見つけました。高レベルでのclient-goよりも使い方は間違いなく簡単ですが、この動作はサポートされていません。


1
コンテナーのディスクにkube
KFC_

1
YAMLファイルのコンテンツを読み取り、^---$コードで分割しないのはなぜですか?
Shawyeok

@Shawyeokそれでも、ファイルにどのタイプがあるかを知る必要があります。予想されるいくつかのタイプ(Kubernetesオブジェクト)をテストせずに動的にタイプを取得する方法はありません。予想されるタイプが存在しない場合、オブジェクトはクラスターに適用されません(さらに問題が発生します)。これにより、複数のコンポーネントに対応できない単一のコンポーネントに対して多くのコードを記述する必要が生じます。デコードを超えて、適切なAPIメソッドを呼び出してオブジェクトをクラスターに適用します。
Simon

回答:


3

YAMLファイルをKubernetesにデシリアライズする方法を理解したようですruntime.Objectが、問題はruntime.Object、Kindごとに特別なコードを記述せずにを動的にデプロイすることです。

kubectlREST APIと直接対話することでこれを実現します。具体的には、resource.Helper使用します。

私のコードには、次のようなものがあります:

import (
    meta "k8s.io/apimachinery/pkg/api/meta"
    "k8s.io/cli-runtime/pkg/resource"
    "k8s.io/client-go/kubernetes"
    "k8s.io/client-go/rest"
    "k8s.io/client-go/restmapper"
    "k8s.io/apimachinery/pkg/runtime"
)

func createObject(kubeClientset kubernetes.Interface, restConfig rest.Config, obj runtime.Object) error {
    // Create a REST mapper that tracks information about the available resources in the cluster.
    groupResources, err := restmapper.GetAPIGroupResources(kubeClientset.Discovery())
    if err != nil {
        return err
    }
    rm := restmapper.NewDiscoveryRESTMapper(groupResources)

    // Get some metadata needed to make the REST request.
    gvk := obj.GetObjectKind().GroupVersionKind()
    gk := schema.GroupKind{Group: gvk.Group, Kind: gvk.Kind}
    mapping, err := rm.RESTMapping(gk, gvk.Version)
    if err != nil {
        return err
    }

    name, err := meta.NewAccessor().Name(obj)
    if err != nil {
        return err
    }

    // Create a client specifically for creating the object.
    restClient, err := newRestClient(restConfig, mapping.GroupVersionKind.GroupVersion())
    if err != nil {
        return err
    }

    // Use the REST helper to create the object in the "default" namespace.
    restHelper := resource.NewHelper(restClient, mapping)
    return restHelper.Create("default", false, obj, &metav1.CreateOptions{})
}

func newRestClient(restConfig rest.Config, gv schema.GroupVersion) (rest.Interface, error) {
    restConfig.ContentConfig = resource.UnstructuredPlusDefaultContentConfig()
    restConfig.GroupVersion = &gv
    if len(gv.Group) == 0 {
        restConfig.APIPath = "/api"
    } else {
        restConfig.APIPath = "/apis"
    }

    return rest.RESTClientFor(&restConfig)
}


こんにちはケビン、答えてくれてありがとう!私はこれを試す機会がありませんでしたが、知らなかったのでpackage restmapperこれは非常に有望に見えます。現時点では答えを受け入れますが、すぐに再訪します。
Simon

1
それがあなたのために働くことを願っています!
Kevin Lin
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.