Azureにデプロイしたマイクラ鯖コンテナをdiscordから起動・停止制御をする試行
目標のイメージ
- Azure上で動作しているマイクラ鯖がある(Done)
- 起動・停止時にDiscordに通知するBOTも仕込まれている
- discordのSlash Command(鯖専用)を使ってコンテナを操作する
現在身内discord鯖では、Azure Container Intsances上にdockerデプロイされたマイクラ鯖を使って
マルチプレイをしている
ここら辺は過去記事や過去scrapを参考にしていただきたい
背景
Container Instancesにデプロイされたコンテナを操作するには、Az-CLIもしくはAzure Portalが必要で、
マイクラ鯖を起動するときは毎回鯖缶に問い合わせてもらう必要があった
起動するときはいいのだが、鯖が不可で落ちてしまったときに再起動してもらうのもお願いしていて、
ちょっと申し訳なかったので
いちいち鯖缶がPortalで作業しなくていいように、discordから操作できないかを検討していた
起動処理は以下の順序で実行される想定をしている
- discordからSlashCommandを発行
- コールバック登録されているAzure FunctionsへPOSTされる
- optionの値を見てstartコマンドと判断する
- ACIの起動コマンドをたたく
- ACIのマイクラ鯖が起動する
- DiscordSRVによって起動したメッセージがdiscordに届く
ACIのマイクラ鯖はStorageAccountのFileShareにマウントされている(復習)
ローカルのazure functionsからACIの起動・停止コマンドを送ることには成功した
ACIの操作にはaz-cliやREST API、ポータルから行えるが
今回はPowerShellのInvoke-AzResourceAction
コマンドレットによる操作をすることにした
参考は以下のサイト
Slash コマンドはdiscordのDeveloperポータルからアプリを作った後に、
Slashコマンドを送るBotの設定と、適切な権限を付与したうえで鯖に追加する必要がある
詳しくは以下の記事
この記事ではSlashコマンドの登録をしてくれるjavascriptのコードを書いてくれている、助かりました
現在の進捗は
- ローカルのfunctionsからaciの操作が可能になった
- slashコマンドのinteractionの書式に対応して起動・停止・再起動命令を出すpowershellコードを書いた
- discordの鯖にslashコマンドを登録できた
次の作業は
- functionsをAzureにデプロイする
- このときに認証情報を
Connect-AzAccount -Identify
でできるようにポータルで設定
- このときに認証情報を
- 実際に運用する鯖にSlashコマンドを登録
Managed IDという仕組みを使うことで、Azureのリソース自体にID情報を埋め込むことができるらしい
これをすると、Connect-AzAccount -Identity
で資格情報を取得できる
参考:
無事AzureFunctionsのデプロイに成功し、鯖にもスラッシュコマンドを設定できたが、
なぜかdiscrodのポータルからinteractionのエンドポイントを設定できない
ちゃんと常時{type:1}
を返しているはずなんだけど、なぜか認証できないと言われる
もしかして常時返していちゃダメなのかな
これっぽいな
jsで実装してる例も見つけた
正直なところ、discordのエンドポイント認証をpowershellで処理できる自信がないので、
ここはtypescriptで作った別のfunctionsに認証部分を任せようと思っている
そういえば、invoke-AzResourceAction
コマンドレットでなぜかACIが起動しない問題が実はあった
ちょっとググってみたらこんな記事に出会ったので、あとで試してみたい
うそみたいにうまくいくようになった
デプロイした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しろよ的なことが書いてあったのでやってみたら、
無事インタラクションが実行できた!
なのでレスポンス処理はこうなった
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で書き直せば二つの関数を統合できそう
せっかくだからDurable Functionsとか使ってみたい感じもする
認証をするFunctionと起動・停止を制御するFunctionを分けてオーケストレーションするイメージで