研究室で運用しているWebシステムのサーバインフラを式年遷宮した話

#diary 2020/12/11


これは klis Advent Calendar 2020 11日目の記事です。

私が所属している研究室では、Crowd4U というクラウドソーシングプラットフォームを開発しています。Crowd4Uは2011年に公開され、2020年の現在に至るまで開発と運用が続いています。

自分は2015年の12月ぐらいに学類(学部)の卒業研究生としてこの研究室に入りました。なのですでにCrowd4Uは運用されていて、学生や共同研究者からの要望に応えるための機能改修が随時行われていました。このような作業は学生やスタッフらによって行われます。

一方で、直接的に研究に繋がりにくいOSやミドルウェアのアップデートやコードのメンテナンスは放置されていて、Scientific linux 6の上にRuby 1.9.3で動くSinatraアプリが動いている状況でした (2016年ごろ)。 OSやミドルウェアのバージョンが古いことは、セキュリティ的な観点を無視すればどうでも良いのですが、年単位でそれらが放置されていたり、障害対応のための変更やチョットデキル学生が色々な素敵機能を追加していくことにより、大半の人にとってサーバ内はブラックボックスと化していました。

そこで、Crowd4UをRuby2.xで動かすことを目標としてサーバインフラを刷新するプロジェクトが2017年ぐらいに立ち上がりました。これはCrwod4U式年遷宮プロジェクトと呼ばれ、VMをInfrastructure as codeツールであるVagrantとAnsibleを使って構築できるようにしました。控えめに言っても9割の作業を自分が行ったこの式年遷宮はすでに完了しており、現在はUbuntu16の上のRuby2.5でアプリケーションが稼働しています。なぜ最新版でないかについては後述します。

この記事では、人の入れ替わりの激しい大学の研究室というやや特殊なチームにおいて、どうやって継続的にソフトウェアを開発するかについて、システムの式年遷宮に関する知見を踏まえてポエムを書きたいと思います。

システム開発における式年遷宮

式年遷宮の由来について、伊勢神宮のWebサイトには「式年遷宮の「式年」とは定められ年を、「遷宮」とは宮を遷すことを意味します。」という記述があります。つまり定期的に遷宮するつもりがないのであれば単に遷宮と呼ぶべきであるということです。

「「凡おおよそ太神宮は廿年はたとせに一度ひとたび、正殿宝殿及び外幣殿を造り替えよ」と記載があります。しかし、その理由についてはいずれの書籍にも記載がなく、これまで様々な理由が推定されてきました。結果的にみると、20年ごとに行われてきたことが、唯一神明造ゆいいつしんめいづくりという建築技術や御装束神宝などの調度品を現在に伝えることができ…」とあるように、N年ごとに遷宮することが技術を伝承することに繋がったということです。

2013年に第62回式年遷宮が行われたことから、システム開発の文脈において持続性向上や技術伝承の目的で式年遷宮という言葉が使われた例を見つけることができます:

式年遷宮する前の状況

式年遷宮以前のCrowd4Uは、VirtualBoxのVMとして稼働していました。本番VMとテスト用VMが存在し、またテスト用VMのコピーを学生のラップトップで動かして実験の準備などが出来るようになっていました(図1)。

テスト用VMには当然それまでに登録されたタスクのデータやログファイル、画像ファイルなどが入っているわけですが、それらをそのままコピーしてくるのでVMのサイズは約50GBぐらいありました。

また単にコピーしてきたVMがそのまま動くわけではなく、ネットワークI/Fの設定を書き換えたりする必要があったりして、それが絶妙なドキュメントに書いてあってそれを頑張ると動く感じでした。

機能追加の観点で言えば、OSやapache、rubyが古すぎて新しいライブラリを導入しにくいことが問題になっていました。

図1: 式年遷宮以前の構成

式年遷宮した後の状況

式年遷宮以降のCrowd4Uもまた、VirtualBoxのVMとして動いています。本番VMやテスト用VM、ローカルVMという用途が異なるVMが存在しますが、これらのVMはVagrantとAnsibleというツールを使ってコードによって自動的に構築されます(図2)。用途による違いは、ローカルVM以外のVMではCPUとかメモリの設定を増やしたりletsencryptなどの設定をしたい、一方でローカルVMにはubuntu-desktopをインストールしたいなどです。

ローカルVMは最新版がラボ内のファイルサーバに置いてあって、使う人はそのovaファイルをダウンロードしてきて展開するとVirtualBoxにVMが登録されます。単に起動すればlocalhost:3000でWebアプリが閲覧でき、localhost:2222でsshできるようになっています。

ovaファイルの容量は3GB程度に収まっているのと、特に設定しなくても起動するので、ローカルVMを壊してしまったときは新しくVMを登録しなおせば初期状態の環境を入手することが出来ます。壊れた時のリカバリが簡単であればあるほど、新しいことを試しやすくなるのでこれはとても大切なことだと思っています。

VMの構成はVagrantとAnsibleによってコードで管理されているので、ローカルVMを含む全てのVMを同一かつ最新の状態に維持することが出来ます。

図2: 式年遷宮以降の構成

Infrastructure as codeと技術選定

Scientific linux 6の上でRuby2が動く50GBのVMが配れる状況になっても何も嬉しくないので、まずはアプリケーションコードを動かすために必要な最小限の構成を調査することにしました。あとはDBのmigration scriptが途中からしかなかったので作ったりもやりました。ここではOSをUbuntu16に変える以外はミドルウェアやライブラリのバージョンを一切変更しないことを目指しました。システムの不具合がVMを作り直したことによるものか、ライブラリのアップデートによるものかを明確にするためです。

必要最低限の機能を動かすために必要な依存関係が明らかになったところで、真っ新なOSに必要なミドルウェアのインストールやアプリケーションのgit cloneを行う方法を検討し始めました。サーバなどのリソースを目的の状態に変更することはprovisioningと呼ばれることもあります。

今回の式年遷宮では、単に新しいVMを作って終わりではなく、VMを作る方法をコード化したいと考えました。インフラをコードやプログラムで記述する方法はInfrastructure as code (IaC) と呼ばれています。既存のVMをコードで記述し、単にRubyのバージョンだけを変えたVMを作った上で、バージョンの変更による不具合を1つづつ解決していくことで、アプリケーションへの変更を最小限に抑えながらRuby2へと移行することを目指しました。

候補となるツールとしては自作shell script、Docker、Ansible、Chef、Puppetがありました。 筆者としてはDocker以外の選択肢は考えられなかったのですが、チームとしてDockerを習得して年単位で運用を続けるのが不可能であることは想像に難しくありません。 自分が研究室を離れてからもシステムを動かし続けるために、また将来の自分のような哀れな学生が少しても研究に専念できるようにするために、既存の技術スタックを全く変えずに保守性だけを向上させることにしました。

最終的にはVagrantをVirtualBoxVMの構成管理に使い、AnsibleでVM内の環境構築をすることに決めました。 Ansibleは作法にしたがって記述すれば冪等性(何度実行しても結果が変化しない)が得られることと、ある程度息の長いプロジェクトであり公式ドキュメントが充実していることが利点だと考えています。学生主導のチームだと十分な量の内部ドキュメントをメンテナンスするのはとても難しいので、OSSコミュニティによって高品質なドキュメントが提供されることの恩恵はとても大きいです。

AnsibleにはAnsible Galaxyというモジュールを公開できる仕組みがあり、これを使うとletsencryptとかのセットアップが簡単に実現できますが、このプロジェクトでは基本的に全てのタスクは標準添付モジュールのみで記述しました。 Ansibleもまた1つの道具であり、いつかは廃れて別の時代に合ったツールが現れるはずです。その時にできるだけ簡単にそれらのツールを採用する判断ができるようにしたいという意図があります。出来るだけ基本的な仕組みだけに乗っておくことで、Ansible playbookが実行できなくなったとしても資料としての価値を見いだせるだろうとも考えています。

IaCに関する日本語の情報は、最近だとInfra Study Meetupとかで手に入ります。

式年遷宮の当日

約1年ぐらいかけてVagrant+AnsibleによるVMを整備して、さらに1年ぐらい使ってテストVMとローカルVMだけ新VMに移行及び布教する活動を進めていたので、本番VMの式年遷宮では技術的なトラブルは起こりませんでした。個人的には上手くいったと考えています。

Webサイト上の画像が無い(なんとgit管理されていなかった)とかcronが走っていないなどある程度想定していて、事前に準備することを諦めた不具合が指摘されたのでそれらは後日修正を入れました。

式年遷宮のその後

移行後もいくつかの微調整は必要だったもののコアのアプリケーションコードは安定して稼働させることができました。

式年遷宮を終えてから数ヶ月後に、本番VMをホストしているマシンのRAID下のディスクが壊れる障害が起こりました。新しいディスクが届いてセットアップされるまでの間、本番VMを別のサーバで稼働させるような応急処置を式年遷宮とほぼ同じ手順で数時間以内に完了することができました。

古いVMのまま稼働させていたら本番のデータを全て失っていた可能性もあったと思いますがそんなことを考えた人は誰もいないでしょう。

Infrastructure as code (IaC) の難しさ

既存のアーキテクチャを維持しながら、IaCによりメンテナンス性の高いインフラ環境を作り出すことができたので、自分では満足のいく成果が出せたと考えています。古いVMを使い続けていたら不可能であっただろう改善(安定性向上、バックアップとレストアの仕組み、ログ収集とか)を導入することにも繋がりました。一方で、実際にやってみて気づいた課題がありました:

  • IaCを記述する方法論というか考え方を学習するのは多くの人にとってとても難しく直感的でないということに気づきました。例えば、本番サーバとテストサーバ、ローカルの環境などを統一的したいという技術的背景を知らずに、単に本番サーバのMySQLのconfigを書き換えたい場合に、直接SSHしてそのファイルを書き換えるのとansible playbookを書くのでは明らかに後者の作業及び学習コストが高いです。
  • IaCツールで管理する対象をどこまでカバーするかというのも難しい問題です。例えばIaCツールでは宣言していない管理対象の変化に気づくことは難しいです。一方で、全てのサーバ内の状態を記述することは現実的でありません。ですが、管理対象でない=IaCツールを使わずに設定変更できると判断して状態を変えられてしまうことが多々あります。

サーバへのSSHを許さないといった厳しい制約を導入しない限りこれを防ぐのは不可能だと考えています。

未来

式年遷宮プロジェクトを開始した当初から意識していたことは、次の遷宮をどのように行うかです。自分もいずれ研究室を離れる可能性が高いので、未来の遷宮は別の誰かがリードするかもしれません。

もちろんもっとも理想的なのは、Ansible playbookのメンテナンスを継続することです。OSやミドルウェアのバージョンをあげるのはもちろん、DBを別のVMに移動したりKVSを導入するとかが気軽に試せると思います。 Ansible playbookを作ったことで、ミドルウェアの依存関係が明確になったので、Dockerfileを書いてコンテナ化することにも成功しました。またplaybookをそのままEC2で動かせばAWSに展開するといったことも可能です。

自分は図1の状況を図2のように変えるために式年遷宮をやりましたが、自分がいなくなってIaCする必要がなくなり、IaCを捨てて図2を図1に戻したいと指摘されるかもしれません。その時には今回の式年遷宮で作ったクリーンなVMが数年は機能して、その後は自分が古いVMと呼んでいるやつに置き換わるでしょう。

一方で、Crowd4Uをよりスケーラブルにするために複数のインスタンスやコンテナ技術を採用したいと考えるかもしれません。その場合はAnsibleよりも高度なIaCツールやオーケストレーションツールを採用する必要があるでしょう。

いずれのシナリオにせよ、Ansible playbookがそれらの方法への移行に役立てば嬉しいです。今回作ったIaCツールは本体アプリのリポジトリとは分けてあるので、IaCツールの部分だけ捨てられる構成になっています。時が来たらナウいツールで書き直すことを推奨します。IaCツールがメインの足枷となる必要はありません。

まとめ

研究室で運用しているWebシステムのサーバインフラを式年遷宮した話を紹介しました。 技術的には成功したと思いますが、一方でチームとしては失敗だと考えています。せっかくの技術伝承の機会をほぼ一人でこなしてしまったからです。 とはいえ、自分は式年遷宮を通して対象のシステムのみならずラボ内のインフラ全体やこのシステムの歴史を俯瞰できるようになったので、遷宮の効果は偉大だと実感することが出来ました。