Vue.jsとFlaskで作るシングルページアプリ。デプロイメント

仮想プライベートサーバーへの展開

Vue.js と Flask を使ったフルスタック Web 開発のチュートリアルシリーズの 7 回目、最終回へようこそ。この投稿では、このシリーズで構築したアプリケーションをどのようにデプロイするかについて説明します。

この投稿のコードは、私の GitHub アカウントの SeventhPost というブランチに掲載されています。

シリーズ内容

  1. VueJSの紹介と概要
  2. Vueルーターの操作
  3. Vuexによる状態管理
  4. FlaskによるRESTful API
  5. REST APIによるAJAXの統合
  6. JWT認証
  7. 仮想プライベートサーバへのデプロイメント (あなたはここにいます)

技術の概要

このチュートリアルでは、Flask REST API と Vue.js SPA アプリケーションを分散配置するために必要ないくつかの技術について説明します。以下に、その技術と用途を列挙します。

  • Ubuntu LTS 16.04: 様々なアプリケーションやサーバーを実行するためのホストサーバーです。
  • uWSGI。Pythonアプリケーション(今回はFlask)を実行するためのWSGI(Webserver Gateway Interface)コンテナサーバです。
  • Nginx: uWSGIへのリバースプロキシが可能な高パフォーマンスのノンブロッキングHTTPウェブサーバです。
  • Node.js / NPM。Vue.js SPAアプリケーションを構築するためのJavascript環境

デプロイメントに必要なコードの準備

アプリケーションを本番環境にデプロイした後、コードをより保守しやすくするために、いくつかの変更を行う必要があります。

例えば、Vue.jsアプリケーションの survey-spa の api/index.js では、 API_URL という変数が開発サーバー http://127.0.0.1:5000/api を指すようにハードコードされています。これでは、デプロイするたびに、これを本番サーバのIPアドレスに変更する必要があります。

経験上、このIPアドレスを更新するのを忘れてしまうようなアプリケーションの変更が常にあり、将来のデプロイメントが必要になることを学びました。より良いアプローチは、私がこの更新を忘れるリスクを取り除き、代わりにビルドプロセスの設定を利用してこれを処理し、デプロイ時に覚えなければならないことを少なくする(つまり、必要なステップ数を減らす)ことです。これにより、将来のアップデートでデプロイに失敗するリスクを大幅に減らすことができます。

Survey-spa/config ディレクトリに移動し、 dev.env.js と prod.env.js ファイルを修正して、以下のように API_URL という変数を定義し、 dev では http://localhost:5000/api 、prod では http://${process.env.BASE_URL}/api という値を割り当てて、この処理を実現しています。

// dev.env.js


'use strict'
const merge = require('webpack-merge')
const prodEnv = require('./prod.env')


module.exports = merge(prodEnv, {
  NODE_ENV: '"development"',
  API_URL: JSON.stringify(`http://localhost:5000/api`)
})


// prod.env.js
'use strict'
module.exports = {
  NODE_ENV: '"production"',
  API_URL: JSON.stringify(`http://${process.env.BASE_URL}/api`)
}


注: process.env.BASE_URL の値は、Ubuntuサーバーのユーザーの .bash_profile に追加する環境変数で、サーバーのIPアドレスと同じ値に設定します。

次に、api/index.jsの const API_URL = 'http://127.0.0.1:5000/api' という行を修正し、 process.env.API_URL と等しくなるように設定します。

次に、Flaskアプリケーションに、Flask REST APIのエントリポイントとして機能するwsgi.pyという新しいモジュールを追加する必要があります。wsgi.pyモジュールは、appオブジェクトの run(...) メソッドへの呼び出しを持たないことを除けば、appserver.pyモジュールと非常によく似ています。これは、appオブジェクトが、app.run(...)が呼ばれた時に生成される通常の開発サーバーではなく、uwsgiコンテナサーバーがその高速なバイナリプロトコルに対して実行するための呼び出し可能として機能するためです。

# backend/wsgi.py


from surveyapi.application import create_app
app = create_app()


これで、バージョンコントロールに変更を反映させ、本番サーバーに移動してプロジェクトをプルダウンし、本番サーバーでアプリケーションを実行するために使用するプログラムをセットアップすることができるようになりました。

Ubuntuサーバーの準備

次に、AWS、DigitalOcean、Linodeなどのクラウドサービスでホストされている本番Ubuntu仮想プライベートサーバに乗り込み、「技術の概要」セクションで挙げたすべてのグッズのインストールを開始します。

$ apt-get update
$ apt-get install python3-pip python3-dev python3-venv nginx nodejs npm


これらのインストールが完了したら、アプリケーションを実行するために “survey” というユーザーを作成し、コードを格納します。

$ adduser survey
$ usermod -aG sudo survey
$ su survey
$ cd


これで、survey ユーザーのホームディレクトリ /home/survey に移動しました。

survey ユーザーを作成したら、.bash_profile ファイルの最後に次の行を追加して、プロダクションサーバーの IP アドレスを含むように更新します。123.45.67.89 は、私のサーバの偽の IP アドレスを表していることに注意してください。もし、あなたがこの通りにやるなら、これをあなたの本当のIPアドレスに置き換えてください。

export BASE_URL=123.45.67.89


次に、ファイアウォール(ufw)にOpenSSHが使えることを伝え、有効化したいと思います。

$ sudo ufw allow OpenSSH
$ sudo ufw enable


これが終わったら、サーバーにレポをクローンして、ビルドとデプロイを行います。

$ git clone https://github.com/amcquistan/flask-vuejs-survey.git


flask-vuejs-survey/frontend/survey-spa に cd して、フロントエンドの依存関係をインストールし、本番アプリケーションをビルドします。

$ cd flask-vuejs-survey/frontend/survey-spa
$ npm install
$ npm run build


dist “というディレクトリが作成され、index.htmlページと、”static “というディレクトリには、コンパイルされたCSSとJavaScriptのファイルが格納される予定です。これらは、SPA のフロントエンドアプリケーションを構成するために、Nginx サーバーを立ち上げるものです。

次に、Pythonアプリケーションを実行するために、/home/surveyディレクトリにPython3インタプリタを分離した仮想環境を作成します。インストールが完了したら起動し、バックエンドプロジェクトのディレクトリに移動して、requirements.txt ファイルに指定されている依存パッケージをインストールします。

$ python3 -m venv venv
$ source venv/bin/activate
(venv) $ cd flask-vuejs-survey/backend
(venv) $ pip install -r requirements.txt


これで、sqliteデータベースを初期化し、マイグレーションを実行して、REST APIで必要な様々なデータベーステーブルを作成することができます。

(venv) $ python manage.py db upgrade


この時点で、Flask の開発サーバーを起動して、すべてが期待通りに動いていることを確認したいと思います。その前に、ポート5000でのトラフィックを許可するように ufw サービスに指示する必要があります。

(venv) $ sudo ufw allow 5000
(venv) $ python appserver.py


ブラウザで http://123.45.67.89:5000/api/surveys/ にアクセスすると、[] というシンプルな JSON レスポンスが表示されます。さらに、サーバーに接続されたターミナルには、ブラウザから発行された GET リクエストのログメッセージが表示されるはずです。

ターミナルで Ctrl+C を押して Flask 開発サーバを kill し、Flask REST API の実行を制御するための uwsgi の設定に移ります。uwsgi がどこから来たかというと、先ほど pip でインストールした requirements.txt で要件として指定されているものです。

uWSGIコンテナサーバーのセットアップ

Flaskの開発サーバーと同様に、uWSGIサーバーがアプリケーションを提供できるかどうか、以下のようにテストします。

(venv) $ uwsgi --socket 0.0.0.0:5000 --protocol=http -w wsgi:app


ここでも、ブラウザで前回と同じリクエストをリフレッシュすると、空のJSON配列のレスポンスが返ってくるはずです。満足したら、またターミナルで Ctrl+C キーを押して、次に進みます。

uWSGIコンテナ・サーバーの設定を完了するには、あと2つのステップを踏まなければなりません。一つは、uWSGI が読み込む設定ファイルを作成することで、上で使用したコマンドラインのフラグや引数の多くを置き換えることができます。2番目のステップは、uWSGIコンテナサーバをUbuntuサーバ上で既に動作している他の多くのサービスのように管理するためのsystemdサービスファイルを作成することです。

バックエンドディレクトリに surveyapi.ini というファイルを作成し、以下のように記述しています。

[uwsgi]
module = wsgi:app


master = true
processes = 4


socket = myproject.sock
chmod-socket = 660
vacuum = true


die-on-term = true


この設定ファイルにより、uWSGIは呼び出し可能なオブジェクトがwsgi.pyモジュール内のappオブジェクトであることを知ることができます。また、surveyapi.sock というソケットファイルを介して通信されるアプリケーションのリクエストを処理するために、4つのプロセスを生成して使用するように指示します。このソケットファイルは、Nginx Web サーバが読み取りと書き込みができるように、十分に緩いパーミッションを持っています。vacuumdie-on-term` の設定は、適切なクリーンアップを行うためのものです。

systemd のサービスファイルは、/etc/systemd/system ディレクトリに surveyapi.service というファイルを作成し、以下のようにディスクリプタとアクセス、書き込み、実行コマンドを追加する必要があります。

(venv) $ sudo nano /etc/systemd/system/surveyapi.service


そして、その中に以下のように入力します。

[Unit]
Description=uWSGI Python container server
After=network.target


[Service]
User=survey
Group=www-data
WorkingDirectory=/home/survey/flask-vuejs-survey/backend
Environment="PATH=/home/survey/venv/bin"
ExecStart=/home/survey/venv/bin/uwsgi --ini surveyapi.ini


[Install]
WantedBy=multi-user.target


これで、サービスを起動して状態を確認し、バックエンドディレクトリに surveyapi.sock が含まれていることを確認できます。

(venv) $ sudo systemctl start surveyapi
(venv) $ sudo systemctl status surveyapi
   Loaded: loaded (/etc/systemd/system/surveyapi.service; disabled; vendor preset: enabled)
   Active: active (running) since Mon 2018-04-23 19:23:01 UTC; 2min 28s ago
 Main PID: 11221 (uwsgi)
    Tasks: 6
   Memory: 28.1M
      CPU: 384ms
   CGroup: /system.slice/surveyapi.service
           ├─11221 /home/survey/venv/bin/uwsgi --ini surveyapi.ini
           ├─11226 /home/survey/venv/bin/uwsgi --ini surveyapi.ini
           ├─11227 /home/survey/venv/bin/uwsgi --ini surveyapi.ini
           ├─11228 /home/survey/venv/bin/uwsgi --ini surveyapi.ini
           ├─11229 /home/survey/venv/bin/uwsgi --ini surveyapi.ini
           └─11230 /home/survey/venv/bin/uwsgi --ini surveyapi.ini


Apr 23 19:23:01 ubuntu-s-1vcpu-2gb-sfo2-01 uwsgi[11221]: mapped 437520 bytes (427 KB) for 5 cores
Apr 23 19:23:01 ubuntu-s-1vcpu-2gb-sfo2-01 uwsgi[11221]: *** Operational MODE: preforking ***
Apr 23 19:23:01 ubuntu-s-1vcpu-2gb-sfo2-01 uwsgi[11221]: WSGI app 0 (mountpoint='') ready in 0 seconds on interpreter 0x8b4c30 pid: 112
Apr 23 19:23:01 ubuntu-s-1vcpu-2gb-sfo2-01 uwsgi[11221]: *** uWSGI is running in multiple interpreter mode ***
Apr 23 19:23:01 ubuntu-s-1vcpu-2gb-sfo2-01 uwsgi[11221]: spawned uWSGI master process (pid: 11221)
Apr 23 19:23:01 ubuntu-s-1vcpu-2gb-sfo2-01 uwsgi[11221]: spawned uWSGI worker 1 (pid: 11226, cores: 1)
Apr 23 19:23:01 ubuntu-s-1vcpu-2gb-sfo2-01 uwsgi[11221]: spawned uWSGI worker 2 (pid: 11227, cores: 1)
Apr 23 19:23:01 ubuntu-s-1vcpu-2gb-sfo2-01 uwsgi[11221]: spawned uWSGI worker 3 (pid: 11228, cores: 1)
lines 1-23
(venv) $ ls -l /home/survey/flask-vuejs-survey/backend
-rw-rw-r-- 1 survey survey     201 Apr 23 18:18 appserver.py
-rw-rw-r-- 1 survey survey     745 Apr 23 17:55 manage.py
drwxrwxr-x 4 survey survey    4096 Apr 23 18:06 migrations
drwxrwxr-x 2 survey survey    4096 Apr 23 18:52 __pycache__
-rw-rw-r-- 1 survey survey     397 Apr 23 18:46 requirements.txt
drwxrwxr-x 3 survey survey    4096 Apr 23 18:06 surveyapi
-rw-rw-r-- 1 survey survey     133 Apr 23 19:04 surveyapi.ini
srw-rw---- 1 survey www-data     0 Apr 23 19:23 surveyapi.sock
-rw-r--r-- 1 survey survey   10240 Apr 23 18:19 survey.db
-rw-rw-r-- 1 survey survey      84 Apr 23 18:42 wsgi.py


素晴らしい 最後に、システムが起動するたびに自動起動を有効にし、アプリケーションが常に起動していることを確認します。

(venv) $ sudo systemctl enable surveyapi


Nginxのセットアップ

Nginx を使って HTML、CSS、JavaScript などの静的コンテンツを提供したり、Flask / uWSGI アプリケーションへの REST API 呼び出しをリバースプロキシしたりする予定です。これらのことを達成するためにnginxをセットアップするために、これらの様々なリクエストを管理する方法を定義する設定ファイルを作成する必要があります。

etc/nginx/sites-available に survey というファイルを作成し、以下の内容を記述します。

server {
    listen 80;
    server_name 123.45.67.89;


location /api {
        include uwsgi_params;
        uwsgi_pass unix:/home/survey/flask-vuejs-survey/backend/surveyapi.sock;
    }


location / {
    root /home/survey/flask-vuejs-survey/frontend/survey-spa/dist;
    try_files $uri $uri/ /index.html;
  }
}


このファイルでは、IP アドレス 123.45.67.89 を標準の HTTP ポート 80 でリッスンする新しいサーバーブロック設定を作成します。次に、/api で始まる URI パスを探して、先に定義したソケットファイルを使用して Flask / uWSGI REST API サーバにリバースプロキシする、と書いてあります。最後に、/の下にある他のすべてをキャッチして、Vue.jsのフロントエンドSPAアプリケーションを事前に構築したときに作成したdistディレクトリのindex.htmlファイルを提供するように設定されています。

この設定ファイルを作成した上で、/etc/nginx/sites-enabled ディレクトリにシンボリックリンクを作成して、Nginx に利用可能なサイトであることを知らせる必要があるのですが、以下のようにします。

$ sudo ln -s /etc/nginx/sites-available/survey /etc/nginx/sites-enabled


HTTP ポートのトラフィックを許可して Nginx にバインドするために、ufw に以下のアップデートを行い、先に開いていた 5000 ポートを閉じます。

$ sudo ufw delete allow 5000
$ sudo ufw allow 'Nginx Full'


このコマンドの後、アップデートを有効にするために、このようにNginxサービスを再起動する必要があります。

$ sudo systemctl restart nginx


これで、再びブラウザで http://123.454.67.89 にアクセスすると、以前の記事で紹介したアンケートアプリケーションが表示されます。

結論

さて今回は、Flask と Vue.js を使って REST API 対応の SPA アプリケーションを構築する方法に関する、この複数パートからなるチュートリアルシリーズの最終ポストです。私は、Flask と Vue.js の技術に関する予備知識がほとんどないことを前提に、多くの Web アプリケーションのユースケースに共通する重要なトピックのほとんどをカバーしようと試みました。

このシリーズにお付き合いいただき、ありがとうございます。また、以下のコメントや批評を遠慮なくお寄せください。

タイトルとURLをコピーしました