Clund Runを試したので、次はGAE。
Cloud Runでの課題
試してみた結果、コールドスタートがよく出るっぽい。。
Cloud Schedulerを使って、定期的にリクエストを送って、
インスタンスが停止しないようにする or 停止してたら起こす、ようにするればよいが気になる。。
GAEのメリット
GAEはGoogle Cloud Platformが提供するPaaSサービスの一つ。
決まった言語や構成であれば、簡単にサーバをたてられる。
特に最小インスタンス数を設定でき、常に1つは稼働させられるので、
Cloud Runでの課題が解消できそう
GAEでデプロイする
Clund Runを試したで以下の2つを実施すればOK
1. GAEの設定ファイル(app.yaml)の用意
Nuxtの公式ドキュメントを参照しつつ対応。
・Google App Engine へデプロイするには? - NuxtJS
app.yamlの設定は以下の通り。細かい設定は、以下を参照。
・app.yaml 構成ファイル | Node.js 用 App Engine スタンダード環境に関するドキュメント
# Nodeのバージョン設定 runtime: nodejs12 # インスタンス: F1は無料なので、F1を設定 instance_class: F1 service: default # ハンドラー。URLごとの挙動を設定 handlers: # Nuxt v2のクライアントモジュールは .nuxt/dist/client に生成される。静的化しておく。 - url: /_nuxt static_dir: .nuxt/dist/client secure: always # static ディレクトリも同様に静的化しておく。 - url: /(.*\.(gif|png|jpg|ico|txt|json|svg))$ static_files: static/\1 upload: static/.*\.(gif|png|jpg|ico|txt|json|svg)$ secure: always # その他はそのまま - url: /.* script: auto secure: always # インスタンスのスケール設定 automatic_scaling: # インスタンスの最大数を指定。各バージョンで有効 max_instances: 1 # アイドル中のインスタンス最小数を指定。トラフィック割当が多いバージョンで有効 min_idle_instances: 1 # インスタンスが設定値。CPUやスループットの稼働率は最大に設定 target_cpu_utilization: 0.95 target_throughput_utilization: 0.95 max_concurrent_requests: 50 min_pending_latency: 3000ms max_pending_latency: automatic # 環境変数の設定 env_variables: HOST: "0.0.0.0" PORT: "8080" NODE_ENV: "production"
無料枠に収まるよう、F1で1インスタンスが稼働する状態になるよう、
インスタンスのスケールを設定している。(automatic_scaling周り)
また、nuxt.config.tsでソースディレクトリを変更している場合は、
staticディレクトリのパスも変わるので注意
handlers: # ...略 # static ディレクトリも同様に静的化しておく。 - url: /(.*\.(gif|png|jpg|ico|txt|json|svg))$ # ※app/staticに変更 static_files: app/static/\1 # ※app/staticに変更 upload: app/static/.*\.(gif|png|jpg|ico|txt|json|svg)$ secure: always # その他はそのまま
手動デプロイ
gcloudコマンドを利用した手動デプロイは以下。
$ gcloud app deploy --project=<PROJECT-ID>
コマンドの他の引数は、以下の公式ドキュメントを参照。
・gcloud app deploy | Cloud SDK Documentation | Google Cloud
2. Cloud Buildの設定ファイル(cloudbuild.yaml)に"gcp-build"を追加
Cloud Buildを使って、GitHubにプッシュしたらGAEにデプロイされるように設定する。
基本は、Clund Runを試したときと同じなので割愛。
cloudbuild.yamlはこんな感じ。
steps: - name: gcr.io/google.com/cloudsdktool/cloud-sdk id: Deploy entrypoint: gcloud args: - app - deploy - "--appyaml=${_APP_YAML}" - "--project=${PROJECT_ID}" - "--version=${_VERSION}" - "--quiet" options: substitutionOption: ALLOW_LOOSE substitutions: _APP_YAML: "app.yaml" _VERSION: "${COMMIT_SHA}" tags: - gcp-cloud-build-deploy-gae
Cloud Runのときと比べ、デプロイするだけのstepは1つのみ。
ただ、ビルドは必要なので、package.jsonにカスタムビルドの設定が必要。
Node.jsでのカスタムビルドは、scriptsにgcp-build
で指定できる。
・カスタム ビルドステップの実行 | Node.js 用 App Engine スタンダード環境に関するドキュメント
{ // ... "scripts": { // ... "gcp-build": "npm run build" }, // ... }
これで、デプロイ時にnpm run gcp-build
を実行してくれるようになる感じ。
ハマったポイント
メリットはあるけど、かなりハマった。。注意点たくさん。。
GAE関連
リージョンは一度設定すると変更できない
そのため、Firebaseで作成したプロジェクトだと、Firebaseで設定したものが反映される。 変更はできないので、必要があれば、別プロジェクトを作成するしかない。
Firebaseで利用しているプロジェクトだとGAEを停止できない
FirebaseでGAEを利用しているので、GAE停止したいなと思っても難しい。
GAEは最低1つはインスタンスを稼働していなければならず、
GAEの無効化をするとFirestoreにアクセスできなくなる。。
なので、一度上げてしまった場合、削除ができなくなるので、
アクセスがないとインスタンスが0になる設定で空のアプリをデプロイした対応した。
また、無効化してFirestoreが利用できなくなった場合、
有効化すれば、5分後くらいにFirestoreが使えるようになる。
GAEはdefaultサービスがないといけない
app.yamlでサービス名(service
)を指定すると、複数のサービスをデプロイできるらしい。
ただ、defaultない状態で、deployすると以下のエラーが出る。
The first service (module) you upload to a new application must be the 'default' service (module). Please upload a version of the 'default' service (module) before uploading a version for the 'release' service (module). See the documentation for more information. Python: (https://developers.google.com/appengine/docs/python/modules/#Python_Uploading%%20modules) Java: (https://developers.google.com/appengine/docs/java/modules/#Java_Uploading%%20modules)
F1は自動スケーリングのインスタンス
GAEのインスタンスタイプには、2種類ある。
- F系(F1,F2...。自動スケーリング)と
- B系(B1, B2...。基本スケーリング/手動スケーリング)
無料枠のF1は自動スケーリングでは、自動でシャットダウンされる。 - インスタンスの管理方法 | Node.js 用 App Engine スタンダード環境に関するドキュメント | Google Cloud
デフォルトのautomatic_scaling設定だと、期待してるインスタンスの常駐がされない。
そのため、min_instancesやmin_idle_instancesなどを設定が必要だが、
それぞれ特徴がある。
- min_instances/max_instances
- バージョンごとに有効。なのでトラフィック割当のないバージョンでも残ってしまう...
- min_idle_instances/min_idle_instances
- トラフィックの大半を受信するバージョンにのみ適用。
なので、上記のサンプルでは、以下のように設定して、
複数のインスタンスが立たないようにし、トラフィック割当が少ないものはインスタンスが0になるよう設定。
automatic_scaling: # インスタンスの最大数を指定。各バージョンで有効 max_instances: 1 # アイドル中のインスタンス最小数を指定。トラフィック割当が多いバージョンで有効 min_idle_instances: 1
min_instancesを1にすると古いバージョンでも残り続け、
max_idle_instancesを1にするとアイドル1、稼働中1と2つのインスタンスが立ち上がってしまう。
ウォームアップ リクエストを使っても起動が遅い?
GAEでも、「別のバージョンのアプリを再デプロイするとき」や「リクエスト数が容量を超え、新しいインスタンスが作成されるとき」に読み込みリクエストが発生し遅くなってしまう。
この初期の立ち上げのレイテンシを回避するため、ウォームアップ リクエストの機能がある。
・ウォームアップ リクエストを構成してパフォーマンスを改善する
ウォームアップ リクエストを有効にすると、デプロイ後に/_ah/warmup
に対してGET
リクエストを発行する。
そのため、NuxtのserverMiddlewareを追加して、リクエストを処理する関数を追加する。
app.yamlに、以下を追加して、有効化し、
inbound_services: - warmup
Nuxt側でこんな関数を用意して、
// serverMiddleware/warmup.ts import { IncomingMessage, ServerResponse } from "http"; export default async function(req: IncomingMessage, res: ServerResponse) { res.end(); }
nuxt.config.tsで設定を追加した。
import { Configuration } from "@nuxt/types"; const config: Configuration = { // ... serverMiddleware: [ { path: "/_ah/warmup", handler: "~/serverMiddleware/warmup.ts" }, ], }; export default config;
実際に試してみたところ、
また、warmup.ts
で/
に対しリクエストする処理を追加してみたところ、
タイミングによっては、トラフィック割当が0のため、読み込みリクエストを発生させられないっぽい。。
ウォームアップ リクエストが 特別な読み込みリクエスト であるからかもしれない。
さらに、ウォームアップ リクエスト自体が必ず呼び出されるものではないらしい。。
ウォームアップ リクエストは必ず呼び出されるとは限りません。代わりに読み込みリクエストが送信されることがあります。 ... すでにウォームアップされているインスタンスにリクエストを送信する「ベスト エフォート方式」が試行されます。
・ウォームアップ リクエストを有効にする|ウォームアップ リクエストを構成してパフォーマンスを改善する
そのため、期待した動作をしない可能性が高い感じだっった。。
Cloud Build関連
buildにname: 'gcr.io/cloud-builders/gcloud'
を使うとエラー
以下のドキュメントに従って設定したが、エラーに。。 ・App Engine へのデプロイ | Cloud Build のドキュメント | Google Cloud
ドキュメントが古いかも?
name: gcr.io/google.com/cloudsdktool/cloud-sdk
を使って解決
versionの制限
GAEのversionに使える文字には制限があり、エラーになった。
versionは、小文字の英字と数字とハイフン。小文字の英字か数字。63文字までらしい。
versionにv2.0.2-6053688856cc6b05a3af8e65c73a3507f357483f
を指定したら以下のメッセージが出た。。
ERROR: (gcloud.app.deploy) argument --version/-v: Bad value [v2.0.2-6053688856cc6b05a3af8e65c73a3507f357483f]: May only contain lowercase letters, digits, and hyphens. Must begin and end with a letter or digit. Must not exceed 63 characters.
この場合、bash形式の文字列操作を利用して、.
を-
に置き換えるなどすれば、対応できたりする。
App Engine Admin APIを有効にしないとデプロイできない
権限や有効化していないと、以下のエラーが出る
ERROR: (gcloud.app.deploy) User [581217444506@cloudbuild.gserviceaccount.com] does not have permission to access app [tsundoku-web] (or it may not exist): App Engine Admin API has not been used in project 581217444506 before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/appengine.googleapis.com/overview?project=581217444506 then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.
また、Cloud Buildの設定で、App Engine管理者を有効化していないと、
Cloud Buildでパーミッションエラーになる
・App Engine へのデプロイ | Cloud Build のドキュメント | Google Cloud
Cloud RunとGAEを試してみた結果
Cloud Runを利用することにしようと思ってる。
ポイントは、以下の4点
- FirebaseとリンクしているとGAEが停止できない
- リージョンを変更できない
- 複数のサービスを建てるときがめんどくさい(stagingとか作りたいため)
- カスタムドメインを設定がやや複雑(stagingとか作りたいときにちょっと手間)
上記2つは別プロジェクトにすれば解決するけど、プロジェクトを増やすと管理が大変なので、
個人的には統一できて設定も楽なCloud Runが良さそうな感じ。
ただ、Cloud Schedulerで10分ごとにインスタンスを立たせる必要があるため、
実際やってみて費用面でどうかを確認していきたい感じ。
以上!!
参考サイト
- Google App Engine へデプロイするには? - NuxtJS
- App Engine での Node.js アプリのビルド | Node.js 用 App Engine スタンダード環境に関するドキュメント | Google Cloud
- ウェブサービスのデプロイ | Node.js 用 App Engine スタンダード環境に関するドキュメント | Google Cloud
- 簡単!GAE/SE Node.jsでNuxt.jsをUniversalモードで動かしてみた | apps-gcp.com
- App Engineの標準環境でNuxtを使って無料SSR - Crieit
- Google App Engineを無料で運用する方法(2018年版) - koni blog
- Vue.jsで作成したSPAなアプリをGoogle App Engineへデプロイする | cloudpack.media
- Google App Engineのログを確認する方法 - Qiita
- cron.yaml を使用したジョブのスケジューリング | Node.js ドキュメントに対応した App Engine フレキシブル環境 | Google Cloud