前書き
Webアプリケーションを水平方向にスケーリングする場合、通常直面する最初の問題は、ファイルストレージとデータの永続性に対処することです。 これは主に、複数のアプリケーションノード間で変数データの一貫性を維持するのが難しいという事実によるものです。 1つのノードで作成されたデータをクラスター内の他のノードですぐに使用できるようにするために、適切な戦略を実施する必要があります。
整合性の問題を解決する実際的な方法は、managed databasesおよびobject storageシステムを使用することです。 前者はデータの永続性を管理されたデータベースにアウトソースし、後者はユーザーがアップロードした画像などの静的ファイルと可変コンテンツを保持できるリモートストレージサービスを提供します。 各ノードは、アプリケーションレベルでこれらのサービスに接続できます。
次の図は、PHPアプリケーションのコンテキストでこのようなセットアップを使用して水平スケーラビリティを実現する方法を示しています。
このガイドでは、既存のLaravel 6アプリケーションを更新して、管理されたMySQLデータベースに接続し、S3互換オブジェクトストアを設定してユーザー生成ファイルを保存することにより、水平スケーラビリティに対応します。 最後に、Nginx + PHP-FPM Webサーバーで実行される旅行リストアプリケーションが作成されます。
[.note]#Note:このガイドでは、DigitalOcean Managed MySQLとSpacesを使用して、管理対象データベースとオブジェクトストレージを使用したスケーラブルなアプリケーションセットアップを示します。 ここに含まれる手順は、他のサービスプロバイダーでも同様に機能するはずです。
#
前提条件
このチュートリアルを開始するには、最初に次の前提条件が必要です。
-
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コンポーネントに関心があるかを選択できる画面が表示されます。
デフォルトのオプションでは必要なリポジトリがインストールされるため、ここで何も変更する必要はありません。 「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ライブラリmysqlnd
doesn’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 Migrationsとdatabase 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-xml
、php-mbstring
、php-xml
、php-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_USERNAME
:Step 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+X
、Y
、ENTER
の順に入力して、ファイルを保存して閉じます。
アプリケーションがMySQLデータベースに接続するように構成されたので、Laravelのコマンドラインツールartisan
を使用してデータベーステーブルを作成し、サンプルデータを入力できます。
データベースの移行とデータ投入
次に、Laravel Migrationsとdatabase 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
次のページが表示されます。
このページが表示された場合、アプリケーションは構成済みの管理されたデータベースから場所と写真に関するデータを正常にプルしています。 画像ファイルは引き続きローカルディスクに保存されますが、このガイドの次のステップでこれを変更します。
アプリケーションのテストが終了したら、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]#Note: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
次のステップでは、オブジェクトストレージサービスをアプリケーションに統合します。 これにより、旅行写真に使用されている現在のローカルディスクストレージが置き換えられます。
[[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を使用します。
->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
次のフォームが表示されます。
これで、数枚の写真をアップロードして、オブジェクトストレージの統合をテストできます。 コンピューターから画像を選択した後、ドロップダウンメニューから既存の場所を選択するか、名前と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にアクセスし、次の手順に従います。
-
Databasesに移動し、MySQLクラスターを選択します。
-
Actions
をクリックし、ドロップダウンメニューからAdd a read-only node
を選択します。 -
ノードオプションを構成し、Createボタンを押します。 新しいノードの準備が整うまで数分かかる場合があることに注意してください。
-
2つの読み取り専用ノードがあるように、手順1〜4をもう一度繰り返します。
-
Laravel構成に必要になるため、2つのノードのホストを書き留めます。
読み取り専用ノードの準備ができたら、ターミナルに戻ります。
次に、複数のデータベースノードで動作するようにLaravelアプリケーションを構成します。 完了すると、INSERT
やUPDATE
などのクエリがプライマリクラスタノードに転送され、すべての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の新しい項目(read
、write
、および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
次のようなページが表示されます。
ページを数回リロードすると、Server ID
の値が変化し、リクエストが2つの読み取り専用ノード間でランダムに分散されていることがわかります。
結論
このガイドでは、可用性の高いスケーラブルな環境向けにLaravel 6アプリケーションを準備しました。 データベースシステムを外部の管理されたMySQLサービスにアウトソースし、S3互換オブジェクトストレージサービスをアプリケーションに統合して、ユーザーがアップロードしたファイルを保存しました。 最後に、追加の読み取り専用クラスターノードをアプリの構成ファイルに含めることで、アプリケーションのデータベースをスケールアップする方法を見てきました。
このガイドで行われたすべての変更を含む更新されたデモアプリケーションコードは、Github上のアプリケーションのリポジトリの2.1 tag内にあります。
ここから、Load Balancerを設定して、負荷を分散し、アプリケーションを複数のノードにスケーリングできます。 この設定を利用してcontainerized environmentを作成し、Dockerでアプリケーションを実行することもできます。