VueJSで作成したwebアプリケーションをCloud Runで公開するまで

# はじめに

本記事は、VueJSで作成したwebアプリケーションをGCPのCloud Runを使用して公開するまで行った作業の備忘録の記事になります。本記事を書く執筆者はweb・クラウド系のエンジニアではなく、独自で調べてwebアプリケーションを構築しているので、誤記載や更に効率的な構築手順などがありましたらご指摘していただけると幸いです。

## 構築環境
もし本記事を参考にwebアプリケーションを構築する場合は、事前に以下環境の準備をしてください。
* GCPのアカウント作成と請求先の紐付け
* GCPのサービス「Cloud Run」と「Artifact Registry」の有効化
* 開発環境のクライアントPCにDockerおよびdocker-composeのインストールの実施
* 開発環境のクライアントPCに[Cloud SDKのインストール](https://cloud.google.com/sdk/docs/install?hl=ja)を実施済み

本記事の執筆者の開発環境のフォルダ階層は以下の通りである。`webapp`というフォルダに`vujes_project`という名前のvuejsのプロジェクトファイルを作成している。また、`webapp`のフォルダ改装にDockerfileなどのコンテナ化をする定義ファイルを配置している。

```:フォルダ階層
webapp
    |-vujes_project
    |   |-dist
    |   |-node_modules
    |   |-public
    |   |-src
    |   |-package-loc.json
    |   |-package.json
    |
    |-docker-compose.yml
    |-Dockerfile
    |-nginx.conf
```

## VueJSとは
ReactやAngulerといったJavaScriptライブラリ・フレームワークの中で、UIの構築が行いやすいとされているのがVue.jsです。今回私は、HTML,CSS,JavaScriptを多少知っており、シングルページアプリケーションに向いているとのことで、VueJSを使用してちょっとしたwebアプリケーションを作ってみようと思いました。

## Cloud Runとは

>Cloud Run はマネージド コンピューティング プラットフォームで、リクエストまたはイベント経由で呼び出し可能なコンテナを実行できます。Cloud Run はサーバーレスです。インフラストラクチャ管理が一切不要なため、最も重要な作業であるアプリケーションの構築に集中できます。

[Google Cloud 公式ドキュメントより](https://cloud.google.com/run/docs#docs)
つまり、作成したweb系のアプリケーションのコンテナイメージを構築していれば、デプロイするだけでクラウド上でweb系のアプリケーションが実行することができます。また、トラフィックに応じてスケールアップやスケールダウンも自動的にしてくれるので、インフラストラクチャを開発者が管理することもないので、今回はCloud Runを使用することにしました。
また、以下のインフラスペック内であれば無料なのもCloud Runを使うメリットとして考えいました。

| CPU | メモリ | リクエスト |
|:-----------|:------------:|:------------|
| 180,000 vCPU | 360,000 GiB 秒 | 200 万リクエスト |

# Cloud Runで公開するまでの手順

## VueJSのデプロイとコンテナ化

VueJSでwebアプリケーションを開発する際は`npm run serve`を用いてローカルホスト上でwebアプリケーションを動かして、動作確認しながら開発すると思いますが、開発したwebアプリケーションを公開する際は`npm run build`を用いて、開発したソースを圧縮して公開できる状態にします。

`npm run build`を行うと、`vujes_project`という名前のvuejsのプロジェクトフォルダ内に`dist`というフォルダが作成しされ、VueJSで開発したソースなどが全て`dist`フォルダ内に圧縮されます。

```:フォルダ階層
webapp
    |-vujes_project
    |   |-dist
    |   |-node_modules
    |   |-public
    |   |-src
    |   |-package-loc.json
    |   |-package.json
    |
    |-docker-compose.yml
    |-Dockerfile
    |-nginx.conf
```

ビルド後フォルダ`dist`にはビルドされた公開用のwebアプリケーションのモジュール一式が配置されますが、ビルドされたモジュール一式は`npm run serve`で実行して確認することができません。ビルドされたモジュールをNode.js静的ファイルサーバーを使用して実行できるようにします。実行方法は以下の通りです。
※ビルドされたモジュール一式を実行してみると想定外にwebアプリケーションのUIなどが崩れているので、念の為実行することをお勧めします。

```
npm install -g serve
```

```:静的ファイルサーバーの実行
serve -s dist
```

上記の静的ファイルサーバーを実行することで、以下のようにローカルホストのポート3000番にて動作確認することが出来ます。

```:例
   ┌───────────────────────────────────────────────────┐
   │                                                   │
   │   Serving!                                        │
   │                                                   │
   │   - Local:            http://localhost:3000       │
   │   - On Your Network:  http://xxx.xx.xxx.xxx:3000  │
   │                                                   │
   │   Copied local address to clipboard!              │
   │                                                   │
   └───────────────────────────────────────────────────┘
```

デプロイ用にビルドされたモジュール一式を、サーバソフトウェアnginxを使用して動作・公開できるようにします。この時、nginxはローカル環境やサーバにインストールして用意するのではなく、dokcerのコンテナを利用してコンテナ内でnginxが動作する環境を用意して、その環境内でデプロイされたモジュール一式を動かします。

今回、作成したDokcerfileおよびdocker-compose.ymlは以下の通りになります。

```:Dockerfile
# 本番環境
FROM nginx:1.20.1-alpine

COPY ./vujes_project/dist /usr/share/nginx/html
ADD nginx.conf /etc/nginx/nginx.conf
EXPOSE 8080
CMD ["nginx", "-g", "daemon off;"]
```

```:docker-compose.yml
version: '3'
services:
  web:
    container_name: my-vuejs-project
    build:
      context: .
      dockerfile: Dockerfile
    ports:
    - "8080:8080"
```

また、コンテナ内で動作するnginxの設定ファイルを用意します。今回用意したnginxのファイルは以下の通りです。
```:nginx.conf
user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';

    #access_log  /var/log/nginx/access.log  main;

    #sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    # データ圧縮
    gzip on;
    gzip_disable "msie6";

    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_buffers 16 8k;
    gzip_http_version 1.1;
    gzip_types text/plain text/css application/javascript application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;

    server {
        listen 8080;

        root /usr/share/nginx/html;
        index index.html;

        server_name localhost;

        server_tokens off;

        location ~* \.(?:manifest|appcache|html?|xml|json)$ {
            expires -1;
        }

        location ~* \.(?:css|js)$ {
            try_files $uri =404;
            expires 1y;
            access_log off;
            add_header Cache-Control "public";
        }

        location ~ ^.+\..+$ {
            try_files $uri =404;
        }

        location / {
            try_files $uri $uri/ /index.html;
        }
    }
}
```

上記に記載した`Dockerfile`、`docker-compose.yml`および`nginx.conf`を用意できたら、以下コマンドを実行してコンテナイメージの作成および、起動を行います。
※オプションで--buildをつけることで起動前にイメージも構築します。
```:コンテナイメージの作成と起動
docker-compose up --build
```

コンテナイメージの作成と起動後、以下のようなコンソールログが出力されたら`http://localhost:8080`にアクセスしましょう。問題なくデプロイされた開発したwebアプリケーションが動作してることを確認できたら、コンテナ化成功です。(コマンド`docker images`でもコンテナイメージが問題なく作成されたかどうか確認することが出来ます)

```
Successfully built xxxxxxxxxxxxxx
Successfully tagged my-vuejs-project:latest
```

## コンテナイメージをArtifact Registryにアップロード
コンテナイメージが問題なく作成されたら、ローカル環境で作成したコンテナイメージをGCPのArtifact Registryにアップロードします。
まず、ナビゲーションメニューを開きArtifact Registryを選択します。選択後、「リポジトリを作成」を選択します。

![スクリーンショット 2022-09-26 20.35.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154154/7b949b90-6f43-9902-af89-43dde392a5c4.png)

「リポジトリを作成」を選択後以下のようにリポジトリの「名前」を記入したり設定します。この時、形式は「Docker」を選択してください。私はロケーションタイプは「マルチリージョン」の「asia」にしましたが、「リージョン」やその他地域にしても特に問題ありません。「リージョン」や私と違う地域を選択した方は後ほどの実行コマンドが若干異なるのでご注意ください。

ちなみに、「リージョンを」指定した場合は、指定したリージョンにデータが保管され、単一リージョン内のゾーン間でデータレプリケーションが行われます。「マルチリージョン」を選択した場合は、選択した地域のリージョン内に分散してデータが保管されるため、非常に広域に渡って可用性を高めることができます。

![スクリーンショット 2022-09-26 20.36.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154154/e59d2650-2987-2f2a-47d7-9086f1bff097.png)

作成するリポジトリの設定が終わったら、上記の設定画面内の「作成」を選択するとリポジトリが「作成」されます。
リポジトリを作成後、ローカル端末にあるdockerのコンテナイメージをArtifact Registryにコミットする設定を行なっていきます。Artifact Registry上で作成されたリポジトリを選択すると右上側の画面に「設定の手順」と表示されている箇所を選択します。すると、Cloud SDK上で実行する必要があるコマンドが表示されるので、それをコピー＆ペーストを行い実行します。
※ローカル環境にインストールしてあるCloud SDKに認証していない方は`gcloud auth login`を実行をしてユーザアカウント認証もしくは、サービスアカウントを作成しキーファイルをダウンロードしてサービスアカウント認証を行なってください。

![設定の手順.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154154/5ef73fcf-789d-8b14-9da0-0a171668718c.png)

```:実行コマンド
gcloud auth configure-docker asia-docker.pkg.dev
```

次に、ローカル環境にあるコンテナイメージを作成したArtifact Registry上にあるリポジトリに紐付けてpushするためにtag で名前を変えます。設定するタグ名は以下画像のような、設定手順の下のコピーアイコンを選択するとコピーすることが出来ます。

![タグ名のコピー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154154/e9900717-3e76-162a-4dd7-3ebd5baf5d4b.png)

タグ付けを行う場合は以下のようなコマンドを実行します。

```
docker tag [ローカルで作成したコンテナ名] [タグ名]
```

私の場合は以下のようなコマンドになります。

```:実行コマンド例
docker tag my-vuejs-project:latest asia-docker.pkg.dev/[projectID]/my-vuejs-project/deploy:tag1
```

タグ付けが完了したら、以下コマンドを実行しArtifact Registry上にローカル環境で作成したコンテナイメージをpushします。

```
docker push asia-docker.pkg.dev/[projectID]/my-vuejs-project/deploy:tag1
```

プッシュが完了するとリポジトリに`deploy`が作成されていることが確認できます。

![スクリーンショット 2022-09-28 20.19.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154154/c0f5436f-fb2b-06a2-5adc-5ed233e96125.png)

## Cloud RunにArtifact Registryのコンテナイメージをデプロイ
最後に、Artifact RegistryのコンテナイメージをCloud Runにデプロイしてwebサイトを公開できる状態にします。GCPのナビゲーションメニューからCloud Runを選択します。
Cloud Run選択後、「サービスの作成」を選択します。

![スクリーンショット 2022-09-28 20.40.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154154/8b0f9fb0-de92-ac9f-25f6-5cd29c9c50bf.png)

「サービスの作成」を選択すると作成するサービスの設定画面が表示されます。今回、Cloud Run上でのデプロイはArtifact Registryのコンテナイメージからデプロイするので、「既存のコンテナ イメージから 1 つのリビジョンをデプロイする」のラジオボタンを選択し、コンテナイメージのURLの入力欄を選択すると以下画像のように、デプロイするコンテナイメージを選択することができます。

![スクリーンショット 2022-09-28 20.41.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154154/2078172e-5b89-d863-6c73-52c34e9ed722.png)

「コンテナイメージの選択」画面から、Artifact Registryに`push`したコンテナイメージを選択し、Cloud Runのサービスの設定画面の「サービス名」・「リージョン」を任意で設定をし、一番下の「作成」を選択すれば、Cloud Runにデプロイ完了です。

![スクリーンショット 2022-09-28 20.47.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154154/1ea6d839-2a69-5dd1-b6cb-c33a85060642.png)

# まとめ
本記事は、VueJSで作成したwebアプリケーションをCloud Runにデプロイする手順の備忘録として記載しました。何かご指摘事項があればコメントして頂ければ幸いです。
なお、作成しているwebアプリケーションが大きくなって将来性を考えてVueJSのプロジェクトをNext.jsとReactに移行中です。移行完了後、Next.jsやReactのプロジェクトも本手順で問題なくCloud Runにデプロイできるか検証してみようかと思います。