ネットにあまり情報がなく、自分で試行錯誤しながら色々調べてわかった備忘録的なネタです。
フロントエンドフレームワークではない、サーバーサイドアプリとしての Node.js で使える node-canvas というパッケージモジュールがあります:
https://www.npmjs.com/package/canvas
これは HTML5 の Canvas 互換機能をサーバーサイドで使えるようになるモジュールです。UI のないサーバーサイドで HTML5 の Canvas ??と思う人がいるかもしれませんが、例えば画像を生成して返すような API を作ろうとした際に(仮想的な)Canvas を生成してグラフィックを描画し、最後に Canvas を画像化して(image/png などのコンテントタイプを指定して)出力する、といった使い方ができて便利です:
便利なのですが、実はこの node-canvas は利用する上での大きな制約があります。システムにあらかじめ特定のライブラリがネイティブインストールされている必要があり、その前提でインストールすることによって使える、というものです。つまり普通にインストールしようとして "$ npm install canvas" と実行するだけでは正しくインストールできないことがあり、その前に必要なライブラリをインストールしておく必要があるのでした。例えば Ubuntu の場合は以下を実行しておく必要があります(他のシステムの場合はこちらの compiling 節を参照ください):
さて、アプリを普通に動かすだけならこの(ドキュメントに書かれている)前提コマンドを知っているだけでいいのですが、Docker コンテナ上で動かすとなると少し事情が変わってきます。システム(= Docker ホスト)にあらかじめインストールしたライブラリは関係なく、Docker コンテナとして動かす、そのコンテナ上に上述の依存ライブラリを導入しておく必要があります。しかも Docker コンテナとして動かす場合は Dockerfile 内でベースイメージを指定することになるのですが、このベースイメージによっても(追加で)インストールが必要なライブラリが変わってきます。そういった複雑な事情がある中で、どのようにすれば Node.js で node-canvas が動くコンテナを作れるようになるか、というのがこのブログエントリの(自分の備忘録としての)目的です。もちろん自分以外で同様に Docker コンテナ内で node-canvas を使いたい人がいたとしたら、(特に日本語だとなかなかまとまった情報が見つからないので)以下の情報が有用であると思っています。
答としてはこんな感じです。node:16-alpine をベースとした場合のサンプルを記載します:
特に注目してほしいのは、上記 Dockerfile 内の赤字部分です。ここで "apt install build-essensial libcairo2-dev libpango1.0-dev libjpeg-dev libgif-dev librsvg2-dev" に相当するコマンドを実行して docker build 時の "RUN npm install" 実行直前に依存ライブラリをインストールしています。"npm install" でインストールする各モジュールを "apk add" 時にはどのように指定するか、1つ1つ調べる必要があり、その結果がこれでした。
この1行を追加しておくことで直後の "RUN npm install" (ここで node-canvas をインストール)も成功するようになり、Docker コンテナ内で node-canvas を使ったアプリケーションが期待通りに動くようになりました。
(補足)
"node-canvas" と互換性のある "@napi-rs/canvas" というモジュールが存在していて、こちらを使うとネイティブライブラリのインストールは不要です。絵文字などのフォントも内蔵しているようで、これはこれで便利そうです。
ただ上述のコードでは "canvas.createPNGStream()" を実行している箇所がありますが、この関数は @napi-rs/canvas では未実装らしくエラーになってしまいました。もしかするとこちらにも同様の互換関数があるのかもしれませんが未調査です。
フロントエンドフレームワークではない、サーバーサイドアプリとしての Node.js で使える node-canvas というパッケージモジュールがあります:
https://www.npmjs.com/package/canvas
これは HTML5 の Canvas 互換機能をサーバーサイドで使えるようになるモジュールです。UI のないサーバーサイドで HTML5 の Canvas ??と思う人がいるかもしれませんが、例えば画像を生成して返すような API を作ろうとした際に(仮想的な)Canvas を生成してグラフィックを描画し、最後に Canvas を画像化して(image/png などのコンテントタイプを指定して)出力する、といった使い方ができて便利です:
var { createCanvas } = require( 'canvas' ); : : app.get( '/image', function( req, res ){
// (仮想的な)Canvas を生成 var canvas = createCanvas( 200, 100 );
// グラフィックコンテキストを取得(ここから先は HTML5 の場合と同様) var ctx = canvas.getContext( '2d' );
// ctx に目的のグラフィックを描画していく
ctx.beginPath(); ctx.moveTo( 100, 0 ); ctx.lineTo( 200, 100 ); ctx.strokeStyle = 'red'; ctx.stroke(); : :
// 最後に Canvas の内容を 'imaga/png' で出力する res.contentType( 'image/png' ); var stream = canvas.createPNGStream(); stream.pipe( res ); });
便利なのですが、実はこの node-canvas は利用する上での大きな制約があります。システムにあらかじめ特定のライブラリがネイティブインストールされている必要があり、その前提でインストールすることによって使える、というものです。つまり普通にインストールしようとして "$ npm install canvas" と実行するだけでは正しくインストールできないことがあり、その前に必要なライブラリをインストールしておく必要があるのでした。例えば Ubuntu の場合は以下を実行しておく必要があります(他のシステムの場合はこちらの compiling 節を参照ください):
$ sudo apt install build-essensial libcairo2-dev libpango1.0-dev libjpeg-dev libgif-dev librsvg2-dev
さて、アプリを普通に動かすだけならこの(ドキュメントに書かれている)前提コマンドを知っているだけでいいのですが、Docker コンテナ上で動かすとなると少し事情が変わってきます。システム(= Docker ホスト)にあらかじめインストールしたライブラリは関係なく、Docker コンテナとして動かす、そのコンテナ上に上述の依存ライブラリを導入しておく必要があります。しかも Docker コンテナとして動かす場合は Dockerfile 内でベースイメージを指定することになるのですが、このベースイメージによっても(追加で)インストールが必要なライブラリが変わってきます。そういった複雑な事情がある中で、どのようにすれば Node.js で node-canvas が動くコンテナを作れるようになるか、というのがこのブログエントリの(自分の備忘録としての)目的です。もちろん自分以外で同様に Docker コンテナ内で node-canvas を使いたい人がいたとしたら、(特に日本語だとなかなかまとまった情報が見つからないので)以下の情報が有用であると思っています。
答としてはこんな感じです。node:16-alpine をベースとした場合のサンプルを記載します:
# base image
FROM node:16-alpine
# working directory
WORKDIR /usr/src/app
COPY package*.json ./
RUN apk update && apk upgrade && apk add python3 alpine-sdk
# apt install build-essensial libcairo2-dev libpango1.0-dev libjpeg-dev libgif-dev librsvg2-dev
RUN apk add build-base cairo-dev pango-dev libjpeg-turbo-dev giflib-dev librsvg-dev
RUN npm install
COPY . .
EXPOSE 8080
CMD ["node", "app.js"]
特に注目してほしいのは、上記 Dockerfile 内の赤字部分です。ここで "apt install build-essensial libcairo2-dev libpango1.0-dev libjpeg-dev libgif-dev librsvg2-dev" に相当するコマンドを実行して docker build 時の "RUN npm install" 実行直前に依存ライブラリをインストールしています。"npm install" でインストールする各モジュールを "apk add" 時にはどのように指定するか、1つ1つ調べる必要があり、その結果がこれでした。
この1行を追加しておくことで直後の "RUN npm install" (ここで node-canvas をインストール)も成功するようになり、Docker コンテナ内で node-canvas を使ったアプリケーションが期待通りに動くようになりました。
(補足)
"node-canvas" と互換性のある "@napi-rs/canvas" というモジュールが存在していて、こちらを使うとネイティブライブラリのインストールは不要です。絵文字などのフォントも内蔵しているようで、これはこれで便利そうです。
ただ上述のコードでは "canvas.createPNGStream()" を実行している箇所がありますが、この関数は @napi-rs/canvas では未実装らしくエラーになってしまいました。もしかするとこちらにも同様の互換関数があるのかもしれませんが未調査です。