えいのうにっき

a-knowの日記です

Pixelaを支える技術

Pixelaの技術的な話(といっても高度なことは殆どしてないんだけど......)とか、あと今回の個人的な頑張りポイントである利用規約・GDPR対応といったところは、後日別エントリとしてまとめたいな〜と思っています。そのうち書くので、よろしかったらそちらも楽しみにしてやってくださいっ。 commit以外の数値でも草を生やせる、PixelaというAPIサービスを作った! - えいのうにっき

blog.a-know.me

などと書いておきつつ、3週間ほど経ってしまった。ということで、今回はこの点に関して書く。あと、過大なタイトルについてはすみません。これ以外もう何も思いつかなかった。

思い当たる限りで、ざっと箇条書きにしていく。この記事に限らないことだけど、なにか間違ってることとか、もっといいやり方あるよ、というところがあれば、ぜひ教えて欲しい!

サーバーサイド

GCP(Google Cloud Platform)内の1サービスである、GAE(Google App Engine)を使っている。いわゆるPaaS。

App Engine 環境の選択  |  App Engine ドキュメント  |  Google Cloud

そこそこの制約はあるがマネージドで強力なスケーラビリティのある Standard Environment(SE) か、自分でビルドしたコンテナを利用できるという自由度はあるがマネージドさはそこそこな Flexibile Environment(FE) とがあって、どちらも選択できるが、Pixela は前者の環境で動いている。JavaやPHP、Goなどの言語でアプリケーションを実装・GAE SE環境にデプロイしておくことで、ユーザーからのリクエストに応じてインスタンスが起動(スピンアップ)する......といった仕組み(だいぶざっくりとした説明)。

GAEは、自分がWeb技術に触れるきっかけとなった(つまり、人生を変えてくれたといっても過言ではない!)プラットフォームであるということもあり、思い入れが深い技術でありプラットフォーム。一時期仕事としても触っていたということと、Pixelaのワークロードにもぴったり合っていそうだったことで、今回は久しぶりに GAE でやってみることにした。その他の取り得る選択肢(僕の技術的引き出し)としては、EC2やGCEなどのIaaS上に構築する方法やherokuなどもあったと思う。

アプリケーション

言語としてGoを採用した。僕にとって「スラスラと書ける」という言語だったわけではまだ決してなかったけれど、Goで書いたアプリケーションをGAE上で動かした場合、上述のスピンアップの速度がめちゃくちゃ早い、という他の何物にも代えがたいメリットが享受できることが決め手。もちろん、界隈で人気、ということもある。

テスト

趣味開発ではあるが、そこそこ頑張って書いている...、、決して綺麗ではないけれど。カバレッジとしては約65%前後を推移している。まだCI環境の構築はできていないのでやっていきたい。

appengineアプリケーションのテストについては、別途以下のようなエントリも書いておいたので、参考にしてもらえたらと。

blog.a-know.me

データベース

GCP的に今はたぶん微妙な時期だと思うのだけど *1 、GAE SEから用いるデータベースとしては Datastore(Cloud Datastore)がデファクトだと思う。

DatastoreはいわゆるKVSで、開発時に注意しておくことのうち、僕がぼんやりと覚えていることに、以下のようなものがある。

  • keyによるentityの取得は、とてもコスト(速度面・料金面)が低く、また Strong Consistency である
  • keyによる取得以外の方法としては、クエリによる取得がある
    • もしクエリを用いてデータ取得する場合には、あらかじめ発行するクエリに応じたカスタムのインデックスを定義しておく必要がある
    • インデックスを定義しておくと、その定義に従って、entity登録時にインデックステーブルにも都度変更が入る
      • このコストが低くはない
      • クエリでの取得結果についても Eventual Consistency になる
  • entity間に親子関係を持たせることができる
    • 親子をずぼっと取ってこれる Ancestor Query というのもあるが、これは Strong Consistency になる

上記を踏まえて Pixela では、全体を通して、コストの高いクエリはひとつも発行せずに済むように実装している。

  • Pixelaで登場する主なentityは User Graph Pixel の三種類
    • それぞれ、親・子・孫、という関係になっている
    • なので、Ancestor Query でガッと取ってきてゴリっと何かやる、みたいなこともできる
  • Pixelaでは「約1年分の Pixel を取得する」ということをおこなう必要があるAPIがあるが、
    • Pixel のキー名称を yyyyMMdd をベースに生成できるようにしている
    • そのため、クエリを発行するのではなく、1年分の Pixel に対応するキーをガッと作成し、できたキー配列を使って getMulti() でガッと取ってくるようにしている
    • これにより、より早く・より安く・確実に(遅延なく)データを取得できている

なお、いくらコストが低いからといってグラフにアクセスがあるたびにこれをやるのは効率が悪いので、一度生成したグラフデータは memcache にキャッシュしておき、それに対する変更がない限りは memcache にキャッシュしたものから返すようにしている *2

フレームワーク・ライブラリなど

できるだけ薄く作りたい、という気持ちがあり、Pixela から直接使っている外部ライブラリなどは以下の5つ。

  • google.golang.org/appengine
  • go.mercari.io/datastore
  • github.com/google/uuid
  • github.com/go-chi/chi
  • github.com/favclip/testerator

go-chi/chi はなんかずっと使ってる。。

フロントエンド・デザイン・画像など

フロントエンド、興味がないわけでは決してないんだけど、自分の技術に対する興味順としては下の方に位置しがちということもあり、積極的に外部リソースを活用している。

幸いPixelaはAPIサービスということもあり、フロントエンドと呼べるようなものはサイトのトップページ(にして唯一のページ)くらいしかない。このデザインは、有償bootstrapテーマが買えるwrapbootstrapで購入した。今回買ったテーマは↓これ。

wrapbootstrap.com

余談だけど、僕はwrapbootstrapをそこそこ活用していて、今回が8つめの購入テーマだった(総計$81も使ってた......)。

f:id:a-know:20181104072107p:plain

いろんなテーマを見ていると、創作意欲がムクムクと湧いてくるのでオススメです。

ロゴについては、Squarespace で生成。

また Pixela の場合、生成されるSVG画像・その生成についてもフロントエンドと言えるかもしれない。SVGデータの作成には、Goのtemplateを使っている。Pixelaとは別に、以前からgrass-graphというサービスを開発・運用しているのだけど、今回、SVGを動的に生成するコードを今回くらいの速度で実装できたのは、その運用によってノウハウ(特に "草SVG" に対するノウハウw)がある程度たまってたからかなー、と思っている。じゃないと、SVGに触る機会すらなかったと思う......。。

あと、最近APIドキュメントを別ページ(GitHub Pages + Jekyll)として切り出したんだけど、これには GitHub - Wiredcraft/carte: Simple Jekyll-based documentation site for APIs. を使わせてもらった。

docs.pixe.la

そして、サービス公開後にページ内にあった変なところを見つけて修正に参加してくれた id:mako_wis さん、本当にありがとうございます!

モニタリング

GAEにおける使用リソースなどの推移の確認には、基本的にはGCP標準のダッシュボードで確認することになる。

f:id:a-know:20181104072713p:plain

Mackerel の中の人として、なんとかして Mackerel を使って何か見たい、ということで、Goアプリケーション内で memcache.Stats(Context) を呼び出すことにより取れる値を Mackerel に投稿することで、memcacheまわりのメトリックについては Mackerel で見れるようにしてみた。

f:id:a-know:20181104072750p:plain

個人的には、こういった監視のための仕組みをサービス公開前の段階からあらかじめ仕込めておけたのは良かったなーと思っている。

またサービスの正常性の監視としては、GAEのようなPaaSで動くアプリケーションだとどうしたもんか悩ましかったんだけど、いったん、Pixelaを構成するAPIひととおりに対して Mackerel の外形監視を仕込むことで対応してみている。

f:id:a-know:20181104072835p:plain

f:id:a-know:20181104072904p:plain

参照系だけでなく更新系も含めて、可能なものについては全てに対して設定している。監視だけで結構なリクエストが発生してしまうけど、これによって、プラットフォームの不調だけでなく、アプリケーションが不意に壊れてしまったりしてないか、みたいな不安についてもぐっと減らすことができたなと思っている。

ネーミング

これは結構紆余曲折があった。w

まず最初にネーミングについて思いを馳せたのは GitHub リポジトリを作るときだったんだけど、このときはまだそれほど真面目に考えてなくて、「草を生やすわけだから、とりま www で!ポチー」みたいな感じで勢いよくリポジトリを作った。なお、今もまだ www のままの模様。w

f:id:a-know:20181104072933p:plain

で、その後いよいよ名前を決めなきゃいけない、ってなったときに、「草を生やすAPIサービスだから...... kusAPI(くさピ) でどや!」となり、そのまま結構最後の方までこの名前で進めていた。

ただ、いよいよリリース直前、というタイミングでのCTOレビュー(後述)で僕から「 kusAPI って名前なんですけど、どうですかね?どんなサービスかイメージが付く名前がいいかな、と思ってつけたんですけど......」と聞いてみたところ、「うーん、なんか kuso っぽくないです?」と言ってもらってようやく目が覚めた。w ドメインとか取る前で本当によかった...。。

ただ、大変だったのはこのあと。それまで考えてた名前がダメならどんな名前にすればええんや、ということで、結構悩んだ。当時の右往左往っぷりが Trello のカードに残っている。w

f:id:a-know:20181104073108p:plain

Pixelapion ピクセレーピョン めちゃくちゃかわいい、どうしても api を推したかったらしい。 PixelaPixel ピクセラピクセル 謎のテクマクマヤコン感。

その後、ドメイン検索をしたら pixe.la が空いていたこともあり、無事に今の形に落ち着いた。よかったよかった。

開発期間・進め方

このアイデアを実現させようと手を動かし始めたのは、8月31日。

f:id:a-know:20181104073210p:plain

基本的に僕は平日は昼間の仕事に全力投球している(※誇張しています)ので、こうした趣味活動に手をつけられるのは週末・土日祝日になる。もし Pixela に関する謝辞を述べる機会があるとするならば、開発着手した8月31日からサービス公開の10月14日までの間に何度かあった三連休に対して僕は最大限の感謝の意を表することになるであろう。w

開発におけるタスク管理などには、Trelloを使った。ふとした拍子に実装のアイデアを思いついたり、なんてことはしょっちゅうだったので、iOSアプリもある Trello には大変助けられた。

f:id:a-know:20181104073349p:plain

残暑が続いていたということもあり、チェアリングも活用して気分転換しながら実装を進めることができた。

僕が勤める会社・はてな では、エンジニア全員に対してメンターが付くのだけど、僕のメンターでありCTOのmotemenさんとの1on1の場で、「今、おもしろWebサービスを作っている」「ある程度できたらレビューしてほしい」と、開発の早い段階から伝えていた。

そして実際にレビューしてもらったときには、とてもおもしろがってもらえて、その場ですぐユーザー登録からグラフを表示させるところまで体験してもらえたのがとても良かった(motemenさんもすごくて、「kusAPI、なるほど、草を生やせるサービスなのかな?」みたいに、その場その場で感じたこと思ったことを口に出しながら触ってくれたので、僕は想像以上のフィードバックを得られた)。そして上述のネーミングに対する意見とかあると便利そうな機能( /increment )についてのアドバイスをもらえたのは去ることながら、僕が業務で携わっているプロダクト(サーバー監視サービス・Mackerel)になぞらえて「Pixela、これもまた時系列データベースですね」と言ってもらえたことが、とても嬉しかった。

今現在のチーム内での役割柄、日頃業務においてコードを書くことが少ないから、殊更うれしかったんだと思う。

今後も Pixela をよろしくおねがいします

というところで、「Pixelaを支える技術」というイキったタイトルにしつつも裏話的なところに終始してしまった気はするけれど、こんなかんじである。僕はこれまでに2桁個以上のアプリケーション、Webサービスを趣味で作ってきたけれど、今回ほどに多くの人に触ってもらえたものはなかった。特に、Pixela の API に対するAPIクライアントなどを色んな人に・次々と作ってもらえたときのその得も言われぬ感動は、これまでに味わったことのないものだった。

今後もまた、自分がおもしろいと思えるアイデアを形にして、「おもしろくて楽しい場所であるインターネット」の一部となれるようなものを作り続けていけたらなー。と思う。ちなみに Pixela は、その稼働に多少のお金が掛かってはいるけれども、僕個人で十分まかなえる範囲内・今後も十分持続可能であろう範囲内に収まっているので、安心してもっともっと使ってほしいなーと思う!

GDPR対応などについて

そして、21世紀のこの現代において、インターネットを「おもしろくて楽しい場所」とするためにもはや避けて通れなくなったことがいくつかあると思っていて、そのうちのひとつが、利用規約の整備だったりGDPRへの対応だったりすると思っている。僕がWebサービスを作るときというのは、だいたい何か一つくらいは自分にとっての新しいことへの挑戦が含まれるんだけど、今回のPixelaにおけるそれが、まさに「利用規約の整備」「GDPR対応」に取り組むこと、だった。

実は、このままこのエントリでそれらについても書こうと思っていたんだけど、ここまで書いてきた話が思いのほか長くなってしまったので、GDPR対応などについては次回・別のエントリにしたいと思う。できるだけ早めに書きたいと思っているので、書けたときにはぜひそちらも読まれたい!

*1: 昔のGAE(GCP というものがまだ存在していなかった時代)では、GAEとdatastore(appengine datastore)はニコイチ・セットで存在しているようなものだった。それが最近になって、datastoreだけ独立したマネージドサービスとして切り出されて "Google Cloud Datastore" となり、そして今や、appengine datastore の方は無くなっていく流れにあるようす。なおこれは全て聞きかじりの知識である。

*2:この memcache についても datastore と同じ流れになるのかなぁ?