StackStormコトハジメ

[DMM.com Advent Calendar 2018](https://qiita.com/advent-calendar/2018/dmm) 16日目の記事です。

インフラにてサーバの面倒見たりしてます @sinnershiki です。

去年のDMM.comアドベントカレンダーでは[AnsibleとNW機器的な話](https://qiita.com/sinnershiki/items/fb35b3e1fb322c0b29c0)をしましたが、今年は社内で使っているツールである[StackStorm](https://stackstorm.com/)について紹介できたらと思います。

# StackStormとは

StackStormは、イベントドリブンな自動化ツールとなります。OSSツールな[IFTTT](https://ifttt.com/)サービスをイメージしていただくとわかりやすいかと思います。

有名なところではNetflixで使われていたりします。

具体的にどういったことができるかというと、zabbixからnginxのダウン検知を受けてnginxのプロセスを起動させるみたいなことが可能です。

DMMのオウンドメディアであるinsideでも[記事](https://inside.dmm.com/entry/2018/06/12/operational-automation)が公開されていますので、参照してみてください。

[日本StackStormユーザ会](https://stackstorm.jp/)も存在します 

# 概要

StackStormは、簡単に下記のような仕組み[^st2-overview]でできています。 

[^st2-overview]: https://docs.stackstorm.com/overview.html


- Sensor

StackStormでは、センサーを利用して外部イベントを監視しています。

- Trigger

センサーは全てのイベントを監視しており、トリガーはその内容を元にイベントのハンドリングを行います。

- Action/Workflow

アクションは、実際に実行する動作を指します。Slackにメッセージを投稿やnginxを起動など。ワークフローは、アクションの組み合わせになります。Aというアクションを実行後Bを実行など流れを記載する形です。

- Rule

センサー・トリガーやアクションは一般的にpackと呼ばれるStackStormから提供されるパッケージを利用する場合が多いです。ルールは、その提供されるアクションとトリガーを紐付けるために運用するユーザで記載するものになります。


# システム構成

StackStormは、StackStorm本体以外に複数のミドルウェアから成り立ちます。[^st2-components]

[^st2-components]: StackStormの構成: https://docs.stackstorm.com/install/overview.html


- StackStorm本体
- nginx
- mongo
- RabbitMQ
- etc...

実際に使う場合は、All in Oneで全てを1台に入れることも可能ですが、HA構成として各コンポーネントを別々に用意し、可用性を高めることも可能です。

# 使ってみる

## インストール

公式には様々なインストール方法[^st2-install]が用意してあるのですが、今回はDockerイメージを使って手軽に試してみましょう

[^st2-install]: インストール方法: https://docs.stackstorm.com/install/index.html

```shell-session
% git clone https://github.com/stackstorm/st2-docker
% cd st2-docker
% make env
% docker-compose up -d
% docker-compose exec stackstorm bash
root@853b4719244d:/# st2 --version
st2 2.9.1, on Python 2.7.6
```

Web UIもあるので、そちらも確認してみましょう。ログインに必要な情報は下記にあります。

```
% ls conf/
mongo.env       postgres.env    rabbitmq.env    redis.env       stackstorm.env
% cat conf/stackstorm.env
ST2_USER=st2admin
ST2_PASSWORD=Ch@ngeMe
```

<img width="350" alt="スクリーンショット 2018-12-15 21.39.39.png" src="https://qiita-image-store.s3.amazonaws.com/0/63136/903f395e-58bf-7358-d848-3ceb62c26be1.png">

<img width="1234" alt="スクリーンショット 2018-12-15 21.40.58.png" src="https://qiita-image-store.s3.amazonaws.com/0/63136/10ce119f-559c-d689-aa33-235dd8889f29.png">



## pack

先程も少し話したように、StackStormではpackを入れて使います。

packの一覧は[Exchange](https://exchange.stackstorm.org/)にまとまっています。

今回はSlack packを入れてみましょう。

```
root@853b4719244d:/# st2 pack install slack

For the "slack" packs, the following content will be registered:

rules     |  1
sensors   |  3
aliases   |  0
actions   |  146
triggers  |  0

Installation may take a while for packs with many items.

	[ succeeded ] download pack
	[ succeeded ] make a prerun
	[ succeeded ] install pack dependencies
	[ succeeded ] register pack

+---------+-------------------------+---------+------------------+
| name    | description             | version | author           |
+---------+-------------------------+---------+------------------+
| slack   | Slack Chat integrations | 0.12.5  | StackStorm, Inc. |
+---------+-------------------------+---------+------------------+
root@853b4719244d:/# st2 pack list
+----------+----------+---------------------+---------+------------------+
| ref      | name     | description         | version | author           |
+----------+----------+---------------------+---------+------------------+
| chatops  | chatops  | ChatOps integration | 1.0.0   | StackStorm, Inc. |
|          |          | pack                |         |                  |
| core     | core     | Basic core actions. | 1.0.0   | StackStorm, Inc. |
| default  | default  | Default pack where  | 1.0.0   | StackStorm, Inc. |
|          |          | all resources       |         |                  |
|          |          | created using the   |         |                  |
|          |          | API with no pack    |         |                  |
|          |          | specified get       |         |                  |
|          |          | saved.              |         |                  |
| examples | examples | Example sensors,    | 0.2.1   | StackStorm, Inc. |
|          |          | triggers, actions   |         |                  |
|          |          | and rules.          |         |                  |
| linux    | linux    | Generic Linux       | 1.0.1   | StackStorm, Inc. |
|          |          | actions             |         |                  |
| packs    | packs    | Pack management     | 1.0.0   | StackStorm, Inc. |
|          |          | functionality.      |         |                  |
| slack    | slack    | Slack Chat          | 0.12.5  | StackStorm, Inc. |
|          |          | integrations        |         |                  |
+----------+----------+---------------------+---------+------------------+
```

GUIだとこんな形で確認できます

<img width="1239" alt="スクリーンショット 2018-12-15 21.43.27.png" src="https://qiita-image-store.s3.amazonaws.com/0/63136/0b43de9b-fc67-4dc1-a1c2-26a47088935a.png">


## Action

先程入れたSlackのActionを確認してみるには下記の方法で確認できます。

```
root@853b4719244d:/# st2 action list --pack=slack
+-----------------------------------+-------+-----------------------------------+
| ref                               | pack  | description                       |
+-----------------------------------+-------+-----------------------------------+
| slack.api.test                    | slack | Checks API calling code.          |
| slack.apps.permissions.info       | slack | Returns list of permissions this  |
|                                   |       | app has on a team.                |
| slack.chat.postMessage            | slack | Sends a message to a channel.     |
| slack.chat.unfurl                 | slack | Provide custom unfurl behavior    |
|                                   |       | for user-posted URLs              |
| slack.chat.update                 | slack | Updates a message.                |
| slack.conversations.leave         | slack | Leaves a conversation.            |
| slack.conversations.list          | slack | Lists all channels in a Slack     |
|                                   |       | team.                             |
~~ いっぱいあるので省略 ~~
| slack.users.setPhoto              | slack | Set the user profile photo        |
| slack.users.setPresence           | slack | Manually sets user presence.      |
| slack.users_filter_by             | slack | Lists users in a Slack team       |
|                                   |       | matching certain creterias.       |
+-----------------------------------+-------+-----------------------------------+
```

当然Web UIからも確認できます。また、試しにActionを実行することも可能です。


<img width="1234" alt="スクリーンショット 2018-12-15 22.12.16.png" src="https://qiita-image-store.s3.amazonaws.com/0/63136/77b27cad-787f-b0c3-cdfc-8a685a5be44d.png">

<img width="1232" alt="スクリーンショット 2018-12-15 22.13.10.png" src="https://qiita-image-store.s3.amazonaws.com/0/63136/93f39897-7319-dbbe-88a3-28d8342c1d0d.png">

<img width="901" alt="スクリーンショット 2018-12-15 22.13.55.png" src="https://qiita-image-store.s3.amazonaws.com/0/63136/cca0e178-6f16-4c16-6f5b-bafcdd923d59.png">


## Triggerを指定してActionを動かす

では、実際にRuleを作ってアクションを実行してみましょう

GUIからもルールは作れるのですが、ルールをyamlで記載し、git等で管理ができることもまたStackStormのメリットであるため、CLIからルールの作成と登録をやってみましょう

また、全てのルールには登録するpack名を決める必要があるため、今回は `test-pack` という名前を利用することを決めておきます

ルールを置くディレクトリ名をStackStormのpack保存ルールに合わせるために予めディレクトリも用意します

```
root@853b4719244d:/# mkdir -p  /opt/stackstorm/packs/test-pack/rules
root@853b4719244d:/# vi /opt/stackstorm/packs/test-pack/rules/st2-webhook-to-slack.yaml
root@853b4719244d:/# st2 rule create /opt/stackstorm/packs/test-pack/rules/st2-webhook-to-slack.yaml
+-------------+----------------------------------------------------------+
| Property    | Value                                                    |
+-------------+----------------------------------------------------------+
| id          | 5c15114db29dc70173918139                                 |
| name        | st2-webhook-to-slack                                     |
| pack        | test-pack                                                |
| description | post message to slack from webhook.                      |
| action      | {                                                        |
|             |     "ref": "slack.post_message",                         |
|             |     "parameters": {                                      |
|             |         "username": "st2",                               |
|             |         "webhook_url": "<webhook_url>",                  |
|             |         "message": "{{trigger.body.message}}",           |
|             |         "icon_emoji": ":ok_woman:",                      |
|             |         "channel": "{{trigger.body.channel}}"            |
|             |     }                                                    |
|             | }                                                        |
| context     | {                                                        |
|             |     "user": "st2admin"                                   |
|             | }                                                        |
| criteria    | {                                                        |
|             |     "trigger.body.event_name": {                         |
|             |         "pattern": "post_message",                       |
|             |         "type": "equals"                                 |
|             |     }                                                    |
|             | }                                                        |
| enabled     | True                                                     |
| ref         | test-pack.st2-webhook-to-slack                           |
| tags        |                                                          |
| trigger     | {                                                        |
|             |     "type": "core.st2.webhook",                          |
|             |     "ref": "core.9c059214-c1a9-43d9-9e3a-18635b63261e",  |
|             |     "parameters": {                                      |
|             |         "url": "webhook2slack"                           |
|             |     }                                                    |
|             | }                                                        |
| type        | {                                                        |
|             |     "ref": "standard",                                   |
|             |     "parameters": {}                                     |
|             | }                                                        |
| uid         | rule:test-pack:st2-webhook-to-slack                      |
+-------------+----------------------------------------------------------+
```

```/opt/stackstorm/packs/test-pack/rules/st2-webhook-to-slack.yaml
name: "st2-webhook-to-slack"
pack: "test-pack"
description: "post message to slack from webhook."
enabled: true

trigger:
  type: "core.st2.webhook"
  parameters:
    url: "webhook2slack"

criteria:
  trigger.body.event_name:
    pattern: "post_message"
    type: "equals"

action:
  ref: "slack.post_message"
  parameters:
    message: "{{trigger.body.message}}"
    channel: "{{trigger.body.channel}}"
    username: "st2"
    icon_emoji: ":ok_woman:"
    webhook_url: "<webhook_url>"
```

これで `https://localhost/api/v1/webhooks/webhook2slack` をトリガーにするRuleができました

`criteria` は、Ruleにおける条件です。今回は、webhookのbodyにevent_nameという項目があり、その項目の中身がpost_messageの場合イベントが発火します。また、メッセージやチャンネルの情報はwebhookから受け取った情報を利用します。

StackStormのwebhookを利用するには、StackStormへのログイン情報が必要になります。今回は、st2 authコマンドを利用して事前にtokenを取得しておき、その値を利用します。

```shell-session
root@853b4719244d:/# st2 auth st2admin -p Ch@ngeMe
+----------+----------------------------------+
| Property | Value                            |
+----------+----------------------------------+
| user     | st2admin                         |
| token    | 90190020e2a94040a38c551132ab86c1 |
| expiry   | 2018-12-16T14:29:35.455141Z      |
+----------+----------------------------------+
root@853b4719244d:/# curl -k https://localhost/api/v1/webhooks/webhook2slack -d '{"message": "test message from webhook", "event_name": "post_message", "channel": "<channel_name>"}' -H 'Content-Type: application/json' -H 'X-Auth-Token: 90190020e2a94040a38c551132ab86c1'
{
    "event_name": "post_message",
    "message": "test message from webhook",
    "channel": "<channel_name>"
```

<img width="901" alt="スクリーンショット 2018-12-15 23.40.29.png" src="https://qiita-image-store.s3.amazonaws.com/0/63136/e37065ed-8166-52ca-543e-6179fa0d8d8e.png">

とまあこんな具合でwebhookのイベントをトリガーとしてslackへ投稿することができました。

# まとめ

StackStormの使い方について紹介しました。StackStormでは、イベントハンドリングによる自動化を実現することが可能です。また、今回は紹介しませんでしたが、ワークフローを用いればより高度なことを実現することも可能になります。

機会があれば、Workflowの作り方やHA構成でのStackStormの構築など紹介できればと思います。