26Z21a

QK·2026년 8월 21일

Headlamp는 쿠버네티스 표준 API 및 리소스(CRD 포함)를 기본으로 탐색할 수 있으며, React 기반 플러그인(Plugin)을 통해 사이드바에 전용 메뉴를 생성하고 CNPG 및 StarRocks 전용 상태 대시보드를 구성할 수 있습니다.

Headlamp의 클러스터 배포 방법과 전용 플러그인 구현 소스코드를 정리해 드립니다.


1. Headlamp 클러스터 배포 (In-Cluster)

Helm을 사용하여 쿠버네티스 클러스터 내부에 배포합니다.

Helm 배포 스크립트

# 1. Helm Repo 추가
helm repo add headlamp https://headlamp-k8s.github.io/headlamp/
helm repo update

# 2. In-Cluster 배포 (Ingress 및 플러그인 Volume 설정 포함)
helm install headlamp headlamp/headlamp \
  --namespace headlamp \
  --create-namespace \
  --set ingress.enabled=true \
  --set ingress.hosts[0].host=headlamp.internal.example.com \
  --set ingress.hosts[0].paths[0].path=/ \
  --set ingress.hosts[0].paths[0].type=ImplementationSpecific \
  --set config.pluginsDir=/headlamp/plugins

관리자 접근용 ServiceAccount 및 ClusterRoleBinding

apiVersion: v1
kind: ServiceAccount
metadata:
  name: headlamp-admin
  namespace: headlamp
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: headlamp-admin-binding
subjects:
  - kind: ServiceAccount
    name: headlamp-admin
    namespace: headlamp
roleRef:
  kind: ClusterRole
  name: cluster-admin
  apiGroup: rbac.authorization.k8s.io

토큰 생성: kubectl create token headlamp-admin -n headlamp --duration=24h 후 웹 UI 로그인 시 입력.


2. CNPG & StarRocks 상태 대시보드 플러그인 구현

Headlamp 플러그인은 @kinvolk/headlamp-plugin/lib SDK를 사용하여 작성하며, K8s Custom Resource를 비동기로 조회해 React 컴포넌트로 렌더링합니다.

플러그인 프로젝트 생성 및 의존성

npx @kinvolk/headlamp-plugin create lakehouse-monitor
cd lakehouse-monitor

src/index.tsx (전체 소스코드)

사이드바에 "Data Lakehouse" 탭을 추가하고, CNPG Cluster와 StarRocksCluster의 인스턴스별 상태(Phase, Primary, Replicas, FE/BE 노드 수)를 카드로 집계하는 플러그인 코드입니다.

import { 
  registerSidebarEntry, 
  registerRoute, 
  K8s, 
  CommonComponents 
} from '@kinvolk/headlamp-plugin/lib';
import React from 'react';

const { SectionBox, SimpleTable, StatusLabel } = CommonComponents;

// 1. K8s Custom Resource Definition 매핑
const CNPGCluster = K8s.ResourceClasses.makeCustomResourceClass({
  apiInfo: [{ group: 'postgresql.cnpg.io', version: 'v1', resource: 'clusters' }],
  isNamespaced: true,
});

const StarRocksCluster = K8s.ResourceClasses.makeCustomResourceClass({
  apiInfo: [{ group: 'starrocks.com', version: 'v1', resource: 'starrocksclusters' }],
  isNamespaced: true,
});

// 2. Lakehouse 상태 대시보드 메인 컴포넌트
function LakehouseDashboard() {
  const [cnpgList, setCnpgList] = React.useState<any[]>([]);
  const [srList, setSrList] = React.useState<any[]>([]);
  const [loading, setLoading] = React.useState(true);

  React.useEffect(() => {
    Promise.all([
      CNPGCluster.apiEndpoint.get('/apis/postgresql.cnpg.io/v1/clusters'),
      StarRocksCluster.apiEndpoint.get('/apis/starrocks.com/v1/starrocksclusters'),
    ])
      .then(([cnpgRes, srRes]) => {
        setCnpgList(cnpgRes?.items || []);
        setSrList(srRes?.items || []);
      })
      .catch((err) => console.error('Failed to fetch Lakehouse CRDs:', err))
      .finally(() => setLoading(false));
  }, []);

  if (loading) {
    return <div>Loading Lakehouse Clusters...</div>;
  }

  return (
    <div>
      {/* CloudNativePG (PostgreSQL) Status Section */}
      <SectionBox title={`CloudNativePG Clusters (${cnpgList.length})`}>
        <SimpleTable
          columns={[
            { label: 'Namespace', getter: (item: any) => item.metadata.namespace },
            { label: 'Name', getter: (item: any) => item.metadata.name },
            { 
              label: 'Phase / Status', 
              getter: (item: any) => {
                const phase = item.status?.phase || 'Unknown';
                const statusType = phase === 'Cluster in healthy state' ? 'success' : 'warning';
                return <StatusLabel status={statusType}>{phase}</StatusLabel>;
              } 
            },
            { label: 'Primary Pod', getter: (item: any) => item.status?.currentPrimary || '-' },
            { 
              label: 'Instances (Ready/Total)', 
              getter: (item: any) => `${item.status?.readyInstances || 0} / ${item.status?.instances || 0}` 
            },
          ]}
          data={cnpgList}
        />
      </SectionBox>

      {/* StarRocks Status Section */}
      <SectionBox title={`StarRocks Clusters (${srList.length})`}>
        <SimpleTable
          columns={[
            { label: 'Namespace', getter: (item: any) => item.metadata.namespace },
            { label: 'Name', getter: (item: any) => item.metadata.name },
            { 
              label: 'Phase', 
              getter: (item: any) => {
                const phase = item.status?.phase || 'Running';
                const statusType = phase === 'Running' ? 'success' : 'error';
                return <StatusLabel status={statusType}>{phase}</StatusLabel>;
              } 
            },
            { 
              label: 'FE Replicas (Ready/Req)', 
              getter: (item: any) => 
                `${item.status?.starRocksFeStatus?.readyReplicas || 0} / ${item.spec?.starRocksFeSpec?.replicas || 0}` 
            },
            { 
              label: 'BE Replicas (Ready/Req)', 
              getter: (item: any) => 
                `${item.status?.starRocksBeStatus?.readyReplicas || 0} / ${item.spec?.starRocksBeSpec?.replicas || 0}` 
            },
          ]}
          data={srList}
        />
      </SectionBox>
    </div>
  );
}

// 3. 사이드바 및 라우트 등록
registerRoute({
  path: '/lakehouse',
  sidebar: 'lakehouse',
  name: 'Data Lakehouse',
  exact: true,
  component: () => <LakehouseDashboard />,
});

registerSidebarEntry({
  name: 'lakehouse',
  label: 'Lakehouse DBs',
  url: '/lakehouse',
  icon: 'mdi:database-outline',
});

3. 플러그인 빌드 및 Headlamp 마운트 방법

  1. 플러그인 패키징 (빌드)
npm run build
# 빌드 결과물: dist/main.js
  1. ConfigMap 또는 initContainer로 Headlamp에 주입
    빌드된 main.js를 ConfigMap으로 만들어 Pod의 /headlamp/plugins/lakehouse-monitor/main.js 경로에 마운트합니다.
apiVersion: v1
kind: ConfigMap
metadata:
  name: headlamp-lakehouse-plugin
  namespace: headlamp
data:
  main.js: |
    # dist/main.js 내용 붙여넣기

Helm values.yaml에 볼륨 마운트 추가:

extraVolumes:
  - name: lakehouse-plugin
    configMap:
      name: headlamp-lakehouse-plugin
extraVolumeMounts:
  - name: lakehouse-plugin
    mountPath: /headlamp/plugins/lakehouse-monitor/main.js
    subPath: main.js
  1. Headlamp 웹 UI에 접속하면 좌측 사이드바에 "Lakehouse DBs" 메뉴가 생성되며, 클러스터 내 모든 CNPG 및 StarRocks 인스턴스의 헬스 상태와 복제 토폴로지가 단일 화면에 집계됩니다.
profile
engineer

0개의 댓글