Closed29

Azureにデプロイしたマイクラ鯖コンテナをdiscordから起動・停止制御をする試行

にー兄さんにー兄さん

目標のイメージ

  • Azure上で動作しているマイクラ鯖がある(Done)
    • 起動・停止時にDiscordに通知するBOTも仕込まれている
  • discordのSlash Command(鯖専用)を使ってコンテナを操作する
にー兄さんにー兄さん

背景

Container Instancesにデプロイされたコンテナを操作するには、Az-CLIもしくはAzure Portalが必要で、
マイクラ鯖を起動するときは毎回鯖缶に問い合わせてもらう必要があった

起動するときはいいのだが、鯖が不可で落ちてしまったときに再起動してもらうのもお願いしていて、
ちょっと申し訳なかったので
いちいち鯖缶がPortalで作業しなくていいように、discordから操作できないかを検討していた

にー兄さんにー兄さん

起動処理は以下の順序で実行される想定をしている

  1. discordからSlashCommandを発行
  2. コールバック登録されているAzure FunctionsへPOSTされる
  3. optionの値を見てstartコマンドと判断する
  4. ACIの起動コマンドをたたく
  5. ACIのマイクラ鯖が起動する
  6. DiscordSRVによって起動したメッセージがdiscordに届く
にー兄さんにー兄さん

現在の進捗は

  • ローカルのfunctionsからaciの操作が可能になった
  • slashコマンドのinteractionの書式に対応して起動・停止・再起動命令を出すpowershellコードを書いた
  • discordの鯖にslashコマンドを登録できた

次の作業は

  • functionsをAzureにデプロイする
    • このときに認証情報をConnect-AzAccount -Identifyでできるようにポータルで設定
  • 実際に運用する鯖にSlashコマンドを登録
にー兄さんにー兄さん

無事AzureFunctionsのデプロイに成功し、鯖にもスラッシュコマンドを設定できたが、
なぜかdiscrodのポータルからinteractionのエンドポイントを設定できない
ちゃんと常時{type:1}を返しているはずなんだけど、なぜか認証できないと言われる

にー兄さんにー兄さん

正直なところ、discordのエンドポイント認証をpowershellで処理できる自信がないので、
ここはtypescriptで作った別のfunctionsに認証部分を任せようと思っている

にー兄さんにー兄さん

デプロイしたFunctionsから試したら、なんか権限がないよ的なエラーが出て困った
それで思い当たったのがManaged IDで、先ほどは閲覧者で作ったIDを所有者で作成し直したら無事起動できた

にー兄さんにー兄さん

認証部分のfunctionsを作るのに苦労していた
このようなことには二度と遭遇したくない......

にー兄さんにー兄さん

躓いて、色々コンソールデバッグなどをしてなんとかSlashコマンドのInteraction Endpointの設定ができた
関数自体は結局Typescriptで書いたりした、やっぱ書きやすい
signatureの検証の部分はこんな感じにしたら通った

  const sig = req.headers["x-signature-ed25519"];
  const time = req.headers["x-signature-timestamp"];
  const isValid = await verifyKey(req.rawBody, sig, time, CLIENT_PUBLIC_KEY);

  if (!isValid) {
    context.res = {
      status: 401,
      Headers: {},
      body: "",
    };
    return;
  }
にー兄さんにー兄さん

discordのSlashコマンドを使ってコンテナの起動と停止を確認できた
しかしなぜかdiscord上の表示だとinteractionに失敗してしまっているよう
個々だけ治ればもう完成

にー兄さんにー兄さん

ひとまず制御用のfunctionへリクエストは飛ばさずに、インタラクションだけ通すようにしたい

にー兄さんにー兄さん

ドキュメントをよく見てみたら、interactionに返答するのではなく、
interactionにPOSTしろよ的なことが書いてあったのでやってみたら、
無事インタラクションが実行できた!
https://discord.com/developers/docs/interactions/slash-commands#responding-to-an-interaction

にー兄さんにー兄さん

なのでレスポンス処理はこうなった

    const interaction_id = req.body.id;
    const interaction_token = req.body.token;

    const url = `https://discord.com/api/v8/interactions/${interaction_id}/${interaction_token}/callback`;
    request.post(url, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE,
        data: {
          content: `${req.body.data.options[0].value} command accepted.`,
        },
      }),
    });
にー兄さんにー兄さん

いろいろやった後に知ったんだけど、Azure SDK for JavaScriptなるものがあるらしい
これ、Azure REST APIと同じ構成になっていて、TSで書かれたnpmパッケージがあるみたいなので
Powershellで書いたFunctionsをTypeScriptで書き直せば二つの関数を統合できそう
https://www.npmjs.com/package/@azure/arm-containerinstance
https://github.com/Azure/azure-sdk-for-js/blob/main/sdk/containerinstance/arm-containerinstance/src/operations/containerGroups.ts

にー兄さんにー兄さん

せっかくだからDurable Functionsとか使ってみたい感じもする
認証をするFunctionと起動・停止を制御するFunctionを分けてオーケストレーションするイメージで

このスクラップは2021/07/22にクローズされました