AWS Synthetics Canaryで日本語フォントを設定する


:::note info
syn-nodejs-puppeteer-3.7 で修正されたようです。
[Node.js と Puppeteer を使用するランタイムバージョン - syn-nodejs-puppeteer-3.7](https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/CloudWatch_Synthetics_Library_nodejs_puppeteer.html#CloudWatch_Synthetics_runtimeversion-nodejs-puppeteer-3.7)
:::

syn-nodejs-puppeteer-3.4まではスクリーンショットを撮影した際に、普通に日本語フォントは表示されていたのですが、syn-nodejs-puppeteer-3.5以降でスクリーンショットの日本語フォント部分が豆腐になった人向けです。

AWS Synthetics Canaryの記事をよむと、リリース当初頃、日本語が豆腐になるというのを読むことがありますが、私はsyn-nodejs-puppeteer-3.3から始めたので、日本語フォントに苦労したことはなかったです。しばらくして 3.5で豆腐になりました。

AWSサポートにも問合せしましたが、同様の問い合わせをいただいていて開発にフィードバック中とのことでした。
そのうち修正されるかなとおもったのですが、3.6になっても修正されていないようなので調査をしました。


## 原因

fonts.conf の変更により、/opt/fonts/.fonts/ 以下においてある日本語フォントを読みにいかなくなったためです。

## 対応

canaryのスクリプトで/opt/fonts/.fonts/*.otf を /tmp/.fonts/以下にコピーして、synthetics.launch() しなおす。

## fonts.confの変更点

syn-nodejs-puppeteer-3.4 と 3.6 のfonts.confの一部を下に示します。
fonts.confですが、`$FONTCONFIG_PATH`が指定されているため、`/tmp/aws/fonts.conf`になります。

```xml
<?xml version="1.0" ?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>
  <dir>/tmp/aws/.fonts</dir>
  <dir>~/.fonts</dir>
  <!-- syn-nodejs-puppeteer-3.4 (中略)-->
</fontconfig>
```

syn-nodejs-puppeteer-3.6 のfonts.conf

```xml
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>
  <dir>/tmp/aws/.fonts</dir>
  <dir>/tmp/.fonts</dir>
  <dir>/opt/.fonts</dir>
  <!-- syn-nodejs-puppeteer-3.6 (中略)-->
</fontconfig>
```

3.4では `/tmp/aws/.fonts`, `~/.fonts` だったものが、3.6から `/tmp/aws/.fonts`, `/tmp/.fonts`, `/opt/.fonts` となっています。
`~/.fonts`がなくなったことで、日本語フォントをよめなくなりました。

`~/.fonts`ってどこだよ?って話ですが、`$HOME` は `/opt/fonts` です。
これはlambdaに移動して、スクリプトを読むとそうなっているのが分かります。22行目です。

```js
const log = require('SyntheticsLogger');
const synthetics = require('Synthetics');
const syntheticsUploader = require('SyntheticsUploader');
const fs = require('fs');

exports.handler = async (event, context) => {
    const PASS_RESULT = 'PASSED';
    const FAIL_RESULT = 'FAILED';
    const NO_RESULT = 'ERROR';

    let canaryResult = NO_RESULT;
    let canaryError = null;
    let startTime = null;
    let endTime = null;
    let returnValue = null;
    let resetTime = null;
    let setupTime = null;
    let launchTime = null;
    let customerCanaryCompleted = false;
    try {
        // Setting HOME environment variable for fonts
        process.env.HOME = '/opt/fonts'; // <-ココ
// 以下、略
```

/opt/fonts/.fonts を調べると以下のようになっています。
フォントはあるけど、3.4では読み込めて、3.5以降では読み込めていないんですね。

```txt
/opt/fonts/.fonts
  - NotoSansCJKjp-Regular.otf
  - NotoSansCJKsc-Regular.otf
  - NotoSansCJKtc-Regular.otf
```

まとめると、これまではfonts.conf の `~/.fonts` の記述により、`/opt/fonts/.fonts` 以下のフォントファイルがよみこまれていたが、3.5からfonts.confが変わって、読み込まれなくなったということになります。

## 修正

ということでlambdaでも書き込みやすそうな `/tmp/.fonts` に `/opt/fonts/.fonts` 以下にある日本語フォントをコピーすることにします。

以下のようなスクリプトで `/tmp/.fonts` にフォントをコピーします。

```js
    const { exec } = require('child_process');
    function sh(cmd){
        return new Promise((ok, ng) => {
            exec(cmd, (err, stdout, stderr) => {
                if (err) {
                    ng(stderr);
                } else {
                    ok(stdout);
                }
            });
        });
    }

    await sh('mkdir -p /tmp/.fonts')
    await sh('cp -t /tmp/.fonts /opt/fonts/.fonts/*.otf');
    await synthetics.launch({});
```

他にも、以下のような方法が考えられます。

- x `/opt/.fonts/` に必要なフォントを含んだレイヤーを作成しておき、Canary作成後、Lambdaの方でレイヤーを挿入する
    - やってみたけど、うまくいかなかった。Canaryが呼び出すLambdaはバージョンが指定されていて、レイヤーを追加して、新しい関数バージョンを発行しても、Canary側でその関数バージョンを指定する方法がないためです。
- Canaryのコードzipに `.fonts` ディレクトリを作成し、フォントファイルを含めておく
    - ファイルサイズが大きくなりますが、これは成功します。以下のようなディレクトリレイアウトになります。
    

```txt
- nodejs
    - node_modules
- .fonts
    - NotoSansJP-Regular.otf
```


## 所感


Lambdaであれば、レイヤーとしてフォントファイルを含めたものを用意すればよいと思うのだけど、Canaryだとずいぶんハードルが高くないか? という気分になる。ただしカナリーのスクリーンショットの再現性を高める目的があれば、フォントを同梱することはあるかもしれない。

しかしBlueprintのモニタリングなどを使用してもスクリーンショットで日本語が豆腐になるし、カナリーのコードがzip作成が必須になってくる点を考えるとAWS側で何か修正を行ってほしい。思いつくのは以下の3つ。

1. `fonts.conf` に `/opt/fonts/.fonts` を含める
1. `/tmp/aws` 以下に日本語フォントを入れておく
1. Canaryのオプションとして、作成されるLambdaに追加レイヤーを指定するオプションの追加

- 3.4 -> 3.5の変更時に `/opt/fonts/.fonts` を `/opt/.fonts` に間違えたよね。これ。
    - それともCanaryのzipに.fontsファイルを含めることでカスタムフォントを使えるような修正なのだろうか?

## 追記 / syn-nodejs-puppeteer-3.8で確認

syn-nodejs-puppeteer-3.8がリリースされていたので3.7ではなく、3.8で確認しました。
結果的に /opt/.fonts に NotoSansCJK のフォントがありました。

```sh
ls -la /opt/.fonts
total 48117drwxr-xr-x 2 root root      114 Oct 17 17:19 .
drwxr-xr-x 6 root root       93 Nov 17 02:12 ..
-rwxr-xr-x 1 root root 16427228 Oct 17 17:19 NotoSansCJKjp-Regular.otf
-rwxr-xr-x 1 root root 16412332 Oct 17 17:19 NotoSansCJKsc-Regular.otf
-rwxr-xr-x 1 root root 16431292 Oct 17 17:19 NotoSansCJKtc-Regular.otf
```