Vue.jsとFlaskでフルスタックなWebアプリの開発環境を構築 その1〜〜環境構築〜〜
- はじめに
- vue-cliで雛形のインストール(フロントエンド)
- サンプルページの追加
- ビルドディレクトリの変更
- Flaskのインストール(バックエンド)
- Flaskのサンプル作成
- 404ページの追加
- APIの追加
- 終わりに
はじめに
最近のWebアプリはReact.jsやVue.jsを使用したSPAが主流かと思いますが、データ取得のAPIを実装したりなど、サーバサイドの実装も必要になります。
自分はJavascriptに不慣れなのでサーバサイドはJavascriptではなくPythonを使いたかったので、Flaskと共存できる構成にしたかったのがきっかけです。
いろいろと探してみたらすでに先人がやっていました。
Full-stack single page application with Vue.js and Flask
Single Page Apps with Vue.js and Flask
以下のような構成で開発できるようにします。
app_dir ┗frontend // Vue.jsのプログラム ┗backend // Flaskのプログラム
英語記事なのと、それぞれの記事で若干やっていることが異なるため
それぞれを混ぜる形で環境を構築したので、備忘として自分の言葉でまとめておこうと思います。
vue-cliで雛形のインストール(フロントエンド)
以下コマンドでvue-cliをインストールできます。
$ npm install -g vue-cli
アプリの雛形をインストールします。
$ vue init webpack frontend ? Project name (frontend):[Enter] ? Project description (A Vue.js project):[Enter] ? Author (hogehoge <hogehogehogehoge@gmail.com>):[Enter] ? Vue build (Use arrow keys):[Enter] ❯ Runtime + Compiler: recommended for most users Runtime-only: about 6KB lighter min+gzip, but templates (or any Vue-specific HTML) are ONLY allowed in .vue files - render functions are required elsewhere ? Install vue-router? (Y/n):[Enter] ? Use ESLint to lint your code? (Y/n):[Enter] ? Pick an ESLint preset (Use arrow keys) ❯ Standard (https://github.com/standard/standard) Airbnb (https://github.com/airbnb/javascript) none (configure it yourself) ? Set up unit tests (Y/n):[Enter] ? Pick a test runner (Use arrow keys):[Enter] ❯ Jest Karma and Mocha none (configure it yourself) ? Setup e2e tests with Nightwatch? (Y/n):[Enter] ? Should we run `npm install` for you after the project has been created? (recommended) (Use arrow keys) ❯ Yes, use NPM Yes, use Yarn No, I will handle that myself vue-cli · Generated "frontend". # Installing project dependencies ... # ======================== (略) # Project initialization finished! # ======================== To get started: cd frontend npm run dev Documentation can be found at https://vuejs-templates.github.io/webpack $ cd frontend $ npm install # after installation $ npm run dev DONE Compiled successfully in 21497ms 12:42:34 I Your application is running here: http://localhost:8080
ブラウザでhttp://localhost:8080
を開いて以下の画面が表示されれば、アプリの雛形インストール完了です。
サンプルページの追加
Vue.jsの構成や説明はここでは記載しませんが、練習用にページを追加してみましょう。
frontend/src/components
にHome.vue
とAbout.vue
を追加します。
// Home.vue <template> <div> <p>Home page</p> </div> </template>
// About.vue <template> <div> <p>About</p> </div> </template>
また、frontend/src/router/index.js
を以下のように編集します。
import Vue from 'vue' import Router from 'vue-router' // もともと記載されていたものは削除orコメントアウト // import HelloWorld from '@/components/HelloWorld' // Vue.use(Router) // export default new Router({ // routes: [ // { // path: '/', // name: 'HelloWorld', // component: HelloWorld // } // ] // }) const routerOptions = [ { path: '/', component: 'Home' }, { path: '/about', component: 'About' } ] const routes = routerOptions.map(route => { return { ...route, component: () => import(`@/components/${route.component}.vue`) } }) Vue.use(Router) export default new Router({ routes, mode: 'history' })
このように編集したら再度ブラウザでlocalhost:8080
とlocalhost:8080/about
を開いてページが表示されたらOKです。
ビルドディレクトリの変更
npm run build
コマンドで立ち上がるのは開発用のサーバなので、実際にサービス提供する際はブラウザが解釈できる形にビルドする必要があります。
そのビルドした結果がどこに出力されるかはfrontend/config/index.js
に記載があります。
もともとの記載は、
build: { // Template for index.html index: path.resolve(__dirname, '../dist/index.html'), // Paths assetsRoot: path.resolve(__dirname, '../dist'), //////以下略////// }
となっており、frontend/dist
配下にビルドされたソースコードが出力される設定となっております。
このままだとFlaskでindex.htmlにアクセスするときにfrontend
配下を参照しなくてはならなくなってしまうので、管理上明確に分離するためにfrontend
の上の階層に出力されるようにします。
build: { // Template for index.html index: path.resolve(__dirname, '../../dist/index.html'), // Paths assetsRoot: path.resolve(__dirname, '../../dist'), //////以下略////// }
以下コマンドでビルドします。
$ npm run build
以下のような構成になります。
app_dir ┗frontend // Vue.jsのプログラム ┗dist // コンパイルされたVue.jsのプログラム
$ ls ../dist/
index.html static
Flaskのインストール(バックエンド)
バックエンドのプログラムには今回はFlaskを使用します。
元記事にはPythonの環境を整えるのにvirtualenvを使用していますが、僕はpyenv(pyenv-virtualenv)のほうが使い慣れているのでこちらを使います。
pyenvは以下コマンドでインストールできます。
$ brew install pyenv $ echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bash_profile $ echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bash_profile $ echo 'eval "$(pyenv init -)"' >> ~/.bash_profile $ source ~/.bash_profile
以下コマンドで本アプリ用のPython3.6.1の環境を作ります。
$ pyenv install --list $ pyenv install 3.6.1 $ pyenv virtualenv 3.6.1 vue-flask $ pyenv versionos system 3.6.1 3.6.1/envs/vue-flask vue-flask
app_dir
に行き、以下のコマンドを実行します。
$ pyenv local vue-flask (vue-flask) $ # このようにプロンプトが変わったらOK
これで、app_dir
内に閉じたPython3.6.1の環境が作れました。
(この中でパッケージのインストールを行っても他の環境には影響ありません。)
以下コマンドでFlaskをインストールします。
$ pip install Flask
$ mkdir backend
Flaskのサンプル作成
いよいよFlaskでバックエンドのプログラムを作成していきます。
今回は以下のようなファイル構成にしようと思います。
app_dir ┗appserver.py ┗frontend/ ┗backend/ ┗api.py ┗application.py ┗config.py ┗router.py
それぞれのファイルの中身は以下です。
# appserver.py from backend.application import create_app from flask import render_template app = create_app() @app.route('/') def index(): return render_template("index.html") if __name__ == '__main__': app.run()
# application.py from flask import Flask def create_app(app_name='FLASK-VUE'): app = Flask(app_name, static_folder = "./dist/static", template_folder = "./dist") app.config.from_object('backend.config.BaseConfig') return app
# config.py class BaseConfig(object): DEBUG = True
ポイントは、application.py
において、
app = Flask(app_name, static_folder = "./dist/static", template_folder = "./dist")
を指定することです。
これを指定することで、Flaskのテンプレート参照先が./dist
配下になり、Vue.jsでビルドしたindex.html
が参照されることになります。
以下コマンドで、Flaskの開発用サーバが立ち上がります。
$ python appserver.py * Serving Flask app "FLASK-VUE" (lazy loading) * Environment: production WARNING: Do not use the development server in a production environment. Use a production WSGI server instead. * Debug mode: on * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) * Restarting with stat * Debugger is active! * Debugger PIN: 977-628-540
http://localhost:5000/
にアクセスすると、先程vue.jsで作成したホーム画面が表示されます。
しかし、vue.js側でルーティングを実装したはずの、http://localhost:5000/about
にアクセスしても、Not Foundとなってしまいます。
これは、Flask側ではルーティングをしていないにもかかわらず、Flask側で処理しようとしてしまっているためです。
そこで以下をappserver.py
に追記して、/
以下のパスをvue.jsでビルドしたindex.html
にリダイレクトするようにします。
# appserver.py from backend.application import create_app from flask import render_template app = create_app() #### 以下を追記 ### @app.route('/', defaults={'path': ''}) @app.route('/<path:path>') def catch_all(path): return render_template("index.html") ################# if __name__ == '__main__': app.run()
再度、http://localhost:5000/about
にアクセスするとAboutページが表示されます。
404ページの追加
これで、ブラウザ上のルーティングがvue.jsでビルドしたindex.html
にリダイレクトされるので、vue.js側で404ページも追加しておかなくてはなりません。
frontend/src/components
配下に以下NotFound.vue
を作成します。
// NotFound.vue <template> <div> <p>404 - Not Found</p> </div> </template>
また、frontend/src/router/index.js
に以下の通り、ページがない場合のパスを追記します。
const routerOptions = [ { path: '/', component: 'Home' }, { path: '/about', component: 'About' }, { path: '*', component: 'NotFound' } ]
http://localhost:8080/
配下の適当なパスにアクセスしてNotFoundのページが表示されればOKです。
npm run build
でビルドすればFlask側のサーバhttp://localhost:5000/
でも同様にNotFoundのページが表示されます。
APIの追加
さて、基本的な環境は整ってきたので、バックエンド側にAPIエンドポイントを追加していきたいと思います。
バックエンド(Flask)→APIエンドポイントの追加
フロントエンド(Vue.js)→APIアクセスし結果を描画
まずはFlask側にAPIに対するルーティングを追加します。
index.html
に対するルーティングと同様に、そのままappserver.py
に記載しても良いのですが、APIのルーティングは別ファイルで管理したいので、FlaskのBluprintを使用します。
backend
配下にapi.py
というファイルを作成します。
from flask import Blueprint, jsonify, request from random import * api = Blueprint('api', __name__) @api.route('/hello/<string:name>/') def say_hello(name): response = { 'msg': "Hello {}".format(name) } return jsonify(response) @api.route('/random') def random_number(): response = { 'randomNumber': randint(1, 100) } return jsonify(response)
今回は試しに名前に対してHelloのメッセージを返すAPIと乱数を返すAPIを作成しました。
backend/application.py
にてこのAPIのルーティングを記載したBlueprintファイルをimportします。
# application.py from flask import Flask def create_app(app_name='FLASK-VUE'): app = Flask(app_name, static_folder = "./dist/static", template_folder = "./dist") app.config.from_object('backend.config.BaseConfig') ##### ここを追記 ##### from backend.api import api app.register_blueprint(api, url_prefix="/api") #################### return app
url_prefix="/api"
を指定しているので、ここで記載するルーティングはすべて/api/
配下のルーティングとなります。
http://localhost:5000/api/random
にアクセスすると以下の結果
{ "randomNumber": 38 }
http://localhost:5000/api/hello/hoge
にアクセスすると以下の結果
{ "msg": "Hello hoge" }
上記のような結果になればOKです。
次にfrontend
配下のVue.jsのプログラム側でこのAPIにリクエストをし、画面表示させたいと思います。
まずは、Vue.jsでajaxを利用するために、axiosをインストールします。
$ cd frontend $ npm install --save axios
frontend/src/components/Home.vue
を以下のように書き換えます。
// Home.vue <template> <div> <p>Home page</p> <p>Random number from backend: {{ randomNumber }}</p> <button @click="getRandom">New random number</button> </div> </template> <script> import axios from 'axios' export default { data () { return { randomNumber: 0 } }, methods: { getRandom () { const path = 'http://localhost:5000/api/random' axios.get(path) .then(response => { this.randomNumber = response.data.randomNumber }) .catch(error => { console.log(error) }) } }, created () { this.getRandom() } } </script>
バックエンドのFlask側では、デフォルトでは他のサーバからのリクエストを受け付ける設定になっていないため、フロントエンドのVue.jsからのAjaxのリクエストを受け入れるようにします。
まずは以下コマンドでCORSをインストールします。
$ pip install -U flask-cors
backend/application.py
を以下のように書き換えます。
# application.py from flask import Flask from flask_cors import CORS # ここを追記 def create_app(app_name='FLASK-VUE'): app = Flask(app_name, static_folder = "./dist/static", template_folder = "./dist") app.config.from_object('backend.config.BaseConfig') from backend.api import api app.register_blueprint(api, url_prefix="/api") # ここを追記 cors = CORS(app, resources={r"/api/*": {"origins": "*"}}) return app
これでhttp://localhost:8080/
アクセスしたときにAPIから取得した乱数を表示することができるようになったのですが、
もし静的なファイルをFlask経由で取得する必要が無いのであれば、COREの機能を使う必要はありません。
その場合、backend/appserver/py
を次のように書き換えればOKです。
# appserver.py from backend.application import create_app from flask import render_template import requests # ここを追記 app = create_app() @app.route('/', defaults={'path': ''}) @app.route('/<path:path>') def catch_all(path): #### 以下を追記 ### if app.debug: return requests.get('http://localhost:8080/{}'.format(path)).text return render_template("index.html") ################# if __name__ == '__main__': app.run()
必要に応じて以下コマンドを実行。
$ pip install requests
これで、開発モード(DEBUG=1
)の時は、http://localhost:5000
にアクセスした時はhttp://localhost:8080
にリダイレクトされるようになり、Vue.js側の画面でちゃんと乱数が取得できるようになります。
Flask側のサーバhttp://localhost:5000
とVue.js側のサーバhttp://localhost:8080
どちらにアクセスしても同様の結果が得られます。
終わりに
これで、バックエンドのAPIエンドポイントはFlaskで実装し、フロントエンドのUIはVue.jsで実装するフルスタックなアプリケーションが構築できました。
Flask側の開発用サーバとVue.jsの開発用サーバどちらも起動しておけば、それぞれ配下のプログラムを編集した際にホットリロードでリアルタイムに反映されるので、とても開発がしやすいです。
FlaskとVue.jsの詳細な使い方は今後もっと勉強していきたいと思います。
dropbox-sdk-jsを使ってフォルダ内アイテムの共有リンクを取得
はじめに
最近フロントエンドの勉強をしています。
今作っているWebアプリで、Dropboxに保存してある写真を表示するという機能を作りたかったのですが、サーバサイドでAPIを叩き個別に取得して表示させるととても画面レスポンスが遅くなってしまいました。
そこでJavascriptで非同期に取得し、順次画面表示させてユーザーの体感速度を早くしようとしたのですが、そこで少しハマってしまったので解決方法を備忘として残しておきます。
dropbox-sdk-jsの導入
まずはdropbox-sdk-jsを導入します。
今回はnpmではなくCDN版のものを使用します。
Getting started | Dropbox JavaScript SDK
以下をhtmlに記載します。
<script src="https://unpkg.com/dropbox/dist/Dropbox-sdk.min.js"></script> <script src="https://unpkg.com/dropbox/dist/DropboxTeam-sdk.min.js"></script>
フォルダ内アイテム一覧を取得
まずは共有リンクを取得したい画像アイテムのリストを取得します。
filesListFolder
メソッドにフォルダのパスを渡せば取得出来るみたいです。
var dropbox_api_token = "((dropbox_api_token))"; var dbx = new Dropbox.Dropbox({ accessToken: dropbox_api_token }); var dir_path = "path/to/items" dbx.filesListFolder({path: dir_path}) .then(function(response) { console.log(response.entries) }) .catch(function(error) { console.error(error); });
これはチュートリアルに載っているコードそのままで取得できます。
これでresponse.entries
にアイテム一覧が配列で取得できました。
forループでフォルダ内のアイテムそれぞれの共有リンクを取得
一覧が取得できたので、それぞれのアイテムの共有リンクを取得しようと思います。
本来なら、共有リンクを作成dbx.sharingCreateSharedLinkWithSettings
の後に共有リンクを取得dbx.sharingListSharedLinks
する流れですが、自分の環境だとサーバサイドで共有リンクの作成は完了しているので、リンク取得のみを記述します。
こんな感じです。
var dropbox_api_token = "((dropbox_api_token))"; var dbx = new Dropbox.Dropbox({ accessToken: dropbox_api_token }); var entries; var urls = [] //共有リンクの配列 var dir_path = "path/to/items" dbx.filesListFolder({path: dir_path}) .then(function(response) { console.log(response.entries) entries = response.entries; for(var i = 0;i<entries.length;i++) { dbx.sharingListSharedLinks({path: entries[i].path_display}) .then(function(response) { urls.push(response.links[0].url); }) } }) .catch(function(error) { console.error(error); }); /////////////////////////////////////// //以下に共有リンクの配列(urls)を取得した後の処理を記載 ///////////////////////////////////////
JavaScript初心者だった自分は、なんとなくこれで動作しそうだなーと思って、こんなコードを書いたのですが、これが全然うまくいきませんでした。
共有リンクが取得できている前提で後の処理を書いても「urlsの中身が空だよー」とエラーになってしまいます。
再帰的に関数を呼び出す
非同期処理が入っているため順番がめちゃくちゃになってしまうようなので、きっちりと順番順番に処理をしてもらうために、以下のように関数を再帰的に呼び出して処理することにしました。
var dropbox_api_token = "((dropbox_api_token))"; var dbx = new Dropbox.Dropbox({ accessToken: dropbox_api_token }); var urls = [] //共有リンクの配列 var dir_path = "path/to/items" dbx.filesListFolder({path: path}) .then(function(response) { // 関数の呼び出し getSharingLinks(0, urls, response.entries); $("#loading-img").fadeOut('slow'); }) .catch(function(error) { console.error(error); }); // 関数を定義 function getSharingLinks(i, urls, entries) { dbx.sharingListSharedLinks({path: entries[i].path_display}) .then(function(response) { var url = response.links[0].url.replace("www.dropbox.com","dl.dropboxusercontent.com").replace("?dl=0",""); urls.push(url); i++; if ( i == entries.length) { return console.log(url); } else { // 再帰的に関数を呼び出し getSharingLinks(i, urls, entries); } }) .catch(function(error) { console.error(error); }); }
まとめ
Javascriptで非同期の処理を扱う際は、Promiseやasyncなどを使うのが基本みたいですが、ちょっと調べただけではよく理解できなかったので、今回はきれいではないですがとりあえずこんな感じで解決しました。 いつか必要になると思うのでasyncについてもいつか勉強しようと思います。
heroku container:push でエラー
先日からherokuCLIをアップデートしたあたりからherokuにデプロイしようとする度に以下のエラーが出るようになってしまいました。
(node:26614) Error Plugin: heroku-container-tools: files attribute must be specified in /Users/username/.local/share/heroku/node_modules/heroku-container-tools/package.json module: @oclif/config@1.6.27 plugin: heroku-container-tools root: /Users/username/.local/share/heroku/node_modules/heroku-container-tools See more details with DEBUG=* (node:26614) Error Plugin: heroku-container-tools: files attribute must be specified in /Users/username/.local/share/heroku/node_modules/heroku-container-tools/package.json module: @oclif/plugin-legacy@1.0.15 plugin: heroku-container-tools root: /Users/username/.local/share/heroku/node_modules/heroku-container-tools See more details with DEBUG=* Uninstalling heroku-container-tools... done
エラーメッセージを読んでもよくわからなかったのでいろいろ検索したところ以下の記事がヒットしました。
Latest Docker update broken Heroku cli?
症状は違いますがheroku-container-tools
が悪さをしているようだったので、以下コマンドでアンインストールし、heroku-container-registry
をインストールし直したところ、エラーは出なくなりました。
$ heroku plugins:uninstall heroku-container-tools $ heroku plugins:install heroku-container-registry
Node.jsの環境をDockerで構築&herokuにデプロイ
はじめに
それぞれ他サイトを参照して作ったのみですが、自身の備忘までに。
会社でSkyway使うかも?という話が出てきたため、検証環境を作るのが目的です。 * 他にもいろいろパッケージ入れて検証するかもーついでにnodejsとかの勉強もしなきゃ * 最近Docker触ってないしDockerで環境つくりたい * というかherokuにDockerのコンテナをデプロイできるようになったみたいだし使ってみたい ということで、node.jsの環境をDocker上に構築し、それをHerokuにデプロイするまでを試してみたいと思います。
Skywayとは?
NTT Comunicationsが開発している、WebRTCのためのプラットフォームです。
公式サイト
Enterprise Cloud Skyway
SkyWayとは
ビデオ会議やコンタクトセンター、遠隔作業支援、オンライン教育、ライブ配信など、さまざまな機会において、オンラインでのリアルタイムコミュニケーションのニーズが高まっています。 ビデオ・音声通話、データ通信といったリアルタイムコミュニケーションの標準技術である「WebRTC」が登場し、リアルタイムコミュニケーションがより実現しやすくなってきました。 SkyWayを利用すれば、WebRTCに必要なサーバを構築・運用することなく、手軽にビデオ・音声通話、データ通信を利用できます。 自社サービスの開発・提供に専念して、イノベーションに集中することができます。
要は、Skywayを使えばWebRTCのアプリが簡単に開発できるというものです。
WebRTCはクライアント間で直接通信する方法ですが、そのためにはシグナリングサーバーと呼ばれる、クライアント間の接続する仲介をするサーバーを準備する必要があるのですが(これがまた複雑、らしい)、Skywayではそういったシグナリング等の、WebRTCに必要なサーバサイドの機能をPFとして公開してくれているため、我々開発者はクライアント側の実装に集中できるのです。
自分もまだそんなに詳しくないのでこの辺で。。
Node.jsのDocker環境
まずはDocker上の環境を作っていきます。
とはいえこれまでほとんどNode.jsなんて触ったことがなく全く知識が無いため、 とりあえず先人の行いに習って作ってみます。
以下を参考にしました。
【初心者向け】Dockerで手軽にNode.js開発環境構築 (2)
参考というかほとんど完コピだけど。。
以下のDockerfile
とdocker-compose.yml
を作成
FROM node:8.9.4-alpine ENV NODE_ENV=development RUN npm install -g express-generator@4.15.0 WORKDIR /app EXPOSE 3000
version: '3' services: webserver: build: ./ image: node-express-dev:1.0 container_name: node tty: true volumes: - ./:/app ports: - "8080:3000"
ファイルの配置は記事とは変えてすべて同じディレクトリにしています。
MyApp ├─ Dockerfile └─ docker-compose.yml
そしてビルドと起動
$ docker-compose build $ docker-compose up -d
コンテナに接続し、express-generator
でアプリの雛形を作成します。
参照先サイトでは、オプションにpug
を指定しています。
pug
はテンプレートエンジンですが、構文が馴染めなそうだったので、今回はhtmlライクに書けそうなejs
を指定しました。
$ docker exec -it node /bin/sh # express -f --view=ejs /app # npm install
雛形が生成され、こんな構成になりました。
MyApp ├── Dockerfile ├── app.js ├── bin │ └── www ├── docker-compose.yml ├── node_modules ├── package-lock.json ├── package.json ├── public │ ├── images │ ├── javascripts │ └── stylesheets ├── routes │ ├── index.js │ └── users.js └── views ├── error.ejs └── index.ejs
以下コマンドで開発用サーバを起動します。
# npm start
http://localhost:8080
をブラウザで開いてExpressのスタートページが表示されれば成功です。
Dockerコンテナ上ではポート3000番で立ち上がっていますが、ブラウザの8080番ポートが3000番に転送されています。
Skywayのチュートリアル
さて、とりあえずnode.jsとWebサーバが立ち上がる環境はDocker上に構築出来たので、次はいよいよskywayのチュートリアルアプリを立ち上げていきます。
Javascript SDKの概要は以下公式ページに記載があります。
Skyway JavaScript SDK チュートリアル
とりあえず、今回はまず動くものを動作させたいので、チュートリアルのコードをそのまま使います。
Github上に公開されているサンプルコードを使用します。
今回はP2Pの1対多通信のものを使用しました。
github : skyway/skyway-js-sdk/examples/p2p-broadcast
先程構築したnode環境ではview/index.ejs
がホームにて表示されるページのテンプレートになっています。
↑のgithubに上がっていたindex.html
の内容をそのままvies/index.ejs
に貼り付けます。
script.js
、style.css
はそれぞれ、public/javascripts/script.js
、pubic/stylesheets/style.css
に配置します。
また、APIキーをSkywayのダッシュボードから取得し、public/javascripts/key.js
に記載します。
javascript
window.__SKYWAY_KEY__ = '<YOUR_KEY_HERE>'
index.ejsの読み込み先のパスも変更
<head> <meta charset="utf-8"> <title>SkyWay - Broadcast example</title> <link rel="stylesheet" href="/stylesheets/style.css"> <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script> <script type="text/javascript" src="//cdn.webrtc.ecl.ntt.com/skyway-latest.js"></script> <script type="text/javascript" src="/javascripts/key.js"></script> <script type="text/javascript" src="/javascripts/script.js"></script> </head>
ここまで出来れば、http://localhost:8080
にてSkywayのサンプルが動作するはずです。
browserifyの導入
一旦ここまでで、Skywayの動作確認は出来ますが、jQueryやSkywayのJavascript SDKをCDNから読み込んでいます。
ここままでも問題ないのですが、後々いろんなパッケージを導入することを考えると、(今回もWebRTCでの配信→音声認識とかを試したいなーと思ってるので)
パッケージのバージョン管理が大変になったり、何よりも今回はnode.jsの環境構築の勉強も兼ねていたので、
* npmでパッケージインストール
* browserifyでコンパイル
という流れでパッケージ管理ができるよう変更したいと思います。
まずはnpmでjQueryとskyway-jsをインストールします。
# npm install jquery --save # npm install -D browserify
パッケージをインストールしたら、script.js
内でそれらのライブラリをrequire
する分を記載します。
'use strict'; // Imports jQuery const $ = require('jQuery'); $(function() { // Imports the skyway library const Peer = require('skyway-js');
読み込みも簡単ですね。
ただしこれだけだとrequire
をブラウザが解釈できないので、動作しません。
browserify
でコンパイルする必要があります。
一旦上記script.js
はscript/
ディレクトリを作成し、その配下で管理、開発、コンパイルしたらpublic/javascripts/script.js
が生成されるようにします。
コマンドは以下。
# $(npm bin)/browserify script/script.js -o public/javascripts/script.js
これでpublic/javascripts/script.js
がコンパイルされた形で生成されました。
再びnpm start
コマンドでサーバを立ち上げ、http://localhost:8080
をブラウザで開いてExpressのスタートページが表示されれば成功です。
Herokuへのデプロイ
ひとまず、Docker上に環境を作ることができたので、いよいよHerokuの環境にデプロイをしていきます。
以下のサイトを参考にしました。 * Container Registry & Runtime (Docker Deploys) * Heroku で Docker を使う場合の諸注意
herokuにDockerコンテナをデプロイするためには以下の注意点があるみたいです。
- PORT 環境変数で Listen する Webアプリケーションであること
- Network link は未サポート
- Default working directory = '/'。変更するときは WORKDIR を指定する。
- CMDが必須。ENTRYPOINTはオプション。
- VOLUME、EXPOSE、STOPSIGNAL、SHELL、HEALTHCHECKは未サポート
今回のサンプルはPORT
が3000で動作し、起動コマンドはnpm start
なので、以下をDockerfileに追記します。
ENV PORT 3000 CMD ["npm", "start"]
あとは以下コマンドでHerokuにデプロイします。
$ heroku login # herokuにログイン $ heroku container:login # Heroku 上の Container Registry へログイン $ heroku create # Heorkuアプリの作成 $ heroku container:push web # コンテナをにContainer RegistryにPush # 2018/5/15の変更にて以下のコマンドが必要になった $ heroku container:release web # heroku アプリをデプロイ
heroku open
でherokuアプリがブラウザで開けば成功です。
※5/15時点で変更があり、heroku container:push web
だけではherokuアプリにはデプロイされなくなったようです。実際にアプリにデプロイするにはheroku container:release web
を実行する必要があります。
Pushing images to Container Registry, either via the heroku container:push CLI command or using docker push, no longer releases those images to your application. To create a new release using the images pushed to Container Registry, run heroku container:release (specifying the process types you would like to release). Separating push and release allows you to:
- Push several images and then release them all at the same time.
- Use the release phase feature (run tasks before a new release of your app is deployed).
- Push one image and release it to multiple process types with different CMDs, via heroku.yml or API.
Pushing images to Container Registry no longer creates a release
まとめ
Herokuはこれまでもとても便利だったので大変重宝しておりましたが、自分自身余りgitを使わないし、グローバル環境で動作試験をしたいときもわざわざgit commit
してログに残るのが嫌だったので、Dockerコンテナを気軽にデプロイ出来るのはとても便利だなーと思いました。
参考サイト
プライバシーポリシー
こんにちは管理人のti_takaです。
下記、「プライバシーポリシー」に関して記載致しましたので、ご一読願います。
当サイトに掲載されている広告について
当サイトでは、第三者配信の広告サービス(Googleアドセンス、もしもアフィリエイト)を利用しています。 このような広告配信事業者は、ユーザーの興味に応じた商品やサービスの広告を表示するため、当サイトや他サイトへのアクセスに関する情報 『Cookie』(氏名、住所、メール アドレス、電話番号は含まれません) を使用することがあります。 またGoogleアドセンスに関して、このプロセスの詳細やこのような情報が広告配信事業者に使用されないようにする方法については、こちらをクリックしてください。
当サイトが使用しているアクセス解析ツールについて
当サイトでは、Googleによるアクセス解析ツール「Googleアナリティクス」を利用しています。
このGoogleアナリティクスはトラフィックデータの収集のためにCookieを使用しています。
このトラフィックデータは匿名で収集されており、個人を特定するものではありません。
この機能はCookieを無効にすることで収集を拒否することが出来ますので、お使いのブラウザの設定をご確認ください。
この規約に関して、詳しくはこちら、またはこちらをクリックしてください。
当サイトへのコメントについて
当サイトでは、スパム・荒らしへの対応として、コメントの際に使用されたIPアドレスを記録しています。
これはブログの標準機能としてサポートされている機能で、スパム・荒らしへの対応以外にこのIPアドレスを使用することはありません。
また、メールアドレスとURLの入力に関しては、任意となっております。
全てのコメントは管理人であるti_takaが事前にその内容を確認し、承認した上での掲載となりますことをあらかじめご了承下さい。
加えて、次の各号に掲げる内容を含むコメントは管理人の裁量によって承認せず、削除する事があります。
- 特定の自然人または法人を誹謗し、中傷するもの。
- 極度にわいせつな内容を含むもの。
- 禁制品の取引に関するものや、他者を害する行為の依頼など、法律によって禁止されている物品、行為の依頼や斡旋などに関するもの。
- その他、公序良俗に反し、または管理人によって承認すべきでないと認められるもの。
免責事項
当サイトで掲載している画像の著作権・肖像権等は各権利所有者に帰属致します。
権利を侵害する目的ではございません。
記事の内容や掲載画像等に問題がございましたら、各権利所有者様本人が直接メールでご連絡下さい。
確認後、対応させて頂きます。
当サイトからリンクやバナーなどによって他のサイトに移動された場合、移動先サイトで提供される情報、サービス等について一切の責任を負いません。
当サイトのコンテンツ・情報につきまして、可能な限り正確な情報を掲載するよう努めておりますが、誤情報が入り込んだり、情報が古くなっていることもございます。
当サイトに掲載された内容によって生じた損害等の一切の責任を負いかねますのでご了承ください。
プライバシーポリシーの変更について
当サイトは、個人情報に関して適用される日本の法令を遵守するとともに、本ポリシーの内容を適宜見直しその改善に努めます。
修正された最新のプライバシーポリシーは常に本ページにて開示されます。
運営者:ti_taka
[Python]Apache + Gunicorn + hug で簡単APIサーバ
先日、Apacheとmod_wsgiでPythonを動かし、APIサーバを作成してみました。
しかし、hugに備わっているBasic認証を追加することは出来ませんでした。
その後Gunicornを試してみたら、驚くほど簡単に実現できてしまいました。。
最初からこっちにすればよかった。。
前提
Pythonの環境とアプリケーションは前回のものを使います。
import sys,json,os import hug import util from dotenv import load_dotenv dotenv_path = join(dirname(__file__), '.env') load_dotenv(dotenv_path) HUG_USER_NAME = os.environ.get("HUG_USER_NAME") HUG_PASSWORD = os.environ.get("HUG_PASSWORD") authentication = hug.authentication.basic(hug.authentication.verify(HUG_USER_NAME, HUG_PASSWORD)) @hug.get("/hoge",,requires=authentication) def hoge(text): hoge_list = util.getHogeList(text) return json.dumps({"hoge":hoge_list})
前回記述したsys.path.append('/var/www/app')
はなくても今回は正常に動作しました。
また、動作させるアプリケーションは、gunicornを起動する際にパラメータとして渡すので、application = __hug_wsgi__
も不要です。
Gunicornのインストール
pipでインストールします。
$ pip3.6 install gunicorn
これで完了です。
$ cd /var/www/app/ # gunicornの起動 $ gunicorn app:__hug_wsgi__ [2017-09-12 22:54:04 +0000] [3994] [INFO] Starting gunicorn 19.7.1 [2017-09-12 22:54:04 +0000] [3994] [INFO] Listening at: http://127.0.0.1:8000 (3994) [2017-09-12 22:54:04 +0000] [3994] [INFO] Using worker: sync [2017-09-12 22:54:04 +0000] [3997] [INFO] Booting worker with pid: 3997
アプリがポート8000で起動します。
$ curl http://localhost:8000/hoge?text=hoge
結果が帰ってくればOKです。(Basic認証をかけているので認証エラーとなりますが。)
Apacheの設定
次にApacheの設定をします。
/etc/httpd/confd/httpd.conf
に以下の1行を記述し、リクエストをすべてgunicornに流すよう設定します。
ProxyPass / http://localhost:8000/
gunicornを起動した状態で、ブラウザからhttp://192.168.33.10/hoge?text=hoge
にアクセスして認証画面がでて認証できればOK!なのですが、なにやらまた、「Internal Server Error」が。。。
前回とまた同じです。
SELinuxの無効化
Apacheのエラーログを見ると、
[Tue Sep 12 12:03:36.233033 2017] [proxy:error] [pid 5746] (13)Permission denied: AH00957: HTTP: attempt to connect to 127.0.0.1:8000 (localhost) failed [Tue Sep 12 12:03:36.233187 2017] [proxy:error] [pid 5746] AH00959: ap_proxy_connect_backend disabling worker for (localhost) for 60s [Tue Sep 12 12:03:36.233202 2017] [proxy_http:error] [pid 5746] [client 192.168.33.1:62957] AH01114: HTTP: failed to make connection to backend: localhost
proxyがうまくいっていないみたいです。
エラーログを調べてみると色々情報がありました。
SELinuxが原因で、どうやらよくあることらしいです。
こちらを参考にしました。
/var/log/audit/audit.log
を見てみると、たしかに以下のエラーが。
type=AVC msg=audit(1505218092.201:865): avc: denied { name_connect } for pid=5745 comm="httpd" dest=8000 scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:soundd_port_t:s0 tclass=tcp_socket
/etc/sysconfig/selinux
を修正して、SELINUX=disabled
とします。
OSを再起動して、再度gunicornを立ち上げると、無事動作しました。
Basic認証も問題なく、きちんと認証できます。
gunicornの自動起動設定
OSを立ち上げたときにApacheは自動起動するよう設定しましたが、gunicornも自動起動するように設定しないとアプリをWebサーバで動作させられません。
ここに記載されているとおりに実行すればOKです。
$ cd /etc/systemd/system/ $ vim app.service
今回gunicornのパスは/bin/gunicorn
で、アプリケーションのディレクトリは/var/www/app
なのでapp.service
の中身は以下のようになりました。
[Unit] Description=gunicorn daemon After=network.target [Service] User=apache Group=apache WorkingDirectory=/var/www/app ExecStart=/bin/gunicorn schedule:__hug_wsgi__ [Install] WantedBy=multi-user.target
自動起動するように設定します。
$ systemctl enable app $ systemctl start app
OSを再起動し、ブラウザからhttp://192.168.33.10/hoge?text=hoge
にアクセスして結果が出ればOKです。
まとめ
前回mod_wsgiでアプリケーションを動作させたよりももっと簡単に実現できました。
また、前回躓いたBasic認証も正常に動作させることが出来ました。
今回はgunicornを運用していこうと思います。