Как настроить масштабируемое приложение Laravel 6, используя управляемые базы данных и хранилище объектов

Вступление

При горизонтальном масштабировании веб-приложений первые трудности, с которыми вы обычно сталкиваетесь, связаны с хранением файлов и сохранением данных. Это происходит главным образом из-за того, что трудно поддерживать согласованность переменных данных между несколькими узлами приложения; должны быть разработаны соответствующие стратегии, чтобы обеспечить немедленную доступность данных, созданных на одном узле, другим узлам кластера.

Практический способ решения проблемы согласованности - использование системmanaged databases иobject storage. Первый будет передавать данные о сохранении данных в управляемую базу данных, а второй будет предоставлять службу удаленного хранения, где вы можете хранить статические файлы и переменное содержимое, например изображения, загруженные пользователями. Каждый узел может затем подключиться к этим сервисам на уровне приложений.

Следующая диаграмма демонстрирует, как такая установка может использоваться для горизонтальной масштабируемости в контексте приложений PHP:

Laravel at scale diagram

В этом руководстве мы обновим существующее приложение Laravel 6, чтобы подготовить его к горизонтальной масштабируемости, подключив его к управляемой базе данных MySQL и настроив S3-совместимое хранилище объектов для сохранения файлов, созданных пользователем. К концу у вас будет приложение списка поездок, работающее на веб-сервере Nginx + PHP-FPM:

Travellist v1.0

[.note] #Note: в этом руководстве используютсяDigitalOcean Managed MySQL иSpaces для демонстрации масштабируемой настройки приложения с использованием управляемых баз данных и хранилища объектов. Приведенные здесь инструкции должны работать аналогичным образом для других поставщиков услуг.
#

Предпосылки

Чтобы начать этот урок, вам сначала понадобятся следующие предварительные условия:

  • Доступ к серверу Ubuntu 18.04 от имени пользователя без полномочий root с правами sudo и активным брандмауэром, установленным на вашем сервере. Чтобы настроить их, обратитесь к нашемуInitial Server Setup Guide for Ubuntu 18.04.

  • Nginx и PHP-FPM установлены и настроены на вашем сервере, как описано в шагах1 и3 изHow to Install LEMP on Ubuntu 18.04. Вы должны пропустить шаг, где установлен MySQL.

  • Composer установлен на вашем сервере, как описано в шагах1 и2 изHow to Install and Use Composer on Ubuntu 18.04.

  • Учетные данные администратора для управляемой базы данных MySQL 8. В этом руководстве мы будем использовать кластерDigitalOcean Managed MySQL, но приведенные здесь инструкции должны работать аналогично для других служб управляемых баз данных.

  • Набор ключей API с разрешениями на чтение и запись для S3-совместимой службы хранения объектов. В этом руководстве мы будем использоватьDigitalOcean Spaces, но вы можете использовать поставщика по своему выбору.

  • Утилитаs3cmd установлена ​​и настроена для подключения к вашему объектному хранилищу. Инструкции по настройке для DigitalOcean Spaces см. В нашемproduct documentation.

[[step-1 -—- install-the-mysql-8-client]] == Шаг 1 - Установка клиента MySQL 8

Репозитории Ubuntu apt по умолчанию поставляются с клиентом MySQL 5, который не совместим с сервером MySQL 8, который мы будем использовать в этом руководстве. Чтобы установить совместимый клиент MySQL, нам нужно будет использовать MySQL APT Repository, предоставляемый Oracle.

Начните с перехода кMySQL APT Repository page в своем браузере. Найдите кнопкуDownload в правом нижнем углу и перейдите на следующую страницу. На этой странице вам будет предложено войти в систему или зарегистрировать учетную запись Oracle. Вы можете пропустить это и вместо этого найти ссылку сNo thanks, just start my download. Скопируйте адрес ссылки и вернитесь в окно терминала.

Эта ссылка должна указывать на пакет.deb, который настроит репозиторий MySQL APT на вашем сервере. После его установки вы сможете использоватьapt для установки последних выпусков MySQL. Мы будем использоватьcurl, чтобы загрузить этот файл во временную папку.

Перейдите в папкуtmp вашего сервера:

cd /tmp

Теперь загрузите пакет сcurl и используя URL-адрес, который вы скопировали со страницы репозитория MySQL APT:

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 -—- created-a-new-mysql-user-and-database]] == Шаг 2 - Создание нового пользователя и базы данных MySQL

На момент написания этой статьи собственная PHP-библиотека MySQLmysqlnddoesn’t supportcaching_sha2_authentication, метод аутентификации по умолчанию для MySQL 8. Нам нужно будет создать нового пользователя с методом аутентификацииmysql_native_password, чтобы иметь возможность подключить наше приложение Laravel к серверу MySQL 8. Мы также создадим выделенную базу данных для нашего демонстрационного приложения.

Чтобы начать, войдите на свой сервер, используя учетную запись администратора. Замените выделенные значения вашим собственным пользователем, хостом и портом 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 для настройки таблиц нашего приложения. Если вам необходимо перенести существующую локальную базу данных в базу данных MySQL, управляемую DigitalOcean, обратитесь к нашей документации по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.

Чтобы получить код приложения, совместимый с этим руководством, мы загрузим1.1 release из репозитория проекта на Github. Мы сохраним загруженный 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

Перед тем, как продолжить, нам нужно установить несколько модулей PHP, которые требуются фреймворке Laravel, а именно: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 и установка ключа приложения

Теперь мы создадим файл.env, содержащий переменные, которые будут использоваться для настройки приложения Laravel для каждой среды. Приложение включает в себя файл примера, который мы можем скопировать, а затем изменить его значения в соответствии с настройками нашей среды.

Скопируйте файл.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: имя базы данных приложения, которую мы создали вStep 2.
DB_USERNAME: пользователь базы данных, который мы создали вStep 2.
DB_PASSWORD: пароль для пользователя базы данных, который мы определили вStep 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+X, затемY иENTER, когда закончите редактирование.

Теперь, когда приложение настроено для подключения к базе данных MySQL, мы можем использовать инструмент командной строки Laravelartisan для создания таблиц базы данных и заполнения их образцами данных.

Миграция и заполнение базы данных

Теперь мы будем использовать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.

Это создаст символическую ссылку внутри каталогаpublic, указывающую наstorage/app/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, для обслуживания приложения в течение длительного времени.

Мы будем использовать порт8000 для временного обслуживания приложения для тестирования. Если на вашем сервере включен брандмауэр UFW, сначала вы должны разрешить доступ к этому порту с помощью:

sudo ufw allow 8000

Теперь, чтобы запустить встроенный PHP-сервер, который Laravel предоставляет через инструментartisan, запустите:

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

Эта команда заблокирует ваш терминал, пока не будет прерванаCTRL+C. Он будет использовать встроенный веб-сервер PHP для обслуживания приложения в целях тестирования на всех сетевых интерфейсах, используя порт8000.

Теперь перейдите в браузер и войдите в приложение, используя доменное имя сервера или IP-адрес на порту8000:

http://server_domain_or_IP:8000

Вы увидите следующую страницу:

Travellist v1.0

Если вы видите эту страницу, это означает, что приложение успешно извлекает данные о местах и ​​фотографиях из настроенной управляемой базы данных. Файлы изображений по-прежнему хранятся на локальном диске, но мы изменим это на следующем шаге этого руководства.

Когда вы закончите тестирование приложения, вы можете остановить командуserve, нажавCTRL+C.

Не забудьте снова закрыть порт8000, если вы используете UFW на своем сервере:

sudo ufw deny 8000

[[step-4 -—- configuring-nginx-to-serve-the-application]] == Шаг 4. Настройка Nginx для обслуживания приложения

Хотя встроенный веб-сервер PHP очень полезен для целей разработки и тестирования, он не предназначен для использования в качестве долгосрочного решения для обслуживания приложений PHP. Рекомендуется использовать полнофункциональный веб-сервер, такой как Nginx.

Для начала переместим папку приложения в/var/www, это обычное место для веб-приложений, работающих на Nginx. Сначала используйте командуmv, чтобы переместить папку приложения со всем ее содержимым в/var/www/travellist:

sudo mv ~/travellist /var/www/travellist

Теперь нам нужно предоставить пользователю веб-сервера доступ на запись к папкамstorage иbootstrap/cache, где Laravel хранит файлы, созданные приложением. Мы установим эти разрешения с помощьюsetfacl, утилиты командной строки, которая позволяет более надежно и детально настраивать разрешения для файлов и папок.

Чтобы включить разрешения на чтение, запись и выполнение (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

Следующий файл конфигурации содержит настройкиrecommended для приложений Laravel на Nginx:

/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 и настройте выделенные значения в соответствии с вашей собственной конфигурацией. Сохраните и закройте файл, когда вы закончите редактирование.

Чтобы активировать новый файл конфигурации виртуального хоста, создайте символическую ссылку наtravellist вsites-enabled:

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

[.note] #Note: Если у вас есть другой файл виртуального хоста, который ранее был настроен для того жеserver_name, который использовался на виртуальном хостеtravellist, вам может потребоваться деактивировать старую конфигурацию, удаление соответствующей символической ссылки внутри/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

Теперь перейдите в свой браузер и получите доступ к приложению, используя доменное имя или IP-адрес сервера, как определено директивойserver_name в вашем файле конфигурации:

http://server_domain_or_IP

Travellist v1.0

На следующем шаге мы интегрируем сервис хранения объектов в приложение. Это заменит текущее локальное дисковое хранилище, используемое для фотографий путешествий.

[[шаг-5 -—- интеграция-an-s3-совместимого-объектного хранилища в приложение]] == Шаг 5 - Интеграция S3-совместимого хранилища объектов в приложение

Теперь мы настроим приложение на использование S3-совместимого сервиса хранения объектов для хранения фотографий путешествий, показанных на странице указателя. Поскольку в приложении уже есть несколько образцов фотографий, хранящихся на локальном диске, мы также будем использовать инструментs3cmd для загрузки существующих локальных файлов изображений в хранилище удаленных объектов.

Настройка драйвера S3 для Laravel

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

В этом файле мы создадим новую записьdisk в массивеdisks. Мы назовем этот диск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 по умолчанию. Таким образом, приложение остается гибким в использовании нескольких решений для хранения данных, и вы можете переключаться между провайдерами для каждой среды.

Приложение теперь настроено на использование хранилища объектов, но нам все еще необходимо обновить код, который загружает новые фотографии в приложение.

Давайте сначала исследуем текущий маршрутuploadPhoto, расположенный в классеPhotoController. Откройте файл с помощью вашего текстового редактора:

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. В этом случае он использует диск по умолчанию для хранения загруженных изображений.

Мы изменим это поведение, чтобы изображение сохранялось в хранилище объектов вместо локального диска. Для этого нам нужно заменить дискpublic на дискspaces в вызове методаstore. Нам также необходимо убедиться, что видимость загруженного файла установлена ​​наpublic вместоprivate.

Следующий код содержит полный классPhotoController, включая обновленный методuploadPhoto:

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, чтобы использовать URL-адрес файла с диска храненияspaces:

Если вы сейчас зайдете в браузер и перезагрузите страницу приложения, на ней будут показаны только битые изображения Это происходит потому, что файлы изображений для этих фотографий путешествий все еще находятся только на локальном диске. Нам необходимо загрузить существующие файлы изображений в хранилище объектов, чтобы фотографии, уже сохраненные в базе данных, могли быть успешно выставлены на странице приложения.

Синхронизация локальных образов с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, вы увидите, что три новых файла были добавлены в корневую папку вашей корзины хранилища объектов:

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 - Масштабирование базы данных MySQL, управляемой DigitalOcean, с узлами только для чтения (Необязательный)

Поскольку операции только для чтения обычно более часты, чем операции записи на серверах баз данных, обычной практикой является масштабирование кластера базы данных путем настройки нескольких узлов только для чтения. Это распределит нагрузку, создаваемую операциямиSELECT.

Чтобы продемонстрировать эту настройку, мы сначала добавим 2 узла только для чтения в наш кластер MySQL, управляемый DigitalOcean. Затем мы настроим приложение Laravel для использования этих узлов.

ОткройтеDigitalOcean Cloud Panel и следуйте этим инструкциям:

  1. Перейдите кDatabases и выберите свой кластер MySQL.

  2. ЩелкнитеActions и выберитеAdd a read-only node в раскрывающемся меню.

  3. Настройте параметры узла и нажмите кнопкуCreate. Обратите внимание, что для готовности нового узла может потребоваться несколько минут.

  4. Повторите шаги 1-4 еще раз, чтобы у вас было 2 узла только для чтения.

  5. Запишите хосты двух узлов, так как они понадобятся нам для конфигурации Laravel.

Когда у вас есть готовые узлы только для чтения, вернитесь к своему терминалу.

Теперь мы настроим наше приложение 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

Найдите записьmysql внутри массиваconnections. Вы должны включить новые элементыthree в этот массив конфигурации:read,write и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

Вы увидите страницу, подобную этой:

mysql node test page

Перезагрузите страницу несколько раз, и вы заметите, что значениеServer ID изменилось, показывая, что запросы случайным образом распределяются между двумя узлами только для чтения.

Заключение

В этом руководстве мы подготовили приложение Laravel 6 для высокодоступной и масштабируемой среды. Мы передали систему базы данных на внешний управляемый сервис MySQL и интегрировали S3-совместимую службу хранения объектов в приложение для хранения файлов, загруженных пользователями. Наконец, мы увидели, как увеличить базу данных приложения, добавив дополнительные узлы кластера только для чтения в файл конфигурации приложения.

Обновленный код демонстрационного приложения, содержащий все изменения, внесенные в это руководство, можно найти в2.1 tag в репозитории приложения на Github.

Отсюда вы можете настроитьLoad Balancer для распределения нагрузки и масштабирования приложения между несколькими узлами. Вы также можете использовать эту настройку для созданияcontainerized environment для запуска вашего приложения в Docker.

Related