OneUptime 与 Jira 双向集成实战:用 Workflow 自动同步 Incident 与 Issue

📅 发布时间:2026/9/19 10:27:16
OneUptime 与 Jira 双向集成实战:用 Workflow 自动同步 Incident 与 Issue
OneUptime 与 Jira 双向集成实战用 Workflow 自动同步 Incident 与 Issue【免费下载链接】oneuptimeComplete open-source monitoring and observability platform.项目地址: https://gitcode.com/GitHub_Trending/on/oneuptimeOneUptime 的 Workflow 引擎允许你通过低代码画布把监控与告警和外部工单系统打通。本文以 Jira 为例讲解完整的双向集成方案OneUptime 每次声明 Incident 时自动在 Jira 创建 Issue随着 Incident 推进持续更新评论、状态流转同时由 Jira 的自动化规则把 Issue 的状态变更回调到 OneUptime 并更新对应 Incident。读完本文你将掌握 Basic Auth 凭据的存储与轮换、Atlassian Document FormatADF的构造、JQL 反查、Webhook 触发器的安全校验以及 Jira Cloud 与 Jira Data Center 两套 API 的差异。整个方案不需要安装任何 Jira 专用插件OneUptime 通过 Workflow 组件中的 API 组件直接调用 Jira REST APIJira 则通过调用 OneUptime 的 Webhook 触发器把事件推回来。双向数据流如下OneUptime Incident → On Create ──► API Post (POST /rest/api/3/issue) ──► Jira issue Jira issue transitioned ──► Automation rule (Send web request) ──► OneUptime Webhook trigger ──► Update One Incident注意Atlassian 在 Jira Cloud 中持续重命名术语——project在多数界面中已改称spaceissue改称work item。不同租户界面词汇并存下文在术语关键处会同时给出两种叫法。前置条件一个 Jira Cloud 站点https://your-domain.atlassian.net以及一个用于登记 Issue 的项目记下它的project key——即OPS-1234中的OPS。一个能在该项目中创建 Issue 的 Jira 账号及其API token在id.atlassian.com/manage-profile/security/api-tokens生成。建议使用服务账号而非个人账号——这样创建的 Issue 会归属于 token 持有者。拥有在该项目创建自动化规则的权限用于入站方向。一个 OneUptime 项目并且能创建 Workflow 和全局变量。第一步把 Jira 凭据保存为 SecretJira Cloud 的 REST API 使用Basic Auth将 Atlassian 账号邮箱与 API token 拼成email:api_token后整体 base64 编码。一次性完成编码printf %s youexample.com:your_api_token | base64必须用printf而不是echo。echo会附加一个换行符换行符会连同内容一起被编码Jira 会返回401而从粘贴的字符串上根本看不出原因。在 OneUptime 中进入Flujos de trabajo → Variables Globales → Crear工作流 → 全局变量 → 创建命名为JIRA_AUTH把 base64 字符串粘贴为Content并打开Secret开关。再添加一个非 Secret 变量JIRA_URL值为https://your-domain.atlassian.net末尾不要带斜杠。此后任何组件都可以用Basic {{global.variables.JIRA_AUTH}}作为Authorization请求头而 token 永远不会出现在 Workflow 本体或其执行日志中。全局变量的引用语法、Secret 的脱敏行为参见 variables.md变量创建后不可编辑只能删除重建。关于 Atlassian API token有两个会咬人的细节token 会过期。创建时可选 1 天到 1 年有效期默认 1 年且无法续期——过期后必须在同一页面手动更换并重新编码进JIRA_AUTH。请把过期日期记进日历。当某个运行了数月的 Workflow 突然开始返回401原因多半在此。带 scope 的 token 需要不同的 base URL。token 页面除经典Create API token外还提供Create API token with scopes。带 scope 的 token 更安全但它不指向你的站点而是指向https://api.atlassian.com/ex/jira/cloudId——此时JIRA_URL应改为该地址下方所有路由不变地挂在其后。你的cloudId可从https://your-domain.atlassian.net/_edge/tenant_info返回的 JSON 中获得。把带 scope 的 token 发往your-domain.atlassian.net只会失败。如果组织使用 Atlassian 集中式用户管理还有第三种选择能彻底绕开过期问题为服务账号创建 OAuth 2.0 凭据页面的两组件结构相同一个API Post (JSON)组件获取 token后续所有请求携带Bearer token。API base URL 为https://api.atlassian.com具体换取 token 的请求格式以 Atlassian 官方页面为准。第二步每个 Incident 自动创建 Jira Issue打开Flujos de trabajo → Crear flujo de trabajo工作流 → 创建工作流命名为Incidents → Jira进入Constructor画布编辑器。点击虚线占位符组件添加触发器On Create Incident。在Select Fields中声明需要向下游传递的字段{ _id: true, title: true, description: true, incidentNumber: true, incidentSeverity: { name: true } }保持其Identifier为默认值incident-on-create-1——后续组件靠它引用这个触发器的输出。点击Añadir componente添加组件加入API Post (JSON)组件从触发器的Success端口拖线连到新组件的入口。打开该组件把Identifier设为create-issue填写URL:{{global.variables.JIRA_URL}}/rest/api/3/issueRequest Headers:{ Authorization: Basic {{global.variables.JIRA_AUTH}}, Accept: application/json }Request Body:{ fields: { project: { key: OPS }, issuetype: { name: Bug }, summary: OneUptime #{{local.components.incident-on-create-1.returnValues.model.incidentNumber}}: {{local.components.incident-on-create-1.returnValues.model.title}}, labels: [oneuptime], description: { type: doc, version: 1, content: [ { type: paragraph, content: [ { type: text, text: {{local.components.incident-on-create-1.returnValues.model.description}} } ] } ] } } }把OPS换成你的 project keyBug换成该项目中真实存在的 issue 类型。两者也都可以用 id 指定——{id: 10000}——这是 Atlassian 官方示例的做法当站点里有两个同名 issue 类型时务必用 id。下文createmeta的调用会给出这些 id。关于 Atlassian Document FormatADF描述字段看起来很重是因为 Jira Cloud 的 API v3 把富文本作为Atlassian Document Format接收——一个文档树而不是字符串。上面的结构是合法文档的最小形态一个paragraph包含一个text节点。environment以及多行文本类型的自定义字段同样适用单行文本自定义字段则仍然接受普通字符串。启用与验证从Vista General → Editar flujo de trabajo → Habilitado概览 → 编辑工作流 → 启用打开 Workflow 开关声明一个测试 Incident然后打开Ejecuciones y Registros执行与日志。create-issue组件应显示201响应体包含新 Issue 的id、key和self。画布上的修改是自动保存的——没有保存按钮而禁用的 Workflow 无论如何都无法执行包括手动执行。新 Issue 的 key 对后续任意组件可用{{local.components.create-issue.returnValues.response-body.key}}这里的response-body、response-status、response-headers是 API 组件的标准返回字段。从 API/Post.ts 的实现可以看到请求发出后组件把HTTPResponse或HTTPErrorResponse统一转换为这三个返回值并按结果走向success或error端口4xx/5xx 会走 error 端口同时把状态码与响应体保留在response-body中供排查。填充更多字段fields中常见的补充Priority——priority: { id: 20000 }使用你站点的优先级 id。要把 OneUptime 的严重级别映射到 Jira 优先级可在触发器与 API 组件之间放一个If / Else组件对{{local.components.incident-on-create-1.returnValues.model.incidentSeverity.name}}做分支。Assignee——assignee: { id: accountId }。Jira Cloud 用 Atlassian account id 标识人员username与userKey多年前已从 Cloud API 移除。Labels——labels: [oneuptime, sev1]扁平字符串数组。label 不能包含空格。Components——components: [{ id: 10000 }]。Custom fields——customfield_10034: ...用字段自己的 id。值的形态取决于字段类型单选 select 接受{value: red}多选接受 id 数组多行文本字段则要传 ADF 文档。与其猜测项目到底要求什么不如直接问 Jira。先列出项目的 issue 类型再列出其中某个类型的字段curl -u youexample.com:your_api_token \ https://your-domain.atlassian.net/rest/api/3/issue/createmeta/OPS/issuetypes curl -u youexample.com:your_api_token \ https://your-domain.atlassian.net/rest/api/3/issue/createmeta/OPS/issuetypes/10001第二个调用会列出该 issue 类型接受的所有字段、哪些是必填的以及确切的customfield_NNNNNid。对于已有的 Issue可用?expandnames读取字段 id。第三步把 Incident ID 带进 Jira双向同步的两半需要某一方保存另一方的标识符而 Jira 是最合适的存放处OneUptime 的customFields列是单个 JSON blob从 Workflow 写入一个值会覆盖该 Incident 的全部自定义字段。方案一有 Jira 管理员配合在项目的创建屏幕添加一个短文本自定义字段——比如叫OneUptime Incident ID——用createmeta查到它的 id然后把它和其他字段一起提交customfield_10050: {{local.components.incident-on-create-1.returnValues.model._id}}方案二没有管理员放进 label。label 不能有空格而 OneUptime 的 id 是纯 UUID所以oneuptime-id是合法 labellabels: [oneuptime, oneuptime-{{local.components.incident-on-create-1.returnValues.model._id}}]入站 Workflow 需要从 label 列表中提取该 id这在一个Run Custom JavaScript组件里只需几行。能建自定义字段的话还是它更干净。顺带值得在 Jira Issue 里加一个回链到 Incident 的链接。在create-issue之后再加一个API Post (JSON)组件指向{{global.variables.JIRA_URL}}/rest/api/3/issue/{{local.components.create-issue.returnValues.response-body.key}}/remotelink{ globalId: systemhttps://oneuptime.comid{{local.components.incident-on-create-1.returnValues.model._id}}, object: { url: https://oneuptime.com/dashboard/{{local.components.incident-on-create-1.returnValues.model.projectId}}/incidents/{{local.components.incident-on-create-1.returnValues.model._id}}, title: OneUptime incident #{{local.components.incident-on-create-1.returnValues.model.incidentNumber}} } }这给 Jira 里所有人一条一键返回的路径。为此需要在触发器的Select Fields中追加projectId。globalId让该调用可安全重放Jira 会更新已携带该 id 的链接而不是新增第二个。由于更新同样会把省略的字段置空务必总是发送完整的object而非补丁。第四步随 Incident 推进评论与流转状态把这个构建成第二个Workflow这样它的任何故障都不会阻碍 Issue 的创建。Crear flujo de trabajo命名为Incident updates → Jira添加触发器On Update Incident。在Listen on中填{currentIncidentStateId: true}——这样触发器只在状态变更时触发而不是每次编辑都触发。在Select Fields中请求{_id: true, currentIncidentState: {name: true}}。添加If / Else组件Input 1为{{local.components.incident-on-update-1.returnValues.model.currentIncidentState.name}}Operator为Input 2为Resolved或你项目中实际的已解决状态名。OneUptime 的状态模型见 states-and-severities.md状态行为由isCreatedState、isAcknowledgedState、isResolvedState三个布尔标志驱动与状态名称无关所以重命名状态不会破坏该判断。在Sí分支中先找回第二步创建的 Issue——用第三步保存的 id 向 Jira 查询。添加一个API Post (JSON)组件Identifier设为find-issueURL:{{global.variables.JIRA_URL}}/rest/api/3/search/jqlRequest Body:{ jql: project OPS AND labels \oneuptime-{{local.components.incident-on-update-1.returnValues.model._id}}\, maxResults: 1 }如果第三步用的是自定义字段而非 label则把子句改为cf[10050] ~ ...用你自己的字段 id。Issue 的 id 随后为{{local.components.find-issue.returnValues.response-body.issues[0].id}}下方所有 endpoint 用 id 与用 key 效果相同。关于该 endpoint 有三点要知道JQL 放在请求体中而不是 URL——URL 查询串里包含的值在离开 Workflow 时会被截断而 JQL 几乎全是查询必须加限定条件——裸的order by key desc会被400拒绝这正是project 子句存在的原因/rest/api/3/search/jql才是现行 endpoint——旧的/rest/api/3/search已弃用并走向移除不要回退使用。留下评论一个API Post (JSON)组件指向{{global.variables.JIRA_URL}}/rest/api/3/issue/id/comment正文同样用 ADF{ body: { type: doc, version: 1, content: [ { type: paragraph, content: [{ type: text, text: Resolved in OneUptime. }] } ] } }流转 Issue需要两次调用因为 transition 用 id 标识而 id 在不同 Workflow 之间、某些看板下不同 Issue 之间都可能有差异一个API Get (JSON)组件访问{{global.variables.JIRA_URL}}/rest/api/3/issue/id/transitions返回从该 Issue 当前状态出发的所有可用流转每条含id、name以及描述目标状态的to对象。一个API Post (JSON)组件向同一 URL 执行流转{ transition: { id: 31 } }成功的流转返回204且无响应体。如果不希望运行时读取列表可以手动对处于合适状态的 Issue 调用一次并硬编码 id——但要记住它与该 Workflow 绑定Jira 管理员日后修改 Workflow 会静默破坏它。入站方向从 Jira 回调 OneUptime现在处理另一个方向有人把 Issue 移到 DoneOneUptime 的 Incident 应当跟随。先构建接收端 WorkflowCrear flujo de trabajo命名为Jira → OneUptime添加Webhook触发器。打开该 Workflow 的Ajustes设置复制Clave secreta del webhookWebhook 密钥。你的回调 URL 是https://oneuptime.com/workflow/trigger/webhook secret key自托管安装使用自己的主机名。请把该 URL 当作密码保管——任何拿到它的人都能触发 Workflow——若泄露则从同一页面重置密钥。Webhook 触发器的实现位于 Webhook.ts它注册了GET /trigger/:secretkey与POST /trigger/:secretkey两个路由用路径参数中的密钥查询WorkflowService找到对应 Workflow然后把request-headers、request-params、request-body三个值注入执行上下文并立即返回{status: Scheduled}。添加一个If / Else组件在任何其他逻辑执行前校验共享密钥。Input 1为{{local.components.webhook-1.returnValues.request-headers.x-oneuptime-secret}}Operator为Input 2为{{global.variables.JIRA_WEBHOOK_SECRET}}——这是一个你自拟并保存为全局 Secret 变量的值。从Sí分支添加Update One Incident组件Query:{_id: {{local.components.webhook-1.returnValues.request-body.oneuptimeIncidentId}}}Data (JSON Object): 该 Jira 变更在 OneUptime 侧应产生的效果——通常是一次状态变更。流转 Incident 需要目标状态的 id可用Find One Incident State组件配合查询{name: Resolved}得到{{local.components.incident-state-find-one-1.returnValues.model._id}}把它写入currentIncidentStateId。保持该 Workflow 为启用状态然后给 Jira 一个可调用的目标。用 Jira 自动化规则发送事件在 Jira 打开项目的自动化规则新租户在Space settings → Automation旧租户在Project settings → Automation。跨项目的规则用Settings → System → Global automation这需要全局Administer Jira权限。Create rule并选择触发器Work item transitioned旧租户为Issue transitioned配置为状态进入Done时触发。请用这个触发器而不是Work item updated更新触发器刻意排除了状态变更。添加Send web request动作并配置Web request URL: 上面 OneUptime 的 Webhook URL。HTTP method:POSTHeaders:Content-Type/application/json以及X-OneUptime-Secret/ 你的共享密钥。密钥值用Hide选项其他规则编辑者将无法读取——注意隐藏对该值不可逆且导出或复制规则时隐藏值会丢失。Web request body: 选Custom format以便完全控制形态{ oneuptimeIncidentId: {{issue.customfield_10050}}, issueKey: {{issue.key}}, summary: {{issue.summary}}, status: {{issue.status.name}} }如果第三步用的是 label 而非自定义字段则发送labels: {{issue.labels}}在 OneUptime 侧用Run Custom JavaScript组件提取 id。启用规则把测试 Issue 移到 Done然后两侧核对Jira 侧看规则自身的审计日志OneUptime 侧看Ejecuciones y Registros。依赖这套机制前需要知道的几件事目标端口受限。Send web request 只能访问 80、8080、443、6017、8443、8444、7990、8090、8085、8060、8900 和 9900 端口。OneUptime Cloud 在 443自托管且端口不寻常的安装无法被这样回调。请求没有签名。该动作没有 HMAC 选项所以 HTTPS 上的共享密钥 header 是 Atlassian 文档化的机制。接收端 Workflow 第三步的If / Else校验正是让它有意义的原因。规则执行计入配额。Jira Cloud 把规则成功执行计入月度配额随套餐而异——Free 100 次、Standard 1,700 次、Premium 1,000 × 用户数、Enterprise 不限量。在高频项目上每条流转都触发的规则会很快累积。值不会被 URL 编码。只有发送表单编码的 body 时才需要关心上面的 JSON 没问题。Atlassian 公布出站 IP 段ip-ranges.atlassian.com供 OneUptime 位于白名单之后时使用。这些段会变化请定期查询而非固化地址。或者改用 Jira WebhookJira 管理员也可以在Settings → System → Advanced → WebHooks直接注册 webhook选择要发送的事件并可用 JQL 查询限定触发它的 Issue。与自动化规则相比载荷是 Jira 原生的而非你自定义的包含webhookEvent、issue_event_type_name、完整issue以及一个changelog其items数组给出每个被修改字段的前后值。对状态变更你要找field为status的条目。在 Workflow 内解析它通常需要一个Run Custom JavaScript组件。Webhook可以签名——给 webhook 一个 secretJira 会发送X-Hub-Signatureheader请求体字节的 HMAC——但 Workflow 无法校验。签名覆盖的是 Jira 发送的确切字节而 Webhook 触发器交给 Workflow 的是已被解析为 JSON 的 body没有可哈希的原始内容。若要认证请求用带共享密钥 header 的自动化规则。URL 必须是 HTTPS 且端口在 Jira 自己的端口列表内与自动化动作的列表不同——这里不允许 80 端口。投递会重试最多五次间隔 5 到 15 分钟因此 Workflow 必须容忍同一事件到达两次。应用通过/rest/api/3/webhook注册的 webhook 是另一回事除非刷新否则注册 30 天后过期。管理员注册的上文所述不过期。Jira Data Center 的差异自托管 Jira 的机制相同只需少量替换。Jira Server已于 2024 年 2 月停止支持且不再修复因此把 Data Center 视为自托管目标。CloudData Center/rest/api/3/.../rest/api/2/...—— Data Center 没有 v3description为 ADF 文档description为 wiki markup 普通字符串Authorization: Basic base64(email:api_token)Authorization: Bearer personal access tokenAPI token 来自 id.atlassian.comProfile → Personal access tokens → Create token你自己的 Jira 账号自动化动作Send web request自动化动作Send outgoing web request因此创建 Issue 的组件变成对/rest/api/2/issue的POST{ fields: { project: { key: OPS }, issuetype: { name: Bug }, summary: OneUptime #123: Checkout is down, description: Plain text goes straight in here. } }模板化更简单——没有文档树。其他应预判的差异Personal access token自 Jira Core / Jira Software 8.14 与 Jira Service Management 4.15 起可用。默认 365 天过期过期前 5 天界面会标记Expires soon。用户名密码的 Basic 认证在 Data Center 仍可用但少数几次失败登录会触发 CAPTCHA把账号完全挡在 REST API 之外直到有人用浏览器解决——这不是发现拼写错误的好方式优先用 token。自动化内置于 Jira Data Center 10.0 起更早版本需要单独安装 Automation for Jira 应用。其出站请求默认超时 3000 ms可通过属性outgoing.webhook.timeout.ms调整。Webhook在Administration → System → Advanced → WebHooks注册支持用 JQL 限定。请保持过滤器严格Jira 在触发事件所在线程中评估每个已注册 webhook 的 JQL十几个宽松过滤器会拖慢触发它们的用户操作。自 Data Center 10.0 起 webhook 投递为异步且没有同步选项事件可能乱序到达。接收端 Workflow 要幂等。Jira 10 移除了 webhook URL 变量中的$——${issue.id}变成{issue.id}——并把 webhook REST 资源从/rest/webhooks/1.0/webhook移到/rest/jira-webhook/1.0/webhooks。用同样的方式处理告警Alert以上全部围绕 Incident 编写因为这是最常见场景但告警的工作方式完全相同——换记录类型即可IncidentAlertOn Create Incident(incident-on-create-1)On Create Alert(alert-on-create-1)On Update Incident(incident-on-update-1)On Update Alert(alert-on-update-1)incidentNumber、currentIncidentState、incidentSeverityalertNumber、currentAlertState、alertSeverityFind One Incident StateFind One Alert StateUpdate One IncidentUpdate One Alert一个 Workflow 恰好只有一个触发器因此 Incident 与 Alert 各需一个 Workflow。若两者要做同样的工作就把 Jira 侧逻辑构建一次再用Execute Workflow组件从两边调用它。从源码结构看这些记录型触发器由基类工厂统一生成——ComponentMetadata.ts 遍历所有数据库模型通过BaseModelComponentFactory为每个模型批量注册 On Create / On Update 等组件OnCreateBaseModel.ts 即对应on-create触发类型Workflow 执行本身在 ComponentCode.ts 中被入队调度Webhook 与各组件代码统一从 Common/Server/Types/Workflow/Components 注册。故障排查先在Ejecuciones y Registros打开失败的组件。Jira 会返回一个 JSON 响应体精确指出被拒绝的内容API 组件把它保留在response-body中。401 Unauthorized。用printf重新编码email:api_token并更新JIRA_AUTHecho带入的尾部换行是常见原因。然后确认 token 所属账号能在该项目创建 Issue。在 Data Center确认发送的是Bearer而非Basic。400 Bad Request且点名某个字段。该 issue 类型在该项目不存在或项目有必填字段未发送。对那个项目和 issue 类型运行上文createmeta调用并逐项比对。400报description的问题。Cloud v3 的 description 必须是 ADF 文档而非字符串。要么发送上文展示的文档结构要么把该组件改为/rest/api/2/issue并发送纯文本。404 Not Found。检查 base URL 与 API 版本——Cloud 用/rest/api/3/...Data Center 用/rest/api/2/...。429 Too Many Requests。Jira 在限流。响应带Retry-After秒与RateLimit-Reason命中的是哪个限额。对同一 Issue 的写操作配额很紧——约 2 秒内 20 次——所以一个快速连续评论与流转的 Workflow 可能被单个 Issue 触发限流。在调用间放一个Delay组件或把批量工作移到定时 Workflow。流转调用返回400。该 transition id 从 Issue当前状态出发无效。重新获取/transitions并使用响应中的 id。自动化规则显示成功但 OneUptime 什么都没收到。先查端口——对照上文受限端口列表。然后用curl自行向 webhook URL 发一次请求看是否出现在Ejecuciones y Registros如果你的请求到达而 Jira 的没有问题在 Jira 侧。Workflow 执行了但 Incident 没变。Update One Incident在查询无匹配时报告Items Updated: 0而这算成功而非错误。确认载荷中的 id 确实是 OneUptime Incident 的 id且查询的是_id。一条{{...}}引用原样出现在 Jira Issue 里。未解析的引用会按原文字符传递而不是清空。执行日志会点名未解析的引用——通常是写错的组件 Identifier 或改名后的变量。延伸阅读Integrations 总览 —— 入站/出站模式与快速认证指南。Microsoft Dynamics 365 集成 —— 针对 Dynamics 的同一套双向构建。Workflows 总览 与 创建工作流 —— 画布、Identifier 与如何启用 Workflow。组件 —— API、If / Else 与 OneUptime 数据组件。变量 —— Secret以及如何从下一个组件读取上一个组件的输出。配置与安全 —— Webhook 安全与出站网络访问。ServiceNow 与 PagerDuty —— 面向其他工具的同一出站模式。【免费下载链接】oneuptimeComplete open-source monitoring and observability platform.项目地址: https://gitcode.com/GitHub_Trending/on/oneuptime创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考