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

前書き

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

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

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

Laravel at scale diagram

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

Travellist v1.0

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

前提条件

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

  • sudo特権を持つ非rootユーザーとしてUbuntu 18.04サーバーにアクセスし、サーバーにアクティブなファイアウォールをインストールします。 これらを設定するには、Initial Server Setup Guide for Ubuntu 18.04を参照してください。

  • How to Install LEMP on Ubuntu 18.04のステップ1および3で説明されているように、サーバーにインストールおよび構成されたNginxおよびPHP-FPM。 MySQLがインストールされているステップをスキップする必要があります。

  • How to Install and Use Composer on Ubuntu 18.04のステップ1および2で説明されているように、サーバーにインストールされているComposer

  • 管理されたMySQL 8データベースの管理者資格情報。 このガイドでは、DigitalOcean Managed MySQLクラスターを使用しますが、ここでの手順は、他のマネージドデータベースサービスでも同様に機能するはずです。

  • S3互換オブジェクトストレージサービスに対する読み取りおよび書き込み権限を持つAPIキーのセット。 このガイドでは、DigitalOcean Spacesを使用しますが、任意のプロバイダーを自由に使用できます。

  • オブジェクトストレージドライブに接続するようにインストールおよび構成されたs3cmdツール。 DigitalOcean Spacesにこれを設定する方法については、product documentationを参照してください。

[[step-1 -—- installing-the-mysql-8-client]] ==ステップ1— MySQL8クライアントのインストール

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

WebブラウザでMySQL APT Repository pageに移動することから始めます。 右下隅にあるDownloadボタンを見つけて、クリックして次のページに進みます。 このページでは、ログインするか、Oracle Webアカウントにサインアップするよう求められます。 それをスキップして、代わりにNo thanks, just start my downloadというリンクを探すことができます。 リンクアドレスをコピーして、ターミナルウィンドウに戻ります。

このリンクは、サーバーにMySQL APTリポジトリをセットアップする.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コンポーネントに関心があるかを選択できる画面が表示されます。

MySQL APT Repository Install

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

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

sudo apt update

これで、最終的にMySQL 8クライアントをインストールできます。

sudo apt install mysql-client

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

mysql --version

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

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

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

[[step-2 -—- creating-a-new-mysql-user-and-database]] ==ステップ2—新しいMySQLユーザーとデータベースの作成

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

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

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

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

最初に、アプリケーション用の新しいデータベースを作成します。 次のコマンドを実行して、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データベースに接続できるようにします。

[.note]#このガイドでは、Laravel Migrationsdatabase seedsを使用してアプリケーションテーブルを設定します。 既存のローカルデータベースをDigitalOceanマネージドMySQLデータベースに移行する必要がある場合は、How to Import MySQL Databases into DigitalOcean Managed Databases
#に関するドキュメントを参照してください。

[[step-3 -—- setting-up-the-demo-application]] ==ステップ3—デモアプリケーションのセットアップ

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

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

このチュートリアルと互換性のあるアプリケーションコードを取得するために、Githubのプロジェクトのリポジトリから1.1 releaseをダウンロードします。 ダウンロードした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:%で作成したアプリケーションデータベースの名前(t5)s。
DB_USERNAMEStep 2で作成したデータベースユーザー。
DB_PASSWORD:%(で定義したデータベースユーザーのパスワードt11)s。

独自の管理された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を使用してデータベーステーブルを作成し、サンプルデータを入力できます。

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

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

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

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ツールを使用してこれを行うことができます。

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

これにより、storage/app/publicを指すpublicディレクトリ内にシンボリックリンクが作成され、旅行の写真が保存されます。 リンクが作成されたことと、それが指す場所を確認するには、次を実行します。

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

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

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

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

Travellist v1.0

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

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

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

sudo ufw deny 8000

[[step-4 -—- configuring-nginx-to-serve-the-application]] ==ステップ4—アプリケーションにサービスを提供するためのNginxの構成

組み込みのPHP Webサーバーは、開発およびテストの目的には非常に役立ちますが、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アプリケーションのrecommended設定が含まれています。

/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-enabled内のtravellistへのシンボリックリンクを作成します。

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

[.note]#Notetravellist仮想ホストで使用されているのと同じ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

Travellist v1.0

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

[[step-5 --- integrating-an-s3-compatible-object-storage-into-the-application]] ==ステップ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'),

編集が完了したら、ファイルを保存して閉じます。 コントローラから、デフォルトのcloudディスクにアクセスするためのショートカットとしてStorage::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

 $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')
   

Travel Photos [ Upload Photo ]

@foreach ($photos as $photo)

{{ $photo->place->name }}

@endforeach @endsection

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

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

ローカルイメージを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

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

Travellist Photo Upload Form

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

[[step-6 -—- scaling-up-a-digitalocean-managed-mysql-database-with-read-only-nodes-optional]] ==ステップ6—読み取り専用ノードを使用してDigitalOceanマネージドMySQLデータベースをスケールアップする(オプション)

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

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

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

  1. Databasesに移動し、MySQLクラスターを選択します。

  2. Actionsをクリックし、ドロップダウンメニューからAdd a read-only nodeを選択します。

  3. ノードオプションを構成し、Createボタンを押します。 新しいノードの準備が整うまで数分かかる場合があることに注意してください。

  4. 2つの読み取り専用ノードがあるように、手順1〜4をもう一度繰り返します。

  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エントリを探します。 この構成配列には、threeの新しい項目(readwrite、およびsticky)を含める必要があります。 readおよびwriteエントリはクラスターノードをセットアップし、trueに設定されたstickyオプションは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

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

mysql node test page

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

結論

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

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

ここから、Load Balancerを設定して、負荷を分散し、アプリケーションを複数のノードにスケーリングできます。 この設定を利用してcontainerized environmentを作成し、Dockerでアプリケーションを実行することもできます。

Related