PhotoructionのWebエンジニアの下川原です。
今回は、AWS Lambdaについて語ろうと思います!
関連ワード
- Lambda
- nodejs
- sharp
- 画像処理
- 雑談
もくじ
1.AWS Lambdaが変わったこと
参考:https://aws.amazon.com/jp/blogs/aws/aws-lambda-now-supports-up-to-10-gb-ephemeral-storage/
Lambdaはもともと512MBのtmpが提供されていた。
しかし、2022/3/24にアップデートがされた。要約すると
Lambdaが提供するtmpを512MBから10GBまで増やせるよ!
※ただし512MB以上の容量追加は追加料金あるよ!
やったね!
2.Lambdaに何をしてしまったのか
'use strict';
const sharp = require('sharp');
exports.mainHandler = async (event, context, callback) => {
let work_folder = createWorkDirectoryStructure();
//画像をDLしてくる処理
let src = getSourceData(event.data.bucket, event.data.image_path, work_folder);
toProcessImage(src, work_folder, event.data.image_name);
CleanupTempDirectory(work_folder);
return true;
};
const toProcessImage = async (src, output_base_path, slice_file_base_name) => {
let src_img = sharp(src);
let tmp_path = path.join(output_base_path, slice_file_base_name);
//切り取り処理 数字は適当
await src_img.extract({
left: 100,
top: 200,
width: 500,
height: 500,
})
.toFile(tmp_path);//加工した画像を配置する処理
}
const createWorkDirectoryStructure = () => {
let work_folder = path.join('/', 'tmp', 'work-' + shortid.generate());
if (!fs.existsSync(work_folder)) {
fs.mkdirSync(work_folder);
}
return work_folder;
}
const getSourceData = (bucket, key, directory) => {
let file_extension = path.extname(key);
let dst = path.join(directory, `src${file_extension}`);
return downloadFile(bucket, key, dst);
}
//ディレクトリを削除する処理
const CleanupTempDirectory = (dir) => {
if (fs.existsSync(dir)) {
fs.rmdir(dir, {"recursive": true}, (error) => {
if (error) {
console.log("clean up progress: " + error);
}
});
console.log("Cleanup complete");
}
}
こんなように、適当に画像を切り出す処理があったとする。
AWSコンソールから、
テスト実行・・・成功
テスト実行・・・成功
テスト実行・・・失敗
エラー:No space left on device
短い期間、ほぼ連続で実行するとエラーになる
なぜ?
容量不足と出ているなら、この処理のどれかが悪さをしているのだろう。
・画像をDLしてくる処理
・加工した画像を配置する処理
・ディレクトリを削除する処理
Lambdaコンテナの中で何がおきてるか、確認するのが早いだろう(Linuxのコマンドが使えてよかった・・)
処理のはじめと終わりに以下のコードを追加
console.log(execSync("df -h").toString());
console.log(execSync("du -h /tmp").toString());
コード挿入後の実行ログ
1回目テスト実行・・・成功
INFO Filesystem Size Used Avail Use% Mounted on
/mnt/root-rw/opt/amazon/asc/worker/tasks/rtfs/nodejs14.x-amzn-2 9.8G 8.8G 947M 91% /
/dev/vdb 1.5G 14M 1.4G 1% /dev
/dev/vdd 526M 872K 514M 1% /tmp
/dev/root 9.8G 8.8G 947M 91% /etc/passwd
/dev/vdc 9.5M 9.5M 0 100% /opt
INFO 4.0K /tmp
INFO Cleanup complete
INFO Filesystem Size Used Avail Use% Mounted on
/mnt/root-rw/opt/amazon/asc/worker/tasks/rtfs/nodejs14.x-amzn-2 9.8G 8.8G 947M 91% /
/dev/vdb 1.5G 14M 1.4G 1% /dev
/dev/vdd 526M 178M 337M 35% /tmp
/dev/root 9.8G 8.8G 947M 91% /etc/passwd
/dev/vdc
INFO 4.0K /tmp
2.7M /tmp
2回目テスト実行・・・成功
INFO Filesystem Size Used Avail Use% Mounted on
/mnt/root-rw/opt/amazon/asc/worker/tasks/rtfs/nodejs14.x-amzn-2 9.8G 8.8G 947M 91% /
/dev/vdb 1.5G 14M 1.4G 1% /dev
/dev/vdd 526M 178M 337M 35% /tmp
/dev/root 9.8G 8.8G 947M 91% /etc/passwd
/dev/vdc 9.5M 9.5M 0 100% /opt
INFO 4.0K /tmp
INFO Cleanup complete
INFO Filesystem Size Used Avail Use% Mounted on
/mnt/root-rw/opt/amazon/asc/worker/tasks/rtfs/nodejs14.x-amzn-2 9.8G 8.8G 947M 91% /
/dev/vdb 1.5G 14M 1.4G 1% /dev
/dev/vdd 526M 355M 160M 70% /tmp
/dev/root 9.8G 8.8G 947M 91% /etc/passwd
/dev/vdc
INFO 4.0K /tmp
2.7M /tmp
3回目テスト実行・・・失敗
INFO Filesystem Size Used Avail Use% Mounted on
/mnt/root-rw/opt/amazon/asc/worker/tasks/rtfs/nodejs14.x-amzn-2 9.8G 8.8G 947M 91% /
/dev/vdb 1.5G 14M 1.4G 1% /dev
/dev/vdd 526M 355M 160M 70% /tmp
/dev/root 9.8G 8.8G 947M 91% /etc/passwd
/dev/vdc 9.5M 9.5M 0 100% /opt
INFO 4.0K /tmp
INFO Cleanup complete
INFO Filesystem Size Used Avail Use% Mounted on
/mnt/root-rw/opt/amazon/asc/worker/tasks/rtfs/nodejs14.x-amzn-2 9.8G 8.8G 947M 91% /
/dev/vdb 1.5G 14M 1.4G 1% /dev
/dev/vdd 526M 512M 2.4M 100% /tmp
/dev/root 9.8G 8.8G 947M 91% /etc/passwd
/dev/vdc
INFO 4.0K /tmp
2.7M /tmp
順調に増えてる・・・
画像はそこまでインパクトは無い・・・
作業ディレクトリはエラー無くしっかり消せている・・・
・画像をDLしてくる処理
・加工した画像を配置する処理
・ディレクトリを削除する処理
//切り取り処理 数字は適当
await src_img.extract({
left: 100,
top: 200,
width: 500,
height: 500,
})
.toFile(tmp_path);//加工した画像を配置する処理
どうやら、toFile時に
関数とは違う実行ユーザーでファイルが置かれてしまうらしい
lrwx------ 1 sbx_user1051 990 64 Dec 3 01:43 24 -> /tmp/vips-0-3816661850.v (deleted)
lrwx------ 1 sbx_user1051 990 64 Dec 3 01:43 25 -> /tmp/vips-1-2724960011.v (deleted)
lrwx------ 1 sbx_user1051 990 64 Dec 3 01:43 26 -> /tmp/vips-2-485543279.v (deleted)
lrwx------ 1 sbx_user1051 990 64 Dec 3 01:43 27 -> /tmp/vips-3-930892267.v (deleted)
どおりで、/tmp配下に存在しない”ように”見えてしまっていたわけ
3.解決方法
では、上記の問題をどう解決したか?
ファイルを開く時に、メモリ経由かディスク経由かを判定するために
サイズを指定するパラメータ(VIPS_DISC_THRESHOLD)があり、
これを適切に設定しないと、ストレージが枯渇してしまう問題を見つけた。
https://github.com/lovell/sharp/issues/707
https://github.com/lovell/sharp/issues/1851
つまり、VIPS_DISC_THRESHOLDの設定を極端に少なくすれば常にメモリ経由で処理を継続してくれるというのだ。
Lambdaの環境変数に次のように追加することで設定可能
VIPS_DISC_THRESHOLD=10M
すると何回連続して実行を行っても、問題がおきることは無くなった。
4.さいごに
このような問題を発見、解決した数日後に「tmpを拡張可能にしたよ!」の記事が・・・。
さすがに数日で対応したわけは無いと思う、自由に使えるtmp容量を増やしてほしいという声は、
LambdaでAWS EFSを活用する記事をみると前々から需要があったのだろう。
気軽にtmpを拡張することができるので、ディスク上で操作する幅が広がったLambda
最後に改めて紹介しよう。
https://aws.amazon.com/jp/blogs/aws/aws-lambda-now-supports-up-to-10-gb-ephemeral-storage/
株式会社フォトラクションでは一緒に働く仲間を募集しています