😀

GCP CloudRunにSlack Bolt(TypeScriptで)を導入してSlackBotを作成する

2022/11/28に公開

皆さんCloudRunを使っていますか?
素早くDocker環境に秒単位でデプロイができるこのサービスに私はメロメロです。

で本日はCloudRunを使ってSlackBot (Bolt)を導入して簡単なスラッシュコマンドを作成してみたい。と思います。

完成品は /ping コマンドを発行したらbotから pong が返ってくるものを作成します

1.png

事前に必要なもの

使うもの

SlackBotにかかる料金についての試算目安

  • Cloud Run
  • Cloud Build

について説明します。この他にStorageの料金等細かい部分が発生しますが、省略させていただきます。

CloudRunの料金について

料金はシンプルです。

公式 GCP CloudRun 料金より
課金はメモリ(GB)秒単位で課金されていきますが、無料枠(月あたり)が

最初の 180,000 vCPU 秒は無料

ありますので50時間分は無料枠になります。

GB 秒単位が用いられています。GB 秒とは、たとえば 1 GB のインスタンスを 1 秒間実行することです。これは 256 MB のインスタンスを 4 秒間実行することとも言い換えられます。vCPU 秒 単位にも、同じ原則が当てはまります。

なので例えば256MBのメモリで平均で1秒でレスポンスを返すCloudRunを実行する場合は

180000(秒) / (256MB) * 4 / 60(分) / 60(時間) = 12.5(時間)

約12.5時間分の無料枠が使えます。
SlackBotでこの位の時間を使い切るのはeventなどで発言をpollingしない限りは難しいとおもいますのでほぼ無料枠で収まると思います。

Cloud Buildの料金について

CloudRunのコンテナイメージをBuildする時に使用します。
使わなくてもCloudRun自体は使えますが、便利なので是非使って下さい。

デフォルトのマシンタイプ(n1-standard-1 1vCPU, 3.75Memory)ならば
公式 GCP Cloud Build 料金より

1 日あたり最初の 120 ビルド分は無料です。

なので超頻繁にデプロイしない限りは無料でいけそうです。

SlackBotを作成する手順

ではSlackBotを作成してみましょう。

Slack Appsを作成する

Slack Bolt 入門ガイドから

  • アプリを作成する
  • トークンとアプリのインストール

迄を済まして下さい。

後で

export SLACK_SIGNING_SECRET=<your-signing-secret>

export SLACK_BOT_TOKEN=xoxb-<your-bot-token>

を使います。

Slack Bolt(ローカルを)動かすまで

とりあえずdocker-composeでBoltが動くところまでやります

Dockerfileを作成する

Dockerfile
FROM node:10.22.0-stretch

WORKDIR /app
ARG STAGE
ENV STAGE=${STAGE}
COPY package.json /app/package.json
COPY package-lock.json /app/package-lock.json
COPY tsconfig.json /app/tsconfig.json
COPY envs/.env.$STAGE /app/envs/.env.$STAGE
COPY src/ /app/src

RUN mkdir dest/ && npm i && npm run build
CMD ["npm", "start"]

続いてdocker-compose.yaml
(注意事項としてCloudRunのデフォルトのポート待受は8080です)

docker-compose.yaml
version: '3'
services:
  app:
    container_name: "bot"
    build:
      dockerfile: Dockerfile
      context: ./
      args:
        STAGE: dev
    volumes:
      - ./:/app
    ports:
      - "8080:8080"
    tty: true
    stdin_open: true

NodeJSの設定

pakage.jsonを作成します(TypeScriptで動作させるのでこんな感じで)

{
  "name": "{app name}",
  "version": "1.0.0",
  "description": "",
  "main": "dest/index.js",
  "scripts": {
    "start": "node dest/index.js",
    "build": "tsc",
    "build:watch": "tsc --watch"
  },
  .
  .
  "devDependencies": {
    "@types/node": "^13.11.1"
  },
  "dependencies": {
    "@slack/bolt": "^2.1.1",
    "dotenv": "^8.2.0",
    "typescript": "^3.8.3"
  },
  ...
}

インストール

$ npm i

続いてtsconfig.json(お好みがあると思うのでよしなに)

tsconfig.json
{
  "compilerOptions": {
    "target": "ES2018",
    "module": "commonjs",
    "sourceMap": true,
    "outDir": "./dest",
    "strict": true,
    "moduleResolution": "node",
    "noUnusedLocals": true
  },
  "typeRoots": [
    "node_modules/@types"
  ],
  "include": [
    "src/**/*.ts"
  ],
  "exclude": [
    "node_modeles"
  ],
  "compileOnSave": true,
  "types": [
    "src/types/*"
  ],
  "rules": {
    "eofline": true
  }
}

ディレクトリを作成する

/path/to/dir
$ mkdir dest src envs

SlackBotのSecret情報をdotenvに記載する

$ touch envs/.env.dev .gitignore
# secret情報なのでコミットしないためのおまじない
$ echo "envs/.env.dev" >> .gitignore

SLACK_SIGNING_SECRETとSLACK_BOT_TOKENはSlack Appページから取得できます。

envs/.env.dev
SLACK_SIGNING_SECRET=<your-signing-secret>
SLACK_BOT_TOKEN=<your-bot-token>
APP_LISTEN_PORT=8080

src/index.tsを書く

src/index.ts
import { App } from "@slack/bolt";
import * as dotenv from 'dotenv';
dotenv.config({ path: `envs/.env.${process.env.STAGE}` });

const app = new App({
  token: process.env.SLACK_BOT_TOKEN,
  signingSecret: process.env.SLACK_SIGNING_SECRET
});

(async () => {
  // Start your app
  await app.start(process.env.APP_LISTEN_PORT);

  console.log('⚡️ Bolt app is running!');
  app.command('/ping', async ({ command, ack, say, respond }) => {
    console.log(command);

    // コマンドリクエストを確認
    await ack();
    await say(`pong`);
  });
})();

ここまででこんな感じのディレクトリになっていると思います。

$ tree -L 2 -I 'node_modules'
.
├── Dockerfile
├── dest
├── docker-compose.yaml
├── envs
│   └── .env.dev
├── package.json
├── src
│   └── index.ts
└── tsconfig.json

Docker Build && Up

下記の様なメッセージが出ればOKです。

$ docker-compose build
$ docker-compose up

> node dest/index.js

⚡️ Bolt app is running!

Cloud Runにデプロイする

GCRにDocker Imageをpushする

Cloud Buildの設定を書いていきます。

<your app name> の部分はGCRのリポジトリになりますのでよしなに書き換えて下さい。

cloudbuild.yaml
steps:
- name: 'gcr.io/cloud-builders/docker'
  args: [ 'build', '-f', 'Dockerfile',
                   '-t','gcr.io/$PROJECT_ID/vqbot/${_STAGE}',
                   '--build-arg', 'STAGE=${_STAGE}',
                   '.'
        ]
substitutions:
  _STAGE: dev
images:
  - 'gcr.io/$PROJECT_ID/<your app name>/dev'

GCRにpush

  • <your GCP project> は自分のGCP projectを指定して下さい
export STAGE=dev
export PROJECT=<your GCP project>
gcloud builds submit \
  --project="$PROJECT" \
  --config cloudbuild.yaml

コンソールからCloud Buildの経過が出力されるので終了するのをお待ち下さい。

Cloud Runにデプロイ

  • <your GCP project> は自分のGCP projectを指定して下さい
  • <your service name> は Cloud Runの名前になります
  • --allow-unauthenticated はCloud RunのURLが公開されるオプションです
export STAGE=dev
export SERVICE_NAME=<your service name>
export PROJECT=<your GCP project>
gcloud run deploy $SERVICE_NAME \
  --project="$PROJECT" \
  --image="gcr.io/$PROJECT/$SERVICE_NAME/$STAGE" \
		--platform=managed \
		--region=asia-northeast1 \
		--allow-unauthenticated

GCP Console Cloud Runのページへいく

open https://console.cloud.google.com/run?hl=ja&project=$PROJECT

こんな感じの結果になります。
デプロイされたURLにアクセスして Cannot GET / が表示されることを確認してください(Slack BotのコマンドはPostで受け付けるのでboltの内部で動いているexrepssがレスポンスを返してくれれば良いです)

<img width="1266" alt="2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5564/29f677f1-c9a2-a514-4960-0e2f84c0a3d9.png">

SlackAppsでSlashCommandを作成する

これでやっと外部に公開されているURLが生成できたのでSlashCommandの設定ができます。

Slack Apps ページに再びいき Create New Command でコマンドを新規作成します

<img width="916" alt="4.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5564/a83a8865-073f-6c80-b86c-05b6e7664530.png">

Request URLに関する注意点

SlackBoltはデフォルトで https://{ドメイン}/slack/events というpathでSlackからの応答を待ちますので

{Cloud RunのURL}/slack/events

という形式で入力してください。

1.png

Botをチャンネルにinviteする

Botを実行したいチャンネルにinviteしないとレスポンスを返してくれませんのでBotを動作させたいSlack チャンネルで

/invite @{botの名前}

でinviteして下さい。

動作確認

スラッシュコマンドを実際に動かしてみたい。と思います

/ping

1.png

お疲れ様でした。

最後に

Boltのチュートリアルですとglitch等の紹介方法があるかと思いますが、CloudRunでもかなり高速に動作確認をすることができると思います。
Cloud Functionsに比べて自由度が高いCloud Runで素敵なBot生活をお楽しみいただけたら。と思っています。

Discussion