在Kubernetes容器中运行Golang测试的最佳实践

在Kubernetes容器中运行Golang测试的最佳实践 我大量使用 Kubernetes,并且有一些测试依赖于投射服务账户令牌卷。这意味着我必须在 Kubernetes Pod 内部运行我的集成测试。

我使用 Skaffold 来部署 Pod,但 Skaffold 希望 Pod 永远运行,因此当 Pod 退出时,它会报错并退出。

我想我会把它转换成一个 Job,但我想知道是否有更好的方法来解决这个问题。有没有人遇到过投射服务账户令牌卷的类似问题,并找到了好的解决方案?

1 回复

更多关于在Kubernetes容器中运行Golang测试的最佳实践的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在 Kubernetes Pod 中运行 Go 测试时,使用 Job 确实是标准做法。以下是具体实现方案:

1. 创建测试专用的 Job 配置

# test-job.yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: go-integration-test
spec:
  ttlSecondsAfterFinished: 3600  # 完成后1小时自动清理
  backoffLimit: 0  # 失败不重试
  template:
    spec:
      serviceAccountName: test-service-account  # 使用有权限的SA
      automountServiceAccountToken: true  # 自动挂载令牌
      containers:
      - name: test-runner
        image: golang:1.21-alpine
        command: ["go", "test", "./...", "-v", "-count=1"]
        volumeMounts:
        - name: serviceaccount
          mountPath: /var/run/secrets/kubernetes.io/serviceaccount
          readOnly: true
        env:
        - name: KUBERNETES_SERVICE_HOST
          value: "kubernetes.default.svc"
        - name: KUBERNETES_SERVICE_PORT
          value: "443"
        workingDir: /app
        resources:
          requests:
            memory: "256Mi"
            cpu: "250m"
          limits:
            memory: "512Mi"
            cpu: "500m"
      restartPolicy: Never
      volumes:
      - name: serviceaccount
        projected:
          sources:
          - serviceAccountToken:
              path: token
              expirationSeconds: 3600
              audience: api

2. Go 测试代码示例

// integration_test.go
package main

import (
    "fmt"
    "io/ioutil"
    "os"
    "testing"
    
    "k8s.io/client-go/kubernetes"
    "k8s.io/client-go/rest"
)

func TestKubernetesIntegration(t *testing.T) {
    // 使用 Pod 内的服务账户令牌
    tokenPath := "/var/run/secrets/kubernetes.io/serviceaccount/token"
    
    // 读取服务账户令牌
    token, err := ioutil.ReadFile(tokenPath)
    if err != nil {
        t.Fatalf("Failed to read service account token: %v", err)
    }
    
    // 创建 Kubernetes 客户端配置
    config, err := rest.InClusterConfig()
    if err != nil {
        t.Fatalf("Failed to create in-cluster config: %v", err)
    }
    
    // 创建客户端
    clientset, err := kubernetes.NewForConfig(config)
    if err != nil {
        t.Fatalf("Failed to create kubernetes client: %v", err)
    }
    
    // 测试 Kubernetes API 访问
    _, err = clientset.CoreV1().Namespaces().List(context.Background(), metav1.ListOptions{})
    if err != nil {
        t.Fatalf("Failed to list namespaces: %v", err)
    }
    
    t.Log("Successfully authenticated with Kubernetes API")
}

func TestWithServiceAccount(t *testing.T) {
    // 验证令牌文件存在
    if _, err := os.Stat("/var/run/secrets/kubernetes.io/serviceaccount/token"); os.IsNotExist(err) {
        t.Fatal("Service account token not found")
    }
    
    // 验证 CA 证书存在
    if _, err := os.Stat("/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"); os.IsNotExist(err) {
        t.Fatal("CA certificate not found")
    }
    
    // 验证命名空间文件存在
    namespace, err := ioutil.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/namespace")
    if err != nil {
        t.Fatalf("Failed to read namespace: %v", err)
    }
    
    t.Logf("Running in namespace: %s", string(namespace))
}

3. 使用 Skaffold 运行测试 Job

# skaffold.yaml
apiVersion: skaffold/v4beta1
kind: Config
profiles:
- name: test
  test:
  - image: go-test-image
    context: .
    custom:
    - command: kubectl apply -f test-job.yaml
    - command: kubectl wait --for=condition=complete job/go-integration-test --timeout=300s
    - command: kubectl logs job/go-integration-test --tail=-1
    - command: kubectl delete -f test-job.yaml

4. 使用 Makefile 简化流程

.PHONY: test-integration
test-integration:
    kubectl apply -f test-job.yaml
    kubectl wait --for=condition=complete job/go-integration-test --timeout=300s
    kubectl logs job/go-integration-test --tail=-1
    kubectl delete -f test-job.yaml

.PHONY: test-debug
test-debug:
    kubectl run go-test-debug \
        --image=golang:1.21-alpine \
        --serviceaccount=test-service-account \
        --restart=Never \
        --command -- sh -c "apk add git && git clone https://github.com/your/repo.git /app && cd /app && go test ./... -v"
    kubectl logs -f go-test-debug

5. 使用测试容器镜像

# Dockerfile.test
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN go test -c -o /test-runner ./cmd/test

FROM alpine:latest
RUN apk --no-cache add ca-certificates
COPY --from=builder /test-runner /test-runner
COPY --from=builder /var/run/secrets/kubernetes.io/serviceaccount /var/run/secrets/kubernetes.io/serviceaccount
ENTRYPOINT ["/test-runner"]

这种方案确保了测试在真实的 Kubernetes 环境中运行,能够正确访问投射的服务账户令牌卷,同时通过 Job 的完成状态明确测试结果。

回到顶部