管理対象データベースとオブジェクトストレージを使用してスケーラブルなLaravel6アプリケーションをセットアップする方法

提供:Dev Guides
移動先:案内検索

序章

Webアプリケーションを水平方向にスケーリングする場合、通常直面する最初の問題は、ファイルストレージとデータの永続性の処理です。 これは主に、複数のアプリケーションノード間で可変データの一貫性を維持することが難しいという事実によるものです。 あるノードで作成されたデータをクラスター内の他のノードですぐに利用できるようにするには、適切な戦略を立てる必要があります。

整合性の問題を解決する実際的な方法は、管理対象データベースおよびオブジェクトストレージシステムを使用することです。 前者はデータの永続性を管理対象データベースにアウトソーシングし、後者は静的ファイルやユーザーがアップロードした画像などの可変コンテンツを保持できるリモートストレージサービスを提供します。 その後、各ノードはアプリケーションレベルでこれらのサービスに接続できます。

次の図は、PHPアプリケーションのコンテキストで、このようなセットアップを水平方向のスケーラビリティーに使用する方法を示しています。

このガイドでは、既存のLaravel 6アプリケーションを更新して、管理対象のMySQLデータベースに接続し、ユーザーが生成したファイルを保存するようにS3互換のオブジェクトストアを設定することで、水平方向のスケーラビリティに備えます。 最終的に、Nginx +PHP-FPMWebサーバーで旅行リストアプリケーションを実行できるようになります。

:このガイドでは、 DigitalOcean ManagedMySQLおよびSpacesを使用して、管理対象データベースとオブジェクトストレージを使用したスケーラブルなアプリケーションセットアップを示します。 ここに含まれる手順は、他のサービスプロバイダーでも同様に機能するはずです。


前提条件

このチュートリアルを開始するには、最初に次の前提条件が必要です。

  • sudo権限を持つ非rootユーザーとしてUbuntu18.04サーバーにアクセスし、サーバーにアクティブなファイアウォールをインストールします。 これらを設定するには、 Ubuntu18.04の初期サーバー設定ガイドを参照してください。
  • Ubuntu18.04にLEMPをインストールする方法の手順1および3で説明されているように、NginxおよびPHP-FPMがサーバーにインストールおよび構成されています。 MySQLがインストールされているステップはスキップする必要があります。
  • Ubuntu18.04にComposerをインストールして使用する方法の手順1および2で説明されているように、サーバーにComposerがインストールされています。
  • 管理対象のMySQL8データベースへの管理者クレデンシャル。 このガイドでは、 DigitalOcean Managed MySQL クラスターを使用しますが、ここでの手順は、他のマネージドデータベースサービスでも同様に機能するはずです。
  • S3互換のオブジェクトストレージサービスへの読み取りおよび書き込み権限を持つAPIキーのセット。 このガイドでは、 DigitalOcean Spaces を使用しますが、選択したプロバイダーを自由に使用できます。
  • s3cmdツールは、オブジェクトストレージドライブに接続するようにインストールおよび構成されています。 DigitalOcean Spacesでこれを設定する方法については、製品ドキュメントを参照してください。

ステップ1—MySQL8クライアントのインストール

デフォルトのUbuntuaptリポジトリにはMySQL5クライアントが付属していますが、これはこのガイドで使用するMySQL8サーバーと互換性がありません。 互換性のあるMySQLクライアントをインストールするには、Oracleが提供するMySQLAPTリポジトリを使用する必要があります。

WebブラウザのMySQLAPTリポジトリページに移動することから始めます。 右下隅にあるダウンロードボタンを見つけて、クリックして次のページに進みます。 このページでは、OracleWebアカウントにログインまたはサインアップするように求められます。 それをスキップして、代わりにというリンクを探すことができます。いいえ、ダウンロードを開始してください。 リンクアドレスをコピーして、ターミナルウィンドウに戻ります。

このリンクは、サーバーにMySQLAPTリポジトリをセットアップする.debパッケージを指している必要があります。 インストール後、aptを使用してMySQLの最新リリースをインストールできるようになります。 curlを使用して、このファイルを一時的な場所にダウンロードします。

サーバーのtmpフォルダーに移動します。

cd /tmp

次に、curlを含むパッケージをダウンロードし、MySQLAPTリポジトリページからコピーしたURLを使用します。

curl -OL https://dev.mysql.com/get/mysql-apt-config_0.8.13-1_all.deb

ダウンロードが完了したら、dpkgを使用してパッケージをインストールできます。

sudo dpkg -i mysql-apt-config_0.8.13-1_all.deb

デフォルトとして選択するMySQLバージョンと、関心のあるMySQLコンポーネントを選択できる画面が表示されます。

デフォルトのオプションでは必要なリポジトリがインストールされるため、ここでは何も変更する必要はありません。 「OK」を選択すると、設定が完了します。

次に、aptキャッシュを次のように更新する必要があります。

sudo apt update

これで、最終的にMySQL8クライアントを次のようにインストールできます。

sudo apt install mysql-client

そのコマンドが終了したら、ソフトウェアのバージョン番号をチェックして、最新のリリースであることを確認します。

mysql --version

次のような出力が表示されます。

Outputmysql  Ver 8.0.18 for Linux on x86_64 (MySQL Community Server - GPL)

次のステップでは、MySQLクライアントを使用して管理対象のMySQLサーバーに接続し、アプリケーション用のデータベースを準備します。

ステップ2—新しいMySQLユーザーとデータベースを作成する

この記事の執筆時点では、ネイティブMySQLPHPライブラリmysqlnd は、MySQL8のデフォルトの認証方法であるcaching_sha2_authenticationをサポートしていません。 LaravelアプリケーションをMySQL8サーバーに接続できるようにするには、mysql_native_password認証方式で新しいユーザーを作成する必要があります。 また、デモアプリケーション専用のデータベースを作成します。

開始するには、管理者アカウントを使用してサーバーにログインします。 強調表示された値を独自のMySQLユーザー、ホスト、およびポートに置き換えます。

mysql -u MYSQL_USER -p -h MYSQL_HOST -P MYSQL_PORT

プロンプトが表示されたら、管理者ユーザーのパスワードを入力します。 ログインすると、MySQL8サーバーのコマンドラインインターフェイスにアクセスできるようになります。

まず、アプリケーション用の新しいデータベースを作成します。 次のコマンドを実行して、travellistという名前の新しいデータベースを作成します。

CREATE DATABASE travellist;

次に、このユーザーのデフォルトの認証方法としてmysql_native_passwordを使用して、新しいユーザーを作成し、パスワードを設定します。 強調表示された値を独自の値に置き換え、強力なパスワードを使用することをお勧めします。

CREATE USER 'travellist-user'@'%' IDENTIFIED WITH mysql_native_password BY 'MYSQL_PASSWORD';

次に、アプリケーションデータベースに対するこのユーザー権限を付与する必要があります。

GRANT ALL ON travellist.* TO 'travellist-user'@'%';

これで、次のコマンドでMySQLプロンプトを終了できます。

exit;

これで、Laravelアプリケーションから接続するための専用データベースと互換性のあるユーザーができました。 次のステップでは、アプリケーションコードを取得して構成の詳細を設定し、アプリが管理対象のMySQLデータベースに接続できるようにします。

このガイドでは、 LaravelMigrationsデータベースシードを使用してアプリケーションテーブルを設定します。 既存のローカルデータベースをDigitalOceanマネージドMySQLデータベースに移行する必要がある場合は、MySQLデータベースをDigitalOceanマネージドデータベースにインポートする方法に関するドキュメントを参照してください。


ステップ3—デモアプリケーションのセットアップ

開始するには、デモLaravelアプリケーションをGithubリポジトリからフェッチします。 次のコマンドを実行する前に、アプリケーションの内容を自由に調べてください。

デモアプリケーションは、 Ubuntu18.04でLEMPを使用してLaravelをインストールおよび構成する方法に関するガイドで最初に開発されたトラベルバケットリストアプリです。 更新されたアプリには、訪問者がアップロードできる旅行写真や世界地図などの視覚的な改善が含まれています。 また、artisanコマンドを使用して、アプリケーションテーブルを作成し、サンプルデータを入力するためのデータベース移行スクリプトとデータベースシードも紹介します。

このチュートリアルと互換性のあるアプリケーションコードを入手するには、Githubのプロジェクトのリポジトリから1.1リリースをダウンロードします。 ダウンロードしたzipファイルをtravellist.zipとしてホームディレクトリに保存します。

cd ~
curl -L https://github.com/do-community/travellist-laravel-demo/archive/1.1.zip -o travellist.zip

次に、アプリケーションのコンテンツを解凍し、ディレクトリの名前を次のように変更します。

unzip travellist.zip
mv travellist-laravel-demo-1.1 travellist

travellistディレクトリに移動します。

cd travellist

先に進む前に、Laravelフレームワークに必要ないくつかのPHPモジュール、つまりphp-xmlphp-mbstringphp-xmlphp-bcmathをインストールする必要があります。 ]。 これらのパッケージをインストールするには、以下を実行します。

sudo apt install unzip php-xml php-mbstring php-xml php-bcmath

アプリケーションの依存関係をインストールするには、次のコマンドを実行します。

composer install

次のような出力が表示されます。

OutputLoading composer repositories with package information
Installing dependencies (including require-dev) from lock file
Package operations: 80 installs, 0 updates, 0 removals
  - Installing doctrine/inflector (v1.3.0): Downloading (100%)         
  - Installing doctrine/lexer (1.1.0): Downloading (100%)         
  - Installing dragonmantank/cron-expression (v2.3.0): Downloading (100%)         
  - Installing erusev/parsedown (1.7.3): Downloading (100%)      
   
...

Generating optimized autoload files
> Illuminate\Foundation\ComposerScripts::postAutoloadDump
> @php artisan package:discover --ansi
Discovered Package: beyondcode/laravel-dump-server
Discovered Package: fideloper/proxy
Discovered Package: laravel/tinker
Discovered Package: nesbot/carbon
Discovered Package: nunomaduro/collision
Package manifest generated successfully.

これで、アプリケーションの依存関係がインストールされました。 次に、管理対象のMySQLデータベースに接続するようにアプリケーションを構成します。

.env構成ファイルの作成とアプリキーの設定

次に、環境ごとにLaravelアプリケーションを構成するために使用される変数を含む.envファイルを作成します。 アプリケーションには、環境設定を反映するように値をコピーして変更できるサンプルファイルが含まれています。

.env.exampleファイルを.envという名前の新しいファイルにコピーします。

cp .env.example .env

次に、アプリケーションキーを設定する必要があります。 このキーはセッションデータの暗号化に使用され、32文字の一意の文字列に設定する必要があります。 artisanツールを使用して、このキーを自動的に生成できます。

php artisan key:generate

環境構成ファイルを編集して、データベースの詳細を設定しましょう。 選択したコマンドラインエディタを使用して、.envファイルを開きます。 ここでは、nanoを使用します。

nano .env

データベースクレデンシャルセクションを探します。 次の変数には注意が必要です。

DB_HOST:管理対象のMySQLサーバーホスト。 DB_PORT:管理対象のMySQLサーバーポート。 DB_DATABASEステップ2で作成したアプリケーションデータベースの名前。 DB_USERNAMEステップ2で作成したデータベースユーザー。 DB_PASSWORDステップ2で定義したデータベースユーザーのパスワード。

強調表示された値を独自の管理対象MySQL情報とクレデンシャルで更新します。

...
DB_CONNECTION=mysql
DB_HOST=MANAGED_MYSQL_HOST
DB_PORT=MANAGED_MYSQL_PORT
DB_DATABASE=MANAGED_MYSQL_DB
DB_USERNAME=MANAGED_MYSQL_USER
DB_PASSWORD=MANAGED_MYSQL_PASSWORD
...

編集が完了したら、CTRL+XYENTERの順に入力して、ファイルを保存して閉じます。

アプリケーションがMySQLデータベースに接続するように構成されたので、Laravelのコマンドラインツールartisanを使用してデータベーステーブルを作成し、サンプルデータを入力できます。

ストレージリンクの設定

artisanコマンドで提供されるデータベースツールを実行する前に、アプリケーションで使用している旅行の写真をホストするパブリックストレージフォルダーへのシンボリックリンクを作成する必要があります。 これが必要なのは、データベースシードスクリプトがこれらのサンプル写真に依存してplacesテーブルにデータを挿入するためです。

次のコマンドは、publicディレクトリ内にシンボリックリンクを作成します。このリンクは、アプリケーションの内部ストレージディレクトリstorage/app/publicを指し、Webサーバーを通じて公開されます。

php artisan storage:link
OutputThe [public/storage] directory has been linked.

リンクが作成され、それが指している場所を確認するには、次のコマンドを実行します。

ls -la public/

次のような出力が表示されます。

Outputtotal 36
drwxrwxr-x  5 sammy sammy 4096 Oct 25 14:59 .
drwxrwxr-x 12 sammy sammy 4096 Oct 25 14:58 ..
-rw-rw-r--  1 sammy sammy  593 Oct 25 06:29 .htaccess
drwxrwxr-x  2 sammy sammy 4096 Oct 25 06:29 css
-rw-rw-r--  1 sammy sammy    0 Oct 25 06:29 favicon.ico
drwxrwxr-x  2 sammy sammy 4096 Oct 25 06:29 img
-rw-rw-r--  1 sammy sammy 1823 Oct 25 06:29 index.php
drwxrwxr-x  2 sammy sammy 4096 Oct 25 06:29 js
-rw-rw-r--  1 sammy sammy   24 Oct 25 06:29 robots.txt
lrwxrwxrwx  1 sammy sammy   41 Oct 25 14:59 storage -> /home/sammy/travellist/storage/app/public
-rw-rw-r--  1 sammy sammy 1194 Oct 25 06:29 web.config

データベースの移行と入力

次に、 LaravelMigrationsデータベースシードを使用してアプリケーションテーブルを設定します。 これは、データベース構成が期待どおりに機能するかどうかを判断するのに役立ちます。

アプリケーションが使用するテーブルを作成する移行スクリプトを実行するには、次のコマンドを実行します。

php artisan migrate

次のような出力が表示されます。

OutputMigration table created successfully.
Migrating: 2019_09_19_123737_create_places_table
Migrated:  2019_09_19_123737_create_places_table (0.26 seconds)
Migrating: 2019_10_14_124700_create_photos_table
Migrated:  2019_10_14_124700_create_photos_table (0.42 seconds)

データベースにサンプルデータを入力するには、次のコマンドを実行します。

php artisan db:seed

次のような出力が表示されます。

OutputSeeding: PlacesTableSeeder
Seeded:  PlacesTableSeeder (0.86 seconds)
Database seeding completed successfully.

これで、アプリケーションテーブルが作成され、サンプルデータが入力されます。

テストサーバーの実行(オプション)

artisan serveコマンドを使用すると、アプリケーション内ですべてが正しくセットアップされていることをすばやく確認できます。その後、Nginxなどのフル機能のWebサーバーを構成して、アプリケーションを長期間提供する必要があります。

ポート8000を使用して、テスト用のアプリケーションを一時的に提供します。 サーバーでUFWファイアウォールを有効にしている場合は、最初に次の方法でこのポートへのアクセスを許可する必要があります。

sudo ufw allow 8000

ここで、Laravelがartisanツールを介して公開する組み込みのPHPサーバーを実行するには、次のコマンドを実行します。

php artisan serve --host=0.0.0.0 --port=8000

このコマンドは、CTRL+Cで中断されるまで端末をブロックします。 組み込みのPHPWebサーバーを使用して、ポート8000を使用して、すべてのネットワークインターフェイスでテスト目的でアプリケーションを提供します。

次に、ブラウザに移動し、ポート8000でサーバーのドメイン名またはIPアドレスを使用してアプリケーションにアクセスします。

http://server_domain_or_IP:8000

次のページが表示されます。

このページが表示されている場合は、アプリケーションが構成済みの管理対象データベースから場所と写真に関するデータを正常に取得していることを意味します。 イメージファイルは引き続きローカルディスクに保存されますが、このガイドの次の手順で変更します。

アプリケーションのテストが終了したら、CTRL+Cを押してserveコマンドを停止できます。

サーバーでUFWを実行している場合は、ポート8000を再度閉じることを忘れないでください。

sudo ufw deny 8000

ステップ4—アプリケーションにサービスを提供するようにNginxを構成する

組み込みのPHPWebサーバーは、開発とテストの目的で非常に役立ちますが、PHPアプリケーションを提供するための長期的なソリューションとして使用することを目的としたものではありません。 Nginxのようなフル機能のWebサーバーを使用することをお勧めします。

まず、アプリケーションフォルダーを/var/wwwに移動します。これは、Nginxで実行されているWebアプリケーションの通常の場所です。 まず、mvコマンドを使用して、アプリケーションフォルダーとそのすべての内容を/var/www/travellistに移動します。

sudo mv ~/travellist /var/www/travellist

次に、Webサーバーユーザーにstorageおよびbootstrap/cacheフォルダーへの書き込みアクセスを許可する必要があります。Laravelはアプリケーションで生成されたファイルを保存します。 これらのアクセス許可は、setfaclを使用して設定します。これは、ファイルやフォルダーでより堅牢できめ細かいアクセス許可を設定できるコマンドラインユーティリティです。

必要なディレクトリに対するWebサーバーユーザーへの読み取り、書き込み、および実行(rwx)権限を含めるには、次のコマンドを実行します。

sudo setfacl -R -m g:www-data:rwx /var/www/travellist/storage
sudo setfacl -R -m g:www-data:rwx /var/www/travellist/bootstrap/cache

これでアプリケーションファイルは正常になりましたが、コンテンツを提供するようにNginxを構成する必要があります。 これを行うには、/etc/nginx/sites-availableに新しい仮想ホスト構成ファイルを作成します。

sudo nano /etc/nginx/sites-available/travellist

次の構成ファイルには、Nginx上のLaravelアプリケーションの推奨設定が含まれています。

/ etc / nginx / sites-available / travellist

server {
    listen 80;
    server_name server_domain_or_IP;
    root /var/www/travellist/public;

    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Content-Type-Options "nosniff";

    index index.html index.htm index.php;

    charset utf-8;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }

    error_page 404 /index.php;

    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~ /\.(?!well-known).* {
        deny all;
    }
}

このコンテンツを/etc/nginx/sites-available/travellistファイルにコピーし、強調表示された値を調整して、独自の構成に合わせます。 編集が完了したら、ファイルを保存して閉じます。

新しい仮想ホスト構成ファイルをアクティブ化するには、sites-enabledtravellistへのシンボリックリンクを作成します。

sudo ln -s /etc/nginx/sites-available/travellist /etc/nginx/sites-enabled/

travellist仮想ホストで使用されているのと同じserver_nameに対して以前に構成された別の仮想ホストファイルがある場合は、削除して古い構成を非アクティブ化する必要があります。 /etc/nginx/sites-enabled/内の対応するシンボリックリンク。


構成に構文エラーが含まれていないことを確認するには、次を使用できます。

sudo nginx -t

次のような出力が表示されます。

Outputnginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

変更を適用するには、次のコマンドでNginxをリロードします。

sudo systemctl reload nginx

ここでブラウザをリロードすると、アプリケーションの画像が壊れます。 これは、アプリケーションディレクトリをサーバー内の新しい場所に移動したために発生します。そのため、アプリケーションストレージフォルダへのシンボリックリンクを再作成する必要があります。

次のコマンドで古いリンクを削除します。

cd /var/www/travellist
rm -f public/storage

次に、artisanコマンドをもう一度実行して、ストレージリンクを生成します。

php artisan storage:link

次に、ブラウザに移動し、構成ファイルのserver_nameディレクティブで定義されているように、サーバーのドメイン名またはIPアドレスを使用してアプリケーションにアクセスします。

http://server_domain_or_IP

次のステップでは、オブジェクトストレージサービスをアプリケーションに統合します。 これにより、旅行の写真に使用されている現在のローカルディスクストレージが置き換えられます。

ステップ5—S3互換のオブジェクトストレージをアプリケーションに統合する

次に、S3互換のオブジェクトストレージサービスを使用して、インデックスページに表示された旅行の写真を保存するようにアプリケーションを設定します。 アプリケーションにはすでにローカルディスクに保存されているサンプル写真がいくつかあるため、 s3cmd ツールを使用して、既存のローカル画像ファイルをリモートオブジェクトストレージにアップロードします。

Laravel用のS3ドライバーのセットアップ

Laravelはleague/flysystemを使用します。これは、Laravelアプリケーションがローカルディスクやクラウドサービスを含む複数のストレージソリューションを使用および組み合わせることができるようにするファイルシステム抽象化ライブラリです。 s3ドライバーを使用するには、追加のパッケージが必要です。 このパッケージは、composer requireコマンドを使用してインストールできます。

アプリケーションディレクトリにアクセスします。

cd /var/www/travellist
composer require league/flysystem-aws-s3-v3

次のような出力が表示されます。

OutputUsing version ^1.0 for league/flysystem-aws-s3-v3
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 8 installs, 0 updates, 0 removals
  - Installing mtdowling/jmespath.php (2.4.0): Loading from cache
  - Installing ralouphie/getallheaders (3.0.3): Loading from cache
  - Installing psr/http-message (1.0.1): Loading from cache
  - Installing guzzlehttp/psr7 (1.6.1): Loading from cache
  - Installing guzzlehttp/promises (v1.3.1): Loading from cache
  - Installing guzzlehttp/guzzle (6.4.1): Downloading (100%)         
  - Installing aws/aws-sdk-php (3.112.28): Downloading (100%)         
  - Installing league/flysystem-aws-s3-v3 (1.0.23): Loading from cache
...

必要なパッケージがインストールされたので、アプリケーションを更新してオブジェクトストレージに接続できます。 まず、.envファイルを再度開いて、オブジェクトストレージサービスのキー、バケット名、リージョンなどの構成の詳細を設定します。

.envファイルを開きます。

nano .env

次の環境変数を含め、強調表示された値をオブジェクトストア構成の詳細に置き換えます。

/var/www/travellist/.env

DO_SPACES_KEY=EXAMPLE7UQOTHDTF3GK4
DO_SPACES_SECRET=exampleb8e1ec97b97bff326955375c5
DO_SPACES_ENDPOINT=https://ams3.digitaloceanspaces.com
DO_SPACES_REGION=ams3
DO_SPACES_BUCKET=sammy-travellist

完了したら、ファイルを保存して閉じます。 次に、config/filesystems.phpファイルを開きます。

nano config/filesystems.php

このファイル内で、disksアレイに新しいdiskエントリを作成します。 このディスクにspacesという名前を付け、.envファイルで設定した環境変数を使用して新しいディスクを構成します。 disksアレイに次のエントリを含めます。

config / filesystems.php

'spaces' => [
   'driver' => 's3',
   'key' => env('DO_SPACES_KEY'),
   'secret' => env('DO_SPACES_SECRET'),
   'endpoint' => env('DO_SPACES_ENDPOINT'),
   'region' => env('DO_SPACES_REGION'),
   'bucket' => env('DO_SPACES_BUCKET'),
],

同じファイル内で、cloudエントリを見つけて変更し、新しいspacesディスクをデフォルトのクラウドファイルシステムディスクとして設定します。

config / filesystems.php

'cloud' => env('FILESYSTEM_CLOUD', 'spaces'),

編集が完了したら、ファイルを保存して閉じます。 コントローラから、Storage::cloud()メソッドをショートカットとして使用して、デフォルトのcloudディスクにアクセスできるようになりました。 このように、アプリケーションは複数のストレージソリューションを使用する柔軟性を維持し、環境ごとにプロバイダーを切り替えることができます。

これで、アプリケーションはオブジェクトストレージを使用するように構成されましたが、新しい写真をアプリケーションにアップロードするコードを更新する必要があります。

まず、PhotoControllerクラスにある現在のuploadPhotoルートを調べてみましょう。 テキストエディタを使用してファイルを開きます。

nano app/Http/Controllers/PhotoController.php

app / Http / Controllers / PhotoController.php

…

public function uploadPhoto(Request $request)
{
   $photo = new Photo();
   $place = Place::find($request->input('place'));

   if (!$place) {
       //add new place
       $place = new Place();
       $place->name = $request->input('place_name');
       $place->lat = $request->input('place_lat');
       $place->lng = $request->input('place_lng');
   }

   $place->visited = 1;
   $place->save();

   $photo->place()->associate($place);
   $photo->image = $request->image->store('/', 'public');
   $photo->save();

   return redirect()->route('Main');
}

このメソッドは、POSTリクエストを受け入れ、写真テーブルに新しい写真エントリを作成します。 まず、写真アップロードフォームで既存の場所が選択されているかどうかを確認します。選択されていない場合は、提供された情報を使用して新しい場所を作成します。 次に、場所がvisitedに設定され、データベースに保存されます。 その後、新しい写真が指定された場所にリンクされるように関連付けが作成されます。 その後、イメージファイルはpublicディスクのルートフォルダに保存されます。 最後に、写真がデータベースに保存されます。 次に、ユーザーは、アプリケーションのインデックスページであるメインルートにリダイレクトされます。

このコードで強調表示されている行は、私たちが関心を持っているものです。 その行で、イメージファイルはstoreメソッドを使用してディスクに保存されます。 storeメソッドは、filesystem.php構成ファイルで定義されたディスクのいずれかにファイルを保存するために使用されます。 この場合、アップロードされた画像を保存するためにデフォルトのディスクを使用しています。

この動作を変更して、イメージがローカルディスクではなくオブジェクトストアに保存されるようにします。 そのためには、storeメソッド呼び出しでpublicディスクをspacesディスクに置き換える必要があります。 また、アップロードされたファイルの可視性がprivateではなくpublicに設定されていることを確認する必要があります。

次のコードには、更新されたuploadPhotoメソッドを含む完全なPhotoControllerクラスが含まれています。

app / Http / Controllers / PhotoController.php

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Photo;
use App\Place;
use Illuminate\Support\Facades\Storage;

class PhotoController extends Controller
{
   public function uploadForm()
   {
       $places = Place::all();

       return view('upload_photo', [
           'places' => $places
       ]);
   }
  
   public function uploadPhoto(Request $request)
   {
       $photo = new Photo();
       $place = Place::find($request->input('place'));

       if (!$place) {
           //add new place
           $place = new Place();
           $place->name = $request->input('place_name');
           $place->lat = $request->input('place_lat');
           $place->lng = $request->input('place_lng');
       }

       $place->visited = 1;
       $place->save();

       $photo->place()->associate($place);
       $photo->image = $request->image->store('/', 'spaces');
       Storage::setVisibility($photo->image, 'public');
       $photo->save();

       return redirect()->route('Main');
   }
}

更新されたコードを独自のPhotoControllerにコピーして、強調表示された変更を反映させます。 編集が完了したら、ファイルを保存して閉じます。

アプリケーションのメインビューを変更して、オブジェクトストレージファイルのURLを使用して画像をレンダリングする必要があります。 travel_list.blade.phpテンプレートを開きます。

nano resources/views/travel_list.blade.php

次に、ページのfooterセクションを見つけます。これは、現在次のようになっています。

resources / views / travel_list.blade.php

@section('footer')
   <h2>Travel Photos <small>[ <a href="{{ route('Upload.form') }}">Upload Photo</a> ]</small></h2>
   @foreach ($photos as $photo)
       <div class="photo">
          <img src="{{ asset('storage') . '/' . $photo->image }}" />
           <p>{{ $photo->place->name }}</p>
       </div>
   @endforeach

@endsection

現在のイメージsrc属性を置き換えて、spacesストレージディスクのファイルURLを使用します。

<img src="{{ Storage::disk('spaces')->url($photo->image) }}" />

今すぐブラウザにアクセスしてアプリケーションページをリロードすると、壊れた画像のみが表示されます。 これは、これらの旅行写真の画像ファイルがまだローカルディスクにのみ存在するために発生します。 データベースにすでに保存されている写真をアプリケーションページに正常に表示できるように、既存の画像ファイルをオブジェクトストレージにアップロードする必要があります。

ローカル画像をs3cmdと同期する

s3cmdツールを使用して、ローカルファイルをS3互換のオブジェクトストレージサービスと同期できます。 syncコマンドを実行して、storage/app/public/photos内のすべてのファイルをオブジェクトストレージサービスにアップロードします。

publicアプリストレージディレクトリにアクセスします。

cd /var/www/travellist/storage/app/public

リモートディスクにすでに保存されているファイルを確認するには、s3cmd lsコマンドを使用できます。

s3cmd ls s3://your_bucket_name

次に、syncコマンドを実行して、パブリックストレージフォルダー内の既存のファイルをオブジェクトストレージにアップロードします。

s3cmd sync ./ s3://your_bucket_name --acl-public --exclude=.gitignore

これにより、現在のフォルダー(storage/app/public)がリモートオブジェクトストレージのルートディレクトリと同期されます。 次のような出力が得られます。

Outputupload: './bermudas.jpg' -> 's3://sammy-travellist/bermudas.jpg'  [1 of 3]
 2538230 of 2538230   100% in    7s   329.12 kB/s  done
upload: './grindavik.jpg' -> 's3://sammy-travellist/grindavik.jpg'  [2 of 3]
 1295260 of 1295260   100% in    5s   230.45 kB/s  done
upload: './japan.jpg' -> 's3://sammy-travellist/japan.jpg'  [3 of 3]
 8940470 of 8940470   100% in   24s   363.61 kB/s  done
Done. Uploaded 12773960 bytes in 37.1 seconds, 336.68 kB/s.

ここで、s3cmd lsを再度実行すると、3つの新しいファイルがオブジェクトストレージバケットのルートフォルダーに追加されたことがわかります。

s3cmd ls s3://your_bucket_name
Output2019-10-25 11:49   2538230   s3://sammy-travellist/bermudas.jpg
2019-10-25 11:49   1295260   s3://sammy-travellist/grindavik.jpg
2019-10-25 11:49   8940470   s3://sammy-travellist/japan.jpg

ブラウザに移動して、アプリケーションページをリロードします。 これですべての画像が表示されるはずです。ブラウザのデバッグツールを使用して画像を調べると、すべての画像がオブジェクトストレージのURLを使用していることがわかります。

統合のテスト

これで、デモアプリケーションは完全に機能し、リモートオブジェクトストレージサービスにファイルを保存し、管理対象のMySQLデータベースにデータを保存します。 これで、セットアップをテストするためにいくつかの写真をアップロードできます。

ブラウザから/uploadアプリケーションルートにアクセスします。

http://server_domain_or_IP/upload

次のフォームが表示されます。

これで、いくつかの写真をアップロードして、オブジェクトストレージの統合をテストできます。 コンピューターから画像を選択した後、ドロップダウンメニューから既存の場所を選択するか、名前と地理座標を指定して新しい場所を追加し、アプリケーションマップに読み込むことができます。

ステップ6—読み取り専用ノードを使用したDigitalOceanマネージドMySQLデータベースのスケールアップ(オプション)

通常、読み取り専用操作はデータベースサーバーでの書き込み操作よりも頻繁に行われるため、複数の読み取り専用ノードを設定してデータベースクラスターをスケールアップするのが一般的な方法です。 これにより、SELECT操作によって生成された負荷が分散されます。

このセットアップを示すために、最初に2つの読み取り専用ノードをDigitalOceanマネージドMySQLクラスターに追加します。 次に、これらのノードを使用するようにLaravelアプリケーションを構成します。

DigitalOcean Cloud Panel にアクセスし、次の手順に従います。

  1. データベースに移動し、MySQLクラスターを選択します。
  2. Actionsをクリックし、ドロップダウンメニューからAdd a read-only nodeを選択します。
  3. ノードオプションを設定し、作成ボタンを押します。 新しいノードの準備ができるまでに数分かかる場合があることに注意してください。
  4. 手順1〜4をもう一度繰り返して、読み取り専用ノードを2つ作成します。
  5. Laravel構成で必要になるため、2つのノードのホストを書き留めます。

読み取り専用ノードの準備ができたら、ターミナルに戻ります。

次に、複数のデータベースノードで動作するようにLaravelアプリケーションを構成します。 終了すると、INSERTUPDATEなどのクエリがプライマリクラスターノードに転送され、すべてのSELECTクエリが読み取り専用ノードにリダイレクトされます。

まず、サーバー上のアプリケーションのディレクトリに移動し、選択したテキストエディタを使用して.envファイルを開きます。

cd /var/www/travellist
nano .env

MySQLデータベース構成を見つけて、DB_HOST行をコメントアウトします。

/var/www/travellist/.env

DB_CONNECTION=mysql
#DB_HOST=MANAGED_MYSQL_HOST
DB_PORT=MANAGED_MYSQL_PORT
DB_DATABASE=MANAGED_MYSQL_DB
DB_USERNAME=MANAGED_MYSQL_USER
DB_PASSWORD=MANAGED_MYSQL_PASSWORD

完了したら、ファイルを保存して閉じます。 次に、テキストエディタでconfig/database.phpを開きます。

nano config/database.php

connectionsアレイ内のmysqlエントリを探します。 この構成配列には、3つの新しいアイテムreadwrite、およびstickyを含める必要があります。 readおよびwriteエントリはクラスターノードをセットアップし、stickyオプションをtrueに設定すると、write接続が再利用されます。データベースに書き込まれたデータは、同じ要求サイクルですぐに利用できます。 この動作を望まない場合は、falseに設定できます。

/var/www/travel_list/config/database.php

...
      'mysql' => [
         'read' => [
           'host' => [
              'READONLY_NODE1_HOST',
              'READONLY_NODE2_HOST',
           ],
         ],
         'write' => [
           'host' => [
             'MANAGED_MYSQL_HOST',
           ],
         ],
       'sticky' => true,
...

編集が完了したら、ファイルを保存して閉じます。 すべてが期待どおりに機能することをテストするために、routes/web.php内に一時ルートを作成して、データベースからデータを取得し、使用されている接続の詳細を表示できます。 このようにして、読み取り専用ノード間でリクエストがどのように負荷分散されているかを確認できます。

routes/web.phpファイルを開きます。

nano routes/web.php

次のルートを含めます。

/var/www/travel_list/routes/web.php

...

Route::get('/mysql-test', function () {
  $places = App\Place::all();
  $results = DB::select( DB::raw("SHOW VARIABLES LIKE 'server_id'") );
  
  return "Server ID: " . $results[0]->Value;
});

次に、ブラウザに移動して、/mysql-testアプリケーションルートにアクセスします。

http://server_domain_or_IP/mysql-test

次のようなページが表示されます。

ページを数回リロードすると、Server IDの値が変化し、2つの読み取り専用ノード間でリクエストがランダムに分散されていることがわかります。

結論

このガイドでは、高可用性でスケーラブルな環境向けのLaravel6アプリケーションを用意しました。 データベースシステムを外部のマネージドMySQLサービスにアウトソーシングし、S3互換のオブジェクトストレージサービスをアプリケーションに統合して、ユーザーがアップロードしたファイルを保存しました。 最後に、アプリの構成ファイルに追加の読み取り専用クラスターノードを含めることで、アプリケーションのデータベースをスケールアップする方法を確認しました。

このガイドで行われたすべての変更を含む更新されたデモアプリケーションコードは、Githubのアプリケーションのリポジトリにある2.1タグ内にあります。

ここから、ロードバランサーをセットアップして、負荷を分散し、アプリケーションを複数のノードにスケーリングできます。 この設定を利用して、コンテナー化された環境を作成し、Dockerでアプリケーションを実行することもできます。