Jason Pan

如何利用好团队的测试资源

潘忠显 / 2025-06-13


最近恰好整理组内一些功能自测和压测需求。周围同学有两个痛点:

针对以上痛点,可以使用一种方案进行解决:使用 DevCloud 提供的测试容器集群

测试容器集群是什么

DevCloud团队提供专为测试环境使用 DevCloud 和 IDC 区域测试容器集群

dev-idc-network

如果你拿IDC测试专区的容器部署长期资源,来当虚拟机使用倒是也可以,但是不能利用到的容器集群的优势,不如直接使用云服务器。

测试容器集群,有明显的优势:

一个典型的CI/CD过程

其实分析我们的测试过程,也是一个典型的CI/CD的过程:

我们这里的测试跟 CI/CD 中的 Testing 有区别,主要是视角不同:

cicd

具体实现

我这里通过构建基础镜像、编辑流水线触发、封装K8s操作脚本等处理,在测试代码仓库中,通过脚本可以将仓库代码一键式推送到 IDC 测试集群运行:

> git checkout -b dev1
> echo 'print("this is branch dev1")' > test_dev1.py
> git add test_dev1.py && git commit -m "Add test dev1" && git push origin dev1 
> # 等待2分钟,用于构建镜像
> cd k8s
> ./deploy.sh  --branch dev1 --credid xxx -c "python3 test_dev1.py"

动图演示输出:

showcase

框架

一个典型的CI/CD过程:

framework

目录组织

为了方便,我把部署脚本相关内容放在了测试脚本的仓库中。

.
├── ci/ci.yml       # 流水线配置文件
├── Dockerfile.base # 基础镜像的Dockerfile
├── Dockerfile      # 用于CICD构建镜像的Dockerfile
├── rec_test/       # 内部库
├── src/            # 实际的测试脚本放的位置
└── k8s             # 使用测试集群相关文件
    ├── ae-auto-test.enc    # 加密后的K8s集群凭证
    ├── .s3cfg.enc          # 加密后的S3配置
    ├── deploy-base.yaml    # 基础的K8s部署配置文件
    └── deploy.sh           # 启动脚本

基础镜像

压测工具是我自己写的一个类似于 wrk 的工具 - 「固定频率压测工具

功能测试依赖 Python3.13 和自己写的库 - 「Session Tester - 面向会话的测试框架

基于以上两个原因,构建一个基础镜像,安装好 wrkx、Python3.13、session-tester 库是必要的步骤。

为了跟流水线使用的Dockerfile 区分,这里命名为 Dockerfile.base

流水线构建镜像

功能测试的脚本,会统一放在同一个仓库中。工蜂自带 pipeline 的功能,在我们推送代码之后,可以触发流水线。

给流水线配置一些功能:

(在 IDC 上直接拉 git 代码是不规范的,网络也是不通的,所以不应该在运行容器时拉取代码,而应该在运行容器前获得)

比如有同学修改了某个分支 dev1,他 push 完代码之后,会自动产生一个这样的镜像:

mirrors.tencent.com/jasonzxpan/rec-func-test:dev1

部署测试服务Job

镜像已经产生好了,如何让大家更便捷的运行部署,大家直接自己修改K8s部署配置+ 调用 kubectl不太现实。

于是提供了一个基础配置+ bash 脚本,开发同学可以调用这个脚本,选择他要使用的分支、自动更新基础配置(包括镜像tag)、执行K8s的指令运行 Job。

export REC_TEST_KEY=xxxxxxxxxxxxxxxxxxxxxx
./deploy.sh --branch master --credid xxx -c "python3 -c 'print(1)'"

deploy.sh 脚本主要功能:

这里特地强调了一下,K8s集群凭证的加解密。K8s凭证是在DevCloud控制台下载的,依次作为操作 K8s 特定命名空间的凭证。

因为直接将明文密钥放在的仓库,或者镜像中都是有风险的,所以这里放在仓库中的是加密后的凭证,需要解密。

加解密使用上边那个 REC_TEST_KEY 环境变量:

# 加密
openssl enc -aes-256-cbc -d -salt -pbkdf2 -in ae-auto-test.enc -out ae-auto-test.conf -pass env:REC_TEST_KEY

# 解密
openssl enc -aes-256-cbc -d -salt -pbkdf2 -in ae-auto-test.enc -out ae-auto-test.conf -pass env:REC_TEST_KEY

内部使用凭证

上边说的 K8s 集群凭证是在集群外/容器外使用的,其实容器中还需要用到一些凭证,比如 S3 配置文件 .s3cfg,又或者Redis 的用户名和密码等。

这里可以使用 K8s Secret 但是这里为了简单,也是直接将加密后的凭证,比如 .s3cfg.enc 直接复制到镜像中,将密钥通过 env 的方式传递到容器中,启动容器之前进行解密

具体配置如下:

spec:
  parallelism: PARALLELISM_PLACEHOLDER    # 并行运行的Pod数量
  completions: PARALLELISM_PLACEHOLDER    # 需要成功完成的Pod数量
  backoffLimit: 0   # 失败后不重试
  activeDeadlineSeconds: 3600  # 1小时后超时
  template:
    spec:
      containers:
      - name: rec-func-test
        image: mirrors.tencent.com/jasonzxpan/rec-func-test:BRANCH_NAME_PLACEHOLDER
        imagePullPolicy: Always
        workingDir: /data/code
        env:
        - name: REC_TEST_KEY
          valueFrom:
            secretKeyRef:
              name: rec-test-secrets
              key: rec-test-key
        command: ["bash", "-c"]
        args: ["openssl enc -aes-256-cbc -d -salt -pbkdf2 -in k8s/.s3cfg.enc -out ~/.s3cfg -pass env:REC_TEST_KEY && COMMAND_PLACEHOLDER"]

可以看到,上边有多个 PlaceHolder:分支、启动指令,这个是需要调用脚本会替换该占位的。

动态指定配置

CPU和MEMORY的占位,可以支持命令行动态指定配置:

./deploy.sh  --branch master --credid xxx --resources 16C16G1 -c "sleep 1"

配置中内容:

        resources:
          requests:
            cpu: "CPU_REQUEST_PLACEHOLDER"
            memory: "CPU_REQUEST_PLACEHOLDERGi"
          limits:
            cpu: "CPU_REQUEST_PLACEHOLDER"
            memory: "CPU_REQUEST_PLACEHOLDERGi"

依赖安装

前面介绍过,有两个Dockerfile,其中一个是基础镜像Dockerfile.base,平时不需要调整。

另外一个Dockerfile(文件同名),可以在功能测试的时候使用Python,将依赖的库和项目中自己创建的模块放在这里安装:

RUN pip3 install session-tester==0.1.0.dev19 qq-doc==0.1.0.dev2 redis elasticsearch boto3

COPY rec_test /usr/local/lib/python3.13/dist-packages/rec_test

COPY . /data/code

实际运行展示

功能测试

这里只展示了读取的 IDC 环境 S3的内容:

showcase-s3

压力测试

这里展示了以QPS 60000 压测服务,指定使用16核心CPU和16GB内存,持续30秒:

showcase-wrkx

补充一些知识点

TODO

JobDeployment 以及其他工作负载类型

ConfigmapSecret 两种配置

PVCStorageClass 两种存储的区别

资源的 requestslimits 的区别