ノードを維持するためのトップ4戦術.js Dockerでロッキン

これはからのゲスト投稿です ド ッカーキャプテンブレットフィッシャー、長年のDevOpsシステム管理者であり、Node.jsのDocker Masteryを含む人気のある Docker Masteryコースでコンテナスキルを教えているスピーカーです。 ウィークリー ユーチューブライブショー、およびDockerを採用する企業へのコンサルティング。 8月28日に開催されるオンラインミートアップにBretが参加し、Node.jsおよびDockerのトピックに関するデモとQ&Aを行います。

C3プロモーション小
フォクシー、私のドッカーマスタリーマスコットはノードとドッカーのファンです

私たちは皆、お気に入りの言語とフレームワークを持っており、Node.jsは私にとってトップです。 私はミッションクリティカルなアプリケーションの初期からDockerでNode.jsを実行してきました。 私は、このフレームワークと、Dockerを使用してnpm、Yarn、nodemonなどのツールを最大限に活用する方法について、すべての人を教育することを使命 としています。

Node.jsをDockerで使用する方法についてはたくさんの情報がありますが、 その多くは何年も古くなっているため、Node.js 10+およびDocker 18.09+のセットアップを最適化するお手伝いをします。 これらのトピックなどをカバーする私のDockerCon 2019トークを見たい場合は、 YouTubeでチェックしてください

Node.jsコンテナを歌わせるための4つのステップを見てみましょう! 簡単な「長すぎる;それを必要とする人のために「読まなかった」。

現在のベースディストリビューションに固執する

DR: Node.js アプリをコンテナーに移行する場合は、現在運用環境にあるホスト OS の基本イメージを使用します。 その後、私のお気に入りの基本イメージはではなく公式 node:slimnode:alpineであり、これはまだ良いですが、通常は実装する作業が多く、制限があります。

Node.js アプリを Docker に配置するときに最初に尋ねる質問の 1 つは、「 Node.js Dockerfile をどのベース イメージから開始する必要があるか」です。

ドッカーイメージlsノード
スリムとアルパインはデフォルトの画像よりもかなり小さいです

これには複数の要因がありますが、すべてのMBが重要なIoTまたは組み込みデバイスを扱っていない限り、「画像サイズ」を最優先事項にしないでください。 近年、スリムなイメージのサイズは150MBに縮小され、最も幅広いシナリオで最適に機能します。 Alpineは非常に最小限のコンテナディストリビューションであり、最小のノードイメージはわずか75MBです。 ただし、パッケージマネージャー(aptからapk)を交換し、 エッジケースを処理し、 セキュリティスキャンの制限 を回避するための労力のレベルにより、ほとんどのユースケースでnode:alpineを推奨することを延期します。

コンテナ技術を採用するときは、他のものと同様に、変更率を減らすためにできることをする必要があります。 非常に多くの新しいツールとプロセスがコンテナに付属しています。 開発者と運用者が最もよく慣れ親しんでいる基本イメージを選択すると、多くの予期しない利点があるため、CentOS、Ubuntuなどのカスタムイメージを作成することを意味する場合でも、意味がある場合はそれに固執するようにしてください。

ノードモジュールの処理

DR: 適切なローカル開発のためのいくつかのルールに従っている限り、コンテナ内のnode_modulesを再配置する必要はありません。 2番目のオプションは、Dockerfileのディレクトリmode_modules上に移動し、コンテナを適切に構成することで、最も柔軟なオプションを提供しますが、すべてのnpmフレームワークで機能するとは限りません。

私たちは皆、アプリで実行するすべてのコードを記述しない世界に慣れており、それはアプリフレームワークの依存関係を処理することを意味します。 よくある質問の 1 つは、コンテナー内のコードの依存関係がアプリのサブディレクトリである場合に、それらをどのように処理するかです。 開発用のローカル バインド マウントは、それらの依存関係がコンテナー OS ではなくホスト OS で実行するように設計されている場合、アプリに異なる影響を与える可能性があります。

Node.jsのこの問題の核心は、node_modulesホストOS用にコンパイルされたバイナリを含めることができ、コンテナOSと異なる場合、開発のためにホストからバインドマウントするときにアプリを実行しようとするとエラーが発生することです。 純粋なLinux開発者であり、Linux x64 for Linux x64で開発する場合、このバインドマウントの問題は通常問題にならないことに注意してください。

Nodeの場合.js独自の利点と制限がある2つのアプローチを提供します。

解決策A:シンプルに保つ

node_modules動かないでください。 コンテナー内のアプリの既定のサブディレクトリに引き続き配置されますが、これは、ホストで作成されたnode_modulesが開発中にコンテナーで使用されないようにする必要があることを意味します。

これは、純粋なDocker開発を行うときに私が好む方法です。 これは、ローカル開発のために従わなければならないいくつかのルールでうまく機能します。

  1. コンテナを介してのみ開発します。 なぜでしょうか。基本的に、ホスト上のnode_modulesとコンテナ内のnode_modulesを混同したくありません。 macOS と Windows では、Docker デスクトップは OS の障壁を越えてコードをバインドマウントするため、ホスト OS 用に npm を使用してインストールしたバイナリで、コンテナー OS では実行できない問題が発生する可能性があります。
  2. すべてのnpmコマンドをドッカーコンポーズから実行します。 つまり、プロジェクトのイニシャル npm installdocker-compose run <service name> npm installになります。

解決策B:コンテナモジュールを移動し、ホストモジュールを非表示にする

Dockerfile のファイル パスnode_modules上に再配置して.jsコンテナーの内外で Node を開発できるようにし、ホスト ネイティブ開発と Docker ベースの開発を切り替える依存関係が衝突しないようにします。

Node.jsは複数のOSとアーキテクチャで動作するように設計されているため、常にコンテナで開発したくない場合があります。 Node.jsアプリをホスト上で直接開発/実行し、ローカルコンテナでスピンアップする柔軟性が必要な場合は、ソリューションBがジャムです。

この場合、その OS 用に構築されたホスト上のnode_modulesと、Linux 用のコンテナー内の別のnode_modulesが必要です。

ノードソリューションb
パスを上node_modulesするために必要な基本的な線

このソリューションのルールは次のとおりです。

  1. コンテナー イメージ内のディレクトリの上にnode_modules移動します。 Node.jsは常にnode_modulesをサブディレクトリとして検索しますが、見つからない場合は、見つかるまでディレクトリパスをたどります。 ここでDockerfileでそれを行う例
  2. ホストnode_modulesサブディレクトリがコンテナに表示されないようにするには、「空のbind-mount」と呼ばれる回避策を使用して、ホストnode_modulesがコンテナで使用されないようにします。 あなたの作曲YAMLでは 、次のようになります
  3. これはほとんどのNode.jsコードで動作しますが、一部の大規模なフレームワークやプロジェクトは、node_modulesがサブディレクトリであるという前提でハードコーディングしているようです。

どちらのソリューションでも、常に .dockerignore ファイルにnode_modules を追加することを忘れないでください (.gitignore と同じ構文) したがって、ホストのモジュールを使用して誤ってイメージをビルドすることはありません。 ビルドでイメージビルド 内で npm installを実行することを常に望んでいます。

ノード ユーザーを使用し、最小特権に移動

すべての公式ノード.jsイメージには、ノードと呼ばれるアップストリームイメージにLinuxユーザーが追加されています。 このユーザーはデフォルトでは使用されない ため、Node.jsアプリはデフォルトでコンテナ内でルートとして実行されます。 これはまだそのコンテナに分離されているため、最悪のことではありませんが、Nodeをrootとして実行する必要がないすべてのプロジェクトで有効にする必要があります。 ドッカーファイルに新しい行を追加するだけです。 USER node

これを使用するためのいくつかのルールは次のとおりです。

  1. ドッカーファイル内の場所は重要です。 apt / yum / apkコマンドの後、 通常は npmインストールコマンドの前にユーザーを追加します。
  2. コピーするファイルの所有者を制御するための独自の構文を持つCOPYなど、すべてのコマンドに影響するわけではありません。
  3. 必要に応じて、いつでも元に戻す USER root ことができます。 より複雑なDockerfilesでは、オプションのステージ中にテストとセキュリティスキャンを実行することを含む マルチステージの例 のように、これが必要になります。
  4. 開発中にアクセス許可がわかりにくくなると、既定で非ルートユーザーとしてコンテナーで作業を行うようになります。 これを回避する方法は、これらの1回限りのコマンドをrootとして実行することをDockerに指示して、npm installなどを実行することです。 docker-compose run -u root npm install


本番環境でプロセスマネージャーを使用しない

DR: ローカル開発を除き、ノードの起動コマンドを何もラップしないでください。 npm、nodemonなどは使用しないでください。あなたのドッカーファイルCMDを次のような  [“node”, “file-to-start.js”] ものにしてください また、コンテナの管理と交換も簡単になります。

Nodemonやその他の「ファイルウォッチャー」は開発に必要ですが、Node.jsアプリにDockerを採用することの大きなメリットの1つは、サーバー上でpm2、nodemon、forever、systemdを使用していたジョブをDockerが引き継ぐことです。

Docker、Swarm、および Kubernetes は、ヘルスチェックを実行し、失敗した場合にコンテナーを再起動または再作成する役割を果たします。 また、オーケストレーターの仕事は、pm2 や forever などのツールを使用していたアプリのレプリカの数を増やすことです。 ほとんどの場合、Node.jsはまだシングルスレッドであるため、単一のサーバーでも、複数のコンテナレプリカを起動して複数のCPUを利用することをお勧めします。

私のサンプルリポジトリ は、Dockerfileでノードを直接使用し、ローカル開発のために、を使用して別のイメージステージ docker build --target <stage name>をビルドするか、YAMLの作成でCMDをオーバーライドする方法を示しています。

ドッカーファイルでノードを直接起動する

DR また、npmを使用してDockerfileでアプリを起動することもお勧めしません。 説明させてください。

Node.jsアプリでこれに対処する方法についてオンラインで混乱や誤った情報を見つける「PID 1問題」のために、ノードバイナリを直接呼び出すことをお勧めします。 ブロゴスフィアの混乱を解消するために、DockerとNode.jsの間に「init」ツールが常に必要なわけではなく、アプリがどのように正常に停止するかについて考えるのにもっと時間を費やす必要があります

Node.js は、アプリを適切にシャットダウンするために重要な OS からの SIGINT や SIGTERM などの信号を受け入れて転送します。 Node.js は、これらのシグナルの処理方法を決定するのはアプリに任されているため、コードを記述したり、モジュールを使用して処理したりしないと、アプリは正常にシャットダウンされません。 これらのシグナルは無視され、タイムアウト期間が経過すると Docker または Kubernetes によって強制終了されます (Docker のデフォルトは 10 秒、Kubernetes は 30 秒です)。 アプリを更新するときに接続が切断されないようにする必要がある本番HTTPアプリがあれば、これについてさらに気にする必要があります。

たとえばnpmのように、他のアプリを使用してNode.jsを起動すると、このシグナリングが壊れることがよくあります。 npmはこれらのシグナルをアプリに渡さないため、DockerfilesのエントリポイントとCMDから除外することをお勧めします。 これには、コンテナーで実行されているバイナリが 1 つ少なくなるという利点もあります。 もう1つの利点は、コンテナが起動されたときにアプリが何をするかをDockerfileで 正確に 確認できるため、package.jsonで実際の起動コマンドを確認する必要がないことです。

Dockerfileで tini を使用するなどの docker run --init initオプションを知っている人にとっては、アプリコードを変更できない場合に適したバックアップオプションですが、グレースフルシャットダウンの適切なシグナル処理を処理するコードを書く方がはるかに優れたソリューションです。 2つの例は、 私がここに持っているいくつかの定型コードと、 stoppableのようなモジュールを見ています。

それだけですか?

いいえ。 これらは、ほぼすべてのNode.jsチームが対処する懸念事項であり、それに伴う他の多くの考慮事項があります。 マルチステージビルド、HTTPプロキシ、npmインストールパフォーマンス、ヘルスチェック、CVEスキャン、コンテナロギング、イメージビルド中のテスト、マイクロサービスDocker-composeセットアップなどのトピックはすべて、Node.jsクライアント、および学生にとって一般的な質問です。

これらのトピックに関する詳細情報が必要な場合は、 このトピックに関する DockerCon 2019 セッション ビデオをご覧いただくか、https://www.bretfisher.com/node で Docker for Node .js の 8 時間のビデオを確認してください  

読んでくれてありがとう。 Twitterで私に連絡することができ、毎週の DevOpsとDockerニュースレターを入手し、 毎週のYouTubeビデオとライブショーを購読し、 他のDockerリソースとコースをチェックすることができます。

ドッカリングを続けてください!

もっと詳しく知りたいですか?8月28日のオンラインミートアップにブレットに参加してください

フィードバック

「ノードを維持するためのトップ0戦術.js Dockerのロッキン」に関する4つの考え