LoginSignup
15
9

More than 1 year has passed since last update.

【Docker】コンテナでNode.jsを実行してみた

Last updated at Posted at 2021-03-06

前の記事に引き続き、今回はコンテナでNode.jsのアプリケーションを動かすことに挑戦してみました。

スクリーンショット 2021-03-06 13.44.31.png

実装

実装は以下の手順で行いました。

  1. Node.jsアプリの作成
  2. Dockerfileの作成
  3. Dockerfileのビルド
  4. コンテナの起動
  5. ブラウザとアプリの接続

Node.jsアプリの作成

localhost:8080を開いたときにHi thereがブラウザに表示されることを目指します。

package.jsonindex.jsの中身は以下のようにします。

package.json
{
  "dependencies": {
    "express": "*"
  },
  "scripts": {
    "start": "node index.js"
  }
}
index.js
const express = require('express');

const app = express();

app.get('/', (req, res) => {
  res.send('Hi there');
});

app.listen(8080, () => {
  console.log('Listening on port 8080');
});

Dockerfileの作成

Dockerイメージをビルドしたとき、ファイルシステムにアプリに必要なプログラム(dependencies)をインストールし、メタ情報にアプリの起動コマンド(npm start)を導入することを目指します。

前の記事でredisをコンテナで立ち上げたときのように、試しにalpineのベースイメージを使ってDockerfileを作成してみます。

FROM alpine

RUN npm install

CMD ["npm", "start"]

これをビルドすると、RUN npm installの部分でこけます。
なぜかというと、alpineイメージは軽量であるがゆえに、nodenpmもプリインストールされていないからです。

そのため、ベースイメージにはnodenpmがプリインストールされているnodeイメージなどを選択する必要があります。
ただ、nodeイメージにはnpmやnode以外にもテキストエディタツールなどの余計なプログラムもプリインストールされているので、軽量なalpineにnpmnodeがプリインストールされたnode:alpinenode:slimなどの最適化されたベースイメージを使うのがベストプラクティスです。

ベースイメージをnode:alpineに変えて再度ビルドしてみます。

FROM node:alpine

RUN npm install

CMD ["npm", "start"]

このビルドもこけます。
なぜなら、node:alpineのファイルシステムにpackage.jsonが含まれておらず、インストールするプログラムが不明だからです。
もしたまたまハードドライブにpackage.jsonが含まれていたとしても、コンテナに割り当てられたハードドライブのセグメントにファイルがあるわけではないので、npm installは実行できません。

ここでDockerfile内に、RUNの前にファイルコピーするための記述COPYを加えます。
COPY ./ ./とすることで、ローカルのカレントディレクトリのファイル(package.json, index.js)をコンテナ内のファイルシステムにコピーすることができます。

FROM node:alpine

WORKDIR /usr/app

COPY ./ ./
RUN npm install

CMD ["npm", "start"]

*WORKDIR /usr/appという記述がありますが、これはファイルのコピー先(ワーキングディレクトリ)を指定しています。詳細は後述します。

これでようやくビルドが成功します。

(base) [9:42:15] → docker build -t suzuki0430/simpleweb:latest .                                                                               ~/Programs/docker/simpleweb
Sending build context to Docker daemon  4.096kB
Step 1/5 : FROM node:alpine
 ---> b3dce3e0529f
Step 2/5 : WORKDIR /usr/app
 ---> Using cache
 ---> d995d6f0fd23
Step 3/5 : COPY ./ ./
 ---> 8b999fe7dc6e
Step 4/5 : RUN npm install
 ---> Running in 52b539c857e3

added 50 packages, and audited 50 packages in 3s

found 0 vulnerabilities
npm notice
npm notice New minor version of npm available! 7.0.8 -> 7.6.1
npm notice Changelog: <https://github.com/npm/cli/releases/tag/v7.6.1>
npm notice Run `npm install -g npm@7.6.1` to update!
npm notice
Removing intermediate container 52b539c857e3
 ---> 5f7e4d45df44
Step 5/5 : CMD ["npm", "start"]
 ---> Running in e8085a051d01
Removing intermediate container e8085a051d01
 ---> e76e6a91dc2b
Successfully built e76e6a91dc2b
Successfully tagged suzuki0430/simpleweb:latest

コンテナの作成・起動を行います。

(base) [9:43:11] → docker run suzuki0430/simpleweb                                                                                             ~/Programs/docker/simpleweb

> start
> node index.js

Listening on port 8080

コンテナの起動も正常にできたみたいなのでこれで成功!といきたいところなのですが、ブラウザからアプリが開けません...えっ?
スクリーンショット 2021-03-06 10.26.21.png

ポートマッピング

アプリが開けなかったのは、コンテナのポート8080とローカルホストのポート8080が接続されていなかったためです。
ブラウザからlocalhost:8080にアクセスする際、localhost(ローカルPC)のポートに接続しようとします。
ただ、今回はコンテナの中でNode.jsのアプリケーションを実行しているため、コンテナのポートと接続する必要があります。
スクリーンショット 2021-03-06 14.30.41.png

ローカルPCとコンテナのポートを紐付けることをポートマッピングと呼びます。
ポートマッピングを実行するためにはdocker run -p 8080:8080 [image id]コマンドを実行します。
コンテナの作成・起動とポートマッピングを同時に実行してくれます。

これで開けるようになりました!
スクリーンショット 2021-03-06 10.43.55.png

キャッシュの利用

アプリは開けるようになったのですが、気になる部分が1つあります。
それは今のDockerfileの内容だと、ファイルを更新してイメージをビルドする際にnpm installが必ず実行されてしまうことです。
最終的にビルドは完了するので問題がないといったらないのですが、ビルドの待ち時間はできるだけ避けたいものです...

そこでキャッシュの利用を考えます。

DockerfileをRUN npm installの前にpackage.jsonのみをコピーするように書き換えます。

FROM node:alpine

WORKDIR /usr/app

COPY ./package.json ./
RUN npm install
COPY ./ ./

CMD ["npm", "start"]

すると、package.jsonの中身が同じであれば、RUN npm installまでの手順をキャッシュによってスキップすることができるようになります。

Step 3/6 : COPY ./package.json ./
 ---> Using cache
 ---> 1e7f34ded131
Step 4/6 : RUN npm install
 ---> Using cache
 ---> 8f0ae8a2f7f6

ワーキングディレクトリの指定

Dockerfile内にWORKDIR /usr/appという記述がありますが、ここではRUN, CMD, ENTRYPOINT, COPY, ADD, docker run, execで実行するコンテナプロセスのワーキングディレクトリを指定しています。

WORKDIR /と指定することももちろんできるのですが、ルートディレクトリにはいろんなファイルやフォルダが含まれているので、ファイル名が同じだったりすると干渉が起こってしまいます。そのため、コンテナプロセス用のディレクトリを指定してあげる必要があります。

コンテナ起動時にシェルを開くと、確かにワーキングディレクトリが/usr/appとなっています。

(base) [11:06:07] → docker run -it suzuki0430/simpleweb sh                                                                                     ~/Programs/docker/simpleweb
/usr/app #

おわりに

Node.jsをコンテナで動かすことができるようになりました。次はDocker-composeについて学習します。

参考資料

15
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
9