Sử dụng ứng dụng khách đi đến `kubectl áp dụng` đối với API Kubernetes trực tiếp với nhiều loại trong một tệp YAML


10

Tôi đang sử dụng https://github.com/kubernetes/client-go và tất cả đều hoạt động tốt.

Tôi có một bảng kê khai (YAML) cho Bảng điều khiển Kubernetes chính thức: https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0-beta4/aio/deploy/recommends.yaml

Tôi muốn bắt chước kubectl applybảng kê khai này trong mã Go, sử dụng client-go.

Tôi hiểu rằng tôi cần thực hiện một số (un) sắp xếp các byte YAML thành các loại API chính xác được xác định trong gói: https://github.com/kubernetes/api

Tôi đã chỉnh sửa thành công Createcác loại API đơn thành cụm của mình, nhưng làm cách nào để làm điều này cho một bảng kê khai có chứa danh sách các loại không giống nhau ? Có một nguồn kind: List*hỗ trợ các loại khác nhau?

Cách giải quyết hiện tại của tôi là phân tách tệp YAML bằng cách sử dụng csplit--- làm dấu phân cách

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

Tiếp theo, tôi lặp qua các phần (14) mới đã được tạo, đọc byte của chúng, bật loại đối tượng được bộ giải mã của UniversalDeserializer trả về và gọi các phương thức API chính xác bằng cách sử dụng máy khách k8s của tôi.

Tôi muốn làm điều này để lập trình để cập nhật cho bất kỳ phiên bản mới nào của bảng điều khiển vào cụm của tôi. Tôi cũng sẽ cần phải làm điều này cho Máy chủ Metrics và nhiều tài nguyên khác. Phương pháp thay thế (có thể đơn giản hơn) là gửi mã của tôi với kubectl được cài đặt vào hình ảnh container và gọi trực tiếp kubectl apply -f -; nhưng điều đó có nghĩa là tôi cũng cần phải viết cấu hình kube vào đĩa hoặc có thể truyền nội tuyến để kubectl có thể sử dụng nó.

Tôi thấy vấn đề này rất hữu ích: https://github.com/kubernetes/client-go/issues/193 Bộ giải mã sống ở đây: https://github.com/kubernetes/apimachowder/tree/master/pkg/r nb / nối tiếp

Nó được hiển thị trong ứng dụng khách - truy cập tại đây: https://github.com/kubernetes/client-go/blob/master/kubernetes/scheme/register.go#L69

Tôi cũng đã xem phương thức RunConvert được sử dụng bởi kubectl: https://github.com/kubernetes/kubernetes/blob/master/pkg/kubectl/cmd/convert/convert.go#L139 và cho rằng tôi có thể cung cấp genericclioptions của riêng tôi.IOStreams để có được đầu ra?

Có vẻ như RunConvert đang trên con đường phản đối

Tôi cũng đã xem xét các câu hỏi khác được gắn thẻ [client-go] nhưng hầu hết sử dụng các ví dụ cũ hoặc sử dụng tệp YAML với một kindđịnh nghĩa duy nhất và API đã thay đổi kể từ đó.

Chỉnh sửa: Bởi vì tôi cần phải làm điều này cho nhiều cụm và đang tạo các cụm theo chương trình (API AWS EKS + CloudFormation / eksctl ), tôi muốn giảm thiểu chi phí tạo ra ServiceAccounttrên nhiều bối cảnh cụm, trên nhiều tài khoản AWS. Lý tưởng nhất, bước xác thực duy nhất liên quan đến việc tạo ứng dụng khách của tôi là sử dụng aws-iam-Authenticator để nhận mã thông báo bằng cách sử dụng dữ liệu cụm (tên, vùng, chứng chỉ CA, v.v.). Đã không có bản phát hành aws-iam-Authenticator trong một thời gian, nhưng nội dung mastercho phép sử dụng vai trò tài khoản chéo của bên thứ ba và ID bên ngoài được thông qua. IMO, điều này sạch hơn so với sử dụng ServiceAccount(và IRSA) bởi vì có các dịch vụ AWS khác mà ứng dụng (API phụ trợ tạo và áp dụng các tiện ích bổ sung cho các cụm này) cần phải tương tác.

Chỉnh sửa: Gần đây tôi đã tìm thấy https://github.com/ericchiang/k8s . Nó chắc chắn đơn giản để sử dụng hơn so với máy khách, ở cấp độ cao, nhưng không hỗ trợ hành vi này.


1
Thay vì ghi cấu hình kube vào đĩa chứa, hãy thử sử dụng tài khoản dịch vụ kubernetes.io/docs/t task / configure
pod

1
Tại sao bạn không đọc nội dung của tệp YAML và phân chia ^---$trong mã của bạn?
Shawyeok

@Shawyeok Bởi vì điều đó vẫn đòi hỏi tôi phải biết những loại trong tệp. Không có cách nào để có được kiểu động mà không cần kiểm tra đối với một số loại dự kiến ​​(đối tượng Kubernetes) và nếu không có loại dự kiến ​​thì đối tượng sẽ không được áp dụng cho cụm (điều này dẫn đến nhiều vấn đề hơn). Điều này cũng sẽ dẫn đến việc phải viết rất nhiều mã cho một thành phần duy nhất không mở rộng cho một số thành phần. Ngoài việc giải mã là gọi phương thức API chính xác để áp dụng đối tượng vào một cụm.
Simon

Câu trả lời:


3

Nghe có vẻ như bạn đã tìm ra cách giải tuần tự các tệp YAML vào Kubernetes runtime.Object, nhưng vấn đề là tự động triển khai một runtime.Objectmã mà không viết mã đặc biệt cho mỗi Loại.

kubectlđạt được điều này bằng cách tương tác trực tiếp với API REST . Cụ thể, thông qua tài nguyên.Helper .

Trong mã của tôi, tôi có một cái gì đó như:

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

Xin chào Kevin, cảm ơn bạn đã trả lời! Tôi chưa có cơ hội để thử điều này, nhưng tôi không nhận ra package restmappervà điều này có vẻ rất hứa hẹn. Chấp nhận câu trả lời ngay bây giờ, nhưng sẽ xem lại nó sớm.
Simon

1
Hi vọng nó sẽ giúp ích cho bạn!
Kevin Lin
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.