マネージドクラウドのチームにJOINして、1ヶ月たちました。
ツイートから周りからは「楽しそうですね」と勘違いされているようですが、アーキテクチャや技術スタックのキャッチアップに大変な毎日を送っています。楽しいです。
Webサーバに機能を追加するということ
ところで、広く知られていることですが、マネージドクラウドではngx_mrubyがかなり活用されています。
何をもって活用されていると言えるのかは要出典ですが、NGINXが単なるWebサーバではなく「別の機能を持った何か」に見えるくらいには使われています。
「Webサーバに機能を追加する」という感覚は一般的なWebアプリケーションの上で生きてきた自分としては新鮮で、「最近のフレームワークは例えばMiddlewareというような階層構造で機能を付与するものがあるが、その一部をさらにその外に移譲しても良いんだ」という面白さがありました。
思えば、AWSでサーバーレスアーキテクチャを構築する上でも、機能を
- CloudFrontのLambda@Edgeに持たせるのか
- API Gatewayに持たせるのか
- HTTPリクエストから発火したAWS LambdaのFunctionに持たせるのか
- さらにそのFunction内のmiddyで多層化されている
- はたまたバックのDynamoDBのストリームからイベント発火したAWSLambdaに持たせるのか
と、機能を持たせる場所というのがシステム全体のアーキテクチャを考える上で重要で、できれば選択肢が多いほうがより構成の選択肢が増えるのは実感としてわかっていました。
「もしかして、これは、そこまで規模の大きくないWebアプリケーションを開発する上でも、アーキテクチャとして『Webサーバに機能を追加する』という選択肢を持っていたら強いのでは?」
と思って、PHPカンファレンス福岡2018に「プログラマブルWebサーバ -Webサーバに機能を追加してみよう-」というタイトルで応募しました。
そして、不採択でした。残念。
mod_mruby / ngx_mrubyというレイヤー
mod_mrubyやngx_mrubyというレイヤー、GMOペパボではMiddleware Configuration as Codeと呼ばれて活用されています。
なんだか新しい、とっつきづらいもののように聞こえるのですが、作者の @matsumotory から聞いてちょっとおもしろかったことがあります。
mod_mrubyのApacheへの組み込まれ方は、実はmod_phpと同じだそうです。
Apacheが実行する(主に)Webアプリケーション用のインタプリタとして使われることを目的としているのか、Apacheの処理のさまざまな箇所でフックやフィルタように動かす用のインタプリタとして使われることを目的としているのかの違いだけです。
「mod_phpと同じだ」とか、途端に親近感がわきます。
とはいえ自前ビルドは技術採用のハードルになる
mod_mrubyもngx_mrubyもApacheやNGINXに組み込みます。
そうすると、「Webサーバも自前ビルドなのか?」「そうでなくてもmod_mrubyやngx_mrubyも自前ビルドなのか?」となり、運用を考えると躊躇してしまいます。
自前ビルドより yum
や apt-get
を使いたいです。よね?
そういったわけでRPMリポジトリを作ってみました。
今回作ったRPMリポジトリの要件は以下です。
そのために、mod_mrubyとngx_mrubyの以下の機能をあきらめています。
- mrbgemの追加ビルド
- ngx_mrubyのTCPロードバランシング機能
mrbgemについては、もう少し知見が溜まったら、ゴールデンAMIならぬ「ゴールデンビルド」を決めてリリースしようと思います。
ngx_mrubyのTCPロードバランシング機能については、現時点ではNGINX自体のビルドオプションを変える必要があるため、なかなか難しそうです。
それでも十分強力なので、それなりの意義があると思っています。
作ったRPMリポジトリは以下です。
packagecloud.io
packagecloud.io
使い方
よくある外部RPMリポジトリの利用と同様に、RPMリポジトリの登録とインストールの2STEPになります。
packagecloud の機能をフル活用しているので、
mod_mrubyの場合は
$ curl -s https://packagecloud.io/install/repositories/k1low/mod_mruby/script.rpm.sh | sudo bash
ngx_mrubyの場合は
$ curl -s https://packagecloud.io/install/repositories/k1low/nginx-module-mruby/script.rpm.sh | sudo bash
で終わりです。
インストール
mod_mrubyの場合は
$ sudo yum install mod_mruby
ngx_mrubyの場合は
$ sudo yum install nginx-module-mruby
で終わりです。
モジュールの有効化
Apacheの場合は、confに
LoadModule mruby_module modules/mod_mruby.so
NGINXの場合は、eventディレクティブの前に
load_module modules/ndk_http_module.so;
load_module modules/ngx_http_mruby_module.so;
と追記すれば有効化されます。あとは使うだけです。
簡単ですね!
作ったときのはなし
以下は特に読む必要はないはなしです
実際に動いているCIのスクリプトは以下のリポジトリになります。
github.com
github.com
RPMパッケージを作るだけなのにそれなりに大変でした。
Rubyのバージョンが古くてpackage_cloud.gemがインストールできなかったり、Gitが古くてCircleCIがまともに動かなかったり、全然本流とは違うところで躓くことに。
そこはTwitterでCircleCIマスターの @kunit から直球の解法を教えてもらって、なんなく解決できました。ありがとうございました!
フィードフォースの皆さんありがとうございます!
CircleCIのScheduled Workflowが最高
今回作ったRPMリポジトリの「全自動化」はCircleCIのこの機能を知っていたからなのですが、もう最高です。
circleci.com
ようはcronでビルドを定期実行してくれるという機能です。うまく .circleci/config.yml
を書ければいろいろな用途に使える機能なのでオススメです。
モジュールの利用にもSELinuxのポリシー設定が必要
RPMリポジトリからモジュールをインストールするだけではダメで、そのモジュールに適切にSELinuxのポリシーを設定する必要があるということを知りました。
/var/log/audit/audit.log
を見たり、audit2allow
で必要ポリシーを見つけたり、 ls -Z
で他のモジュールとポリシーを比較したり、少しはSELinuxと仲良くなれたかもしれません。
NGINXのダイナミックモジュールのロードにはApacheのモジュールよりも一手間以上が必要
ngx_mrubyのダイナミックモジュールについては、ほぼ以下の記事で完結しています。本当にお世話になりました。
medium.com
実際に自分で手を動かしてみると、本当に面倒でした。
Dockerのバグ?にあたる。そしてCircleCIでは治っていない
やろうとしていたことはmod_mrubyのRPMビルドのために centos/7
イメージでhttpd-develを入れようとしただけなのですが、依存関係にあるhttpdがインストールされるときにエラーになるというものでした。
これは実は既に解決された問題のようで、ホストOS側のUbuntuでのAUFSの問題だったようです。
普通なら「じゃあホストOSのUbuntuを最新にしようか」ということになるかと思いますが、CircleCIのホストOSをいじれるわけはなく、仕方なくhttpdのソースコードを取ってくるという形に変更しました。
たった2つのRPMリポジトリを実現するためだけに、なかなか大変でした。
リポジトリのパッケージ管理者の方には頭が上がりません。
なお
このRPMリポジトリの成果の一部はGMOペパボ福岡の有志開発合宿で作成されたものです。