Google Cloud Platformでサービスを公開してみた

前々からセコセコと作っていた線画抽出のネットワークですが、とりあえず外部から利用できるようにしてみようと、Google Cloud Platform（以下GCP）で公開してみました。

仕事ではAppEngineを触ったことがありましたが、まとも？にGCPを利用するのは今回が初めてでした。そういうわけで、サービスの構成とかと合わせて、GCPの使い勝手を主観的に書いていこうと思います。

## サービスについて ##
これをサービスと呼ぶと、実際にサービスを作っている人に怒られそうですが、それくらいしか適当な呼び名が無いのでご勘弁を。

http://line-art-generator.deltabox.site/

![site_image.png](https://qiita-image-store.s3.amazonaws.com/0/12490/e6a12ca5-c085-cc85-64c5-fa44d88a6253.png)

* 画像から線画を抽出します
* 抽出範囲を選択できます
* 抽出した画像は右クリックから保存できます
* 抽出できる画像は、 **512×512** が最大サイズです

### アーキテクチャ ###

アーキテクチャとしては次のようになっています。

* Google Compute Engine上で動作
    * フロントエンドとバックエンドは同じコンテナ上に載せてます
    * [Container-Optimized OS](https://cloud.google.com/container-optimized-os/?hl=ja) を利用しています
* フロントエンド
    * Reactベースで作成
    * JS部分は全て [BuckleScript](http://bucklescript.github.io/bucklescript/) で構築
* バックエンド
    * WerkzeugでAPIサーバー + 静的ファイルの配布
    * 線画抽出のネットワークはTensorflowで構築したものを利用


静的ファイルについては、本来であればCDNとかCloud Storageに置いておく方がいいと思いますが、まぁ量はないからいいかな、と。

### 抽出速度 ###

Tensorflowで線画抽出を行っていますが、実用的な速度を求める場合、やはりGPUは必須です。

ローカルで実行してみたところだと、

* GPU（NVidia GeForce 1060）
  * 0.5秒くらい
* CPU（Core i7 4790 3.60GHz）
  * 5秒くらい（8コア全部使って）

というくらいの差が出ました。約10倍といったところですね。GCP上だと16秒ほどかかります（n1-highcpu-2）。

GCP上には、個人として構築しているので、GPUなんてつけらんねー（preemptibleですら **$500/月↑** ）です。。。そのため、CPUだけでなんとかなるギリギリのサイズとして、512×512という制約をかけています。
16秒かかるってのがなんとかなるって時間なのかどうかは置いておいて・・・。

> 参考までに、800×800くらいの画像を1CPUでやったりすると、2分（！）とかかかります

## GCPの感想 ##

### Deployment Manager ###
いくら学ぶためとは言え、一から手動で作るってのは疲れるので、基本路線としてはCloudFormation的な感じで一括で作成したいなーと。
そのためのサービスとして、 [DeploymentManager](https://cloud.google.com/deployment-manager/) があります。

参考： http://qiita.com/marcy-terui/items/1d19aa089fd076873180

参考のURLは若干古く、現在ではCloudFormationでもYAMLが利用できます。CloudFormationとDeploymentManagerの大きな違いは、テンプレートに対する考え方かと思います。

今回は、公式のサンプル（これも若干古く、いくつか書き換えたりしましたが）を利用しましたが、このサンプルを見ると、DeploymentManagerとCloudFormationの違いは肌で感じられると思います。

https://cloud.google.com/deployment-manager/create-advanced-deployment?hl=ja

どっちが優れているとかではないですが、DeploymentManagerの方がよりプログラムっぽい仕上がりになって、CloudFormationはまさにTemplate!って感じになるのが違いかなぁと。

まぁ、どっちにしても書くのが面倒なことは面倒なんですが・・・。

個人的に書く分には、DeploymentManager方が（調べるっていう意味で）楽です。企業とかでより厳密な管理、とかの場合にはCFnのほうなのかなぁって（ChangeSetがあるので）思います。

### ServiceAccount ###
しばらくぶりに触りましたが、権限周りが大分変わっていて驚きました。AWSのIAMもそうですが、権限関連は大分ややこしいっていうのは変わりませんね。

ただ、GCPの方が、画面からだけでなんとかなるので、IAMのJSONと格闘する必要がない分、（若干）わかりやすいかもしれません。

また、ServiceAccountをインスタンスに付ける際に、インスタンス毎に権限をカスタマイズすることができるので、そのへんはIAMよりも融通が効きそうです。

> 当然ながらやり過ぎると権限がわけわかめになるので、基本はServiceAccountをrole毎に分けて付ける、というのが常套手段かと。

### Console ###
AWSのManagement Consoleも相当高機能ですが、GCPのConsoleはある意味でその上を行きます。特にCompute Instanceにブラウザ上から直接SSH出来る謎技術は、デバッグにおいて非常に役立ちました。

それと、デザインについて全体的な統一感が取れているので、サービス間でありゃ？となることが（個人的に）少なかったです。

### preemptible ###
AWSのSpotInstanceに相当する感じですが、SpotInstanceよりもシンプルな分制約も多い、という感じです。

SpotFleetとかに相当するものはまだなさそうなので、コスト最適化とかはAWSの方が考えやすいかも知れません。
また。SpotInstanceは条件が許せばずっと立ちっぱなしにも出来ますが、preemptibleは24時間で強制的に死ぬので、SpotInstanceと同じような使い勝手、というわけにはいかなさそうです。

ただ、SpotInstanceを立てるときのめんどくさい感じが一切なく、検証とかでちょっとだけ立てたい、という時には物凄い役立ちます。

### External IPが色々と必要 ###
基本的にはExternal IPはあんまり使いたくないんですが、外部との通信をする場合はNATなども必要になるのは、AWSと変わらないです。

ただ、Google Container Registryなど、GCPのAPIしか外部アクセスがないのであれば、 [Private Google Access](https://cloud.google.com/compute/docs/private-google-access/configure-private-google-access) を有効にすることで、external IpをつけなくてもLoadBalancerにぶら下げることができます。

AWSで言うS3 Endpointみたいなものですね。細かい制御は効かないようですが（あんまり効かせても意味無いですが）、こっちの方が便利ですね。VPC Endpointももっと出て欲しいです。


## DeepLearningを利用したサービスの課題 ##
実際に個人でやってみたわけですが、学習自体はローカルで時間をかければまぁなんとかなる（GPUは **必須** ）ようにはなっています。ただ、やはりサービスとなると、計算量にもよりますが、GPUが高い確率で必要となりそうです。
特に、ネットワークが深ければ深いほど、CPUの並列度ではどんどん遅くなっていくため、GPUが必要になる確率は上がります。今回のネットワークは、 Encoder＝10層/Decoder＝9層（全部にBatch normalization入）という構成です。このレベルの構成でも10秒単位の時間がかかるのであれば、もっと他の方法を考える必要があるでしょう・・・。

Tensorflowの場合ServingというC++で作られているアプリケーションがあるため、これを利用することでスピードが上がるという可能性があります。
ただ、今回時間がかかっているような部分は、そもそもC++で処理されている場所なはずなので、果たして速度が上がるのかどうか・・・。

なお、このサービスは送られた画像はメモリ上にだけ存在しています。破産したくないので、一切画像の保存などはしていません。

必要最小限の実装ですが、待つ時間がある方は、好きな画像を突っ込んで見てください。経験上、綺麗に出るのは次のような画像です。

* 線がかなりくっきりしている
* 元々の線の上にエフェクトが無い
* 濃淡がはっきりしている

どっちかというとGCPを使ってみたかったという感じですが、まぁそれもありということで。