clientcmd를 사용한 일관된 API 서버 접근

발행: (2026년 1월 20일 오전 03:00 GMT+9)
13 분 소요

Source: Kubernetes Blog

만약 Kubernetes API용 명령줄 클라이언트를 개발하고 싶다면—특히 kubectl 플러그인으로 사용할 수 있는 경우—사용자에게 kubectl과 친숙하게 느껴지도록 만드는 방법이 궁금할 수 있습니다.
kubectl options 출력물을 한눈에 보면 압도적일 수 있습니다:

“정말로 저 옵션들을 모두 구현해야 하나요?”

걱정하지 마세요! Kubernetes 프로젝트에서는 대부분의 복잡한 작업을 대신해 주는 두 개의 라이브러리를 이미 제공하고 있습니다:

  • clientcmd – kubectl 스타일의 명령줄 인수를 파싱하고 restclient.Config를 구축합니다.
  • cli‑runtimeclientcmd 위에 추가 기능을 제공합니다.

이 글에서는 clientcmd 라이브러리에 초점을 맞춥니다.

일반 철학

clientcmdclient‑go의 일부이므로 궁극적인 목적은 API 서버에 요청을 보낼 수 있는 restclient.Config를 생성하는 것입니다. 이는 kubectl과 동일한 의미 체계를 따릅니다:

소스설명
~/.kube/config (또는 동등한 파일)기본 설정
KUBECONFIG 환경 변수내용이 병합되는 하나 이상의 파일
명령줄 인수위의 모든 것을 재정의

clientcmd는 자동으로 --kubeconfig 플래그를 추가하지 않습니다; “플래그 바인딩” 섹션에서 추가하는 방법을 확인할 수 있습니다.

사용 가능한 기능

clientcmd는 프로그램이 다음을 처리하도록 합니다:

  • KUBECONFIG를 통한 kubeconfig 선택
  • 컨텍스트 선택
  • 네임스페이스 선택
  • 클라이언트 인증서 및 개인 키
  • 사용자 가장(임퍼시네이션)
  • HTTP 기본 인증(사용자 이름/비밀번호)

구성 병합

  • KUBECONFIG는 파일들의 콜론으로 구분된 목록을 포함할 수 있으며, 내용이 병합됩니다.
  • 맵 기반 설정첫 번째 정의가 적용되고, 이후 정의는 무시됩니다.
  • 맵이 아닌 설정마지막 정의가 적용됩니다.
  • KUBECONFIG에 지정된 존재하지 않는 파일은 경고만 발생시킵니다.
  • 사용자가 경로를 명시적으로 제공할 경우(예: --kubeconfig), 해당 파일은 존재해야 합니다.
  • KUBECONFIG가 설정되지 않은 경우, ~/.kube/config가 사용됩니다(존재한다면).

전체 프로세스

일반적인 사용 패턴은 clientcmd 패키지 문서에 요약되어 있습니다:

// 1️⃣ 로딩 규칙 구축 (기본값은 KUBECONFIG 또는 ~/.kube/config)
loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
//   – 필요에 따라 여기서 순서/파일을 사용자 정의할 수 있습니다

// 2️⃣ 오버라이드 준비 (플래그에서 나중에 채워짐)
configOverrides := &clientcmd.ConfigOverrides{}

// 3️⃣ 지연 로딩 클라이언트 구성 생성
kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
    loadingRules,
    configOverrides,
)

// 4️⃣ 최종 REST 구성 해결
config, err := kubeConfig.ClientConfig()
if err != nil {
    // 오류 처리
}

// 5️⃣ 클라이언트 구축 (동적 클라이언트를 사용하는 예시)
client, err := dynamic.NewForConfig(config)
if err != nil {
    // 오류 처리
}

이 글에서는 위 번호가 매겨진 주석에 해당하는 여섯 단계를 차례대로 살펴보겠습니다:

  1. 로드 규칙 구성
  2. 오버라이드 구성
  3. 플래그 집합 구축
  4. 플래그 바인딩
  5. 병합된 구성 구축
  6. API 클라이언트 얻기

1️⃣ Configure the Loading Rules

loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
  • KUBECONFIG가 설정되어 있으면 사용하고, 그렇지 않으면 ~/.kube/config를 사용합니다.
  • 기본 파일이 사용될 경우, 매우 오래된 기본값(~/.kube/.kubeconfig)에서 설정을 마이그레이션할 수 있습니다.

표준이 아닌 순서나 추가 파일이 필요하면 사용자 정의 ClientConfigLoadingRules를 만들 수 있지만, 대부분의 경우 기본값으로 충분합니다.

2️⃣ Configure the Overrides

clientcmd.ConfigOverrides는 kubeconfig 파일에서 로드된 내용에 덮어쓰기할 값을 저장하는 구조체입니다. 이 가이드에서는 오버라이드를 명령줄 플래그(pflag 라이브러리, Go의 flag 패키지를 대체하는 라이브러리)를 통해 채웁니다.

configOverrides := &clientcmd.ConfigOverrides{}

보통 여기서 직접 값을 설정하지 않으며, 다음 단계에서 구조체 필드를 플래그에 바인딩하게 됩니다.

3️⃣ 플래그 집합 만들기

플래그는 명령줄 인수를 나타냅니다 (예: --namespace 또는 -n). clientcmd는 각각 FlagInfo 구조체로 감싸진 세 그룹의 플래그를 제공합니다:

플래그 그룹일반적인 인수
인증--certificate-authority, --token, --as, --username, --password
클러스터--server, --certificate-authority, --insecure-skip-tls-verify, --proxy-url, --tls-server-name, --compress
컨텍스트--context, --cluster, --user, --namespace

추천 집합에는 세 그룹 모두--timeout 플래그가 포함됩니다.

추천 플래그 생성자

// No prefix → flags like --context, --namespace, etc.
flags := clientcmd.RecommendedConfigOverrideFlags("")
// With a prefix → flags like --from-context, --from-namespace, etc.
flags := clientcmd.RecommendedConfigOverrideFlags("from-")
  • --timeout 플래그는 기본값이 0입니다.
  • --namespace 플래그는 짧은 별칭 -n도 가집니다.

주의: 접두사는 짧은 이름에 영향을 주지 않습니다. 여러 접두사가 붙은 플래그 집합을 만들 경우, -n 별칭을 유지할 수 있는 것은 하나뿐입니다. 다른 집합에서는 짧은 이름을 비워 주세요:

kflags := clientcmd.RecommendedConfigOverrideFlags(prefix)
kflags.ContextOverrideFlags.Namespace.ShortName = "" // removes -n

4️⃣ 플래그 바인딩

이제 플래그 정의를 ConfigOverrides 구조체에 연결하여 명령줄에 입력된 값이 오버라이드에 채워지도록 합니다.

// Assume you have a pflag.FlagSet called fs
fs := pflag.NewFlagSet("myclient", pflag.ExitOnError)

// Build the flag structs (choose a prefix if you like)
flags := clientcmd.RecommendedConfigOverrideFlags("")

// Register the flags
flags.BindFlags(fs)                     // registers the flags
flags.BindFlagSet(fs, configOverrides) // populates configOverrides when parsed

kubectl과 동일하게 --kubeconfig 플래그도 필요하다면 직접 추가합니다:

fs.StringVar(&configOverrides.ClusterInfo.KubeConfigPath, "kubeconfig", "", "Path to the kubeconfig file")

마지막으로 명령줄을 파싱합니다:

if err := fs.Parse(os.Args[1:]); err != nil {
    // handle parse error
}

5️⃣ 병합된 구성 만들기

로드 규칙과 오버라이드가 준비되면, 지연 로딩 클라이언트 구성을 생성하고 최종 rest.Config를 해결합니다:

kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
    loadingRules,
    configOverrides,
)

restConfig, err := kubeConfig.ClientConfig()
if err != nil {
    // handle error (e.g., missing kubeconfig, invalid overrides)
}

restConfig에는 이제 다음으로부터 병합된 설정이 포함됩니다:

  • KUBECONFIG(또는 기본 파일)로 지정된 파일들
  • 플래그를 통해 제공된 오버라이드

6️⃣ API 클라이언트 얻기

rest.Config를 사용하여 필요한 Kubernetes 클라이언트를 인스턴스화합니다. 다음은 몇 가지 일반적인 예시입니다:

// CoreV1 client
coreClient, err := kubernetes.NewForConfig(restConfig)

// Dynamic client (works with any resource)
dynClient, err := dynamic.NewForConfig(restConfig)

// Discovery client
discClient, err := discovery.NewDiscoveryClientForConfig(restConfig)

여기서부터는 kubectl이 하는 것처럼 클러스터와 상호작용할 수 있습니다.

TL;DR – 6단계 체크리스트

단계코드 스니펫
1️⃣ Loading rulesloadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
2️⃣ OverridesconfigOverrides := &clientcmd.ConfigOverrides{}
3️⃣ Flagsflags := clientcmd.RecommendedConfigOverrideFlags("")
4️⃣ Bindflags.BindFlags(fs); flags.BindFlagSet(fs, configOverrides)
5️⃣ MergekubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides); restConfig, _ := kubeConfig.ClientConfig()
6️⃣ Clientclient, _ := kubernetes.NewForConfig(restConfig)

이 단계들을 따르면 kubectl과 동일한 외관과 동작을 Go CLI에 적용할 수 있으며, 모든 플래그를 직접 구현할 필요가 없습니다. 즐거운 코딩 되세요!

플래그 비활성화

플래그를 완전히 비활성화하려면, 해당 플래그의 긴 이름을 비워두세요:

kflags.ContextOverrideFlags.Namespace.LongName = ""

플래그 바인딩 (대안)

플래그 집합이 정의되면, clientcmd.BindOverrideFlags를 사용하여 명령줄 인수를 오버라이드에 바인딩할 수 있습니다. 이는 Go의 표준 flag 패키지 대신 pflag FlagSet이 필요합니다.

--kubeconfig도 바인딩하려면, 로딩 규칙에서 ExplicitPath를 바인딩하여 지금 수행하십시오:

flags.StringVarP(&loadingRules.ExplicitPath,
    "kubeconfig", "", "", "absolute path(s) to the kubeconfig file(s)")

병합된 구성 빌드 (대안)

병합된 구성을 만들기 위해 두 가지 함수가 제공됩니다:

  • clientcmd.NewInteractiveDeferredLoadingClientConfig
  • clientcmd.NewNonInteractiveDeferredLoadingClientConfig

interactive 버전은 제공된 리더를 사용해 인증 정보를 대화형으로 물어볼 수 있는 반면, non‑interactive 버전은 호출자가 제공한 정보만 사용합니다. 두 함수 모두 “지연(deferred)” 방식이며, 명령줄 인수를 파싱하기 이전에 호출할 수 있습니다; 실제로 구성이 생성될 때까지 파싱된 플래그 값이 구성에 반영됩니다.

API 클라이언트 얻기 (대안)

병합된 구성은 ClientConfig 인스턴스로 반환됩니다. ClientConfig() 메서드를 호출하면 해당 인스턴스에서 API 클라이언트를 얻을 수 있습니다.

구성을 찾을 수 없는 경우(예: KUBECONFIG가 비어 있거나 존재하지 않는 파일을 가리키고, ~/.kube/config가 없으며, 명령줄 오버라이드가 제공되지 않은 경우) 기본 설정은 KUBERNETES_MASTER를 언급하는 모호한 오류를 반환합니다. 이 레거시 동작은 kubectl--local--dry-run 플래그에 대해서만 유지됩니다.

Tip: clientcmd.IsEmptyConfig(err) 로 “빈 구성” 오류를 감지하고 사용자에게 더 명확한 메시지를 표시하세요.

Namespace() 메서드도 유용합니다—사용해야 할 네임스페이스를 반환하고, 사용자가 --namespace 플래그를 통해 네임스페이스를 오버라이드했는지 여부를 알려줍니다.

전체 예제

다음은 완전하고 실행 가능한 예제입니다:

package main

import (
	"context"
	"fmt"
	"os"

	"github.com/spf13/pflag"
	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/tools/clientcmd"
)

func main() {
	// Loading rules – no configuration yet
	loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()

	// Overrides and flag (command‑line argument) setup
	configOverrides := &clientcmd.ConfigOverrides{}
	flags := pflag.NewFlagSet("clientcmddemo", pflag.ExitOnError)

	// Bind the standard override flags
	clientcmd.BindOverrideFlags(configOverrides, flags,
		clientcmd.RecommendedConfigOverrideFlags(""))

	// Bind the --kubeconfig flag
	flags.StringVarP(&loadingRules.ExplicitPath,
		"kubeconfig", "", "", "absolute path(s) to the kubeconfig file(s)")

	// Parse the command‑line arguments
	flags.Parse(os.Args)

	// Construct the client configuration (non‑interactive)
	kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
		loadingRules, configOverrides)

	// Get a REST config for the client‑go libraries
	config, err := kubeConfig.ClientConfig()
	if err != nil {
		if clientcmd.IsEmptyConfig(err) {
			panic("Please provide a configuration pointing to the Kubernetes API server")
		}
		panic(err)
	}

	// Build the typed clientset
	client, err := kubernetes.NewForConfig(config)
	if err != nil {
		panic(err)
	}

	// Determine which namespace to use
	namespace, overridden, err := kubeConfig.Namespace()
	if err != nil {
		panic(err)
	}
	fmt.Printf("Chosen namespace: %s; overridden: %t\n", namespace, overridden)

	// Use the client – list all nodes
	nodeList, err := client.CoreV1().Nodes().List(context.TODO(), v1.ListOptions{})
	if err != nil {
		panic(err)
	}
	for _, node := range nodeList.Items {
		fmt.Println(node.Name)
	}
}

코딩을 즐기세요, 그리고 친숙한 사용 패턴으로 도구를 만드는 데 관심을 가져 주셔서 감사합니다!

Back to Blog

관련 글

더 보기 »

StatefulSet 프로젝트

전제 조건 StatefulSet은 다음 구성 요소가 필요합니다: - Headless Service – 각 pod에 대해 안정적인 DNS를 제공합니다. - StatefulSet manifest – pod들을 정의합니다.

컨테이너에서 PostgreSQL 가속화

문제: 오래된 CI 머신에서 느린 디스크를 사용해 테스트 스위트를 실행했을 때 PostgreSQL이 주요 병목 현상으로 드러났습니다. 각 테스트 실행은 완료하는 데 1시간 이상이 걸렸습니다.