Node.jsウェブ・アプリケーションをデプロイするには?
Node.jsはJavaScriptの実行環境で、ブラウザの外でJavaScriptコードを実行できる。
Node.jsは、Chrome V8JavaScriptエンジンをベースに構築されており、イベントドリブン、ノンブロッキングI/Oモデルを特徴としており、サーバーサイド・アプリケーションの構築に非常に効率的である。
この記事では、サーバーサイド・アプリケーション開発におけるNode.jsの利点と限界、およびNode.jsアプリケーションのデプロイメント・オプションを探ります。
さらに、Node.jsアプリケーションのビルド、Docker化、Back4app Containersへのデプロイを無料で行います。
Contents
ウェブアプリケーション開発におけるNode.jsの利点
2009年にNode.jsがリリースされて以来、Node.jsはサーバーサイドのウェブ・アプリケーションを構築するための主要な選択肢となっている。その理由は以下の通りだ。
効率性と拡張性
前述したように、Node.jsはChromeのV8エンジン上で動作し、JavaScriptコードを実行できる。このエンジンは、ジャスト・イン・タイム(JIT)コンパイルを使用して、ネイティブのJavaScriptコードをマシンコードに変換する。
実行時に、V8 の Turbofan および Crankshaft コンポーネントはマシン コードを分析し、再コンパイルして、可能な限り最高のパフォーマンスを提供します。
さらに、Node.jsのイベント・ドリブン・モデルにより、アプリケーションのメイン・スレッドをブロックすることなく、ユーザー・インタラクションなどのイベントに応答してコードを実行できるため、大規模なトラフィックを伴うアプリケーションに最適です。
Node.jsのモジュラー・アーキテクチャーとクラスタリングのビルトイン・サポートにより、Node.jsで開発されたアプリケーションは簡単に拡張できます。そのモジュラー・アーキテクチャにより、アプリケーションを独立したスケーリングが可能なコンポーネント・パーツに分割することができます。
一方クラスタ・モジュールでは、複数のコアやサーバにまたがってアプリケーションの複数のインスタンスを生成することができる。これにより水平スケーリングが可能になり、クラスターにサーバーを追加することでアプリケーションをスケールさせることができる。
浅い学習曲線
Node.jsは、ウェブ開発に広く使われているプログラミング言語であるJavaScriptをベースにしている。Node.jsでJavaScriptを使用することで、すでにJavaScriptに慣れ親しんでいる開発者にとって、Node.jsによるサーバーサイド開発がより身近なものになりました。
これにより、ウェブ開発の複雑さが軽減され、開発プロセスが合理化される。
大規模エコシステム
Node.jsには、モジュールやパッケージの膨大なエコシステムを作り上げた開発者の大規模で活発なコミュニティがある。
Node.jsのパッケージ・マネージャーであるnpmは、アプリケーションに機能を追加するために使用できる100万以上のパッケージをホストしている。
これらのパッケージは、lodashのような小さなユーティリティ・ライブラリから、複雑なウェブ・アプリケーションを構築するために使用できる大規模なフレームワークNest.jsまで多岐にわたる。
幅広いモジュールやパッケージが利用できるため、ウェブ・アプリケーションの開発に必要な時間と労力を大幅に削減できる。
これらのパッケージを活用して、認証、データベース統合、サーバーサイド・レンダリングなどの機能をアプリケーションに追加することができる。
ウェブアプリケーション開発におけるNode.jsの限界
Webアプリケーション開発に関して言えば、Node.jsは効率的なパフォーマンス、スケーラビリティ、モジュールやパッケージの膨大なエコシステムなど、多くの利点を提供します。しかし、他のテクノロジーと同様に、Node.jsにもいくつかの制限があります。その制限には次のようなものがあります。
大きなメモリ消費
Node.jsはノンブロッキングI/Oモデルを使用しているため、新しいスレッドを作成することなく、多くのリクエストを同時に処理することができる。しかしながら、各リクエストは、その処理のために割り当てられるメモリを必要とします。
これは、Node.jsアプリケーションが、特に多くの同時リクエストを処理する場合、多くのメモリを消費する可能性があることを意味します。これは、メモリが制限されたシステム上で実行されるアプリケーションにとって問題となる可能性があります。
非同期プログラミング・モデル
Node.jsの非同期プログラミング・モデルは大きな利点ですが、開発者にとっては複雑さの原因にもなります。
非同期プログラミングでは、プログラムの流れについて異なる考え方が必要になる。このシフトは、同期プログラミングに慣れた開発者にとっては難しいことかもしれない。
さらに、非同期プログラミングは、コールバック地獄を引き起こす可能性がある。コールバックが入れ子になることで、コードが読みづらくなり、保守が困難になる状況だ。
シングルスレッド・イベント・ループ
Node.jsは、ネットワーク通信、ファイルI/O、データベース操作など、I/O集約的なタスクを処理するように設計されている。
しかし、複雑な計算やデータ処理、機械学習など、CPUに負荷のかかる作業には最適ではないかもしれない。
これは、Node.jsがシングルスレッド・イベント・ループ・モデルを使用しているためで、一度に1つのタスクしか実行できないことを意味する。
タスクの完了に時間がかかると、イベント・ループがブロックされ、アプリケーションが応答しなくなる可能性がある。
Node.jsウェブアプリケーションのデプロイ
Node.jsアプリケーションをデプロイする方法は複数ある。そのいくつかを探ってみよう。
クラウド・ホスティング・サービス
クラウド・ホスティング・サービスを利用すると、Amazon Web Services(AWS)、Google Cloud Platform(GCP)、Microsoft Azureなどの企業が管理するサーバー上にNode.jsアプリケーションをデプロイできます。
スケーラビリティ、グローバルな可用性、容易なデプロイメント、従量課金などのメリットを提供する。さらに、データベースやロードバランシングといった他のクラウド・サービスと統合することで、より優れたアプリケーションの構築を支援する。
クラウド・ホスティング・サービスを利用するには、Node.jsアプリケーションをクラウド・プロバイダーのサーバーにデプロイするだけだ。その後、ウェブ・ブラウザや他のクライアント・アプリケーションからアプリケーションにアクセスできる。
Node.js用のクラウド・ホスティング・サービスの例としては、以下のようなものがある:
- AWS Elastic Beanstalk
- GCP App Engine
- Microsoft Azure App Service
これらのプラットフォームは、基盤となるインフラストラクチャを気にすることなく、Node.jsアプリケーションのデプロイ、スケール、管理を容易にします。
さらに、自動スケーリング、ロードバランシング、ビルトインモニタリングなどの機能を提供し、アプリケーションのスムーズな稼働をサポートします。
仮想専用サーバー (VPS)
仮想専用サーバ(VPS)は、物理サーバ上で動作する仮想マシンで、専用サーバ上で動作しているかのようにNode.jsアプリケーションをインストールして実行することができます。
仮想プライベートサーバーは、共有ホスティングよりも優れたコントロールとカスタマイズオプションを提供する一方で、専用サーバーよりも費用対効果の高い代替手段を提供します。
Node.jsアプリケーションにVPSホスティングを使用するには、設定済みのNode.jsイメージを提供するホスティングプロバイダを選択するか、Node.jsおよびその他の依存関係を自分でインストールします。
Node.js用のVPSホスティング・プロバイダの例には、以下のものがあります:
- DigitalOcean
- Linode
- Vultr
コンテナ化
コンテナ化とは、基盤となるインフラから分離されたコンテナ化された環境でアプリケーションをデプロイし、実行するための技術である。
コンテナは、従来の仮想マシンに代わる軽量で柔軟な選択肢を提供し、Node.jsアプリケーションのデプロイに最適です。
ポータブルなので、ローカルマシンでアプリケーションをビルドしてテストし、コンテナ化をサポートするあらゆるプラットフォームにデプロイすることができる。
また、コンテナはワークロードに応じて簡単にスケールアップまたはスケールダウンできるため、スケーラビリティが向上する。異なるプラットフォーム間で一貫性が保たれるため、アプリケーションの管理と保守が容易になる。
サービスとしてのコンテナ化(CaaS)を提供するプラットフォームの例には、以下のようなものがある:
- Back4app Containers
- AWS ECS
- Azure ACI
- Google GKE
Back4appコンテナを使ってBack4appにNode.jsアプリをデプロイする
Back4appはクラウドプラットフォームであり、直感的なUIやフル機能のCLIツールを使ってウェブアプリケーションを作成、管理、デプロイすることができる。Back4appは様々なサービスを提供しており、その1つにコンテナ化がある。
Back4appのコンテナは、繰り返しのタスクを自動化し、サーバーサイドのインフラを管理することで、開発と本番のギャップをなくし、DevOpsを心配する必要がなくなります。
この記事では、Back4appコンテナを使ってシンプルなNode.jsアプリケーションをビルドし、デプロイします。構築するNode.jsアプリケーションは、CRUD(Create、Read、Update、Delete)機能をサポートしたシンプルなブックストアAPIだ。
開発環境のセットアップ
新しいプロジェクト・ディレクトリを作成し、プロジェクト・ディレクトリで以下のコマンドを実行してnpmを初期化する:
mkdir bookstoreapp && cd bookstoreapp && npm init -y
次に、以下のコマンドを実行して、プロジェクトに必要な依存関係をインストールする:
npm install express dotenv mysql knex
上記でインストールした依存関係は以下の通り:
- Express.js:Expressは、Node.jsアプリケーション開発プロセスを簡素化するNode.jsフレームワークです。
- dotenv: dotenvは環境変数の管理に使えるnpmパッケージだ。
- MySQL:MySQLの依存関係は、MySQL用のNode.jsドライバで、このアプリケーションのデータベースとして使用します。
- KnexKnex は JavaScript 用のクエリビルダです。生のSQLクエリを書かずにデータベースとやりとりするには、この依存関係が必要です。
次に、プロジェクトのルート・ディレクトリにroutes.jsと
index.js
ファイルを作成する。
次に、以下のコード・ブロックをindex.js
ファイルに追加する:
//index.js
require("dotenv").config();
const express = require("express");
const app = express();
const port = 3000;
const router = require("./routes.js");
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use("/", router);
app.listen(port, () => {
console.log(`App listening at ${port}`);
});
上記のコードブロックは、Expressサーバーを作成し、ポート3000でHTTPリクエストの着信をリッスンする。ミドルウェア関数を使用して受信データを解析し、ルート・パスにルーターを関連付けて受信リクエストを処理する。
最後に、サーバーを起動し、指定したポートでリッスンしていることを示すメッセージをコンソールに記録する。
次に、package.json
ファイルに開始
スクリプトを追加する。次のように:
"start": "node index.js",
データベースへの接続
Knexは、データベースに接続するための設定オプションを含むknexファイルを必要とします。
以下のコマンドを実行してknexfileを作成する:
knex init
MySQLを使用するようにKnexを設定するには、knexfile.js
ファイルの内容を以下のコードブロックに置き換えてください:
// Update with your config settings.
require("dotenv").config()
/**
* @type { Object.<string, import("knex").Knex.Config> }
*/
module.exports = {
development: {
client: "mysql",
connection: {
host: process.env.DEV_HOST,
user: process.env.DEV_USER,
password: process.env.DEV_PASSWORD,
database: process.env.DEV_NAME,
},
migrations: {
directory: "./db/migrations",
}
},
production: {
client: "mysql",
connection: {
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
},
migrations: {
directory: "./db/migrations",
}
},
};
次に、プロジェクトのルート・ディレクトリにdb
フォルダを作成し、db.js
ファイルを作成する。
以下のコードブロックをdb.js
ファイルに追加する:
//db.js
const knex = require("knex");
const knexFile = require("../knexfile.js");
const environment = process.env.NODE_ENV || "development";
module.exports = knex(knexFile[environment]);
上記のコード・ブロックは、環境
変数にNODE_ENVを
設定するか、NODE_ENVが
設定されていない場合はdevelopmentを
設定します。このように、開発環境と本番環境など、環境ごとに異なる設定を指定することができます。
移行ファイルの作成
以下のコマンドを実行して、データベースのマイグレーションファイルを作成します:
knex migrate:make bookstore
上記のコマンドは、knexfile.jsで
指定したファイルパス(“./db/migrations”)にマイグレーションファイルを作成します。
次に、マイグレーションファイルを開き、その中のコードを以下のコードブロックに置き換える:
/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
exports.up = function (knex) {
return knex.schema.createTable("books", (table) => {
table.increments("id").primary();
table.string("title");
table.string("author");
table.string("genre");
table.timestamps(true, true);
});
};
/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
exports.down = function (knex) {
return knex.schema.dropTableIfExists("books");
};
次に、以下のコマンドを実行してマイグレーションファイルを実行する:
knex migrate:latest
ルーティングの実装
最後に、以下のコードブロックをroutes.js
ファイルに追加する:
const express = require("express");
const router = express.Router();
const db = require("./db/db.js");
// GET /books
router.get("/books", async (req, res) => {
try {
const books = await db("books");
res.json(books);
} catch (err) {
console.error(err);
res.status(500).send("Internal Server Error");
}
});
//POST /books/new
router.post("/books/new", async (req, res) => {
try {
const { title, author, genre } = req.body;
const book = await db("books").insert({
title,
author,
genre,
});
res.status(201).json(book);
} catch (err) {
console.error(err);
res.status(500).send("Internal Server Error");
}
});
//PUT /books/:id
router.put("/books/:id", async (req, res) => {
const { id } = req.params;
try {
const { title, author, genre } = req.body;
const book = await db("books").where({ id }).update(
{
title,
author,
genre,
},
["id", "title", "author", "genre"]
);
if (book.length !== 0) {
res.status(201).send(book);
} else {
res.status(404).json({ error: "Book not found" });
}
} catch (err) {
console.error(err);
res.status(500).send("Internal Server Error");
}
});
//DELETE /books/:id
router.delete("/books/:id", async (req, res) => {
const { id } = req.params;
try {
const book = await db("books").where({ id }).del();
if (book !== 0) {
res.status(200).json({ message: "Book deleted" });
} else {
res.status(404).json({ error: "Book not found" });
}
} catch (err) {
console.error(err);
res.status(500).send("Internal Server Error");
}
});
module.exports = router;
上記のコードブロックは、すべてのブックの取得、新しいブックの作成、既存のブックの更新、ブックの削除など、HTTPリクエストを処理するためのいくつかのルートを定義しています。
Dockerファイルの作成
Dockerfileは、Dockerイメージの構築方法を特定のフォーマットで記述した命令セットを含むファイルです。Dockerイメージはコンテナのスナップショットであり、アプリケーションコード、ランタイム、ライブラリ、システムツールなど、アプリケーションの実行に必要なすべてを含んでいる。
Node.jsアプリケーションをBack4app Containers上で実行するには、Dockerイメージのビルド手順を含むDockerfileを作成する必要があります。
以下のコマンドを実行してDockerfileを作成する:
touch Dockerfile
次に、Node.jsアプリケーションのベースイメージを選択する必要があります。Dockerのベースイメージは、新しいDockerイメージを構築するための出発点です。Dockerイメージが構築される基礎となるものです。
以下のコードブロックをDockerfileに追加し、ベースイメージを指定します:
# Specify base image
FROM node:18-alpine
この行は、このDockerイメージをビルドするベース・イメージを指定します。この場合、Node.js 18バージョンはAlpine Linuxディストリビューション上で動作しています。
次に、作業ディレクトリを指定する必要がある。Dockerfile内の後続のコマンドはすべて、このディレクトリを基準に実行される。
以下のコードブロックをDockerfileに追加し、作業ディレクトリを指定する:
# Specify working directory
WORKDIR /app
次に、package.jsonと
package-lock.json
ファイルをカレントディレクトリ(つまりDockerfileがあるディレクトリ)から作業ディレクトリ(/app
)にコピーする必要がある。
以下のコードブロックをDockerfileに追加し、ファイルをコピーする:
# Copy package.json and package-lock.json
COPY package*.json ./
次に、作業ディレクトリでnpm install
コマンドを実行して、package.jsonと
package-lock.jsonに
記載されているNode.jsの依存関係をインストールする必要がある。
以下のコードをDockerfileに追加して、依存関係をインストールする:
# Install dependencies
RUN npm install
上記のコマンドは、package.json
****およびpackage-lock.json
ファイルにリストされているすべての依存関係をインストールし、指定された作業ディレクトリのnode_modules
フォルダに格納します。
次に、アプリケーションのソースコードを作業ディレクトリにコピーする必要がある。
以下のコードブロックをDockerfileに追加し、ソースコードをコピーする:
# Copy source code
COPY . .
次に、ホストマシンにポートを公開する必要がある。ポートを公開することで、Dockerコンテナを実行するときに、公開されたポートでネットワーク接続を受信できるようになる。
以下のコードブロックをDockerfileに追加し、ホストマシンにポート3000を公開する:
# Expose port 3000
EXPOSE 3000
最後に、Dockerコンテナの起動時に実行するコマンドを指定する必要がある。通常、Node.jsアプリケーションはnpm start
コマンドを使って起動する。
以下のコードブロックをDockerfileに追加し、コマンドを指定する:
# Run the app
CMD ["npm", "start"]
完成したDockerfileは、以下のコードブロックのようになるはずだ:
# Specify base image
FROM node:18-alpine
# Specify working directory
WORKDIR /app
# Copy package.json and package-lock.json
COPY package*.json ./
# Install dependencies
RUN npm install
# Copy source code
COPY . .
# Expose port 3000
EXPOSE 3000
# Run the app
CMD ["npm", "start"]
Dockerファイルを作成したら、コードをGitHubにプッシュする。
新しいBack4appアプリケーションを作成する
Back4appでNode.jsアプリケーションをデプロイするための最初のステップは、Back4appのアカウントを作成することです(アカウントをお持ちでない場合)。以下の手順で作成できます。
- Back4appのウェブサイトに移動します。
- 次に、ランディングページの右上にあるサインアップボタンをクリックします。
- 最後に、登録フォームに必要事項を記入して送信する。
Back4appアカウントの作成が完了したら、Back4appアカウントにログインし、右上にある「NEW APP」ボタンをクリックしてください。
このボタンをクリックすると、「新しいアプリの作成方法」を選択するページに移動する。コンテナ化を使ってデプロイするので、下の画像のように、Containers as a Serviceを選択する。
次に、GitHubアカウントをBack4appアカウントに接続します。Back4appにアカウント内の全てのリポジトリへのアクセス権を与えるか、特定のリポジトリへのアクセス権を与えるかを選択できます。
デプロイしたいアプリケーション(この場合は、このチュートリアルでビルドしたアプリケーション)を選択し、[Select]をクリックします。
選択ボタンをクリックすると、アプリの名前、ブランチ、ルートディレクトリ、環境変数などの情報を入力するページに移動します。
アプリケーションが機能するために必要な環境変数をすべて入力してください。必要事項を入力したら、下図のように「Create App」をクリックします。
アプリの作成]ボタンをクリックすると、デプロイ プロセスが開始されます。デプロイプロセスが完了すると、下の画像のように、デプロイしたアプリケーションにアクセスできる URL が画面の左隅に指定されます。
デプロイプロセスに時間がかかっている場合は、デプロイ時にエラーが発生していないかログを確認するか、Back4appのトラブルシューティングガイドを参照してください。
結論
Node.jsは、高速なパフォーマンス、スケーラビリティ、柔軟性など、多くの利点を提供する人気のフレームワークです。しかし、シングルスレッドであるため重いワークロードを処理するのが難しいなどの制限もあります。
このような制限があるにもかかわらず、Node.jsアプリケーションには、信頼性が高く、ユーザーフレンドリーなNode.jsアプリケーションのホスティングと管理のためのプラットフォームを提供するBack4appを含む、いくつかのデプロイオプションが利用可能です。
Node.jsアプリを作成し、この記事で説明するステップに従うことで、Node.jsアプリケーションを簡単にBack4appにデプロイし、その多くの利点を活用することができます。
Node.jsのデプロイとホスティングに興味がありますか?以下の2つのチュートリアルをご覧ください:
よくあるご質問
Node.js Web アプリケーションをデプロイする方法
– 開発環境をセットアップする
– データベースに接続する
– 移行ファイルを作成する
– ルーティングを実装する
– Dockerfile を作成する
– 新しい Back4app アプリケーションを作成する