koukiblog

たぶんweb系の話題

APIGatewayパターンとBFF

マイクロサービスアーキテクチャでよく利用される、APIGatewayパターンとBFFについて調べたのでメモ

なぜAPIGateway?

マイクロサービスアーキテクチャでシステムを構築する場合、複数のサービスを組み合わせる形でシステムが構築される。

たとえば、ECサイトの商品詳細を表示するために、商品情報サービス、レコメンデーションサービス、レビューサービスを利用する必要がある、ということ。この場合アプリケーションの実装によっては並行リクエストを行うのが難しかったり、モバイルアプリケーションの場合は帯域の問題も発生する。(その他にもいろいろあるが割愛)

そこで、アプリケーションとサービスの間にAPIGatewayというレイヤーを追加することで問題を解決する。

APIGatewayの実装

http://microservices.io/i/apigateway.jpg 単純な場合はこの図のようになって、全てのアプリケーションからのリクエストを1つのAPIGatewayで受け付ける。

ただ、これはOSFA(one size fit all)APIというアンチパターンになってしまう可能性がある。アンチパターンかどうかは微妙なところだが、Netflixのblog( Embracing the Differences : Inside the Netflix API Redesign ) でOSAFには限界があるという記述がある。

そこでBFF

アプリケーション毎にAPIGatewayを用意し、それぞれのAPIGatewayは各クライアント専用のエンドポイントを定義する。

http://microservices.io/i/bffe.png

SPAの文脈でnodeJSを置いてそれをBFFと呼んでいることがあるが、マイクロサービスアーキテクチャのBFFとはちょっと違うのかなーという印象。例えばRailsなどのモノシリックのWebアプリケーションを、nodeJS + RailsAPIに分割する場合、SPAでいうとnodeJSがBFFになるが、マイクロサービスアーキテクチャのBFFは、nodeJSとRailsAPIの間にAPIGatewayを置くのがBFFになる、はず。(多分

感想

APIGatewayといえばAWSのAPIGateway、BFFといえば、SPAの文脈のサーバサイドJS、というイメージが強く、間違って認識していたことも多かったので改めて調べなおしてよかった。

ソースはここ

API gateway pattern

Chefで1年運用した感想

仕事でChefを利用しているのですが、なんでこんな難しいんだろうなーという感覚に対して、ある程度自分の答えを出せたのでメモ。

アーキテクチャ

Runbook — Chef Docs

Chefは基本的にClient-Serverモデルです。Cehf Clientをnode(サーバ)にインストールし、Chef Serverからrecipeをダウンロードして実行する、という考え方です。ぱっと思いつく利用方法である「手元でレシピを作ってそれをサーバで実行、適用させる」ことができません。まずnodeとして登録し、そのnodeにroleやrun_listなどの形でレシピを定義し、convergeするという流れになります。

サーバ側は、Nginx, Apache Solr, RabbitMQ, Redisなどなど、かなり分厚い技術スタックを持っています。この辺りがメリットでもありデメリットでもあるんじゃないかなと思います。

Chef Soloはもういない

Chef Soloはもうありません。(といっても4年前の話なのですが。。)

日本語の情報はChef Solo時代で止まってることが多いです。今も流用できる知識は多いのですが、どこまでがChef Solo固有の話なのか判断するのが難しいので基本的にはChef Solo使っていたら読む必要はないと思います。

現在はChef Zeroを利用します。公式のブログがこちら。 blog.chef.io

Chefのメリット

Ansible,Itamaeなどのツールに比べてどういうメリットがあるのかというと、Chef Serverにより、数千台規模までスケールできるアーキテクチャになっていることなんじゃないかなと思います。Chefはこのためにサーバー側をErlangで書き直したようです。 実績として、有名どころではFacebookがあります。

Facebook | Chef

アプリケーションのデプロイについて

Chefでやるかどうかは、利用者の判断に任されている、という認識です。deploy resourceは一応用意されているけど、あまり便利ではありません。ある特定の状態に収束させるという考え方と、頻繁に更新、ロールバックがあるデプロイといは相性が悪いのではと思っています。

正式なサポートとしては、Chef Automateという有料の上位製品で、Chef Workflowというプロダクトがあります。こっちではちゃんとサポートされていそうですが、有料プロダクトなこともあり未検証です。

OpsWorks Stackについて

仕事でChefを触る場合はOpsWorks Stack( https://aws.amazon.com/jp/opsworks/ )の場合が多いんじゃないでしょうか。 OpsWorks Stackは内部的にはChef Zeroを利用していて、cookbook,nodeの管理をOpsWorks Stack独自の方法で行なっています。 そのため、レシピの知識はそのまま流用できますが、Nodeの管理については独自の用語や考え方を学ぶ必要があります(ありました)

また、AWSとしてはあまり積極的にメンテしていく方針ではないみたいで、今後新規で採用するのはちょっと微妙かなと思いました。 今後はChef, Puppetそれぞれの商用製品である、Chef Automate, Puppet Enterpriseをマネージドで提供する方針のようです。OpsWorks Stackとして提供していたソリューションをChef,Puppetそれぞれの商用製品がカバーし始めたため、同じようなツールをAWSとして開発はしない、みたいな感じだと想像してます。

chef-apply

ぱっとレシピを試したい場合は、Vagrantなどで仮想マシンを作ってchef-applyが一番手軽です。 www.creationline.com

最近あまり流行っているように見えない理由

  • Ansible, Itamaeの方が手軽
  • Docker使い出すとkubernetesという選択肢も。。
  • Chefがうまくはまる分野はブログなどでアウトプットすることが少ないエンタープライズ系な気がする

あたりかなと思っています。

まとめ

小規模のシステムにChefを採用するメリットは薄くなってきていると思います。OpsWorks Stackは別でしたが、OpsWorks Stack自体が今後新規で採用するには微妙。

複数のプロダクトを運用している組織全体でChefを利用するのは、Chefが想定しているユースケースにうまくはまりそうな気がしました。今後機会あれば検討してみたいです。

Rubyのマイナーバージョンを上げたらSyntaxErrorになった件

あるプロジェクトのRubyのバージョンを 2.4.0 から2.4.3に上げたら今まで問題なかった構文がSyntaxErrorになるようになりました。経緯が気になって調べたのでそのメモです。

問題の構文は

let :product { create(product)}

のようなものです。(letの括弧を省略して、{}でブロックを渡している)

これが、Ruby 2.4.1までは、

$ruby -ce "m :a {}"
> Syntax OK

となってok 2.4.2(2.4.3と同じ挙動でした)に上げると

$ruby -ce "m :a {}"
>-e:1: syntax error, unexpected '{', expecting end-of-input
m :a {}
      ^

m(:a {}) と解釈されるみたいでエラー

マイナーバージョンアップで後方互換性壊すことなんてあるのかなーと思ってソースコードを見てみると、それっぽいコミットを発見しました。ちなみに僕は全くRubyのコードは読めません。ただ、構文解析周りだろうなーと思って2.4.1から2.4.2のparse.yに変更があったコミットを調べて見つけました。

github.com

このコミットから辿れるruby-coreがこれで

[ruby-core:81037] [Ruby trunk Bug#13547] [].delete 1 { 'NG' }

バグチケットがこれです

Bug #13547: [].delete 1 { 'NG' } - Ruby trunk - Ruby Issue Tracking System

内容を確認すると、2.4.2へのバージョンアップで壊れたのではなく、2.4.0, 2.4.1でだけ意図せず使えていたのがバグで、本来の挙動が文法エラーだったようです。

僕が遭遇した状況と同じようなバグチケットもいくつか確認できました。

Bug #13939: Ruby 2.4.2 has issue supporting Seattle.rb style for define_method - Ruby trunk - Ruby Issue Tracking System

Bug #13898: Block parsing regression - Ruby trunk - Ruby Issue Tracking System

Bug #14023: SyntaxError on array argument and block - Ruby trunk - Ruby Issue Tracking System

というわけで、対応としてはRubyはバージョンアップに問題はない。エラーになる構文は今まで通っていたのが間違いだったので全て修正する、ということに。

文法エラーになるようなバグに遭遇したのは初めてだったので、普段何気なく使っているRubyも日々いろんな方の労力で維持・開発されてるんだなーと実感できた出来事でした。 今後はまめにバージョンアップして、何かエラーがあれば報告するぐらいの貢献は行なっていきたい。

RubocopとParserの関係

今日遭遇した問題に対応するのに、RubocopとParserの関係がよくわかっていなくて混乱したのでメモ。

rubocop?

github.com こっちはみんな知ってると思いますがruby用の静的解析ツールです。

Parser

github.com

rubyで書かれたrubyのパーサーです。rubyソースコードを読み込みASTを作ります。rubocopの内部で利用されています。 rubocopが利用しているParserのバージョンは -Vオプションで表示できます

rubocop -V
> 0.52.0 (using Parser 2.5.0.2, running on ruby 2.4.1 x86_64-darwin16)

今回遭遇した問題

ある日突然SideCIでだけrubocopのエラーが検出されるようになりました

Lint/Syntax: unexpected token tRCURLY
(Using Ruby 2.4 parser; configure using TargetRubyVersion parameter, under AllCops)

のようなエラーです。 SideCIに問い合わせてみたところ、Parserのバージョンの可能性を教えてもらい確認してみたところ手元の環境でも再現し、問題を特定することができました。

Parserは、Ruby2.5系に対応するためにバージョンアップしており、SideCIは2.5系のコードを解析するためにParserのバージョンアップが必須。手元の環境はGemfileでParserのバージョン指定していなかったので古いになっててエラー検出されず、という状態でした。

対応

結局できることはなくて、様子見になっています。

Parserにはissue報告済み。修正できるならやってみようと思ったのですが、Rubyのパース処理は僕の理解を超えてました。同じような報告が過去にいくつかあがっててメンテナンス大変そうです。

unexpected token tLCURLY at 2.5.0.2 · Issue #454 · whitequark/parser · GitHub

SideCIには、Parserのバージョンを利用者側で指定できるようにしたいっていう要望を伝えたところ検討してもらえるようでした。

Lint/Syntax 系のエラーはrubocopではなくparser由来のものってところだけ覚えておくと、色々対応できると思います。 http://www.rubydoc.info/gems/rubocop/RuboCop/Cop/Lint/Syntax

データ分析を始める前に読んだ本と読んだ後にやったこと

きっかけ

1年ほど前に受託開発をやっている会社からwebサービスを運営している会社に転職したのですが、いきなり新しいサービスの立ち上げに参加しそのあとの運用も担当することになったためです。 僕と事業担当者で施策出したりしてたのですが、勘と雰囲気でやってもなかなか進まないし、ここはデータ分析かなーと。 データサイエンティストとして仕事してる友人におすすめの書籍を教えてもらって全部読んでみました。

読んだ本

データドリブンマーケティング

Amazonの社員が読んでるらしい。LTV、CPA、解約率など基本的な単語の意味を知ることができる www.amazon.co.jp

ロジカルデータ分析

何のためにデータ分析するのかがよくまとまっている。帯に「パッション」ってあるように各所エモい。 www.amazon.co.jp

確率思考の戦略論

データ分析というかマーケティングの本。データを使ってどうしたいのかという点で参考にできる www.amazon.co.jp

戦略的データサイエンス概論

Webエンジニアがぱっと読んですぐ実践できる内容ではない。 データサイエンティストを雇うとこういうことをやってくれるのか!という学びがありました。 www.amazon.co.jp

ビッグデータ分析・活用のためのSQLレシピ

SQLのレシピ集。普段の業務では一番役に立つ。これがあるだけで様々な指標をSQL一発で出せます。 あらためてSQL勉強し直しました。特に分析関数。 www.amazon.co.jp

やったこと

いきなりかっこいいデータ分析をすることはできないが、ログ設計してデータ分析をするところまではやるべき、となってそこまでは一気に進めました。たしかその判断したのがリリース1週間前ぐらいだった気がしますが、多少壊れててもユーザーには影響ないしいいかと思ってやってしまいました。 やったことは以下の3つです

GitHub - kaizenplatform/fluent-plugin-bigquery これ使えばさくっとできました。感謝。

  • EmbulkでRDBのデータを全てBigQueryにinsert

RDBに格納しているデータを個人情報はマスクした状態でBigQueryにinsertしています。 cronを設定して深夜に全件洗い替えしてます。

  • データの可視化にはGoogleDataStudioを利用

無料で使えたのでさくっと導入。

現状

データをがっつり活用できているかというとまだ全然そんなレベルではないのですが、何か仮説があったときに実際のデータに対してSQLを利用して数値を確認し、必要に応じてレポート化して定期的にモニタリングするような運用ができるようになりました。 いずれはフルタイムでデータサイエンティスト雇って一緒に何かやりたい!

Mediumで独自ドメインを使う時

ここ(https://help.medium.com/hc/en-us/articles/115005579728-Get-started-with-custom-domains)に書いてある通りなのですが、publicationを作成し、作成したpublicationの設定画面から独自ドメインの設定を行うことができます。 金額は$75です。 "Use your own domain"の遷移先はクレジットカード情報の入力フォームになっていて、決済が完了するとMediumからメールが送られてきます。 そのメールにDNSの設定情報が記載されているので、その通りに設定すれば完了です。

medium.com この公式っぽい記事が古くて、問い合わせフォームから問い合わせそうになってしまったのでメモ。

vueのXSSの件について考えてみた

↓の記事のことです。 qiita.com

vue管理のテンプレートに、UGC(User Generated Content)を含めない。 というのが原則だとは思うのだけど、既にRailsレンダリングしてるviewのイベント周りだけvueに任せたい、など、vueを薄く使っている場合はサーバサイドのテンプレートエンジンでvueのテンプレートを出力することは結構あるんじゃないでしょうか。

vue側の反応

fix #4223, add interpolation config. by alfa-jpn · Pull Request #6203 · vuejs/vue · GitHub

However, there is no silver bullet for server side vue template, especially when users want to mix user generated content with programmer’s template. IMHO, Vue’s users can and should think over how to separate UGC and template. On the other hand, Vue’s API is already large enough. Vue.config.interpolation can be easily replaced by v-pre that already exists. Users can even provide non-matching regex to mock this pull request. While we thank your contribution, we might still keep our API surface not too large. (Vue’s API is already not as small as it used to be). For more reasoning, please see #6004 (comment).

ユーザー側でなんとかしてほしいということで、まぁそうだねーいう感じ。

vueには、v-preという機能が既にあり、v-pre配下はvueのコンパイルがスキップされるのでそれを使えばよいということです。 https://jp.vuejs.org/v2/api/index.html#v-pre

{{ }} をサーバー側で防ぐ

ぱっと思いついたのは、サーバ側でhtml escapeするときにvueのマスタッシュ構文にも対応するという方法です。 ただ、HTMLエスケープは、Railsのように自動でHTMLエスケープしている環境ではパフォーマンス上重要な箇所( Escape Velocity · GitHub )であり、そこに追加するのはあまりよくない気がしました。

なので、Railsでやるとすれば↓のようにマスタッシュ構文を削除するなりスペース挟むなりするhelper作って明示的に防ぐことになるのかなーと思うのですが

<div>
  <%= v_safe(@user.name) %>
</div>

その場合

<div v-pre>
  <%= @user.name %>
</div>

とほぼ同じだしv-pre使った方が確実でvueのコンパイルもスキップできてパフォーマンス上も有利だし、わざわざ自前でhelper書く意味はなさそう。

結論

vueのテンプレートと、HTMLは違うコンテキストを持っている、ということを意識してやっていくしかなさそう。仕組みで防げなくて微妙な感じもしますが、PRへのコメントにもあった通り銀の弾丸はないってことなんでしょう。

interpolation無効化するのは超単純なアプリの場合はそれでいいかもしれないけど、ある程度複雑なアプリになると初期表示後のイベントとかどこかで必要になるんじゃないかなと思いました。