あんパン

こしあん派

CDKでISUCONの練習環境を立ち上げる

最近、ISUCON*1の練習でもしようかな〜と思いAWSに環境を立ち上げることにした。これまでは有志で作られていたTerraformの定義をありがたく利用させてもらっていたのだが、AWSだしCDKで立ち上げられると便利だよな〜という思いがあり定義を書いた。

ここ2年くらい天下n品というチームでISUCONに参加していて、ときどき練習もしている。なのでこの中で使えるといいなと思って天下n品のorgに置いておいた。

以下のような手順で練習環境を立ち上げることができる。Ed25519の公開鍵をEC2に送るので、デプロイ前にrepoの直下に配置しておく。もし持っていなかったら作る必要がある。

$ git clone git@github.com:tenka-n-hin/cdk-isucon.git
$ cd cdk-isucon
$ npm i
$ cp $HOME/.ssh/id_ed25519.pub ./
$ npm run cdk deploy isucon9q -- --profile YOUR_AWS_PROFILE

各環境(ISUCON9予選とかISUCON10予選とか)がそれぞれひとつのStackとなっていて、何も指定せずにデプロイすると全環境を立ち上げようとするので注意。デプロイできるStackは cdk ls で確認できる。

$ npm run cdk ls

> cdk-isucon@0.1.0 cdk
> cdk ls

isucon10q
isucon11q
isucon8q
isucon9q

いまのところ8から11の予選に対応している。本戦の定義は単に面倒で追加してないだけ…… 12は公式にCloudFormationの定義が配られているのでそちらを使うのがよさそう。

なお、AMIはmatsuuさん(id:tmatsuu)が管理されているmatsuu/aws-isuconを利用している。いつもお世話になっています。自前のAMIに変えたい場合は cdk.json に定義があるのでこれを書き換えれば良い。

他にも cdk.json の中で実際の予選で使われたのと同等のインスタンスタイプを指定していたり、optionalでセキュリティグループの開放ポート設定を行えたりする。詳しくはコードを読んでください。TypeScriptだしコード量も少ないので読みやすいはず。

というわけで、8から11の予選を立ち上げたい場合はどうぞご利用ください。

ところでcdk-isuconを書いた結果、環境を立ち上げるので満足してしまってまだ練習はできていない。がんばろう。

*1:「ISUCON」は、LINE株式会社の商標または登録商標です。この記事は「ISUCON」を運営するLINE株式会社とは一切関係ありません。 https://isucon.net

MastodonのおひとりさまインスタンスをAmazon Lightsailに立てる

最近はあんまりTwitterを見ておらずMastodonにいる。もともと某サーバにいたけれど水が合わないと感じていて、せっかくならおひとりさまインスタンスを持っておくといいだろうなと思い、建立することにした。

立てる前にMasto.hostやHostdonなどのマネージドおひとりさまインスタンス建立サービスも眺めてみたが、運用で遊べる方がいいという気持ちが勝った。どうせやるなら面白そうなほうがいい。あと単純にActivityPubの勉強用途でも使えるんじゃないかという魂胆もある。どうせなんらか遊ぶ過程でMastodonのコードは眺めにいくことになると思う。

というわけで単純におひとりさまインスタンスが欲しいだけであればこれらのマネージドサービスを推す。安いし。ここからは道楽のコーナーです。

構成

普段からAWSにいろいろデプロイして遊んでいるので今回もAWS上に置くことにした。動かすための要件として考えたのは

  • 最低限遊べるくらいのスペックのインスタンス
  • ただしDBの運用はマネージドに逃がしたい(スナップショットとか勝手に撮ってほしい)

くらい。最初はEC2+RDSを検討していたけど、Amazon LightsailにもマネージドMySQL/PostgreSQLの機能があり、こちらの方が若干安いように見えたのでこれにした。最終的な構成は以下の通り。

  • Amazon Lightsail 5ドルプラン
    • 1GBメモリ
    • 1コアプロセッサ
    • 40GB SSD
  • Amazon Lightsail PostgreSQL 15ドル スタンダードプラン
    • 1GBメモリ
    • 1コアプロセッサ
    • 40GB SSD
  • 静的ファイル配信用CDNとしてCloudFront + S3

インストール

基本的には Installing from source - Mastodon documentation の手順通りに進めていく。のだが、いろいろとひっかかったので順を追って書いていきたい。といっても手順側は悪くなくて、自分が手順から逸脱しようとして転んだ話。

swap領域を作る

いきなり手順に書いてないことだけど、1GBメモリだとswap領域を作らないと足りないので初手で作っておく。

dd if=/dev/zero of=/swapvol bs=1M count=2048
chmod 600 /swapvol
mkswap /swapvol
swapon /swapvol
echo -e "/swapvol\tswap\tswap\tdefaults\t0\t0" >> /etc/fstab

上から順に実行してあげればswap領域を作ることができる。 swapon -s で様子を見てあげると丁寧。

Ruby, Node.jsのバージョン

なにごとも最新がいいでしょ、と思って最新版を入れたらやや詰まった。まずRubyについては.ruby-versionでバージョンが固定されているので手順通りのバージョンを指定しないと動かない。

Node.jsについては16が指定されているが18を入れても動く。ただしOpenSSLの互換エラーが出る(Ubuntu 20.04 LTSでインストールされるのは1.1.1fなため)ので rake assets:precompile を走らせる段で NODE_OPTIONS=--openssl-legacy-provider オプションを追加する必要がある(実際には rake mastodon:setup が内部で assets:precompile を実行しているので、ここで NODE_OPTIONS を足す)。

メモリが少ないため rake assets:precompile が落ちる

Node.jsのバージョンだけでなく、メモリが少ないだけでも rake assets:precompile が失敗する。rake mastodon:setup の実行時にも

The final step is compiling CSS/JS assets.
This may take a while and consume a lot of RAM.

と表示されていて丁寧だね……という気持ちになる*1。具体的にはヒープ領域が足りずNode.jsのプロセスがクラッシュする。このような場合は NODE_OPTIONS="--max-old-space-size=2048" のような形で指定することで回避できる。

つまり、Node.js 18環境かつメモリが少ない場合はsetupの際に以下のコマンドを発行すれば良い。

NODE_OPTIONS="--openssl-legacy-provider --max-old-space-size=2048" RAILS_ENV=production bundle exec rake mastodon:setup

S3にオブジェクトを書き込むことができない

これはややひっかかった。S3 bucketの中身はCloudFront経由で配信するので、bucketの設定でパブリックアクセスをすべてブロックしている。Mastodonの中でS3を操作するのに使っているPaperclipはデフォルトでpublic-readのパーミッションでオブジェクトを作成しにいこうとする。すると403 Forbiddenエラーが返ってきてしまう。回避するためにはMastodonのディレクトリ直下にある .env.productionS3_PERMISSION=private という値を追記する。これでオブジェクトを作れるようになる。

実際にどういう通信をしているのかな〜と確認するのに以下のIssueコメントが役に立った。

config/initializers/paperclip.rb の中で http_wire_trace: true をオプションとして渡すと通信の様子がログに出てくる。こういうのをproductionにおもむろに差しこんで遊べるのはおひとりさまインスタンスのいいところだと思う。

運用

そんなに難しいことはしていない。ざっくりヘルスチェックと、アップデート戦略を決めるくらい。

Mackerel導入

Ubuntuなので公式の手順に則ってmackerel-agentをインストールしただけ。個人のSlackにmastodonチャンネルを作ってホストがreachableかどうかと、外形監視だけ通知するように仕込んだ。Mastodonのヘルスチェックは /health にエンドポイントがあるのでこれを利用する。

加えていくつかのミドルウェアのメトリックも取得することにした。PostgreSQLのメトリックも見たいけどなぜか設定してなかったのでこれはあとでやる。

# Plugin for accesslog
[plugin.metrics.accesslog]
command = "mackerel-plugin-accesslog /var/log/nginx/access.log"

# Plugin for Linux
[plugin.metrics.linux]
command = "mackerel-plugin-linux"

# Plugin for Redis
[plugin.metrics.redis]
command = "mackerel-plugin-redis"

# Plugin for Sidekiq
[plugin.metrics.sidekiq]
command = "mackerel-plugin-sidekiq"

詳しくはこちら

mackerel.io

しばらく見ている限りは1GBのメモリだと常時70%-90%くらい使っていて暇がない感じがする。が、まあ普通に動いているので良いでしょうという感じ。だいたい30フォロー/フォロワーの規模感でこれなので、もっと増えたときにどうなるのかは分からない。

アップデート戦略

あんまり凝ったことはせず、ひとまず以下のようにした。

  • アップデート情報をキャッチできるようにRSSを購読する
  • 週1でアップデートデーを設けて必要だったら更新する

アップデート情報はRSSをSlackに流すようにした。都度SlackのIntegrationを変更するのは面倒なので、一旦大チェッカーで受けて、ひとつのフィードにまとめてからSlackに流すようにしている。

daichkr.hatelabo.jp

アップデートデーはTodoist上で管理していて、週1でリマインドされる。もちろん緊急性の高い更新がやってきた場合はアップデートデーによらず対応するつもりだけど、いまのところはNode.jsの軽微な更新しか来ていない。

引っ越し

もともと別のサーバにいたのでアカウントを引っ越した。MastodonにはActivityPubのMove Activityを利用した引っ越しの仕組みがあって、フォロワー側の実装がこれに対応している場合はそのフォロワーのフォロー先を強制的に新しいインスタンスに向けることができる。misskeyやgotosocial、Wildebeestなどでは現時点では対応しておらず、別途フォローをお願いすることになる。

docs.joinmastodon.org

投稿はエクスポートできるがインポートはできないので注意が必要。


というわけで https://social.masawada.me におります。いまのところ流速がおだやかで平和〜という状態。

Twitter APIの思い出

いいネタで面白かった。そういえばなんだかんだTwitter APIとの思い出があるなと思ったので自分も書く。

出会ったころの話

今のTwitterはOAuthでreadやwriteの権限をとってくるが、昔はもっと牧歌的でBASIC認証を利用してAPIにアクセスできた。当時書いたコードはもうどこにもないと思うけど、ちまちま情報を取得してうまく加工するツールみたいなのを作っていたと思う。

そんな折にBASIC認証が廃止されると聞いて、めんどくさいなあと思った覚えがある。

これが高校生くらいの頃で、OAuthなんて全然知らなかったので切り替わる時にドキュメントを全部印刷して読んでOAuthとはなんぞやを学んだ。当時はiPadが出たばかりでタブレット端末などは持っていなかったので、長い文章を読む時は家のレーザプリンタで印刷して読んでいたのだった。おそらくプラスの2穴ファイルに挟まれたTwitter OAuth 1.0aの書類がまだ実家に眠っているのではないか。

作ったものたち

もうどれも動かないと思う。上から順に新しい(一番下が古い)。ふしぎなもので、この順番で思い入れが強まっていく。 作った時期が大学生ごろに集中しているので、これは青春補正だろうなと思う。

tw2slack

ヤパチー(YAP(achimon)C::Asia Hachioji 2016 mid in Shinagawa)のスタッフをやっていて、Twitterを監視してなにかあったらアクティブサポートする担当として動いていた。

スマートフォンでツイートを監視したくて、TweetDeckでは間に合わなかったのでSlackで監視することにした。Slack Integrationだとリアルタイム性に欠けるのでStreaming APIでなんとかしたいと思って、専用のデーモンみたいなやつを作った。結構便利だったけど用途が結構限定されるのでこれ以降使ってない。

当時書いた紹介記事はこちら。

owlproxy

当時(今はどうか知らない)の夜フクロウはTwitter公式の画像アップローダに対応していなかった。任意のサードパーティ画像アップローダを指定する仕組みはあったため、ローカルにプロキシを立てて公式側にアップロードできるようにした。

Gyazoにも投稿できるようにして、GyaPC::Asia Tokyo 2015のLTで話した。

飯テロ.in

GyaPCの発表資料にもちょろっと書いた、Twitterアカウントでログインできるご飯画像アップローダ(閉鎖済、ドメインも手放し済)。通報機能が悪用されて画像が一つも無くなったりしたら嫌だなと思って、通報数で公開状態を制限するんじゃなくてTwitterでmentionをもらって自分の判断で公開停止する形にした。通報されたら手でMySQLのレコードに論理削除フラグを立てる仕組み。これまた牧歌的だったと思う。ぼくが恣意的にご飯画像かどうかを判断できるので微妙な仕組みではある。いまの時代に生まれていたら機械学習を学んでなんとかしていたと思う。

ちなみにGIGAZINEで紹介されたことがある。GIGAZINEはいろいろあるけどこういう記事でもずっと残していて偉い。

余談だけどこのブログのタイトルのせいで作者名をあんパンと勘違いされた状態で記事が出ていた。問い合わせて直してもらった。

violet

github.com

JavaScriptでTwitter APIを扱えるようにするクライアントライブラリも自作していた。Streaming API(昔はHTTPでコネクションを閉じずツイートをリアルタイムに受け取るAPIがあった)に対応していて、Firefox OSで動かすのに便利だった。

これを作った当時、世の中の動向に疎すぎたのでnpmの存在を知らなかった。package.jsonもなにもなくて、当然依存モジュールは存在しないのでOAuth周りも全部自分で書いていた(正確にはsha1まわりのライブラリだけ他repoからもってきている)。minifyにはGoogle Closure Compilerを使っていた。ECMAScript 5以前のコードなのでPromiseはPの字もなくcallbackでなんとかしていて涙ぐましい。自分の中では黒歴史的な存在だけど、書いてて楽しかったので思い入れはそこそこ強い。

別れ

明確にもうTwitter APIで遊ぶのをやめようと思ったタイミングがあって、Streaming APIが廃止されたときだった。あれ以降、遊び場としてのTwitterは完全に面白みを失って興味がなくなったと思う。プラットフォームとしての興味もこのころからだんだん薄れていった。負荷をかけまくるユーザが減ったということでビジネス的には正しかったのかもしれない。

では最近はどうかというとあんまりTwitterを見てなくて https://social.masawada.me に引きこもってます。という話を来週くらいに書くであろう。

お名前.comで管理しているドメインをGandiに移管する

おもしろドメインを取得するのが趣味で、現在22のドメインを保持している。これでもいくつかは削ったのだけれど、思い入れのあるものはどうしても手元に残したいし使い続けたい。レジストラもいろいろあり

  • お名前.com
  • ゴンベエドメイン(インターリンク)
  • Google Domains
  • Amazon Route 53 Domains
  • Gandi

という状態。管理が煩雑なのと、個人的な信条によりお名前.comの利用を廃止したいという思いがあり、どこかにまとめたいと長年考えていた。そんな中、2月からお名前.comがサービス維持調整費という名目で値上げすることを発表し、取り柄であったやや値段が安いという部分も崩壊したため全てのドメインを移管することにした。

www.onamae.com

Gandiについて

移転先のレジストラを検討し、最終的にはGandiを利用することにした。Gandiはフランスの企業が運営するレジストラで、会社の歴史は以外と古く1999年から存在している。Amazon Route 53 DomainsのOEM元でもある。

最初はAmazon Route 53 Domainsに移管することを考えていたが、持っているTLD全てに対応しているわけではなく、結局はGandiと併用することになりそうだったため利用を諦めた。その点Gandiは対応しているTLDが多く、.moeや.みんななど一部のレジストラでは扱っていないようなものも大概は登録できる。

また、No Bullshitというモットーを掲げているところが良くて、とても共感できる。

www.gandi.net

TLDによって値が張るものがいくつかあり(なぜかAmazon Route 53 Domainsの方が安いTLDもある)、移管前後で年間約1万円ほど値上がりしてしまう計算だが、どうせ値上がりするならいずれにせよこちらに移管する方が得と判断した。

お名前.comからの移管

.jp以外のドメインの移管(トランスファー)は基本的にどこも同じ流れになる。具体的に行う作業は以下の通り。

  • (移管元) AuthCodeの発行
  • (移管先) ドメイン移管手続の開始(AuthCodeの入力)
  • (移管元) ドメイン移管の承認

ドキュメントにもだいたい同じことが書いてある。

help.onamae.com

お名前.comの場合、以下に気をつける必要がある

  • 移管ロックを設定している場合は事前に解除しておく
  • Whois情報公開代行設定をしている場合は事前に解除しておく
    • (他のレジストラではこんなことをさせられる記憶がないのだがどうだろうか)
  • ドメイン移転後もDNSの設定が残るので削除すること

上2つについてはそもそも移管手続ができないので問題ないかと思う。一番下のものだけ厄介で、AuthCode発行時に以下のような表示がなされるが、そのまま手続きできてしまう。

DNSの設定は移管後に手で削除する必要がある

お名前.comには、お名前.comが管理するネームサーバを利用してDNSのレコードを返す機能がある。この設定を行った状態で移管が完了すると、この設定は残り続けたまま課金の対象となる。移管後にネームサーバやDNSレコード設定の引っ越しを行い、お名前.com側の設定を削除する必要がある(消すまではお名前.com側が名前を解決してくれるので、必ず設定の引っ越しが終わってから消す必要がある)。Gandiでは移管時の手続でネームサーバ等を変更する操作がやや難しいので、移管が完了してからGandiが提供するDNSに切り替えることになる。

「DNS追加オプションの解約、DNSレコード設定削除のお手続きはこちら」から手続きできる

動線がやや分かりづらいが「DNS設定/転送設定-ドメイン一覧」内の「DNS追加オプションの解約、DNSレコード設定削除のお手続きはこちら」を押して「DNSレコード設定のゾーン削除」を選択すれば手続きできる。

移管直後に設定を削除した場合はどうやら課金の対象とはならないようにみえる。いまのところカードの明細に引き落としの情報は見えていない。

お名前.comについては以上の操作を行いひとつひとつドメインを移管していけば良い。これまでお世話になりました。

おまけ: 移管するドメインの管理

Todoistで移管する日付を管理している

このブログで度々登場しているとおりTodoistを利用して移管する日付を管理している。期限2週間前になると移管できなくなってしまうため、十分に余裕を持った状態で移管したい。しかし、移管時に費用の請求が走るため一気に移管するとそこそこの金額がかかってしまう。そのため、期限を管理しつつ1年かけて移管しようとしている。ひととおり終えたらまたどこかに感想などを書くことがあるかもしれない。

はてなブログで記事を書く際にやっていること11連発

はてなブログで記事を書く際にやっていること、主にツールの使い方について。

初手で下書き保存する

編集中のものを誤って公開すると悲しい気持ちになるので初手で下書き保存にしておく。以降、公開に切り替えなければ下書きで保存され続ける。

こまめに保存する

Ctrl+S(macOSならCommand+S)で保存できる。保存しなかったとしてもローカルに残してくれる神機能があるけど、あくまでバックアップのバックアップくらいで使うのが良さそう。

こまめにプレビュー確認する

何度か読んでおくと都度体裁を整えられたりtypoに気付くタイミングが増える。日本語として通っていないところも見付けやすくなる。

見出しはh3からh5を使う

これが推奨らしい(はてな記法ではh3からh5が使われるため?)。Markdownの場合は ### から ##### までを利用することになる。

staff.hatenablog.com

過去に似た話題で書いているなら過去記事を貼り付ける

似た記事があるなら書いておくと参考になりそう。なんとなく文中のキーワードで検索すると全く同じことを書いてないかなどにも気を払える。

サイドバーから貼り付けられます

スマートフォンで撮った写真を利用する際はアプリからアップロードしてしまう

パソコンに転送して写真を貼るのは大変なので、スマートフォンのアプリで新規に記事を作成して写真を貼る。この記事はアップロードが終わったら破棄する。Web側では下書き保存していればリロードすると同じ記事の下書きを開けるので、そのままリロードする。写真サイドバーの一覧にアップロードした写真が登場するのでそのまま貼る。

写真のサイズを調整する

フォトライフ記法の最後に w300h300 などを付けると幅や高さの値を設定できる。巨大な画像を目にするとウワッとなるので、特に縦長のスクリーンショットなどは高さを指定してあげると丁寧。

縦長のスクリーンショット

サイズを調整したスクリーンショット

help.hatenablog.com

Amazonの商品を貼り付けたい場合はAmazonで検索してからASINをAmazon商品紹介サイドバーに入力して貼り付ける

Amazon商品紹介サイドバーだと検索した書籍がKindle版と物理版のどちらか見分けが付かないのでこういう振舞いをするようになった。Amazon側で商品ページを開いてASINをコピーしてからサイドバーで検索する。当該の商品がピンポイントで出てくるのでそのまま貼り付ける。

公開する前にアイキャッチ画像を確認する

SNSにシェアした際に意図しない画像がバーンと出てしまうのを防げる。最近のTwitterはキャッシュを破棄する仕組みがなく、よく分からないタイミングで更新されるのでミスるとつらい気持ちになる。

最後に一通り読み直す

最終チェック。こまめに読んでいるはずなのでざっくりで。

記事を投稿したらTwitterに放流する

これはお好みで。放流しないこともある。このブログの記事は基本的に放流しているけど、masawadaの日記は基本的に放流しないことにしている。