20220109のNode.jsに関する記事は5件です。

お正月休みにGitをインタラクティブに操作できるサブコマンドツール作ってきました

年末からお正月にかけての時間で趣味でツールを作ってみました? Gitのちょっと面倒な操作をインタラクティブに行えるGitのサブコマンドツールgit-exを作成してnpmに公開しています。 僕は普段はAndroidエンジニアとしてKotlinを書いていますが、趣味ではVimのカラースキーム作ったり、Goでツール作ったり、Rustに手を出したり、、、など色々しています。今回はJavaScriptのターミナルのライブラリがとても良さそうだったのでnode製のCLIツールを作ってみました?! このQiita記事では前半にツールの紹介と、後半で実装面を書いてみようと思います。 ツールの紹介 僕は普段Gitをターミナル上でコマンドで使っているのですが、たまにSourceTreeなどのGUIツールやTIGなどのツールが使いたくなるときがあります。 たとえば、ファイルパスをいちいちGitの引数に指定しなければならないときなどです。そういうときだけSource Tree起動するのも面倒ですし、TIGを使うんとなると操作を覚えないとなかなか使い勝手がよくありません。 もう少し、ターミナル上で簡易に使えるようなツールがあれば便利だなと思って開発してみました。 git-exをインストールすると、いちいちファイルパスを指定しないといけないようなちょっと面倒なGit操作をターミナル上でインタラクティブに操作することができるようになります。 npmで公開しているJavaScript製のツールになります。インストールは下記で可能です。 npm i -g @yasukotelin/git-ex このツールをインストールすると git-ex コマンドが使えるようになるのですが、Gitはgit-xxx形式のコマンドをサブコマンドとして認識するので、git ex 形式で実行することが可能です。 サブコマンドとして認識されない場合は、一度ターミナルを再起動すると反映されると思います。 git-ex コマンドがそもそも見つからない場合は、npmのグローバルインストールモジュールへのパスが通っていないと思うので環境変数を確認してください。 git ex stage / unstage ステージング操作をするときは通常ターミナルでステージングするには下記のようなコマンドを打ちます。 # Gitプロジェクト内のすべての変更をステージングする git add -A # こっち使う人も多いかも。 git add . 大抵のケースでは正直これで事足ります。ですが、特定のファイルをaddしたいときは結構面倒です。 git add ファイルパス 例えば変更が3ファイルあって、1ファイルはコミットに含めたくないみたいなケースです。この場合、毎回愚直に引数にパス指定をするか、一度add allしてから、省きたいファイルをresetするみたいな操作をする必要があり、かなり面倒です。 ましてや、自分はAndroid開発をしているのもあって、Java系のファイルパスの長さは地獄です。とても補完で選択はしてられません。 そこで git ex stage コマンドを使うとマルチセレクトでファイルをステージングすることができるのでとても便利です? また逆の操作のアンステージも git ex unstage でできます。Gitってアンステージ処理をしようと思うと、そもそもコマンドが確かRESETかなんかで覚えにくいという部分もあります。個人的には git ex stage コマンドよりも重宝してます。 git ex stash 次はStashをよく使う人にもおすすめな git ex stash コマンドです。 Stashの基本的な操作をインタラクティブに選ぶ機能と、Stashの一覧から復元や破棄を選択して実行できる機能があります。 こんな感じです。 Stashの一覧を見て、選択するだけでpopしています。 これをGitのコマンドでやろうと思うと、微妙にやり方忘れててhelp引いたり、Listのどの部分を引数に指定すればいいんだっけってなっていたので助かります。 あと、Gifだと確認しづらいですが、うっすらとpopやapplyなどにカーソル合わせた時に説明も右側に出しているので、popだっけapplyだっけってなっても安心です。 追記ですが、記事執筆中にStashの内容も機能追加しました? git ex discard 変更を削除したいとき、SourceTreeなどのGUIツールでは変更を破棄するDiscard処理(Reset処理)が簡単にできると思います。 ターミナルで変更を破棄したい場合は、 git checkout ファイル名 や、 git clean -df などでできますが、そのものずばりな操作な感じではないのでいつも迷います。 この git ex discard を使えば下のGif画像のようにインタラクティブに変更を破棄できます。 PRレビューでちょっと試した差分を破棄したいときとかに便利です。 このDiscard処理は破棄になるのでGitの履歴には残りません。ご使用には十分ご注意ください。 git ex rm-merged 最後に、微妙に便利な、マージ済みブランチを削除できる git ex rm-merged コマンドを紹介します。 マージ済みのブランチをいつまでも残しておくと、checkout(switch)するときに無駄に選択肢が残っていたり、名前被りしていいことなかったりします。 GithubではPRをマージしたタイミングでマージ済みブランチを削除できるのでリモートブランチは削除しやすいです。ですがローカルは別途自分で git branch -d ブランチ名 しなくてはならないので億劫になりがちです。 そこでこの git ex rm-merged を使うとマージ済みブランチをリスト表示してくれてマルチセレクトで削除することができます。 このGifでは1件なのですが、たくさんあって一括で削除したい時も複数選択できます。ちなみに、aをタイプするとallで全選択されます。 マージ済みブランチはmain(master)やdevelopも対象になるのですが、現状はハードコーディングで対象外にしています。親ブランチなどを誤って削除しないようにご注意ください。 実装面 nodeで動作するJavaScript製のツールです。最初はTypeScriptで実装していたのですが、紆余曲折あって今はJavaScriptで実装されています。 webpackやbabelなどのビルドツール的なものは一切使っていないシンプル構成になっています。 JavaScript(TypeScript)はだいぶ前に業務で触ってたことがある程度で、普段は書いてません。なので実装時間よりも結構色々調べるところのほうが時間がかかりました? JavaScriptとTypeScript 普段Kotlinを書いてますし、今までツールを書くときにはGoをよく使っています。 久しぶりに型定義のない言語も書いてみたくなったというのもあってTypeScriptではなくJavaScriptを選びました(笑) 正直それ以外の理由は基本的には特にないです。JavaScriptが書きたくなったので選んだって感じです? それにしても、型定義しないで書くの、なんだか魔術感があってわくわくしますね? 個人で書くには楽ちんで結構ありだよなとちょっと再認識もできました。 あとは、ここ最近のJavaScriptの進化も体感してみたいというのもありました。jQuery全盛時代から考えると本当に色々書きやすくなっていて、不満はなかったですね。。classも、自分が読んでいた本ではprototypeを使って頑張るイメージでしたが普通に使えますし、いつの間にかフィールドも普通に定義できるようになってるんですね。 ESモジュールとCommonJS Reactの開発経験はあったのでES6は知っていましたが、CommonJSはよくわかっていませんでした(意識したことなかった)。 端的にまとめると、nodeはCommonJSなのでrequireを使ってモジュールをインポートしますが、ブラウザサイドはESモジュールなのでimportを使います。 // CommonJS const a = require('module'); // ESモジュール import a from 'a'; 最初ここらへんの知識がなかったので微妙に苦戦しました。僕はESモジュールで書きたかったのですが、 nodeはESモジュールにも対応しているようだったので、package.jsonで指定しました。 { "type": "module", } これでnodeでもESモジュール形式になってimport形式になりました。 また、import側だけでなくてモジュールをexportする側の書き方もESモジュールとCommonJSで書き方が違うので注意が必要でした。 ESモジュール指定をしていれば下記のような形式で書くことができます。 export default class XXX {} package.jsonを読み込みたい コマンドラインツールなので、 --help でバージョン表示などをしたいのですが、ここは可能ならpackge.jsonで定義しているバージョン情報などを使いたいところです。 CommonJSであれば、requireでjsonを読み込むことができます。 const { version, description } = require("../package.json"); ですが、ESModuleでは上記のように読み込むことはできません? その場合は、requireを生成してあげることで取得することができました。 const require = createRequire(import.meta.url); const { version, description } = require("../package.json"); CLIツールとして動作させるために nodeのモジュールをCLIツールとして動作させるには、package.jsonのbinに実行パスを指定するのとシバンの指定をすればOKです。 package.json { "bin": { "git-ex": "./src/index.js" }, } "git-ex" の部分がコマンド名です。右辺側に実行するパスを指定します。TypeScriptなどでコンパイルしている場合はそちらを指定します。 TypeScriptでdistに出力しているような場合 package.json { "bin": { "git-ex": "./dist/index.js" }, } あとはその実行ファイルの先頭行にシバンの指定をしてあげます。 #! /usr/bin/env node import { Option, program } from "commander"; import { RmMerged } from "./command/rmMerged.js"; // 実装がつらつら シバンとはなんぞやという方はこちらをご覧ください。 要はそのスクリプトファイルを何で実行するかを定義してあげる必要がある感じです。 ちょっと注意点としては、webpackを使った際にこのシバンの定義が消えてしまっていて、npm installした際に動かなくなってしまいました。今回は結局そういうのは使わなかったのでいいのですが、使いたい場合は調べたほうが良さそうです(回避策はありそう?な感じでした) ソースコードからインストール 開発中は以下のように実行できます。 node ./src/index.js package.jsonのscriptに指定して、普通は npm start とかで実行するものかなと思うのですが、ソースコードからグローバルインストールするにはどうすればいいのでしょうか? 意外とググっても出てこなかったのですが、そのままカレントディレクトリを指定すればOKみたいです npm install -g ./ こうするとローカルのプロジェクトをグルーバルインストールできます。npmに公開しなくてもツールのインストールができるので便利ですね。 commander.js コマンドラインの解析ツールにはcommander.jsを使用しました。 引数のパース処理とか、オプション、サブコマンド、ヘルプの自動生成をしてくれるライブラリで、これを使えば誰でもCLIツールが作れるようになります(?) なにか自分でCLIツールを作りたいときは、その言語でこの手のライブラリを見つけることから始めるといいと思います。だいたいどの言語にも作ってくれている人がいると思うので、活用するのがおすすめです(自分でやろうと思ったらあまりにも大変だと思う。。) これは git-ex の一部ですが、こんな感じで引数のサブコマンドを指定してあげるだけでよしなに関数を呼び出してくれる感じです。しかも、ヘルプまで生成してくれます。 index.js program .command("switch") .description("switch branch") .option("-r, --remote", "switch remote branch") .action((options) => switchBranch.action(options.remote)); program .command("stage") .description("stage files") .addOption(instructionsOption) .action((options) => stage.action(options.instructions)); prompts 2つ目がpromptsです。git-exの核を担っているライブラリで、ターミナルでセレクターや入力などのプロンプトのUIを提供してくれているライブラリです。 正直、このライブラリが良さそうだったので、git-exはJavaScriptで書きました。 このライブラリとても良くできていて、書きやすく、動作もばっちしでした。 ただ、作者がTypeScriptを使っていないというのもあって、TypeScriptから使おうとすると苦戦するかもです。 READMEの書き方で書けないですし、記載もないので自分でPRかコードを見て把握する必要があります。また、ESCキーなどでのキャンセルの動作もサポートされているはずなのですが、TypeScriptで記述した場合Exceptionが返ってきました(笑) Gitの呼び出し部分 皆さんご存知Gitは、なかなか融通の効かない感じになっておりまして、git-exはGitを呼び出しては、わりとごにょごにょ工夫して実装している部分がおおいです(笑) Gitの呼び出しには child_process の execSync() と spawnSync() を使っています。 同期関数なので処理はブロックしてしまうのですが、ターミナルは止めても問題ないので特に気にせず使っています。 使い分けなのですが、Gitのコマンド実行の結果を画面に表示せず取得したいときは execSync で、Gitのコマンド結果を画面に出力したい場合は spawnSync を使用しています。 例えば、ブランチの取得ですが、実行するのは git branch です。これをspawnのほうで実行してしまうとターミナルに結果が出力されてしまうので、execのほうを使ってあげることで文字列として結果を取得することができます。 const branches = execSync("git branch").toString().split(/\n/); // 最後の空行はトリミング 今度はDiffの実行です。今度は、ユーザーが選択したファイルをもとにDiffの結果を出力してあげたいので、spawnを使ってあげます。 spawnSync("git", ["diff", ...files], { stdio: "inherit" }); spawnのほうは若干癖のある書き方の気がしますね。inheritの部分をなしで実行するとdiffの結果が色無しで出力されてしまいます。inheritをつけてあげると、そのまま git diff を実行したときと同じ結果が出力されるようになる感じです。 prettierとGithub Actions テストとかLinterとかは書いてないですし導入もしてないのですが、フォーマッターは欲しいなと思ってprettierを入れてみました。 特にこだわりはないのでprettierはデフォルトの設定のまま入れています。 npm i -d prettier # フォーマット実行 npx prettier --write 'src/**/*.js" VSCodeの自動保存でフォーマットをかけるか、PR前に忘れずに実行すればいいのですが、人間忘れがちなものです。 そこでGithub Actionsで prettier --check 'src/**/*.js' を実行するようにして、PR時にパスしてなければやり直してねって形にしています。 おわり npmに公開するCLIツールを作ったのは初めてだったのですが、かなり書きやすくていい感じでした。 npmの公開の仕方も簡単でしたし、保守もやりやすそうです。 今後別のツールを作るときもしばらくはnodeで作ろうかなと思います。 慣れておけばWebのフロントエンドやサーバーサイドも書けるのでやっぱり便利よなって思いました。 git-ex自体も機能追加していって便利にしていく予定です✨
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Dockerで安全にnode.jsウェブアプリをコンテナ化する

Happy New Year! 年末、年始があっという間に終わり、明日は成人の日。 来週からコーディングのオンラインクラスを受けることになった。4−6ヶ月になりそうであるが、無事乗り切れるのか、少々不安も。javascriptを習得するコースなため、最終的にnode.jsのサーバーサイドでのコーディングもできるようになるまでの知識を得られるよう頑張ろう。node.jsの環境構築に不可欠ともいえるdocker。 今回は、10 best practices to containerize Node.js web applications with Docker の翻訳記事のご紹介です。 今回は、特に翻訳に苦労しました。読みにくい部分もあると思いますが、どうぞ最後までお付き合いください。 Dockerでnode.jsウェブアプリケーションをコンテナ化するための10のベストプラクティス LiranTal、Yoni Goldberg 2021年1月13日 Webアプリケーション用のNode.js の Dockerイメージの構築方法に関するベストプラクティスについて説明します。 本記事では、本番環境を想定した安全かつ最適化されたNode.js の Dockerイメージを構築するためのガイドラインを提供します。Node.jsアプリケーションを構築する場合において、以下のようなケースで役立つ内容を説明します。 React にサーバーサイドレンダリング(SSR)Node.js 機能を使用してフロントエンドアプリケーションを構築することを考えている Fastify や NestJS、その他のアプリケーションフレームワークを実行させるマイクロサービス用のNode.js の Dockerイメージの正しいビルド方法についてアドバイスがほしい このガイドを書いた理由 Node.jsアプリケーション用のDockerイメージを構築する方法は他のブログなどでも見つけることができます。しかし、多くの記事は単に Node.js の Docker イメージにてアプリケーションを実行することしか説明しておらず、セキュリティやベストプラクティスについての慎重な検討がされていませんでした。 本記事では、Node.js の Web アプリケーションをコンテナ化する方法をステップ・バイ・ステップで学習することができます。まず、シンプルに機能する Dockerfile から始めて、 Dockerfile ディレクティブ における落とし穴や危険性について理解した後、修正をしていきます。 こちらから英語チートシートをダウンロードしてください。 シンプルな Node.js の Dockerイメージの構築 よく見かけるブログ記事の多くは、以下の Node.js の Docker イメージ構築のための基本的な Dockerfile コマンドの開始と終了の説明しかしていません。 FROM node WORKDIR /usr/src/app COPY . /usr/src/app RUN npm install CMD "npm" "start" Dockerfile という名前のファイルにコピーして 、ビルドして実行します。 $ docker build . -t nodejs-tutorial $ docker run -p 3000:3000 nodejs-tutorial とてもシンプルかつ動作もしますが、Node.js の Docker イメージを構築する際の間違いや悪習慣が含まれていますので、上記方法は絶対に避けてください。 では、 Docker にて最適化された Node.js Web アプリケーションを構築できるように、このDockerfileの改善を始めましょう。 このリポジトリをクローンすることで、チュートリアルを進める事ができます。 1. 明示的で決定論的な Docker ベースイメージタグを使用 node Docker イメージをベースにイメージを構築するのは当然のことのように思えるかもしれませんが、イメージを構築するときに実際には何をプルしているのでしょうか?Dockerイメージは常にタグで参照され、タグを指定しない場合はデフォルトである :latest タグが使用されます。 そのため、実際には、Dockerfile に以下のように指定することで、Node.js の Docker ワーキンググループでビルドされた最新版の Docker イメージを常にビルドすることができます。 node からビルド デフォルトの node イメージをベースにビルドする場合の欠点は以下の通りです。 Docker イメージのビルドの一貫性が保てない。ちょうど、npmパッケージをインストールするときに、常に決定論的な npm install の動作を行うために lockfiles を使うように、決定論的な docker イメージビルドを得取する方法を取りたいと考えます。イメージをnodeからビルドした場合、これは node:latest タグがあることを意味し、ビルドするたびに node の新たにビルドされた Docker イメージをプルしてしまいます。このような非決定論的な動作が伴う導入は推奨できません。 node の Docker イメージは Node.js の Web アプリケーションを実行するために必要なライブラリやツールが満載であるフルサイズのオペレーティングシステムをベースにしています。これには2つのデメリットがあります。まず、イメージが大きくなるということです。ダウンロードサイズも大きくなり、ストレージの必要性が増すだけでなく、イメージのダウンロードと再構築にも時間がかかります。次に、これらのライブラリやツールに存在するかもしれないセキュリティ上の脆弱性をイメージに取り込んでしまう可能性もあります。 実際、 node の Docker イメージは非常に大きく、様々な種類と深刻度が入り混じった数百ものセキュリティの脆弱性を含んでいます。以下の表にもあるように、node を使っている場合、デフォルトで 642個のセキュリティ脆弱性があるイメージが出発点となり、プルやビルドのたびに何百メガバイトものイメージデータがダウンロードされることになります。 より優れた Docker イメージを構築するための推奨事項: 小さな Docker イメージを使用する これにより、Docker イメージのソフトウェアフットプリントが小さくなり、潜在的な脆弱性が減少します。また、サイズが小さくなるため、イメージの構築プロセスが高速化されます。 イメージの静的 SHA256 ハッシュである Docker イメージダイジェストを使用する これにより、ベースイメージから決定論的な Docker イメージビルドを確実に取得できます。 この方針に基づいて、長期サポート (LTS) バージョンの Node.js を使用し、イメージ上のサイズとソフトウェアのフットプリントが最小になるように、最小限の alpine イメージタイプを使用するようにしましょう。 FROM node:lts-alpine これでもまだ、このベースイメージディレクティブは、そのタグの新しいビルドをプルします。その SHA256 ハッシュは、このNode.jsタグの Docker Hubで見つけることができます。また、このイメージをローカルに取り込んだ後、次のコマンドを実行して、出力に Digest フィールドを配置します。 $ docker pull node:lts-alpine lts-alpine: Pulling from library/node 0a6724ff3fcd: Already exists 9383f33fa9f3: Already exists b6ae88d676fe: Already exists 565e01e00588: Already exists Digest: sha256:b2da3316acdc2bec442190a1fe10dc094e7ba4121d029cb32075ff59bb27390a Status: Downloaded newer image for node:lts-alpine docker.io/library/node:lts-alpine SHA256 ハッシュを見つけるための別の方法として、 次のコマンドを実行します。 $ docker images --digests REPOSITORY TAG DIGEST IMAGE ID CREATED SIZE node lts-alpine sha256:b2da3316acdc2bec442190a1fe10dc094e7ba4121d029cb32075ff59bb27390a 51d926a5599d 2 weeks ago 116MB これで、このNode.js の Docker イメージの Dockerfile を次のように更新できます。 FROM node@sha256:b2da3316acdc2bec442190a1fe10dc094e7ba4121d029cb32075ff59bb27390a WORKDIR /usr/src/app COPY . /usr/src/app RUN npm install CMD "npm" "start" しかし、上記の Dockerfile では、イメージタグを使わずに Node.js の Docker イメージの名前だけを指定しているため、実際どのイメージタグが使われているのかが曖昧になっています。これでは可読性に欠けるため、メンテナンスが難しく、デベロッパにとって良いユーザーエクスペリエンスとはいえません。 この問題を解決するために、Dockerfile を更新し、 SHA256 ハッシュに対応する Node.js のバージョンの完全なベースイメージタグを提供しましょう。 FROM node:lts-alpine@sha256:b2da3316acdc2bec442190a1fe10dc094e7ba4121d029cb32075ff59bb27390a WORKDIR /usr/src/app COPY . /usr/src/app RUN npm install CMD "npm" "start" 2. Node.js の Docker イメージに本番用で必要な依存関係のみをインストールする 以下 Dockerfileディレクティブは、アプリケーションの機能的な動作には必要のない devDependencies を含むすべての依存関係をコンテナにインストールします。この方法では、開発の依存関係として使用されるパッケージに不要なセキュリティリスクが追加されるだけでなく、イメージサイズも不必要に肥大化してしまいます。 RUN npm install 以前紹介したnpmのセキュリティ10のベストプラクティスから、決定論的なnpm ci ビルドの必要性をご理解していただけたと思います。lockfile から逸脱すると停止するため、継続的インテグレーション(CI)フローでの予期しない挙動を防ぐことができます。 本番用のDockerイメージを構築する場合は、本番用の依存関係のみを確実にインストールしたいので、コンテナイメージに npm の依存関係をインストールする際のベストプラクティスとして以下を推奨します。 RUN npm ci --only=production この段階で更新された Dockerfile の内容は以下の通りです。 FROM node:lts-alpine@sha256:b2da3316acdc2bec442190a1fe10dc094e7ba4121d029cb32075ff59bb27390a WORKDIR /usr/src/app COPY . /usr/src/app RUN npm ci --only=production CMD "npm" "start" 3. Node.jsのツールを本番用に最適化する Node.js の Docker イメージを本番用に構築する際、すべてのフレームワークとライブラリが、パフォーマンスとセキュリティのために最適な設定になっているか確認する必要があります。 そのため、以下のDockerfile ディレクティブを追加する必要があります。 ENV NODE_ENV production npm install の段階で既に本番用の依存関係のみを指定しているので、一見すると冗長に見えますが、なぜ必要なのでしょうか? デベロッパは、NODE_ENV=production 環境変数の設定を、本番環境に関連する依存関係のインストールと関連付けることが多いのですが、この設定には他の影響もあるため、注意が必要です。 フレームワークやライブラリの中には、 NODE_ENV 環境変数が production に設定されていると、本番環境に適した最適な構成でしか動作しないものがあります。これがフレームワークにとって良いことなのか悪いことなのかという意見はさておき、このことを知っておくことは重要です。 例えば、 Express documentation では、パフォーマンスやセキュリティ関連の最適化を有効にするために、この環境変数を設定することの重要性が説明されています。 NODE_ENV 変数はパフォーマンスに大きく影響します。 Dynatrace のデベロッパたちが、ExpressアプリケーションでNODE_ENVを省略した場合の劇的な影響についての詳細をブログ記事で説明しています。 依存している他のライブラリの多くもこの変数が設定されていることを想定している可能性もあるため、Dockerfile にこの変数を設定する必要があります。 アップデートした Dockerfile は、NODE_ENV 環境変数の設定が組み込まれた以下のような内容になります。 FROM node:lts-alpine@sha256:b2da3316acdc2bec442190a1fe10dc094e7ba4121d029cb32075ff59bb27390a ENV NODE_ENV production WORKDIR /usr/src/app COPY . /usr/src/app RUN npm ci --only=production CMD "npm" "start" 4. コンテナを root で実行しない 最小権限の原則は、Unix の黎明期から長く続いているセキュリティ管理であり、コンテナ化された Node.js の Web アプリケーションを実行する際には、常にこれに従うべきです。 脅威の評価は非常にわかりやすいものです。攻撃者が、コマンドインジェクションやディレクトリパストラバーサルを可能にする方法で web アプリケーションを侵害することができた場合、これらはアプリケーション・プロセスを所有するユーザで起動されます。そのプロセスがたまたま root であった場合、攻撃者はコンテナ内で事実上すべてのことを行うことができ、コンテナのエスケープや権限昇格を試みることも可能となります。わざわざ、そのようなリスクを冒す必要はありません。 非常に重要:「コンテナを root として実行させないこと!」 公式の node Dockerイメージや、alpine のようなバリアントには、同じ名前の最小権限ユーザーである node が含まれています。しかし、 node としてプロセスを実行するだけでは十分ではありません。例えば、次のような場合、アプリケーションがうまく機能するためには理想的ではないかもしれません。 USER node CMD "npm" "start" 理由は、USER Dockerfile ディレクティブは、プロセスの所有者が node ユーザーであることを保証するだけだからです。それ以前に COPY 命令でコピーしたすべてのファイルについて考えてみましょう。それらは root が所有しています。これが Docker のデフォルトの動作です。 権限の完全かつ適切な削除方法は以下の通りで、この時点までの最新のDockerfileプラクティスについても示しています。 `FROM node:lts-alpine@sha256:b2da3316acdc2bec442190a1fe10dc094e7ba4121d029cb32075ff59bb27390a ENV NODE_ENV production WORKDIR /usr/src/app COPY --chown=node:node . /usr/src/app RUN npm ci --only=production USER node CMD "npm" "start"@ 5. イベントを適切に処理してNode.jsのDocker Webアプリケーションを安全に終了させる Node.js アプリケーションを Docker コンテナで実行する際のコンテナ化に関するブログや記事でよく見かける間違いの1つは、プロセスの呼び出し方法です。以下や類似する方法は、全て避けるべき悪いパターンです。 CMD "npm" "start" CMD ["yarn", "start"] CMD "node" "server.js" CMD "start-app.sh" それでは、それぞれの違いと、なぜ全て避けるべきパターンなのかを説明します。 Node.jsのDockerアプリケーションを適切に実行・終了させるための背景を理解るためには、以下の点が重要になります。 Docker Swarm や Kubernetes、あるいは Docker エンジン自体などのオーケストレーションエンジンには、コンテナ内のプロセスにシグナルを送る方法が必要です。ほとんどの場合、これらは SIGTERM や SIGKILL のような、アプリケーションを終了させるためのシグナルです。 プロセスは間接的に実行される可能性があり、その場合、これらのシグナルを受け取ることは必ずしも保証されません。 Linuxカーネルでは、プロセスID1(PID)として実行されるプロセスは、他のプロセスIDとは異なる扱いになります。 この知識を念頭におき、私たちがビルドしている Dockerfile の例から、コンテナのプロセスを呼び出す方法について詳しくみていきましょう。 CMD "npm" "start" ここでの注意点は2つあります。まず、npm クライアントを直接起動することで、間接的にnodeアプリケーションを実行していることです。npm CLI がすべてのイベントを node ランタイムに転送するとは限りません。それを簡単にテストすることができます。 Node.js アプリケーションで、イベントを送信するたびにコンソールにログを記録するSIGHUP シグナル用のイベントハンドラを設定していることを確認してください。簡単なコード例は以下のようになります。 function handle(signal) { console.log(`*^!@4=> Received event: ${signal}`) } process.on('SIGHUP', handle) 次に、コンテナを実行し、起動したら、 docker CLIと特別な --signal コマンドラインフラグを使用して、SIGHUP シグナルを送信します。 $ docker kill -signal=SIGHUP elastic_archimedes 何も起こりませんでした。これは、npmクライアントが、自分が生成したnodeプロセスにシグナルを転送していないからです。 もう1つの注意点は、Dockerfileに CMD ディレクティブを指定する方法が異なることです。2つの方法があり、それらは同じではありません。 シェルフォーム表記では、コンテナがプロセスをラップするシェルインタープリタを起動します。この場合、シェルはシグナルをプロセスに適切に転送しない可能性があります。 execform 表記:シェルをラップせずに直接プロセスを起動します。次のようなJSON 配列表記で指定します。 コンテナに送られたシグナルは、そのままプロセスに送られます。例: CMD ["npm", "start"] その知識に基づいて、Dockerfile のプロセス実行指令を次のように改善します。 CMD ["node", "server.js"] これで node プロセスを直接起動することになり、シェルインタプリタにラップされることなく、送られてきたすべてのシグナルを確実に受け取ることができます。 しかし、これには別の落とし穴があります。 プロセスが PID 1として実行されると、事実上、OSやプロセスの初期化を担当するinit システムの責任の一部を負うことになります。カーネルは、PID 1を他のプロセス識別子とは異なる方法で扱います。カーネルがこのように特別な扱いをするということは、実行中のプロセスに対する SIGTERM シグナルの処理では、プロセスがまだハンドラーを設定していない場合、プロセスを停止するというデフォルトのフォールバック動作が呼び出されないことを意味します。 Node.jsのDockerワーキンググループの勧告を引用がこちらです。「Node.jsはPID 1で動作するように設計されていないため、Docker 内で動作する際に予期せぬ動作を引き起こします。例えば、PID 1として実行されているNode.js プロセスは、SIGINT (CTRL-C) や同様のシグナルに反応しません。」 このため、init プロセスのような動作をする、PID 1で呼び出され、Node.js アプリケーションを別のプロセスとして起動し、すべてのシグナルが Node.js プロセスにプロキシされるようにするツールを使用します。可能であれば、コンテナイメージにセキュリティの脆弱性が追加されないように、ツールのフットプリントをできるだけ小さくする必要があります。 Snyk では、スタティックリンクでフットプリントが小さい dumb-init を使っています。ここでは、その設定方法を紹介します。 RUN apk add dumb-init CMD ["dumb-init", "node", "server.js"] これで、以下のような最新の Dockerfile ができあがりました。イメージ宣言の直後にdumb-init パッケージのインストールを配置しています。これにより、Docker のレイヤーのキャッシュを利用することができます。 FROM node:lts-alpine@sha256:b2da3316acdc2bec442190a1fe10dc094e7ba4121d029cb32075ff59bb27390a RUN apk add dumb-init ENV NODE_ENV production WORKDIR /usr/src/app COPY --chown=node:node . . RUN npm ci --only=production USER node CMD ["dumb-init", "node", "server.js"] 注意:docker kill や docker stop コマンドは、PID 1のコンテナプロセスにしかシグナルを送らないということです。シェルスクリプトで Node.js アプリケーションを実行している場合、例えば /bin/sh のようなシェルインスタンスは子プロセスにシグナルを転送しないので、アプリケーションが SIGTERM を受け取ることはありません。 6. Node.js ウェブアプリケーションの適切なシャットダウン アプリケーションを終了させるプロセス・シグナルについてはすでに説明しましたが、ユーザーに迷惑をかけずに適切にシャットダウンしていることを確認しましょう。 Node.js アプリケーションが、SIGINT、CTRL+Cのような割り込み信号を受信すると、別の動作で処理するようにイベントハンドラが設定されていない限り、突然の kill プロセスが行われます。これは、ウェブアプリケーションに接続されているクライアントが直ちに切断されることを意味します。ここで、Kubernetes によってオーケストレーションされた何百もの Node.js の Web コンテナが、スケールアップやエラー管理のために必要に応じてアップダウンする様子を想像してみてください。決して良いユーザーエクスペリエンスとは言えません。 この問題を簡単にシミュレーションすることができます。ここでは、Fastify Web アプリケーションの例として、エンドポイントの応答が 60 秒遅れることを想定しています。 fastify.get('/delayed', async (request, reply) => { const SECONDS_DELAY = 60000 await new Promise(resolve => { setTimeout(() => resolve(), SECONDS_DELAY) }) return { hello: 'delayed world' } }) const start = async () => { try { await fastify.listen(PORT, HOST) console.log(`*^!@4=> Process id: ${process.pid}`) } catch (err) { fastify.log.error(err) process.exit(1) } } start() このアプリケーションを実行して、このエンドポイントにシンプルな HTTP リクエストを送信します。 $ time curl https://localhost:3000/delayed 実行中のNode.js のコンソールウィンドウで CTRL+C を押すと、curl リクエストが突然終了したことがわかります。これは、コンテナがダウンしたときにユーザーが受けるのと同じ体験をシミュレートしたものです。 より良い体験を提供するために、次のようにすることができます。 1.SIGINT や SIGTERM のような様々な終了シグナルのイベントハンドラを設定する。 2.ハンドラは、データベース接続や進行中の HTTP リクエストなどのクリーンアップ操作を待ちます。 3.ハンドラはその後、Node.js プロセスを終了させます。 特に Fastify では、ハンドラが fastify.close() を呼び出し、待ち受けるプロミスを返すようにできます。また、Fastify はアプリケーションが利用できないことを示すために、すべての新しい接続に対して HTTP ステータスコード 503 で応答します。 それでは、ここでイベントハンドラを追加します。 async function closeGracefully(signal) { console.log(`*^!@4=> Received signal to terminate: ${signal}`) await fastify.close() // await db.close() if we have a db connection in this app // await other things we should cleanup nicely process.exit() } process.on('SIGINT', closeGracefully) process.on('SIGTERM', closeGracefully) 上記は Dockerfile に関連するというよりも、一般的な Web アプリケーションに関する問題ですが、オーケストレーション環境ではさらに重要になります。 7. Node.jsのDockerイメージのセキュリティ脆弱性を見つけて修正する Node.js アプリケーションのための小さな Docker ベースイメージの重要性について説明しました。このテストを実践してみましょう。 Snyk CLI を使ってDockerイメージをテストしてみようと思います。Snykの無料アカウントは こちらからサインアップできます。 $ npm install -g snyk $ snyk auth $ snyk container test node@sha256:b2da3316acdc2bec442190a1fe10dc094e7ba4121d029cb32075ff59bb27390a --file=Dockerfile 最初のコマンドでSnyk CLIをインストールし、続いてAPI キーを取得するためにコマンドラインから簡単なサインインフローを行い、セキュリティ上の問題がないかコンテナをテストします。以下はその結果です。 Organization: snyk-demo-567 Package manager: apk Target file: Dockerfile Project name: docker-image|node Docker image: node@sha256:b2da3316acdc2bec442190a1fe10dc094e7ba4121d029cb32075ff59bb27390a Platform: linux/amd64 Base image: node@sha256:b2da3316acdc2bec442190a1fe10dc094e7ba4121d029cb32075ff59bb27390a ✓ Tested 16 dependencies for known issues, no vulnerable paths found. Snyk は、当社の Node.js ランタイム実行ファイルを含む 16 のオペレーティングシステムの依存関係を検出し、脆弱性のあるバージョンは見つかりませんでした。 では、以下のような FROM node ベースイメージディレクティブを使っていたらどうなっていたでしょうか? FROM node:14.2.0-slim Node.js のバージョンを明確にし、 slim タイプのイメージを使用しているので、Docker イメージ内の依存関係のフットプリントが小さくなっています。では、Snyk でテストしてみましょう。 … ✗ High severity vulnerability found in node Description: Memory Corruption Info: https://snyk.io/vuln/SNYK-UPSTREAM-NODE-570870 Introduced through: node@14.2.0 From: node@14.2.0 Introduced by your base image (node:14.2.0-slim) Fixed in: 14.4.0` ✗ High severity vulnerability found in node Description: Denial of Service (DoS) Info: https://snyk.io/vuln/SNYK-UPSTREAM-NODE-674659 Introduced through: node@14.2.0 From: node@14.2.0 Introduced by your base image (node:14.2.0-slim) Fixed in: 14.11.0` Organization: snyk-demo-567 Package manager: deb Target file: Dockerfile Project name: docker-image|node Docker image: node:14.2.0-slim Platform: linux/amd64 Base image: node:14.2.0-slim Tested 78 dependencies for known issues, found 82 issues. Base Image Vulnerabilities Severity node:14.2.0-slim 82 23 high, 11 medium, 48 low Recommendations for base image upgrade: Minor upgrades Base Image Vulnerabilities Severity node:14.15.1-slim 71 17 high, 7 medium, 47 low Major upgrades Base Image Vulnerabilities Severity node:15.4.0-slim 71 17 high, 7 medium, 47 low Alternative image types Base Image Vulnerabilities Severity node:14.15.1-buster-slim 55 12 high, 4 medium, 39 low node:14.15.3-stretch-slim 71 17 high, 7 medium, 47 low FROM node:14.2.0-slim などの特定の Node.js ランタイムバージョンであれば、安全であるように見えますが、Snyk は2つの主要なソースからセキュリティの脆弱性を見つけました。 1.Node.js ランタイム自体の脆弱性 — Snyk のレポートには 2つの主要なセキュリティの脆弱性が表示されています。これらは、Node.js ランタイムについての既知のセキュリティの問題で、Node.js バージョンをアップグレードすることで即時に修正可能です。Snyk はレポートで修正されたバージョン (14.11.0) についても通知しています。 2.glibc、bzip2、gcc、perl、bash、tar、libcrypt など、この debian ベースイメージにインストールされているツールやライブラリなどです。コンテナ内のこれらの脆弱なバージョンはすぐには脅威にならないかもしれませんが、使わないのに持っている理由を考える必要があります。 この Snyk CLI レポートの一番いいところは、他のベースイメージに切り替えることも Snyk が推奨してくれるところです。代替のイメージを探すのは非常に時間がかかる可能性がありますが、Snyk はその手間を省いてくれます。 ここでの私の推奨は以下の通りです。 1.Docker Hub や Artifactory などのレジストリで Docker イメージを管理している場合は、それらを Snyk にインポートして、プラットフォームがこれらの脆弱性を検出できるようにします。これにより、新たに発見されたセキュリティ脆弱性について継続的に Docker イメージを監視するだけでなく、Snyk の UI で推奨アドバイスを提供することもできます。 2.CI の自動化に Snyk CLI を使いましょう。非常に柔軟性の高い Snyk CLI は、任意のカスタムワークフローに適用することができます。お好みに応じて Snyk for GitHub Actionsも用意しています。 8. マルチステージ・ビルドの使用 マルチステージビルドは、シンプルでありながらエラーが発生する可能性のある Dockerfile から、Docker イメージを構築するステップを分離して、機密情報の漏洩を防ぐことができる優れた方法です。それだけでなく、より大きな Docker ベースイメージを使って依存関係をインストールし、必要であればネイティブの npm パッケージをコンパイルし、alpine の例のように、全てのアーティファクトを小さな本番ベースのイメージにコピーすることができます。 機密情報漏洩の防止 ここで紹介する機密情報の漏洩回避のユースケースは、思ったよりも一般的です。 もしあなたが仕事でDockerイメージを構築しているなら、プライベートな npm パッケージも管理している可能性が高いでしょう。もしそうであれば、おそらくその秘密のNPM_TOKENを npm のインストール時に利用できるようにするための何らかの方法を見つける必要があるでしょう。 こちらがその例です。 FROM node:lts-alpine@sha256:b2da3316acdc2bec442190a1fe10dc094e7ba4121d029cb32075ff59bb27390a RUN apk add dumb-init ENV NODE_ENV production ENV NPM_TOKEN 1234 WORKDIR /usr/src/app COPY --chown=node:node . . #RUN npm ci --only=production RUN echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > .npmrc && \ npm ci --only=production USER node CMD ["dumb-init", "node", "server.js"] しかし、これを行うと、秘密の npm トークンを含む .npmrc ファイルが Docker イメージ内に残ってしまいます。以下のように、後から削除することで改善を試みることができます。 RUN echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > .npmrc && \ npm ci --only=production RUN rm -rf .npmrc しかし、今度は .npmrc ファイルが Docker イメージの別のレイヤーで利用できるようになりました。もし、このDocker イメージが公開されていたり、誰かが何らかの方法でアクセスできてしまうと、あなたのトークンは危険にさらされます。より良い改善策は以下のようになります。 RUN echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > .npmrc && \ npm ci --only=production; \ rm -rf .npmrc ここで問題となるのは、Dockerfile 自体が秘密の npm トークンを含んでいるため、Dockerfile 自体をシークレットアセットとして扱う必要があることです。 幸いなことに、Docker はビルドプロセスに引数を渡す方法をサポートしています。 ARG NPM_TOKEN RUN echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > .npmrc && \ npm ci --only=production; \ rm -rf .npmrc そして、以下のようにビルドします。 $ docker build . -t nodejs-tutorial --build-arg NPM_TOKEN=1234 この時点で全てが完了したと思われたでしょうが、残念ながらそうではありません。 セキュリティの世界では、当たり前のことが別の落とし穴になることもあるのです。 さて、何が問題なのでしょうか?このように Docker に渡されたビルド引数は、履歴ログに残ります。自分の目で確かめてみましょう。次のコマンドを実行してください。 $ docker history nodejs-tutorial 実行すると、次のように表示されます。 IMAGE CREATED CREATED BY SIZE COMMENT b4c2c78acaba About a minute ago CMD ["dumb-init" "node" "server.js"] 0B buildkit.dockerfile.v0 <missing> About a minute ago USER node 0B buildkit.dockerfile.v0 <missing> About a minute ago RUN |1 NPM_TOKEN=1234 /bin/sh -c echo "//reg… 5.71MB buildkit.dockerfile.v0 <missing> About a minute ago ARG NPM_TOKEN 0B buildkit.dockerfile.v0 <missing> About a minute ago COPY . . # buildkit 15.3kB buildkit.dockerfile.v0 <missing> About a minute ago WORKDIR /usr/src/app 0B buildkit.dockerfile.v0 <missing> About a minute ago ENV NODE_ENV=production 0B buildkit.dockerfile.v0 <missing> About a minute ago RUN /bin/sh -c apk add dumb-init # buildkit 1.65MB buildkit.dockerfile.v0 そこにシークレットのnpmトークンがあるのがわかりましたか?そこが問題です。 コンテナイメージのシークレットを管理するための優れた方法はありますが、今回はこの問題の緩和策としてマルチステージビルドを導入するとともに、ミニマムイメージを構築する方法を紹介します。 Node.jsのDockerイメージへのマルチステージビルドの導入 ソフトウェア開発における「関心事の分離」の原則と同じように、Node.js の Docker イメージを構築するためにも同じ考え方を適用します。Node.js の世界では、npm パッケージをインストールし、必要に応じてネイティブの npm モジュールをコンパイルします。これが最初のステージとなります。 Docker ビルドの第2ステージを表す2つ目のDocker イメージは、本番用のDockerイメージになります。この2番目で最後のステージは、実際に最適化してレジストリがあればそこに公開するイメージです。 build イメージと呼ばれる最初のイメージは廃棄され、クリーンアップされるまでビルドしたDocker ホストにぶら下がったままのイメージとして残されます。 これまでの進捗状況を表す Dockerfile の更新は次のとおりですが、2つのステージに分かれています。 # --------------> The build image FROM node:latest AS build ARG NPM_TOKEN WORKDIR /usr/src/app COPY package*.json /usr/src/app/ RUN echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > .npmrc && \ npm ci --only=production && \ rm -f .npmrc # --------------> The production image FROM node:lts-alpine@sha256:b2da3316acdc2bec442190a1fe10dc094e7ba4121d029cb32075ff59bb27390a RUN apk add dumb-init ENV NODE_ENV production USER node WORKDIR /usr/src/app COPY --chown=node:node --from=build /usr/src/app/node_modules /usr/src/app/node_modules COPY --chown=node:node . /usr/src/app CMD ["dumb-init", "node", "server.js"] ご覧の通り、 build ステージでは、ネイティブの npm パッケージをコンパイルするために gcc (GNU Compiler Collection) などのツールが必要になるかもしれないので、大きなイメージを選びました。 第2ステージでは、 COPY 指示のための特別な表記があり、ビルド用 Docker イメージからnode_modules/フォルダをこの新しい本番 ベースイメージにコピーします。 また、今、 build 中間 Docker イメージに ビルド引数として NPM_TOKEN が渡されています。これは本番の Docker イメージには存在しないので、 docker history nodejs-tutorial コマンドの出力にはもう表示されていません。 9. Node.jsのDockerイメージから不要なファイルを排除する 不要なファイルや潜在的な機密ファイルで git リポジトリを汚染しないように、.gitignore ファイルを用意していますよね?同じことが Docker イメージにも当てはまります。 Docker には .dockerignore があり、これはDocker デーモンへの glob パターンマッチの送信をスキップするようになっています。ここでは、Docker イメージに何を入れているのか、理想的には避けたいファイルのリストを紹介します。 .dockerignore node_modules npm-debug.log Dockerfile .git .gitignore ご覧のとおり、 node_modules/ をスキップすることは実際には非常に重要です。なぜなら、もしこれを無視していなかったら、最初に行った単純な Dockerfile バージョンでは、ローカルの node_modules/ フォルダがそのままコンテナにコピーされてしまうからです。 FROM node@sha256:b2da3316acdc2bec442190a1fe10dc094e7ba4121d029cb32075ff59bb27390a WORKDIR /usr/src/app COPY . /usr/src/app RUN npm install CMD "npm" "start" 実際、複数ステージの Docker ビルドを実践する際には、 .dockerignore ファイルを用意することがさらに重要になります。第2ステージの Docker ビルドがどのようなものか、以下おさらいとして。 # --------------> The production image FROM node:lts-alpine RUN apk add dumb-init ENV NODE_ENV production USER node WORKDIR /usr/src/app COPY --chown=node:node --from=build /usr/src/app/node_modules /usr/src/app/node_modules COPY --chown=node:node . /usr/src/app CMD ["dumb-init", "node", "server.js"] .dockerignore を持つことの重要性は、2番目の Dockerfile ステージから COPY . /usr/src/app を行うと、ローカルの node_modules/ もDockerイメージにコピーされてしまいます。これは、node_modules/ 内の修正されたソースコードをコピーしてしまう可能性があるため、絶対避けるべきです。 さらに、ワイルドカードの COPY .を使用しているため、認証情報やローカル設定を含む機密ファイルを Docker イメージにコピーしている可能性もあります。 ここでの .dockerignoreファイルの要点は以下の通りです。 1.Docker イメージ内の node_modules/ の潜在的に変更されたコピーをスキップします。 2..env や aws.json ファイルの内容に含まれる認証情報が Node.js の Docker イメージに入ってしまうようなシークレットの暴露を防止します。 キャッシュ無効化の原因となるようなファイルを無視するため、Docker ビルドの高速化に貢献します。例えば、ログファイルが変更されたり、ローカル環境の設定ファイルが変更されたりすると、ローカルディレクトリをコピーした段階で、Docker イメージのキャッシュが無効になります。 10. シークレットをDockerビルドイメージにマウントする .dockerignore ファイルについて注意すべき点は、オール・オア・ナッシングのアプローチしかできず、Docker マルチステージビルドにおいてビルドステージごとにオン/オフを切り替えることはできないということです。 なぜそれが重要になるのでしょうか? 理想的には、ビルド段階で .npmrc ファイルを使用することが推奨されます。これは、プライベートな npm パッケージにアクセスするための秘密の npm トークンが含まれているため、必要になる場合があるためです。また、パッケージを取得するために、特定のプロキシやレジストリの設定が必要になることもあります。 つまり、 build ステージで .npmrc ファイルを利用できるようにしておくことは理にかなっていますが、本番イメージのための第2ステージでは全く必要ありませんし、シークレット npm トークンなどの機密情報が含まれている可能性があるため利用したくありません。 この .dockerignore の注意点を軽減する一つの方法は、ビルドステージで利用可能なローカルファイルシステムをマウントすることですが、もっと良い方法があります。 Docker は Docker secrets と呼ばれる比較的新しい機能をサポートしており、.npmrc で必要とされるケースに適しています。その仕組みを紹介します。 docker build コマンドを実行する際には、新しいシークレット ID を定義し、シークレットのソースとなるファイルを参照するコマンドライン引数を指定します。 Dockerfileでは、 RUN ディレクティブにフラグを追加して、本番 npm をインストールします。この npm は、シークレットIDで参照されたファイルを、目的の場所(ローカルディレクトリの .npmrc ファイル)にマウントします。 .npmrc ファイルはシークレットとしてマウントされ、Docker イメージにコピーされることはありません。 最後に、.npmrc ファイルを .dockerignore ファイルの内容に追加することを忘れないでください。そうすれば、ビルドイメージでも本番イメージでも、イメージには一切組み込まれなくなります。 これらがどのように機能するか見てみましょう。まず、更新された .dockerignore ファイルです。 .dockerignore node_modules npm-debug.log Dockerfile .git .gitignore .npmrc 次に、完全な Dockerfile と、 .npmrc マウントポイントを指定しながら npm パッケージをインストールするために更新された RUN ディレクティブです。 # --------------> The build image FROM node:latest AS build WORKDIR /usr/src/app COPY package*.json /usr/src/app/ RUN --mount=type=secret,mode=0644,id=npmrc,target=/usr/src/app/.npmrc npm ci --only=production # --------------> The production image FROM node:lts-alpine RUN apk add dumb-init ENV NODE_ENV production USER node WORKDIR /usr/src/app COPY --chown=node:node --from=build /usr/src/app/node_modules /usr/src/app/node_modules COPY --chown=node:node . /usr/src/app CMD ["dumb-init", "node", "server.js"] そして最後に、Node.js の Docker イメージをビルドするコマンドです。 docker build . -t nodejs-tutorial --secret id=npmrc,src=.npmrc 注:Secrets は Docker の新機能であり、古いバージョンを使用している場合は、Buildkit を有効にする必要があるかもしれません。 $ DOCKER_BUILDKIT=1 docker build . -t nodejs-tutorial --build-arg NPM_TOKEN=1234 --secret id=npmrc,src=.npmrc まとめ これで、最適化された Node.js の Docker ベースイメージを作成するところまでが終わりました。 この最後のステップで、Node.js の Docker ウェブアプリケーションのコンテナ化に関するこのガイド全体が終了しました。パフォーマンスとセキュリティに関する最適化を考慮して、プロダクショングレードの Node.js の Docker イメージを確実に構築することができます。 フォローアップの資料をぜひご覧ください。 10 Docker image security best practices→参考翻訳記事 Docker for Java Developers: 5 things you need to know not to fail your security→参考翻訳記事 Node.js アプリケーション用に安全でパフォーマンスの高い Docker ベースイメージを構築したら、Snyk の無料アカウントでコンテナの脆弱性を見つけて修正しましょう。 最後まで、読んでいただきありがとうございました!!! Contents provided by: Jesse Casman, Fumiko Doi, Content Strategists for Snyk, Japan, and Randell Degges, Community Manager for Snyk Global
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Node.js: MySQL 8.0 で接続エラーが出た時の対策

次のエラーが出た時の対策です。 code: 'ER_NOT_SUPPORTED_AUTH_MODE', errno: 1251, sqlMessage: 'Client does not support authentication protocol requested by server; consider upgrading MySQL client', sqlState: '08004', fatal: true エラーが出るプログラム maria_version.js #! /usr/local/bin/node // --------------------------------------------------------------- // maria_version.js // // Jan/09/2022 // // --------------------------------------------------------------- 'use strict' // --------------------------------------------------------------- console.error ("*** 開始 ***") var mysql = require('mysql') // var mysql = require('mysql2') var connection = mysql.createConnection ({ host: 'localhost', user: 'scott', password: 'tiger123' }) connection.query("select version()", function (err, rows) { if (err) throw err console.log (rows.length) console.log (rows[0]) connection.end() console.error ("*** 終了 ***") }) 解決方法 require('mysql') を require('mysql2') にすれば、問題は解決します。 実行結果 *** 開始 *** 1 { 'version()': '8.0.27-0ubuntu0.20.04.1' } *** 終了 *** MySQL サーバーで対処することもできます。 mysql> ALTER USER scott@localhost IDENTIFIED WITH mysql_native_password BY 'tige r123';
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Githubにある自作のnpmモジュールをnpm installしてENOENTなエラーが出た時

はじまり 自分で作ったnpmモジュールをubuntu環境へinstallしようとしたときに以下のエラーが出ました。 log root@ca04e13630cb:/usr/app# npm install git+https://github.com/Landmaster135/FeedFetcher.git npm ERR! code ENOENT npm ERR! syscall spawn git npm ERR! path git npm ERR! errno -2 npm ERR! enoent An unknown git error occurred npm ERR! enoent This is related to npm not being able to find a file. npm ERR! enoent npm ERR! A complete log of this run can be found in: npm ERR! /root/.npm/_logs/2022-01-08T17_49_46_172Z-debug-0.log 原因と解消法 僕の場合、gitがubuntu環境に入っていないことが原因だった。 console apt install git-all -y gitを入れた後は、以下の何れのコマンドでもnpm installできた。権限とかpackage.jsonの問題ではなかった。 console npm install git+https://github.com/<user name>/<repo name>.git console npm install git+ssh://git@github.com/<user name>/<repo name>.git console npm install git+ssh://github.com/<user name>/<repo name>.git console npm install https://github.com/<user name>/<repo name> おしまい An unknown git error occurredとは、なんてスパルタな・・・。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Githubにある自作のnpmモジュールをnpm installしてENOENTが出た時

はじまり 自分で作ったnpmモジュールをubuntu環境へinstallしようとしたときに以下のエラーが出ました。 log root@ca04e13630cb:/usr/app# npm install git+https://github.com/Landmaster135/FeedFetcher.git npm ERR! code ENOENT npm ERR! syscall spawn git npm ERR! path git npm ERR! errno -2 npm ERR! enoent An unknown git error occurred npm ERR! enoent This is related to npm not being able to find a file. npm ERR! enoent npm ERR! A complete log of this run can be found in: npm ERR! /root/.npm/_logs/2022-01-08T17_49_46_172Z-debug-0.log 原因と解消法 僕の場合、gitがubuntu環境に入っていないことが原因だった。 console apt install git-all -y gitを入れた後は、以下の何れのコマンドでもnpm installできた。権限とかpackage.jsonの問題ではなかった。 console npm install git+https://github.com/<user name>/<repo name>.git console npm install git+ssh://git@github.com/<user name>/<repo name>.git console npm install git+ssh://github.com/<user name>/<repo name>.git console npm install https://github.com/<user name>/<repo name> おしまい An unknown git error occurredとは、なんてスパルタな・・・。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む