ノード.jsアプリケーションをコンテナ化するための9つのヒント

過去5年間、Node.jsはプロの開発者の間で トッププラットフォームとしての地位を維持してきました 。 これは、スループットを最大化するように設計されたオープンソースのクロスプラットフォームのJavaScriptランタイム環境です。 Node.jsは、イベント駆動型のノンブロッキングI/Oモデルを使用しているため、軽量で効率的であり、データ集約型のリアルタイム分散アプリケーションに最適です。 

90,500以上の星と24,400のフォークを備えた Nodeの 開発者コミュニティは非常に活発です。 これまで以上に多くの開発者がNode.jsアプリを作成する中、ビルドとデプロイ、クロスプラットフォームの効率的な方法を見つけることが重要です。 ガイドの核心に飛び込む前に、コンテナ化がどのように役立つかについて説明しましょう。 

Nodeアプリケーションをコンテナ化することが重要なのはなぜですか?

Node アプリケーションをコンテナー化することには多くの利点があります。 まず、Dockerの使いやすいCLIベースのワークフローにより、開発者はコンテナ化されたNodeアプリケーションを構築、共有、実行できます。 次に、開発者は単一のパッケージからアプリをインストールし、数分で起動して実行できます。 第 3 に、Node 開発者は、開発から本番環境までの一貫性を確保しながら、ローカルでコーディングとテストを行うことができます。

Node.jsアプリをコンテナにすばやくパッケージ化する方法を説明します。 また、イメージの脆弱性、イメージの肥大化、イメージタグの欠落、ビルドパフォーマンスの低下など、忘れがちな重要な懸念事項にも取り組みます。 簡単な ToDo リスト アプリを調べて、9 つのヒントがどのように適用されるかについて説明しましょう。

単純な ToDo リスト・アプリケーションの分析

まず、単純な ToDo リスト アプリケーションを考えてみましょう。 これは、ノード.jsバックエンド、およびMongoDBデータベースを備えた基本的なReactアプリケーションです。 完全なプロジェクトのソース コードは、 GitHub サンプル リポジトリ内で入手できます。

アプリケーションのビルド

さいわい、サンプル アプリケーションはわずか数ステップで構築できます。 まず、適切な素晴らしい作成サンプルを複製して、プロジェクトで使用する必要があります。

git clone https://github.com/dockersamples/awesome-compose/
cd awesome-compose/react-express-mongodb
docker compose -f docker-compose.yaml up -d

次に、コマンドを入力して docker compose ps 、ターミナルにサービスを一覧表示します。 これにより、すべてが考慮され、適切に機能していることが確認されます。

docker compose ps
NAME                COMMAND                  SERVICE             STATUS              PORTS
backend             "docker-entrypoint.s…"   backend             running             3000/tcp
frontend            "docker-entrypoint.s…"   frontend            running             0.0.0.0:3000->3000/tcp
mongo               "docker-entrypoint.s…"   mongo               running             27017/tcp

3 番目に、ブラウザーを開き、[ https://localhost:3000 ] に移動して、アプリケーションの動作を確認します。 ToDo リストの UI が表示され、アプリケーションと直接対話できるようになります。

リストビュー

これは、機能的なアプリケーションを短時間で起動するための優れた方法です。 ただし、これらのサンプルは、構築できる基盤であることに注意してください。 それらはあなたのニーズによりよく合うようにカスタマイズ可能です。 そして、これはパフォーマンスの観点から重要な場合があります—上記の例は完全に最適化されていないためです。 次に、可能な限り最高のアプリを構築するのに役立つ一般的な最適化のヒントなどを紹介します。 

Nodeアプリケーションをコンテナ化および最適化するための9つのヒント

1)「バージョン:最新」の代わりに特定のベースイメージタグを使用する

Docker イメージをビルドするときは、バージョン情報、目的の宛先 (本番環境やテストなど)、安定性、または環境間でアプリケーションをデプロイするためのその他の有用な情報をコード化する便利なタグを指定することを常にお勧めします。

ローカル開発以外では、Docker が自動的にプルするタグに依存し latest ないでください。 使用する latest と予測できず、予期しない動作が発生する可能性があります。 イメージ バージョンをプル latest するたびに、アプリケーションを破損する可能性のある新しいビルドまたはテストされていないコードが含まれている可能性があります。 

ではなく、特定の node:lts-buster Docker イメージをベースイメージ node:latest として使用する次の点 Dockerfileを考慮してください。このアプローチは、安定したイメージであるため lts-buster 、好ましい場合があります。

# Create image based on the official Node image from dockerhub
FROM node:lts-buster

# Create app directory
WORKDIR /usr/src/app

# Copy dependency definitions
COPY package.json ./package.json
COPY package-lock.json ./package-lock.json

# Install dependencies
#RUN npm set progress=false \
#    && npm config set depth 0 \
#    && npm i install
RUN npm ci

# Get all the code needed to run the app
COPY . .

# Expose the port the app runs in
EXPOSE 3000

# Serve the app
CMD ["npm", "start"]

全体として、多くの場合 FROM node:latest Dockerfile、.

2)マルチステージビルドを使用する

マルチステージ ビルドでは、Docker ビルドでコンパイル、パッケージ化、単体テストに 1 つの基本イメージを使用できます。 別のイメージは、アプリケーションのランタイムを保持します。 これにより、最終的なイメージのセキュリティが強化され、フットプリントが縮小されます (開発ツールやデバッグ ツールが含まれていないため)。 多段 Docker ビルドは、ビルドが 100% 再現可能で無駄のないものであることを保証するのに役立ちます。 内に Dockerfile 複数のステージを作成して、そのイメージの構築方法を制御できます。

Node アプリケーションは、多層アプローチを使用してコンテナ化できます。 各レイヤーには、ソース コード、リソース、スナップショットの依存関係など、さまざまなアプリ コンポーネントが含まれている場合があります。 前述のように、アプリケーションを独自のイメージにパッケージ化する場合はどうなりますか? それがどのように行われるかを確認するには、以下 Dockerfile を確認してください。

FROM node:lts-buster-slim AS development

WORKDIR /usr/src/app

COPY package.json ./package.json
COPY package-lock.json ./package-lock.json
RUN npm ci

COPY . .

EXPOSE 3000

CMD [ "npm", "run", "dev" ]

FROM development as dev-envs
RUN <<EOF
apt-get update
apt-get install -y --no-install-recommends git
EOF


# install Docker tools (cli, buildx, compose)
COPY --from=gloursdocker/docker / /
CMD [ "npm", "run", "dev" ]

まず、 AS development node:lts-buster-slim ステートメントにラベルを追加します。これにより、このビルドステージを他のビルドステージで参照できます。 次に、というラベル dev-envsの付いた新しい開発ステージを追加します。 このステージを使用して開発を実行します。

それでは、イメージを再構築して開発を実行しましょう。 上記と同じ docker build コマンドを使用しますが、 --target 開発ビルドステージを具体的に実行するための開発フラグを追加します。

docker build -t node-docker --target dev-envs .

3)ノードイメージのセキュリティ脆弱性を修正する

今日の開発者は、サービスを構築する際にサードパーティのコードとアプリに依存しています。 外部ソフトウェアは、注意しないとコードに不要な脆弱性をもたらす可能性があります。 信頼できるイメージを活用し、コンテナーを継続的に監視することで、保護に役立ちます。

Docker イメージをビルド node:lts-buster-slim するたびに、Docker Desktop は、既知の脆弱性を検出するためにイメージのセキュリティ スキャンを実行するように求めます。

Docker Desktop 用の Snyk 拡張機能 を使用して、Node.js アプリケーションを検査してみましょう。まず、 Docker Desktop 4.8.0+MacWindows、または Linux マシンにインストールします。 次に、[設定]>[拡張機能]内のチェックボックスをオンにして、 Docker拡張機能を有効にします

次に、左側のサイドバーにある[拡張機能の追加]ボタンをクリックしてから、Snykを検索することで、拡張機能マーケットプレイスを閲覧できます。

Snyk拡張機能マーケットプレイス

Snykの拡張機能を使用すると、ローカルとリモートの両方のDockerイメージをすばやくスキャンして脆弱性を検出できます。

Snyk install

Snykをインストールし、[ node:lts-buster-slim イメージ名の選択]フィールドにノードドッカーの公式イメージを入力します。 スキャンを開始するには、Docker Hubにログインする必要があります。 アカウントをお持ちでなくても心配しないでください—無料で作成に数分しかかかりません。

スキャンを実行すると、Docker デスクトップ内に次の結果が表示されます。

スナイク画像スキャン

Snykは、このスキャン中にさまざまな重大度の70の脆弱性を発見しました。 これらに気づいたら、イメージを強化するための修復を開始できます。

それだけじゃないです。 脆弱性チェックを実行するには、次のコマンド Dockerfile を直接使用できます docker scan

docker scan -f Dockerfile node:lts-buster-slim

4)ヘルスチェックの活用

この命令は HEALTHCHECK 、コンテナーをテストし、それがまだ機能していることを確認する方法を Docker に指示します。 たとえば、サーバープロセスがまだ実行されている場合でも、Webサーバーが無限ループに陥り、新しい接続を処理できない場合を検出できます。

アプリケーションが運用環境に到達すると、ほとんどの場合、Kubernetes やサービス ファブリックなどのオーケストレーターがアプリケーションを管理します。 HEALTHCHECKを使用すると、コンテナーの状態をオーケストレーターと共有して、構成ベースの管理タスクを実行できます。次に例を示します。

# syntax=docker/dockerfile:1.4

FROM node:lts-buster-slim AS development

# Create app directory
WORKDIR /usr/src/app

COPY package.json ./package.json
COPY package-lock.json ./package-lock.json
RUN npm ci

COPY . .

EXPOSE 3000

CMD [ "npm", "run", "dev" ]

FROM development as dev-envs
RUN <<EOF
apt-get update
apt-get install -y --no-install-recommends git
EOF

RUN <<EOF
useradd -s /bin/bash -m vscode
groupadd docker
usermod -aG docker vscode
EOF

HEALTHCHECK CMD curl --fail http://localhost:3000 || exit 1  


# install Docker tools (cli, buildx, compose)
COPY --from=gloursdocker/docker / /
CMD [ "npm", "run", "dev" ]
```

HEALTHCHECK が存在する場合 Dockerfileは、コマンドを実行した後 docker psSTATUS 列にコンテナーの正常性が表示されます。このチェックに合格したコンテナーは正常です。 CLI は、異常なコンテナーに異常のラベルを付けます。

docker ps
CONTAINER ID   IMAGE                            COMMAND                  CREATED          STATUS                             PORTS                    NAMES
1d0c5e3e7d6a   react-express-mongodb-frontend   "docker-entrypoint.s…"   23 seconds ago   Up 21 seconds (health: starting)   0.0.0.0:3000->3000/tcp   frontend
a89721d3c42d   react-express-mongodb-backend    "docker-entrypoint.s…"   23 seconds ago   Up 21 seconds (health: starting)   3000/tcp                 backend
194c953f5653   mongo:4.2.0                      "docker-entrypoint.s…"   3 minutes ago    Up 3 minutes                       27017/tcp                mongo

Docker Compose内で(大文字と小文字の違いに注意してください)を定義する healthcheck こともできます。 これは、を使用していない Dockerfileときに非常に便利です。 プレーンテキストの命令を記述する代わりに、この構成を YAML 形式で記述します。 

ファイル内で docker-compose.yml 定義 healthcheck できる設定例を次に示します。

backend:
    container_name: backend
    restart: always
    build: backend
    volumes:
      - ./backend:/usr/src/app
      - /usr/src/app/node_modules
    depends_on:
      - mongo
    networks:
      - express-mongo
      - react-express
    expose:
      - 3000
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000"]
      interval: 1m30s
      timeout: 10s
      retries: 3
      start_period: 40s

5).ドッカー無視を使用する

ビルドのパフォーマンスを向上させるには .dockerignoreDockerfileファイルを .このチュートリアルでは、 .dockerignore ファイルには1行だけを含める必要があります。

node_modules

この行は、 node_modules Maven からの出力を含むディレクトリを Docker ビルドコンテキストから除外します。 慎重に構成 .dockerignore する多くの正当な理由があります ファイルですが、今のところこの単純なファイルで十分です。

それでは build context 、説明しましょう そしてなぜそれが不可欠であるか 。 このコマンドは、 docker build と "コンテキスト" から Dockerfile Docker イメージをビルドします。 このコンテキストは、指定した PATH または URLにあるファイルのセットです。 ビルド プロセスでは、これらのファイルを参照できます。 

一方、コンパイルコンテキストは開発者が作業する場所です。 これは、Mac、Windows、またはLinuxディレクトリ上のフォルダである可能性があります。 このディレクトリには、ソースコード、構成ファイル、ライブラリ、プラグインなど、必要なすべてのアプリケーションコンポーネントが含まれています。 と .dockerignore ファイルを使用すると、新しいイメージの構築中に除外するソースコード、構成ファイル、ライブラリ、プラグインなどの次の要素のどれを決定できます。 

方法 .dockerignore は次のとおりです ビルドから除外することを選択した場合、 node_modules directory ファイルが表示される場合があります。

バックエンド:

Dockerignore backend

フロントエンド:

Dockerignore フロントエンド

6)セキュリティ目的で非rootユーザーとして実行する

ユーザー特権でアプリケーションを実行すると、リスクの軽減に役立つため、より安全です。 同じことがDockerコンテナにも当てはまります。 既定では、Docker コンテナーと実行中のアプリにはルート権限があります。 したがって、Dockerコンテナは非ルートユーザーとして実行することをお勧めします。 

これを行うには、内に指示 Dockerfile を追加します USER。この命令は USER 、イメージの実行中に優先ユーザー名 (または UID) とオプションでユーザーグループ (または GID) を設定し、後続の RUN, CMDまたは ENTRYPOINT 命令に対して設定します。

# syntax=docker/dockerfile:1.4
FROM node:lts-buster AS development

WORKDIR /usr/src/app

COPY package.json ./package.json
COPY package-lock.json ./package-lock.json

RUN npm ci

COPY . .

EXPOSE 3000


CMD ["npm", "start"]

FROM development as dev-envs


RUN <<EOF
   apt-get update
   apt-get install -y --no-install-recommends git
EOF

RUN <<EOF
   useradd -s /bin/bash -m vscode
   groupadd docker
   usermod -aG docker vscode
EOF

USER vscode

# install Docker tools (cli, buildx, compose)
COPY --from=gloursdocker/docker / /
CMD [ "npm", "start" ]

7)マルチアーキテクチャのDockerイメージを優先する

CPUは、ネイティブアーキテクチャのバイナリのみを実行できます。 たとえば、x86 システム用に構築された Docker イメージは、Arm ベースのシステムでは実行できません。 AppleがカスタムArmベースのシリコンに完全に移行しているため、x86(IntelまたはAMD)コンテナイメージがAppleのMシリーズチップで動作しない可能性があります。 

そのため、 マルチアーキテクチャコンテナイメージの構築を常に推奨しました。 以下は、 mplatform/mquery 任意のパブリックレジストリ内のパブリックイメージのマルチプラットフォームステータスを照会できるDockerイメージです。

docker run --rm mplatform/mquery node:lts-buster
Unable to find image 'mplatform/mquery:latest' locally
d0989420b6f0: Download complete
af74e063fc6e: Download complete
3441ed415baf: Download complete
a0c6ee298a93: Download complete
894bcacb16df: Downloading [=============================================>     ]  3.146MB/3.452MB
Image: node:lts-buster (digest: sha256:a5d9200d3b8c17f0f3d7717034a9c215015b7aae70cb2a9d5e5dae7ff8aa6ca8)
 * Manifest List: Yes (Image type: application/vnd.docker.distribution.manifest.list.v2+json)
 * Supported platforms:
   - linux/amd64
   - linux/arm/v7
   - linux/arm64/v8

マルチアーキテクチャ イメージの構築に役立つコマンドを導入し docker buildx ました。 Buildx は、使い慣れた Docker ユーザー エクスペリエンスで多くの強力なビルド機能を有効にする Docker コンポーネントです。
すべての Buildx ビルドは、 Moby BuildKit エンジンを使用して実行されます。

BuildKitは、マルチプラットフォームビルド、またはユーザーのローカルプラットフォームをターゲットにしないビルドに優れているように設計されています。 ビルドを呼び出すときに、フラグを設定して --platform 、ビルド出力のターゲットプラットフォーム(linux/amd64、、linux/arm/v7linux/arm64/v8など)を指定できます。

docker buildx build --platform linux/amd64,linux/arm/v7 -t node-docker .

8)ノードのグレースフルシャットダウンオプションを調べる

Docker コンテナーは本質的に一時的なものです。 それらは停止して破壊し、最小限の労力で再構築または交換することができます。 プロセスに通知シグナルを送信する SIGTERM ことで、コンテナを終了できます。 この短い猶予期間では、アプリが進行中の要求を処理し、リソースをタイムリーにクリーンアップしていることを確認する必要があります。 

一方、Node.jsは、アプリを適切にシャットダウンするための鍵となるOSなどの SIGINT SIGTERM 信号を受け入れて転送します。Node.js を使用すると、これらのシグナルの処理方法をアプリで決定できます。 コードを記述したり、モジュールを使用して処理したりしない場合、アプリは正常にシャットダウンされません。 タイムアウト期間後に Docker または Kubernetes が強制終了するまで、これらのシグナルは無視されます。 

アプリ内 Dockerfileやtini などの docker run --init 特定の init オプションを使用することは、アプリコードを変更できない場合に実行可能です。 ただし、グレースフル シャットダウンのために適切なシグナル処理を処理するコードを記述することをお勧めします。

Docker の Captain Bret Fisher (12:57) によるこのビデオでは、利用可能な 3 つのノード シャットダウン オプションすべてについて詳しく説明しています。

9) OpenTelemetry API を使用して NodeJS のパフォーマンスを測定する

Node開発者はどのようにしてアプリをより速く、よりパフォーマンスの高いものにしますか? 一般に、開発者はサードパーティの可観測性ツールを使用してアプリケーションのパフォーマンスを測定します。 このパフォーマンス監視は、一流のユーザーエクスペリエンスを備えた多機能ノードアプリケーションを作成するために不可欠です。

可観測性は、アプリケーションのパフォーマンスだけにとどまりません。 メトリック、トレース、ログが前面と中央に配置されました。 メトリックは開発者がシステムの問題点を理解するのに役立ち、トレースはシステムがどのように間違っているかを発見するのに役立ちます。 ログは、それが間違っている理由を教えてくれます。 開発者は、特定のメトリックとトレースを掘り下げて、システムの動作を全体的に理解できます。

Node アプリケーションを監視するということは、Node のメトリック、リクエスト率、リクエストエラー率、およびリクエスト期間を追跡することを意味します。 OpenTelemetryは、ノード.jsアプリケーションのインストゥルメント化に役立つツールとAPIの一般的なコレクションの1つです。

SigNoz などのオープンソースツールを使用して、アプリのパフォーマンスを分析することもできます。SigNozはフルスタックの可観測性ツールを提供しているため、複数のツールに依存する必要はありません。

結論

このガイドでは、慎重に作成する Dockerfile ことからSnykスキャンによるイメージの保護まで、Dockerイメージを最適化するための多くの方法について説明しました。 より優れたNode.jsアプリの構築は複雑である必要はありません。 いくつかのコアファンダメンタルズを釘付けにすることで、あなたは素晴らしい体調になります。 

さらに詳しく知りたい場合は、安全な運用グレードの Docker イメージを構築するための追加の推奨事項とベスト プラクティスを確認してください。

Docker ブログ nodejs のベスト プラクティス v2 1