Memorystoreで危うく溶けそうになった話

# はじめに
株式会社トラストバンクで[地域通貨プラットフォーム chiica(チーカ)](https://chiica.jp/)の開発責任者をしております湊（みなと）（@karura618）です。
この記事は トラストバンク[Advent Calender 2024](https://qiita.com/advent-calendar/2024/trustbank) の記事です。

今回は[昨年の記事](https://qiita.com/karura618/items/3c443d40a2070e44a83a)に関連して、GCPのCloudRun + ~~Memorystore~~ Cloud Storage FUSE を使用したコンテナ間でのキャッシュ保持についての話を。

# タイトルの件
「はじめに」にすでに記載しててネタバレですが。
CloudRunにてある画面を作成した際、入力画面した情報をSessionで保持させようとしたのですが、CloudRun自体は最小インスタンス数をデフォルトの「0」に設定しているため[一定時間経過すると自動的に停止します](https://cloud.google.com/run/docs/configuring/cpu-allocation?hl=ja)。

そもそもオートスケーリングも設定してるので[セッションアフェニティ](https://cloud.google.com/run/docs/configuring/session-affinity?hl=ja)を設定しても同一コンテナで処理されるかどうかどうかは担保されない。。。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/308142/c26b3664-2fb9-3b5b-0904-365b9cc5181c.png)

さてどうしたものかと思いとりあえずMemorystore(Redis)上にSessionを保存しようかなと思ったら

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/308142/9050c00e-072f-c4a6-e542-f4a89019e576.png)
（10/3～12の青色箇所がMemorystoreのコスト）

...( ﾟдﾟ) ...(つд⊂)ｺﾞｼｺﾞｼ(;ﾟдﾟ)oh...

他コストを余裕で追い抜いた。。。
まあ、それまで一番コストかかってたのって[Container Registry Vulnerability Scanning](https://cloud.google.com/artifact-registry/docs/analysis?hl=ja)なのでほんとに小規模なものしか作っていないのですがそれにしても。。。

[わかってはいたけど](https://cloud.google.com/memorystore/docs/redis/pricing?hl=ja)改めてみると高い。。。マイクロサービスのSession保存するのに使うにしては高い。。。

# 代替案（Cloud Storage FUSE）
どうしたものかと思ったら知らないうちにCloudRunで[Cloud Storage FUSE](https://cloud.google.com/storage/docs/cloud-storage-fuse/overview)が使えるようになってた。

のでちょっと試しに使ってみました。

# 設定手順（for Laravel）
LaravelのSession、Cacheの各ファイルをGCS上にマウントするにあたり、以下のような設定をしました。

## config/session.php
```php
<?php

use Illuminate\Support\Str;

$file_path = !empty(env('STORAGE_PATH')) ? env('STORAGE_PATH') . env('APP_ENV') . '/framework/sessions' : storage_path(env('APP_ENV') . '/framework/sessions');
// 一時保存用ディレクトリがなければ作成
if (!file_exists($file_path)) {
    try {
        if (!mkdir($file_path, 0775, true) && !is_dir($file_path) && !touch($file_path . '/.ignore', time())) {
            throw new RuntimeException(sprintf('Directory "%s" was not created', $file_path));
        }
    } catch (Throwable $e) {
        Log::info($e->getMessage());
    }
}
return [

// 中略

    'files' => $file_path, // 変更
```

## config/cache.php
```php
<?php

use Illuminate\Support\Str;

$file_path = !empty(env('STORAGE_PATH')) ? env('STORAGE_PATH') . env('APP_ENV') . '/framework/cache/data' : storage_path(env('APP_ENV') . '/framework/cache/data');
// 一時保存用ディレクトリがなければ作成
if (!file_exists($file_path)) {
    try {
        if (!mkdir($file_path, 0775, true) && !is_dir($file_path) && !touch($file_path . '/.ignore', time())) {
            throw new RuntimeException(sprintf('Directory "%s" was not created', $file_path));
        }
    } catch (Throwable $e) {
        Log::info($e->getMessage());
    }
}

return [
// 中略
    'stores' => [

        'apc' => [
            'driver' => 'apc',
        ],

        'array' => [
            'driver' => 'array',
            'serialize' => false,
        ],

        'database' => [
            'driver' => 'database',
            'table' => 'cache',
            'connection' => null,
            'lock_connection' => null,
        ],

        'file' => [
            'driver' => 'file',
            'path' => $file_path, // 変更
        ],
```

## CloudRunの設定
ボリューム設定で「Cloud Storageバケット」を指定し、ボリュームマウントしたいバケットを選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/308142/a7022a75-3d88-9209-7c90-0f31e0ee4dd7.png)

あとは「コンテナ」⇒「ボリュームのマウント」からボリューム設定で追加したボリューム名を選択
マウント先となるパスを指定（特にこだわりなければ「mnt/gcs」が無難）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/308142/62eb2a68-7746-46b3-fdc4-ec7fd0a6374c.png)

これだけ。

# 劇的Before After
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/308142/cf0a7f69-060c-e198-a3b6-5c7659357381.png)
切り替え後は当然ながらMemorystoreのコストは消え且つ、FUSEによるコストが増えたりすることもない状態。
そもそもFUSE自体は料金が発生せず、あくまで[CloudStorageに関する料金](https://cloud.google.com/storage/pricing?hl=ja)しか発生しません。
やだ、めちゃくちゃお買い得。。。


# 注意点（宣言事項）
とはいえFUSEにも[制限事項](https://cloud.google.com/storage/docs/cloud-storage-fuse/overview#expandable-1)があり、以下のような用途には向かないです。

> ### メタデータ
> Cloud Storage FUSE は、mtime および symlink ターゲットを除き、Cloud Storage にファイルをアップロードするときにオブジェクト メタデータを転送しません。つまり、 Cloud Storage FUSE を使用してファイルをアップロードするときにオブジェクト メタデータを設定することはできません。オブジェクト メタデータを保持する必要がある場合は、 Google Cloud CLI、JSON API、または Google Cloud コンソールを使用してファイルをアップロードすることを検討してください 。
>
> ### 同時実行
> Cloud Storage FUSE では、同じファイルへの複数の書き込みに対して同時実行制御は提供されません。複数の書き込みがファイルを置き換えようとすると、最後の書き込みが優先され、それ以前の書き込みはすべて失われます。後続の上書きについては、マージ、バージョン管理、ユーザーへの通知は行われません。
>
> ### リンク
> Cloud Storage FUSE はハードリンクをサポートしていません。
ファイルのロックとファイルのパッチ適用: Cloud Storage FUSE は、ファイルのロックやファイルのパッチ適用をサポートしていません。バージョン管理システムはファイルのロックとパッチ適用に依存しているため、バージョン管理システム リポジトリを Cloud Storage FUSE マウント ポイントに保存しないでください。また、Cloud Storage FUSE をファイルの代替として使用しないでください。

# おわりに
ちょこちょこCloudRunでマイクロサービス作ってますがどんどん便利になってきてますね。
自分が使い始めのころはマルチステージビルドも対応してなかったのに（しみじみ


# エンジニア募集
弊社では絶賛エンジニア募集中です！
気になった方、是非お気軽に[Wantedly](https://www.wantedly.com/companies/trustbank/projects)からご連絡ください！