Lesson 12

複数の環境に適用する

Lesson 12 Chapter 1
複数環境に適用するメリット

ここまでNode.js(Express)を用いたWebアプリケーション開発を学んできました。設計や実装、デバッグなど幅広い観点で知識を身につけられたことでしょう。

最後の章となるLesson12では、アプリケーションの環境展開について学びます。

「環境」とは何か

コンピューターの世界では、しばしば「環境」という言葉が使われます。たとえば、アプリケーション実行環境、開発環境、本番環境といったような具合です。

よくよく考えてみると、この「環境」という言葉には二つの意味があることに気が付きます。

まず、環境には場所を表す「場」という意味があります。先ほどの「アプリケーション実行環境」であれば、「アプリケーションを実行する場所」という意味です。

同時に、環境には何かを動かすために必要な「条件」という意味もあります。例として「Node.jsを使ったWebアプリケーション実行環境」という言葉を考えてみましょう。これは言い換えると「Node.jsを使ったWebアプリケーションを動かすために必要な条件」という意味になります。

Node.jsを使ったWebアプリケーションを動作させるためには、もちろんNode.jsをインストールする必要があります。データベースと連携している場合、MySQLなどのデータベース管理システムをインストールする必要があります。そもそもOSはLinuxにすべきかもしれません。

というように、必要となるソフトウェア、ソフトウェアの設定、機器などをまとめたもの――それこそが「Node.jsを使ったWebアプリケーション実行環境」です。

2つ目の「環境」が示す意味は、もちろん文脈によって異なります。たとえば「ネットワーク環境」であれば、通信をするために必要な機器やソフトウェア、各種設定が含まれるでしょう。

Webアプリケーションの稼働環境

Webアプリケーション開発における稼働環境としては、少なくとも次の2つが考えられます。

環境 利用される状況
本番環境 完成したアプリケーションを稼働させ、Web上に公開する
開発環境 Webアプリケーションの開発を進める

開発環境とは、普段あなたがコードを記述するローカル環境と考えてください。

一方の本番環境は、完成したアプリケーションを稼働させる環境です。実際にアプリケーションをWeb上で公開するときに使う環境と考えてください。

一昔前なら、本番環境に使われるのは自社サーバーが当たり前でした。しかし、近年はAmazon Web Service(AWS)、Microsoft Azure、Google Cloud Platform(GCP)といったクラウドサービスが主流です。

完成したアプリケーションをこれらのクラウドサービス上に配置し、いくつかの必要な設定を行えば、すぐにデプロイすることができます。

次のChapterからは、アプリケーションを稼働環境に適用する上で必要な知識を学んでいきます。

Lesson 12 Chapter 2
環境変数とは

ここからのChapterでは、アプリケーションを本番環境で動かす上で大切な「環境変数」と「デプロイ」について解説します。

「環境変数」は、システムの設定が保存されている変数のことです。やや複雑な概念ですが、アプリケーションを本番環境で稼働させる上で必要になってきます。今回はポート番号を例に説明します。

「デプロイ」は、配置する・配備するという意味の言葉で、アプリケーションが本番環境で動作するよう設定や操作をすることを指します。本章では「Heroku」というクラウドサービスにアプリケーションをデプロイする例を説明します。

まずは、「環境変数」から見ていきましょう。

環境変数とは

環境変数は、OSやアプリケーションが動作する際の基本設定が保存されてる変数です。具体的には、次の画像のようなものが環境変数にあたります。

env.png 環境変数の例

Windows環境であれば、コマンドプロンプトからsetコマンドを実行してみましょう。環境変数の一覧が表示されます。Mac / Linux環境であれば、printenvコマンドを実行すると確認できます。

環境変数の使用例

環境変数の利用シーンについて、Expressで作成した次のアプリケーションの「ポート番号」を例に説明していきます。

app.js
const express = require("express");
const app = express();
const port = 5000;

app.get("/", (req, res) => {
  res.send("Hello World!");
});

app.listen(port, () => {
  console.log(`Example app listening on port ${port}`);
});

さて、これまでの開発環境ではポート番号を5000番としていました。しかし、本番環境では31256番を割り当てる必要があるとします。どのように記述を変更すべきでしょうか?

一番最初に思いつくポート番号の切り替え方法は、次のようにif文を記述するものです。

let port;

if (isDevelopment()) {

  // 開発環境のポート番号
  port = 5000;

} else if (isProduction()) {

  // 本番環境のポート番号
  port = 31256;

}

たしかに、単一の本番環境でアプリケーションを稼働させる場合は、上の記述で要件を満たせます。しかし、複数の本番環境で稼働させる場合はどうでしょうか。

たとえば、3つの本番環境でアプリケーションを稼働させるとき、ポート番号について次の要件を満たす必要があるとします。

  • 本番環境Aは11256番
  • 本番環境Bは21256番
  • 本番環境Cは31256番

if文で記述すると、3つの環境ごとにポート番号の記述が違うソースコードが作成されます。これでは本番環境A用のアプリケーション、本番環境B用のアプリケーション…という具合に環境ごとのバージョンが生まれてしまうでしょう。

このように、if文を記述すると本番環境の数だけソースコードの変更が増え、拡張性の低いアプリケーションになります。

process.envによる解決

ポート番号のように、アプリケーションの稼働環境によって変わる値は、「環境変数」を用いて外部から取得できます。環境変数を用いると、ポート番号の記述は次のようになります。

const port = process.env.PORT;

条件分岐がなくなり、1行ですっきりした記述になりました。process.envは、環境変数を参照する方法で、Node.jsにより提供されています。

このように、アプリケーションを稼働させる環境によって変わる値は、process.env経由で参照しましょう。そうすれば、複数の本番環境でアプリケーションを稼働させる際もコードの修正が不要になり、拡張性を保持できます。

今回は、ポート番号を例に挙げて環境変数を解説しました。開発するアプリケーションに応じて扱う環境変数は変わります。ポート番号に限らず、積極的な活用を心がけてください。

環境変数の具体例

環境変数の例として、データーベース接続情報、外部サービスのAPIキー(クレジットカード決済サービスなどを思い浮かべてください)などが挙げられます。

Lesson 12 Chapter 3
dotenvモジュール

Chapter2では環境変数の基本的な役割を解説しました。

その中の例で見たように、実際のアプリケーション開発では複数の環境を使い分けます。しかし、それぞれに環境変数を設定し直すのは面倒です。そこで通常はプロジェクト内に環境変数を記述したファイルを配置し、process.env経由で値を参照します。

上記の仕組みを実現するNode.jsモジュールとして「dotenv」があります。今回のChapterでは、このdotenvモジュールの使い方を学びましょう。

dotenvモジュールとは

dotenvモジュールを使うと、「.env」ファイルに記述した変数名と値を環境変数として扱えるようになります。

dotenvモジュールの具体的な使い方は以下の通りです。

  1. 「dotenvモジュール」をインストールする
  2. 「.envファイル」を作成し、環境変数の設定を記述する

順を追って見ていきましょう。

dotenvモジュールのインストール

まずは、コマンドラインからnpm install --save-dev dotenvを実行し、dotenvモジュールをインストールします。

--save-devオプション

--save-devオプションを付けることで、パッケージを開発環境用にインストールできます。「package.json」を開くと、dependenciesではなくdevDependencies以下にパッケージ名が追加されているはずです。このように開発/本番環境用のパッケージを区別することで、ビルド時の負担が少しでも軽減できます。

envファイルの作成と処理の記述

プロジェクト直下に「.env」ファイルを作成し、以下のように記述しましょう。

.env
MY_DOMAIN="my-app-dev.jp"

この「.env」ファイルの値を参照するためには、ソースコード内で次のように記述します。

dot-env.js
// require
require("dotenv").config();

const domin = process.env.MY_DOMAIN;

// 出力
console.log("domain: " + domain);

準備が整ったら、コマンドラインからnode dot-env.jsを実行しましょう。domain: my-app-dev.jpという出力結果が得られるはずです。

「.env」ファイルに環境変数を記述したことで、process.envによる値の取得が可能になりました。もちろん、従来通りの方法で環境変数を設定することも可能です。

以上、開発で環境変数を設定する際に便利なdotenvモジュールの使い方でした。

Lesson 12 Chapter 4
envファイルの活用

このChapterでは、先ほどの「app.js」とdotenvモジュール、さらに「.env」ファイルを組み合わせた方法を紹介します。

ポート番号を例に説明します。

まずは、次のように記述した「.env」ファイルを用意してください。

.env
PORT=5000

続いて「app.js」の記述を次のように変更してください。

app.js
require("dotenv").config();

const express = require("express");
const app = express();
const port = process.env.PORT;

app.get("/", (req, res) => {
  res.send("Hello World!");
});

app.listen(port, () => {
  console.log(`Example app listening on port ${port}`);
});

準備が整ったら、コマンドラインからnode app.jsと実行します。Webブラウザから「http://localhost:5000」にアクセスし、Hello World!と出力されることを確認してください。

dotenvのデメリット

こうして見ると、dotenvモジュールの使用はメリットばかりである気がします。しかし、実際に運用してみると不都合が生じることも少なくありません。

アプリケーションを本番環境にデプロイする場合を考えてください。デプロイ先が5つであれば、管理する「.env」ファイルの数も5つになってしまいます。

このような背景から、dotenvモジュールと「.env」ファイルの組み合わせによる環境変数の取得は開発環境に限定し、本番環境では設定した環境変数に依存してアプリケーションを動かす、という方針がとられることもあります。

一方で、開発環境と本番環境でURLを変更する、というようにアプリケーションのデプロイ先が増えても管理する「.env」ファイルの数が増えない場合は、dotenvモジュールと「.env」ファイルを組み合わせて使う、という判断も十分に考えられます

Lesson 12 Chapter 5
package.jsonの修正

Chapter5では、本番環境に配置する「package.json」について学びます。

とはいえ、開発環境と本番環境で大きく変わる点はありません。実際にデプロイする上でどのような対応が必要なのか、詳しく見ていきましょう。

scripts: start

公開できる状態になったWebアプリケーションは、以下の通り「package.json」にstartスクリプトを記述することが一般的です。

package.json
{
  "name": "nodejs-sample",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "NODE_ENV=production node app.js" // この1行を追記
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "express": "^4.18.1"
  }
}

「package.json」のscriptsフィールドには、プロジェクトで必要なタスクをスクリプトとして記述します。今回は、本番環境の実行時に使用するstartスクリプトを記述しました。

このように、本番環境用のスクリプト名はstartとすることが一般的です。

scriptsの予約語

というのも、npmには予約語として「start」が登録されているためです。通常、設定したスクリプトはnpm run [スクリプト名]と打つことで実行できますが、startは例外的にnpm startと省略できます(その他の予約語にrestart, stop, testがあります)。

スクリプトの内容も詳しく見ていきましょう。

NODE_ENV

NODE_ENVは環境変数です。アプリケーションの実行環境を指定する際に用いられるもので、多く0のパッケージがNODE_ENVの値を参照します。

今回はNODE_ENV=productionと記述し、実行環境であることを示しました。基本的に、NODE_ENVproduction、もしくはdevelopmentいずれかの値をとります。

NODE_ENVの指定により、そのアプリケーション内で使われるモジュールの動作が変わります。今回のアプリケーションであれば、Expressの挙動に影響を与えます。

処理の違いを確認する

一例として、エラーが発生したときの動作を説明します。「app.js」を次のように編集し、NODE_ENVの違いによる振る舞いの変化を見てみましょう。

app.js
const express = require("express");
const app = express();
const port = 5000;

app.get("/", (req, res) => {

  // エラーを発生させる
  throw new Error("エラーです");
});

app.listen(port, () => {
  console.log(`Example app listening on port ${port}`);
});

まず、コマンドラインからnode app.jsを実行します。Webブラウザから「 http://localhost:5000」にアクセスしてみましょう。次の画像のようにエラーの発生箇所が出力されます。

express-error-message.png 表示されるエラーの例

次は、NODE_ENV=production node app.jsを実行し、同じくWebブラウザから「http://localhost:5000」にアクセスしてください。今度はInternal Server Errorとのみ表示されるはずです。

NODE_ENV=productionを指定すると、Expressは開発用のスタックトレースを出力しません。このようにNODE_ENVの値を設定することで、アプリケーション内で使われる各モジュールの動作が本番用に最適化されます。

本番環境でのロギング

上記の例から分かる通り、本番環境で開発環境と同じスタックトレースを出力するのはアンチパターンとされます。これは開発者しか知るべきでないソースコードや実行環境の情報が外部に漏れ、深刻なセキュリティリスクにつながるためです。

次のChapterでは、本教材における最後の実践として本番環境へのデプロイを行います。

Lesson 12 Chapter 6
本番環境へのデプロイ

ここまで環境展開のノウハウを学んできました。

このChapter6では、本教材の総まとめとして、実際に本番環境へのデプロイを行います。

Herokuとは

今回は「Heroku」へのデプロイを例に説明します。

Herokuとは、Salesforceが提供するクラウドサービスの1つで、PasS(Platform as a Service)に分類されています。

PaaS(Platform as a Service)とは

アプリケーションが動作するために必要な基本環境を提供するクラウドサービス。PasSを利用することで、サーバー機器やOSなどの環境を自前で用意せずにアプリケーションを稼働できる。

Herokuの一部サービスは無料で提供されています。今回は無料で利用できるサービスを使い、本番環境へのデプロイを説明していきます。

Herokuへのデプロイの説明を通して、本番環境へのデプロイに対する具体的なイメージをつかんでいきましょう。

なお、Herokuへのデプロイには「git」を使います。コマンドラインからgitを使える状態にしておいてください。

アプリケーションの構成

今回デプロイするアプリケーションは、以下のような構成を取ります。

app.png 今回デプロイするアプリケーションの構成

今回の実践では、gitのローカルリポジトリにあるmasterブランチをHerokuのリモートリポジトリにプッシュすることで、アプリケーションのデプロイとします。そのため、上の3つのファイルがローカルのmasterブランチにコミットされた状態にしてください。

以下の項目では、3つのファイルの内容と役割を説明します。

Procfile

1つ目は「Procfile」という名前のファイルです。次の1行のみを記述してください。

Procfile
web: npm start

この「Procfile」はHerokuにアプリケーションをデプロイするにあたって必要なファイルです。アプリケーションを起動するためのコマンドを記述します。

package.json

2つ目は、お馴染みの「package.json」です。今回は次の通り記述してください。

package.json
{
  "name": "sample-heroku",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "NODE_ENV=production node app.js"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "express": "^4.18.1"
  },
  "engines": {
    "node": "14.16.x"
  }
}

今回の「package.json」のポイントは3つです。

  • scripts: start
  • このアプリケーションを起動するためのstartスクリプトを追加しています。

  • dependencies: express
  • アプリケーションで使用するexpressのパッケージ名を記述しています。

  • engines: node
  • アプリケーションを実行するNode.jsのバージョンを指定しています。

app.js

3つ目は「app.js」です。今回は以下の内容で記述します。

app.js
const express = require("express");
const app = express();
const port = process.env.PORT || 5000;

app.get("/", (req, res) => {
  res.send("Hello World!");
});

app.listen(port, () => {
  console.log(`Example app listening on port ${port}`);
  console.log("process.env.PORT: " + process.env.PORT);
});

今回の「app.js」におけるポイントは、const port = process.env.PORT || 5000の記述です。ここではデプロイしたアプリケーションをHeroku上で動かすため、環境変数から指定したポート番号を渡しています。

Herokuの要件

ポート番号に関する詳しい要件は、Herokuの公式Webページに説明があります。もしHeroku以外のサービスを使う場合は、使うサービスの公式ページを参照し、アプリケーションに必要な変更を加えてください。

以上3つのファイルについて、ローカルのmasterブランチにコミットされた状態にしてください。

Herokuアカウントの作成

Herokuへデプロイするためにはアカウントが必要です。以下の手順に従って作成しましょう。

以下の参考画像は2022年8月時点のものであり、配置・デザイン・手順が変わっている可能性があります。実際の画面と異なる場合、適宜読み替えて進めるようにしてください。

まずは、Herokuの公式webページにアクセスします。アカウント新規作成ページから必要項目を入力してださい。

heroku-web-1.png 新規アカウント作成画面

無事アカウントが作成されると、次の画像のような画面が表示されます。

heroku-web-2.png 新規アカウント作成完了画面

以上でHerokuのサービスが利用できるようになりました。

Heroku CLIのインストール

次は「Heroku CLI」をインストールしましょう。インストール方法は公式ページに案内があります。リンク先を参考にインストールを進めてください。

以上で事前準備は完了です。あらためて、次の手順が正く終わっていることを確認しましょう。

  • ローカルのmasterブランチに上で説明した3つのファイルをコミット済み
  • Herokuのアカウントを作成済み
  • Heroku CLIをインストール済み

Herokuにデプロイする

それでは、作成したアプリケーションをHerokuにデプロイしましょう。

デプロイの手順は次の3つです。先ほどインストールしたHeroku CLIとgitコマンドを使ってデプロイを行います。

  1. heroku loginを実行
  2. heroku createを実行
  3. git push heroku masterを実行

heroku loginの実行

まずは、コマンドラインからheroku loginを実行してください。実行後、次のようなメッセージが表示されます。

heroku-login-cli-1.png コマンドラインの出力

上記のメッセージを確認したら、q以外のキー(Enterキーなど)を押してください。キーを押すと、Webブラウザに次のページが表示されます。

heroku-login-1.png CLIからログインコマンドを実行すると表示されるwebサイト1

上記のページを確認したら、「Log in」ボタンをクリックしてください。画像のようなメールアドレスとパスワードの入力画面に移ります。

heroku-login-2.png CLIからログインコマンドを実行すると表示されるwebサイト2

ここで、Herokuアカウント作成の際に設定した、メールアドレスとパスワードを入力し、「Log in」をクリックしてください。Webブラウザが次の画面に切り替わります。

heroku-login-3.png CLIからログインコマンドを実行すると表示されるwebサイト3

また、コマンドライン側は、次のようなメッセージが表示されます。

heroku-login-cli-2.png コマンドラインの出力

heroku createの実行

次は、heroku createを実行してください。コマンドを実行すると、次のメッセージが表示されます。正常に実行されたことを確認しましょう。

heroku-create-cli-1.png コマンドラインの出力

git push heroku masterの実行

次は、git push heroku masterを実行してください。コマンドを実行すると、デプロイ処理の過程が出力されます(次の画像は、出力を一部省略したものです)。

デプロイが正常に終了したことを確認しましょう。

git-push-heroku-master-1.png デプロイ処理の様子の例

以上が、デプロイの手順です。

デプロイされたアプリケーションの確認

git push heroku masterが正常に終了したことを確認したら、heroku openコマンドを実行してください。heroku openは、デプロイしたアプリケーションをブラウザで開くコマンドです。ブラウザで開いたページにHello World!と表示されることを確認してください。

ここで注目すべき点は、以下の2つです。

https://から始まるURLにブラウザからアクセスできた

今まではアプリケーションをローカルで実行し、http://localhost:5000のようにブラウザからアクセスしていました。

今回はHerokuにデプロイしたことで、https://から始まるURLを知っている人であれば誰でもアクセスできるようになりました。これは本番環境にアプリケーションが公開されたことを意味します。

git pushを実行するとアプリケーションが稼働し始めた

今回はPaaSの1つであるHerokuを利用したため、サーバー機器やOSなどを自前で用意することなく、git pushコマンドを実行しただけで、アプリケーションが稼働する状態になりました。

今回は、本番環境にアプリケーションをデプロイする方法を学びました。今回はHerokuのサービスを使用しましたが、他のサービスでも大枠の流れは変わりません。そのサービスの手順に従ってプロジェクトのファイルを配置し、アプリケーションのデプロイを行います。

まとめ

ここまでのLessonを通して、Node.jsとExpress、そしてEJSによるWebアプリケーションの基礎を学びました。

こうしたフレームワークやライブラリを組み合わせるだけで、Webアプリケーションの仕組みは簡単に構築できます。Herokuのようなクラウドサービスを用いれば、自身の作成したアプリケーションを公開することも決して難しくはありません。

ぜひ今後も学習を継続し、Webアプリケーションのスキルを身につけていってください。