TechVue.jsApp EngineJapanese

[ Vue 3 ] Vue CLI を GCP App Engine に デプロイ する手順

Tech

Vue CLI (Vue3) で作成した サンプルアプリケーションを GCP App Engine に デプロイ する手順をまとめてみました。なお、こちらの記事 でまとめている通り、ライブラリの追加など、細かなカスタマイズが必要な場合は 同じ GCP であれば Cloud Run を用いる必要があります。

環境
npm: 7.23.0
node: v14.17.0
vue: @vue/cli 4.5.12

前提
GCP プロジェクト作成済み
GCP プロジェクトの課金設定済み
Google Cloud SDK (gcloud コマンド ) 導入済み
[ads]

ローカル 側準備

Vue CLI プロジェクト の準備

まずは、任意のディレクトリに Vue CLI プロジェクト を作成していきます。 今回は vue3-cli という名称で プロジェクト を作成して進めていきます。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
% vue create vue3-cli
Vue CLI v4.5.12
┌───────────────────────────────────────────┐
│ │
│ New version available 4.5.12 → 4.5.13 │
│ │
└───────────────────────────────────────────┘
? Please pick a preset:
<span class="hljs-built_in"> Default </span>([Vue 2] babel, eslint)
❯<span class="hljs-built_in"> Default </span>(Vue 3 Preview) ([Vue 3] babel, eslint)
Manually select features
% vue create vue3-cli Vue CLI v4.5.12 ┌───────────────────────────────────────────┐ │ │ │ New version available 4.5.12 → 4.5.13 │ │ │ └───────────────────────────────────────────┘ ? Please pick a preset: <span class="hljs-built_in"> Default </span>([Vue 2] babel, eslint) ❯<span class="hljs-built_in"> Default </span>(Vue 3 Preview) ([Vue 3] babel, eslint) Manually select features
% vue create vue3-cli
Vue CLI v4.5.12
┌───────────────────────────────────────────┐
│                                           │
│   New version available 4.5.12 → 4.5.13   │
│                                           │
└───────────────────────────────────────────┘

? Please pick a preset:
  Default ([Vue 2] babel, eslint)
❯ Default (Vue 3 Preview) ([Vue 3] babel, eslint)
  Manually select features

起動確認していきます。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
% <span class="hljs-keyword">cd</span> vue3-<span class="hljs-keyword">cli</span>
% sudo npm <span class="hljs-keyword">run</span> serve
% <span class="hljs-keyword">cd</span> vue3-<span class="hljs-keyword">cli</span> % sudo npm <span class="hljs-keyword">run</span> serve
% cd vue3-cli
% sudo npm run serve

おなじみのトップ画面が表示され、Vue CLI プロジェクトが作成されたことが確認できます。

app.yaml の準備

App Engine のデプロイには Vue CLI プロジェクト の トップディレクトリ に app.yaml ファイルを配置する必要があります。 app.yaml ファイルがない場合、 デプロイ 時に以下のような エラー が出力されてしまいます。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<span class="hljs-keyword">ERROR: </span>An app.yaml (or appengine-web.xml) file is required to deploy this directory as an App Engine application.
<span class="hljs-keyword">ERROR: </span>An app.yaml (or appengine-web.xml) file is required to deploy this directory as an App Engine application.
ERROR: An app.yaml (or appengine-web.xml) file is required to deploy this directory as an App Engine application.

Vue CLI 用 app.yaml

以下の内容を app.yaml として、Vue CLI のトップディレクトリに配置してください。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<span class="hljs-attribute">runtime</span>: nodejs14
<span class="hljs-attribute">env</span>: standard
<span class="less"><span class="hljs-attribute">handlers</span>:
- <span class="hljs-attribute">url</span>: /
<span class="hljs-attribute">static_files</span>: dist/index.html
<span class="hljs-attribute">upload</span>: dist/index.html
- <span class="hljs-attribute">url</span>: /(.*)
<span class="hljs-attribute">static_files</span>: dist/\<span class="hljs-number">1</span>
<span class="hljs-attribute">upload</span>: dist/(.*)</span>
<span class="hljs-attribute">runtime</span>: nodejs14 <span class="hljs-attribute">env</span>: standard <span class="less"><span class="hljs-attribute">handlers</span>: - <span class="hljs-attribute">url</span>: / <span class="hljs-attribute">static_files</span>: dist/index.html <span class="hljs-attribute">upload</span>: dist/index.html - <span class="hljs-attribute">url</span>: /(.*) <span class="hljs-attribute">static_files</span>: dist/\<span class="hljs-number">1</span> <span class="hljs-attribute">upload</span>: dist/(.*)</span>
runtime: nodejs14
env: standard

handlers:
- url: /
  static_files: dist/index.html
  upload: dist/index.html

- url: /(.*)
  static_files: dist/\1
  upload: dist/(.*)

この app.yaml ですが、 今回は ローカルの環境にあわせ nodejs14 としていますが、そのまま 推奨されている nodejs16 としても サンプルアプリに関しては動作するようでした。


Google App Engine 側準備

GCP への ログイン

最初に 対象となる GCP プロジェクトを選択していきます。 gcloud auth login を実行すると、ブラウザが起動します。既に ログイン済みの場合は 次の手順に進みます。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
% gcloud auth login
Your browser <span class="hljs-built_in">has</span> been opened <span class="hljs-keyword">to</span> visi<span class="hljs-variable">t:</span>
http<span class="hljs-variable">s:</span>//accounts.google.<span class="hljs-keyword">com</span>/<span class="hljs-keyword">o</span>/oauth2/auth?XXXXX
% gcloud auth login Your browser <span class="hljs-built_in">has</span> been opened <span class="hljs-keyword">to</span> visi<span class="hljs-variable">t:</span> http<span class="hljs-variable">s:</span>//accounts.google.<span class="hljs-keyword">com</span>/<span class="hljs-keyword">o</span>/oauth2/auth?XXXXX
% gcloud auth login
Your browser has been opened to visit:

    https://accounts.google.com/o/oauth2/auth?XXXXX

対象の Google アカウント を選択してすすめていきます。

アクセス権限確認が表示されますので、問題なければ Allow をクリックします。

対象となる GCP プロジェクト の選択

まず、 GCP Console のプロジェクトダッシュボードにいき、 プロジェクトID を確認します。

次に、gcloud config set project コマンドを利用して、対象となる プロジェクト を選択していきます。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
% gcloud<span class="hljs-built_in"> config </span><span class="hljs-builtin-name">set</span> project sample-for-blog
Updated property [core/project].
% gcloud<span class="hljs-built_in"> config </span><span class="hljs-builtin-name">set</span> project sample-for-blog Updated property [core/project].
% gcloud config set project sample-for-blog
Updated property [core/project].

以下のコマンドで、対象となるプロジェクトが確認できます。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
% gcloud<span class="hljs-built_in"> config </span>list core/project
[core]
project = sample-for-blog
% gcloud<span class="hljs-built_in"> config </span>list core/project [core] project = sample-for-blog
% gcloud config list core/project
[core]
project = sample-for-blog

Vue CLI プロジェクト の ビルド

npm run build コマンドを実行して、デプロイ用のビルドイメージを作成します。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
% npm run build
> vue3-<span class="hljs-symbol">cli@</span><span class="hljs-number">0.1</span><span class="hljs-number">.0</span> build
> vue-cli-service build
⠋ Building <span class="hljs-keyword">for</span> production...
DONE Compiled successfully <span class="hljs-keyword">in</span> <span class="hljs-number">7151</span>ms <span class="hljs-number">20</span>:<span class="hljs-number">06</span>:<span class="hljs-number">59</span>
File Size Gzipped
dist/js/chunk-vendors.e1ffbd46.js <span class="hljs-number">84.96</span> KiB <span class="hljs-number">31.72</span> KiB
dist/js/app<span class="hljs-number">.9</span>de03c6e.js <span class="hljs-number">4.47</span> KiB <span class="hljs-number">1.60</span> KiB
dist/css/app.fb0c6e1c.css <span class="hljs-number">0.33</span> KiB <span class="hljs-number">0.23</span> KiB
Images <span class="hljs-keyword">and</span> other types of assets omitted.
DONE Build complete. The dist directory <span class="hljs-keyword">is</span> ready to be deployed.
INFO Check <span class="hljs-keyword">out</span> deployment instructions at https:<span class="hljs-comment">//cli.vuejs.org/guide/deployment.html</span>
% npm run build > vue3-<span class="hljs-symbol">cli@</span><span class="hljs-number">0.1</span><span class="hljs-number">.0</span> build > vue-cli-service build ⠋ Building <span class="hljs-keyword">for</span> production... DONE Compiled successfully <span class="hljs-keyword">in</span> <span class="hljs-number">7151</span>ms <span class="hljs-number">20</span>:<span class="hljs-number">06</span>:<span class="hljs-number">59</span> File Size Gzipped dist/js/chunk-vendors.e1ffbd46.js <span class="hljs-number">84.96</span> KiB <span class="hljs-number">31.72</span> KiB dist/js/app<span class="hljs-number">.9</span>de03c6e.js <span class="hljs-number">4.47</span> KiB <span class="hljs-number">1.60</span> KiB dist/css/app.fb0c6e1c.css <span class="hljs-number">0.33</span> KiB <span class="hljs-number">0.23</span> KiB Images <span class="hljs-keyword">and</span> other types of assets omitted. DONE Build complete. The dist directory <span class="hljs-keyword">is</span> ready to be deployed. INFO Check <span class="hljs-keyword">out</span> deployment instructions at https:<span class="hljs-comment">//cli.vuejs.org/guide/deployment.html</span>
% npm run build

> vue3-cli@0.1.0 build
> vue-cli-service build


⠋  Building for production...

 DONE  Compiled successfully in 7151ms                                                                                                                                                 20:06:59

  File                                 Size                                                                       Gzipped

  dist/js/chunk-vendors.e1ffbd46.js    84.96 KiB                                                                  31.72 KiB
  dist/js/app.9de03c6e.js              4.47 KiB                                                                   1.60 KiB
  dist/css/app.fb0c6e1c.css            0.33 KiB                                                                   0.23 KiB

  Images and other types of assets omitted.

 DONE  Build complete. The dist directory is ready to be deployed.
 INFO  Check out deployment instructions at https://cli.vuejs.org/guide/deployment.html

App Engine へ デプロイ

Vue CLI のトップディレクトリ (ここでは vue3-cli ディレクトリ) で、 gcloud app deploy コマンドを実行します。なお、後ほど説明しますが、 App Engine では サービス という単位で デプロイ することになります。先程の app.yaml では サービス を指定していないため、 default のサービスとして デプロイ することになります。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
% gcloud app deploy
Services <span class="hljs-keyword">to</span> deploy:
descriptor: [DIRPATH/vue3-cli/app.yaml]
source: [DIRPATH/vue3-cli]
target project: [sample-for-blog]
target service: [default]
target version: [20210911t200825]
target url:
<a rel="noopener" href="https://sample-for-blog.an.r.appspot.com" title="403 Forbidden" class="blogcard-wrap external-blogcard-wrap a-wrap cf" target="_blank"><div class="blogcard external-blogcard eb-right cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img data-src="https://s.wordpress.com/mshots/v1/https%3A%2F%2Fsample-for-blog.an.r.appspot.com?w=320&h=180" alt="" class="blogcard-thumb-image external-blogcard-thumb-image lozad lozad-img" loading="lazy" width="320" height="180"><noscript><img <span class="hljs-attribute">loading</span>=<span class="hljs-string">"lazy"</span> <span class="hljs-attribute">decoding</span>=<span class="hljs-string">"async"</span> <span class="hljs-attribute">src</span>=<span class="hljs-string">"https://s.wordpress.com/mshots/v1/https%3A%2F%2Fsample-for-blog.an.r.appspot.com?w=320&#038;h=180"</span> <span class="hljs-attribute">alt</span>=<span class="hljs-string">""</span> <span class="hljs-attribute">class</span>=<span class="hljs-string">"blogcard-thumb-image external-blogcard-thumb-image"</span> <span class="hljs-attribute">width</span>=<span class="hljs-string">"320"</span> <span class="hljs-attribute">height</span>=<span class="hljs-string">"180"</span> /></noscript></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">403 Forbidden</div><div class="blogcard-snippet external-blogcard-snippet"></div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img data-src="https://www.google.com/s2/favicons?domain=https://sample-for-blog.an.r.appspot.com" alt="" class="blogcard-favicon-image external-blogcard-favicon-image lozad lozad-img" loading="lazy" width="16" height="16"><noscript><img <span class="hljs-attribute">loading</span>=<span class="hljs-string">"lazy"</span> <span class="hljs-attribute">decoding</span>=<span class="hljs-string">"async"</span> <span class="hljs-attribute">src</span>=<span class="hljs-string">"https://www.google.com/s2/favicons?domain=https://sample-for-blog.an.r.appspot.com"</span> <span class="hljs-attribute">alt</span>=<span class="hljs-string">""</span> <span class="hljs-attribute">class</span>=<span class="hljs-string">"blogcard-favicon-image external-blogcard-favicon-image"</span> <span class="hljs-attribute">width</span>=<span class="hljs-string">"16"</span> <span class="hljs-attribute">height</span>=<span class="hljs-string">"16"</span> /></noscript></div><div class="blogcard-domain external-blogcard-domain">sample-for-blog.an.r.appspot.com</div></div></div></div></a>
target<span class="hljs-built_in"> service </span>account: [App Engine<span class="hljs-built_in"> default service </span>account]
<span class="hljs-keyword">Do</span> you want <span class="hljs-keyword">to</span> continue (Y/n)?
Beginning deployment of<span class="hljs-built_in"> service </span>[default]<span class="hljs-built_in">..</span>.
Uploading 7 files <span class="hljs-keyword">to</span> Google Cloud Storage
14%
29%
43%
57%
71%
86%
100%
100%
File upload done.
Updating<span class="hljs-built_in"> service </span>[default]<span class="hljs-built_in">..</span>.done.
Setting traffic split <span class="hljs-keyword">for</span><span class="hljs-built_in"> service </span>[default]<span class="hljs-built_in">..</span>.done.
Deployed<span class="hljs-built_in"> service </span>[default] <span class="hljs-keyword">to</span>
<a rel="noopener" href="https://sample-for-blog.an.r.appspot.com" title="403 Forbidden" class="blogcard-wrap external-blogcard-wrap a-wrap cf" target="_blank"><div class="blogcard external-blogcard eb-right cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img data-src="https://s.wordpress.com/mshots/v1/https%3A%2F%2Fsample-for-blog.an.r.appspot.com?w=320&h=180" alt="" class="blogcard-thumb-image external-blogcard-thumb-image lozad lozad-img" loading="lazy" width="320" height="180"><noscript><img <span class="hljs-attribute">loading</span>=<span class="hljs-string">"lazy"</span> <span class="hljs-attribute">decoding</span>=<span class="hljs-string">"async"</span> <span class="hljs-attribute">src</span>=<span class="hljs-string">"https://s.wordpress.com/mshots/v1/https%3A%2F%2Fsample-for-blog.an.r.appspot.com?w=320&#038;h=180"</span> <span class="hljs-attribute">alt</span>=<span class="hljs-string">""</span> <span class="hljs-attribute">class</span>=<span class="hljs-string">"blogcard-thumb-image external-blogcard-thumb-image"</span> <span class="hljs-attribute">width</span>=<span class="hljs-string">"320"</span> <span class="hljs-attribute">height</span>=<span class="hljs-string">"180"</span> /></noscript></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">403 Forbidden</div><div class="blogcard-snippet external-blogcard-snippet"></div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img data-src="https://www.google.com/s2/favicons?domain=https://sample-for-blog.an.r.appspot.com" alt="" class="blogcard-favicon-image external-blogcard-favicon-image lozad lozad-img" loading="lazy" width="16" height="16"><noscript><img <span class="hljs-attribute">loading</span>=<span class="hljs-string">"lazy"</span> <span class="hljs-attribute">decoding</span>=<span class="hljs-string">"async"</span> <span class="hljs-attribute">src</span>=<span class="hljs-string">"https://www.google.com/s2/favicons?domain=https://sample-for-blog.an.r.appspot.com"</span> <span class="hljs-attribute">alt</span>=<span class="hljs-string">""</span> <span class="hljs-attribute">class</span>=<span class="hljs-string">"blogcard-favicon-image external-blogcard-favicon-image"</span> <span class="hljs-attribute">width</span>=<span class="hljs-string">"16"</span> <span class="hljs-attribute">height</span>=<span class="hljs-string">"16"</span> /></noscript></div><div class="blogcard-domain external-blogcard-domain">sample-for-blog.an.r.appspot.com</div></div></div></div></a>
You can stream logs <span class="hljs-keyword">from</span> the command line by running:
$ gcloud app logs tail -s<span class="hljs-built_in"> default
</span>
<span class="hljs-keyword">To</span> view your application <span class="hljs-keyword">in</span> the web browser run:
$ gcloud app browse
% gcloud app deploy Services <span class="hljs-keyword">to</span> deploy: descriptor: [DIRPATH/vue3-cli/app.yaml] source: [DIRPATH/vue3-cli] target project: [sample-for-blog] target service: [default] target version: [20210911t200825] target url: <a rel="noopener" href="https://sample-for-blog.an.r.appspot.com" title="403 Forbidden" class="blogcard-wrap external-blogcard-wrap a-wrap cf" target="_blank"><div class="blogcard external-blogcard eb-right cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img data-src="https://s.wordpress.com/mshots/v1/https%3A%2F%2Fsample-for-blog.an.r.appspot.com?w=320&h=180" alt="" class="blogcard-thumb-image external-blogcard-thumb-image lozad lozad-img" loading="lazy" width="320" height="180"><noscript><img <span class="hljs-attribute">loading</span>=<span class="hljs-string">"lazy"</span> <span class="hljs-attribute">decoding</span>=<span class="hljs-string">"async"</span> <span class="hljs-attribute">src</span>=<span class="hljs-string">"https://s.wordpress.com/mshots/v1/https%3A%2F%2Fsample-for-blog.an.r.appspot.com?w=320&#038;h=180"</span> <span class="hljs-attribute">alt</span>=<span class="hljs-string">""</span> <span class="hljs-attribute">class</span>=<span class="hljs-string">"blogcard-thumb-image external-blogcard-thumb-image"</span> <span class="hljs-attribute">width</span>=<span class="hljs-string">"320"</span> <span class="hljs-attribute">height</span>=<span class="hljs-string">"180"</span> /></noscript></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">403 Forbidden</div><div class="blogcard-snippet external-blogcard-snippet"></div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img data-src="https://www.google.com/s2/favicons?domain=https://sample-for-blog.an.r.appspot.com" alt="" class="blogcard-favicon-image external-blogcard-favicon-image lozad lozad-img" loading="lazy" width="16" height="16"><noscript><img <span class="hljs-attribute">loading</span>=<span class="hljs-string">"lazy"</span> <span class="hljs-attribute">decoding</span>=<span class="hljs-string">"async"</span> <span class="hljs-attribute">src</span>=<span class="hljs-string">"https://www.google.com/s2/favicons?domain=https://sample-for-blog.an.r.appspot.com"</span> <span class="hljs-attribute">alt</span>=<span class="hljs-string">""</span> <span class="hljs-attribute">class</span>=<span class="hljs-string">"blogcard-favicon-image external-blogcard-favicon-image"</span> <span class="hljs-attribute">width</span>=<span class="hljs-string">"16"</span> <span class="hljs-attribute">height</span>=<span class="hljs-string">"16"</span> /></noscript></div><div class="blogcard-domain external-blogcard-domain">sample-for-blog.an.r.appspot.com</div></div></div></div></a> target<span class="hljs-built_in"> service </span>account: [App Engine<span class="hljs-built_in"> default service </span>account] <span class="hljs-keyword">Do</span> you want <span class="hljs-keyword">to</span> continue (Y/n)? Beginning deployment of<span class="hljs-built_in"> service </span>[default]<span class="hljs-built_in">..</span>. Uploading 7 files <span class="hljs-keyword">to</span> Google Cloud Storage 14% 29% 43% 57% 71% 86% 100% 100% File upload done. Updating<span class="hljs-built_in"> service </span>[default]<span class="hljs-built_in">..</span>.done. Setting traffic split <span class="hljs-keyword">for</span><span class="hljs-built_in"> service </span>[default]<span class="hljs-built_in">..</span>.done. Deployed<span class="hljs-built_in"> service </span>[default] <span class="hljs-keyword">to</span> <a rel="noopener" href="https://sample-for-blog.an.r.appspot.com" title="403 Forbidden" class="blogcard-wrap external-blogcard-wrap a-wrap cf" target="_blank"><div class="blogcard external-blogcard eb-right cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img data-src="https://s.wordpress.com/mshots/v1/https%3A%2F%2Fsample-for-blog.an.r.appspot.com?w=320&h=180" alt="" class="blogcard-thumb-image external-blogcard-thumb-image lozad lozad-img" loading="lazy" width="320" height="180"><noscript><img <span class="hljs-attribute">loading</span>=<span class="hljs-string">"lazy"</span> <span class="hljs-attribute">decoding</span>=<span class="hljs-string">"async"</span> <span class="hljs-attribute">src</span>=<span class="hljs-string">"https://s.wordpress.com/mshots/v1/https%3A%2F%2Fsample-for-blog.an.r.appspot.com?w=320&#038;h=180"</span> <span class="hljs-attribute">alt</span>=<span class="hljs-string">""</span> <span class="hljs-attribute">class</span>=<span class="hljs-string">"blogcard-thumb-image external-blogcard-thumb-image"</span> <span class="hljs-attribute">width</span>=<span class="hljs-string">"320"</span> <span class="hljs-attribute">height</span>=<span class="hljs-string">"180"</span> /></noscript></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">403 Forbidden</div><div class="blogcard-snippet external-blogcard-snippet"></div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img data-src="https://www.google.com/s2/favicons?domain=https://sample-for-blog.an.r.appspot.com" alt="" class="blogcard-favicon-image external-blogcard-favicon-image lozad lozad-img" loading="lazy" width="16" height="16"><noscript><img <span class="hljs-attribute">loading</span>=<span class="hljs-string">"lazy"</span> <span class="hljs-attribute">decoding</span>=<span class="hljs-string">"async"</span> <span class="hljs-attribute">src</span>=<span class="hljs-string">"https://www.google.com/s2/favicons?domain=https://sample-for-blog.an.r.appspot.com"</span> <span class="hljs-attribute">alt</span>=<span class="hljs-string">""</span> <span class="hljs-attribute">class</span>=<span class="hljs-string">"blogcard-favicon-image external-blogcard-favicon-image"</span> <span class="hljs-attribute">width</span>=<span class="hljs-string">"16"</span> <span class="hljs-attribute">height</span>=<span class="hljs-string">"16"</span> /></noscript></div><div class="blogcard-domain external-blogcard-domain">sample-for-blog.an.r.appspot.com</div></div></div></div></a> You can stream logs <span class="hljs-keyword">from</span> the command line by running: $ gcloud app logs tail -s<span class="hljs-built_in"> default </span> <span class="hljs-keyword">To</span> view your application <span class="hljs-keyword">in</span> the web browser run: $ gcloud app browse
% gcloud app deploy
Services to deploy:

descriptor:                  [DIRPATH/vue3-cli/app.yaml]
source:                      [DIRPATH/vue3-cli]
target project:              [sample-for-blog]
target service:              [default]
target version:              [20210911t200825]
target url:                  
403 Forbidden
target service account: [App Engine default service account] Do you want to continue (Y/n)? Beginning deployment of service [default]... Uploading 7 files to Google Cloud Storage 14% 29% 43% 57% 71% 86% 100% 100% File upload done. Updating service [default]...done. Setting traffic split for service [default]...done. Deployed service [default] to
403 Forbidden
You can stream logs from the command line by running: $ gcloud app logs tail -s default To view your application in the web browser run: $ gcloud app browse

Unknown url handler type. が出力される場合

gcloud app deploy コマンド 実行時に、 以下のような エラー が出力されることがあります。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<span class="hljs-type">Unknown</span> url <span class="hljs-keyword">handler</span> <span class="hljs-keyword">type</span>.
<span class="hljs-type">Unknown</span> url <span class="hljs-keyword">handler</span> <span class="hljs-keyword">type</span>.
Unknown url handler type.

これは、 url の次の行以降で記載している部分の先頭に 空白(ブランク)が 2つ 入っていないことが原因です。 今回の例でいうと、static_files, upload の各行の先頭に 2つ の 空白(ブランク) を入れることで エラー が解消されます。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<span class="hljs-comment"># "Unknown url handler type." because of single blank</span>
<span class="hljs-string">Unknown</span> <span class="hljs-string">url</span> <span class="hljs-string">handler</span> <span class="hljs-attr">typhandlers:</span>
<span class="hljs-attr">- url:</span> <span class="hljs-string">/</span>
<span class="hljs-attr"> static_files:</span> <span class="hljs-string">dist/index.html</span>
<span class="hljs-attr"> upload:</span> <span class="hljs-string">dist/index.htmle.</span>
<span class="hljs-comment">#</span>
<span class="hljs-comment"># Work without Error because of double blank</span>
<span class="hljs-attr">handlers:</span>
<span class="hljs-attr">- url:</span> <span class="hljs-string">/</span>
<span class="hljs-attr"> static_files:</span> <span class="hljs-string">dist/index.html</span>
<span class="hljs-attr"> upload:</span> <span class="hljs-string">dist/index.html</span>
<span class="hljs-comment">##</span>
<span class="hljs-comment"># "Unknown url handler type." because of single blank</span> <span class="hljs-string">Unknown</span> <span class="hljs-string">url</span> <span class="hljs-string">handler</span> <span class="hljs-attr">typhandlers:</span> <span class="hljs-attr">- url:</span> <span class="hljs-string">/</span> <span class="hljs-attr"> static_files:</span> <span class="hljs-string">dist/index.html</span> <span class="hljs-attr"> upload:</span> <span class="hljs-string">dist/index.htmle.</span> <span class="hljs-comment">#</span> <span class="hljs-comment"># Work without Error because of double blank</span> <span class="hljs-attr">handlers:</span> <span class="hljs-attr">- url:</span> <span class="hljs-string">/</span> <span class="hljs-attr"> static_files:</span> <span class="hljs-string">dist/index.html</span> <span class="hljs-attr"> upload:</span> <span class="hljs-string">dist/index.html</span> <span class="hljs-comment">##</span>
# "Unknown url handler type." because of single blank
Unknown url handler typhandlers:
- url: / 
 static_files: dist/index.html
 upload: dist/index.htmle.
#

# Work without Error because of double blank
handlers:
- url: / 
  static_files: dist/index.html
  upload: dist/index.html
##

デプロイ 確認

ブラウザで確認

デプロイ が完了した後、先程の デプロイログ のなかで表示された target url に記載された URL にアクセスすると、ローカルで起動したものと同じ内容で VUE CLI が表示していることが確認できます。

GCP Console で確認

以下の URL から GCP Console を開き、デプロイ先のプロジェクトを選択し、 左のメニューから App Engine を選択します。何もデプロイしていない状況では、何も表示されていなかったところに各種情報が表示されるようになりました。

Google Cloud Platform
Google Cloud Platform lets you build, deploy, and scale applications, websites, and services on the same infrastructure ...


補足:サービスについて

app.yaml ファイルに service: として名称を定義することで、 1 つの プロジェクト 内に複数の サービス をデプロイすることが可能です。サービスを指定しない場合、 default というサービスにデプロイされることになります。 ただし、 初めて デプロイ する場合は、default サービス として デプロイ しないと以下のエラーが出力されデプロイすることができないことに注意してください。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
The <span class="hljs-keyword">first</span> service (module) you upload <span class="hljs-built_in">to</span> <span class="hljs-keyword">a</span> <span class="hljs-built_in">new</span> application must be <span class="hljs-keyword">the</span> <span class="hljs-string">'default'</span> service (module).
The <span class="hljs-keyword">first</span> service (module) you upload <span class="hljs-built_in">to</span> <span class="hljs-keyword">a</span> <span class="hljs-built_in">new</span> application must be <span class="hljs-keyword">the</span> <span class="hljs-string">'default'</span> service (module).
The first service (module) you upload to a new application must be the 'default' service (module).


例えば、 runtime に nodejs16 を指定し、 service に nodejs16 を指定した app.yaml は以下の通りです。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<span class="hljs-attribute">runtime</span>: nodejs16
<span class="hljs-attribute">env</span>: standard
<span class="hljs-attribute">service</span>: nodejs16
<span class="less"><span class="hljs-attribute">handlers</span>:
- <span class="hljs-attribute">url</span>: /
<span class="hljs-attribute">static_files</span>: dist/index.html
<span class="hljs-attribute">upload</span>: dist/index.html
- <span class="hljs-attribute">url</span>: /(.*)
<span class="hljs-attribute">static_files</span>: dist/\<span class="hljs-number">1</span>
<span class="hljs-attribute">upload</span>: dist/(.*)</span>
<span class="hljs-attribute">runtime</span>: nodejs16 <span class="hljs-attribute">env</span>: standard <span class="hljs-attribute">service</span>: nodejs16 <span class="less"><span class="hljs-attribute">handlers</span>: - <span class="hljs-attribute">url</span>: / <span class="hljs-attribute">static_files</span>: dist/index.html <span class="hljs-attribute">upload</span>: dist/index.html - <span class="hljs-attribute">url</span>: /(.*) <span class="hljs-attribute">static_files</span>: dist/\<span class="hljs-number">1</span> <span class="hljs-attribute">upload</span>: dist/(.*)</span>
runtime: nodejs16
env: standard
service: nodejs16

handlers:
- url: /
  static_files: dist/index.html
  upload: dist/index.html

- url: /(.*)
  static_files: dist/\1
  upload: dist/(.*)

なお、このときの target url は以下の様になり、default の URL とは異なるものが設定されます。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
target url:
<a rel="noopener" href="https://nodejs16-dot-sample-for-blog.an.r.appspot.com" title="403 Forbidden" class="blogcard-wrap external-blogcard-wrap a-wrap cf" target="_blank"><div class="blogcard external-blogcard eb-right cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img data-src="https://s.wordpress.com/mshots/v1/https%3A%2F%2Fnodejs16-dot-sample-for-blog.an.r.appspot.com?w=320&h=180" alt="" class="blogcard-thumb-image external-blogcard-thumb-image lozad lozad-img" loading="lazy" width="320" height="180"><noscript><img <span class="hljs-attribute">loading</span>=<span class="hljs-string">"lazy"</span> <span class="hljs-attribute">decoding</span>=<span class="hljs-string">"async"</span> <span class="hljs-attribute">src</span>=<span class="hljs-string">"https://s.wordpress.com/mshots/v1/https%3A%2F%2Fnodejs16-dot-sample-for-blog.an.r.appspot.com?w=320&#038;h=180"</span> <span class="hljs-attribute">alt</span>=<span class="hljs-string">""</span> <span class="hljs-attribute">class</span>=<span class="hljs-string">"blogcard-thumb-image external-blogcard-thumb-image"</span> <span class="hljs-attribute">width</span>=<span class="hljs-string">"320"</span> <span class="hljs-attribute">height</span>=<span class="hljs-string">"180"</span> /></noscript></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">403 Forbidden</div><div class="blogcard-snippet external-blogcard-snippet"></div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img data-src="https://www.google.com/s2/favicons?domain=https://nodejs16-dot-sample-for-blog.an.r.appspot.com" alt="" class="blogcard-favicon-image external-blogcard-favicon-image lozad lozad-img" loading="lazy" width="16" height="16"><noscript><img <span class="hljs-attribute">loading</span>=<span class="hljs-string">"lazy"</span> <span class="hljs-attribute">decoding</span>=<span class="hljs-string">"async"</span> <span class="hljs-attribute">src</span>=<span class="hljs-string">"https://www.google.com/s2/favicons?domain=https://nodejs16-dot-sample-for-blog.an.r.appspot.com"</span> <span class="hljs-attribute">alt</span>=<span class="hljs-string">""</span> <span class="hljs-attribute">class</span>=<span class="hljs-string">"blogcard-favicon-image external-blogcard-favicon-image"</span> <span class="hljs-attribute">width</span>=<span class="hljs-string">"16"</span> <span class="hljs-attribute">height</span>=<span class="hljs-string">"16"</span> /></noscript></div><div class="blogcard-domain external-blogcard-domain">nodejs16-dot-sample-for-blog.an.r.appspot.com</div></div></div></div></a>
target url: <a rel="noopener" href="https://nodejs16-dot-sample-for-blog.an.r.appspot.com" title="403 Forbidden" class="blogcard-wrap external-blogcard-wrap a-wrap cf" target="_blank"><div class="blogcard external-blogcard eb-right cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img data-src="https://s.wordpress.com/mshots/v1/https%3A%2F%2Fnodejs16-dot-sample-for-blog.an.r.appspot.com?w=320&h=180" alt="" class="blogcard-thumb-image external-blogcard-thumb-image lozad lozad-img" loading="lazy" width="320" height="180"><noscript><img <span class="hljs-attribute">loading</span>=<span class="hljs-string">"lazy"</span> <span class="hljs-attribute">decoding</span>=<span class="hljs-string">"async"</span> <span class="hljs-attribute">src</span>=<span class="hljs-string">"https://s.wordpress.com/mshots/v1/https%3A%2F%2Fnodejs16-dot-sample-for-blog.an.r.appspot.com?w=320&#038;h=180"</span> <span class="hljs-attribute">alt</span>=<span class="hljs-string">""</span> <span class="hljs-attribute">class</span>=<span class="hljs-string">"blogcard-thumb-image external-blogcard-thumb-image"</span> <span class="hljs-attribute">width</span>=<span class="hljs-string">"320"</span> <span class="hljs-attribute">height</span>=<span class="hljs-string">"180"</span> /></noscript></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">403 Forbidden</div><div class="blogcard-snippet external-blogcard-snippet"></div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img data-src="https://www.google.com/s2/favicons?domain=https://nodejs16-dot-sample-for-blog.an.r.appspot.com" alt="" class="blogcard-favicon-image external-blogcard-favicon-image lozad lozad-img" loading="lazy" width="16" height="16"><noscript><img <span class="hljs-attribute">loading</span>=<span class="hljs-string">"lazy"</span> <span class="hljs-attribute">decoding</span>=<span class="hljs-string">"async"</span> <span class="hljs-attribute">src</span>=<span class="hljs-string">"https://www.google.com/s2/favicons?domain=https://nodejs16-dot-sample-for-blog.an.r.appspot.com"</span> <span class="hljs-attribute">alt</span>=<span class="hljs-string">""</span> <span class="hljs-attribute">class</span>=<span class="hljs-string">"blogcard-favicon-image external-blogcard-favicon-image"</span> <span class="hljs-attribute">width</span>=<span class="hljs-string">"16"</span> <span class="hljs-attribute">height</span>=<span class="hljs-string">"16"</span> /></noscript></div><div class="blogcard-domain external-blogcard-domain">nodejs16-dot-sample-for-blog.an.r.appspot.com</div></div></div></div></a>
target url:                  
403 Forbidden

App Engine のサービスは、 マイクロサービス を実現する目的で提供されているようです。以下の概念図や、引用元である、 Microservices Architecture on Google App Engine  を確認してください。

引用元: Microservices Architecture on Google App Engine


デプロイ した サービス を非公開にする方法

デプロイしたサービスは、そのままでは全世界に公開されている状態ですので、非公開にしたい場合は、以下の方法を検討してください。

  • サービスの削除
  • アプリ(プロジェクト)  無効化
  • ファイアウォール の活用

default サービス 以外の削除

デプロイした サービス を削除するには、 GCP Console で App Engine を開き、左のメニューから サービス を選択します。表示された サービス にチェックを入れ、 「削除」 をクリックします。

確認メッセージで、 「削除」 をクリックすることでサービスが削除されます。

default サービス の削除 ( アプリ無効化 )

先程は、名称を付与した サービス について削除を実行しましたが、 default サービス に関しては削除することができません。代替案として、 アプリケーションを無効にする という方法が提供されています。

確認ダイアログが表示されるので、 アプリ ID を入力し、「無効にする」 をクリックします。

アプリケーションが無効化されると、以下のような画面が表示されます。 「アプリケーションを有効にする」ボタンから、再度有効にすることができます。

この状態でデプロイ先の URL にアクセスすると以下のように 404 エラー となります。

なお、 アプリ無効化 を実施すると、 AppEngine だけでなくプロジェクト内のその他のサービス( Firestore, Cloud Functions 等 ) も合わせて無効になってしまうの点に注意してください。

default サービスへのアクセス無効化

アプリ無効化 ではプロジェクト内の他のサービスにも影響が出てしまうため、それは困るという場面もあるかと思います。その場合、 ファイアウォール ルール を編集し default サービスへのアクセス無効化 を検討するとよいかもしれません。ここでは手っ取り早く、 アクセスを拒否する方法を紹介します。

初期状態であれば、 GCP Console から ファイアウォール ルール を選択すると 以下のように 「すべての通信を許可するデフォルト設定」 が一つ設定されていると思いますので、これを編集していきます。

「一致したときのアクション」 から 「許可しない」を選択し 「保存」 をクリックします。

この状態でデプロイ先の URL にアクセスすると以下のように 404 エラー となります。


まとめ

  • Vue.js などの Node.js アプリ をデプロイするには App Engine, または Cloud Run を用いる
  • Vue CLI の場合、 プロジェクトトップディレクトリに app.yaml を配置する必要がある
  • デプロイは gcloud app deploy コマンドを用いる
  • App Engine は サービス単位でデプロイする構造
  • デフォルトサービスは削除不可。 アプリ無効化 や ファイアウォール ルール を活用する方法を検討する

関連記事

[ads]
Ads