1. 程式人生 > 實用技巧 >OpenStack 工作流元件: Mistral

OpenStack 工作流元件: Mistral


1 Mistral 簡介

Mistral 是由 Mirantis 開發,貢獻給 OpenStack 社群的工作流元件,它提供 Workflow As a Service 服務。 在計算機中通常處理的任務是由多個連續並有依賴關係的任務組成,而且多個任務可能分佈在不同的機器上執行,如物理機,虛擬機器,container 等。基於這樣的考慮,Mistral 應運而生,它可以靈活的編排多個任務(task),任務之間具有一定的依賴關係,每個任務又分為多個行為(action),行為可以在不通的機器上執行。這些任務的集合稱為 Workflow,Mistral 將 Workflow 作為一種服務實現任務的編排,管理,執行。
如上圖所示,Mistral service 主要有 Engine,Scheduler,API Server,Task Executor 等元件組成。各元件的主要功能有:
  • Engine:從 Workflow 佇列中選出 Workflow,處理 Workflow 中 task 資料轉換,條件判斷等操作。
  • Scheduler:Engine 和 Task Executor 互動的中間元件,儲存並執行延時呼叫的 task。
  • Task Executor:從 Task 佇列中選出 Task ,執行 task 的 action,將執行結果發給 Engine。
  • API Server:提供 Mistral API 給使用者,同時通過 Mistral API 可以監控 Workflow 的執行流。

2 部署 Mistral

2.1 Mistral 安裝

Mistral 安裝及配置可看這裡

2.2 Mistral 服務

安裝及配置好的 Mistral 大致有以下幾個服務:
  • MistralAPIservice
  • mistral-dashboardservice
  • MistralEngineservice
  • MistralExecutorservice

3 Mistral 語法

3.1 Mistral workbook 示例

---
version: '2.0'
 
name: my_workbook 
description: My set of workflows and ad
-hoc actions workflows: local_workflow1: type: direct tasks: task1: action: local_action str1='Hi' str2=' Mistral!' on-complete: - task2 task2: action: global_action
上圖是 Mistral 的 workbook,它是基於 yaml 語法寫的一系列 workflow 的集合。其中, workflow 對任務 task 進行編排,它在 workflows 欄位下定義。task 是執行的任務,它在 tasks 欄位下定義,而 action 則是任務所做的行為,它是真正處理任務的單元。 在本示例中,名為 my_workbook 的 workbook 中定義了一個名為 local_workflow1 的 workflow,該 workflow 會執行兩個 task:task1 和 task2。task2 依賴於 task1,當 task1 執行完成之後開始執行 task2。其中,task1 會執行名為 local_action 的行為,它所需要的輸入引數是 str1 和 str2,而 task2 執行的 global_action 則不需要輸入引數。 值得一提的是:Mistral 自帶一種稱為 Namespacing 的機制,該機制使得每個 action/task/workflow 具有唯一性。如上圖所示,workbook my_workbook 包含兩個 workflow 和 兩個 action,將該 workbook 上傳到 Mistral 時,Mistral 會將它們加上 workbook 字首,解析為 my_workbook.workflow1, my_workbook.action1 這種形式,從而保證了不同 workflow 可以使用相同的 task/action。

3.2 Mistral Workflow

Mistral 有兩種型別的 Workflow,它們定義在 type 欄位,分別為 direct 和 reverse:

3.2.1 direct workflow

direct workflow 示意圖如上所示。它清晰的定義了 workflow 中 task 的執行順序,task T1 執行成功會執行 T3,執行失敗則執行 T2,T2 無論執行成功或失敗都將執行 T4,依次類推,最終執行 T7。 決定 task 執行順序的是如下特性:
  • on-success:聲明當前 task 執行成功將執行的 task。
  • on-error:聲明當前 task 執行失敗將執行的 task。
  • on-complete:宣告無論當前 task 執行成功/失敗都將執行的 task。
在實際編排時,往往特性下的 task 不止一個,如下例所示:
tasks:
  create_vm:
    workflow: create_vm
    input:
      operationType: <% $.operationType %>
      operationParams: <% $.operationParams %>
    publish:
      precheck_result: <% task(create_vm).result.precheck_result %>
      finalize_tag: <% task(create_vm).result.precheck_result %>
    publish-on-error:
      error_message: <% task(create_vm).result.error_message %>
      finalize_tag: "error"
    on-success:
      - check_create_vm_done: <% $.precheck_result = 'success'%>
      - set_workflow_fail: <% $.precheck_result = 'error'%>
    on-error:
      - set_workflow_fail
 
  check_create_vm_done:
    action: heat.stacks_get stack_id=<% $.stack.id %>
    publish:
      stack_status: <% task(check_create_vm_done).result.stack_status %>
      error_message: "something wrong!."
    publish-on-error:
      error_message: <% task(check_create_vm_done).result %>
    on-complete:
      - check_vm_exists
      - set_workflow_success
在該例中,task create_vm 執行成功後會檢查變數值。如果變數 precheck_result 為 success,則執行 task check_create_vm_done。如果變數為 error,則執行 task set_workflow_fail。如果 task 執行失敗則直接執行 set_workflow_fail。其中,<% $. %> 為 YAQL 語法,類似的有 <% $.task() %>。Mistral 支援在 workbook 中使用 YAQL 或者 jinja 表示式語言來引用,定義和使用變數。(YAQL 語法看這裡, Jinja 語法看這裡) 當執行 task check_create_vm_done 時,無論結果如何都將執行 check_vm_exists 和 set_workflow_success,它們的執行順序是先執行 check_vm_exists ,再執行 set_workflow_success。 Mistral 自帶 Engine command 來控制 workflow 的狀態,參考這裡瞭解更多。

3.2.2 reverse workflow

reverse workflow 如上圖所示。workflow 中定義了 Target task T1 ,執行 target task T1 需要依賴 T2 的執行,同樣的 T2 依賴 T6 和 T5,層層往上執行依賴的 task。在該例中,T3 和 T4 不會被執行,因為 T1 依賴的 task 並沒有依賴於它們。 reverse workflow 需要的特性是 requires,在 requires 裡定義了執行當前 task 需要執行的 task。舉例,如下所示:
---
version: '2.0'
 
create_vm_and_send_email:
  type: reverse
 
  input:
    - vm_name
    - image_id
    - flavor_id
 
  output:
    result: <% $.vm_id %>
 
  tasks:
    create_vm:
      action: nova.servers_create name=<% $.vm_name %> image=<% $.image_id %> flavor=<% $.flavor_id %>
      publish:
        vm_id: <% task(create_vm).result.id %>
 
    search_for_ip:
      action: nova.floating_ips_findall instance_id=null
      publish:
        vm_ip: <% task(search_for_ip).result[0].ip %>
 
    associate_ip:
      action: nova.servers_add_floating_ip server=<% $.vm_id %> address=<% $.vm_ip %>
      requires: [search_for_ip]
 
    send_email:
      action: send_email to='[email protected]' body='Vm is created and id <% $.vm_id %> and ip address <% $.vm_ip %>'
      requires: [create_vm, associate_ip]

3.3.3 common workflow 語法

前面已經介紹了幾個 workflow 示例,從示例中大致可以看出 workflow 主要有如下幾個特性:
  • type:定義 workflow 是哪種型別,direct 或 reverse。可選項,預設為 direct。
  • input:提供給 workflow 的引數,引數名前加 “-”。可選項。
  • output:workflow 執行的輸出結果,該結果可被上一層 workflow 使用。可選項。
  • tasks:workflow 定義的 task,非可選項。

3.3 Mistral task

Mistral task 主要有如下幾個特性:
  • name:定義的 task 名稱,非可選。
  • description:task 的描述文字,可選。
  • action:執行 task 需要執行的 action。
  • workflow:執行 task 需要執行的 workflow。task 既支援執行 action 也支援執行 workflow。
  • input:task 執行需要的輸入引數。
  • publish:task 執行成功的輸出,輸出是 json 格式文字。在後面的操作中可以使用 publish 的結果。
  • publish-on-error:task 執行失敗的輸出。
  • timeout: 以秒為單位定義 task 的超時時間,如果 task 在規定時間內未完成則 task 被視為執行失敗,屬於 timeout policy。
  • retry: 定義 task 執行失敗需要執行的模式,有 count,delay,break-in 和 continue-on 特性:
get_vm_result:
    action: vm_result
    input:
      operation_parameters: <% $.operationParams %>
    retry:
      count: <% 10 %>
      delay: 30

本例中,task get_vm_result 的 action 如果執行失敗後,會進入 retry 模式,重新執行 10 次 action,且每次 action 的間隔是 30 秒,直到執行成功才退出 retry 模式。它屬於 retry_policy,更多 task policy 可看這裡

3.4 Mistral action

Mistral action 是真正執行任務的單元,它分為 system action 和 ad-hoc action。system action 是 Mistral 自帶的 action,安裝好 Mistral 即可使用。ad-hoc action 是使用者在自帶 action 的基礎上自定義的 action。

3.4.1 system action

系統自帶的 action 有很多,列幾個常見 action 如下:
  • std.fail:始終執行 fail,常用於 workflow 失敗時執行。
  • std.noop:什麼都不做的 action。
  • stdjavascript:執行 javascript 程式碼,它包含兩個特性:context 和 script。context 中定義了在 script 中使用的變數,script 是javascript 語法,如下所示:
check_vm:
    action: std.javascript
    input:
      context:
        vm: <%  $.vm %>
      script: |
        if( $.vm.count != "0")
          return "yes";
        else
          return "no";
(看這裡瞭解更多 system action)

3.4.2 ad-hoc action

ad-hoc action 是使用者基於 system action 之上自定義的 action,定義好 action 之後將此 action 傳給 Mistral,即可在 workflow 中使用自定義的 action 。 通過自定義 ad-hoc 使得使用者可以頻繁呼叫一個 action 而省去了重複寫多個 action 的過程,關於如何定義和使用 ad-hoc action 可看這裡 注意,除了 system action 和 ad-hoc action,也可以自定義 action,將自定義 action 上傳到 Mistral 即可在 workflow 中使用,關於如何自定義 action 可看這裡

4 Mistral API

Mistral 提供了多組 Rest API 供使用者呼叫,如:
  • GET /v2/workflows
  • POST /v2/workflows
  • DELETE /v2/workflows
  • PUT /v2/workflows
  • GET /v2/actions
  • ...
通過 Mistral 命令列檢視呼叫的 Rest API:
[root@lianhuasheng home]# mistral workflow-list --debug
DEBUG (connectionpool) Starting new HTTP connection (1): localhost:8989
DEBUG (connectionpool) http://localhost:8989 "GET /v2/workflows?fields=id%2Cname%2Cnamespace%2Cproject_id%2Ctags%2Cinput%2Cscope%2Ccreated_at%2Cupdated_at&sort_keys=created_at&sort_dirs=asc HTTP/1.1" 200 68369
DEBUG (httpclient) HTTP GET http://localhost:8989/v2/workflows?fields=id%2Cname%2Cnamespace%2Cproject_id%2Ctags%2Cinput%2Cscope%2Ccreated_at%2Cupdated_at&sort_keys=created_at&sort_dirs=asc 200
...
(看這裡詳細瞭解 Mistral Rest API)

5 Mistral 常用命令

Mistral 提供了一系列命令檢視 workflow 的執行,執行 mistral 進入互動模式,輸入 help 檢視這些命令:
(mistral) help
 
Shell commands (type help <topic>):
===================================
alias  exit  history  py        quit  shell      unalias
edit   help  load     pyscript  set   shortcuts
 
Application commands (type help <topic>):
=========================================
action-create                environment-get       run-action
action-delete                environment-list      service-list
action-execution-delete      environment-update    task-get
action-execution-get         event-trigger-create  task-get-published
action-execution-get-input   event-trigger-delete  task-get-result
action-execution-get-output  event-trigger-get     task-list
action-execution-list        event-trigger-list    task-rerun
action-execution-update      execution-create      workbook-create
action-get                   execution-delete      workbook-delete
action-get-definition        execution-get         workbook-get
action-list                  execution-get-input   workbook-get-definition
action-update                execution-get-output  workbook-list
action-validate              execution-get-report  workbook-update
bash-completion              execution-list        workbook-validate
complete                     execution-update      workflow-create
cron-trigger-create          help                  workflow-delete
cron-trigger-delete          member-create         workflow-get
cron-trigger-get             member-delete         workflow-get-definition
cron-trigger-list            member-get            workflow-list
environment-create           member-list           workflow-update
environment-delete           member-update         workflow-validate
舉個簡單的示例檢視 workflow 的執行情況(該示例來自這裡): 1) 自定義 workflow:
---
version: "2.0"
 
my_workflow:
  type: direct
 
  input:
    - names
 
  tasks:
    task1:
      with-items: name in <% $.names %>
      action: std.echo output=<% $.name %>
      on-success: task2
 
    task2:
      action: std.echo output="Done"
2) 上傳 workflow 到 mistral:
$ mistral workflow-create <workflow.yaml>
 
+------------------------------------+-------------+-----------+--------+-------+---------------------+------------+
|ID                                  | Name        | Namespace | Tags   | Input | Created at          | Updated at |
+------------------------------------+-------------+-----------+--------+-------+---------------------+------------+
|9b719d62-2ced-47d3-b500-73261bb0b2ad| my_workflow |           | <none> | names | 2015-08-13 08:44:49 | None       |
+------------------------------------+-------------+-----------+--------+-------+---------------------+------------+
3) 執行 workflow:
$ mistral execution-create my_workflow '{"names": ["John", "Mistral", "Ivan", "Crystal"]}'
 
+--------------------+--------------------------------------+
| Field              | Value                                |
+--------------------+--------------------------------------+
| ID                 | 49213eb5-196c-421f-b436-775849b55040 |
| Workflow ID        | 9b719d62-2ced-47d3-b500-73261bb0b2ad |
| Workflow name      | my_workflow                          |
| Workflow namespace |                                      |
| Description        |                                      |
| Task Execution ID  | <none>                               |
| Root Execution ID  | <none>                               |
| State              | RUNNING                              |
| State info         | None                                 |
| Created at         | 2017-03-06 11:24:10                  |
| Updated at         | 2017-03-06 11:24:10                  |
+--------------------+--------------------------------------+
4) 檢查 workflow 執行的狀態:
$ mistral execution-get 49213eb5-196c-421f-b436-775849b55040    # 注意這裡的 id 是 execution_id
 
+--------------------+--------------------------------------+
| Field              | Value                                |
+--------------------+--------------------------------------+
| ID                 | 49213eb5-196c-421f-b436-775849b55040 |
| Workflow ID        | 9b719d62-2ced-47d3-b500-73261bb0b2ad |
| Workflow name      | my_workflow                          |
| Workflow namespace |                                      |
| Description        |                                      |
| Task Execution ID  | <none>                               |
| Root Execution ID  | <none>                               |
| State              | SUCCESS                              |
| State info         | None                                 |
| Created at         | 2017-03-06 11:24:10                  |
| Updated at         | 2017-03-06 11:24:20                  |
+--------------------+--------------------------------------+
5) 檢查 workflow 下 task 的執行情況:
$ mistral task-list 49213eb5-196c-421f-b436-775849b55040 
 
+--------------------------------------+-------+---------------+--------------------+--------------------------------------+---------+------------+---------------------+---------------------+
| ID                                   | Name  | Workflow name | Workflow namespace | Execution ID                         | State   | State info | Created at          | Updated at          |
+--------------------------------------+-------+---------------+--------------------+--------------------------------------+---------+------------+---------------------+---------------------+
| f639e7a9-9609-468e-aa08-7650e1472efe | task1 | my_workflow   |                    | 49213eb5-196c-421f-b436-775849b55040 | SUCCESS | None       | 2017-03-06 11:24:11 | 2017-03-06 11:24:17 |
| d565c5a0-f46f-4ebe-8655-9eb6796307a3 | task2 | my_workflow   |                    | 49213eb5-196c-421f-b436-775849b55040 | SUCCESS | None       | 2017-03-06 11:24:17 | 2017-03-06 11:24:18 |
+--------------------------------------+-------+---------------+--------------------+--------------------------------------+---------+------------+---------------------+---------------------+
檢查 task 執行的輸出:
$ mistral task-get-result f639e7a9-9609-468e-aa08-7650e1472efe
 
[
    "John",
    "Mistral",
    "Ivan",
    "Crystal"
]
6) 檢查 task 下 action 的執行情況:
$ mistral action-execution-list f639e7a9-9609-468e-aa08-7650e1472efe   
 
+--------------------------------------+----------+---------------+--------------------+-----------+--------------------------------------+---------+----------+---------------------+---------------------+
| ID                                   | Name     | Workflow name | Workflow namespace | Task name | Task ID                              | State   | Accepted | Created at          | Updated at          |
+--------------------------------------+----------+---------------+--------------------+-----------+--------------------------------------+---------+----------+---------------------+---------------------+
| 4e0a60be-04df-42d7-aa59-5107e599d079 | std.echo | my_workflow   |                    | task1     | f639e7a9-9609-468e-aa08-7650e1472efe | SUCCESS | True     | 2017-03-06 11:24:12 | 2017-03-06 11:24:16 |
| 5bd95da4-9b29-4a79-bcb1-298abd659bd6 | std.echo | my_workflow   |                    | task1     | f639e7a9-9609-468e-aa08-7650e1472efe | SUCCESS | True     | 2017-03-06 11:24:12 | 2017-03-06 11:24:16 |
| 6ae6c19e-b51b-4910-9e0e-96c788093715 | std.echo | my_workflow   |                    | task1     | f639e7a9-9609-468e-aa08-7650e1472efe | SUCCESS | True     | 2017-03-06 11:24:12 | 2017-03-06 11:24:16 |
| bed5a6a2-c1d8-460f-a2a5-b36f72f85e19 | std.echo | my_workflow   |                    | task1     | f639e7a9-9609-468e-aa08-7650e1472efe | SUCCESS | True     | 2017-03-06 11:24:12 | 2017-03-06 11:24:17 |
+--------------------------------------+----------+---------------+--------------------+-----------+--------------------------------------+---------+----------+---------------------+---------------------+
檢查 action 的執行結果:
$ mistral action-execution-get-output 4e0a60be-04df-42d7-aa59-5107e599d079
 
{
    "result": "John"
}