ブログに戻る

Ubuntu 20.04でDockerを使ってNode.js (Express.js) アプリをデプロイする方法

Ubuntu 20.04でDockerを使ってNode.js (Express.js) アプリをデプロイする方法

はじめに

Docker は、軽量で仮想化された、ポータブルな、ソフトウェア定義の標準化された環境であるコンテナプラットフォームです。これにより、物理ホストマシン上で実行されている他のソフトウェアから隔離された状態でソフトウェアを実行できます。Dockerは、ソフトウェア開発における継続的開発および統合(CI/CD)の側面を定義する重要なコンポーネントです。これは、仮想マシンに代わる軽量な選択肢 を提供し、開発者が分散アプリケーションアーキテクチャを利用できるようにします。Dockerエコシステムの詳細な概要については、こちらの記事をご覧ください.

Dockerを使用してアプリケーションを構築するプロセスは、開発者がアプリケーションのイメージを作成することから始まります。その後、そのイメージがコンテナ内にデプロイされます。イメージには、アプリケーションコード、ライブラリ、設定ファイル、環境変数、ランタイム環境など、アプリケーションを定義するコンポーネントが含まれています。イメージはコンテナ内の環境を標準化し、コンテナ化にポータビリティ(移植性)の特徴をもたらします。Node.js は、Webブラウザの外でJavaScriptコードを実行できる、オープンソースでクロスプラットフォームのバックエンドJavaScriptランタイム環境です。これは、ChromeのV8 JavaScript Engine. Express.js は、Node.js上で動作するミニマリストなバックエンドJavaScriptフレームワークです。

このチュートリアルでは、Expressフレームワーク上で動作するWebサイトのイメージを作成します。フロントエンドの見た目を良くするために、フロントエンドライブラリである Bootstrap を使用します。イメージを作成したら、コンテナをビルドして Docker Hub にプッシュします。Docker Hubを使用すると、開発者はコンテナ化されたアプリケーションをホストして、任意のDocker環境に簡単にデプロイできます。コンテナがDocker Hubにホストされたら、それをプルして、実際にWebサイトを提供する別のイメージをビルドします。

前提条件

これは実践的なチュートリアルです。手順に沿って進められる環境を作成してください。

ステップ1:アプリケーションの依存関係の設定

イメージを作成する前に、アプリケーションのソースコードを作成する必要があります。アプリケーションのソースコードには、コンテナにコピーされるコード、静的コンテンツ、および依存関係が含まれます。まず、非ルートユーザーのホームディレクトリにプロジェクト用のディレクトリを作成します。ここでは、そのディレクトリを「node_express」と呼びますが、お好みのディレクトリ名を使用して構いません:

次に、このディレクトリに移動します:

これがアプリケーションのルートディレクトリになります。node.js アプリケーションは、ルートフォルダに package.json ファイルがあることを想定しています。Npm はこのファイルを使用して、アプリケーションに必要な依存関係を決定します。次のコマンドを入力して、このファイルを作成します:

その後、次のコードスニペットをファイルに追加します。名前、作成者、説明、およびエントリポイントファイルは、必要に応じて更新できます:

ご覧のように、このファイルはプロジェクト名、バージョン、作成者、およびアプリケーションコードが共有されるライセンスを指定します。プロジェクトには、次の場所での重複を避けるために、短くてわかりやすい名前を使用することをお勧めします:npmレジストリ。このプロジェクトには、ISCライセンスを指定しており、これによりアプリケーションコードの自由なコピー、変更、または配布が許可されます。

最も重要な点として、ファイル内の次のディレクティブに注意する必要があります:

  • main」:このディレクティブはアプリケーションのエントリポイントを指定します。ここでは index.js に設定しています。このファイルは間もなく作成します。
  • dependencies」:このディレクティブは、次のコマンドを実行したときに npm レジストリから取得されるアプリケーションの依存関係を指定します:npm コマンド。今回のケースでは、Express バージョン 4.17.1 以上が必要です。

Ctrl + O を押してファイルを保存できます。次に、Ctrl + X を押してファイルを閉じます。次に、以下のコマンドを実行して依存関係をインストールします:

このコマンドは、次のファイルで指定されたアプリケーションの依存関係をインストールします:package.json ファイル。インストール先は次のディレクトリ内です:node_modules ディレクトリ。これらはコマンドを最初に実行したときに自動作成されます。アプリケーションの依存関係がインストールされたので、アプリケーションコードの追加を開始できます。

ステップ 2:アプリケーションコードファイルの追加

基本的なレシピウェブサイトを作成します。提供元は allrecipes です。アプリケーションのメインエントリポイントは、次のファイルです:index.js ファイル。次のディレクトリを追加します:views ディレクトリ。ここにはプロジェクトのさまざまなページや静的アセットが保持されます。ウェブサイトには、紹介情報といくつかのレシピへのリンクを含むランディングページが用意されます。

ランディングページのコードは、次のファイルに配置されます:home.html ファイル。まず、index.js ファイルを作成するために、次のコマンドを入力します:

以下のコードを追加します。これは Express アプリケーションをインポートして作成します。また、Router オブジェクト、ベースディレクトリ、およびこのアプリが提供されるポートも指定します:

require はモジュールをロードする JavaScript 関数です。この例では、express モジュールをロードしています。次に、インポートしたモジュールを使用して express オブジェクトと router オブジェクトを作成します。router オブジェクトは、チュートリアルを進めるにつれてこのオブジェクトに追加される HTTP メソッド呼び出しに応答することで、アプリのルーティング機能を実行します。

また、pathport も設定しました。path 定数はコードのベースディレクトリを定義します。今回のケースでは、プロジェクトのルートディレクトリ内にある次のサブディレクトリです:views サブディレクトリ。また、port は express アプリがリッスンするポートを指定します。この例では、次のように設定しています:8090.

定数が用意できたら、次のオブジェクトを使用してアプリケーションのルートを指定できます:router オブジェクト。ルートを指定するために、次のファイルに以下のコードを追加します:index.js ファイル:

You can ミドルウェアを追加するには、次の関数を使用します:router.use。この例では、ルーターのリクエストをアプリケーションのルートに渡す前にログに記録する関数を追加します。アプリケーションのベースに対する GET リクエストは、次のファイルを返します:home.html ページ。次に、特定のレシピページへのGETリクエストを使用して取得される、3つのレシピのページを追加しました。

最後に、次のコードを追加して、router ミドルウェアとアプリケーションの静的アセットをマウントします。さらに、expressアプリケーションに次のポートでリッスンするように指示します。8090:

完成した index.js ファイルは次のようになります。

これでファイルを保存して閉じることができます。次のステップでは、静的なウェブページを views ディレクトリに追加します。まず、次のコマンドを入力してディレクトリを作成します。

次のコマンドを入力して、home.html ランディングページファイルを開きます。

ファイルに次のコードを追加します。このコードはBootstrapをインポートし、ウェブサイトの訪問者にこのウェブサイトがどのようなものであるかについての情報を提供します:

Bootstrapをインポートするだけでなく、このページには基本的なナビゲーションメニューが追加されており、ページ間の移動やランディングページへの戻りを容易にします。また、カスタムCSSファイルをインポートするための行も追加しました。

後ほど、このファイルを使用してアプリケーションにカスタムスタイリングを追加します。それでは、レシピ用の3つのページを作成しましょう。まずは、ラザニアのページから始めます。次のコマンドを使用して、nanoエディタでファイルを開きます。

開いたファイルに、次のコードを追加します。このファイルはBootstrapとcustom.cssファイルをインポートし、ナビゲーションメニューを指定して、ラザニアのレシピ情報を提供します。

同じ手順で、guacamoleのレシピページのファイルを作成しましょう。次のコマンドを実行して、nanoでファイルを開きます。

次に、このコードをファイルに追加します。

最後に、banana_bread.html ファイルを次のコマンドを入力して作成しましょう:

次に、以下のHTMLコードをファイルに追加します:

これで、すべてのページが作成されました。覚えているかと思いますが、css/custom.css ファイルを追加します。次のコマンドを入力してディレクトリを作成します。

次に、以下のコマンドを使用して、nanoエディタでファイルを作成して開きます。

必要に応じて、ウェブサイトをスタイリングするためにさらにCSSコードを追加できます。簡潔にするために、ファイルに次のコードスニペットを追加しましょう。

完了したら、ファイルを保存して閉じます。

アプリケーションのソースコードとプロジェクトの依存関係がインストールされたので、アプリケーションを起動できます。

アプリがポート8090でリッスンするように設定しました。ファイアウォールにこのポート経由のトラフィックを許可するように指示するには、次のコマンドを実行します。別のポートを指定した場合は、コマンド内のポート番号を置き換えてください。

これで、アプリケーションを起動できます。ただし、その前に、次のコマンドを実行してプロジェクトのルートディレクトリにいることを確認してください。

node index.js でアプリケーションを起動します。別のエントリポイントを指定した場合は、それをエントリポイントに置き換えてください。

ブラウザでhttp://your_public_server_ip:8090にアクセスすると、定義されたレシピのランディングページが表示されます。

Recipes

 

ナビゲーションにさまざまなレシピへのリンクが表示されています。いくつかクリックしてみましょう。以下はLasagnaのレシピページです。

Node.js app install on Ubuntu 1

そして、こちらがGuacamoleのレシピページです。

Guacamole

ここまでに、アプリケーションを作成し、期待通りに動作することを確認しました。Ctrl + C を押してサーバーを終了し、Dockerfileの作成に進むことができます。Dockerfileは、必要に応じてアプリケーションのインスタンスを再作成できるようにすることで、スケーラビリティに役立ちます。

ステップ 3: Dockerfileの作成

Dockerはイメージをビルドする際、Dockerfileに指定された命令を読み取ります。これはアプリケーションの実行環境を指定するものです。そのため、開発者が依存関係の不一致や実行環境のバージョンの変更による問題を避けるのに役立ちます。次のコマンドを入力してDockerfileを作成します。

Dockerイメージは、互いに積み重ねられた複数のイメージレイヤーを使用して作成されます。まず、アプリケーションの開始点となるベースイメージを追加することから始めます。

アプリケーションは node.js 環境で動作することを想定しているため、まずは node:10-alpineイメージnode.js用)を追加することから始めます。このチュートリアルを執筆している現在、これはNode.jsの推奨LTSバージョンです。この特定のイメージを選択したのは、それがAlpine Linuxプロジェクトに由来しているためです。これにより、イメージサイズを最小限に抑えることができます。Docker HubのNodeイメージページDocker HubのNodeイメージページには、ニーズに応じて選択できるいくつかのイメージバリアントがあります。

次のコードを追加し、FROMディレクティブを使用してアプリケーションのベースイメージを設定します。

このイメージにはNode.js and npmが含まれています。すべてのDockerfileは、FROMディレクティブで始まる必要があります。Dockerのnodeイメージには、デフォルトで非rootのnodeユーザーが含まれています。これを使用して、アプリケーションコンテナをrootとして実行することもできますが、Dockerのセキュリティ推奨事項では、コンテナをrootとして実行せず、そのリソースを実行するために必要な権限のみに制限することを推奨しています。

そのため、今回はnodeユーザーのホームディレクトリをアプリケーションの作業ディレクトリとして使用し、コンテナ内のユーザーとしても使用します。詳細については、こちらのDocker Nodeイメージのベストプラクティスガイドを参照してください。

アプリケーションコードの権限を合理化するために、node_modulesサブディレクトリを、アプリディレクトリとともに/home/node内に作成します。これらのディレクトリを作成することで、コンテナ内でローカルにnpm installコマンドを実行したときに、適切な権限が設定されるようになります。ディレクトリを作成したら、それらの所有権をnodeユーザーに設定する必要があります。Dockerfile内に次の行を追加してこれを行います。

次に、以下の行を追加して作業ディレクトリを設定します。

Dockerがデフォルトでディレクトリを作成するのを防ぐために、常にWORKDIRを設定することをお勧めします。

次の行を追加して、package.jsonpackage-lock.jsonファイルをコピーします。

It’s recommended to add the COPY命令は、npm installを実行したり、アプリケーションのソースコードをコピーしたりする前に追加することをお勧めします。これにより、Dockerのキャッシュメカニズムを利用できます。ビルドプロセス中、Dockerはすべての命令に対してキャッシュされたレイヤーがあるかどうかを確認します。つまり、package.jsonファイルが変更されていない場合、Dockerは既存のイメージレイヤーを使用し、node modulesの再インストールを回避するため、ビルドプロセスが高速化されます。

Before running npm installを実行する前に、次の行を追加してユーザーをnodeに切り替え、すべてのアプリケーションファイルとnode_modulesディレクトリが非rootのnodeユーザーによって所有されるようにします。

これで、コンテナでnpm installコマンドを実行する準備が整いました。Dockerfileに次の行を追加します。

Once node_modulesがインストールされたら、次の行を追加して、適切な権限と所有権(すなわち非rootのnodeユーザー)でアプリケーションコードをコンテナ上のアプリケーションディレクトリにコピーするようDockerに指示します。

最後のステップは、コンテナのポート8090を公開することです。これはエントリーファイルであるindex.jsで定義した通りです。

EXPOSEは、実行時にコンテナのどのポートを開くかを設定します。CMDはアプリケーションを起動するコマンドを実行します。この場合はnode index.jsです。

Dockerfile内で有効になるのは最後のCMDコマンドのみであるため、CMDコマンドは1つだけにする必要があります。次のDockerfileリファレンスドキュメント を参照して、Dockerfileで実行できることの一覧を確認してください。

完成したDockerfileは以下のようになります。

これでファイルを保存して閉じることができます。

次に行うのは、.dockerignore ファイルの追加です。.gitignore ファイルと同様に、.dockerignore は、プロジェクトディレクトリ内のどのファイルやディレクトリをコンテナにコピーしないかを指定します。

nanoエディタでファイルを開きます:

ファイル内に以下の行を追加します:

もし git リポジトリを使用している場合は、.git ディレクトリと .gitignore ファイルも追加する必要があります。ファイルを保存して閉じます。

すべてがうまくいったら、docker build コマンドを使用してアプリケーションイメージをビルドします。–t フラグを docker build コマンドに追加することで、Dockerがデフォルトで設定するランダムな文字列ではなく、覚えやすい名前でイメージにタグを付けることができます。また、このイメージをDocker Hubにプッシュするため、タグにDocker Hubのユーザー名を含めるのが最適です。

タグ名として nodejs-express-image を使用します。お好みのタグ名を自由に選択してください。イメージをビルドするコマンドは以下の通りです:

your_dockerhub_username は実際の Docker Hub ユーザー名に置き換えてください。末尾の . (ドット) は、ビルドコンテキストが現在のディレクトリであることを指定しています。

ビルドプロセスには1〜2分かかります。完了したら、イメージを確認するコマンドを入力します:

以下のような出力が表示されるはずです:

sudo docker images

なお、your_dockerhub_username は実際のユーザー名に置き換えています。

イメージがビルドされたことを確認したら、docker run を使用して、そのイメージからコンテナを作成できます。以下のフラグが含まれます:

  • -p:コンテナ上のポートを公開し、ホストシステムのポートにマッピングします。デモンストレーション目的で、ホストシステムではポート80を使用します。ただし、そのポートで別のプロセスが実行されている場合は、必要に応じて自由に変更してください。詳細については、Dockerドキュメントのポートバインディングを参照してください。.
  • -d:デタッチドモード用。コンテナをバックグラウンドで実行し続けることができます。
  • --name:Dockerにランダムな文字列を割り当てさせる代わりに、覚えやすい名前を設定するために使用できます。

コンテナを構築するコマンドは以下の通りです。Docker Hubのユーザー名を適切に置き換えてください:

コンテナが構築され、実行が開始されるまで待ちます。実行中のすべてのコンテナを検査するには、次のコマンドを使用します:

以下のような出力が表示されるはずです:

Node.js app install on Ubuntu 3

出力に示されているように、コンテナは現在実行中です。ブラウザでポート番号を指定せずにサーバーのパブリックIPアドレスにアクセスすると、ブラウザで表示できます。ホームページがロードされます:

awesome recipe

 

Dockerを使用してNode Expressの静的ウェブサイトのデプロイに成功しました。将来の使用やスケーリングのために、このイメージをDocker Hubにプッシュする方法を見てみましょう。

ステップ 4: Dockerイメージリポジトリの操作

イメージをDocker Hubなどのイメージレジストリにプッシュして、将来の使用のために保存したり、他の開発者と共有したり、コンテナのスケーリングを可能にしたりできます。作成したイメージをDocker Hubにプッシュし、それを使用してコンテナを再作成できます。

次のコマンドを使用して、Docker Hubアカウントにログインします。実際のDocker Hubユーザー名に置き換えてください。

プロンプトが表示されたらパスワードを入力します。ログインすると、~/.docker/config.json ファイルがユーザーのホームディレクトリに作成され、そこにDocker Hubの資格情報が含まれます。

設定が完了したら、次のコマンドを入力してイメージをDocker Hubにプッシュします。その際、先ほどイメージをビルドしたときに設定したタグを指定します。

このコマンドは、DockerイメージをDocker Hubアカウントにプッシュします。アカウントにアクセスすると、最近プッシュされたイメージを確認できます。

Docker Hub

現在のアプリケーションコンテナを破棄し、リポジトリ内のイメージを使用して再構築することで、イメージリポジトリの有用性をテストできます。

次のコマンドを入力して、現在のコンテナを一覧表示します。

次のような出力が表示されるはずです。

Docker Hub

出力に表示されている CONTAINER ID をメモしてコピーし、次のコマンドでコンテナを停止するために使用します。IDはご自身のものに置き換えてください。

次のコマンドを入力して、システムで利用可能なすべてのDockerイメージを一覧表示します。

出力には、作成したイメージの名前、node.jsイメージ、およびビルドプロセスからのその他のイメージが表示されます。

次のコマンドを入力して、未使用または宙ぶらりん(dangling)なイメージを含むイメージを削除します。

Type y と入力して確認します。これにより、停止したコンテナとイメージが削除されます。一覧表示すると、出力に空のリストが表示されます。

output

これで、アプリケーションを実行しているコンテナとイメージ自体の両方が削除されました。Dockerコンテナ、イメージ、およびボリュームの削除についての詳細は、チュートリアルを参照してください.

これで、まず次のコマンドを使用してDocker Hubからイメージをプルすることで、プロセス全体を再作成できます。Docker Hubのユーザー名は適切に置き換えてください。

次のコマンドで、Dockerイメージを再度一覧表示します。

出力にイメージが表示されるはずです。

sudo docker

これで、ステップ3 のコマンドを使用してコンテナを再構築できます。もちろん、必要に応じてDocker Hubのユーザー名を置き換えてください。

コンテナを一覧表示して、再構築されたことを確認します。

同様の出力が表示されるはずです。

ブラウザでサーバーのパブリックIPアドレスにアクセスすると、アプリが実行されているのを確認できるはずです。

結論

ここまでチュートリアルを進めてきたなら、ExpressとBootstrapで作成され、Dockerでデプロイされた静的ウェブサイトが完成しているはずです。静的ウェブサイトのファイルを使用してDockerイメージをビルドし、そのイメージを使用してコンテナを作成しました。その後、イメージをDockerイメージレジストリであるDocker Hubにプッシュし、将来の使用やスケーリングに利用できるようにしました。イメージレジストリの使用をテストするために、イメージとコンテナを破棄し、レジストリからイメージをプルして、コンテナを再構築しました。

このチュートリアルでは、Node.jsアプリをデプロイする方法を説明しました。別のウェブ開発スタックの使用方法を学びたい場合は、Nginx上でDocker Composeを使用してLaravelアプリをデプロイする.

Dockerの活用に関するその他のリソースについては、次のチュートリアルを参照してください。

ハッピーコンピューティング!

author

Hark Labs

著者 · CloudSigma

Preslav DobrevはCloudSigmaのクリエイティブデザイナーであり、従来型および革新的なマーケティングチャネルを活用した一貫性のあるビジネスアイデンティティに注力しています。彼は芸術的なビジョンと戦略的マーケティングを融合させ、インパクトのあるブランドナラティブを生み出すことに長けています。

コメント

コメントはまだありません。最初のコメントを投稿しましょう。