- 投稿日:2022-02-24T23:36:40+09:00
Deno Deep Dive - Denoでのアプリケーション実装Tips / Node.jsとの比較
概要 本記事ではNode.jsに代わるサーバサイドJavaScriptランタイムであるDenoについて、実際のアプリケーション実装で得られた知見をご紹介します。 まずはDenoの概要について説明し、アプリケーションを実装する上でのDenoの便利な使い方やAPI、各種ツールについて解説していきます。最後に筆者がDenoによる開発を経てNode.jsによる開発と比較した際のDenoの優れている点/不足している点を挙げていきます。 Denoとは DenoはNodejsの開発者であるRyan DahlがNode.jsの反省を活かして作り出したJavaScriptランタイムです。 特徴としては以下のものがあります。 TypeScriptがout of box、つまり設定なしで実行できる ES Modules対応で依存モジュールはURLによってインポートする 権限管理が厳密 フォーマッタやテストランナなどの便利機能がランタイムに付属 まずTypeScriptについて、DenoはTypeScriptで書いたスクリプトを直接実行することができます。例えばちょっとした作業スクリプトを書く場合にも、または本格的なアプリケーションを書く場合にも設定なしですぐにTypeScriptが利用できます。スクリプトを実行する際に事前にトランスパイルやビルドを実行する必要はありません。内部で実行されるDenoのコンパイラはRustで実装されており、高速でトランスパイル/ビルドされます。 DenoはES Modulesを活用したモジュールシステムを採用しており、外部モジュールはURLを使ってインポートしていきます。Node.jsでは外部モジュールはプロジェクトフォルダ内のnode_modulesにダウンロードしていました。Denoは実行時にURLから読み込めばいいのでプロジェクト内に巨大なnode_modulesフォルダを作る必要がありません。もちろん実行のたびにダウンロードすると時間がかかってしまうのでしまうので、ランタイムがキャッシュしてくれます。 Denoではネットワークやファイルシステムに対するアクセス制御を行うことができます。明示的に指定しない限りそれらへのアクセスはできない仕様です。Node.jsと比べてセキュアなランタイムと言えます。 またDenoのランタイムにフォーマッタやテストランナが付属しており、外部ライブラリをインストールする必要なくdenoコマンドによって実行可能です。この辺りの具体的な使い方についても後述します。 Denoでのアプリケーション実装Tips 実装事例 弊社がリリースしたDockpit liteというツールのバックエンドAPIをDenoで実装しました。 DenoのHTTP APIミドルウェアであるoakを活用し、認証や別APIからのデータの取得/加工などの機能を実装しています。 以下ではこのAPIサーバの実装で利用したDenoの機能やユーティリティ、外部ライブラリの使い方を説明していきます。 付属機能/API アプリケーションの実行 DenoではJavaScript/TypeScriptをdeno runコマンドで実行します。TypeScriptで書いたスクリプトを直接実行できます。Node.jsではtsconfig.jsonを書いたり、webpackなどモジュールバンドラの設定をしたりしなければTypeScriptを実行することはできませんでした。 deno run index.ts deno runコマンドにオプションを指定しない限りネットワーク/ファイルシステム/環境変数などにアクセスすることはできません。以下のようにフラグを付与して実行することでアクセス許可を与えることができます。またファイルシステムの読み書きなどについてはアクセスを許可する範囲を限定することが可能です。 deno run --allow-net --allow-read=. --allow-write=./log index.ts 依存パッケージのインポート Denoでは依存パッケージはURLからインポートします。 import { decode, encode } from 'https://deno.land/std@0.120.0/encoding/base64url.ts'; import { Application } from 'https://deno.land/x/oak@v9.0.1/mod.ts'; Deno標準のライブラリとよく利用されるサードパーティのライブラリはdeno.landにアップロードされています。 std@0.126.0 | Deno Third Party Modules | Deno 例えばAPIミドルウェアのoakライブラリをインポートする場合はhttps://deno.land/x/oak@v[version]/mod.tsを指定します。ここに置かれているソースコードはブラウザ上でも閲覧できるようになっています。またES Modulesなのでソースコードのモジュールファイル(mod.ts)から直接必要なものをインポートできます。つまり、ブラウザ上でライブラリのソースコードを閲覧して必要なものを直接URLによってインポートできるということです。このURLインポートとES Modulesの組み合わせによってライブラリを活用した開発体験が良いのもDenoの特徴です。 内部モジュールのインポート 内部モジュールはNode.jsと同様にプロジェクトフォルダ内の相対パスでインポートできます。ただしDenoではインポートするモジュールの拡張子が必要な点に注意が必要です。 import { ApiError } from './utils/Error.ts'; またRyan DahlがNode.jsの反省点に挙げていたように、Denoにはフォルダ内のindex.jsをそのフォルダ名でインポートする機能はありません。ファイルシステムの構造通りにインポートします。 インポートマップ さて、以上で述べたようなURLでのインポートや相対パスでのインポートを毎回書くのは面倒です。そこでDenoにはインポートマップという機能があります。 Import maps | Manual | Deno この機能によって、絶対パスでのモジュールのインポートや外部ライブラリインポートURLの省略(エイリアスの作成)ができます。以下のようなJSONにエイリアスとその内容を記述していきます。 importMap.json { "imports": { "@/": "./", "oak": "https://deno.land/x/oak@v9.0.1/mod.ts" } } このインポートマップのパスを実行時にオプションで指定します。 deno run --importmap=importMap.json index.ts このように設定することによって、以下のようにインポートできるようになります。 import { Application } from 'oak'; import { ApiError } from '@/utils/Error.ts'; インポートマップの機能は記述の省略以外にも依存ライブラリのバージョンの管理に役立ちます。アプリケーション全体でライブラリのバージョンを変更したい場合はインポートマップの記述のみを修正すれば良いからです。インポートマップを利用せずにそれぞれのモジュールでバージョンを記述しているとその全てを修正する必要があります。 watchモード こうして様々なライブラリやモジュールを利用しながら開発を進めていきます。開発中のアプリケーションを更新するためにはdeno runを再実行する必要がありますが、これは非常に面倒です。Denoにはwatchモードがあり、ソースの変更を検知して自動でアプリケーションを再起動してくれます。 Command line interface | Manual | Deno deno run --watch index.ts 環境変数 完成したアプリケーションをデプロイするに際して、対象の環境に応じて環境変数を変更して動作を変えることがよくあります。Denoでは標準APIのDeno.envによって環境変数を利用することができます。 > Deno.env.set('ENVIRONMENT', 'development'); > Deno.env.get('ENVIRONMENT'); "development" また.envファイルに記述した環境変数を読み込むならdotenvライブラリが利用できます。 .env.development ENVIRONMENT="development" import { config } from "https://deno.land/x/dotenv@v3.2.0/mod.ts"; config({path: './.env.development', export: true}); Deno.env.get('ENVIRONMENT'); Denoで環境変数利用する場合、実行時に--allow-envフラグを付ける必要があります。逆に言えばこのフラグを付与しなければ意図しない環境変数の読み取り/変更を防ぐことができます。 deno run --allow-env index.ts トップレベルawait Node.js 14.8以降で使えるトップレベルawaitがDenoでもデフォルトで利用できます。トップレベルawaitとは、スクリプト実行時に非同期関数外でawaitできる機能です。例えば以下のようなコードを実行することができます。 const data = await fetch('https://localhost:8000/user/get', { method: 'POST', body: JSON.stringify({ userid: 1 }), }); console.log(data); console.log(await data.json()); この機能により、非同期処理やPromiseオブジェクトの扱いが簡単になります。 フォーマッタ DenoにはJavaScriptでいうprettierにあたるフォーマッタが標準搭載されています。 Formatter | Manual | Deno deno fmtコマンドで実行してソースコードの整形が行えます。細かいフォーマッタの設定は以下のようにdeno.jsonに記述します。 deno.json { "fmt": { "options": { "useTabs": false, "singleQuote": true, "indentWidth": 4, "lineWidth":120 } } } この設定ファイルを利用してフォーマットする際は実行時のオプションで設定ファイルへのパスを指定します。 deno fmt --config ./deno.json テストランナ Denoにはテストランナが標準搭載されています。Node.jsでいうJESTといった外部ライブラリのインストールは不要です。 Testing | Manual | Deno deno testコマンドでテストコードを実行できます。自動でプロジェクトフォルダ内のjs/tsファイルがチェックされ、テストコードが見つかると実行されます。テストコードの記述にはDeno.testAPIを利用します。 test.ts import { assertEquals } from "https://deno.land/std@0.126.0/testing/asserts.ts"; Deno.test("test 1", () => { const x = 1 + 2; assertEquals(x, 3); }); Deno.test("test 2", () => { const x = 1 + 2; assertEquals(x, 5); }); $ deno test running 2 tests from file:///.../test.ts test test 1 ... ok (10ms) test test 2 ... FAILED (13ms) failures: test 2 AssertionError: Values are not equal: [Diff] Actual / Expected - 3 + 5 ... またネットワークやファイルにアクセスするテストを実行する場合にはdeno runと同様に各種オプションをつけてdeno testを実行する必要があります。 外部ツール スクリプトランナ 以上で見たように、細かい設定を行なった上でDenoを実行するためにはdeno runやdeno testに様々なオプションを付ける必要があります。そうした場合に以下のようにdeno runコマンドが長くなってしまいます。 deno run --watch --importmap=importMap.json --allow-net --allow-env --allow-read=. --allow-write=./log index.ts これを解決してくれるのがスクリプトランナーのvelociraptorです。 次のようなyamlを定義しておくとコマンド実行時のフラグ、インポートマップ、環境変数ファイルを指定することができます。複数のコマンドを使い分ける場合にもそれぞれにオプションを記述できて便利です。 velociraptor.yaml allow: - net - env scripts: start-dev: cmd: deno run index.ts envFile: .env.development watch: true imap: importMap.json allow: - read=. - write=./log start: cmd: deno run index.ts envFile: .env.production watch: true imap: importMap.json allow: - read=. - write=/var/log velociraptorをインストールした上で、以下のコマンドで設定ファイルに指定した各コマンドを実行できます。Node.jsでいうpackage.jsonへのコマンドの記述のイメージです。 vr start-dev vr start velociraptorは実際には設定ファイルと.envファイルを読み取って以下のようなコマンドを生成しています。こちらを利用する場合は環境変数はファイルから読み取ってあらかじめシェルの環境変数にセットされているので、dotenvライブラリは不要になります。Deno上で.evnファイルの環境変数を利用する場合は直接Deno.env.get()するだけです。 ENVIRONMENT="development" \ deno run \ --watch --importmap=importMap.json --allow-net --allow-env --allow-read=. --allow-write=./log \ index.ts "$@" サーバ環境でvelociraptorをインストールしたくない場合はvr exportで設定した各コマンドをシェルスクリプトに展開しておくことができます。そのシェルスクリプトをサーバにデプロイして実行するだけでローカル環境と同じ動作になります。 vr export start-dev vr export start エディタ 開発するエディタとしてVSCodeを想定します。Denoで実行するTypeScriptを普通に開いても、VSCodeはNode.js用の構文解析をしてしまいます。Deno用の構文解析をさせるにはVSCodeにvscode-deno拡張機能をインストールする必要があります。 ワークスペースでDenoの拡張機能を有効にするためには.vscode/settings.jsonに以下のように記述します。 .vscode/settings.json { "deno.enable": true, "deno.lint": true, "deno.unstable": true, "deno.config": "deno.json", "deno.suggest.imports.hosts": { "https://deno.land": true }, "deno.importMap": "./importMap.json", "editor.formatOnSave": true, "[typescript]": { "editor.defaultFormatter": "denoland.vscode-deno" } } ここにインポートマップの指定もしておくと、インポートパスをエイリアスで指定していてもエディタ上でエラー表示になりません。 またeditor.formatOnSaveを有効にしてファイルを自動フォーマットさせる際に先述のDeno付属用のフォーマッタの設定を利用するにはdeno.configの項目で設定ファイル(deno.json)を指定しておきます。こうすると毎回deno fmtを実行しなくてもファイルの保存と同時にフォーマットすることができます。 Node.jsとの比較 Denoは以下の点でNode.jsより優れていると考えます。 TypeScriptを利用した際の開発体験の向上 デプロイの簡略化 権限管理の厳密化 Node.jsと比較した際に、やはりTypeScriptで書いたスクリプトを直接実行できる点によって開発体験が向上しているのを感じます。トランスパイラやモジュールバンドラといった依存要素が大幅に減っていることで、アプリケーションの実装を始めるまでの時間的/心理的ハードルが低くなっています。 またTypeScriptを直接実行可能なことでDenoは「スクリプト言語としてのTypeScript」を発明したといってもいいかもしれません。業務上簡単なスクリプトを書いて仕事を効率化する際にもTypeScriptを利用することができます。ざっくり言うとDeno+TypeScriptはPythonに近い使用感があります。この点においてもDenoは非常に便利だと感じています。 また実装した成果物をサーバにデプロイする際に、Node.jsではソースコードを配置した上で依存ライブラリのインストール、ビルド、アプリケーションの起動と3プロセスが必要です。一方でDenoではソースコードを配置してそれを直接実行できます。ライブラリのインストールとビルドの2プロセスが省略されます。 もちろん上記の二つのポイントについてはTypeScriptやECMAScriptを利用しない際はNode.jsとDenoでそれほど差異はありません。しかしながら現代において複雑なアプリケーションをJavaScriptを実装する際にそれらを利用しないことはほとんど考えられないでしょう。 最後に権限管理について、Denoではネットワークやファイル、環境変数へのアクセスに明示的な許可が必要です。不必要な権限を与えることがないのでNode.jsで実装した場合よりセキュアになります。筆者は幸いにもNode.jsアプリケーションに対するハッキング被害にあったことはありませんが、やはり権限の管理は厳しいに越したことはないでしょう。 一方で以下の点でNode.jsに及んでいないと考えます。 サードパーティライブラリのエコシステム フロントエンド開発での利用 Node.jsの歴史は長く、膨大な数のサードパーティライブラリが存在します。一方のDenoにもある程度サードパーティライブラリは揃っているものの、総数では及ぶべくもありません。ただしサードパーティライブラリがないなら自分で実装すればいい、という発想もあり得ます。筆者もCookieの署名やJWTの検証をDeno標準のエンコードライブラリを用いて実装したりしました。 またReactなどフロントエンド開発フレームワークを用いる場合は依然としてNode.jsを利用する必要があります。DenoでもAlephといったフロントエンド開発のフレームワークが開発されていますが、未だベータ版です。 これらの点については今後のエコシステムの発達によってNode.jsに追いつくこともできるかもしれません。 おわりに 本記事ではアプリケーションを実装する上でのDenoの便利な機能や外部ツールを解説しました。また現時点でのDenoとNode.jsの優劣についても述べていきました。 個人的にはJS/TSでのサーバサイドのアプリケーションの実装や日々の業務効率化スクリプト実装においてDenoを積極的に利用していきたいと考えています。本記事で関心を持たれた方は是非利用してみてください。
- 投稿日:2022-02-24T17:40:17+09:00
【2022年版】Raspberry Pi 4とChinachuで録画サーバーを立てる
はじめに Chinachuを使って、ここ2年ほど録画サーバーを運用していました。2022年1月に、Raspberry Piの新しいOS「Bullseye」の安定版がリリースされました。OSのアップデートでは、MicroSDを書き直す必要があり、その再構築をした記録です。 以下の記事を参考に、2022年の環境での違いなどを交えつつ書いていきます。 Raspberry Pi 4+Chinachu v0.10.1-gamma.0 地デジ録画サーバー構築 知識0からRaspberryPi4で録画機を作る! おねがい 他の記事でも言われていることですが、自己責任でサーバーの構築をしてください。 趣味の範囲で、個人的な利用をお願いします。 テレビ番組のコピーや配信は違法になることがあリます。よく注意をして使ってください。 生じた損害に対して、筆者は一切の責任を負いません。 EPG StationとChinachu どっちがいい? はじめ、Dockerを使ったEPG Stationをインストールしてみました。色々な方法でトライしましたが、うまくいきませんでした。(ラズパイごとダウンしたり)参考にしたサイトではubuntuやCent OSを使っていました。ラズパイはスペック的に非力なので、Dockerを使うのは難しいと思いました。 結論として、ラズパイを録画サーバーにするのであれば、Chinachuを使うことをおすすめします。 使うもの Raspberry Pi 4 ModelB PLEX USB接続ドングル型地上デジタルTVチューナー PX-S1UD V2.0(同時に1チャンネルの録画ができる。4チャンネル同時にできる、PX-Q1UDもある。) SCM ICカードリーダー/ライター SCR3310/v2.0 B-CASカード アンテナケーブル HDD(何TBでも) 構築用のPCやWi-Fi環境など 準備 セットアップ この記事で分かりやすく、解説されています。 Raspberry Pi 4 スタートガイド【2021】セットアップと初期設定 ★ OSはFULLバージョンのインストールをお勧めします。 キーボードの日本語入力の設定 Raspberry Piに日本語で入力できる設定方法(fcitx-mozc) で丁寧に解説されています。 ※ OSをFULLバージョンでインストールした場合は、設定のみを行います。 セキュリティ対策 ユーザー名やパスワードの変更、ファイアウォールを敷く、SSHは公開鍵認証で通す…などがあります。実際にNASAでは、ラズパイを踏み台にサイバー攻撃がありました。大ごとにならなくても、念を入れておきたいですね。 以下の記事を参考に、ぜひ設定してください。 買ったらまず実施!RaspberryPiのセキュリティ対策 デフォルトだと危険!Raspberry Piの初期セキュリティ対策やってみた ムダな処理を減らしたければ… スワップの無効化 sudo swapoff --all sudo apt remove dphys-swapfile -y sudo sed -i 's/$/ coherent_pool=4M dwc_otg.host_rx_fifo_size=2048/' /boot/cmdline.txt sudo sed -i 's/^CONF_SWAPSIZE=100/CONF_SWAPSIZE=1024/' /etc/dphys-swapfile sudo apt autoremove ログの最小化 sudo nano /etc/rsyslog.conf ファイル中ほどの部分を、以下のように一部コメントアウトします。 # First some standard log files. Log by facility. # auth,authpriv.* /var/log/auth.log *.*;auth,authpriv.none -/var/log/syslog #cron.* /var/log/cron.log #daemon.* -/var/log/daemon.log #kern.* -/var/log/kern.log #lpr.* -/var/log/lpr.log #mail.* -/var/log/mail.log #user.* -/var/log/user.log 1. sambaのセットアップ 録画したものをPCで見るときなどに使います。 インストール sudo apt-get update sudo apt-get install samba 設定 sudo nano /etc/samba/smb.conf ファイルの一番下の行に、このように入力します。 [rspi] comment = Raspberry Pi Share path = /mnt/recorded guest ok = yes read only = no browsable = yes force user = <ユーザ名> force user のパスワードを設定します。 sudo smbpasswd -a <ユーザー名> sambaを再起動しActive(正常)か確認します。 sudo systemctl restart smbd sudo systemctl status smbd PCからリモートアクセスをしてください。 Macの場合は、Finderを開き command + Kでサーバへの接続画面となります。smb://<ラズパイのIPアドレス>と入力し、接続できます。 2. チューナーのドライバのインストール wget http://plex-net.co.jp/plex/px-s1ud/PX-S1UD_driver_Ver.1.0.1.zip unzip PX-S1UD_driver_Ver.1.0.1.zip sudo cp PX-S1UD_driver_Ver.1.0.1/x64/amd64/isdbt_rio.inp /lib/firmware/ rm PX-S1UD_driver_Ver.1.0.1.zip rm -rf PX-S1UD_driver_Ver.1.0.1 チューナーが正しく認識されているか、次のコマンドで確かめます。 dmesg | grep PX-S1UD [ 2.896943] usb 1-1.4: Product: PX-S1UD Digital TV Tuner のような表示が返って来ればOK。 3. 外付けHDDのマウント USBを接続 以下のコマンドで認識されているか調べます。 sudo fdisk -l ここで、/dev/sda1などと認識されていることをチェックします。 Device Boot Start End Sectors Size Id Type /dev/sda1 2048 1953525167 1953523120 931.5G 83 Linux マウント sudo mkfs.ext4 /dev/sda1 sudo mkdir /mnt sudo mount /dev/sda1 /mnt そして、mntフォルダへのアクセスを許可します。 sudo chown 1000:1000 /mnt 再起動時の自動マウント設定 HDDのPARTUUIDを調べます。 sudo blkid /dev/sda1 このように出力されるので、PARTUUIDをコピーしてください。 /dev/sda1: UUID="6c3d3bef-405e-41b4-ay4b-4b1cf71a11c8" BLOCK_SIZE="4079" TYPE="ext4" PARTUUID="d1b117d7-01" ★ UUIDではなく、PTUUIDが出力されることがあります。この場合は、HDDのUSBを挿し直したり、ラズパイの再起動を試してください。 fstabに設定を追加します。 sudo nano /etc/fstab ファイルを開き、以下のように、付け加えます。 PARTUUID=<コピーしたPARTUUID> /mnt ext4 defaults,nofail 0 0 マウントのチェック 一旦、再起動してから確認をします。 sudo reboot df -h | grep /mnt こんな感じでmntのディレクトリに、外付けHDDがリンクされていればOK。 /dev/sda1 916G 276G 595G 32% /mnt 4. カードリーダーとB-CASカード ドライバをインストール sudo apt-get install build-essential git sudo apt-get install pcscd libpcsclite-dev libccid pcsc-tools USBを接続し、型番の確認 lsusb | grep SCM カードリーダーが認識していると、次のように表示されます。 Bus 001 Device 003: ID 04e6:5116 SCM Microsystems, Inc. SCR331-LC1 / SCR3310 SmartCard Reader ★ 認識しない場合は、lsusbコマンドのみで実行、USBを抜く、または再起動をお勧めします。 B-CASカードの認識確認 sudo pcsc_scan 一番下に、このような表示が出れば成功です。 Japanese Chijou Digital B-CAS Card (pay TV) control + Cで実行を終了します。 ★ Scanning present readers...で止まっている場合は、control + Cで実行を中止し、USBやB-CASカードを抜いてみると良いかもしれないです。 B-CASのデコード用ライブラリのインストール 暗号化された放送データをlibarib25で復号します。 sudo apt-get install cmake g++ libpcsclite-dev wget https://github.com/stz2012/libarib25/archive/master.zip -O libarib25.zip unzip libarib25.zip rm libarib25.zip cd libarib25-master cmake . make sudo make install 5. 録画用コマンドツールのインストール cd sudo apt install automake wget http://www13.plala.or.jp/sat/recdvb/recdvb-1.3.2.tgz tar xvzf recdvb-1.3.2.tgz cd recdvb-1.3.2 ./autogen.sh ./configure --enable-b25 make sudo make install 試しに録画 その前に…録画したファイルの保存フォルダを作成しておきます。 sudo mkdir /mnt/recorded ls /mnt ls /mntで、recordedフォルダが作られていることを確認できました。試しに10秒録画をしてみます。 チャンネル番号は、MASPROのチャンネル一覧からお住まいの地域のものを調べてください。 # recdvb [--dev <チューナ番号>] --b25 --strip チャンネル番号 録画秒数 録画ファイル名 recdvb --b25 --strip 22 10 /mnt/recorded/test.m2ts 実行して15秒ほどして、Recorded 10secと表示されれば録画できています。sambaなどを使い、再生してみましょう。 6. Node.JSのインストール ★ 2022年現在、Node.JSのバージョンが14.17.0〜16.0.0でないと、最新のMirakurunは動きません。 私は初め、他の記事にあるようにv10.16.3を入れてみました。しかし、Mirakurunのインストールをする段階で「Node.JSのバージョンを上げるよう」エラーが出ました。 sudo apt-get install npm sudo npm install -g n sudo n 14.17.0 sudo npm install -g npm sudo n #14.17.0を選択し、Returnキーを押下 Node.JSとnpmのバージョンを確かめます。 node -v npm -v #またはnpm --version Node.JSがv14.17.0、npmが6.14.13以上であれば問題ありません。 ★ ここでエラーが出ていると、Mirakurunのインストールで行き詰まります。ワーニングは良くても、エラーがないかよくチェックしてください。 7. Mirakurunの設定 インストール Mirakurunは、チューナーの管理などを自動で行うものです。 sudo npm install pm2 -g sudo npm install mirakurun -g --unsafe --production mirakurunのインストールに成功すると、アスキーアート(記号を使ったイラスト)でPM2と書かれたものが出てきます。またmirakurunのステータスが表形式で示されます。 ★ エラーが出た場合は、一番上のエラーをまずは読むと解決につながることがありました。(例えば、Node.JSのバージョンが古いなど) チューナーの設定 tuners.yml を開く。 sudo nano /usr/local/etc/mirakurun/tuners.yml 一番下の行に以下を追記する。 - name: PX-S1UD-1 types: - GR command: recdvb --b25 --dev 0 <channel> - - チャンネルスキャン curl -X PUT "http://<ラズパイのIPアドレス>:40772/api/config/channels/scan 15分以上かかります。 -> no signal. [Error: 〜 と表示されるチャンネルがありますが、いつも見ているチャンネルの放送局名(TBSなど)が認識されていれば問題ないです。 8. Chinachuの設定 スマホやPCから簡単に録画ができるようにする、録画サーバーです。 インストール sudo apt-get install build-essential curl git-core vainfo git clone git://github.com/kanreisa/Chinachu.git /mnt/chinachu cd /mnt/chinachu ./chinachu installer 1.Auto を選択し、Chinachuをインストールします。 ★ インストールには2~3時間を要します。PCからSSHで実行すると、セッションがタイムアウトし、インストールが上手くいかないことがありました。ラズパイをTVやモニターにつなぎ、そこから実行すると確実です。 初期設定 # /mnt/chinachu の位置で実行 cp config.sample.json config.json sudo nano config.json 以下のように変更します。 "uid": <実行ユーザー名>, "recordedDir" : "/mnt/recorded/", "wuiOpenHost": "0.0.0.0", "recordedFormat": "<title> <subtitle> <episode>[<channel-name>][<date:yymmdd-HHMM>].m2ts", 録画ファイルの初期設定をします。 cd /mnt sudo chown <ユーザー名>:video /mnt/chinachu #権限を付与する ls -la # chinachuの行 左から3つ目が <ユーザー名> video になっているかチェックする echo "[]" > rules.json Chinachu起動 cd /mnt/chinachu/ ./chinachu service wui execute ログがエラーなく出たら、control + C で停止します。 次に、サービスの登録などをします。 sudo pm2 startup sudo pm2 start processes.json sudo pm2 save ステータスの確認 sudo mirakurun status このように、3つ認識され、statusが全てonlineであれば正常です。 ┌────┬────────────────────┬──────────┬──────┬───────────┬──────────┬──────────┐ │ id │ name │ mode │ ↺ │ status │ cpu │ memory │ ├────┼────────────────────┼──────────┼──────┼───────────┼──────────┼──────────┤ │ 2 │ chinachu-operator │ fork │ 0 │ online │ 0% │ 54.6mb │ │ 1 │ chinachu-wui │ fork │ 0 │ online │ 0% │ 176.8mb │ │ 0 │ mirakurun-server │ fork │ 2 │ online │ 0% │ 77.8mb │ └────┴────────────────────┴──────────┴──────┴───────────┴──────────┴──────────┘ 番組表の自動更新 ./chinachu update ブラウザから開いてみる このアドレスでアクセスする。 http://<ラズパイのIPアドレス>:20772/ Chinachu 機能の要約 1週間先まで番組の予約ができる 番組表は見たいチャンネルのみに選択できる ルール機能で番組を登録すると、その番組を自動で録画してくれる (再放送は除くなど細かく条件を決められる) 左側 縦に並んだバーから、システムの情報が得られる 上から2番目:ステータス 動作しているかどうか 上から3番目:ログ Operatorでjsonの情報、SchedulerからMirakurunの動作について 上から4番目:ストレージ 外付けHDDの使用状況 ラズパイと同じWi-Fiに繋がっていれば、スマホから録画ができ便利です! 課題 mp4に簡単にエンコードできるようにしたい たまにラズパイごとダウンしていることがある→更なる安定性 さいごに OSをアップデートしてから、chinachuの運用まで1週間ほどかかりました。エラーにより、ビルドがうまくいかないなど、よく起こりました。楽しみつつ、粘り強く、構築する大切さを学びました。 参考サイト Raspberry Pi 4+Chinachu v0.10.1-gamma.0 地デジ録画サーバー構築 知識0からRaspberryPi4で録画機を作る! Raspberry Pi 4とdocker-mirakurun-epgstationで録画サーバーを構築する (2021年4月版) Chinachuインストール手順 - GitHub Mirakurunインストール手順 - GitHub
- 投稿日:2022-02-24T15:52:20+09:00
Express で レスポンスに "Content-Type" ヘッダを自前セットする方法どれを選べばいいの
概要 Express 4 で、HTTPレスポンスヘッダーに "Content-Type" をマニュアルでセットする方法のハンズオン記録です "Content-Type" ヘッダをセットする書き方はよく見かけるアプローチだけでも res.type, res.set, res.header, res.setHeader, res.writeHead がある それぞれの書き方、結果について実際にやってみた 例として、HTTPレスポンス時 の HTTPヘッダー Content-Type が Content-Type: text/plain; charset=utf-8 となるようにコードをかいてみる 先に、全体像 → Express で HTTPヘッダをセットする方法一覧 「"Hello" を "Content-Type: text/plain;" ヘッダつけてレスポンスする」いろんなやり方 メソッド コード例 説明 res#type res.type('text/plain');res.send('Hello'); "content-type"ヘッダー設定専用メソッド res.type('.txt');res.send(`Hello`); ファイル拡張子を指定すると適切なContent-Typeを設定してくれる res#set res.set('content-type', 'text/plain');res.send('Hello'); ヘッダー名,値 で指定する res.set({'content-type': 'text/plain','x-original-header': 'original_value'});res.send('Hello'); res#setで、ヘッダーを複数指定する書き方 res#header res.header('Content-Type', 'text/plain');res.send('Hello'); res#set のエイリアス res.header({'content-type': 'text/plain','x-original-header': 'original_value'});res.send('Hello'); ヘッダーを複数指定する書き方 res#setHeader res.setHeader('content-type', 'text/plain');res.send(`Hello`); Node.js の 'http' モジュールに所属するメソッド res#writeHead res.writeHead(200, { 'Content-Type': 'text/plain' });res.write('Hello');res.end();// 応答プロセスを終了する Node.js の 'http' モジュールに所属するメソッドres.sendとは併用できない 本編 Expressで簡易サーバーを書く 実験用簡易サーバーを動作させて、HTTPヘッダーがどのように出力されるか curl で確認する 実験用簡易サーバー import express from 'express'; export default class HttpServer { constructor() { this.server = null; } async start(options = {}) { const { port } = options; const app = express(); app.get('test', (req, res) => { // ここでいろいろ試す res.set('content-type', 'text/plain'); res.send('Hello'); }); return new Promise((resolve) => { this.server = app.listen(port, () => { console.log(`Server started on port:${port}`); resolve(); }); }); } stop() { this.server.close((() => { console.log(`Server stopped`); })); } } (async () => { const server = new HttpServer(); await server.start({ port: 8080 }); })() ヘッダー確認用curl curl --head http://localhost:8080/test res.type res#type をつかう。 res#type は "text/plain" のように"/"が含まれていたら、Content-Type : text/plainのように、そのまま反映するが、"/" 含まれていなければ、指定した文字列をファイル拡張子とみなして、MIMEタイプが検索され適切な Content-Typeが設定される。 コード app.get('test', (req, res) => { res.type('text/plain'); res.send('Hello'); }); 結果 Content-Type: text/plain; charset=utf-8 テキストファイルの拡張子を指定してみる コード app.get('/test', (req, res) => { res.type('.txt'); res.send(`Hello`); }); 結果 ちゃんと "text/plain" になっている。 Content-Type: text/plain; charset=utf-8 こうしても結果は同じだった app.get('/test', (req, res) => { res.type('txt'); res.send(`Hello`); }); res.set() res#set をつかう コード app.get('/test', (req, res) => { res.set('content-type', 'text/plain'); res.send('Hello'); }); 以下のように書くこともできる。こちらは、複数ヘッダをセットできる app.get('/test', (req, res) => { res.set({'content-type':'text/plain'}); res.send('Hello'); }); 結果 HTTPヘッダーは以下のとおり。charset が自動付与されているが、Expressではデフォルトcharsetは"utf-8"となる。 Content-Type: text/plain; charset=utf-8 実際の応答はこんな感じ。ひとまず Content-Type に着目する。 curl --head http://localhost:8080/test HTTP/1.1 200 OK X-Powered-By: Express Content-Type: text/plain; charset=utf-8 Content-Length: 5 ETag: W/"5-9/+ei3uy4Jtwk1pdeF4MxdnQq/A" Date: Thu, 24 Feb 2022 05:05:55 GMT Connection: keep-alive Keep-Alive: timeout=5 res#setでヘッダを複数指定してみる app.get('/test', (req, res) => { res.set( { 'content-type': 'text/plain', 'x-original-header': 'original_value' }); res.send('Hello'); }); 結果 ちゃんと複数指定できていた。 Content-Type: text/plain; charset=utf-8 x-original-header: original_value res.header res#header は res#set のエイリアス なので、 res#set と等価(おんなじ) コード app.get('/test', (req, res) => { res.header('Content-Type', 'text/plain'); res.send('Hello'); }); 結果 Content-Type: text/plain; charset=utf-8 setメソッドと同じく複数指定も可 app.get('/test', (req, res) => { res.header( { 'content-type': 'text/plain', 'x-original-header': 'original_value' }); res.send('Hello'); }); res.setHeader res#setHeaderは Express ではなく Node.js のコアモジュールである 'http' モジュールがもつメソッド コード app.get('/test', (req, res) => { res.setHeader('content-type', 'text/plain'); res.setHeader('x-original-header', 'original_value'); res.send(`Hello`); }); 結果 Content-Type: text/plain; charset=utf-8 x-original-header: original_value res.writeHead res#writeHead は Express ではなく Node.js のコアモジュールである 'http' モジュールがもつメソッド コード res.writeHead は res.send と併用できないので注意(理由は後述) app.get('/test', (req, res) => { res.writeHead(200, { 'Content-Type': 'text/plain' }); res.write('Hello'); res.end();// 応答プロセスを終了する }); 結果(ヘッダーぜんぶのせ) curl --head http://localhost:8080/test HTTP/1.1 200 OK X-Powered-By: Express Content-Type: text/plain Date: Thu, 24 Feb 2022 05:50:41 GMT Connection: keep-alive Keep-Alive: timeout=5 res.writeHeadとres.sendを併用できない理由 うっかり以下のようにしてしまうとエラーとなる app.get('/test', (req, res) => { res.writeHead(200, { 'Content-Type': 'text/plain' }); res.send(`hello`); }); エラー Cannot set headers after they are sent to the client これは writeHead を実行すると、即座にレスポンスコードとヘッダ書き込みが起こるが、 res.send 内でも、再度 ヘッダ書き込みを行うため。 おまけ res.send 後に res.end は不要 app.get('/test', (req, res) => { res.writeHead(200, { 'Content-Type': 'text/plain' }); res.write('Hello'); res.end();// 応答プロセスを終了する }); この場合は res.endで応答のプロセスを終了しているが、 res.send の場合は res.send 内で res.end をしているので、 res.end は不要となる。 おまけ、その2: Content-Type ヘッダーを送出しない方法 以下のように、何も返さないで出力を終える ということもできる この場合、 Content-type は付与されない コード app.get('/test', (req, res) => { res.status(200).end(); }); 結果(ヘッダーぜんぶのせ) curl --head http://localhost:8080/test HTTP/1.1 200 OK X-Powered-By: Express Date: Thu, 24 Feb 2022 05:59:52 GMT Connection: keep-alive Keep-Alive: timeout=5 こうやっても同様に何も返さないで出力をおえる コード app.get('/test', (req, res) => { res.status(200).send(); }); 結果(ヘッダーぜんぶのせ) curl --head http://localhost:8080/test HTTP/1.1 200 OK X-Powered-By: Express Date: Thu, 24 Feb 2022 06:03:10 GMT Connection: keep-alive Keep-Alive: timeout=5 res.send で空文字を返すと Content-Type は付与される コード例 app.get('/test', (req, res) => { res.status(200).send(''); }); 結果(ヘッダーぜんぶのせ) Content-Length: 0 だが、Content-Type は text/html が付与される。 >curl --head http://localhost:8080/test HTTP/1.1 200 OK X-Powered-By: Express Content-Type: text/html; charset=utf-8 Content-Length: 0 ETag: W/"0-2jmj7l5rSw0yVb/vlWAYkK/YBwk" Date: Thu, 24 Feb 2022 06:03:34 GMT Connection: keep-alive Keep-Alive: timeout=5 関連
- 投稿日:2022-02-24T10:38:11+09:00
React による シングルページWebアプリケーション (SPA) の開発
demoru.net でのサービス公開イメージ demore.net とは、弊社で提供している SPA 体験サイトです。 SPA とは何か? (アプリ利用者の目線) Actor (アプリ利用者) にとって シングルページWebアプリケーション (以下 SPA) とは、いくつかの JavaScript ファイルと css ファイルで構成されている、ブラウザ上で動作するアプリです。 SPA は通常、ユーザ認証を行なってユーザを特定し、Web API を用いたさまざまな情報サービスを提供します。サーバサイドのデータベースやディスクスペースを利用することもありますが、これらのデータアクセスも例外はありますが Web API を用いています。 SPA ではない、従来のサーバサイドの Webアプリ は、ボタンやリンクをクリックするごとに以下の動作を繰り返します。 ブラウザはサーバに情報取得要求 (リクエスト) を送出する (ex. 情報提供サーバに japan の covid-19 新規感染者数の日別データを要求する) ブラウザはサーバからの応答 (レスポンス) を待つ ブラウザはレスポンスを受信したらレスポンスに含まれる情報をブラウザ上に描画する 通常この描画はブラウザ全体を再描画する動作を伴う (ex. japan の covid-19 累計感染者数の時系列グラフを表示する) ブラウザとサーバのやりとりは、このリクエストしてレスポンスを待ち、再描画するという「ギッコン、バッタン」スタイルで進んでいきます。 SPA は、必要なアプリケーションとデータを可能な限りまとめてブラウザに渡します。アプリの操作や、他のユーザからの操作によってブラウザ画面上の情報を変更する場合は、変更が必要な箇所だけを再描画する仕組みをもっています。「ギッコン、バッタン」スタイルから解放されることから UX は向上します。 SPA とは何か? (開発者の目線) いにしえの昔、SPA は javaアプレット や Adobe Flash などのブラウザプラグインに依存していましたが、現在は JavaScript を用いた実装になっています。2014年にオライリージャパンから「シングルページWebアプリケーション」(ISBN978-4-87311-673-0)という書籍が発行され、フロント (ブラウザ) 側だけでなく、バックエンド (サーバ) 側の Webサーバ (Node.js) とデータベース (MongpDB) もすべて JavaScript で実装するというアプローチが紹介されました。 しかしながら JavaScript でアプリを実装するというのは、小規模なものであれば実現可能でも、多機能かつ大規模なアプリ開発では非現実的と考えていました(個人の感想です)。しかし次のスタックを用いることによって、高品質なアプリをカジュアルに、なおかつ楽しんで開発できるようになりました。 React + redux tool kit + TypeScript による開発 React Facebook (現在 Meta) 社がつくってくれた JavaScript ライブラリで、2013年にオープンソースになりました。 コンポーネント単位での実装 とかく複雑になりがちな UI 実装を、独立したコンポーネント単位に分割することができるようになり、地方分権を容易にしてくれた コンポーネントでは JSX という簡易 JavaScript 構文を使用することができ、複雑な JavaScript と同等の実装を HTMLタグのように簡潔に記述できるようになった React コンポーネントは JavaScript で実装することも可能だが、JSX で実装することによりプログラムの可読性が大きく向上する フック (Hooks) の採用 フックとは何か? は後述 React には組み込みのフックが用意されており、定番の処理を React らしく実装できる サードパーティが提供するフックが多く存在するので、難しい実装はこれらに任せることが可能になる カスタムフックの実装も容易でかつ実用性が高い 仮想 DOM の採用 React は 仮想 DOM を更新し、実際の (ブラウザの) DOM に対しては仮想 DOM との差分のみを反映することにより、効率のよい画面描画を実現している フック (Hooks) とは何か? フックは Reactコンポーネント をクラスを使わないで実装するための機能で、React 16.8 (2019年公開) より利用できるようになった。React コンポーネントの実装には、クラスコンポーネントと関数コンポーネントという2つの実装方式がある。Reactコンポーネントは、関数コンポーネント + フック を採用する方が容易でかつ簡潔に実装できる。 redux tool kit (RTK) とても有用なライブラリであるが「アプリケーションの状態管理のためのライブラリ」というように簡単に紹介されてしまうことが多い。Reactフック を用いた実装になっており、2019年に公開されている。 state (ある状態、ステータス) を保持できる (ex. ログイン済、未など) 各コンポーネントは useSelectorフック を使って state の値を参照できる 各コンポーネントは useDispatchフック を使って state の値を更新できる state の値は Redux Dev Tools でブラウザからいつでも参照することができる RTK のおかげで複雑なデータ構造を持つ大規模アプリケーションであっても、データの見通しが悪くなることなく、快適かつ安全に開発を進めることができる。 TypeScript TypeScript は、JavaScript の型定義を厳密にする「型付け」に特化した JavaScript のスーパーセット (JavaScript を拡張した言語) である。 TypeScript を用いることによって、コーディングにかかる工数は若干増加するものの、コーディングアップ時の品質が向上するので、トータルでの導入効果は高く「使わない手はない」環境といえる。ちなみに Web 上で見かけるサンプルコードも、TypeScript のものが圧倒的に品質が高い。 TypeScript は Microsoft社が開発した言語であるためか、VSCode との相性が良く、両者を採用することで最適な開発者体験が得られている。 開発環境イメージ 開発者のローカル環境に、プロジェクトごとの Dockerコンテナ を立てて開発している イメージは node:current-slim という node.js 公式イメージを使用している docker-compose.yml は後述 プログラムコードは docker volume を使ってコンテナからローカルディスクをマウントした領域に配置している このマウントした領域のフォルダをポイントして VSCode からコードを編集している このマウントした領域は GitHub でホストされているリモートリポジトリになっている ex. docker-compose.yml $ pwd /Users/r/Docker/unrwebpage $ cat docker-compose.yml version: '3' services: node: image: node:current-slim container_name: 'unr_webpage' hostname: 'unr_webpage' ports: - '3001:3001' stdin_open: true tty: true working_dir: '/usr/src/app' volumes: - ./app:/usr/src/app networks: - dumynet networks: dumynet: external: true $ 開発環境の構築手順 Mac (Intel chip) での開発に必要なツールのインストール Docker container を用いた React + Redux Toolkit + TypeScript Webフロントエンド開発環境の構築 開発スタイル 現在は以下のスタイルで開発しています slice をつくる コンポーネントをつくる テストする まず slice をつくる コンポーネントより先に slice をつくります。 slice とは state と、以下に示す reducer を用途 (ex. ログイン機能) ごとにまとめたものになります。 slice をつくることにより、以下が明確になります。 slice の名称 (ex. auth) state の構造 state の名称やデータ形を interface として定義することが多い (ex. ユーザID, ロール, displayName, アバター画像のURL など) state の初期値 reducer の定義 reducer とは コンポーネントから受け取るデータと、state を更新する論理を action (ex. login) として定義したもの コンポーネントは useDispatchフック を使って action を呼び出す コンポーネントから参照可能な state の定義 コンポーネントは useSelectフック を使って state を参照する 現実の開発スタイル slice からつくって、次にコンポーネントをつくり、結合してテストする、というスタイルは、小人数で開発する際のスタイルだと思っています。現実には slice で定義されるデータ構造と action をドキュメント等に定義し、この定義に従って、 同時に開発する slice はダミーコンポーネント (弊社で dumymain と呼んでいるもの) と結合してテストする コンポーネントはモックと結合してテストする 単体でテスト済みの slice とコンポーネントを結合してテストする という流れになるんだと思っています。 成果物 demoru.net 組織 unremoted.com
- 投稿日:2022-02-24T01:04:52+09:00
Docker for Node.jsデベロッパのための大切な5つのセキュリティ
リモートのアルバイトで始めた仕事が、長期化、長時間化。 Slackでの開発者との仕事で、文字が小さくスレッドやチャンネル迷子は相変わらず日常茶飯事。 Slackの中で、最初に覚えたマークダウンはまた、コードをインライン表示すること ` バッククォート` 最近、Qiitaでのありがたい編集のフィードバックで覚えた コードブロックの書き方 (空行) ``` code.... ``` (空行) コーディングとは程遠いですが、新しいトリックを覚えるのはシンプルに楽しいなと思えた瞬間でした。 さて、今回はこちらの翻訳のご紹介です。 こちらに関連し以前ご紹介した 安全なNode.jsのためのDockerイメージ構築のステップバイステップのチュートリアルの翻訳も、ぜひ参照してみてください。 では、今回のお題をご案内しますね。 Docker for Node.jsデベロッパ:セキュリティを失敗させないために知っておくべき5つのこと Liran Talリランタル 2021年1月25日 Dockerは、コンテナイメージのダウンロード数が合計で500億を超えています。Docker Hubでは何百万ものアプリケーションが利用できるため、コンテナベースのアプリケーションは人気があり、アプリケーションを簡単に使用および公開が可能です。 Docker Node.js のWebアプリケーションを構築する単純な方法(qiita翻訳)には、多くのセキュリティリスクが伴う可能性があります。では、Node.jsデベロッパにとってセキュリティをDockerで考慮するにはどうすればよいでしょうか。 Docker for Node.jsとDockerイメージの構築の要点に飛び込む前に、このトピックに関するよくある質問を見てみましょう。 Node.jsアプリケーションをdockerizeするにはどうすればよいですか? DockerコンテナでNode.jsアプリケーションを実行するには、プロジェクトのディレクトリをコピーして、必要なnpmパッケージをすべてインストールするだけでよいのですが、セキュリティや生産に関する多くの懸念事項を見逃す可能性があります。 Dockerを使用したNode.jsWebアプリケーションのコンテナ化では、正しいDockerベースイメージの選択から、多段ビルドの使用、シークレットの安全な管理、本番関連のフレームワーク設定の適切な有効化まで、すべてをカバーしています。 この記事では、Webアプリケーションに適切なNode.js Dockerベースイメージを選択することの影響をよりよく理解するために必要な情報に焦点を当て、アプリケーションで利用できる最も安全なDockerイメージを見つけるためのガイドを提供します。 DockerはNode.jsデベロッパにとってどのように役立ちますか? Node.jsアプリケーションをコンテナーにパッケージ化すると、ランタイム、構成、OSレベルの依存関係、およびWebアプリケーションがさまざまなプラットフォームやCPUアーキテクチャーで実行するために必要なすべてのものを含む完全なアプリケーションをバンドルできます。これらのイメージは、コンテナーイメージと呼ばれるデプロイ可能なアーティファクトとしてバンドルされています。これらのDockerイメージは、簡単に再現可能なビルドを可能にするソフトウェアベースのバンドルであり、Node.jsデベロッパにすべての環境で同じプロジェクトまたは製品を実行する方法を提供します。 最後に、Dockerコンテナを使用すると、デベロッパは、特別な権限を必要とせずに、またはプロジェクトを実行するための専用環境をセットアップすることなく、新しいプラットフォームリリースやその他の変更をより簡単に試すことができます。 アプリケーションに適したNode.jsのDockerベースイメージを選択する Node.jsのプロジェクトでDockerイメージを作成する場合、Docker Hubから引っ張ってきた別のDockerイメージをベースに、独自のアプリケーションイメージを構築します。これはベースイメージと呼ばれるものです。ベースイメージは、これから構築するNode.jsアプリケーション用の新しいDockerイメージの構成要素になります。 ベースイメージの選択は、Dockerイメージのビルド速度から、Webアプリケーションのセキュリティやパフォーマンスまで、すべてに大きく影響するため、非常に重要です。DebianやUbuntuをベースとした本格的なオペレーティングシステムイメージを選択するのは、このイメージで利用可能なすべてのツールやライブラリを活用することができるでしょう。 しかし、これには代償があります。ベースとなるイメージにセキュリティ上の脆弱性があると、新しく作成するイメージもそれを引き継ぐことになります。なぜ、多くの脆弱性を含む大きなベースイメージをデフォルトにした悪条件でスタートしたいのでしょうか? ベースイメージを見ると、セキュリティ脆弱性の多くは、このベースイメージが使用しているOS(Operating System)層に属しています。Snykの2019年の研究「Shifting Docker security left」では、OS層がもたらす脆弱性は、選択する種類によって大きく異なることが示されました。 ちなみにこれは、利用を検討するすべてのベースイメージに対して、当てはまる懸念事項です。例えば、2020年の「オープンソースセキュリティの現状」レポートから、最新のイメージビルドを持つ上位のDockerイメージを紹介すると、Dockerのベースイメージには一貫してデフォルトでセキュリティの脆弱性が含まれていることがわかります。 2. 開発中にNode.jsのDockerイメージをスキャンする 他のイメージを基にDockerイメージを作成したり、それらを再構築することは、新たな脆弱性をもたらす可能性がありますが、あなたがその上に立つための方法があります。 Dockerイメージのビルドプロセスを、他の開発関連の活動と同じように扱います。あなたが書いたコードをテストするのと同じように、あなたがビルドしたDockerイメージもテストすべきです。 これらのテストには、Dockerfileにセキュリティ上の落とし穴やその他の悪いパターンがないことを確認するための静的ファイルチェック(別名linters)が含まれます。Dockerイメージのセキュリティのベストプラクティスで、これらの概要を説明しました。もしあなたがNode.jsアプリケーションのでエロっぱならなら、このステップバイステップの「DockerでNode.jsウェブアプリケーションをコンテナ化する10のベストプラクティス」を読みたいと思うでしょう。 git リポジトリを Snyk に接続することも優れた選択です。SnykはGitHub、GitLab、Bitbucket、Azure Reposとのネイティブな統合をサポートしています。gitとの統合を行うことで、プルリクエストをスキャンし、セキュリティの脆弱性を発見した場合には、セキュリティ情報を注釈として付与することが可能になります。これにより、プルリクエストが新たなセキュリティ上の脆弱性をもたらす場合、ゲートを設け、マージを拒否することができます。 継続的インテグレーション (CI) に柔軟性が必要な場合、または密接に統合された開発者エクスペリエンスが必要な場合は、Snyk CLI をご利用ください。 CLIを使用すると、Dockerコンテナ・イメージを簡単にテストすることができます。例えば、ローカルでDockerイメージを構築して、タグ付けしているとします。 nodejs:notification-v99.9—次のようにテストします: Snyk CLIをインストール: $ npm install -g snyk Snyk CLI が自動でAPIトークンを自動的に取得: $ snyk auth ローカルのベース画像をスキャン: $ snyk container test nodejs:notification-v99.9 テスト結果は、脆弱性を導入したパスであるCVEに関する情報とともに画面に出力されるので、どのOSの依存関係が原因なのかを知ることができます。 以下は、node:15のDockerのベースイメージをテストするための出力例です。 : ✗ High severity vulnerability found in binutils Description: Out-of-Bounds Info: https://snyk.io/vuln/SNYK-DEBIAN9-BINUTILS-404153 Introduced through: dpkg/dpkg-dev@1.18.25, libtool@2.4.6-2 From: dpkg/dpkg-dev@1.18.25 > binutils@2.28-5 From: libtool@2.4.6-2 > gcc-defaults/gcc@4:6.3.0-4 > gcc-6@6.3.0-18+deb9u1 > binutils@2.28-5 Introduced by your base image (node:15) ✗ High severity vulnerability found in binutils Description: Integer Overflow or Wraparound Info: https://snyk.io/vuln/SNYK-DEBIAN9-BINUTILS-404253 Introduced through: dpkg/dpkg-dev@1.18.25, libtool@2.4.6-2 From: dpkg/dpkg-dev@1.18.25 > binutils@2.28-5 From: libtool@2.4.6-2 > gcc-defaults/gcc@4:6.3.0-4 > gcc-6@6.3.0-18+deb9u1 > binutils@2.28-5 Introduced by your base image (node:15) Organization: snyk-demo-567 Package manager: deb Target file: Dockerfile Project name: docker-image|node Docker image: node:15 Platform: linux/amd64 Base image: node:15 Licenses: enabled Tested 412 dependencies for known issues, found 554 issues. Base Image Vulnerabilities Severity node:15 554 56 high, 63 medium, 435 low Recommendations for base image upgrade: Alternative image types Base Image Vulnerabilities Severity node:current-buster-slim 53 10 high, 4 medium, 39 low node:15.5-slim 72 18 high, 7 medium, 47 low node:current-buster 304 33 high, 43 medium, 228 low We have a Snyk CLI Cheatsheet, showing some of the magic you can do with it, and you can also find more information on how to get started with the Snyk Container CLI in our documentation. Snyk CLI Cheatsheetでは、Snyk CLIでできる便利な機能の一部を紹介しています。また、Snyk Container CLIを使い始める方法についての詳細な情報は、当社のドキュメントでご覧いただけます。 3. Dockerイメージに含まれるNode.jsのランタイムの脆弱性を修正する Dockerコンテナ・イメージのリスクを管理する際に見落としがちなのが、アプリケーション・ランタイムそのものです。Java用のDockerを実践している場合でも、Node.jsウェブアプリケーション用のDockerを実行している場合でも、Node.jsアプリケーションランタイムそのものが脆弱である可能性があります。 Node.jsのセキュリティリリースとNode.jsのセキュリティポリシーに注意し、従う必要があります。手動でこれらをフォローする代わりに、Snyk を活用して Node.js のセキュリティ脆弱性を見つけることもできます。 Node.jsのさまざまなベース画像タグのセキュリティ脆弱性について、より多くのコンテキストを提供するために、Snyk CLIでそれらのいくつかをスキャンし、次の対数スケールチャートに結果をプロットしてみました: このようなことがわかりました: 1. node:latestとも呼ばれるデフォルトの node base imageタグは、500以上のセキュリティ脆弱性をバンドルしていますが、Node.jsランタイム自体にも2つのセキュリティ脆弱性を導入しています。現在、Node.js 15バージョンを実運用しており、パッチや修正を行っていない場合は、これは懸念事項となります。 2. node:alpine base image タグは、ベースイメージに脆弱な OS の依存関係をバンドルしていないかもしれません-これが青いバーがない理由です-が、最新の Node.js ランタイム(version 15)の脆弱なバージョンを含んでいることになります。 3. サポートされていないバージョンのNode.js(例えば、Node.js 10)を使用している場合、それは脆弱であり、セキュリティアップデートを受け取っていないことがわかります。 この記事を書いている時点で、リリースされている最新版であるNode.js version 15を選択した場合、実際にはこのコンテナ内の561のセキュリティ脆弱性だけでなく、Node.jsランタイム自体の2つのセキュリティ脆弱性にも晒されることになります。 このパブリック・イメージのテストURLでDockerスキャンのテスト結果を見ることができます。あなたが使用している他のNode.jsベースイメージタグを、この無料で公開されているDockerスキャンサービスでテストすることをオススメします。 SnykはDocker HubとDocker Desktopにおけるコンテナスキャンをサポートしており、セキュリティは今やDockerのワークフローに不可欠な要素となっています。また、SnykはDockerの公式イメージの公式セキュリティプロバイダでもあります。実際、Dockerを開発プラットフォームとして使用している方は、Docker Desktop with Snykと新しいDocker脆弱性チートシートをご参照ください。 すでにDockerのユーザーアカウントをお持ちの場合は、それを使ってSnykに接続し、Docker Hubリポジトリをすぐにインポートすることができます。 4. Node.jsアプリケーションにデプロイされたDockerイメージの監視 Dockerイメージを構築したあと、おそらくイメージを追跡するDockerレジストリにプッシュし、機能的なコンテナアプリケーションとしてデプロイし、起動できるようにしますよね。 なぜDockerのベースイメージを監視する必要があるのか? ベース画像のスキャンと修正で、これまで説明したセキュリティガイドラインをすべて実践しているのであれば、それは素晴らしいことです。しかし、新しいセキュリティの脆弱性は常に発見されていることを心に留めておいてください。今、イメージに78件のセキュリティ脆弱性があったとしても、明日の朝、新しいCVEが報告され、運用中のコンテナに100件の影響がないとは限りません。そのため、コンテナイメージのレジストリ(コンテナのデプロイに使用しているイメージ)を監視することは、セキュリティ問題をすぐに発見し、修正できるようにするために非常に重要です。 もしあなたが有料のDocker Hubレジストリをイメージに使用しているなら、Docker HubにSnykによるDockerセキュリティスキャンが統合されているのを既にご存知かもしれません。 また、Snykアプリから多くのDockerイメージレジストリと直接統合することができます。例えば、Docker Hub、ACR、ECR、GCR、Artifactoryからイメージをインポートすると、Snykがこれらを定期的にスキャンし、見つかったセキュリティ問題についてSlackや電子メールで警告をうけることができます。 5. セキュリティガイドラインとプロダクショングレードの推奨に従って、安全で最適なNode.jsのDockerイメージを作成します。 これまでセキュリティガイドラインについてここまで読みすすめていただき、ありがとうございました! あなたはJavaのデベロッパですか?こちらもぜひ、参照してください。 Javaデベロッパが知るべきDockerセキュリティ5つの大切なこと 10 best practices to containerize Node.js web applications with Docker Dockerでnode.jsウェブアプリケーションをコンテナ化するための10のベストプラクティス - もしあなたがNode.jsデベロッパなら、Node.jsアプリケーション用に安全でパフォーマンスの高いDockerベースイメージを構築する方法を示すこのステップバイステップのチュートリアルは、とても気に入るでしょう。 SnykとDocker IDを使用して、コンテナイメージのテストと修正を開始するにはこちらへ 最後まで読んでいただき、ありがとうございました!!! Contents provided by: Jesse Casman, Fumiko Doi, Content Strategists for Snyk, Japan, and Randell Degges, Community Manager for Snyk Global