非托管对象 和托管对象
介绍 (Introduction)
When scaling web applications horizontally, the first difficulties you’ll typically face are dealing with file storage and data persistence. This is mainly due to the fact that it is hard to maintain consistency of variable data between multiple application nodes; appropriate strategies must be in place to make sure data created in one node is immediately available to other nodes in a cluster.
在水平扩展Web应用程序时,通常会遇到的第一个困难是处理文件存储和数据持久性。 这主要是由于难以维护多个应用程序节点之间的变量数据的一致性。 必须采取适当的策略,以确保在一个节点中创建的数据可立即用于集群中的其他节点。
A practical way of solving the consistency problem is by using managed databases and object storage systems. The first will outsource data persistence to a managed database, and the latter will provide a remote storage service where you can keep static files and variable content such as images uploaded by users. Each node can then connect to these services at the application level.
解决一致性问题的一种实用方法是使用托管数据库和对象存储系统。 前者将数据持久性外包给托管数据库,后者将提供远程存储服务,您可以在其中保留静态文件和可变内容,例如用户上传的图像。 然后,每个节点都可以在应用程序级别连接到这些服务。
The following diagram demonstrates how such a setup can be used for horizontal scalability in the context of PHP applications:
下图演示了如何在PHP应用程序的上下文中将这种设置用于水平可伸缩性:
In this guide, we will update an existing Laravel 6 application to prepare it for horizontal scalability by connecting it to a managed MySQL database and setting up an S3-compatible object store to save user-generated files. By the end, you will have a travel list application running on an Nginx + PHP-FPM web server:
在本指南中,我们将更新现有的Laravel 6应用程序,以通过将其连接到托管MySQL数据库并设置与S3兼容的对象存储来保存用户生成的文件,以使其具有水平可伸缩性。 到最后,您将在Nginx + PHP-FPM Web服务器上运行一个旅行清单应用程序:
Note: this guide uses DigitalOcean Managed MySQL and Spaces to demonstrate a scalable application setup using managed databases and object storage. The instructions contained here should work in a similar way for other service providers.
注意 :本指南使用DigitalOcean托管MySQL和Spaces演示使用托管数据库和对象存储的可伸缩应用程序设置。 对于其他服务提供商,此处包含的说明应以类似的方式工作。
先决条件 (Prerequisites)
To begin this tutorial, you will first need the following prerequisites:
要开始本教程,您首先需要满足以下先决条件:
Access to an Ubuntu 18.04 server as a non-root user with sudo privileges, and an active firewall installed on your server. To set these up, please refer to our Initial Server Setup Guide for Ubuntu 18.04.
以具有sudo特权的非root用户身份访问Ubuntu 18.04服务器,并在服务器上安装了活动的防火墙。 要进行设置,请参阅我们的Ubuntu 18.04初始服务器设置指南 。
Nginx and PHP-FPM installed and configured on your server, as explained in steps 1 and 3 of How to Install LEMP on Ubuntu 18.04. You should skip the step where MySQL is installed.
如如何在Ubuntu 18.04上安装LEMP的步骤1和3中所述,在服务器上安装并配置了Nginx和PHP-FPM。 您应该跳过安装MySQL的步骤。
Composer installed on your server, as explained in steps 1 and 2 of How to Install and Use Composer on Ubuntu 18.04.
作曲家安装在服务器上,因为在步骤1和2解释如何在Ubuntu 18.04安装和使用的作曲家 。
Admin credentials to a managed MySQL 8 database. For this guide, we’ll be using a DigitalOcean Managed MySQL cluster, but the instructions here should work similarly for other managed database services.
到管理MySQL 8数据库的管理员凭据。 在本指南中,我们将使用DigitalOcean托管MySQL集群,但是此处的说明应与其他托管数据库服务类似。
A set of API keys with read and write permissions to an S3-compatible object storage service. In this guide, we’ll use DigitalOcean Spaces, but you are free to use a provider of your choice.
一组具有对S3兼容对象存储服务的读写权限的API密钥。 在本指南中,我们将使用DigitalOcean Spaces ,但您可以自由使用自己选择的提供程序。
The
s3cmd
tool installed and configured to connect to your object storage drive. For instructions on how to set this up for DigitalOcean Spaces, please refer to our product documentation.s3cmd
工具已安装并配置为连接到对象存储驱动器。 有关如何为DigitalOcean Spaces进行设置的说明,请参阅我们的产品文档 。
第1步-安装MySQL 8客户端 (Step 1 — Installing the MySQL 8 Client)
The default Ubuntu apt repositories come with the MySQL 5 client, which is not compatible with the MySQL 8 server we’ll be using in this guide. To install the compatible MySQL client, we’ll need to use the MySQL APT Repository provided by Oracle.
默认的Ubuntu apt存储库与MySQL 5客户端一起提供,该客户端与本指南中将使用MySQL 8服务器不兼容。 要安装兼容MySQL客户端,我们需要使用Oracle提供MySQL APT存储库。
Begin by navigating to the MySQL APT Repository page in your web browser. Find the Download button in the lower-right corner and click through to the next page. This page will prompt you to log in or sign up for an Oracle web account. You can skip that and instead look for the link that says No thanks, just start my download. Copy the link address and go back to your terminal window.
首先,在Web浏览器中导航到“ MySQL APT存储库”页面 。 在右下角找到“ 下载”按钮,然后单击进入下一页。 该页面将提示您登录或注册Oracle Web帐户。 您可以跳过该链接,而是寻找显示“ 不,谢谢”的链接,只需开始下载即可 。 复制链接地址,然后返回到您的终端窗口。
This link should point to a .deb
package that will set up the MySQL APT Repository in your server. After installing it, you’ll be able to use apt
to install the latest releases of MySQL. We’ll use curl
to download this file into a temporary location.
该链接应该指向一个.deb
软件包,该软件包将在您的服务器中设置MySQL APT存储库。 安装后,您将可以使用apt
安装最新版本MySQL。 我们将使用curl
将这个文件下载到一个临时位置。
Go to your server’s tmp
folder:
转到服务器的tmp
文件夹:
- cd /tmp cd / tmp
Now download the package with curl
and using the URL you copied from the MySQL APT Repository page:
现在使用curl
并使用您从MySQL APT Repository页面复制的URL下载该软件包:
curl -OL https://dev.mysql.com/get/mysql-apt-config_0.8.13-1_all.deb
curl -OL https://dev.mysql.com/get/mysql-apt-config_0.8.13-1_all.deb
After the download is finished, you can use dpkg
to install the package:
下载完成后,可以使用dpkg
安装软件包:
sudo dpkg -i mysql-apt-config_0.8.13-1_all.deb
须藤dpkg -i mysql-apt-config_0.8.13-1_all.deb
You will be presented with a screen where you can choose which MySQL version you’d like to select as default, as well as which MySQL components you’re interested in:
将显示一个屏幕,您可以在其中选择要选择MySQL版本作为默认版本,以及感兴趣MySQL组件:
You don’t need to change anything here, because the default options will install the repositories we need. Select “Ok” and the configuration will be finished.
您无需在此处进行任何更改,因为默认选项将安装我们需要的存储库。 选择“确定”,配置完成。
Next, you’ll need to update your apt
cache with:
接下来,您需要使用以下命令更新apt
缓存:
- sudo apt update sudo apt更新
Now we can finally install the MySQL 8 client with:
现在,我们终于可以使用以下命令安装MySQL 8客户端了:
- sudo apt install mysql-client sudo apt安装mysql客户端
Once that command finishes, check the software version number to ensure that you have the latest release:
该命令完成后,请检查软件版本号以确保您具有最新版本:
- mysql --version mysql --version
You’ll see output like this:
您将看到如下输出:
Output
mysql Ver 8.0.18 for Linux on x86_64 (MySQL Community Server - GPL)
In the next step, we’ll use the MySQL client to connect to your managed MySQL server and prepare the database for the application.
在下一步中,我们将使用MySQL客户端连接到托管MySQL服务器,并为应用程序准备数据库。
第2步-创建新MySQL用户和数据库 (Step 2 — Creating a new MySQL User and Database)
At the time of this writing, the native MySQL PHP library mysqlnd
doesn’t support caching_sha2_authentication
, the default authentication method for MySQL 8. We’ll need to create a new user with the mysql_native_password
authentication method in order to be able to connect our Laravel application to the MySQL 8 server. We’ll also create a dedicated database for our demo application.
在撰写本文时,本机MySQL PHP库mysqlnd
不支持 caching_sha2_authentication
,这是MySQL 8的默认身份验证方法。我们需要使用mysql_native_password
身份验证方法创建一个新用户,以便能够连接Laravel应用程序到MySQL 8服务器。 我们还将为演示应用程序创建一个专用的数据库。
To get started, log into your server using an admin account. Replace the highlighted values with your own MySQL user, host, and port:
首先,请使用管理员帐户登录到服务器。 用您自己MySQL用户,主机和端口替换突出显示的值:
mysql -u MYSQL_USER -p -h MYSQL_HOST -P MYSQL_PORT
mysql -u MYSQL_USER -p -h MYSQL_HOST -P MYSQL_PORT
When prompted, provide the admin user’s password. After logging in, you will have access to the MySQL 8 server command line interface.
出现提示时,请提供管理员用户的密码。 登录后,您将可以访问MySQL 8服务器命令行界面。
First, we’ll create a new database for the application. Run the following command to create a new database named travellist
:
首先,我们将为该应用程序创建一个新的数据库。 运行以下命令以创建一个名为travellist
的新数据库:
CREATE DATABASE travellist;
创建数据库旅行清单 ;
Next, we’ll create a new user and set a password, using mysql_native_password
as default authentication method for this user. You are encouraged to replace the highlighted values with values of your own, and to use a strong password:
接下来,我们将使用mysql_native_password
作为该用户的默认身份验证方法来创建一个新用户并设置一个密码。 建议您将突出显示的值替换为您自己的值,并使用强密码:
CREATE USER 'travellist-user'@'%' IDENTIFIED WITH mysql_native_password BY 'MYSQL_PASSWORD';
使用mysql_native_password创建用户' travellist-user '@'%',并通过' MYSQL_PASSWORD '进行身份验证 ;
Now we need to give this user permission over our application database:
现在,我们需要授予该用户对应用程序数据库的权限:
GRANT ALL ON travellist.* TO 'travellist-user'@'%';
将所有内容都授予旅行清单 。* 致 ' 旅行清单用户 '@'%';
You can now exit the MySQL prompt with:
现在,您可以使用以下命令退出MySQL提示符:
- exit; 出口;
You now have a dedicated database and a compatible user to connect from your Laravel application. In the next step, we’ll get the application code and set up configuration details, so your app can connect to your managed MySQL database.
您现在拥有一个专用的数据库和一个兼容的用户,可以从您的Laravel应用程序进行连接。 在下一步中,我们将获取应用程序代码并设置配置详细信息,以便您的应用程序可以连接到托管MySQL数据库。
In this guide, we’ll use Laravel Migrations and database seeds to set up our application tables. If you need to migrate an existing local database to a DigitalOcean Managed MySQL database, please refer to our documentation on How to Import MySQL Databases into DigitalOcean Managed Databases.
在本指南中,我们将使用Laravel Migrations和数据库种子来设置应用程序表。 如果您需要将现有的本地数据库迁移到DigitalOcean托管MySQL数据库,请参阅我们的文档“ 如何将MySQL数据库导入DigitalOcean托管的数据库” 。
步骤3 —设置演示应用程序 (Step 3 — Setting Up the Demo Application)
To get started, we’ll fetch the demo Laravel application from its Github repository. Feel free to inspect the contents of the application before running the next commands.
首先,我们将从其Github存储库中获取演示Laravel应用程序。 在运行下一个命令之前,请随时检查应用程序的内容。
The demo application is a travel bucket list app that was initially developed in our guide on How to Install and Configure Laravel with LEMP on Ubuntu 18.04. The updated app now contains visual improvements including travel photos that can be uploaded by a visitor, and a world map. It also introduces a database migration script and database seeds to create the application tables and populate them with sample data, using artisan
commands.
该演示应用程序是一个旅行清单应用程序,最初是在有关如何在Ubuntu 18.04上使用LEMP安装和配置Laravel的指南中开发的。 更新后的应用程序现在包含视觉上的改进,其中包括可以由访客上传的旅行照片以及世界地图。 它还引入了数据库迁移脚本和数据库种子,以使用artisan
命令创建应用程序表并使用示例数据填充它们。
To obtain the application code that is compatible with this tutorial, we’ll download the 1.1
release from the project’s repository on Github. We’ll save the downloaded zip file as travellist.zip
inside our home directory:
要获取与本教程兼容的应用程序代码,我们将从Github上的项目存储库中下载1.1
版本 。 我们会将下载的zip文件保存为home目录中的travellist.zip
:
- cd ~ 光盘〜
- curl -L https://github.com/do-community/travellist-laravel-demo/archive/1.1.zip -o travellist.zip curl -L https://github.com/do-community/travellist-laravel-demo/archive/1.1.zip -o travellist.zip
Now, unzip the contents of the application and rename its directory with:
现在,解压缩应用程序的内容,并使用以下命令重命名其目录:
- unzip travellist.zip 解压缩travellist.zip
- mv travellist-laravel-demo-1.1 travellist mv travellist-laravel-demo-1.1旅行清单
Navigate to the travellist
directory:
导航到travellist
目录:
- cd travellist cd旅行清单
Before going ahead, we’ll need to install a few PHP modules that are required by the Laravel framework, namely: php-xml
, php-mbstring
, php-xml
and php-bcmath
. To install these packages, run:
在继续之前,我们需要安装Laravel框架所需的一些PHP模块,即: php-xml
, php-mbstring
, php-xml
和php-bcmath
。 要安装这些软件包,请运行:
- sudo apt install unzip php-xml php-mbstring php-xml php-bcmath sudo apt安装解压缩php-xml php-mbstring php-xml php-bcmath
To install the application dependencies, run:
要安装应用程序依赖项,请运行:
- composer install 作曲家安装
You will see output similar to this:
您将看到类似于以下的输出:
Output
Loading 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.
The application dependencies are now installed. Next, we’ll configure the application to connect to the managed MySQL Database.
现在已安装了应用程序依赖项。 接下来,我们将配置应用程序以连接到托管MySQL数据库。
创建.env
配置文件并设置应用程序密钥 (Creating the .env
configuration file and setting the App Key)
We’ll now create a .env
file containing variables that will be used to configure the Laravel application in a per-environment basis. The application includes an example file that we can copy and then modify its values to reflect our environment settings.
现在,我们将创建一个.env
文件,其中包含将用于在每个环境中配置Laravel应用程序的变量。 该应用程序包含一个示例文件,我们可以复制该文件,然后修改其值以反映我们的环境设置。
Copy the .env.example
file to a new file named .env
:
将.env.example
文件复制到名为.env
的新文件中:
- cp .env.example .env cp .env.example .env
Now we need to set the application key. This key is used to encrypt session data, and should be set to a unique 32 characters-long string. We can generate this key automatically with the artisan
tool:
现在我们需要设置应用程序密钥。 该密钥用于加密会话数据,应设置为唯一的32个字符长的字符串。 我们可以使用artisan
工具自动生成此密钥:
- php artisan key:generate PHP的工匠键:生成
Let’s edit the environment configuration file to set up the database details. Open the .env
file using your command line editor of choice. Here, we will be using nano
:
让我们编辑环境配置文件以设置数据库详细信息。 使用您选择的命令行编辑器打开.env
文件。 在这里,我们将使用nano
:
- nano .env 纳米.env
Look for the database credentials section. The following variables need your attention:
查找数据库凭证部分。 以下变量需要引起您的注意:
DB_HOST
: your managed MySQL server host. DB_PORT
: your managed MySQL server port. DB_DATABASE
: the name of the application database we created in Step 2. DB_USERNAME
: the database user we created in Step 2. DB_PASSWORD
: the password for the database user we defined in Step 2.
DB_HOST
:您的托管MySQL服务器主机。 DB_PORT
:您的托管MySQL服务器端口。 DB_DATABASE
:我们在步骤2中创建的应用程序数据库的名称。 DB_USERNAME
:我们在步骤2中创建的数据库用户。 DB_PASSWORD
:我们在步骤2中定义的数据库用户的密码。
Update the highlighted values with your own managed MySQL info and credentials:
使用您自己的托管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
...
Save and close the file by typing CTRL+X
then Y
and ENTER
when you’re done editing.
完成编辑后,请依次按CTRL+X
Y
和ENTER
保存并关闭文件。
Now that the application is configured to connect to the MySQL database, we can use Laravel’s command line tool artisan
to create the database tables and populate them with sample data.
现在已经配置了应用程序以连接到MySQL数据库,我们可以使用Laravel的命令行工具artisan
创建数据库表并用示例数据填充它们。
设置存储链接 (Setting up the storage link)
Before executing the database tools provided by the artisan
command, we need to create a symbolic link to the public storage folder that will host the travel photos we’re using in the application. This is necessary because our database seed script relies on these sample photos to insert data in the places
table.
在执行artisan
命令提供的数据库工具之前,我们需要创建一个指向公共存储文件夹的符号链接,该文件夹将托管我们在应用程序中使用的旅行照片。 这是必需的,因为我们的数据库种子脚本依赖于这些样本照片将数据插入places
表中。
The following command will create a symbolic link inside the public
directory, which is publicly exposed through the web server, pointing to the application’s internal storage directory storage/app/public
:
以下命令将在public
目录内创建一个符号链接,该链接通过Web服务器公开显示,指向应用程序的内部存储目录storage/app/public
:
- php artisan storage:link PHP的工匠存储:链接
Output
The [public/storage] directory has been linked.
To check that the link was created and where it points to, you can run:
要检查链接是否已创建及其指向的位置,可以运行:
- ls -la public/ ls -la public /
You’ll see output like this:
您将看到如下输出:
Output
total 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
迁移和填充数据库 (Migrating and populating the database)
We’ll now use Laravel Migrations and database seeds to set up the application tables. This will help us determine if our database configuration works as expected.
现在,我们将使用Laravel Migrations和数据库种子来设置应用程序表。 这将帮助我们确定数据库配置是否按预期工作。
To execute the migration script that will create the tables used by the application, run:
要执行将创建应用程序使用的表的迁移脚本,请运行:
- php artisan migrate PHP的工匠迁移
You will see output similar to this:
您将看到类似于以下的输出:
Output
Migration 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)
To populate the database with sample data, run:
要用示例数据填充数据库,请运行:
- php artisan db:seed PHP的工匠db:seed
You will see output like this:
您将看到如下输出:
Output
Seeding: PlacesTableSeeder
Seeded: PlacesTableSeeder (0.86 seconds)
Database seeding completed successfully.
The application tables are now created and populated with sample data.
现在创建了应用程序表,并使用示例数据填充了该表。
运行测试服务器(可选) (Running the test server (optional))
You can use the artisan serve
command to quickly verify that everything is set up correctly within the application, before having to configure a full-featured web server like Nginx to serve the application for the long term.
在必须配置诸如Nginx之类的功能齐全的Web服务器来长期服务该应用程序之前,您可以使用artisan serve
命令快速验证该应用程序内的所有设置是否正确。
We’ll use port 8000
to temporarily serve the application for testing. If you have the UFW firewall enabled on your server, you should first allow access to this port with:
我们将使用端口8000
暂时为应用程序提供服务以进行测试。 如果在服务器上启用了UFW防火墙,则应首先使用以下命令允许访问此端口:
- sudo ufw allow 8000 sudo ufw允许8000
Now, to run the built in PHP server that Laravel exposes through the artisan
tool, run:
现在,要运行Laravel通过artisan
工具公开的内置PHP服务器,请运行:
- php artisan serve --host=0.0.0.0 --port=8000 php artisan serve --host = 0.0.0.0 --port = 8000
This command will block your terminal until interrupted with a CTRL+C
. It will use the built-in PHP web server to serve the application for test purposes on all network interfaces, using port 8000
.
此命令将阻止您的终端,直到被CTRL+C
中断为止。 它将使用内置PHP Web服务器使用端口8000
在所有网络接口上为测试目的提供应用程序服务。
Now go to your browser and access the application using the server’s domain name or IP address on port 8000
:
现在转到浏览器,使用服务器的域名或端口8000
上的IP地址访问应用程序:
http://server_domain_or_IP:8000
You will see the following page:
您将看到以下页面:
If you see this page, it means the application is successfully pulling data about locations and photos from the configured managed database. The image files are still stored in the local disk, but we’ll change this in a following step of this guide.
如果看到此页面,则表示该应用程序已成功从配置的托管数据库中提取有关位置和照片的数据。 映像文件仍存储在本地磁盘中,但是我们将在本指南的后续步骤中对此进行更改。
When you are finished testing the application, you can stop the serve
command by hitting CTRL+C
.
测试完应用程序后,可以通过按CTRL+C
来停止serve
命令。
Don’t forget to close port 8000
again if you are running UFW on your server:
如果您在服务器上运行UFW,请不要忘记再次关闭端口8000
:
- sudo ufw deny 8000 sudo ufw拒绝8000
步骤4 —配置Nginx服务应用程序 (Step 4 — Configuring Nginx to Serve the Application)
Although the built-in PHP web server is very useful for development and testing purposes, it is not intended to be used as a long term solution to serve PHP applications. Using a full featured web server like Nginx is the recommended way of doing that.
尽管内置PHP Web服务器对于开发和测试目的非常有用,但并不打算用作服务PHP应用程序的长期解决方案。 建议使用像Nginx这样的功能齐全的Web服务器。
To get started, we’ll move the application folder to /var/www
, which is the usual location for web applications running on Nginx. First, use the mv
command to move the application folder with all its contents to /var/www/travellist
:
首先,我们将应用程序文件夹移动到/var/www
,这是在Nginx上运行的Web应用程序的常用位置。 首先,使用mv
命令将应用程序文件夹及其所有内容移动到/var/www/travellist
:
sudo mv ~/travellist /var/www/travellist
须藤MV〜/ travellist / var / www / travellist
Now we need to give the web server user write access to the storage
and bootstrap/cache
folders, where Laravel stores application-generated files. We’ll set these permissions using setfacl
, a command line utility that allows for more robust and fine-grained permission settings in files and folders.
现在,我们需要为Web服务器用户提供对storage
和bootstrap/cache
文件夹的写访问权,Laravel在其中存储应用程序生成的文件。 我们将使用setfacl
(命令行实用程序)设置这些权限,该实用程序允许在文件和文件夹中进行更强大且更细粒度的权限设置。
To include read, write and execution (rwx) permissions to the web server user over the required directories, run:
要在所需目录上包括对Web服务器用户的读,写和执行(rwx)权限,请运行:
- sudo setfacl -R -m g:www-data:rwx /var/www/travellist/storage 须藤setfacl -R -mg:www-data:rwx / var / www / travellist / storage
- sudo setfacl -R -m g:www-data:rwx /var/www/travellist/bootstrap/cache 须藤setfacl -R -mg:www-data:rwx / var / www / travellist / bootstrap / cache
The application files are now in order, but we still need to configure Nginx to serve the content. To do this, we’ll create a new virtual host configuration file at /etc/nginx/sites-available
:
现在,应用程序文件已经整理好了,但是我们仍然需要配置Nginx来提供内容。 为此,我们将在/etc/nginx/sites-available
创建一个新的虚拟主机配置文件:
sudo nano /etc/nginx/sites-available/travellist
须藤纳米/ etc / nginx / sites-available / travellist
The following configuration file contains the recommended settings for Laravel applications on Nginx:
以下配置文件包含Nginx上Laravel应用程序的建议设置:
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;
}
}
Copy this content to your /etc/nginx/sites-available/travellist
file and adjust the highlighted values to align with your own configuration. Save and close the file when you’re done editing.
将此内容复制到您的/etc/nginx/sites-available/ travellist
文件中,并调整突出显示的值以符合您自己的配置。 完成编辑后,保存并关闭文件。
To activate the new virtual host configuration file, create a symbolic link to travellist
in sites-enabled
:
要激活新的虚拟主机配置文件,请在sites-enabled
创建指向travellist
的符号链接:
sudo ln -s /etc/nginx/sites-available/travellist /etc/nginx/sites-enabled/
sudo ln -s / etc / nginx / sites-available / travellist / etc / nginx / sites-enabled /
Note: If you have another virtual host file that was previously configured for the same server_name
used in the travellist
virtual host, you might need to deactivate the old configuration by removing the corresponding symbolic link inside /etc/nginx/sites-enabled/
.
注意 :如果您有另一个虚拟主机文件,该文件先前已配置为与travellist
虚拟主机中使用的相同的server_name
,则可能需要通过删除/etc/nginx/sites-enabled/
的相应符号链接来停用旧配置。
To confirm that the configuration doesn’t contain any syntax errors, you can use:
要确认配置不包含任何语法错误,可以使用:
- sudo nginx -t 须藤Nginx -t
You should see output like this:
您应该看到如下输出:
- nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx:配置文件/etc/nginx/nginx.conf语法正常
- nginx: configuration file /etc/nginx/nginx.conf test is successful nginx:配置文件/etc/nginx/nginx.conf测试成功
To apply the changes, reload Nginx with:
要应用更改,请使用以下命令重新加载Nginx:
- sudo systemctl reload nginx 须藤systemctl重新加载nginx
If you reload your browser now, the application images will be broken. That happens because we moved the application directory to a new location inside the server, and for that reason we need to re-create the symbolic link to the application storage folder.
如果现在重新加载浏览器,则应用程序映像将损坏。 发生这种情况是因为我们将应用程序目录移动到了服务器内部的新位置,因此我们需要重新创建指向应用程序存储文件夹的符号链接。
Remove the old link with:
使用以下方法删除旧链接:
cd /var/www/travellist
cd / var / www / travellist
- rm -f public/storage rm -f公共/存储
Now run once again the artisan
command to generate the storage link:
现在再次运行artisan
命令以生成存储链接:
- php artisan storage:link PHP的工匠存储:链接
Now go to your browser and access the application using the server’s domain name or IP address, as defined by the server_name
directive in your configuration file:
现在转到浏览器,使用服务器的域名或IP地址访问应用程序,该域名或IP地址由配置文件中的server_name
指令定义:
http://server_domain_or_IP
In the next step, we’ll integrate an object storage service into the application. This will replace the current local disk storage used for the travel photos.
在下一步中,我们将对象存储服务集成到应用程序中。 这将替换用于旅行照片的当前本地磁盘存储。
步骤5 —将兼容S3的对象存储集成到应用程序中 (Step 5 — Integrating an S3-Compatible Object Storage into the Application)
We’ll now set up the application to use an S3-compatible object storage service for storing the travel photos exhibited on the index page. Because the application already has a few sample photos stored in the local disk, we’ll also use the s3cmd
tool to upload the existing local image files to the remote object storage.
现在,我们将应用程序设置为使用与S3兼容的对象存储服务,以存储索引页面上显示的旅行照片。 由于应用程序已经在本地磁盘中存储了一些示例照片,因此我们还将使用s3cmd
工具将现有的本地图像文件上载到远程对象存储中。
为Laravel设置S3驱动程序 (Setting Up the S3 Driver for Laravel)
Laravel uses league/flysystem
, a filesystem abstraction library that enables a Laravel application to use and combine multiple storage solutions, including local disk and cloud services. An additional package is required to use the s3
driver. We can install this package using the composer require
command.
Laravel使用League league/flysystem
这个文件系统抽象库,使Laravel应用程序可以使用和组合多个存储解决方案,包括本地磁盘和云服务。 要使用s3
驱动程序,需要附加软件包。 我们可以使用composer require
命令安装此软件包。
Access the application directory:
访问应用程序目录:
cd /var/www/travellist
cd / var / www / travellist
- composer require league/flysystem-aws-s3-v3 作曲家需要League / flysystem-aws-s3-v3
You will see output similar to this:
您将看到类似于以下的输出:
Output
Using 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
...
Now that the required packages are installed, we can update the application to connect to the object storage. First, we’ll open the .env
file again to set up configuration details such as keys, bucket name, and region for your object storage service.
现在已经安装了必需的软件包,我们可以更新应用程序以连接到对象存储。 首先,我们将再次打开.env
文件以设置配置详细信息,例如对象存储服务的键,存储区名称和区域。
Open the .env
file:
打开.env
文件:
- nano .env 纳米.env
Include the following environment variables, replacing the highlighted values with your object store configuration details:
包括以下环境变量,用对象库配置详细信息替换突出显示的值:
DO_SPACES_KEY=EXAMPLE7UQOTHDTF3GK4
DO_SPACES_SECRET=exampleb8e1ec97b97bff326955375c5
DO_SPACES_ENDPOINT=https://ams3.digitaloceanspaces.com
DO_SPACES_REGION=ams3
DO_SPACES_BUCKET=sammy-travellist
Save and close the file when you’re done. Now open the config/filesystems.php
file:
完成后,保存并关闭文件。 现在打开config/filesystems.php
文件:
- nano config/filesystems.php 纳米config / filesystems.php
Within this file, we’ll create a new disk entry in the disks
array. We’ll name this disk spaces
, and we’ll use the environment variables we’ve set in the .env
file to configure the new disk. Include the following entry in the disks
array:
在此文件中,我们将在disks
数组中创建一个新的磁盘条目。 我们将命名该磁盘spaces
,并使用在.env
文件中设置的环境变量来配置新磁盘。 在disks
阵列中包括以下条目:
'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'),
],
Still in the same file, locate the cloud
entry and change it to set the new spaces
disk as default cloud filesystem disk:
仍在同一文件中,找到cloud
条目并将其更改以将新的spaces
磁盘设置为默认云文件系统磁盘:
'cloud' => env('FILESYSTEM_CLOUD', 'spaces'),
Save and close the file when you’re done editing. From your controllers, you can now use the Storage::cloud()
method as a shortcut to access the default cloud
disk. This way, the application stays flexible to use multiple storage solutions, and you can switch between providers on a per-environment basis.
完成编辑后,保存并关闭文件。 现在,您可以从控制器中使用Storage::cloud()
方法作为访问默认cloud
磁盘的快捷方式。 这样,应用程序可以灵活使用多个存储解决方案,并且您可以基于每个环境在提供程序之间进行切换。
The application is now configured to use object storage, but we still need to update the code that uploads new photos to the application.
现在将应用程序配置为使用对象存储,但是我们仍然需要更新将新照片上传到应用程序的代码。
Let’s first examine the current uploadPhoto
route, located in the PhotoController
class. Open the file using your text editor:
首先让我们检查位于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');
}
This method accepts a POST
request and creates a new photo entry in the photos table. It begins by checking if an existing place was selected in the photo upload form, and if that’s not the case, it will create a new place using the provided information. The place is then set to visited
and saved to the database. Following that, an association is created so that the new photo is linked to the designated place. The image file is then stored in the root folder of the public
disk. Finally, the photo is saved to the database. The user is then redirected to the main route, which is the index page of the application.
此方法接受POST
请求,并在photos表中创建一个新的photo条目。 首先检查在照片上传表单中是否选择了现有地点,如果不是这样,它将使用所提供的信息创建一个新地点。 然后将该地点设置为已visited
并保存到数据库。 之后,创建关联,以便将新照片链接到指定的位置。 然后将映像文件存储在public
磁盘的根文件夹中。 最后,照片被保存到数据库中。 然后,用户将被重定向到主路由,即应用程序的索引页面。
The highlighted line in this code is what we’re interested in. In that line, the image file is saved to the disk using the store
method. The store
method is used to save files to any of the disks defined in the filesystem.php
configuration file. In this case, it is using the default disk to store uploaded images.
这段代码中突出显示的行是我们感兴趣的行。在该行中,使用store
方法将映像文件保存到磁盘。 store
方法用于将文件保存到filesystem.php
配置文件中定义的任何磁盘上。 在这种情况下,它将使用默认磁盘来存储上载的图像。
We will change this behavior so that the image is saved to the object store instead of the local disk. In order to do that, we need to replace the public
disk by the spaces
disk in the store
method call. We also need to make sure the uploaded file’s visibility is set to public instead of private.
我们将更改此行为,以便将映像保存到对象存储而不是本地磁盘。 为此,我们需要在store
方法调用中将public
磁盘替换为spaces
磁盘。 我们还需要确保上传文件的可见性设置为public而不是private 。
The following code contains the full PhotoController
class, including the updated uploadPhoto
method:
以下代码包含完整的PhotoController
类,其中包括更新的uploadPhoto
方法:
<?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');
}
}
Copy the updated code to your own PhotoController
so that it reflects the highlighted changes. Save and close the file when you’re done editing.
将更新的代码复制到您自己的PhotoController
,以使其反映突出显示的更改。 完成编辑后,保存并关闭文件。
We still need to modify the application’s main view so that it uses the object storage file URL to render the image. Open the travel_list.blade.php
template:
我们仍然需要修改应用程序的主视图,以便它使用对象存储文件URL呈现图像。 打开travel_list.blade.php
模板:
- nano resources/views/travel_list.blade.php 纳米资源/视图/travel_list.blade.php
Now locate the footer
section of the page, which currently looks like this:
现在找到页面的footer
部分,当前如下所示:
@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
Replace the current image src
attribute to use the file URL from the spaces
storage disk:
替换当前图像src
属性以使用来自spaces
存储磁盘的文件URL:
<img src="{{ Storage::disk('spaces')->url($photo->image) }}" />
If you go to your browser now and reload the application page, it will show only broken images. That happens because the image files for those travel photos are still only in the local disk. We need to upload the existing image files to the object storage, so that the photos already stored in the database can be successfully exhibited in the application page.
如果您现在进入浏览器并重新加载应用程序页面,它将仅显示损坏的图像。 发生这种情况是因为这些旅行照片的图像文件仍仅位于本地磁盘中。 我们需要将现有的图像文件上传到对象存储,以便可以将已存储在数据库中的照片成功显示在应用程序页面中。
用s3cmd
同步本地图像 (Syncing local images with s3cmd
)
The s3cmd
tool can be used to sync local files with an S3-compatible object storage service. We’ll run a sync
command to upload all files inside storage/app/public/photos
to the object storage service.
s3cmd
工具可用于将本地文件与兼容S3的对象存储服务同步。 我们将运行sync
命令,将storage/app/public/photos
中的所有文件上传到对象存储服务。
Access the public
app storage directory:
访问public
应用程序存储目录:
- cd /var/www/travellist/storage/app/public cd / var / www / travellist / storage / app / public
To have a look at the files already stored in your remote disk, you can use the s3cmd ls
command:
要查看已经存储在远程磁盘中的文件,可以使用s3cmd ls
命令:
s3cmd ls s3://your_bucket_name
s3cmd ls s3:// your_bucket_name
Now run the sync
command to upload existing files in the public storage folder to the object storage:
现在运行sync
命令,将公共存储文件夹中的现有文件上传到对象存储:
s3cmd sync ./ s3://your_bucket_name --acl-public --exclude=.gitignore
s3cmd sync ./ s3:// your_bucket_name --acl-public --exclude = .gitignore
This will synchronize the current folder (storage/app/public
) with the remote object storage’s root dir. You will get output similar to this:
这会将当前文件夹( storage/app/public
)与远程对象存储的根目录同步。 您将获得类似于以下的输出:
Output
upload: './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.
Now, if you run s3cmd ls
again, you will see that three new files were added to the root folder of your object storage bucket:
现在,如果再次运行s3cmd ls
,您将看到三个新文件已添加到对象存储桶的根文件夹中:
s3cmd ls s3://your_bucket_name
s3cmd ls s3:// your_bucket_name
Output
2019-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
Go to your browser and reload the application page. All images should be visible now, and if you inspect them using your browser debug tools, you’ll notice that they’re all using URLs from your object storage.
转到浏览器并重新加载应用程序页面。 现在所有图像都应该可见,并且如果使用浏览器调试工具检查它们,您会注意到它们都使用了对象存储中的URL。
测试集成 (Testing the Integration)
The demo application is now fully functional, storing files in a remote object storage service, and saving data to a managed MySQL database. We can now upload a few photos to test our setup.
演示应用程序现已完全正常运行,将文件存储在远程对象存储服务中,并将数据保存到托管MySQL数据库中。 现在,我们可以上传几张照片来测试设置。
Access the /upload
application route from your browser:
从浏览器访问/upload
应用程序路由:
http://server_domain_or_IP/upload
You will see the following form:
您将看到以下形式:
You can now upload a few photos to test the object storage integration. After choosing an image from your computer, you can select an existing place from the dropdown menu, or you can add a new place by providing its name and geographic coordinates so it can be loaded in the application map.
现在,您可以上传一些照片以测试对象存储集成。 从计算机中选择图像后,可以从下拉菜单中选择一个现有位置,也可以通过提供其名称和地理坐标来添加新位置,以便可以将其加载到应用程序地图中。
第6步-扩大具有只读节点的DigitalOcean托管MySQL数据库(可选) (Step 6 — Scaling Up a DigitalOcean Managed MySQL Database with Read-Only Nodes (Optional))
Because read-only operations are typically more frequent than writing operations on database servers, its is a common practice to scale up a database cluster by setting up multiple read-only nodes. This will distribute the load generated by SELECT
operations.
因为只读操作通常比在数据库服务器上进行写操作更为频繁,所以通过设置多个只读节点来扩展数据库集群是一种常见的做法。 这将分配由SELECT
操作生成的负载。
To demonstrate this setup, we’ll first add 2 read-only nodes to our DigitalOcean Managed MySQL cluster. Then, we’ll configure the Laravel application to use these nodes.
为了演示此设置,我们首先将2个只读节点添加到我们的DigitalOcean托管MySQL集群中。 然后,我们将配置Laravel应用程序以使用这些节点。
Access the DigitalOcean Cloud Panel and follow these instructions:
访问DigitalOcean Cloud Panel并按照以下说明进行操作:
Go to Databases and select your MySQL cluster.
转到数据库,然后选择您MySQL群集。
Click
Actions
and chooseAdd a read-only node
from the drop-down menu.单击“
Actions
然后从下拉菜单中选择“Add a read-only node
”。Configure the node options and hit the Create button. Notice that it might take several minutes for the new node to be ready.
配置节点选项,然后单击“ 创建”按钮。 请注意,准备好新节点可能要花费几分钟。
- Repeat steps 1-4 one more time so that you have 2 read-only nodes. 再重复一次1-4,以便您有2个只读节点。
- Note down the hosts of the two nodes as we will need them for our Laravel configuration. 记下两个节点的主机,因为我们的Laravel配置将需要它们。
Once you have your read-only nodes ready, head back to your terminal.
准备好只读节点后,请回到终端。
We’ll now configure our Laravel application to work with multiple database nodes. When we’re finished, queries such as INSERT
and UPDATE
will be forwarded to your primary cluster node, while all SELECT
queries will be redirected to your read-only nodes.
现在,我们将配置Laravel应用程序以与多个数据库节点一起使用。 完成后,诸如INSERT
和UPDATE
查询将被转发到您的主群集节点,而所有SELECT
查询将被重定向到您的只读节点。
First, go to the application’s directory on the server and open your .env
file using your text editor of choice:
首先,转到服务器上应用程序的目录,然后使用所选的文本编辑器打开.env
文件:
- cd /var/www/travellist cd / var / www / travellist
- nano .env 纳米.env
Locate the MySQL database configuration and comment out the DB_HOST
line:
找到MySQL数据库配置并注释掉DB_HOST
行:
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
Save and close the file when you’re done. Now open the config/database.php
in your text editor:
完成后,保存并关闭文件。 现在,在文本编辑器中打开config/database.php
:
- nano config/database.php 纳米config / database.php
Look for the mysql
entry inside the connections
array. You should include three new items in this configuration array: read
, write
, and sticky
. The read
and write
entries will set up the cluster nodes, and the sticky
option set to true
will reuse write
connections so that data written to the database is immediately available in the same request cycle. You can set it to false
if you don’t want this behavior.
在connections
数组中查找mysql
条目。 您应该在此配置数组中包括三个新项目: read
, write
和sticky
。 在read
和write
条目将设置群集节点和sticky
选项设置为true
将重用write
连接,以便写入到数据库中的数据在同一个请求周期立即可用。 如果您不希望出现这种情况,可以将其设置为false
。
...
'mysql' => [
'read' => [
'host' => [
'READONLY_NODE1_HOST',
'READONLY_NODE2_HOST',
],
],
'write' => [
'host' => [
'MANAGED_MYSQL_HOST',
],
],
'sticky' => true,
...
Save and close the file when you are done editing. To test that everything works as expected, we can create a temporary route inside routes/web.php
to pull some data from the database and show details about the connection being used. This way we will be able to see how the requests are being load balanced between the read-only nodes.
完成编辑后,保存并关闭文件。 为了测试一切正常,我们可以在routes/web.php
创建一条临时路由,以从数据库中提取一些数据并显示有关所使用连接的详细信息。 这样,我们将能够看到请求如何在只读节点之间进行负载平衡。
Open the routes/web.php
file:
打开routes/web.php
文件:
- nano routes/web.php 纳米路线/ web.php
Include the following route:
包括以下路线:
...
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;
});
Now go to your browser and access the /mysql-test
application route:
现在转到浏览器并访问/mysql-test
应用程序路由:
http://server_domain_or_IP/mysql-test
You’ll see a page like this:
您会看到这样的页面:
Reload the page a few times and you will notice that the Server ID
value changes, indicating that the requests are being randomly distributed between the two read-only nodes.
重新加载页面几次,您会注意到Server ID
值更改,表明请求在两个只读节点之间随机分配。
结论 (Conclusion)
In this guide, we’ve prepared a Laravel 6 application for a highly available and scalable environment. We’ve outsourced the database system to an external managed MySQL service, and we’ve integrated an S3-compatible object storage service into the application to store files uploaded by users. Finally, we’ve seen how to scale up the application’s database by including additional read-only cluster nodes in the app’s configuration file.
在本指南中,我们为高可用性和可伸缩环境准备了Laravel 6应用程序。 我们已经将数据库系统外包给了外部托管MySQL服务,并且已经将兼容S3的对象存储服务集成到应用程序中,以存储用户上传的文件。 最后,我们已经了解了如何通过在应用程序的配置文件中包括其他只读群集节点来扩展应用程序的数据库。
The updated demo application code containing all modifications made in this guide can be found within the 2.1 tag in the application’s repository on Github.
可以在Github上的应用程序存储库的2.1标签中找到包含本指南中所有修改的更新的演示应用程序代码。
From here, you can set up a Load Balancer to distribute load and scale your application among multiple nodes. You can also leverage this setup to create a containerized environment to run your application on Docker.
从这里,您可以设置负载均衡器以在多个节点之间分配负载并扩展您的应用程序。 您还可以利用此设置来创建一个容器化环境,以在Docker上运行您的应用程序。
翻译自: https://www.digitalocean.com/community/tutorials/how-to-set-up-a-scalable-laravel-6-application-using-managed-databases-and-object-storage
非托管对象 和托管对象