Menggunakan client-go ke `kubectl apply` terhadap API Kubernetes secara langsung dengan beberapa tipe dalam satu file YAML

10

Saya menggunakan https://github.com/kubernetes/client-go dan semuanya berfungsi dengan baik.

Saya memiliki manifes (YAML) untuk Dashboard Kubernetes resmi: https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0-beta4/aio/deploy/recommended.yaml

Saya ingin meniru kubectl applymanifest ini dalam kode Go, menggunakan client-go.

Saya mengerti bahwa saya perlu melakukan beberapa (tidak) mengolah byte YAML ke dalam jenis API yang benar yang didefinisikan dalam paket: https://github.com/kubernetes/api

Saya telah berhasil Createmengedit satu jenis API ke kluster saya, tetapi bagaimana saya melakukan ini untuk manifes yang berisi daftar jenis yang tidak sama ? Apakah ada sumber daya kind: List*yang mendukung tipe berbeda ini?

Solusi saya saat ini adalah untuk membagi file YAML menggunakan csplitdengan --- sebagai pembatas

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

Selanjutnya, saya mengulangi bagian (14) baru yang dibuat, membaca byte mereka, mengaktifkan jenis objek yang dikembalikan oleh decoder UniversalDeserializer dan memanggil metode API yang benar menggunakan clientset k8s saya.

Saya ingin melakukan ini secara terprogram untuk membuat pembaruan ke versi baru dasbor ke dalam kluster saya. Saya juga perlu melakukan ini untuk Server Metrik dan banyak sumber daya lainnya. Metode alternatif (mungkin lebih sederhana) adalah mengirimkan kode saya dengan kubectl diinstal ke gambar kontainer dan langsung menelepon kubectl apply -f -; tetapi itu berarti saya juga perlu menulis konfigurasi kube ke disk atau mungkin meneruskannya inline sehingga kubectl dapat menggunakannya.

Saya menemukan masalah ini bermanfaat: https://github.com/kubernetes/client-go/issues/193 Dekoder tinggal di sini: https://github.com/kubernetes/apimachinery/tree/master/pkg/runtime/ serializer

Diekspos di klien-masuk di sini: https://github.com/kubernetes/client-go/blob/master/kubernetes/scheme/register.go#L69

Saya juga telah melihat metode RunConvert yang digunakan oleh kubectl: https://github.com/kubernetes/kubernetes/blob/master/pkg/kubectl/cmd/convert/convert.con#L139 dan berasumsi bahwa saya dapat memberikan genericclioptions saya sendiri. IOStreams untuk mendapatkan output?

Sepertinya RunConvert ada di jalur penghentian

Saya juga telah melihat pertanyaan lain yang ditandai [client-go] tetapi kebanyakan menggunakan contoh lama atau menggunakan file YAML dengan kinddefinisi tunggal , dan API telah berubah sejak saat itu.

Sunting: Karena saya perlu melakukan ini untuk lebih dari satu cluster dan saya sedang membuat cluster secara terprogram (AWS EKS API + CloudFormation / eksctl ), saya ingin meminimalkan overhead pembuatan ServiceAccounts di banyak konteks cluster, di banyak akun AWS. Idealnya, satu-satunya langkah otentikasi yang terlibat dalam menciptakan clientset saya adalah menggunakan aws-iam-authenticator untuk mendapatkan token menggunakan data cluster (nama, wilayah, CA cert, dll). Belum ada rilis aws-iam-authenticator untuk sementara waktu, tetapi konten mastermemungkinkan penggunaan peran lintas-peran peran pihak ketiga dan ID eksternal untuk diteruskan. IMO, ini lebih bersih daripada menggunakan ServiceAccount(dan IRSA) karena ada layanan AWS lainnya yang perlu berinteraksi dengan aplikasi (API backend yang membuat dan menerapkan add-on).

Sunting: Saya baru saja menemukan https://github.com/ericchiang/k8s . Jelas lebih mudah digunakan daripada klien-pergi, pada level tinggi, tetapi tidak mendukung perilaku ini.

Simon
sumber
1
Alih-alih menulis konfigurasi kube ke disk penampung, coba gunakan akun layanan kubernetes.io/docs/tasks/configure-pod-container/…
KFC_
1
Mengapa Anda tidak membaca konten file YAML dan membaginya dengan ^---$kode Anda?
Shawyeok
@Shawyeok Karena itu masih mengharuskan saya untuk mengetahui jenis apa yang ada di file. Tidak ada cara untuk mendapatkan tipe secara dinamis tanpa pengujian terhadap beberapa tipe yang diharapkan (objek Kubernetes), dan jika tipe yang diharapkan tidak ada maka objek tidak akan diterapkan ke cluster (yang mengarah ke lebih banyak masalah). Ini juga akan mengakibatkan harus menulis banyak kode untuk komponen tunggal yang tidak skala untuk beberapa komponen. Beyond decoding memanggil metode API yang benar untuk menerapkan objek ke sebuah cluster.
Simon

Jawaban:

3

Kedengarannya seperti Anda telah menemukan cara deserialize file YAML ke dalam Kubernetes runtime.Object, tetapi masalahnya adalah menyebarkan secara dinamis runtime.Objecttanpa menulis kode khusus untuk masing-masing Jenis.

kubectlmencapai ini dengan berinteraksi dengan API REST secara langsung. Secara khusus, melalui sumber daya . Bantuan .

Dalam kode saya, saya memiliki sesuatu seperti:

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)
}

Kevin Lin
sumber
Halo Kevin, terima kasih atas jawaban Anda! Saya belum punya kesempatan untuk mencoba ini, tapi saya tidak menyadarinya package restmapperdan ini terlihat sangat menjanjikan. Menerima jawaban untuk saat ini, tetapi akan mengunjungi lagi nanti
Simon
1
Semoga berhasil untuk Anda!
Kevin Lin