MastodonのタイムラインにNSFWな画像が流れてきたら警告するBotを作った

はじめに

以前、以下の記事で作った自動NSFWとか

gamelinks007.hatenablog.com

以下の自動NSFW検知Botとかを改良したものを新たに作ってみました。

github.com

以前までのBotの問題点

以前までのBotGoogle Cloud Vision APIを使っていたため中々にコストが高くついてました......。 また誤検知なども意外と多く、実運用に向かなさそうだったり......。

そのため数年前に停止していました。

なんでまた作ったのか?

最近、Twitterの買収から始まる一連の騒動でFediverseにユーザーが増えてきたのがきっかけ。 人が増えたことで連合タイムラインとかに、NSFWな画像が流れてくる可能性は増しましたし......。

また僕の運営しているCreatodonでもユーザーが増えてきていることもきっかけの一つです。 現状は人が一気に増えたこともあり新規登録は止めていますが、招待リンク経由での登録はできる状態なので悪意あるユーザーが入ってくる可能性は0ではない状況です。

で、そういう事態が起きる前に自動的に検知できる仕組みは導入したかったので今回再チャレンジしてみた感じです。

作ったもの

作ったBotソースコードとしては以下です。

github.com

opennsfw2とMastodon.pyを使って実装しています。

使い方など

まずBot用のアカウントを作成して、アクセストークンを生成。

次に、ソースコードを稼働させるサーバーなどへCloneします。

git clone https://github.com/S-H-GAMELINKS/auto-nsfw-checker.git

Cloneしてきたソースコードディレクトリへ移動し、.envを作成します

cd auto-nsfw-checker
cp .env.sample .env

.envを編集して利用するサーバーのURLやアクセストークンを追記します。

API_BASE_URL=<サーバーのURL>
ACCESS_TOKEN=<アクセストークン>

最後に以下のようなserviceを作成し、

[Unit]
Description=auto-nsfw-checker
After=network.target

[Service]
Type=simple
User=mastodon
WorkingDirectory=/home/user/auto-nsfw-checker
ExecStart=/home/user/.pyenv/shims/python main.py
TimeoutSec=15
Restart=always

[Install]
WantedBy=multi-user.target

以下のコマンドでserviceを起動すればOKです。

sudo systemctl start auto-nsfw-checker

今後

連合タイムラインに流れてくるNSFWな画像を検知できる仕組みとか作りたいと思いますねー。

2022年振り返り

お仕事

 

お仕事では相変わらずCREとしてGoとReact/TypeScriptでコードを書きつつ、顧客満足度改善につながる改修とか進めてました。

 

ただ去年と違うのはCREチームのリーダーとしてタスクのアサインや他部署との交渉なども行うようになった点ですね。

なので、やることも増え責任も増してきた感じです。

 

なので、仕事では「いかにうまくリソースの配分をするか」「やらないことを決めるか」とかを意識してました。

 

あとは既存のバグ対応とかライブラリアップデート対応したり、開発環境改善とかを回してた感じですね。

大体その辺の内容とかは以下の記事とかにまとめてます。

 

gamelinks007.hatenablog.com

gamelinks007.hatenablog.com

gamelinks007.hatenablog.com

 

 

趣味

大好きなHALOシリーズばっかりをプレイしている一年でしたね......。

なので新作ゲームもいくつか買ってますが、基本的に積んだ状態になってるのであまりよくない......。

 

その甲斐あってか今年ようやくHALOシリーズの Legendaryキャンペーンを三時間以内にクリアできるようになりました。

なので今後はHALOシリーズの各作品でLegendaryキャンペーン三時間以内クリアを目指したいですね。

今のところはHALO2HALO3 ODSTは三時間以内でクリアできたので、HALO3あたりからチャレンジしたいですね。

 

あとLegendaryキャンペーンをクリアしてないのはHALO5だけになったのでそこも進めていきたい(多分、この年末年始でクリアできそう)

 

OSS周り

去年に引き続き、Rubyに色々とPRを投げていました。

github.com

 

個人的には以下のpars.yの文法周りのPRとか出せれたのは良かったです。

github.com

github.com

github.com

github.com

 

あとは仕事で使うライブラリにもサンプルコード投げたり、API追加したりしてました。

 

github.com

 

来年は引き続きRubyにはPRを投げていきつつ、ほかのOSSとかにもコントリビューションしていきたいですね。

あと成れるならRubyコミッタとかなってみたいですが、今んところ目立った機能追加とかできてないので、その辺来年頑張りたいですね。

 

Creatodon

今年はFediverseへ参加する人が多い年だったなぁと思います(とくにTwitter側のあれこれに影響された感じで)

Creatodonのほうでもご新規さんが増えつつあったりして、Fediverseへの参加が増えているのを実感してます。

 

鯖缶としてはアクティブなユーザー数も増えてきたのでサーバーのメンテナンス作業とかを簡略化するようにしたり、アップデート対応とかもやるようになりました

 

 

gamelinks007.hatenablog.com

 

また以下のように新機能の追加とかも進めているので、無理のない範囲で継続していきたいですね。

 

gamelinks007.hatenablog.com

 

あと余談なんですが、CreatodonはRuby 3.2.0 with YJITで動かしていたりします。

 

gamelinks007.net

 

今のところ体感ではだいぶパフォーマンスが良くなっているのを感じています。

来年はパフォーマンス計測したりしたいなぁと考えてます。

 

 

Creatodonに新機能を追加した話

追加した機能

Creatodonに登録されているカスタム絵文字の表示サイズ変更と公開範囲「にゃーん」を新しい機能として追加しています。

実際の挙動などは以下で確認できます。

 

gamelinks007.net

 

カスタム絵文字の表示サイズ変更

Creatodonのユーザーさんから「カスタム絵文字が小さいから見えにくい」という声があったので改修してみました。

 

改修内容などは以下のPRを見て頂ければと思います(突貫でやったので非常に実装が汚い......)

github.com

github.com

github.com

github.com

 

この改修より以前にCreatodonでは何人かのアクティブなユーザーさんにカスタム絵文字を自由に登録できるロールを付与していたので、この機能をリリースした直後はLTLが結構にぎやかになって面白かったですねー。

 

今後はLTL向けのリアクションとしてもカスタム絵文字を利用できそうなのもうれしいですね。

 

公開範囲「にゃーん」

fこの機能は個人的に欲しかったので実装してみました。

 

以前からちょっとした愚痴や公開ステータスでつぶやくのは良くないかなと思った時フォロワー限定などでつぶやいていたんですが、「この愚痴とか見た人への影響減らせないかなぁ」と思うことがままありました。

特に、ロバ耳のような感じでつぶやけるけど何かしらでマスキングされてるといいよなぁと。

 

で、何の気なしにMastodonソースコード読んでたらサクッといい感じで実装できそうだったので実装してみた感じですね。

 

実際の実装などは以下を見ていただければと思います。

github.com

github.com

 

意外とうれしいのが外部からは普通に「にゃーん」とつぶやいているのかが判断できないので投稿を見ている人に負担がない点ですねー

 

Creatodonをv3.4.6からv4.0.2へのアップデートした

はじめに

これは僕が管理しているCreatodonというMastodonのサーバーのアップデートを行ったときの備忘録になります。 一部正確な情報ではないかもしれませんのでご了承ください。

経緯

前回のCreatodonアップデートの記事から一年とちょっと経ち、Fediverse界隈がにわかににぎやかになってきてました。 特にTwitterの買収騒動でのFediverseへの移住(おもにMastodonなどへの)が増え、連合するサーバーの数も徐々に増え始めており「そろそろメンテナンスしないとな」と思い、ガっとアップデートを進めることにしました。

環境

なお、Creatodonの環境としては以下のようになっています - Ubuntu 20.04 - Ruby 2.6 - Node.js v12 - PostgreSQL 12 - Redis 5

この環境でDockerを使わずに運用しています。

やったこと

リリースノートの確認

まずはv3.4.6以降のリリースノートを確認することにしました。

v3.4.6からv4.0.2までのリリースノート確認していくと、v3.5.2のリリースノートの部分にDBへのマイグレーションについての記載があるのが確認できました

github.com

それ以外の部分ではDBへのマイグレーションについての記載はなく、アップデートに関しての大きな注意点になりそうな箇所はないことがわかりました。 あとはNode.jsの最低バージョンなどの変更点があることを確認。

リリースノートとアップデート前のCreatodonの環境などを鑑みると以下の手順でアップデートで必要そうだとわかりました。 - Rubyなどのバージョンを上げる - DBへのマイグレーションをリリースノート記載の通りに対応

DBのバックアップ

DBへのマイグレーションを実行するので最悪の場合に備えてバックアップを取得します。

バックアップ自体は pg_dumpを使って以下のように実行しました。

sudo su - postgres
pg_dump mastodon > dump.sql

Rubyのバージョンアップ

次に使用するRubyのバージョンを上げました。

Mastodonの場合は.ruby-versionに記載されているバージョンのRubyを使うので、そちらを以下のコミットで修正。

github.com

ここでGemfileをちゃんと確認しておらず、3.1.2が使えると思い込んでしまったのが良くなかったですね......。

このあとbundle installを実行した際にエラーになり気付いて、以下のコミットで修正しています。

github.com

この段階で3.0.4がインストールされていない場合は以下のコマンドでインストールしましょう。

rbenv install 3.0.4

Node.jsのバージョンアップ

次にNode.jsのバージョンを上げました。

一旦はnvmを使ってバージョンを上げたんですが、Streaming周りが不安定になっていました。

そこで以下の記事を参考にnをインストールし、Node.jsのバージョンをv18系列まで上げました。

zenn.dev

マイグレーションの実行

RubyやNode.jsのアップデートなどが完了したのでマイグレーションの実行を行いました。 おそらく今回のアップデートで一番気を使った箇所ですね。

リリースノートの内容に合わせて、まずはSKIP_POST_DEPLOYMENT_MIGRATIONS=trueを付けた状態でマイグレーションを実行しました。

SKIP_POST_DEPLOYMENT_MIGRATIONS=true RAILS_ENV=production bundle exec rails db:migrate

かなり長いマイグレーションなので、コーヒーでも飲みながらのんびり待つと良いかもです。

マイグレーションが完了すると次にアセットのビルドを行います。

RAILS_ENV=production bundle exec rails assets:precompile

が、アセットのビルドは失敗。 原因としてはCreatodonで導入している独自テーマのscss周りのimportが最新のMastodonのテーマとそろっていないからでした。

そこで随時ビルドしつつ不要なimportを削除して回り、最終的にビルドできるようにしました。 一旦はビルドできてますが、独自テーマ自体のレイアウト調整などはまだ完了していないので今後折を見て直しておこうと思います。

アセットのビルドが完了したので以下のコマンドでMastodonの各サービスを再起動します。

sudo systemctl restart mastodon-web
sudo systemctl restart mastodon-streaming
sudo systemctl restart mastodon-sidekiq

各サービスが再起動した後、以下のコマンドで再度マイグレーションを実行します。

RAILS_ENV=production bundle exec rails db:migrate

これでマイグレーション周りの対応は完了です。

Sidekiq周りの設定修正

次に、Sidekiq周りの設定を修正します。

元々Creatodonでは以下のような設定でSidekiqを起動していました。

bundle exec sidekiq -c 5 -q default -q mailers -q pull -q push -q scheduler

ただこのままだとv4.0.0以降ではうまく連合TLなどが動作しなくなる可能性があります。

ここら辺はアップデートの作業中に気付いたんですが、v4.0.0のリリースノートにingressキューを追加する必要がある旨の記載がありました。

Requires review of Sidekiq queues in some setups (new queue: ingress)

github.com

なので以下のようにSidekiqの起動処理を修正して対応しました。

bundle exec sidekiq -c 5 -q default -q mailers -q pull -q push -q ingress -q scheduler

ここまでの対応でアップデートは完了です。

追加の対応

メール配送&ポート周りの設定修正

CreatodonではMailGunを使ってメールの配送などをしていたんですが、たまに配送できていないことがあったので対応しました。

具体的にはメール送信で利用しているMailGunのAPIキーなどを新しいものに差し替え。 またその最中に誤ってポートを閉じたりしたので、その辺も修正しました。

DeepLでの翻訳機能の導入

追加でDeepLでの翻訳機能なども導入。

以下の記事を参考にDeepL APIの認証キーを発行。

qiita.com

あとは .env.productionに以下のように発行した認証キーを環境変数として追加し、各サービスを再起動しました。

DEEPL_API_KEY=<発行した認証キー>

ElasticSearchの導入

Creatodonではサーバーの移管前まではElasticSearchを使っていたんですが、移行に際して廃止していました。 今回のアップデートに合わせて再度有効にしました。

公式の以下のドキュメントを参考にElasticSearchをインストールしました。

docs.joinmastodon.org

元々導入していたので.env.productionにはElasticSearch用のポート設定などはあり、その辺は特に設定せずに済みました。

あとは以下のコマンドを実行して各サービスを再起動し、

sudo systemctl restart mastodon-sidekiq
sudo systemctl reload mastodon-web

以下のコマンドで検索用のインデックスを生成してElasticSearchの導入は完了です。

RAILS_ENV=production bin/tootctl search deploy

おわりに

色々と立て込んでいてアップデートができていなかったので追加対応も含めると結構な作業量になってしまった感。 今後はできるだけこまめに追従していきたいですね......。

gqlgenでのgoroutine leakを解消した

はじめに

この記事は、仕事で使っているgqlgenというGo製のGraphQLサーバーを生成するツールで起きていたgoroutine leakを解消した時のまとめです

 

起きたこと

GraphQLのSubscriptionを使った機能(リアルタイム性のある機能)をリリースした直後からメモリリークで本番環境のコンテナが死ぬという現象が起きました。

 

何人かでログを漁ってみたところ、どうやらgoroutineが正常に終了していないままleakしているらしいことが判明(ログなどで本番環境のgoroutine数が異様に増えていたため)

 

やったこと

まず計測用に埋め込んでいたpprofやPrometheusのエンドポイントを有効にし、ローカル環境下でgoroutineが増大しているかを計測しました

 

計測には以下のライブラリを使用しました。

github.com

github.com

 

 

すると、Subscriptionを使った機能を使用した際に、goroutineが増加しているのが確認できました。

 

次に実装の問題なのかライブラリの問題なのかを切り分けることにし、実装側の調査を進めました。

新規で追加されているコード内で数か所goroutineを使っている箇所はありましたが、計測された結果ほど増加している感じではなかったためgqlgen側に問題がありそうと判断。

 

そこでIssueを漁ってみたところ以下のIssueが見つかりました。

github.com

 

GraphQLのSubscriptionは実装としてWebsocketを使っており、gqlgenではWebsocketのコネクションが接続された際にgoroutineを生成しているようです。

 

が、Websocketのコネクションが切れた際に生成されたgorutineが正しく終了しないコードになっているため最終的にgorutine leakが発生するということのようです。

 

修正状況などを確認すると以下のPRで修正されており、v0.17.6で修正が反映されえていることが判明しました

github.com

 

github.com

 

そこで現在使用しているgqlgenのバージョンを確認し、アップデート対応を行いました。

これで大幅にgoroutine数が削減され、leakしなくなりました。

 

解消したが......

が、その後数日様子を見ていると緩やかではありますがメモリの使用量が増大しており、コンテナが落ちていることが判明。

 

ローカル環境下でpprofを使い、gorutine数を確認してみたところ僅かにgoroutineが増えていました。

 

ただ今回はどのAPIを呼び出しても増加しており、その増加量も数件程度。

なので、ライブラリ側というよりはこちらで実装した側の問題の可能性がありそうでした。

 

pprofとログ埋め込みを併用しつつ、実装を確認したところ別で使用しているRedisクライアントがgoroutineを生成していることがわかりました。

さらに実装を追うと、ミドルウェア層にある実装で都度Redisクライアントが生成されており、その影響でgorutine数が増大しているようでした。

 

そこでRedisクライアントを一度だけ生成するように実装を変え、最終的な決着をつけました

おわりに

 

goroutine leakやメモリリークなどはまず計測することから始めるとよいなと再認識。

あとpprofでのgorutine数確認とかが体験が非常に良かった

 

Hamada.rb #34 & Ruby Hacking Challenge in Hamada.rb を開催した

毎月定例のHamada.rbとRuby Hacking Challenge in Hamada.rbを9/20に開催してきた

この記事はその振り返りです

 

Rubyのビルド

今回Ruby Hacking Challenge初参加の方が何人おり、rubyhackchallengeのチュートリアル進めてて、依存関係でビルド出来なかったので対応したりしてました。


で、ビルド出来なかった原因はlibyaml(3.2.0-preview1以降のmasterではlibyamlが同梱されなくなっており、libyamlを入れないとビルドできない)だったので、libyamlをインストールして無事ビルドできました。やったね!

 

ちなみにその後イベント中にPR出されてて非常に良かった!

github.com

 

CRubyのことを知るファーストステップ

「CRubyの実装とかを知るにあたってどんなステップで行けばいいか?」という質問が出たので僕がどう知っていったかを話してました。

 

僕の場合は「Rubyソースコード完全解説」=> 「言語のしくみ」=>「Rubyのしくみ」の順でCRubyの実装とかプログラミング言語の実装とかを知っていった感じでした。

 

i.loveruby.net

www.amazon.co.jp

www.amazon.co.jp

 

あとHamada.rbの時には忘れてたんですが、CRubyで公開されているC APIとか使って

C++の拡張ライブラリとか書いてたみたいです

 

github.com

 

なので、その時にC APIなどに触れつつCRuby内部のコードを知れたのはあるかも

parse.yの話

「parse.yは魔界」とか拡張BNF記法の話とかしつつ、「parse.yの文法パターンとかはRubyソースコード完全解説を読むと良いよ」とオススメしたりしてました

 

あとは最近投げたPRとかを例に挙げつつ、軽くBNF記法の話とかしました

github.com

 

Rubyでのベンチマーク

CRuby向けのPRの話でベンチマークとかの話が出たので

 

make benchmark/benchmark.yml -e BENCH_RUBY="../install/bin/ruby" -e COMPARE_RUBY="~/.rbenv/shims/ruby"

 

とかでベンチマーク取れるのを共有したりしてました。

 

あと、ベンチマーク結果の原因について推察したりしてました(が、結局原因らしきものは明確に突き止められなかったので残念......)

 

その他CRuby関連の情報収集など

CRuby関連の情報収集とかで助かるブログとか紹介しました

github.com

 

ruby-trunk-changes.hatenablog.com

 

secret-garden.hatenablog.com

 

この辺日頃から見ておくと色々知見が得られるのでオススメ

 

次回

次回は10/18(火)にやります!!

hamadarb.connpass.com

hamadarb.connpass.com

 

そういえば

Hamada.rb始めてから丸三年たってました!

hamadarb.connpass.com

 

Ruby Hacking Challenge in Hamada.rbももうすぐ三年経ちそう!

hamadarb.connpass.com

RubyのアプリケーションをPR単位でテストできるようにしてみた

はじめに

RubyのアプリケーションをPR単位でテストできるようにしてみた記事です。
仕事でRubyを使っていて「PR単位で気軽にテスト環境を作りたいなぁ」という人向けの内容です。

背景など

元々仕事で

  • 複数のプロジェクトが同時並行で進んでいる
  • 単一のステージング環境を使い、テストをしている
  • そのためプロジェクトがリリースされるまで、別のプロジェクトの機能がステージング環境でテストできない

という状況が発生しており、新機能リリース時のリードタイムが発生しやすいという課題を抱えていました。
また僕自身も直近ではリリースマネージャ的な動きをしていたこともあり、この状況をどうにかしたいという思いがありました。

そんな時、メルカリから公開されたKubetempuraの「PR単位でテスト環境を構築する」という考えを知り、「この方法で何とかできないだろうか?」と対応方法を考え始めた感じですね

engineering.mercari.com

ただ、

  • 僕のk8sなどへのまだ理解が浅いこと
  • 現状の環境に追加するにはKubetempuraは規模が大きすぎるかもしれない
  • 実際に運用が回るのかスモールスタートで検証したい

というところもあり、Kubetempura導入までは至っていませんでした。

そんな中、個人的な興味から試していたAppRunnerがテスト環境として使えるのではないかと思い始め

gamelinks007.hatenablog.com

色々と試行錯誤してみたところ、以下のようにPR単位でテスト環境を構築できるものができました。

github.com

作ったもの

作ったものとしては以下です。

github.com

シンプルなSinatraのアプリケーションをTerraform/GitHub Actionsを使うことでAWS上にテスト環境を自動で構築できるようにしています。
まだREADMEなどはまだ整備できていませんが、ForkしてAWS周りの認証キーなどをセットすれば動かせると思います。

やったこと

先日公開した以下の記事で作成したTerraformをもとに最低限(AppRunner/IAM/ECRまわり)の環境を構築できるコードを書き、

github.com

以下のようなシンプルなSinatraのWebアプリケーションを作成しています。
(RubySinatraを使っているのは一番使い慣れた言語であることと、テストのための環境構築が楽だったためです)

require 'sinatra'

configure do
  set :bind, '0.0.0.0'
end

get '/' do
  'Hello world!'
end

get '/halo_infinite' do
  'HALO Infinite'
end

get '/halo_2' do
  'HALO 2'
end

それを以下のようにPRが作成されたタイミングでテスト環境を構築するようにしました。

name: 'Create staging with pull request'

on:
  pull_request:
    types: [opened, synchronize]

jobs:
  terraform:
    name: 'Terraform'
    runs-on: ubuntu-latest

    defaults:
      run:
        shell: bash

    steps:
      - name: Checkout
        uses: actions/checkout@v3

      - name: Configure aws credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ${{ secrets.AWS_REGION }}

      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v1
        with:
          terraform_version: 1.1.9

      - name: Init
        run: terraform init
        working-directory: infra
      
      - name: Get
        run: terraform get
        working-directory: infra

      - name: Apply
        id: apply
        run: terraform apply -auto-approve -var 'pr_number=${{ github.event.number }}' -var 'commit_hash=${{ github.sha }}' -var 'region=${{ secrets.AWS_REGION }}'
        working-directory: infra
        continue-on-error: true

      - name: Login to Amazon ECR
        if: ${{ steps.apply.outcome == 'failure' }}
        uses: aws-actions/amazon-ecr-login@v1

      - name: Push Container(Only synchronize pull request)
        if: ${{ steps.apply.outcome == 'failure' }}
        run: |
          docker build -t ${{ secrets.AWS_ID }}.dkr.ecr.${{ secrets.AWS_REGION }}.amazonaws.com/pullrequest-${{ github.event.number }}:latest .
          docker push ${{ secrets.AWS_ID }}.dkr.ecr.${{ secrets.AWS_REGION }}.amazonaws.com/pullrequest-${{ github.event.number }}:latest

初回のみterraform applyが成功し、二回目以降はすでに同じ名前のリソースが生成されているので失敗するようになっています。
またPRの情報(PRやIssueの番号)などを使い、PRごとに環境を作れるようにしています。

二回目以降(つまり追加でPRにコミットがpushされた場合は以下のように失敗した結果をもとにECRへ新しいコンテナをビルドしてプッシュするようにしています。

      - name: Apply
        id: apply
        run: terraform apply -auto-approve -var 'pr_number=${{ github.event.number }}' -var 'commit_hash=${{ github.sha }}' -var 'region=${{ secrets.AWS_REGION }}'
        working-directory: infra
        continue-on-error: true

      - name: Login to Amazon ECR
        if: ${{ steps.apply.outcome == 'failure' }}
        uses: aws-actions/amazon-ecr-login@v1

      - name: Push Container(Only synchronize pull request)
        if: ${{ steps.apply.outcome == 'failure' }}
        run: |
          docker build -t ${{ secrets.AWS_ID }}.dkr.ecr.${{ secrets.AWS_REGION }}.amazonaws.com/pullrequest-${{ github.event.number }}:latest .
          docker push ${{ secrets.AWS_ID }}.dkr.ecr.${{ secrets.AWS_REGION }}.amazonaws.com/pullrequest-${{ github.event.number }}:latest

正直、この辺はもう少しやりようがあるとは思いますね……。
とりあえず、動くものを作りたかったので一旦はこれでOKとしてます(何かいい知見ご存じの方いればご教授いただければ幸いです)

またPRがマージされた(またはクローズされた)場合は以下のように作成されたリソースをAWS CLIで削除するようにしています。

name: 'Delete staging with pull request'

on:
  pull_request:
    types: [closed]

jobs:
  terraform:
    name: 'Terraform'
    runs-on: ubuntu-latest

    defaults:
      run:
        shell: bash

    steps:
      - name: Checkout
        uses: actions/checkout@v3

      - name: Configure aws credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ${{ secrets.AWS_REGION }}

      - name: Get AppRunner Service ARN
        id: apprunner-service-arn
        run: |
          service_arn=$(aws apprunner list-services --query "ServiceSummaryList[?ServiceName=='pullrequest-${{ github.event.number }}'].ServiceArn | [0]")
          echo "::set-output name=stdout::$service_arn"
      - name: Delete AppRunner Service
        run: aws apprunner delete-service --service-arn ${{ steps.apprunner-service-arn.outputs.stdout }}

      - name: Delete ECR repository
        run: aws ecr delete-repository --repository-name pullrequest-${{ github.event.number }} --force

      - name: Get Role attachment policy
        id: role-attachment-policy
        run: |
          role_attachment_policy=$(aws iam list-attached-role-policies --role-name pullrequest-4-service-role --query "AttachedPolicies[0].PolicyArn")
          echo "::set-output name=stdout::$role_attachment_policy"
      - name: Dettach policy
        run: aws iam detach-role-policy --role-name pullrequest-${{ github.event.number }}-service-role --policy-arn ${{ steps.role-attachment-policy.outputs.stdout }}

      - name: Delete role
        run: aws iam delete-role --role-name pullrequest-${{ github.event.number }}-service-role

これのおかげでテスト用に作成された環境を削除し忘れることが回避できています。
この辺ももう少しきれいにしたい感じはありますね……。

今後

今回検証用としてPR単位でのテスト環境自動構築をやってみましたが、大分いい感じではありますね。
なので、一旦仕事のほうでつかえないか導入してみて検証を進めていきたいと思います。

今後課題になりそうなのは実用的なWebアプリケーションなどではDBとの接続(RDSやVPCの設定)が必要になりそうなので、そのあたりをどうするかですね。
また、PRにコミットが積まれるたびにtarraform applyされているのは非常に微妙なのでそのあたりもどうにかしたいですね。

さらに、フロントエンドとバックエンドが分かれているような場合ではAPI側をどう呼び出すのかなどが課題になりそうです。