K6s 脚本如何复用 Terraform 输出的网关地址并自动发现服务

解读

在国内云原生落地场景中,性能测试工程师往往要“左手 Terraform、右手 K6”,既要保证压测脚本随环境一键拉起,又要让脚本在网关 IP、服务端口变化时零修改。
面试官真正想验证的是:

  1. 你是否能把 IaC(Terraform)的输出无缝喂给 K6,避免人肉改脚本;
  2. 你是否理解 K6 的“初始化上下文”与“VU 代码”隔离模型,知道在什么时候、用什么方式把动态地址注入;
  3. 你是否具备“服务自动发现”思维,能在容器、K8s、微服务网关中用最低成本找到真实后端,而不是写死 IP。
    答不到“零侵入、零硬编码”,基本会被判为“只能写死脚本”的初级选手。

知识点

  1. Terraform output 机制:
    • 根模块通过 output 块暴露网关地址,如 output "gw_ip" { value = aws_lb.k8s_ingress.ip }
    • 本地落地常用 terraform output -json > tf-out.json 落盘,或 terraform output -raw gw_ip 直接打印
  2. K6 初始化上下文(init context)与 VU 代码隔离:
    • init 阶段可执行 Node.js 兼容语法,能读文件、发请求、做轻量计算
    • VU 阶段只能使用 K6 提供的 JS API,不能读文件,因此必须在 init 阶段把变量“固化”到全局
  3. K6 环境变量与 __ENV 对象:
    • 启动时 -e GW_IP=1.2.3.4 或 --env-file tf.env 均可注入
    • 脚本中用 let gw = __ENV.GW_IP || 'default' 读取
  4. 服务自动发现三板斧:
  5. 国内混合云细节:
    • 部分金融客户不允许 K6 直接访问 K8s API,需通过跳板“中转服务”把 endpoints 转成 HTTP 接口
    • 阿里 MSE 网关、腾讯 TSE 网关的 CLB 域名会带“-internal”后缀,K6 脚本需识别内网/外网场景,自动拼对 Host 头

答案

整体思路:让 Terraform 把网关地址“吐”出来,K6 在 init 阶段消费;同时用 DNS SRV 或 K8s API 在 init 阶段拉取后端实例,做到脚本零硬编码。
步骤如下:

  1. Terraform 端标准化输出
    在根模块增加 outputs.tf:
    output "gw_ip" {
    value = aws_lb.k8s_ingress.ip
    description = "K8s 入口网关 IP,供 K6 压测使用"
    }
    output "gw_port" {
    value = 80
    }
    执行完 terraform apply 后,本地脚本继续执行:
    terraform output -json > tf-out.json

  2. K6 端在 init 阶段读取 Terraform 输出
    新建 k6-script.js:
    const tf = JSON.parse(open('tf-out.json'));
    const gwIp = tf.gw_ip.value;
    const gwPort = tf.gw_port.value;
    const baseURL = http://${gwIp}:${gwPort};

  3. 自动发现后端服务(以 DNS SRV 为例,兼容 CoreDNS)
    import { resolveSrv } from 'k6/dns';
    const srvRecords = resolveSrv('_http._tcp.cart-svc.default.svc.cluster.local');
    const backends = srvRecords.map(r => ${r.target}:${r.port});

  4. VU 阶段随机挑一个后端发压
    export default function () {
    const backend = backends[Math.floor(Math.random() * backends.length)];
    const url = ${baseURL}/api/cart;
    http.get(url, { headers: { Host: 'cart.example.com' } });
    }

  5. 一键启动命令
    terraform apply -auto-approve && \
    terraform output -json > tf-out.json && \
    k6 run --vus 200 --duration 5m k6-script.js

至此,网关 IP、后端列表全部动态注入,脚本无需任何硬编码,满足国内持续集成流水线“一次构建、任意环境运行”的硬性要求。

拓展思考

  1. 如果客户不允许 K6 直接解析 DNS SRV,可以写一个轻量“发现服务”Sidecar,把 endpoints 转成 REST,K6 在 init 阶段用 http.get 拉取即可;该 Sidecar 随 Terraform 一起部署,保证生命周期一致。
  2. 多网关场景(公网+内网)下,可在 Terraform output 里增加“gw_type”,K6 脚本根据类型自动选择是否走公司代理、是否带 SSL Client Cert,实现“一套脚本压两遍”。
  3. 当后端实例频繁弹性伸缩时,DNS 缓存会导致 K6 拿到的列表滞后,可在 VU 阶段周期性(例如每 30s)调用 http.get 刷新 backends 数组,但注意 K6 的 VU 代码不能阻塞,需用 setTimeout 伪递归实现非阻塞刷新。
  4. 国内部分银行要求压测流量必须带“压测标”,可在 Terraform 输出里同时下发 header 标记值,K6 脚本统一注入,避免测试流量误打生产。