あんパン

こしあん派

初めて触る言語で爆速に物をつくりはじめるまでにやったこと

とある事情により産業用のプロトコルを喋るコードを書く必要があり、そのライブラリがPython向けにしか提供されていないというケースに出くわした。普段はPerlとTypeScriptのみで生きていて、ときどきGoを触ったりRubyを触ったりシェルスクリプトを触ったり……という感じでPythonは数年前にVimのプラグインを書くのにちょろっと触ったくらい。記憶としてはなんかインデントに意味があったはず、くらいが残っている状態。

教科書的にいうと公式ドキュメントのチュートリアルなどから入って書き方を学びつつREPLで試しましょう、GitHubなどで公開されているコードを読みましょう、といったコースが王道だし丁寧にやっていくならそうなるでしょうと思う。しかしだらだらと勉強から始めていたら成果を出せない、品質はホットモックレベルでいいからとにかく高速に物を作りたい、といったときにこれをなぞっていると足が遅い。そういうケースでこうやったぜ的なTipsを書いてみる。主に作りたいものが決まっている人向けRTAテクといえる。

今回はPythonにフォーカスしているので必ずしも普遍的ではないが、まあまあ他のオブジェクト指向っぽい言語でも流用できるのではないかとは思う。どうでしょう。他の言語を学んでいるかとかのバックグラウンドにもよりそうなのでかなり限定的なポジショントークかもしれない。

依存モジュールの管理方法を学ぶ

  • ローカルのディレクトリに依存を閉じ込める方法を最初に学んでおけるといい
  • ミスってグローバルな領域を汚染するとあとで面倒なことになるケースがある
  • どうせ何らかのモジュールを使わないと開発できないのだから初手で学んでおく

printデバッグの方法を確認する

  • とにかく変数の状態を見られればなんとでもなる
  • Hello, Worldぽいけど必ずしもテキストを出力したいわけではない
    • なんかオブジェクトっぽいものの内部状態とかを出力できると便利
    • メソッドの一覧出す方法とか知っておくと役立つこともある

github/gitignoreを眺める

  • リポジトリに混ぜてはいけないものを学ぶために便利
    • まあパクってきたらいい
  • どういうツールが存在するのかを見るのに便利

作りたいもので使う依存モジュールのコードを読みまくる

  • ドキュメントになくてもサンプルコードがあったりテストを読むとなんとなく使い方がわかる
  • 言語としての文化もなんとなく分かる
    • それが必ずしも一般とは限らないけど……

というわけでこまごま書いてみた。みんなやっとるで、というものばかりだけど、最低限これくらいおさえたらあとは流れで物を作ることはできた。他の人は新しい言語で爆速に物を作りたいときにどこを見ているのかはやや気になる。

わくわくMastodon更新日記

手順通りにやったらなんとかなったのでわくわく要素は無。

個人のSlackのmastodonチャンネルに4.1.1の情報が流れてきたので更新した。4.1.0が出てから運用を開始したので初のアップデート。手順はリリースノートの通り。

github.com


どうせ自分しか使っていないので一旦サービスを落とす。

$ sudo systemctl stop mastodon-*

Mastodonユーザになってアップグレード

$ sudo su - mastodon
$ git fetch
$ git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)
$ bundle install
$ yarn install
$ RAILS_ENV=production bundle exec rails db:migrate
$ NODE_OPTIONS="--openssl-legacy-provider --max-old-space-size=2048" RAILS_ENV=production bundle exec rails assets:precompile

ついでに apt update && apt upgrade とかで依存も更新しておく。依存を更新したらrebootしたくなるのでそうした。systemd上はenableされているので再起動後にMastodonのservice群は勝手に立ち上がる。

リリースノートにはDBのバックアップを用意するといいぞいって書いてあるけど自分しか使ってないしいいかとおもってなにもせずに更新した。いずれにせよマネージドなPostgreSQLを利用しているので日次でスナップショットが撮られていて便利。


ついに明後日はYAPC::Kyoto 2023ですね。自分は明日の前日祭からスタッフをやっているので来場されるみなさま一緒にわいわいしましょう。

SSHでログインされたらSlackに通知する

割とn番煎じっぽい小ネタ。このブログは自分用メモみたいなところがあるのでそういうものです。

SSHでログインされたらSlackに通知したい。誰かが入ってきたら分かるようにしたい。コンテナ化された現代の開発ではSSHログインされるサーバがまずなかったりするわけだが、個人がお金をケチって運用しているとまあある。というかこれです。

ホストへのSSHログインがあった場合にそれをトリガーとしてコマンドを実行するには /etc/pam.d/sshd

session optional pam_exec.so PATH_TO_BIN

の1行を足せば良い。PAMはLinuxにおける認証まわりをよしなに扱えるようにする仕組み。configに書いてある行を評価していってログインの成否を決める。sessionはユーザ認証の前後あたりで実行するモジュールを指し、optionalとすることで、以降の成否が認証の成否に影響を与えないようにする。つまりコマンドがコケても無視するというところ。このあたりは man pam.conf を眺めておけば分かる。

pam_execを利用するといくつかの値が環境変数に設定された状態でPATH_TO_BINの実行ファイルが実行される。こちらは man pam_exec を眺めれば分かる。具体的には

the following PAM items are exported as environment variables:
PAM_RHOST, PAM_RUSER, PAM_SERVICE, PAM_TTY, PAM_USER and PAM_TYPE, which contains one of the
module types: account, auth, password, open_session and close_session.

とのこと。これを読みつつPATH_TO_BINの中身は実際には以下のようなシェルスクリプトにしている。

#!/bin/sh

if [ "$PAM_TYPE" != "close_session" ]; then
  echo "$PAM_USER logged in to $(hostname) from $PAM_RHOST" | /opt/bin/notify_slack;
fi

/opt/bin/notify_slack はcatatsuyさんが作られているプロダクトで、この実行ファイルをポン置きしたもの。

github.com

設定はこのドキュメントに書いてある通り、事前にAppを作ってIncoming Webhookを払い出して /etc/notify_slack.toml に置いている。Slackに投稿する部分はcurlなどで書くと煩雑になるので、標準入力で渡せば適当に流してくれるのはありがたい。

ということで、ここまで設定するとSSHログイン時に以下のような通知がSlackへ送られてくる。

Slackにやってきた通知(いろいろ隠した)

SSHログイン時だけでなく単純なユーザのログインなどもトリガーにできるので、デスクトップLinuxでもいろいろ遊べそう。

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 におります。いまのところ流速がおだやかで平和〜という状態。