4、测试环境搭建:Docker、Kubernetes、证书模拟服务
做自动化回归测试,环境搭不好,后面全白忙。证书接口的测试尤其烦人——你得有一个能签发、验证、吊销证书的完整环境,又不能动生产CA。这套环境怎么搭?我踩过不少坑,今天把最稳的路线讲给你。
说白了,我们要三样东西:Docker 把服务包起来、Kubernetes 编排起来、证书模拟服务 把证书链跑起来。缺一个,测试就不完整。
为什么需要这个铁三角?
你想想看,证书接口回归测试,每次都要真实地发证、验证、撤销。如果依赖第三方测试CA,网络慢、限流、证书过期……我遇到过一回,测试因为CA服务器响应超时集体失败,排查半天才发现是外部服务挂了。
所以我的原则是:所有依赖都在本地集群里跑。Docker 保证环境一致,K8s 保证服务编排和自愈,自定义 CA 模拟服务保证证书生命周期完全可控。
核心目标:每次 CI 触发,自动拉起一个隔离的测试环境,跑完回归用例后自动销毁。整个过程不用碰任何外部网络资源。
整体架构
先看一张图,帮你建立全局认知。这张图解释了三个组件怎么协同:
第一步:Docker 容器化服务
所有组件都要 Docker 化。证书模拟服务、被测接口、测试脚本,各自一个镜像。
证书模拟服务的 Dockerfile
我习惯用 CFSSL 作为轻量级 CA。官方镜像太大?自己编一个 Alpine 版:
FROM alpine:3.19
RUN apk add --no-cache cfssl cfssljson
WORKDIR /ca
COPY ca-config.json ca-csr.json .
RUN cfssl gencert -initca ca-csr.json | cfssljson -bare ca
EXPOSE 8888
CMD ["cfssl", "serve", "-address=0.0.0.0", "-port=8888", "-ca=ca.pem", "-ca-key=ca-key.pem", "-config=ca-config.json"]
不要在生产开发共用镜像。回归测试用的 CA 证书有效期设成 10 年,避免频繁更新。我吃过亏,证书过期后管道整整红了一周。
多阶段构建被测服务
# 构建阶段
FROM golang:1.22 AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o cert-api .
# 运行阶段
FROM alpine:3.19
RUN apk add --no-cache ca-certificates
COPY --from=builder /app/cert-api /usr/local/bin/
COPY --from=builder /etc/ssl/certs/ /etc/ssl/certs/
EXPOSE 8443
CMD ["cert-api"]
注意这里拷贝了系统根证书,因为被测服务可能需要验证客户端证书。我在项目里还加了一层 HEALTHCHECK,让 K8s 能感知服务状态。
第二步:Kubernetes 编排
Docker 镜像造好了,接下来怎么在 CI 里跑?用 Minikube 或者 Kind 最省事。我推荐 Kind,因为它能把 集群跑在 Docker 容器里,CI 节点不用额外安装 K8s。
完整的部署清单
# 1. 命名空间隔离
apiVersion: v1
kind: Namespace
metadata:
name: cert-test-$CI_PIPELINE_ID
---
# 2. 证书模拟服务
apiVersion: apps/v1
kind: Deployment
metadata:
name: mock-ca
namespace: cert-test-$CI_PIPELINE_ID
spec:
replicas: 1
selector:
matchLabels:
app: mock-ca
template:
metadata:
labels:
app: mock-ca
spec:
containers:
- name: ca
image: ${CI_REGISTRY}/mock-ca:${CI_COMMIT_SHA}
ports:
- containerPort: 8888
---
apiVersion: v1
kind: Service
metadata:
name: mock-ca-svc
namespace: cert-test-$CI_PIPELINE_ID
spec:
selector:
app: mock-ca
ports:
- port: 8888
---
# 3. 被测服务
apiVersion: apps/v1
kind: Deployment
metadata:
name: cert-api
namespace: cert-test-$CI_PIPELINE_ID
spec:
replicas: 1
selector:
matchLabels:
app: cert-api
template:
metadata:
labels:
app: cert-api
spec:
containers:
- name: api
image: ${CI_REGISTRY}/cert-api:${CI_COMMIT_SHA}
env:
- name: CA_ENDPOINT
value: http://mock-ca-svc:8888
ports:
- containerPort: 8443
关键点:命名空间带上流水线 ID,保证并发跑不同分支时互不干扰。我在项目中曾因为没隔离,两个分支同时测同一个 CA 服务,结果证书状态全乱套了。
Init Container 等待依赖
证书模拟服务启动需要几秒。如果被测服务先启动连不上 CA,测试全失败。加一个 Init Container 做健康检查。
initContainers:
- name: wait-for-ca
image: busybox:1.36
command:
- sh
- -c
- "until nc -z mock-ca-svc 8888; do echo waiting for CA; sleep 1; done"
第三步:证书模拟服务细节
这是整个环境的核心。我们需要一个能模拟 CA 签发的服务,同时支持 OCSP 和 CRL 查询。
使用 cfssl 搭建
cfssl 自带 REST API,正好符合接口测试场景。下面是 ca-config.json 模板:
{
"signing": {
"default": {
"expiry": "87600h",
"usages": ["signing", "key encipherment", "server auth", "client auth"]
},
"profiles": {
"test-server": {
"expiry": "72h",
"usages": ["signing", "key encipherment", "server auth"]
},
"test-client": {
"expiry": "24h",
"usages": ["signing", "key encipherment", "client auth"]
},
"revocable": {
"expiry": "1h",
"usages": ["signing", "key encipherment", "client auth"]
}
}
}
}
为什么加不同 Profile?回归测试需要覆盖:服务器证书、客户端证书、即将过期的证书、已被吊销的证书。每个 Profile 对应一种测试场景。
注入测试数据
在容器启动脚本里,用 cfssljson 预生成一批测试证书:
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -profile=test-server ca-csr.json | cfssljson -bare server1
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -profile=revocable revoke-csr.json | cfssljson -bare to-revoke
# 然后通过 API 吊销 to-revoke
cfssl revoke --ca=ca.pem --ca-key=ca-key.pem --reason="keyCompromise" --serial=$(openssl x509 -in to-revoke.pem -noout -serial | cut -d= -f2)
这套数据会随镜像一起打包。CI 每次拉起环境,证书池都是干净的已知状态,回归用例可以精确断言。
自动化清理
测试跑完后必须清理。用 kubectl delete namespace cert-test-$CI_PIPELINE_ID 在 CI 的 after_script 里执行。如果忘了清理,集群会被占满。我自己一次没配清理钩子,CI 跑了 200 多次后 Minikube 直接挂掉。
| 组件 | 镜像大小 | 启动时间 | 关键配置 |
|---|---|---|---|
| 证书模拟服务 | ~30 MB | <3秒 | ca-config.json 预定义 Profile |
| 被测证书接口 | ~15 MB | <1秒 | CA_ENDPOINT 环境变量 |
| 回归测试容器 | ~50 MB | <1秒 | 挂载测试证书文件 |
嗯,到这里环境搭建的核心内容就讲完了。这些实践我反复打磨过,直接拿去做你的 CI 管道底座,证书接口回归测试就不会再因为环境问题翻车了。