iT邦幫忙

2025 iThome 鐵人賽

DAY 28
2
DevOps

賢者大叔的容器修煉手札系列 第 28

OpenTelemetry Collector 的 KinD 測試實戰:從本地開發到 CI/CD Pipeline 🧪🚀

  • 分享至 

  • xImage
  •  

賢者大叔的容器修煉手札 第 28 篇

OpenTelemetry Collector 的 KinD 測試實戰:從本地開發到 CI/CD Pipeline 🧪🚀

經過前一天建立了完整的 OTel → Loki 日誌數據流,今天我們要學習 OpenTelemetry Collector 項目本身是如何使用 KinD 進行端到端測試的!就像醫生需要在實驗室裡驗證新藥一樣,我們的可觀測性系統也需要在真實的 Kubernetes 環境中進行全面測試。今天讓我們深入 OpenTelemetry Contrib Collector 的測試架構,學習如何用 KinD 建立測試環境!

今日主要了解 OpenTelemetry Contrib Collector 在 GitHub CICD pipeline 中利用 KinD 建立測試環境來進行 e2e testing。

🎯 今日學習目標

✅ 理解 OpenTelemetry Collector 如何使用 KinD 進行測試

✅ 掌握 KinD 在 CI/CD Pipeline 中的最佳實踐

🔍 OpenTelemetry Collector 的測試挑戰

🤔 為什麼需要在 Kubernetes 中測試?

# 🔴 傳統測試的局限性
docker run otel/opentelemetry-collector-contrib  # ✅ 單容器測試
go test ./...                                     # ✅ 單元測試

# 😵 但是無法測試:
# - Kubernetes 特定的 receiver(k8sattributes, k8sobjects)
# - 服務發現和自動配置
# - 多節點網路通訊交互
# - 真實的微服務間追蹤
# - Kubernetes 權限和 RBAC

🟢 KinD 解決的問題

# 現代測試方法:真實 K8s 環境
kind create cluster --config e2e-kind-config.yaml  # 🏗️ 建立測試集群
kubectl apply -f test-manifests/                   # 🚀 部署測試應用
go test --tags=e2e ./...                          # 🧪 端到端測試
kind delete cluster                                # 🧹 清理環境

🏗️ OpenTelemetry Collector 的 KinD 配置分析

📋 KinD 集群配置解析

# e2e-kind-config.yaml - OpenTelemetry 測試用 cluster配置
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4

# 🔧 全局 kubeadm 配置
kubeadmConfigPatches:
  - |
    kind: KubeletConfiguration
    # 🔐 啟用 TLS bootstrap - 用於測試 kubeletstatsreceiver
    serverTLSBootstrap: true

# 🎯 節點配置
nodes:
  - role: control-plane
    labels:
      # 🏷️ 用於 k8sattributesprocessor e2e 測試的自定義標籤
      foo: too

🎨 配置特色分析

  1. 🔐 TLS Bootstrap 配置
kubeadmConfigPatches:
  - |
    kind: KubeletConfiguration
    serverTLSBootstrap: true
  • 目的: 啟用 kubelet 的 TLS 服務器憑證自動申請
  • 用途: 測試 kubeletstatsreceiver 時需要安全的 kubelet API 訪問
  • 重要性: 模擬運營環境的安全設置
  1. 🏷️ 自定義節點標籤
nodes:
  - role: control-plane
    labels:
      foo: too
  • 目的: 為 k8sattributesprocessor 提供測試標籤
  • 用途: 驗證標籤提取和屬性豐富化功能
  • 測試場景: 確保處理器能正確讀取和使用節點標籤

🎭 CI/CD Pipeline 深度解析

📊 測試矩陣策略

e2e-tests.yml 可以看到 OpenTelemetry 使用了非常完整的測試策略:

# 🎯 多維度測試矩陣
strategy:
  fail-fast: false
  matrix:
    k8s-version:
      - "v1.30.0"    # 🚀 最新版本
      - "v1.23.17"   # 🛡️ 舊版本兼容性
    component:
      - receiver/k8sclusterreceiver      # 🏗️ 集群級別監控
      - processor/k8sattributesprocessor # 🏷️ 屬性豐富化
      - receiver/kubeletstatsreceiver    # 📊 節點統計
      - receiver/k8sobjectsreceiver      # 🎭 物件監控
      - extension/observer/k8sobserver   # 👁️ 服務發現

🔄 完整的 CI/CD 流程

  1. 🏗️ 建置階段
collector-build:
  runs-on: ubuntu-24.04
  steps:
    - name: Build Collector
      run: make otelcontribcol
    - name: Upload Collector Binary
      uses: actions/upload-artifact@v4
  1. 🐳 Docker 建置image
docker-build:
  steps:
    - name: Build Docker Image
      run: make docker-otelcontribcol
    - name: export image to tar
      run: docker save otelcontribcol:latest > /tmp/otelcontribcol.tar
  1. 🧪 Kubernetes 測試
kubernetes-test-matrix:
  steps:
    - name: Create kind cluster
      uses: helm/kind-action@v1.12.0
      with:
        node_image: kindest/node:${{ matrix.k8s-version }}
        config: ./.github/workflows/configs/e2e-kind-config.yaml
    
    - name: Fix kubelet TLS server certificates
      run: |
        kubectl get csr -o=jsonpath='{range.items[?(@.spec.signerName=="kubernetes.io/kubelet-serving")]}{.metadata.name}{" "}{end}' | xargs kubectl certificate approve

其中一個 e2e 測試程式在這,是以 Go 撰寫設計的。
透過宣告註解//go:build e2e 然後執行 go test -v --tags=e2e 就只會對有宣告該註解的測試腳本執行測試。

相關的GitHub Action的執行結果如下。

https://ithelp.ithome.com.tw/upload/images/20250912/201049309oiqlFnUtC.png

🧪 Go 語言 E2E 測試深度解析

📝 測試標籤機制

//go:build e2e
// +build e2e

package k8sobserver

import (
    "context"
    "testing"
    "time"
    
    "github.com/stretchr/testify/require"
    "go.opentelemetry.io/collector/component/componenttest"
    "go.opentelemetry.io/collector/extension/extensiontest"
)

🎯 標籤機制的優勢:

  • 分離關注點: 單元測試與 E2E 測試完全分離
  • 選擇性執行: 可以只運行特定類型的測試
  • CI/CD 優化: 在不同階段執行不同測試套件

🏗️ 典型的 E2E 測試結構

func TestK8sObserver_EndToEnd(t *testing.T) {
    // 🎯 測試設置
    ctx := context.Background()
    
    // 🔧 建立測試配置
    cfg := &Config{
        AuthType: "serviceAccount",
        Node:     "${K8S_NODE_NAME}",
    }
    
    // 🏗️ 建立擴展實例
    factory := NewFactory()
    extension, err := factory.CreateExtension(ctx, extensiontest.NewNopCreateSettings(), cfg)
    require.NoError(t, err)
    
    // 🚀 啟動擴展
    err = extension.Start(ctx, componenttest.NewNopHost())
    require.NoError(t, err)
    defer extension.Shutdown(ctx)
    
    // 🧪 執行測試邏輯
    observer := extension.(*k8sObserver)
    
    // ⏳ 等待服務發現
    time.Sleep(10 * time.Second)
    
    // 🔍 驗證發現的端點
    endpoints := observer.ListEndpoints()
    require.NotEmpty(t, endpoints, "Should discover at least one endpoint")
    
    // 🎯 驗證端點屬性
    for _, endpoint := range endpoints {
        require.NotEmpty(t, endpoint.ID, "Endpoint should have ID")
        require.NotEmpty(t, endpoint.Target, "Endpoint should have target")
        
        // 🏷️ 驗證 Kubernetes 屬性
        attrs := endpoint.Details.GetAttributes()
        require.Contains(t, attrs, "k8s.namespace.name")
        require.Contains(t, attrs, "k8s.pod.name")
    }
}

🚀 實作成果

小弟將自己的程式碼跟 GitHub Actions 做整合。

https://ithelp.ithome.com.tw/upload/images/20250913/20104930WQ03S3T7TL.png
從 GitHub Actions 執行結果可以看到,我成功實現了一個完整的 KinD E2E 測試流程!🎉

✅ build-app: 47s       # 應用程式建置
✅ build-test: 1m 59s   # 測試映像建置  
✅ e2e-test (v1.30.0)   # Kubernetes 1.30 測試
✅ e2e-test (v1.29.0)   # Kubernetes 1.29 測試
✅ e2e-summary: 3s      # 測試總結

🔍 測試架構

1. 🏗️ 應用程式結構

// cmd/server/main.go - 簡潔的測試應用
type HealthResponse struct {
    Status    string            `json:"status"`
    Timestamp time.Time         `json:"timestamp"`
    Version   string            `json:"version"`
    Env       map[string]string `json:"env"`
}

func healthHandler(w http.ResponseWriter, r *http.Request) {
    response := HealthResponse{
        Status:    "healthy",
        Timestamp: time.Now(),
        Version:   os.Getenv("APP_VERSION"),
        Env: map[string]string{
            "KUBERNETES_SERVICE_HOST": os.Getenv("KUBERNETES_SERVICE_HOST"),
            "HOSTNAME":               os.Getenv("HOSTNAME"),
        },
    }
    // ... JSON response
}

2. 🧪 E2E 測試實作

// test/e2e/e2e_test.go
//go:build e2e
// +build e2e

func TestApplicationHealthEndpoint(t *testing.T) {
    // 🔍 健康檢查端點測試
    resp, err := http.Get("http://go-e2e-app-service:8080/health")
    require.NoError(t, err)
    defer resp.Body.Close()
    
    require.Equal(t, http.StatusOK, resp.StatusCode)
    
    var health HealthResponse
    err = json.NewDecoder(resp.Body).Decode(&health)
    require.NoError(t, err)
    require.Equal(t, "healthy", health.Status)
}

3. 🎯 Kubernetes 資源配置

# k8s/deployment.yaml - 完整的部署配置
apiVersion: apps/v1
kind: Deployment
metadata:
  name: go-e2e-app
spec:
  replicas: 2
  selector:
    matchLabels:
      app: go-e2e-app
  template:
    spec:
      containers:
      - name: app
        image: go-e2e-app:latest
        imagePullPolicy: Never
        ports:
        - containerPort: 8080
        env:
        - name: APP_VERSION
          value: "1.0.0"
        readinessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 10

4. 🔐 RBAC 權限管理

# k8s/rbac.yaml - 測試用服務帳戶
apiVersion: v1
kind: ServiceAccount
metadata:
  name: e2e-test-sa
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: e2e-test-role
rules:
- apiGroups: [""]
  resources: ["pods", "services", "endpoints"]
  verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
  resources: ["deployments"]
  verbs: ["get", "list"]

5. 🎭 測試執行 Job

# test/k8s/e2e-job.yaml - Kubernetes Job 執行測試
apiVersion: batch/v1
kind: Job
metadata:
  name: e2e-test
spec:
  backoffLimit: 2
  template:
    spec:
      serviceAccountName: e2e-test-sa
      containers:
      - name: e2e-test
        image: go-e2e-test:latest
        imagePullPolicy: Never
        command: ["go", "test", "-v", "--tags=e2e", "./test/e2e/"]
      restartPolicy: Never

總結

🎯 KinD 測試的最佳實踐總結

我們從 OpenTelemetry 學到的經驗

  1. 🏗️ 基礎設施即程式碼(IaC)
# 將測試環境配置版本化
kind-config.yaml     # 集群配置
test-manifests/      # 測試用 K8s 資源
e2e-tests/          # 測試腳本
Makefile            # 自動化命令
  1. 🎭 測試隔離與並行
# 使用不同的集群名稱避免衝突
cluster_name: e2e-test-${{ github.run_id }}
  1. 🔄 完整的生命週期管理
# 完整的測試流程
kind create cluster → 部署應用 → 執行測試 → 收集結果 → 清理環境
  1. 📊 多維度測試覆蓋
  • 功能測試: 基本 API 是否工作
  • 整合測試: 組件間是否正確互動
  • 性能測試: 在負載下的表現
  • 兼容性測試: 多版本 Kubernetes 支持
  1. 🎯 漸進式測試策略
# 階段 1: 基本功能測試
make test

# 階段 2: 容器化測試
make docker-build && make test

# 階段 3: Kubernetes 整合測試
make e2e-local

# 階段 4: 多環境兼容性測試
GitHub Actions 自動執行

KinD 讓我們能在 CI/CD 中運行接近運營環境的測試

  1. 📈 測試金字塔實踐
        🔺 E2E Tests (少量,高價值)
       🔺🔺 Integration Tests (適量)
    🔺🔺🔺🔺 Unit Tests (大量,快速)

上一篇
可觀測性 Log 的組合:OTel → Loki 數據流建構 🏗️📊
下一篇
混沌工程概念初探:在風暴前學會讀天象 🌪🧪
系列文
賢者大叔的容器修煉手札30
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言