ラベル GAE の投稿を表示しています。 すべての投稿を表示
ラベル GAE の投稿を表示しています。 すべての投稿を表示

2025/07/18

さくらのクラウドとサーバレスアーキテクチャ

さくらのクラウドの注目記事

 私が現在利用しているクラウドはGoogle Cloudのみですが、その他には国産クラウドである「さくらのクラウド」にも興味を持っています。現在ガバメントクラウド認定を目指して整備が進められているさくらのクラウドですが、そのアプリケーション実行基盤である「AppRun」はサーバレスであることが発表されています。

さくらインターネット、アプリケーション実行基盤「AppRun β版」にて製品版のトライアルを開始

さくらのクラウド、コンテナをサーバレスで実行する「AppRun」製品版トライアルを開始。トライアル中は全機能が無料に


サーバレスアーキテクチャとは

2008年のApp Engineリリースから使えるようになったものなので、まだご存じない技術者も見かけます。理解が簡単ではないので、普及はこれからといった印象です。またFaaS(Function as a Service)と混同しやすいので注意が必要です。


サーバレスアーキテクチャは、このブログの記事「サーバレスアーキテクチャ」でも説明しています。コンピューティングにおいては、アプリケーションの実行インスタンスは常に起動しているわけではなく、必要な時に必要な分だけのリソースを割り当ててアプリケーションの実行を行うアーキテクチャです。リソースの管理はプラットフォームがやってくれるので、ユーザは意識する必要がありません。

サーバレス アーキテクチャとは

サーバーレス・コンピューティングとは

クラウド以前の従来のサーバは想定された利用状況に応じてあらかじめリソースを割り当てておきます。リソースの管理は、夜間など利用の少ない時間帯にバッチ処理を走らせるなど、計画的で柔軟性はありません。

クラウドでも非サーバレス場合は、最低1つ以上の実行インスタンスが常に割り当てられます。夜間バッチの実装など従来と変わらない設計が主流です。

それに対してサーバレスアーキテクチャでは、リアルタイムで必要なだけリソースを割り当て/開放します。これによりランニングコストの低減や、サーバビジーによる一時的なサービス提供不能の発生低減が期待できます。これはユーザにとって大きなメリットとなります。


しかしクラウドベンダー目線で考えると、上記とは違う特徴を実現することが目的ではないかと考えられます。

リアルタイムにリソース割り当てを変更できることから、データセンター全体でリソースを有効利用できるようになります。またリソースの割り当ては基本的に永続的ではなくなるので、ハードウェアのメンテナンスをしやすくなる(ユーザにとってもメンテナンスの影響を受けにくくなる)利点も考えられます。


「みんなのクラウド」と表現されることがありますが、データセンターのリソースをユーザみんなで共有し、その割り当てをより柔軟にして(非サーバレス以上の)最大限の効果を狙うのがサーバレスアーキテクチャであると言えると思います。


サーバレスアーキテクチャの現状

現在の日本のwebアプリケーションは、まだサーバレスアーキテクチャを採用したものは少ないように見えます。キーワードとしてKubernetesが出てくるアプリケーションは、サーバレスアーキテクチャではありません。それどころかクラウドに最適化されていなくて従来の延長線上でしかないものもあるようです。

現在クラウドの利用方法の主流は、Docker+Kubernetesの構成と思われます。この構成が流行った要因は以下のようなものと考えられます。

  • どのクラウドベンダーでも同じように実装でき、ベンダーロックインが発生しない。これは特に受託開発やSESにとって好都合。
  • クラウド以前の従来の技術・知識・プロダクト(ライブラリ、フレームワーク)を使いまわせる。新たに覚えるべきことはDockerとKubernetesのみ。
  • Kubernetesによって一応スケーリングも可能なので、スケーリングの恩恵も得られる。
主に開発者の都合が中心です。ひどい場合はDockerコンテナにクラウド以前のレガシーな構成をそのまま詰め込んだだけのケースもありそうです。Kubernetesでスケーリングさせているつもりが、その効果さえ確認していないケースもあるようです。クラウドに最適化しないのであれば、クラウドによる恩恵も最低限しか受けられません。


サーバレスアーキテクチャではコンテナオーケストレーションにはKubernetesではなく、Knative(あるいは相当のもの)を用います。


ランニングコストについて

「クラウドに移行したもののランニングコストが予測よりずっと高く、期待したように下がらない」という話を時々聞きます。

自治体システム標準化、ガバクラ移行で運用コスト2~4倍に悲鳴「議会に通らない」(有料記事ですが1ページ目のみ無料で閲覧可能)

 ガバメントクラウド移行で自治体の年間コストが5.7倍に…計画の重要性

 ガバメントクラウド移行で自治体の運用コストは本当に下がるのか?


上記の引用はガバメントクラウドに関するものですが、過去に私が関わった民間企業のwebアプリケーションでも同様の現象が起きていました。この企業で使っていたのもKubernetes(GKE)ですが、「思うようにスケーリングできない」ので無駄にリソースを割り当ててアプリケーションを実行しているという状況でした。

他の原因の1つは、クラウド以前の従来のアーキテクチャを引きずったまま移行していることにあるのではないかと考えられます。クラウドに最適化しないのであれば、クラウドに乗り換える効果も限定的でしかないのは想像に難しくありません。


その他にも、従来の手作業・紙ベースの手順をそのままにしておくことを優先したため、コンピュータ処理に適さない手順を無理やり実装したことによる弊害もあると考えられます。これは今に始まったことではなく、クラウドやアーキテクチャによるものでもない別次元の話なので、本記事ではこれ以上取り上げません。


サーバレスアーキテクチャでの実装

実際にサーバレスアーキテクチャで実装するには、対応するPaaSのプロダクトを利用することになります。しかしそれだけでは実装後に以下のような性能上の問題が発生することがあります。

  • レスポンスが悪い、または悪くなることがある。
  • バッチ処理などバックエンドの処理に時間がかかる。
  • ランニングコストが下がらない。
    (使い方を間違うと効果は得られない)
  • コストの予測が困難。
    (スケーリングする限り非サーバレスでも同じだが、より変化が大きい)


サーバレスアーキテクチャを有効に使いこなすためには、それに対応した設計が必要となります。ここでのノウハウにはサーバレスアーキテクチャを実装した経験からしか得られないものもあります。具体的にここで書ききれる量ではないので別の機会に。

しかしサーバレスアーキテクチャを使いこなせば、スケーリングを最大限に活用できて以下の効果を得られる可能性があります。

  • ランニングコストの削減。
  • サーバビジーによる一時的なサービス不可の減少。


技術的課題

従来のサーバは「起動しておいて必要とされるのを待つ」思想で実装されています。しかしサーバレスアーキテクチャでは「必要とされたから起動する」という思想に変わります。そのためwebサーバのような「ユーザが待っている」ケースでは、起動の遅い実装はユーザを待たせる悪いユーザエクスペリエンスにつながります。バックエンドのような起動の遅延が影響しないケースでは無視できます。

これにより従来のプロダクト(サーバレスアーキテクチャを考量していないライブラリやフレームワーク)が使い物にならなくなっているケースがあります。フレームワークの場合はそれを理解せず使用してしまうと、ほぼすべて再実装でしか改善できないほどの影響も考えられます。たとえ人気で過去に実績もあるプロダクトといえど、サーバレスアーキテクチャを考量してなくて悪影響となるものは、早めに清く捨て去る必要があります。

この思想の違いは開発者にとっては、パラダイムシフトとも言えるほどの影響です。これはサーバレスアーキテクチャの理解や使いこなしを難しくしています。


またクラウドベンダーにより対応の方法がバラバラです。簡単にサーバレスアーキテクチャへの対応をまとめると、以下のようになります。

  • Google Cloud
    • App Engine(Standard Environment):独自コンテナ+Knative、webサーバ。
    • Cloud Run:Docker+Knative。
      • Service:webサーバ。
      • Function:FaaSとしての利用形態で実態はServiceと同じ。
    • GKE + Knative serving:もうCloud Runでよくない?
  • AWS:Serverless Application Modelに基づいてLambdaで実装。
  • Azure:Azure Functionsを利用。
  • さくらのクラウド
    • AppRun:Docker+Knative

そのほかにIBM、Oracleも対応しているようです。

AWS, AzureはFaaS(Function as a Service)の流用にとどまっており、あまりサーバレスアーキテクチャの採用に関して積極的に見えません。

Google CloudはFaaSだけでなくPaaSも用意されており、対応が最も進んでいます。またwebサーバでなくバッチ処理でも同じように使えるプロダクト(Batch, Cloud Run Job)が用意されいます。これらは本来サーバでないのでサーバレスアーキテクチャと呼びませんが、「必要な時に必要な分だけ」リソースを使うことができる点においては同じです。


さくらのクラウドに対する期待

さくらのクラウドでは今後はサーバレスアーキテクチャが普及していくものと予測しているそうです。これは私も同意見で、世の中の技術者たちがサーバレスアーキテクチャの理解を進め、普及していくように活動しています。


2024/02/09

GAEのインスタンス起動時間 #2

動機

Java17 + レガシーバンドルサービスを実装したところ、GAEインスタンスのスピンアップが若干遅いと感じました。いつの間にかJava21もプレビューになっていましたので、あわせてインスタンス起動時間を再測定してみました。

また前回(GAEのインスタンス起動時間)はログで確認していたのですが、その方法の精度にも疑問を感じたので、測定方法も対象も変更しています。


今回はJava8がすでにサポート終了しているので、対象外としました。


測定条件・方法

  • リージョン : 大阪
  • ネットワーク : NTT Flets光 隼、実効300Mbps程度
  • インスタンスクラスはF1。
  • ServletのアプリはGoogleのサンプルに入っていたHelloWorld。
  • SpringBootのアプリはGoogleのサンプルに入っていたHelloWorldそのままなので、バージョンは2.7.18。
  • レガシーバンドルサービスは、Googleのサンプルは余計なコードが入っていたのでpom.xmlのみ参考にし、Hello World相当を自作。
  • GoもHello World相当を自作、今回はフレームワークなしのパターンのみ。
  • ブラウザからAjaxでリクエストし、JavaScriptでレスポンスが返るまでの時間を測定。
  • スピンアップあり/なしの2パターンで、それぞれ10回測定し、平均値を算出。
  • 前記2パターンの差から、スピンアップ+VM起動+フレームワーク起動の時間を計算。

測定結果

単位はすべてmsです。測定結果をtableにすると横長になってレイアウトが崩れたので、画像です。
まずはスピンアップあり。

続いてスピンアップなし。

そして、スピンアップ+VM起動+フレームワーク起動の時間。
Servletspringレガシー
Java11625.24728.62098.6
Java17599.84769.21655.0
Java21630.24622.92451.5
Go1.20176.9--


所感

起動時間は 17<11≒21。わずかとはいえ、最新の21で悪化しているという意外な結果に。まだプレビューなのも影響しているのでしょうか。

SpringBoot

前回と比べてバージョンが2.6.6→2.7.18と変化していますが、結果はほぼ同じ。他はJava21で遅いのに、なぜかspringだけはJava21が最速と、一致しません。なぜ...

レガシーバンドルサービス

遅い気がしたのは気のせいではありませんでした。Java8のスピンアップが遅かったのはもしかして、Googleのカスタマイズのせいだったのではという疑惑が生じます。そしてJava21での遅延が目立って大きくなっています。残念過ぎる...

2024/01/14

ビルドエラー "ERROR: failed to initialize analyzer: getting previous image" の対処 [GAE]

事象

久しぶりにGAEのプロジェクトをビルドすると、mavenで以下のエラーに遭遇しました。
このプロジェクトはappengine-maven-pluginを利用してビルドしています。

[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  01:27 min
[INFO] Finished at: 2024-01-xxTxx:xx:xx+09:00
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal com.google.cloud.tools:appengine-maven-plugin:2.5.0:deploy (default-cli) on project プロジェクトID.サービスID: App Engine application deployment failed: com.google.cloud.tools.appengine.operations.cloudsdk.process.ProcessHandlerException: com.google.cloud.tools.appengine.AppEngineException: Non zero exit: 1 -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoExecutionException


Cloud Buildを確認すると以下のエラーが出てしました。

ERROR: failed to initialize analyzer: getting previous image: getting config file for image "asia.gcr.io/プロジェクトID/app-engine-tmp/app/サービスID/xxxxxx:latest": GET https://storage.googleapis.com/asia.artifacts.プロジェクトID.appspot.com/containers/images/sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx?access_token=REDACTED: unexpected status code 404 Not Found: <error><code>NoSuchKey</code><message>The specified key does not exist.</message><details>No such object: asia.artifacts.プロジェクトID.appspot.com/containers/images/sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx</details></error>


調査

「差分ビルドしているけど前回のビルドイメージが見つからない」と言っているようです。だいぶ前ですが、Cloud Storageのビルドイメージを保存しているバケットにライフサイクルを適用して、生成から14日を過ぎたものは削除するように設定しています。その当時は14日以上空けてビルドしても問題なく再ビルド可能でした。また手作業でCloud Storageのビルドイメージを消してからのビルドも可能でした。それに差分ビルドによるビルド時間短縮の効果もわずかでした。

その後ビルド方法が変更されて差分ビルドしかしてくれなくなったようです。

ネットで探してみると「GAEのデプロイが失敗する: ERROR: (gcloud.app.deploy) Error Response: [9] Cloud build xxxxx status: FAILURE」が見つかりました。まったく同じ状況です。


解決方法

gcloud app deploy のケース

Goで作成しているサービスは上記の記事と同じくgcloud app deploy --no-cacheで回避できました。


maven appengine:deploy のケース

Javaで作成しているサービスはデプロイまでmvn appengine:deployで行っています。appengine-maven-pluginに--no-cacheと同様のパラメータがないか探してみたのですが、見当たりません。serviceAccountKeyFileという怪しいタグがありますがweb上に全く説明が見つからず、またタグ名から推測される動作も異なります。

なぜかJavaで書いたサービスはgcloud app deployだとビルド・デプロイは成功するものの、生成されるwarに依存ライブラリが含まれないので使っていませんでした。

しかし一旦gcloud app deploy --no-cacheでデプロイすればビルドイメージができるので、その後はmvn appengine:deployでデプロイできるようになりました。


2023/12/02

vsReversiアップデート

 Google Cloudのデモとして用意していたvsReversiをアップデートしました。今回はJava8→17の更新だけです。内容は特に変わっていません。Google App EngineのJava8サポート終了が予定されているので、それに備えての更新です。

Java11を飛ばして17にした理由は、単にライフサイクルがJava17のほうが長いからです。App Engineのランタイムサポートスケジュールを参照してください。Java11だと残り1年もないですね。すでにJavaのLTSバージョンは今年秋に21がリリースされていますが、これもそのうちApp Engineで使えるようになることでしょう。


2023/07/03

サーバレス アーキテクチャ

サーバレス アーキテクチャ って何

ちょっとネットで調べてみればわかるのですが、サーバレス アーキテクチャについて書かれた記事はそこそこ数はあります。しかしどれを読んでも結局サーバレスがどういうものなのか理解できない記事ばかりが目立ちます。よく見るのは以下のような例です。

  • こんなサービスですといくつか具体的に紹介されているものの、肝心のサーバレス アーキテクチャそのものについての記述がない。
  • あれではない、これでもないと該当しないサービスの例が挙げてあるものの、では何がサーバレスなのかについては記述がない。
  • 「サーバレス アーキテクチャとはサーバレスな構成です」としか書いてない。←いや説明を説明してw

このような状況なので、調べてもなかなか良い記事にたどり着きません。記事を書いてる人たちも理解していないのではないかと疑いたくなります。


いきなり結論

サーバレス アーキテクチャとは、常時起動しているサーバが存在しない構成

のことです。文字どおりに「サーバがない」と解釈してしまうと「ではどうやって処理するのか」と疑問が湧くだけですが、常時かどうかがポイントです。

以下、従来と比べるとより分かりやすいと思います。


非サーバレス

サーバレスではない従来の構成では、サーバは起動しておいてリクエストが来るのを待っています。リクエストが来なければ、基本的には何もしないでずっと待ち続けています。

GCPでWebサーバを構成する例だと、GCE/GKEに従来のオンプレミスでの構成と同様にWebフレームワーク等をインストールして構成します。

クラウドでは従量課金が基本ですが、アプリが何もしないで待っている間でもプラットフォームから見れば稼働中なので課金されます。またアプリを動かすインフラがスケーリングに対応していない場合、想定される最大の負荷にあわせて構成することになります。その分ランニングコストが高くなります。


サーバレス アーキテクチャ

アプリが必要になった時点で、プラットフォームがアプリを自動的に立ち上げてくれます。アプリが立ち上がったら非サーバレスと同様に、アプリによってリクエストが処理されます。アプリが稼働を停止していると判断されたら、フラットフォームが自動的にアプリを停止してくれます。

クラウドではこのような動作を可能にするプラットフォームが用意されています。当然アプリもそれにふさわしい作り・設定が必要です。アプリが停止した後は、それまでアプリが動作していたインフラ(CPU、メモリ、ネットワーク、etc.)は、別のアプリのために使用されます。そのためインフラ全体では稼働効率をよくすることができます。

また、アプリ起動がインスタンスの増加と同じになるので、水平スケーリングにも対応が容易です。

GCPでWebサーバを構成するなら、GAEを利用します(ただし最小インスタンス数=0設定)。小規模ならCloud Run/Cloud Functionsも利用できます。GKEでも最小Pod数を0にしてネットワークからリクエストで起動するように設定することも可能(なはず)です。負荷の増減に対しても、その瞬間の負荷をさばくのに必要な数だけアプリを立ち上げればいいだけです。

従量課金だとアプリが稼働している間だけ、立ち上がっている数だけ課金されるので、ランニングコストを抑えることができます。


2022/07/06

GAEのインスタンス起動時間

動機

GAEのスタンダード環境では、言語によってインスタンスの起動時間に差があるのは、Java8を含む第1世代VMを利用している人には、良く知られた事実です。既に起動しているインスタンスがあっても、リクエストが増えて新たにインスタンスが起動される場合にも、この起動時間がかかります。そのためインスタンス起動が遅いと、時々レスポンスが悪く感じることになります。

昨年公式に公開されたJava17がいつの間にかGAEでもプレビュー版として使えるようになっていましたので、バージョンアップで起動時間がどう変化したか確かめてみたくなりました。それにSpringの起動時間も気になります。で、言語とそのバージョン + Webフレームワークの違いで、実際のインスタンス起動時間を計ってみました。


最初に断っておきますが、私が使用する(可能性のある)組み合わせのみの測定です。


測定条件・方法

  • 基本的にアプリはGoogleのサンプルに入っていたHelloWorld、それに最低限のログを加えたもの。
  • ただしJava8/Goは手元にあった単純なアプリ。Slim3やObjectifyなどは入っていないので、結果には影響しないはず。
  • SpringBootはGoogleのサンプルに入っていたHelloWorldそのままなので、バージョンは2.6.6。
  • GoのフレームワークはGin。
  • インスタンスクラスはF1。
  • インスタンスが立ち上がっていないことをGCPコンソールで確認してから、ブラウザからリクエスト開始。
  • Cloud Loggingに記録されたログにより、リクエストの到着から、リクエストハンドラの先頭に入れたログが出るまでの時間を計算。
  • それぞれ5回測定し、平均値を算出。
  • 1回測定ごとにGCPコンソールから手動でインスタンスを停止。次の実行まで数分間放置。

測定結果

フレームワーク
なしあり
言語Java82.063-
Java111.0553.737
Java170.9393.551
Go 1.160.1760.205

単位はms。


所感

Java8

過去の経験から3秒くらいと思っていたのですが、なんか早いという結果になりました。意外でしたが、ちょっと嬉しい。

Java11

VMの起動が早いのでしょうか。フレームワークなしならVM起動してても、ほとんど待たされている感覚はありません。PHP/Pythonと同じくらいの起動時間だと思われます。


Java17

まだプレビュー版であることはご理解ください。しかしJava11より少し早くなっているのはいいですね。


SpringBoot

JavaVMに1秒+Springに3秒弱という結果でした。ブラウザを触っていても「あれ、遅いかな」と感じたころに応答が描画される感じです。これだけで十分「使えない」と判断できます。
起動時間の4秒というのは、GAEのJavaというと8しかなかった時代に、起動が遅いといわれていた状況よりも悪くなっています。Springはサーバレスという考えのなかった時代の、「起動しておいて口開けて待っている」ことが前提の時代の産物だけに、時代遅れの遺物になってしまった感があります。
サーバレスでやりたいなら、潔くSpringは捨てましょう。


Go

インタプリタもVMも存在しない、ネイティブ実行されるだけあって、爆速です。ブラウザを触っていても、インスタンス起動が起きていることを全く感じることがありません。


Gin

「フレームワークというよりライブラリ」と言われるだけあって、遅延は30ms程度と、ほとんど影響がありません。SpringBootと対照的な結果ですが、時代が違うのでサーバレスも考慮された結果なのかもしれません。


(2022/07/14追記)

現在、GAEのJava17はプレビューから一般提供に格上げされています。

2022/06/25

残念なレガシーバンドルサービス

GAE/JのJava17対応


(2024/02/26追記)
本記事の内容はすでに古くなっています。また最初の公開時に記載内容に間違いがあった可能性があります。


 久しぶりにApp Engineのドキュメントを見たら、まだプレビュー版ですが、Java17の対応が載っていて驚きました。Java17って去年秋にリリースされたばかりなので、GCPの対応はまだ先かなと想像していたのですが、予想よりずっと早いようです。Java17はJava11以来のLTSなので、注目に値します。


ランタイムの世代

GAEのランタイムには第1世代と第2世代の2種類があります。これらは実行されるランタイムのバージョンが異なるだけでなく、第1世代のみGoogleが実装した拡張機能があります。
これらの拡張機能のうち、よく使われるのは以下ではないでしょうか。
  • Memcache : オンメモリのキャッシュ
  • Search API : Googleのサーチエンジンと同じ全文検索
  • Mail API : メールの送受信
  • Users API : Googleアカウントによるユーザ認証

ランタイムを選択するうえで、これらの機能が必要かどうかも分かれ目になります。

個人的にはまだ第1世代ランタイムであるJava8を使用していて、第2世代ランタイムに移行への移行はまだです。その理由がこれらの機能を使用したいからです。


代替サービス/ライブラリ

以前は第2世代ランタイムでは、前記の第1世代ランタイムでしか提供されない機能の代わりに、他のサービス/ライブラリを使用するように、Googleは説明していました。これらの代替手段は現在でも有効で、本来はこれらの代替手段で対応するものと思います。主なものは以下のとおり。 

Memcache → Memorystore (無料枠なし)
Search API → Elasticsearch
Mail API → SendGrid/Mailgun/Mailgrid など
Users API → Identity Platform/Firebase Authentication/OpenID Connect など


Memcacheは、有料のMemorystoreが嫌なら、ちょっと極端ですが諦めてもいいでしょう。

Search APIは、代わりとして気軽に使えるものが事実上ありません。

Mail APIは、SendGridのみ無料枠が用意されています。SendGridは使いやすくて性能も十分と思います。

Users APIは優秀な代替手段が用意されており、無料枠もあるので、積極的に置き換えるべきと思います。


これらのGAEの機能と同じような状況にあったのがDatastoreですが、Datastoreはすでにサービス終了しています。代わって登場したFirestoreには「Datastore互換モード」があるので、Datastoreとほぼ同じように使えます。DatastoreはJavaライブラリが使いにくいですが、Firestoreはいくらかライブラリが使いやすくなっています。個人的にはFirestoreネイティブモードに乗り換えました。


レガシーバンドルサービスの提供

Java17対応と同時に、第2世代ランタイムに「レガシーバンドルサービス」が提供されるようになっていることに気付いて2度びっくりしました。

レガシーバンドルサービス」は第2世代ランタイムから、第1世代ランタイムのこれらの拡張機能を使用するためのライブラリのようです。これを使用することで、第2世代ランタイムも第1世代ランタイムと同様に使用することが出来るようになるようです。


しかし残念なことに、一番使いたいSearch APIは提供されないようです。

Elasticsearchがあるとはいえ、ちょっと興味本位で調査したい場合や、商用であってもサービス立ち上げ直後で利益が出せるのがいつになるか不安な状態では、安くない月額固定料金のサービスへの乗り換えは厳しいです。


Googleの本音を予想

GAEの第1世代ランタイムに用意されていた拡張機能は、Googleがランタイム(Javaの場合はJavaVM)を独自拡張して実装したのではないかと想像しています (だからランタイムの存在しないGoにはこれらの拡張機能が提供されない)。

App Engineスタンダード環境のランタイム」を読んでいるとGoogleは、第1世代ランタイムを停止して第2世代ランタイムに移行させたいのではないかと思えてきます (実際すぐに第1世代ランタイムが止まることはないことは、「レガシーランタイムの長期サポート」に説明されています)。古くなったランタイムのメンテナンスが行われなくなるのは当然なのでずっと第1世代ランタイムがサポートされることはないのですが、第2世代ランタイムへの移行を促したいのは、ランタイムの独自拡張のメンテナンスも止めたいからではないでしょうか。

なかなか移行が進まない原因を探ったら第1世代ランタイムの拡張機能が原因だったから、第2世代でも利用できるように「レガシーバンドルサービス」という形で対処したものと思われます。


Search APIの代替が用意されない理由はちょっと想像できません。Googleにとっては自社製の検索エンジンの応用らしいので、技術的には用意できなくはないはずです。政治的な理由しか考えられません。Elasticsearchに誘導したい理由は何なのでしょう。

2022/05/17

vsReversiアップデート

GCPのデモとして用意していた vsReversiをアップデートしました。


アップデート内容は以下のとおりです。

  • ユーザのログインと新規登録を別に
  • SPA対応
  • デザイン変更

ユーザのログインと新規登録を別に

ユーザ認証にはGCPのIdentity Platform(Firebase Authentication)を使用していますが、これには出来合いのUIとして提供されるもの(Firebase UI)も、単体のAPIも、ログインと新規登録の区別がありません。それらが一緒になったサインインがあるのみです。

しかし日本では、ユーザ登録と登録済みユーザのログインが別になっているのをよく見るので、それらしく分けてみました。やってみて出来なくはないのですが、かえって実装は面倒になってしまった印象です。

またEメールで登録する場合は、OAuth(Google/Facebook/Twitter/etc.)に比べて、追加で実装しなければならないのものが多いので、余計面倒です。具体的には、

  • 入力されたEメールアドレスを確認する
  • 入力されたEメールアドレスが間違っていた場合にユーザ登録を解除
  • ユーザがパスワードを忘れた場合のパスワードリセット
  • ユーザがEメールアドレスの変更を希望した場合の対処 (vsReversiでは割愛)
  • さらにこれらのUIのカスタマイズ (vsReversiでは割愛)

Firebase UIではこれらの一部が対応済みなので、簡単に実装したいならそれを使うのが楽です。ただしUIのデザインは簡素なものなので、カスタマイズしたくなります。


SPA対応

Angularを使って一部のページをSingle Page Applicationに対応させました。ログイン/新規登録後のページがそれです。マテリアルデザインは採用していないので それっぽい見た目にはなっていないのですが、ページ遷移が速くなりました。

今までGAE/Java8メインで作っていたのですが、「GAE/Java8でSPA対応に苦戦」に記載したように、これは複数のURIに1つのページを割り当てることができないので、実装の変更が大掛かりになりました。具体的には...

  • HTMLなどスタティックコンテンツをFirebase Hostingに移行してURI割り当てを解決。
  • ajaxリクエストエンドポイントは過去のGAEの実装を流用。ただしCORS(Cross Origin Resource Sharing)に対応。
  • 管理者用のページはCloud Runで管理者の認証を実装。


GCPの初期からあるサービスだけに、GAEは単体でいろいろ必要なものが用意されていることを実感できました。今回は対応できないものが見つかったので、他にいろいろと物色することになりました。それぞれが別のサービスとして名前を持っているので、利用したサービスの数が増えています。


デザイン変更

SPA対応のついでですが、見た目だけの変更です。


CPUの思考ルーチンなどその他は以前のままで、変りありません。


2022/04/23

GAE/Java8でSPA対応に苦戦

GAEの第1世代VMであるJava8とGoで動かしているアプリを、Single Page Application(以下 略称のSPAを使用します)に一部作り直そうと検討しているのですが、意外と苦戦しています。


問題

SPAでは、複数のパスを実際には1つのHTML/JavaScriptで表示します。例として、以下のパスの3ページを用意したとします。

/         ←3つとも/index/htmlが対応

/aaa    ←〃

/bbb    ←

見た目には3ページでも、実際にはJavaScriptでリクエストされたパスを見て表示内容を切り替えているだけで、コンテンツとしてはこれら3ページを1つのHTML/JavaScriptが表示します。


で、これらのページをいつものようにGAEの静的ファイルとしてホストしようと思っていました。しかしGAEの静的ファイルは基本的に1パス=1ファイルに対応します。つまり"/"に対応するHTML/JavaScriptで3ページすべてを表示するつくりにすると、残り2ページをリクエストしてもGAEの静的ファイルサーバが「404 Not found」を返してしまい、HTML/JavaScriptが得られません。

Java8以外だとapp.yamlの記述(参照:ハンドラ要素)で、"/aaa", "/bbb"が指定された場合でも"/"と同じコンテンツを返すことが可能です。しかしJava8では、そもそもapp.yamlが使用できません。

サーバサイドのアプリはすでに作成済みのものを使いまわすつもりなので、今更ほかの言語で書き換える気などありません。このアプリではMemcacheやSearch APIなど第1世代VMでしか提供されない機能を利用していますので、使えるのは第1世代VMに縛られます。


解決案

いろいろ調査・検討してみました。ここから先はアイデアだけで試したわけではありません。


A案. Firebase Hostingへの一部移行

静的ファイルのホストをFirebase Hostingに変えます。複数パスが1つのHTML/JavaScriptに対応する問題は、firebase.jsonのリライト設定(参照:リライトを構成する)で解決できそうです。

Firebase Hostingは独自ドメインにも対応しています。GAEで実装したAPIはFirebase Hostingとは別ドメインになりますので、Cross Origin Resource Sharing(以下CORS)ポリシーに触れますが、どちらも自前で実装するので適切にレスポンスを返すだけです。


しかし実際のアプリには前記の3つのパス以外に、SPAに入らない全く独立したページも存在します。以下のような感じです。

/         ←3つとも/index/htmlが対応

/aaa    ←

/bbb    ←

/ccc/xxx    ←独立した動的ページ

この独立ページ"/ccc/xxx"は静的ファイルではありませんので、Firebase Hostingで対応できません。現状は静的ファイルと一緒にGAEで対応していますが、Firebase HostingとGAEでドメインが異なるのは問題です。アドレスはエンドユーザにも見えますので、同じアプリの一部であること、あやしいページに遷移したわけではないことがエンドユーザにもわかるように同じドメインへの配置が必要です。


B案. 動的ページは空コンテンツを用意

前記の独立した動的ページ"/ccc"にはhtml/body要素のみの空ページを用意しておき、"/xxx"の部分は前記のリライトで"/ccc"に置き換えます。中身はajaxで"/xxx"を判断して埋めます。

これならエンドユーザに見えるドメインはすべて同じにできます。ajaxのリスエストエンドポイントはGAEなので別ドメインですが、エンドユーザには見えないので問題にならないはずです。

ただしこのページは他のページとデザインなども異なるので、「空ページを埋める」でどこまで対応できるのか不安が残ります。


C案. API Gatewayで振り分け

一般提供になったばかり(だったと思う)API Gatewayで同じドメインの別パスとして、Firebase HostingとGAEの振り分けを追加する案です。B案の動的ページの不安も解決できる可能性があります。

しかしドキュメントを読んだだけでも以下の懸念が...

API GatewayがFirebase HostingやCloud Storageの静的コンテンツも対象に出来れば面白いと思うのですが。1つのWebアプリとして単一のドメインの中で静的コンテンツやajaxのAPIだけでなく、公開APIも対応できます。

API Gatewayのドキュメントには静的コンテンツに関する記述はなさそうです。名前のとおりWeb APIのみを対象としているのかもしれません。


(2022/4/25追記)

Firebase Hostingの動的コンテンツ配信の機能(参照:Firebase Hosting を使用した動的コンテンツの配信とマイクロサービスのホスティング)がGAEにも対応していれば問題ないのですが、この機能はCloud Functions for FirebaseとCloud Runにしか対応していません。

仮にアプリを書き直すにしてもCloud Functions for FirebaseはJavaScript/TypeScriptにしか対応していないようなのでパフォーマンスとランニングコストに疑問がありますし、Cloud Runでもランニングコストが課題になります。

なかなか、かゆいところに手が届いてくれないですね。

2021/12/08

GCPのデモアプリ : vsReversi


 GCPのデモとして、vsReversiを作ってみました。ネット対戦リバーシ(オセロ)です。

  • 対戦相手を選択できる。
  • 適当にマッチングして対戦もできる。
  • 相手がいないときは対コンピュータ戦もできる。

使用しているGCPのサービスは以下のとおりです。
  • App Engine : Webサーバ
  • Firestore : データベース
  • Identity Platform (Firebase Authentication) : ユーザ認証
  • Cloud Tasks : 対コンピュータ戦の思考ルーチン
対コンピュータ戦の思考ルーチンはJavaで書いていますが、実質数秒以下で1手を打ちます。そのためCloud Tasksを使用する意味はないのですが、GCPのデモとして無理矢理使っています。でないとただのGAEのデモなりそうなので。
デモにコストをかけられるほど裕福ではないので、GAEを中心に無料枠の範囲内で運用できるように、GAEの最大インスタンス数を制限しています。そのため利用が集中すると、応答が悪かったり、エラーになる可能性があります。最大インスタンス数の制限を外せば大勢が同時にアクセスしてもレスポンス低下の起きにくいGAE/Firestoreの特徴を活かせるのですが、残念なことになっています。GAE/Firestoreのいいところを全くアピールできていない、デモとしてはダメアプリです。

それでも、見つけた方は楽しんでいただければと思います。

2021/08/07

サブドメインでのGAEアプリ運用

 独自ドメインのサブドメインでGAEアプリを運用する場合について調査したときの、忘備録です。


Google提供のサービスのみでの実現

当初LotteryServは以下の様なY's Software Atelierのサブドメインでの運用を考えていました。


Y's Software Atelier

ドメイン : ys-software-atelier.biz
実体は下記とは違うGAEアプリ、この独自ドメインはGoogle Domainsで取得。

LotteryServ

サブドメイン : lotteryserv.ys-software-atelier.biz
実体はGAEアプリLotteryServ、上記ドメインのサブドメインにしたい。


いきなり結論ですが、このような構成はGoogleから提供されるサービスのみでは出来ないようです。理由はこちらのGoogle DNSのドキュメント「重複するゾーン」の以下の制限です。

重複する一般公開ゾーン同士は、同じ Cloud DNS ネームサーバー上に存在できません。

メインドメイン/サブドメイン共にGAEアプリであり、どちらもドメイン名をGoogle DNSで割り当てようとしているので、上記の制限に該当します。最初から無理でした。他にも複数のサブドメインで複数のGAEアプリを運用するケースも該当しますね。

解決策

この状況を解決しようと思えば、以下の方法があるようです。どちらも私が試したわけではなく、調査のみですが。

Firebase Hostingを併用する

Googleの提供するDNSサービスはGoogle DNSの他にFirebase Hostingもあります。片方をこれに替えれば解決できそうです。
ただしFirebase Hostingを利用するには、スタティックなwebコンテンツのみの実装にする必要があります。コンテンツに動的に動作/表示を変えたい部分があれば、そこはajaxとFirebase Realtime Datebase/Firestore/Cloud Functionsあたりを使用することになると思います。

Google以外のDNSサービスを併用する

Google以外のサービスも併用すればサブドメインでの運用も実現できるようです。これに挑戦した方の記事としては以下が見つかりました。
どちらも独自ドメインをお名前.comで取得しているので、サブドメインはお名前.comのDNSサービスで実現しています。
お名前.comでなくても、MyDNS.JPでも解決できると思われます。MyDNS.JPは無料で利用できるのが利点ですが、1週間ごとに利用の通知が必要です。これはCloud SchedulerとGAEで簡単に実現できますが。

2021/07/29

ログイン/再認証時のドメイン表示について

LotteryServに Eメールアドレス/パスワード以外の方法でログインまたは再認証するとき、以下の画面キャプチャの様にLotteryServではないドメインが表示されます。これは不具合ではなく、正常な動作ですのでご安心ください。



以下、このような表示になる原因について説明します。技術的な内容ですので、興味のある方だけ読んでいただければ十分です。


LotteryServは主にGCPの中の1サービスであるGAEを使って実装していますので、独自ドメインlotteryserv.bizはGAEアプリに接続しています。

その一方でユーザ認証は同じくGCPの中の別のサービスであるIdentity Platformを使用しています。Identity Platformでは「認証ハンドラのカスタマイズ」からリンクされた「カスタムドメインを接続する」に上記ユーザ認証画面で独自ドメインを表示する方法が説明されています。ここを読めばわかるように、上記のユーザ認証画面で独自ドメインを表示するには、独自ドメインをFirebase Hostingに接続する必要があります。

つまり独自ドメインをGAEとFirebase Hostingで奪い合って競合する事態になります。LotteryServでは独自ドメインはGAEに接続し、ユーザ認証画面での独自ドメイン表示は諦めています。

FirebaseはもともとGoogleとは独立した会社であったFirebase社が2011年に開始したサービスだったのですが、2014年にGoogleに買収されてGCPの一員となった経緯があります。そのためFirebase社の名残がいろいろなところにあったり、今回のユーザ認証画面の表示の様に まだGCPに統合しきれていない部分が残っていたりします。

FirebaseがGCPに完全に統合されるまで、この対応は簡単ではないと思われます。


2021/09/27追記

FirebaseAuth.generateEmailVerificationLink()メソッドなどが生成するEメールアドレス確認のリンクも、同様にドメインがFirebaseですね。

2021/07/26

LotteryServの開発

 LotteryServを開発してみて、苦労した点です。


1. 決済

最も苦労したのは決済です。
クレジットカード決済の実装が初めてで知識も経験もなかったというのもありますが、売掛で1度は実装したためにそれをクレジットカード決済対応に修正する必要が生じてしまったことも、作業量を増やす原因になってしまいました。
まず決済代行サービスの調査・選択から始まりました。決済代行サービスは20社以上出てきたのですが、ほとんどはリアル店舗での小売業を対象としたもので、ネットでAPIを使えるようにしているサービスは限られています。さらに各社から資料を取り寄せてAPIを精査してみると、1回ごとにクレジットカードの情報入力が必要なものと、予めクレジットカード情報を登録しておいてそれを使いまわせるものに分かれています。LotteryServでは応募数によって提供するサービスの途中で決済価格が変わるので、予めクレジットカード情報を登録できる決済代行サービスでないと、主催者に不便を強いることになります。そのため予めクレジットカード情報を登録できる決済代行サービスであることを確認する必要がありました。
またAPIの仕様が古くて、ちょっと使いたくないなと思わされるようなものもありました。10年以上前に作って、新しい技術が使われるようになっても仕様を更新することなくそのままになっている感じです。アクセス元を確認するために固定IPが必要など、GAEでは対応できない仕様を要求するものももあります。GCEなら対応できますが、決済のためだけにGCEを立ち上げたのではランニングコストが増えますし、何よりレスポンスが悪くてUIの構築が面倒になります。このような古い仕様のものは出来れば避けたいものです。
実装を始めてからも、最初は公開されているリクエストエンドポイントを直接たたいていたのですが、POSTのみリクエストを受け付けてもらえないという現象に悩まされました。GETは1発でクリアしたんですけどね。リクエストを受けた際のログなどが見れないので、どこに原因があるかわからず、解析が行き詰ってしまうことになりました。結局これは提供されているライブラリを利用することで回避しました。

2. 抽選

2番目が抽選でした。
応募数が多くなると抽選に時間がかかるようになるのは必然なので、応募数にあわせて並列処理することにしました。最初はとりあえずシングルスレッドで抽選処理の原型を作成。次にGAEで1インスタンスあたりどのくらいのスレッド数まで性能低下が起きないかを調査しておきます。
そしてどう並列実行を実装するのがベストかの調査です。
お手軽なGAEでの実装では、自動スケール/基本スケール/手動スケールそれぞれでパラメータを変えて試してみましたが、思ったようにインスタンスが増えてくれません。増えてほしいのになかなか増えててくれないか、まだ増えなくてもいいのに増えて何もせずに落ちていくとか。やはりGAEで思ったとおりにインスタンスを増減するのは難しいようです。ある程度おおざっぱな制御になります。デフォルト設定でもオートスケールしてくれるのは便利なんですけどね。
結局GAEは諦めてGCEで実装しています。GCEでは自分でインスタンスとスレッドの起動をコントロールするので思いどおりです。GCEでの実装の欠点は、無限にインスタンスを増やすことができないことです。あらかじめ用意しておいたディスクの数だけしかインスタンスを起動できません。これは運用で解決することにしました。


その他は、紹介してもつまらないものなので、割愛します。LotteryServの特徴の1つであるアクセス集中への耐性ですが、これも対応方法さえ知っていればどういうほどのことはありません。小さな工夫はたくさんありますが、長くなるのでまたの機会に。

しかし運用するためにそろえなければならない機能が多く、サービス開始までには思ったより時間がかかりました。