단일 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 코드 에서이 매니페스트 를 모방하고 싶습니다 .

패키지에 정의 된 올바른 API 유형으로 YAML 바이트를 마샬링 해제해야한다는 것을 알고 있습니다 : 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 -. 그러나 이것은 또한 kubec 설정을 디스크에 쓰거나 kubectl이 사용할 수 있도록 인라인으로 전달해야 함을 의미합니다.

이 문제가 도움이된다는 것을 알았습니다. https://github.com/kubernetes/client-go/issues/193 디코더는 여기에 있습니다 : https://github.com/kubernetes/apimachinery/tree/master/pkg/runtime/ 시리얼 라이저

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을 하고 난 가정 내 자신의 genericclioptions.IOStreams 를 제공하여 출력을 얻을 수 있습니까?

RunConvert가 지원 중단 경로에있는 것 같습니다.

또한 [client-go]로 태그 된 다른 질문을 보았지만 대부분 오래된 예제를 사용하거나 단일 kind정의가 있는 YAML 파일을 사용 하면 API가 변경되었습니다.

편집 : 둘 이상의 클러스터에 대해이 작업을 수행하고 프로그래밍 방식으로 클러스터를 생성하고 있기 때문에 (AWS EKS API + CloudFormation / eksctl ) ServiceAccount많은 AWS 계정에서 여러 클러스터 컨텍스트 에서을 생성하는 오버 헤드를 최소화하고 싶습니다 . 이상적으로, 내 클라이언트 세트를 만드는 데 관련된 유일한 인증 단계는 aws-iam-authenticator 를 사용하여 클러스터 데이터 (이름, 지역, CA 인증서 등)를 사용하여 토큰을 얻는 것입니다. 잠시 동안 aws-iam-authenticator가 릴리스되지 않았지만 master타사 역할 교차 계정 역할 및 외부 ID 사용을 허용 하는 내용이 있습니다. IMO, 이것은 ServiceAccount(그리고 IRSA를 사용하는 것보다 깨끗합니다.) 다른 AWS 서비스가 있기 때문에 애플리케이션 (이러한 클러스터에 애드온을 생성하고 적용하는 백엔드 API)과 상호 작용해야합니다.

편집 : 최근 https://github.com/ericchiang/k8s를 발견했습니다 . 상위 수준에서 클라이언트 이동보다 사용이 훨씬 간단하지만이 동작을 지원하지는 않습니다.


1
컨테이너 디스크에 kube 구성을 쓰는 대신 서비스 계정 kubernetes.io/docs/tasks/configure-pod-container/…를 사용하십시오.
KFC_

1
YAML 파일의 내용을 읽고 ^---$코드에서 나누는 것이 어떻습니까?
Shawyeok

@Shawyeok 그래도 파일에 어떤 유형이 있는지 알아야합니다. 여러 예상 유형 (Kubernetes 객체)에 대해 테스트하지 않고 유형을 동적으로 가져올 수있는 방법이 없으며, 예상 유형이 없으면 해당 객체가 클러스터에 적용되지 않으므로 (더 많은 문제가 발생 함) 또한 여러 구성 요소에 맞게 확장되지 않는 단일 구성 요소에 대해 많은 코드를 작성해야합니다. 디코딩을 넘어 올바른 API 메소드를 호출하여 객체를 클러스터에 적용합니다.
Simon

답변:


3

YAML 파일을 Kubernetes로 직렬화 해제하는 방법을 알아 낸 것처럼 들리지만 runtime.Object문제는 runtime.Object각 종류에 대한 특수 코드를 작성하지 않고 동적으로 배포하는 것 입니다.

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이것은 매우 유망한 것으로 보인다. 지금은 답변을 수락하지만 곧 다시 방문 할 것입니다.
사이먼

1
그것이 당신을 위해 작동하기를 바랍니다!
Kevin Lin
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.