Kubernetes 클러스터와 프로그래밍 방식으로 상호작용하기 위한 공식 Go 클라이언트 라이브러리인 client-go에 대해 알아보겠습니다. 이 강력한 라이브러리를 통해 Pod, Service, Deployment 등 Kubernetes 리소스를 관리하는 방법을 단계별로 학습해보겠습니다.

🚀 client-go란?

client-go는 Kubernetes에서 공식적으로 제공하는 Go 언어용 클라이언트 라이브러리입니다. Kubernetes API 서버와 통신하여 클러스터 리소스를 생성, 조회, 수정, 삭제할 수 있는 완전한 기능을 제공합니다.

주요 특징

  • 공식 지원: Kubernetes 프로젝트의 일부로 지속적으로 업데이트됩니다.
  • 타입 안전성: Go의 강력한 타입 시스템을 활용한 안전한 API 호출이 가능합니다.
  • 완전한 기능: kubectl이 제공하는 모든 기능을 프로그래밍 방식으로 사용할 수 있습니다.
  • 확장성: 커스텀 리소스 및 CRD(Custom Resource Definition) 지원이 포함되어 있습니다.

📋 사전 준비사항

시스템 요구사항

  • Go: 1.19 이상
  • Kubernetes 클러스터: minikube, kind, 또는 실제 클러스터
  • kubectl: 클러스터 접근을 위한 kubeconfig 설정

개발 환경 설정

# Go 모듈 초기화
go mod init client-go-tutorial

# client-go 라이브러리 설치
go get k8s.io/client-go@latest
go get k8s.io/apimachinery@latest

🔧 기본 설정 및 연결

kubeconfig 파일 경로 확인

기본적으로 kubeconfig 파일은 ~/.kube/config 경로에 위치하지만, 사용하는 Kubernetes 환경에 따라 경로가 다를 수 있습니다:

  • 표준 설치: ~/.kube/config
  • OrbStack 사용 시: ~/.orbstack/k8s/config.yml
  • Docker Desktop: ~/.kube/config
  • minikube: ~/.kube/config

OrbStack을 사용하는 경우, kubeconfig 파일이 ~/.orbstack/k8s/config.yml 경로에 생성되므로 이를 고려하여 클라이언트를 설정해야 합니다.

kubeconfig 기반 클라이언트 생성

package main

import (
    "context"
    "fmt"
    "os"
    "path/filepath"

    "k8s.io/client-go/kubernetes"
    "k8s.io/client-go/tools/clientcmd"
    "k8s.io/client-go/util/homedir"
)

func main() {
    // kubeconfig 파일 경로 설정
    var kubeconfig string
    if home := homedir.HomeDir(); home != "" {
        // OrbStack 사용 시 경로 확인
        orbstackConfig := filepath.Join(home, ".orbstack", "k8s", "config.yml")
        if _, err := os.Stat(orbstackConfig); err == nil {
            kubeconfig = orbstackConfig
        } else {
            kubeconfig = filepath.Join(home, ".kube", "config")
        }
    }

    // kubeconfig 로드
    config, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
    if err != nil {
        panic(err.Error())
    }

    // Kubernetes 클라이언트 생성
    clientset, err := kubernetes.NewForConfig(config)
    if err != nil {
        panic(err.Error())
    }

    // 클러스터 정보 확인
    serverVersion, err := clientset.Discovery().ServerVersion()
    if err != nil {
        panic(err.Error())
    }

    fmt.Printf("Connected to Kubernetes cluster version: %s\n", serverVersion.GitVersion)
}

In-Cluster 설정 (Pod 내부에서 실행 시)

package main

import (
    "context"
    "fmt"

    "k8s.io/client-go/kubernetes"
    "k8s.io/client-go/rest"
)

func createInClusterClient() (*kubernetes.Clientset, error) {
    // Pod 내부에서 실행될 때 사용
    config, err := rest.InClusterConfig()
    if err != nil {
        return nil, err
    }

    clientset, err := kubernetes.NewForConfig(config)
    if err != nil {
        return nil, err
    }

    return clientset, nil
}

📦 Pod 관리하기

Pod 생성하기

package main

import (
    "context"
    "fmt"

    corev1 "k8s.io/api/core/v1"
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    "k8s.io/client-go/kubernetes"
)

func createPod(clientset *kubernetes.Clientset, namespace string) error {
    pod := &corev1.Pod{
        ObjectMeta: metav1.ObjectMeta{
            Name:      "example-pod",
            Namespace: namespace,
            Labels: map[string]string{
                "app": "example",
            },
        },
        Spec: corev1.PodSpec{
            Containers: []corev1.Container{
                {
                    Name:  "nginx",
                    Image: "nginx:1.21",
                    Ports: []corev1.ContainerPort{
                        {
                            ContainerPort: 80,
                        },
                    },
                    Resources: corev1.ResourceRequirements{
                        Requests: corev1.ResourceList{
                            corev1.ResourceCPU:    "100m",
                            corev1.ResourceMemory: "128Mi",
                        },
                        Limits: corev1.ResourceList{
                            corev1.ResourceCPU:    "500m",
                            corev1.ResourceMemory: "512Mi",
                        },
                    },
                },
            },
            RestartPolicy: corev1.RestartPolicyAlways,
        },
    }

    result, err := clientset.CoreV1().Pods(namespace).Create(
        context.TODO(), pod, metav1.CreateOptions{})
    if err != nil {
        return err
    }

    fmt.Printf("Pod %s created successfully\n", result.GetObjectMeta().GetName())
    return nil
}

Pod 조회하기

func listPods(clientset *kubernetes.Clientset, namespace string) error {
    pods, err := clientset.CoreV1().Pods(namespace).List(
        context.TODO(), metav1.ListOptions{})
    if err != nil {
        return err
    }

    fmt.Printf("Found %d pods in namespace %s:\n", len(pods.Items), namespace)
    for _, pod := range pods.Items {
        fmt.Printf("- Name: %s, Status: %s\n", 
            pod.Name, pod.Status.Phase)
    }

    return nil
}

func getPod(clientset *kubernetes.Clientset, namespace, name string) error {
    pod, err := clientset.CoreV1().Pods(namespace).Get(
        context.TODO(), name, metav1.GetOptions{})
    if err != nil {
        return err
    }

    fmt.Printf("Pod Details:\n")
    fmt.Printf("Name: %s\n", pod.Name)
    fmt.Printf("Namespace: %s\n", pod.Namespace)
    fmt.Printf("Status: %s\n", pod.Status.Phase)
    fmt.Printf("Node: %s\n", pod.Spec.NodeName)
    fmt.Printf("Created: %s\n", pod.CreationTimestamp)

    return nil
}

Pod 삭제하기

func deletePod(clientset *kubernetes.Clientset, namespace, name string) error {
    deletePolicy := metav1.DeletePropagationForeground
    
    err := clientset.CoreV1().Pods(namespace).Delete(
        context.TODO(), name, metav1.DeleteOptions{
            PropagationPolicy: &deletePolicy,
        })
    if err != nil {
        return err
    }

    fmt.Printf("Pod %s deleted successfully\n", name)
    return nil
}

🚢 Deployment 관리하기

Deployment 생성하기

package main

import (
    "context"
    "fmt"

    appsv1 "k8s.io/api/apps/v1"
    corev1 "k8s.io/api/core/v1"
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    "k8s.io/client-go/kubernetes"
    "k8s.io/utils/pointer"
)

func createDeployment(clientset *kubernetes.Clientset, namespace string) error {
    deployment := &appsv1.Deployment{
        ObjectMeta: metav1.ObjectMeta{
            Name:      "nginx-deployment",
            Namespace: namespace,
        },
        Spec: appsv1.DeploymentSpec{
            Replicas: pointer.Int32(3),
            Selector: &metav1.LabelSelector{
                MatchLabels: map[string]string{
                    "app": "nginx",
                },
            },
            Template: corev1.PodTemplateSpec{
                ObjectMeta: metav1.ObjectMeta{
                    Labels: map[string]string{
                        "app": "nginx",
                    },
                },
                Spec: corev1.PodSpec{
                    Containers: []corev1.Container{
                        {
                            Name:  "nginx",
                            Image: "nginx:1.21",
                            Ports: []corev1.ContainerPort{
                                {
                                    ContainerPort: 80,
                                },
                            },
                            Resources: corev1.ResourceRequirements{
                                Requests: corev1.ResourceList{
                                    corev1.ResourceCPU:    "100m",
                                    corev1.ResourceMemory: "128Mi",
                                },
                            },
                        },
                    },
                },
            },
        },
    }

    result, err := clientset.AppsV1().Deployments(namespace).Create(
        context.TODO(), deployment, metav1.CreateOptions{})
    if err != nil {
        return err
    }

    fmt.Printf("Deployment %s created successfully\n", result.GetObjectMeta().GetName())
    return nil
}

Deployment 스케일링

func scaleDeployment(clientset *kubernetes.Clientset, namespace, name string, replicas int32) error {
    // 현재 Deployment 가져오기
    deployment, err := clientset.AppsV1().Deployments(namespace).Get(
        context.TODO(), name, metav1.GetOptions{})
    if err != nil {
        return err
    }

    // 레플리카 수 변경
    deployment.Spec.Replicas = &replicas

    // 업데이트 적용
    _, err = clientset.AppsV1().Deployments(namespace).Update(
        context.TODO(), deployment, metav1.UpdateOptions{})
    if err != nil {
        return err
    }

    fmt.Printf("Deployment %s scaled to %d replicas\n", name, replicas)
    return nil
}

🌐 Service 관리하기

Service 생성하기

func createService(clientset *kubernetes.Clientset, namespace string) error {
    service := &corev1.Service{
        ObjectMeta: metav1.ObjectMeta{
            Name:      "nginx-service",
            Namespace: namespace,
        },
        Spec: corev1.ServiceSpec{
            Selector: map[string]string{
                "app": "nginx",
            },
            Ports: []corev1.ServicePort{
                {
                    Port:       80,
                    TargetPort: intstr.FromInt(80),
                    Protocol:   corev1.ProtocolTCP,
                },
            },
            Type: corev1.ServiceTypeClusterIP,
        },
    }

    result, err := clientset.CoreV1().Services(namespace).Create(
        context.TODO(), service, metav1.CreateOptions{})
    if err != nil {
        return err
    }

    fmt.Printf("Service %s created successfully\n", result.GetObjectMeta().GetName())
    return nil
}

👀 리소스 모니터링 및 Watch

Watch API 사용하기

package main

import (
    "context"
    "fmt"
    "time"

    corev1 "k8s.io/api/core/v1"
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    "k8s.io/apimachinery/pkg/watch"
    "k8s.io/client-go/kubernetes"
)

func watchPods(clientset *kubernetes.Clientset, namespace string) error {
    // Watch 시작
    watchlist, err := clientset.CoreV1().Pods(namespace).Watch(
        context.TODO(), metav1.ListOptions{})
    if err != nil {
        return err
    }
    defer watchlist.Stop()

    fmt.Printf("Watching pods in namespace %s...\n", namespace)

    // 이벤트 수신 및 처리
    for event := range watchlist.ResultChan() {
        pod, ok := event.Object.(*corev1.Pod)
        if !ok {
            continue
        }

        switch event.Type {
        case watch.Added:
            fmt.Printf("Pod Added: %s\n", pod.Name)
        case watch.Modified:
            fmt.Printf("Pod Modified: %s (Status: %s)\n", pod.Name, pod.Status.Phase)
        case watch.Deleted:
            fmt.Printf("Pod Deleted: %s\n", pod.Name)
        }
    }

    return nil
}

Informer 패턴 사용하기

package main

import (
    "context"
    "fmt"
    "time"

    corev1 "k8s.io/api/core/v1"
    "k8s.io/apimachinery/pkg/fields"
    "k8s.io/client-go/kubernetes"
    "k8s.io/client-go/tools/cache"
)

func setupPodInformer(clientset *kubernetes.Clientset, namespace string) {
    // List & Watch 함수 정의
    listWatcher := cache.NewListWatchFromClient(
        clientset.CoreV1().RESTClient(),
        "pods",
        namespace,
        fields.Everything(),
    )

    // Informer 생성
    _, controller := cache.NewInformer(
        listWatcher,
        &corev1.Pod{},
        time.Second*10,
        cache.ResourceEventHandlerFuncs{
            AddFunc: func(obj interface{}) {
                pod := obj.(*corev1.Pod)
                fmt.Printf("Pod Added: %s/%s\n", pod.Namespace, pod.Name)
            },
            UpdateFunc: func(oldObj, newObj interface{}) {
                pod := newObj.(*corev1.Pod)
                fmt.Printf("Pod Updated: %s/%s (Status: %s)\n", 
                    pod.Namespace, pod.Name, pod.Status.Phase)
            },
            DeleteFunc: func(obj interface{}) {
                pod := obj.(*corev1.Pod)
                fmt.Printf("Pod Deleted: %s/%s\n", pod.Namespace, pod.Name)
            },
        },
    )

    // Informer 시작
    stopCh := make(chan struct{})
    defer close(stopCh)
    
    go controller.Run(stopCh)
    
    // 캐시 동기화 대기
    if !cache.WaitForCacheSync(stopCh, controller.HasSynced) {
        fmt.Println("Timed out waiting for caches to sync")
        return
    }

    fmt.Println("Pod informer started successfully")
    
    // 무한 대기 (실제 애플리케이션에서는 적절한 종료 조건 필요)
    <-stopCh
}

🔍 고급 기능 활용하기

라벨 셀렉터를 이용한 필터링

func listPodsWithSelector(clientset *kubernetes.Clientset, namespace, selector string) error {
    pods, err := clientset.CoreV1().Pods(namespace).List(
        context.TODO(), metav1.ListOptions{
            LabelSelector: selector,
        })
    if err != nil {
        return err
    }

    fmt.Printf("Found %d pods matching selector '%s':\n", len(pods.Items), selector)
    for _, pod := range pods.Items {
        fmt.Printf("- %s (Status: %s)\n", pod.Name, pod.Status.Phase)
    }

    return nil
}

// 사용 예시
// listPodsWithSelector(clientset, "default", "app=nginx")
// listPodsWithSelector(clientset, "default", "environment in (production,staging)")

커스텀 리소스 접근하기

package main

import (
    "context"
    "fmt"

    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    "k8s.io/apimachinery/pkg/runtime/schema"
    "k8s.io/client-go/dynamic"
)

func accessCustomResource(dynamicClient dynamic.Interface) error {
    // 커스텀 리소스의 GVR (Group, Version, Resource) 정의
    gvr := schema.GroupVersionResource{
        Group:    "example.com",
        Version:  "v1",
        Resource: "myresources",
    }

    // 커스텀 리소스 목록 조회
    list, err := dynamicClient.Resource(gvr).Namespace("default").List(
        context.TODO(), metav1.ListOptions{})
    if err != nil {
        return err
    }

    fmt.Printf("Found %d custom resources\n", len(list.Items))
    for _, item := range list.Items {
        fmt.Printf("- Name: %s\n", item.GetName())
    }

    return nil
}

🛡️ 에러 처리 및 모범 사례

안전한 에러 처리

package main

import (
    "context"
    "fmt"

    "k8s.io/apimachinery/pkg/api/errors"
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    "k8s.io/client-go/kubernetes"
)

func safeGetPod(clientset *kubernetes.Clientset, namespace, name string) error {
    pod, err := clientset.CoreV1().Pods(namespace).Get(
        context.TODO(), name, metav1.GetOptions{})
    
    if err != nil {
        if errors.IsNotFound(err) {
            fmt.Printf("Pod %s not found in namespace %s\n", name, namespace)
            return nil
        } else if errors.IsForbidden(err) {
            fmt.Printf("Access denied to pod %s\n", name)
            return err
        } else {
            fmt.Printf("Unexpected error: %v\n", err)
            return err
        }
    }

    fmt.Printf("Pod found: %s (Status: %s)\n", pod.Name, pod.Status.Phase)
    return nil
}

func createResourceSafely(clientset *kubernetes.Clientset) error {
    // 리소스 생성 시도
    _, err := createPod(clientset, "default")
    
    if err != nil {
        if errors.IsAlreadyExists(err) {
            fmt.Println("Pod already exists, skipping creation")
            return nil
        }
        return fmt.Errorf("failed to create pod: %w", err)
    }

    return nil
}

컨텍스트와 타임아웃 관리

package main

import (
    "context"
    "time"

    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    "k8s.io/client-go/kubernetes"
)

func listPodsWithTimeout(clientset *kubernetes.Clientset, namespace string) error {
    // 10초 타임아웃 설정
    ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
    defer cancel()

    pods, err := clientset.CoreV1().Pods(namespace).List(ctx, metav1.ListOptions{})
    if err != nil {
        return err
    }

    fmt.Printf("Found %d pods\n", len(pods.Items))
    return nil
}

📝 실전 예제: 전체 애플리케이션

다음은 Pod와 Service를 함께 관리하는 완전한 예제입니다.

package main

import (
    "context"
    "fmt"
    "log"
    "path/filepath"
    "time"

    appsv1 "k8s.io/api/apps/v1"
    corev1 "k8s.io/api/core/v1"
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    "k8s.io/apimachinery/pkg/util/intstr"
    "k8s.io/client-go/kubernetes"
    "k8s.io/client-go/tools/clientcmd"
    "k8s.io/client-go/util/homedir"
    "k8s.io/utils/pointer"
)

type AppManager struct {
    clientset *kubernetes.Clientset
    namespace string
}

func NewAppManager(namespace string) (*AppManager, error) {
    var kubeconfig string
    if home := homedir.HomeDir(); home != "" {
        kubeconfig = filepath.Join(home, ".kube", "config")
    }

    config, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
    if err != nil {
        return nil, err
    }

    clientset, err := kubernetes.NewForConfig(config)
    if err != nil {
        return nil, err
    }

    return &AppManager{
        clientset: clientset,
        namespace: namespace,
    }, nil
}

func (am *AppManager) DeployApp(appName, image string, replicas int32) error {
    // Deployment 생성
    deployment := &appsv1.Deployment{
        ObjectMeta: metav1.ObjectMeta{
            Name:      appName,
            Namespace: am.namespace,
        },
        Spec: appsv1.DeploymentSpec{
            Replicas: pointer.Int32(replicas),
            Selector: &metav1.LabelSelector{
                MatchLabels: map[string]string{
                    "app": appName,
                },
            },
            Template: corev1.PodTemplateSpec{
                ObjectMeta: metav1.ObjectMeta{
                    Labels: map[string]string{
                        "app": appName,
                    },
                },
                Spec: corev1.PodSpec{
                    Containers: []corev1.Container{
                        {
                            Name:  appName,
                            Image: image,
                            Ports: []corev1.ContainerPort{
                                {
                                    ContainerPort: 80,
                                },
                            },
                        },
                    },
                },
            },
        },
    }

    _, err := am.clientset.AppsV1().Deployments(am.namespace).Create(
        context.TODO(), deployment, metav1.CreateOptions{})
    if err != nil {
        return fmt.Errorf("failed to create deployment: %w", err)
    }

    // Service 생성
    service := &corev1.Service{
        ObjectMeta: metav1.ObjectMeta{
            Name:      appName + "-service",
            Namespace: am.namespace,
        },
        Spec: corev1.ServiceSpec{
            Selector: map[string]string{
                "app": appName,
            },
            Ports: []corev1.ServicePort{
                {
                    Port:       80,
                    TargetPort: intstr.FromInt(80),
                },
            },
            Type: corev1.ServiceTypeClusterIP,
        },
    }

    _, err = am.clientset.CoreV1().Services(am.namespace).Create(
        context.TODO(), service, metav1.CreateOptions{})
    if err != nil {
        return fmt.Errorf("failed to create service: %w", err)
    }

    fmt.Printf("App %s deployed successfully\n", appName)
    return nil
}

func (am *AppManager) WaitForDeployment(appName string, timeout time.Duration) error {
    ctx, cancel := context.WithTimeout(context.Background(), timeout)
    defer cancel()

    for {
        select {
        case <-ctx.Done():
            return fmt.Errorf("timeout waiting for deployment %s", appName)
        default:
            deployment, err := am.clientset.AppsV1().Deployments(am.namespace).Get(
                context.TODO(), appName, metav1.GetOptions{})
            if err != nil {
                return err
            }

            if deployment.Status.ReadyReplicas == *deployment.Spec.Replicas {
                fmt.Printf("Deployment %s is ready\n", appName)
                return nil
            }

            fmt.Printf("Waiting for deployment %s... (%d/%d replicas ready)\n",
                appName, deployment.Status.ReadyReplicas, *deployment.Spec.Replicas)
            time.Sleep(5 * time.Second)
        }
    }
}

func (am *AppManager) DeleteApp(appName string) error {
    // Deployment 삭제
    err := am.clientset.AppsV1().Deployments(am.namespace).Delete(
        context.TODO(), appName, metav1.DeleteOptions{})
    if err != nil {
        return fmt.Errorf("failed to delete deployment: %w", err)
    }

    // Service 삭제
    err = am.clientset.CoreV1().Services(am.namespace).Delete(
        context.TODO(), appName+"-service", metav1.DeleteOptions{})
    if err != nil {
        return fmt.Errorf("failed to delete service: %w", err)
    }

    fmt.Printf("App %s deleted successfully\n", appName)
    return nil
}

func main() {
    appManager, err := NewAppManager("default")
    if err != nil {
        log.Fatal(err)
    }

    // 애플리케이션 배포
    err = appManager.DeployApp("my-nginx", "nginx:1.21", 3)
    if err != nil {
        log.Fatal(err)
    }

    // 배포 완료 대기
    err = appManager.WaitForDeployment("my-nginx", 5*time.Minute)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println("App is running! Press Ctrl+C to clean up and exit.")
    
    // 여기서 필요한 작업 수행
    time.Sleep(30 * time.Second)

    // 정리
    err = appManager.DeleteApp("my-nginx")
    if err != nil {
        log.Fatal(err)
    }
}

🔧 디버깅 및 문제 해결

로깅 설정

package main

import (
    "flag"
    "k8s.io/klog/v2"
)

func init() {
    // klog 플래그 초기화
    klog.InitFlags(nil)
    
    // 로그 레벨 설정 (0: INFO, 1: DEBUG, 2: TRACE)
    flag.Set("v", "2")
    flag.Parse()
}

func main() {
    klog.Info("Starting Kubernetes client application")
    // ... 나머지 코드
}

네트워크 연결 문제 해결

func diagnoseConnection(clientset *kubernetes.Clientset) {
    // 1. 서버 버전 확인
    version, err := clientset.Discovery().ServerVersion()
    if err != nil {
        fmt.Printf("Cannot connect to API server: %v\n", err)
        return
    }
    fmt.Printf("Connected to Kubernetes %s\n", version.GitVersion)

    // 2. 네임스페이스 목록 확인
    namespaces, err := clientset.CoreV1().Namespaces().List(
        context.TODO(), metav1.ListOptions{})
    if err != nil {
        fmt.Printf("Cannot list namespaces: %v\n", err)
        return
    }
    fmt.Printf("Found %d namespaces\n", len(namespaces.Items))

    // 3. 현재 사용자 권한 확인
    selfSubjectRulesReview := &authorizationv1.SelfSubjectRulesReview{
        Spec: authorizationv1.SelfSubjectRulesReviewSpec{
            Namespace: "default",
        },
    }
    
    result, err := clientset.AuthorizationV1().SelfSubjectRulesReviews().Create(
        context.TODO(), selfSubjectRulesReview, metav1.CreateOptions{})
    if err != nil {
        fmt.Printf("Cannot check permissions: %v\n", err)
        return
    }
    
    fmt.Printf("User has %d resource rules and %d non-resource rules\n",
        len(result.Status.ResourceRules), len(result.Status.NonResourceRules))
}

📚 모범 사례 및 팁

1. 리소스 관리

// 항상 적절한 리소스 제한 설정
resources := corev1.ResourceRequirements{
    Requests: corev1.ResourceList{
        corev1.ResourceCPU:    "100m",
        corev1.ResourceMemory: "128Mi",
    },
    Limits: corev1.ResourceList{
        corev1.ResourceCPU:    "500m",
        corev1.ResourceMemory: "512Mi",
    },
}

2. 라벨과 어노테이션 활용

// 일관된 라벨링 전략 사용
labels := map[string]string{
    "app.kubernetes.io/name":       "myapp",
    "app.kubernetes.io/instance":   "myapp-prod",
    "app.kubernetes.io/version":    "1.0.0",
    "app.kubernetes.io/component":  "frontend",
    "app.kubernetes.io/part-of":    "myapp-system",
    "app.kubernetes.io/managed-by": "client-go",
}

3. 헬스체크 및 모니터링

// Readiness와 Liveness 프로브 설정
probes := &corev1.Probe{
    ProbeHandler: corev1.ProbeHandler{
        HTTPGet: &corev1.HTTPGetAction{
            Path: "/health",
            Port: intstr.FromInt(8080),
        },
    },
    InitialDelaySeconds: 10,
    PeriodSeconds:       10,
}

container.ReadinessProbe = probes
container.LivenessProbe = probes

🎯 마무리

client-go는 Kubernetes 클러스터를 프로그래밍 방식으로 관리할 수 있는 강력한 도구입니다. 이 가이드에서 다룬 기본 개념과 예제를 바탕으로 다음 단계로 나아가시기 바랍니다.

다음 학습 단계

  • Controller 패턴: Kubernetes 컨트롤러 개발하기
  • Custom Resource Definitions: 커스텀 리소스 생성 및 관리
  • Operator 개발: Kubernetes 오퍼레이터 구축하기
  • 성능 최적화: 대규모 클러스터에서의 효율적인 리소스 관리

유용한 리소스

client-go를 활용하여 Kubernetes 네이티브 애플리케이션을 개발하고, 클러스터 운영을 자동화해보시기 바랍니다.