【Laravel】S3 の代わりに Linode オブジェクト・ストレージ を使う

こんにちは❗フリーランス・エンジニアの 九保すこひ です。

さてさて、先日「S3 の代わりに Cloudflare R2 ストレージを使う」という記事を書いたのですが、その中でCloudflare以外にも「Amazon S3 と互換性がある」ストレージサービスがあることを知りました。

それが・・・・・・

Linode Object Storage

です。

この「Linode object storage」もCloudflare R2に負けず劣らず料金が安いので、もし「S3、ちょっと高いよ…😣」と感じている方には選択肢のひとつになるんじゃないでしょうか。

ちなみに料金は次のようになっています。(2022.5.20 現在)

  • 250 GB: 月額 $5
  • 転送コストは、1TBまで無料

※ 詳しくは、本家のページをご覧ください。日本語対応してます👍

そこで❗

今回も、「Laravel + Linode Object Storage」でS3の代替機能をつくってみたいと思います。

ぜひ何かの参考になりましたら嬉しいです。😄✨

「ピザと一緒に
味噌汁&餃子を食べると、
グローバル化できた気分ですね」

開発環境: Laravel 9.x

Linode に登録する

では、まずはLinodeにユーザー登録します。
Linode のトップページへ移動しページ右上にある「登録」ボタンをクリックしてください。

メールアドレス」「ユーザー名」「パスワード」を入力し、「Continue」ボタンをクリック。

すると、入力したメールアドレスに「本登録のURL」が送信されてくるので、この中にある「Confirm Email」をクリック。

すると、住所や支払い情報を入力するフォームが表示されるので、以下を参考にしてそれぞれ入力し「Create Account」ボタンをクリックしてください。

Billing information

  • First Name: 名
  • Last Name: 姓
  • Company Name: 会社名(省略可)
  • Address: (市区町村以降の)住所
  • Address 2:ビル名など(省略可)
  • Country: Japan
  • Region: 都道府県
  • City: 市区町村
  • Postal Code: 郵便番号
  • Phone: 電話番号(省略可)
  • Tax ID: 納税者番号(省略可)

Secure Payment

  • Credit Card Number: クレジットカード番号
  • Expiration: クレジットカードの有効期限
  • CVV: クレジットカードの裏に書かれている3〜4桁の番号
  • Master Services Agreement: 利用規約への同意です。規約を読んでチェックしてください。

送信すると以下のように完了画面が表示されるので「Go to Cloud Manager」ボタンをクリックし、ダッシュボードへ移動しましょう。

これで、登録は完了です❗

バケット(フォルダ)をつくる

では、続いて「Object Storage」でバケット(保存フォルダ)をつくっていきましょう。

ページ左側メニューにある「Object Storage」をクリックします。

Object Storageのトップページへ移動するので、「Create Bucket」ボタンをクリックします。

バケットの情報を入力するフォームが表示されるので、Labelにバケット名、Regionに保存する地域(シンガポールが一番近いので、速度のことを考えてシンガポールにしました)を選択し「Create Bucket」をクリックします。

※ なお、どうやら東京にも「オブジェクトストレージ」サーバーが設置されるようなので、もし東京が存在していたら、そちらの方がより良いでしょう👍

すると、事前に支払いに関する情報がポップアップされるので、「Enable Object Storage」ボタンをクリックして有効にします。

登録が完了すると、以下のように表示されます。

では、バケット名を以下のように.envへセットしておきましょう。

.env

LINODE_OBJECT_STORAGE_BUCKET="**********"

また、「エンドポイントURL」と「Region」は以下ページの「Cluster URL (S3 endpoint)」で公開されているので、そちらを参照してください。

📝 参考ページ: Guides – Access Buckets and Files through URLs

.env

LINODE_OBJECT_STORAGE_REGION="ap-south-1"
LINODE_OBJECT_STORAGE_ENDPOINT="https://ap-south-1.linodeobjects.com"

※ シンガポールを選択した場合はコピペーでOKです。

API キーを取得する

では、続いてLaravel側からLinodeへアクセスするための「API アクセスキー」&「API シークレットキー」を取得します。

Object Storageトップページにある「Access Keys」をクリックします。

まだ「API キー」は存在していないので、ページ右上にある「Create Access Key」ボタンをクリックし、登録をしていきます。

Labelに識別しやすい(お好みの)名前を入力し、もし条件指定したい場合は「Limited Access」をオンにしてそれぞれ選択し、最終的に「Create Access Key」ボタンをクリックしてください。

すると、「Access Key」と「Secret Key」が表示されるので、これも.envへセットしておきます。

.env

LINODE_OBJECT_STORAGE_ACCESS_KEY="**********************"
LINODE_OBJECT_STORAGE_SECRET_KEY="***************************"

これで、API キーの取得も完了です❗

Laravel を Linode に対応させる

Linode Object Storageは、Cloudflare R2と同じく、Amazon S3との互換性があるので、Amazon S3用のパッケージをインストールします。

以下のコマンドを実行してください。

composer require league/flysystem-aws-s3-v3 "^3.0"

ただし、このままではLinode object storageへのアクセスが設定されていませんので、以下の情報を新規追加します。

config/filesystems.php

// 省略

'disks' => [

    // 省略

    'linode' => [
        'driver' => 's3', // 👈 互換性があるので、ここは s3 で OK
        'key' => env('LINODE_OBJECT_STORAGE_ACCESS_KEY'),
        'secret' => env('LINODE_OBJECT_STORAGE_SECRET_KEY'),
        'region' => env('LINODE_OBJECT_STORAGE_REGION'),
        'bucket' => env('LINODE_OBJECT_STORAGE_BUCKET'),
        'endpoint' => env('LINODE_OBJECT_STORAGE_ENDPOINT'),
    ],

],

これで準備は完了です❗

テストしてみる

では実際のテストしてみましょう。

まずは、/storage/app/imagesに以下の画像をセットします。

そして、折角なので今回はユニットテスト(フィーチャーテスト)をつくって実際にテストしてみましょう。

プライベート(ブラウザからはアクセスできない)アップロード・テスト

まずは URL として公開しない「プライベート」設定のアップロードで、内容は以下の2つです。

  • ファイルがアップロードできるか(ネットに公開はしない)
  • アップロードしたファイルがバケットに存在しているかチェック

では、以下のコマンドを実行してください。

php artisan make:test LinodeObjectStorageTest

すると、ファイルが作成されるので、中身を以下のように変更します。

tests/Feature/LinodeObjectStorageTest.php

<?php

namespace Tests\Feature;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Support\Facades\Storage;
use Tests\TestCase;

class LinodeObjectStorageTest extends TestCase
{
    const FILENAME = 'no_15.png';

    /**
     * アップロードできるかチェック
     */
    public function test_can_upload()
    {
        $filename = self::FILENAME;
        $path = storage_path('app/images/'. $filename);
        $content = file_get_contents($path);
        $result = Storage::disk('linode')->put($filename, $content);

        $this->assertTrue($result);
    }

    /**
     * アップロードしたファイルが存在するかチェック
     */
    public function test_can_confirm_file_exists()
    {
        $filename = self::FILENAME;
        $exists = Storage::disk('linode')->exists($filename);

        $this->assertTrue($exists);
    }
}

では、以下のコマンドを実行してみましょう。

php artisan test --filter LinodeObjectStorageTest

すると・・・・・・

はい❗
うまくいったようです。

では、Linodeの方でもページをリロードして確認してみましょう。

どうなったでしょうか・・・・・・

はい❗
ファイルがアップロードされていることを確認しました。

成功です😄✨

URL として公開する「パブリック」アップロード・テスト

ただし、これは「プライベート設定」のアップロードですので、ブラウザで直接アクセスしても以下のように拒否されてしまいます。

ということで、インターネットから直接アクセスできるよう「公開設定」をしてアップロードしてみましょう。(先ほどアップロードしたファイルは削除しておいてください)

テスト内容は以下2つです。

  • ファイルがアップロードできるか(ネット上に公開する)
  • アップロードした URL にアクセスできるかチェック

以下が公開設定バージョンのテストになります。

tests/Feature/LinodeObjectStorageTest.php

<?php

namespace Tests\Feature;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Storage;
use Tests\TestCase;

class LinodeObjectStorageTest extends TestCase
{
    const FILENAME = 'no_15.png';

    /**
     * 公開設定でアップロードできるかチェック
     */
    public function test_can_upload_publicly()
    {
        $filename = self::FILENAME;
        $path = storage_path('app/images/'. $filename);
        $content = file_get_contents($path);
        $result = Storage::disk('linode')->put($filename, $content, 'public'); // 👈 第3引数を public にする

        $this->assertTrue($result);
    }

    /**
     * アップロードしたファイルが URL としてアクセスできるかチェック
     */
    public function test_can_access_url_online()
    {
        $bucket = config('filesystems.disks.linode.bucket');
        $region = config('filesystems.disks.linode.region');
        $filename = self::FILENAME;
        $url = 'https://'. $bucket .'.'. $region .'.linodeobjects.com/'. $filename;

        $response = Http::get($url);
        $this->assertTrue($response->successful());
    }
}

では、こちらも実行してみましょう。

ドキドキ・・・・・・

はい❗公開設定バージョンもうまくいきました。

では、念のためブラウザからもアクセスできるかチェックしてみましょう。

はい❗
ブラウザからもアクセスできます。

時間制限がある「署名つきURL」のアップロード・テスト

では、最後に時間制限がある「署名付きURL」がうまくいくかチェックしてみましょう。

ということで、以下は署名つきURLバージョンです。

tests/Feature/LinodeObjectStorageTest.php

<?php

namespace Tests\Feature;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Storage;
use Tests\TestCase;

class LinodeObjectStorageTest extends TestCase
{
    const FILENAME = 'no_15.png';

    /**
     * 署名付きURL用にアップロードできるかチェック
     */
    public function test_can_upload_for_sign_url()
    {
        $filename = self::FILENAME;
        $path = storage_path('app/images/'. $filename);
        $content = file_get_contents($path);
        $result = Storage::disk('linode')->put($filename, $content); // プライベートとしてアップロード

        $this->assertTrue($result);
    }

    /**
     * 署名付き URL がうまく動くかチェック
     */
    public function test_can_see_signed_url_works()
    {
        $expiration_seconds = 3; // 有効期限は3秒
        $filename = self::FILENAME;
        $expires = now()->addSeconds($expiration_seconds);
        $signed_url = Storage::disk('linode')->temporaryUrl($filename, $expires); // 署名付きURL
        $response = Http::get($signed_url);

        $this->assertTrue($response->successful()); // 有効期限内ならアクセスできる

        sleep($expiration_seconds); // 3秒待つ
        $response = Http::get($signed_url);

        $this->assertTrue($response->failed()); // 有効期限切れならアクセスできない
    }
}

では、実行します。

はい❗
署名つきバージョンもテストを通過しました。

すべて成功です😄✨

※ なお、実際のテストでは、テスト終了時に実行されるコールバックをセットし、その中でアップロードしたファイルを削除するといいでしょう。

// 省略

public function setUp(): void
{
    parent::setUp();

    $this->beforeApplicationDestroyed(function() {

        // ここで削除処理

    });
}

// 省略

企業様へのご提案

もしAmazon S3と全く同じ利用をしたと仮定すると、Linodeの場合は利用料金をコストカットできることが想定されます。

ちなみにLinodeのページでもAWSGoogle Cloudとの利用料金比較を掲載しているので、ぜひご覧になってみてください。

📝 参考URL: 高い転送コストを避けて節約しよう!

このように、もし固定費を恒久的にコストカットしたいとお考えでしたら、ぜひお問い合わせからご連絡ください。

お待ちしております。😄✨

おわりに

ということで、今回は「Laravel + Linode Object Storage」でAmazon S3の代替機能をつくってみました。

料金に関しては、Cloudflare R2の転送料金が無料ですので、とても有利なのですが(個人的にですが)、Linodeの方が安定している印象を受けましたので、クライアント様方にはおすすめかな、といった印象です。

また、途中でも書きましたがLinodeは東京リージョンでもオブジェクト・ストレージを開始する予定ということなので、さらに日本でも使い勝手がよくなる気がしています。

最後はステマっぽくなってしまいましたが😂、そのあたりはぜひ皆さんご自身で試してみてくださいね。

ではでは〜❗

「風呂上がりは、
帽子をかぶって
縮毛矯正しています👍」

開発のご依頼お待ちしております 😊✨
開発のご依頼はこちらから: お問い合わせ
どうぞよろしくお願いいたします!
このエントリーをはてなブックマークに追加       follow us in feedly  

開発効率を上げるための機材・まとめ