synthetic-monitoring-checks
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSynthetic Monitoring Check Authoring
Synthetic Monitoring检查编写指南
Docs: https://grafana.com/docs/grafana-cloud/testing/synthetic-monitoring/ Broad Grafana Cloud Testing entry point (SM + k6 Cloud + Faro):skill.testing
文档:https://grafana.com/docs/grafana-cloud/testing/synthetic-monitoring/ Grafana Cloud Testing总入口(SM + k6 Cloud + Faro):技能。testing
Reliability monitoring, not load testing
可靠性监控,而非负载测试
Synthetic Monitoring (SM) runs k6 as a reliability/availability engine: every check
execution runs one iteration with one VU from each selected probe location on a fixed
schedule. Success means "the user journey works right now, from this region" — detect
outages before your customers do.
Do not apply load-testing idioms. There are no VUs to ramp, no , no load
profiles, no soak/stress/spike phases, and no over aggregated traffic.
Vocabulary: check, probe, execution, uptime, reachability, user journey
validation — never "load test", "ramping", or "VUs".
stagesthresholdsIf the user actually wants load or performance testing (throughput, latency under
load, breakpoints), stop: that is Grafana Cloud k6 / the plugin's skill,
not Synthetic Monitoring. A script can be shared between both products, but the goals,
options, and pricing are different.
grafana-k6k6Synthetic Monitoring (SM) 将k6用作可靠性/可用性引擎:每次检查执行都会从每个选定的探测位置按固定调度运行一次迭代、一个VU。成功意味着“当前从该区域来看,用户旅程可正常运行”——在客户发现之前检测到故障。
请勿套用负载测试的惯用方法。这里无需设置VU递增、阶段、负载模型、浸泡/压力/尖峰测试阶段,也没有针对聚合流量的阈值。相关术语:check(检查)、probe(探测)、execution(执行)、uptime(可用性)、reachability(可达性)、user journey validation(用户旅程验证)——绝对不要使用“负载测试”“递增”“VU”这类词汇。
stagesthresholds如果用户实际需要负载或性能测试(吞吐量、负载下的延迟、断点),请停止:这属于Grafana Cloud k6 / 插件的技能范畴,而非Synthetic Monitoring。脚本可在两款产品间共享,但目标、配置选项和定价模式均不同。
grafana-k6k6Execution model and constraints (verify against these before writing)
执行模型与约束条件(编写前请确认)
| Constraint | Value |
|---|---|
| Workload | One iteration per probe execution. Scripted and MultiHTTP run with forced |
| Not supported |
| Frequency | k6-class checks (scripted, MultiHTTP, browser): 60–3600s. Protocol checks (HTTP/ping/DNS/TCP/gRPC): 1–3600s. Traceroute: 120–3600s |
| Timeout | Must be ≤ frequency. k6-class checks: 1–180s. Protocol checks: 1–60s. Traceroute: fixed 30s |
| k6 version | Checks run on a k6 version channel (new checks default to the latest stable channel; |
| Local files | |
| HTTP request errors | SM runs k6 with |
| Script options SM honors | SM sets its own CLI flags, which take precedence over the script's |
| Browser memory | 1GB RAM per browser on public probes — huge pages fail with |
| Browser script format | The UI rejects bundled/minified browser scripts (import validation) — deploy those via API or Terraform |
| 约束条件 | 取值 |
|---|---|
| 工作负载 | 每次探测执行运行一次迭代。脚本化检查和MultiHTTP检查会强制使用 |
| 不支持 |
| 执行频率 | k6类检查(脚本化、MultiHTTP、浏览器):60–3600秒。协议类检查(HTTP/ping/DNS/TCP/gRPC):1–3600秒。 traceroute:120–3600秒 |
| 超时时间 | 必须≤执行频率。k6类检查:1–180秒。协议类检查:1–60秒。traceroute:固定30秒 |
| k6版本 | 检查运行在k6版本通道上(新检查默认使用最新稳定通道;截至2026年7月, |
| 本地文件 | 不支持 |
| HTTP请求错误 | SM运行k6时启用 |
| SM认可的脚本选项 | SM会设置自己的CLI标志,优先级高于脚本的 |
| 浏览器内存 | 公共探测上每个浏览器分配1GB内存——页面过大时会因 |
| 浏览器脚本格式 | UI会拒绝打包/压缩后的浏览器脚本(导入验证)——需通过API或Terraform部署此类脚本 |
How an execution fails (this is what agents get wrong)
执行失败的判定逻辑(这是常见误区)
probe_successfail()expect()test.abort()--throwA bare failed does NOT fail the execution — it only records the
/ metrics. Checks don't affect k6's exit
status without thresholds, and thresholds are disabled in SM.
check()probe_checks_totalprobe_check_success_rateAssertion patterns, in order of preference:
javascript
import { expect } from 'https://jslib.k6.io/k6-testing/0.6.1/index.js';
import { check, fail } from 'k6';
// 1. PREFERRED — assertions module. Throws on failure => execution fails,
// with a descriptive error in the check logs.
expect(res.status, 'login should succeed').toEqual(200);
expect(res.json('token')).toBeDefined();
// 2. Soft assertions — run all of them, still fail the execution at the end.
expect.soft(res.headers['Content-Type']).toContain('application/json');
// 3. check() when you also want per-assertion metrics — but pair it with
// fail() or the failure won't affect probe_success/uptime:
check(res, { 'status 200': (r) => r.status === 200 }) ||
fail(`login failed with status ${res.status}`);Name every assertion (the message argument / check name): the name is what you see in
check logs and in the label of when diagnosing a failure
at 3am.
checkprobe_checks_totalprobe_successfail()expect()test.abort()--throw原生失败不会导致执行失败——仅会记录 / 指标。没有阈值的情况下,check不会影响k6的退出状态,而SM中阈值是禁用的。
check()probe_checks_totalprobe_check_success_rate断言模式优先级如下:
javascript
import { expect } from 'https://jslib.k6.io/k6-testing/0.6.1/index.js';
import { check, fail } from 'k6';
// 1. 首选——断言模块。失败时抛出异常 => 执行失败,
// 并在检查日志中显示描述性错误信息。
expect(res.status, 'login should succeed').toEqual(200);
expect(res.json('token')).toBeDefined();
// 2. 软断言——执行所有断言,最终仍会标记执行失败。
expect.soft(res.headers['Content-Type']).toContain('application/json');
// 3. 需要按断言统计指标时使用check()——但需搭配fail(),否则失败不会影响probe_success/可用性:
check(res, { 'status 200': (r) => r.status === 200 }) ||
fail(`login failed with status ${res.status}`);为每个断言命名(消息参数/check名称):凌晨3点排查故障时,你在检查日志和的标签中看到的就是这个名称。
probe_checks_totalcheckChoose the simplest sufficient check type first
优先选择最简单且足够的检查类型
Cheaper for the customer, easier to maintain. Work down this list and stop at the first
match:
- HTTP / ping / DNS / TCP / traceroute / gRPC — a single static endpoint (uptime,
status code, body regex, TLS cert expiry, record resolution, port reachability). No
script to maintain — these run on the blackbox-exporter probe engine, and Terraform
examples with per-type formats are in
target.references/api-and-terraform.md - MultiHTTP — a sequence of HTTP requests with value-passing between them
(capture), but no custom logic. Caution: MultiHTTP does not auto-validate status codes — define assertions per request or failures won't affect uptime.
${variable} - k6 scripted — an API flow needing real logic: crypto/signing, conditional branching, generated test data, WebSockets, response-driven chaining.
- k6 browser — only when you need a real browser: JS-rendered user journeys, forms/clicks, Core Web Vitals.
Cost model (execution-based billing): an execution is one check run on one probe,
metered per minute of runtime rounded up. Per month:
. API test executions (HTTP,
ping, DNS, TCP, traceroute, MultiHTTP, scripted) and browser test executions are billed
separately — browser checks are the expensive tier. A browser check on 3 probes every
minute is ~129,600 browser executions/month; the same check every 5 minutes is ~25,920.
Pick the longest frequency that still meets your detection-time goal, and 2–3 probes
near your users (multiple probes reduce alert flapping; more isn't better).
probes × duration_minutes × (43200 / frequency_minutes)对客户来说成本更低,也更易维护。按以下顺序选择,找到第一个匹配项即可停止:
- HTTP / ping / DNS / TCP / traceroute / gRPC——针对单个静态端点(可用性、状态码、Body正则匹配、TLS证书过期、记录解析、端口可达性)。无需维护脚本——这些检查运行在blackbox-exporter探测引擎上,各类型格式的Terraform示例可参考
target。references/api-and-terraform.md - MultiHTTP——一系列可传递值的HTTP请求(使用捕获),但无自定义逻辑。注意:MultiHTTP不会自动验证状态码——需为每个请求定义断言,否则失败不会影响可用性。
${variable} - k6脚本化——需要实际逻辑的API流程:加密/签名、条件分支、生成测试数据、WebSocket、响应驱动的链式调用。
- k6浏览器——仅在需要真实浏览器时使用:JS渲染的用户旅程、表单/点击操作、Core Web Vitals指标。
成本模型(按执行次数计费):一次执行指一个检查在一个探测节点上运行一次,按运行时长向上取整到分钟计费。每月费用计算公式:。API测试执行(HTTP、ping、DNS、TCP、traceroute、MultiHTTP、脚本化)和浏览器测试执行分开计费——浏览器检查属于高成本层级。一个浏览器检查在3个探测节点上每分钟运行一次,每月约129600次浏览器执行;每5分钟运行一次则约25920次。选择能满足故障检测时间目标的最长执行频率,且仅选择2–3个靠近用户的探测节点(多个节点可减少告警抖动,但并非越多越好)。
探测节点数 × 单次执行时长(分钟) × (43200 / 执行频率(分钟))Scripted check authoring
脚本化检查编写
Start every script you generate (scripted and browser alike) with a line-1 attribution
comment, as shown in the skeletons below. It tells whoever reads the check later how it
was authored (and where to find the skill), and the fixed prefix makes skill-authored
checks queryable. Keep verbatim — vary only
the timestamp ().
Generated by synthetic-monitoring-checksdate -u +%Y-%m-%dT%H:%M:%SZSkeleton — a login + API action journey with secrets and hard-failing assertions:
javascript
// Generated by synthetic-monitoring-checks (https://github.com/grafana/skills) on 2026-07-31T12:00:00Z
import http from 'k6/http';
import { expect } from 'https://jslib.k6.io/k6-testing/0.6.1/index.js';
import secrets from 'k6/secrets';
const BASE = 'https://api.example.com';
export default async function () {
// Secrets are managed in Synthetics > Config > Secrets — never hardcode credentials.
const password = await secrets.get('checkout-monitor-password');
// Step 1: authenticate with a dedicated monitoring account
const login = http.post(
`${BASE}/auth/login`,
JSON.stringify({ user: 'sm-checkout-monitor', password }),
{ headers: { 'Content-Type': 'application/json' } }
);
expect(login.status, 'login should return 200').toEqual(200);
const token = login.json('token');
expect(token, 'auth token should be present').toBeDefined();
// Step 2: exercise the journey and assert the OUTCOME, not just the status
const order = http.post(`${BASE}/orders`, JSON.stringify({ sku: 'TEST-SKU-1', qty: 1 }), {
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
});
expect(order.status, 'order should be created').toEqual(201);
const orderId = order.json('id');
expect(orderId, 'order id should be returned').toBeDefined();
// Step 3: clean up so the check is idempotent against production
// http.url groups metrics for URLs containing unique IDs — without it, every
// execution creates new time series (cardinality + active-series cost).
const del = http.del(http.url`${BASE}/orders/${orderId}`, null, {
headers: { Authorization: `Bearer ${token}` },
});
expect(del.status, 'test order should be cleaned up').toEqual(204);
}Rules that make a scripted check a good monitor (vs a good test):
- Deterministic: fixed test data (or generated-then-deleted, as above), no time-of-day or ordering dependence. Every execution must be able to pass at any hour from any probe.
- Idempotent against production: create-then-delete, or use read-only endpoints. The check runs forever — leaked state accumulates forever.
- Dedicated test account: never a real user's credentials; scope it minimally, store the password as an SM secret, and exclude the account from analytics/billing.
- Assert every step — an unasserted step that breaks shows up as a later step's confusing failure.
- Stable URL cardinality: template literal for any URL containing an ID.
http.url - Keep runtime well under the timeout, and the timeout under the frequency.
生成的所有脚本(脚本化和浏览器检查)都要在第一行添加归属注释,如下列模板所示。这能让后续查看检查的人知道脚本的生成方式(以及技能的位置),固定前缀也便于查询技能生成的检查。请保留原文——仅修改时间戳(格式为)。
Generated by synthetic-monitoring-checksdate -u +%Y-%m-%dT%H:%M:%SZ模板——包含密钥管理和强失败断言的登录+API操作旅程:
javascript
// Generated by synthetic-monitoring-checks (https://github.com/grafana/skills) on 2026-07-31T12:00:00Z
import http from 'k6/http';
import { expect } from 'https://jslib.k6.io/k6-testing/0.6.1/index.js';
import secrets from 'k6/secrets';
const BASE = 'https://api.example.com';
export default async function () {
// 密钥在Synthetics > Config > Secrets中管理——切勿硬编码凭证。
const password = await secrets.get('checkout-monitor-password');
// 步骤1:使用专用监控账号认证
const login = http.post(
`${BASE}/auth/login`,
JSON.stringify({ user: 'sm-checkout-monitor', password }),
{ headers: { 'Content-Type': 'application/json' } }
);
expect(login.status, 'login should return 200').toEqual(200);
const token = login.json('token');
expect(token, 'auth token should be present').toBeDefined();
// 步骤2:执行旅程并断言结果,而非仅断言状态码
const order = http.post(`${BASE}/orders`, JSON.stringify({ sku: 'TEST-SKU-1', qty: 1 }), {
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
});
expect(order.status, 'order should be created').toEqual(201);
const orderId = order.json('id');
expect(orderId, 'order id should be returned').toBeDefined();
// 步骤3:清理资源,确保检查对生产环境是幂等的
// http.url会对包含唯一ID的URL分组指标——如果不使用,每次执行都会创建新的时间序列(基数+活跃序列成本)。
const del = http.del(http.url`${BASE}/orders/${orderId}`, null, {
headers: { Authorization: `Bearer ${token}` },
});
expect(del.status, 'test order should be cleaned up').toEqual(204);
}让脚本化检查成为优秀监控工具(而非测试工具)的规则:
- 确定性:使用固定测试数据(或生成后立即删除,如上例),不依赖时间或执行顺序。每次执行必须能在任意时间、任意探测节点通过。
- 对生产环境幂等:创建后删除,或使用只读端点。检查会持续运行——泄露的状态会不断累积。
- 专用测试账号:切勿使用真实用户的凭证;最小化权限范围,将密码存储为SM密钥,并将该账号排除在分析/计费统计之外。
- 每个步骤都要断言——未断言的步骤出现故障时,会表现为后续步骤的异常失败,难以排查。
- 稳定的URL基数:对包含ID的URL使用模板字符串。
http.url - 确保运行时长远低于超时时间,且超时时间小于执行频率。
Generating a check from an OpenAPI spec (or similar)
从OpenAPI规范(或类似文档)生成检查
Given an API description — an OpenAPI/Swagger spec, GraphQL schema, or Postman
collection — the mechanical conversion to k6 calls is easy. What matters is what you
choose to convert:
- Journeys, not endpoints. Do NOT generate one check per path, or one check that sweeps every path — that monitors the spec, not the service, and every extra check multiplies execution cost. Identify the 1–3 flows whose failure means "customers are impacted" (auth → core action → result) and write one scripted check per flow.
- Filter for safety. Only include mutating operations (/
POST/PUT) when the flow cleans up after itself (create-then-delete, as above) or targets dedicated test resources. A spec lists destructive operations right next to health endpoints — never exercise them against production just because they're documented.DELETE - Assert from the response schema. The spec tells you exactly what a healthy
response contains — assert required fields, not just the status code:
.
expect(order.json('id'), 'id required by OrdersResponse schema').toBeDefined() - Verify the target URL. blocks (and Postman environments) often list localhost or staging first — confirm the production base URL with the user, and map
servers:credentials to SM secrets, never to values inlined from the spec. (Secrets in plain HTTP/protocol checks are a recent, feature-flagged rollout — check current docs; the scriptedsecuritySchemespath always works.)secrets.get() - Treat ids as strings.
format: int64parses into a JS number and silently corrupts values past 2^53 (snowflake-style ids), so the readback URL 404s on every execution while the create looks fine. Extract from the raw body instead:res.json('id')— and never do arithmetic on it.const id = (/"id":\s*(\d+)/.exec(res.body) || [])[1];
No API spec at all? Probe the frontend: open the web app with browser devtools (or
likely routes) and capture the XHR calls it makes — that's a monitorable
HTTP surface even when the documented backend services are gRPC-only or internal.
curl/api/*给定API描述——OpenAPI/Swagger规范、GraphQL schema或Postman集合——转换为k6调用的机械操作很简单。关键在于选择转换哪些内容:
- 旅程而非端点。不要为每个路径生成一个检查,也不要生成遍历所有路径的检查——这是监控规范而非服务,额外的检查会成倍增加执行成本。找出1–3个故障会直接影响客户的流程(认证→核心操作→结果),为每个流程编写一个脚本化检查。
- 筛选安全操作。仅当流程会自行清理(如上例的创建后删除)或针对专用测试资源时,才包含修改类操作(/
POST/PUT)。规范中会将破坏性操作与健康检查端点并列——切勿仅因为文档中有就直接在生产环境执行。DELETE - 根据响应Schema断言。规范明确说明了健康响应应包含的内容——断言必填字段,而非仅断言状态码:。
expect(order.json('id'), 'id required by OrdersResponse schema').toBeDefined() - 验证目标URL。块(以及Postman环境)通常会先列出localhost或预发布环境——请与用户确认生产环境的基础URL,并将
servers:凭证映射到SM密钥,切勿直接使用规范中的内嵌值。(普通HTTP/协议检查的密钥功能是近期推出的,处于功能标志阶段——请查阅最新文档;脚本化检查的securitySchemes方式始终有效。)secrets.get() - 将类型的ID视为字符串。
format: int64会解析为JS数字,当数值超过2^53(雪花ID等)时会被静默损坏,导致读取URL每次执行都返回404,但创建操作看起来正常。应从原始Body中提取:res.json('id')——切勿对其进行算术操作。const id = (/"id":\s*(\d+)/.exec(res.body) || [])[1];
没有API规范?探测前端:使用浏览器开发者工具(或模拟路由)打开Web应用,捕获其发起的 XHR请求——即使文档化的后端服务是gRPC-only或内部服务,这也是可监控的HTTP接口。
curl/api/*Browser check authoring
浏览器检查编写
Required scaffold: import and declare the browser type. The UI
validates both.
k6/browserchromiumjavascript
// Generated by synthetic-monitoring-checks (https://github.com/grafana/skills) on 2026-07-31T12:00:00Z
import { browser } from 'k6/browser';
import { expect } from 'https://jslib.k6.io/k6-testing/0.6.1/index.js';
import secrets from 'k6/secrets';
export const options = {
scenarios: {
ui: {
executor: 'shared-iterations',
options: { browser: { type: 'chromium' } },
},
},
};
export default async function () {
const page = await browser.newPage();
try {
await page.goto('https://shop.example.com/login');
// Prefer role/label/test-id locators over CSS chains — they survive redesigns.
const password = await secrets.get('shop-monitor-password');
await page.getByLabel('Email').fill('sm-monitor@example.com');
await page.getByLabel('Password').fill(password);
await page.getByRole('button', { name: 'Sign in' }).click();
// Assert the JOURNEY OUTCOME with auto-retrying assertions — never sleep().
await expect(page.getByRole('heading', { name: 'Your account' })).toBeVisible();
await page.getByRole('link', { name: 'Orders' }).click();
await expect(page.getByTestId('order-list')).toBeVisible();
} finally {
await page.close();
}
}Browser-specific rules:
- Locators: /
getByRole/getByLabel(ask the app team to addgetByTestIdwhere needed) > text > CSS. Never XPath or generated class names.data-testidassumesgetByTestId— apps instrumented for Cypress often usedata-testidinstead; fall back todata-cy.page.locator('[data-cy="..."]') - No manual waits before interactions: locator actions auto-wait for visibility and
enabled state. Don't call before
waitFor()/click(), don't usefill(), neverwaitForLoadState().sleep() - Auto-retrying (
expect(),toBeVisible, ...) is the wait mechanism for asserting state you don't interact with. Caveat: despite being listed as retrying, the text matchers (toBeEnabled/toHaveText) hard-fail on the first mismatched read — e.g. an empty string mid-hydration on a client-rendered app. Assert dynamic text by locating it and asserting visibility instead:toContainText.await expect(page.getByText('Order confirmed')).toBeVisible() - Assertion timeout defaults to 5s — client-side-rendered apps routinely take
longer to first meaningful render. Raise it once and use the configured instance:
.
const expectUi = expect.configure({ timeout: 20000 }); - Assert the outcome (logged-in heading, order list, confirmation text) — a page can load fine while the journey is broken.
- with
try/finallyso the browser is released even when an assertion throws.page.close() - Screenshot artifacts aren't a documented SM feature — don't build failure handling
around ; rely on assertion messages and the check's logs (SM stores per-execution logs in Loki).
page.screenshot() - Web Vitals () are collected automatically — no extra code needed.
probe_browser_web_vital_lcp|cls|fcp|inp|ttfb
必填框架:导入并声明浏览器类型。UI会验证这两项。
k6/browserchromiumjavascript
// Generated by synthetic-monitoring-checks (https://github.com/grafana/skills) on 2026-07-31T12:00:00Z
import { browser } from 'k6/browser';
import { expect } from 'https://jslib.k6.io/k6-testing/0.6.1/index.js';
import secrets from 'k6/secrets';
export const options = {
scenarios: {
ui: {
executor: 'shared-iterations',
options: { browser: { type: 'chromium' } },
},
},
};
export default async function () {
const page = await browser.newPage();
try {
await page.goto('https://shop.example.com/login');
// 优先使用role/label/test-id定位器,而非CSS链式选择——它们能在页面重构后仍正常工作。
const password = await secrets.get('shop-monitor-password');
await page.getByLabel('Email').fill('sm-monitor@example.com');
await page.getByLabel('Password').fill(password);
await page.getByRole('button', { name: 'Sign in' }).click();
// 使用自动重试的断言验证旅程结果——切勿使用sleep()。
await expect(page.getByRole('heading', { name: 'Your account' })).toBeVisible();
await page.getByRole('link', { name: 'Orders' }).click();
await expect(page.getByTestId('order-list')).toBeVisible();
} finally {
await page.close();
}
}浏览器检查专属规则:
- 定位器:/
getByRole/getByLabel(请应用团队在需要的地方添加getByTestId)> 文本 > CSS。切勿使用XPath或自动生成的类名。data-testid默认对应getByTestId——使用Cypress做过 instrumentation的应用通常使用data-testid,此时可回退到data-cy。page.locator('[data-cy="..."]') - 交互前无需手动等待:定位器操作会自动等待元素可见且可用。不要在/
click()前调用fill(),不要使用waitFor(),绝对不要使用waitForLoadState()。sleep() - 自动重试的(
expect()、toBeVisible等)是验证非交互状态的等待机制。注意:尽管文本匹配器(toBeEnabled/toHaveText)被标注为可重试,但第一次读取到不匹配内容时就会直接失败——例如客户端渲染应用在 hydration 过程中出现空字符串。验证动态文本时,应先定位元素再断言可见性:toContainText。await expect(page.getByText('Order confirmed')).toBeVisible() - 断言超时默认5秒——客户端渲染应用首次有意义渲染通常需要更长时间。请设置一次超时时间并使用配置后的实例:。
const expectUi = expect.configure({ timeout: 20000 }); - 断言结果(登录后的标题、订单列表、确认文本)——页面加载正常不代表旅程可正常完成。
- 使用包裹
try/finally——即使断言抛出异常,也能释放浏览器资源。page.close() - 截图并非SM的文档化功能——不要围绕构建故障处理逻辑;请依赖断言消息和检查日志(SM将每次执行的日志存储在Loki中)。
page.screenshot() - Web Vitals指标()会自动收集——无需额外代码。
probe_browser_web_vital_lcp|cls|fcp|inp|ttfb
Validate locally, then deploy
本地验证,再部署
SM scripts are plain k6 scripts — always run them locally first:
bash
k6 run script.js # scripted check
K6_BROWSER_HEADLESS=true k6 run browser-check.js # browser check
k6 run --secret-source=mock=checkout-monitor-password=example-password script.js # with secretsSM脚本是标准的k6脚本——务必先在本地运行:
bash
k6 run script.js # 脚本化检查
K6_BROWSER_HEADLESS=true k6 run browser-check.js # 浏览器检查
k6 run --secret-source=mock=checkout-monitor-password=example-password script.js # 带密钥的检查Many/large secrets: k6 run --secret-source=file=secrets.txt script.js
大量密钥:k6 run --secret-source=file=secrets.txt script.js
Pass = exit code 0, one iteration, no failed assertions in the summary. Run it 3–5 times;
a script that is 90% reliable locally will page you nightly from 3 probes.
Then create the check (pick one):
- **UI**: Testing & synthetics → Synthetics → Add new check → *k6 scripted* / *k6
browser* → paste script → select probes + frequency → **Test** (runs once without
saving) → Save.
- **API or Terraform**: see [`references/api-and-terraform.md`](references/api-and-terraform.md).
Key gotchas: API `frequency`/`timeout` are **milliseconds** and `settings.scripted.script`
/ `settings.browser.script` are **base64-encoded**; Terraform takes the plain script
via `file()`.
通过标准:退出码为0,运行一次迭代,摘要中无失败断言。请运行3–5次;本地可靠性为90%的脚本,在3个探测节点上会每晚触发告警。
然后创建检查(选择一种方式):
- **UI**:Testing & synthetics → Synthetics → 添加新检查 → *k6 scripted* / *k6 browser* → 粘贴脚本 → 选择探测节点+执行频率 → **测试**(不保存运行一次)→ 保存。
- **API或Terraform**:参考[`references/api-and-terraform.md`](references/api-and-terraform.md)。关键注意事项:API中的`frequency`/`timeout`单位是**毫秒**,`settings.scripted.script` / `settings.browser.script`需要**base64编码**;Terraform通过`file()`读取原始脚本。Verify it works, and rollback
验证有效性并回滚
Wait one frequency interval, then in Explore against the Synthetic Monitoring metrics
(Prometheus) datasource:
promql
undefined等待一个执行周期后,在Explore中针对Synthetic Monitoring指标(Prometheus)数据源运行以下查询:
promql
undefined1 from every selected probe = healthy
所有选定探测节点返回1即为健康
probe_success{job="checkout-flow"}
probe_success{job="checkout-flow"}
Assertion pass rate per named assertion (scripted/browser)
每个命名断言的通过率(脚本化/浏览器检查)
probe_check_success_rate{job="checkout-flow"}
probe_check_success_rate{job="checkout-flow"}
Journey duration per probe — confirm it's comfortably under the timeout
每个探测节点的旅程时长——确认远低于超时时间
probe_script_duration_seconds{job="checkout-flow"}
probe_script_duration_seconds{job="checkout-flow"}
Uptime over time (how the SM app computes it)
历史可用性(SM应用的计算方式)
max by () (max_over_time(probe_success{job="checkout-flow"}[5m]))
A healthy first execution: `probe_success == 1` from every probe, all
`probe_check_success_rate` series at 1, duration stable across probes, and the check's
prebuilt dashboard (Synthetics → check → View dashboard) showing logs for each execution.
Browser checks should additionally show `probe_browser_web_vital_*` series.
**Rollback**: set the check's `enabled: false` (UI toggle, API update, or Terraform) to
stop executions without losing history; delete the check only when you no longer need
its configuration. Alerting: start with `alertSensitivity` / the default alert rules on
`probe_success` — see the [`testing`](../testing/SKILL.md) skill for alert rule examples.max by () (max_over_time(probe_success{job="checkout-flow"}[5m]))
首次执行健康的标志:所有探测节点的`probe_success == 1`,所有`probe_check_success_rate`序列为1,各探测节点的时长稳定,且检查的预构建仪表盘(Synthetics → 检查 → 查看仪表盘)显示每次执行的日志。浏览器检查还应显示`probe_browser_web_vital_*`序列。
**回滚**:将检查的`enabled: false`(UI开关、API更新或Terraform配置)以停止执行但保留历史记录;仅当不再需要配置时才删除检查。告警:从`alertSensitivity`或`probe_success`的默认告警规则开始——告警规则示例请参考[`testing`](../testing/SKILL.md)技能。Common failure modes
常见故障模式
| Symptom | Cause → fix |
|---|---|
| Passes locally, fails on all probes | Target not reachable from the public internet (internal DNS, VPN, IP allowlist). Use private probes for internal targets, or allowlist probe egress |
| Passes locally, fails on some probes | Geo-blocking, regional CDN/WAF rules, or bot protection challenging datacenter IPs. Check |
Check "fails" in your eyes but | Bare |
| Browser check flaps with locator timeouts | Brittle selectors or animation timing. Switch to |
| The locator matches multiple elements (strict mode) — the error message is misleading. Tighten the selector or use |
| Create succeeds but readback 404s on every execution | The id exceeds |
| Secret name mismatch (names are exact, ≤253 chars, letters/numbers/ |
| Executions time out but the journey is fine | Timeout too low for the journey (max 180s) — raise it; or the script does unbounded work per iteration. Also confirm timeout < frequency |
| Page exceeds the 1GB probe browser memory — trim the journey, block heavy third-party resources, or use a private probe with more memory |
| UI rejects a browser script | Bundled/minified script fails the UI's import validation — create it via API or Terraform instead |
| Metrics/billing explosion after adding a check | Unique IDs in URLs creating per-execution time series — use |
| 症状 | 原因 → 修复方案 |
|---|---|
| 本地通过,所有探测节点失败 | 目标无法从公网访问(内部DNS、VPN、IP白名单)。针对内部目标使用私有探测节点,或将探测节点出口IP加入白名单 |
| 本地通过,部分探测节点失败 | 地域封禁、区域CDN/WAF规则,或机器人防护机制针对数据中心IP发起挑战。查看失败记录的 |
你认为检查“失败”但 | 使用了原生 |
| 浏览器检查因定位器超时出现抖动 | 选择器不够健壮或动画时序问题。切换为 |
| 定位器匹配到多个元素(严格模式)——错误信息具有误导性。收紧选择器或使用 |
| 创建成功但每次执行读取都返回404 | ID超过 |
| 密钥名称不匹配(名称需完全一致,≤253字符,仅包含字母/数字/ |
| 执行超时但旅程本身正常 | 超时时间设置过低(最大180秒)——调高超时时间;或脚本每次迭代执行无边界操作。同时确认超时时间<执行频率 |
浏览器检查日志中出现 | 页面超过探测节点浏览器的1GB内存限制——精简旅程、阻止第三方重资源,或使用内存更高的私有探测节点 |
| UI拒绝浏览器脚本 | 打包/压缩后的脚本未通过UI的导入验证——改为通过API或Terraform创建 |
| 添加检查后指标/账单激增 | URL中的唯一ID导致每次执行创建新的时间序列——使用 |
References
参考资料
- — SM API auth + check CRUD payloads (scripted, browser, MultiHTTP) and Terraform examples for every check type, including the protocol checks (HTTP, ping, DNS, TCP, traceroute, gRPC)
references/api-and-terraform.md
- ——SM API认证 + 各类型检查(脚本化、浏览器、MultiHTTP)的CRUD请求体,以及所有检查类型(包括HTTP、ping、DNS、TCP、traceroute、gRPC等协议类检查)的Terraform示例
references/api-and-terraform.md
Resources
资源
- Synthetic Monitoring docs
- k6 scripted checks · k6 browser checks
- Secrets management
- k6 assertions () · k6 browser module
expect - k6 fundamentals and load testing: plugin,
grafana-k6skillk6
- Synthetic Monitoring文档
- k6脚本化检查 · k6浏览器检查
- 密钥管理
- k6断言() · k6浏览器模块
expect - k6基础与负载测试:插件、
grafana-k6技能k6