Операционная система Ubuntu (Xubuntu) поставляется вместе с встроенным сервером Apache2.
Apache
Когда возникает необходимость обновить либо переустановить сервер Apache, можем воспользоваться репозиториями утилы apt-get [22].
sudo apt-get update
sudo apt-get install apache2 (опционально)
sudo service apache2 restart - перезапуск сервиса apache.
В Ubuntu конечный файл настройки (apache2.conf) расделён на несколько файлов, расположенных в разных поддиректориях. Подробней написано в комментариях файла apache2.conf.
/etc/apache2/ |-- apache2.conf
| `-- ports.conf
|-- mods-enabled
| |-- *.load
| `-- *.conf
|-- conf-enabled
| `-- *.conf
`-- sites-enabled
`-- *.conf
Настройки модулей расположены в директории /etc/apache2/mods-available. Для подключения или отключения модулей (настроек модулей) следует использовать соответствующие команды a2enmod или a2dismod. Подключения модуля:
sudo a2enmod
Свои настройки следует сохранять в файлы, расположенные в директории /etc/apache2/conf-available. Для подключения или отключения своих настроек используем соответствующие команды a2enconf или a2disconf. Пример подключения файла со своими настройками:
sudo a2enconf
Настройки виртуальных хостов следует сохранять в файлы, расположенные в директории /etc/apache2/sites-available. Для подключения виртуальных хостов следует использовать соответствующие команды a2ensite или a2dissite. Пример подключения виртуального хоста:
sudo a2ensite
Для указания кодировки по умолчанию используем директиву AddDefaultCharset в файле /etc/apache2/conf-available/charset.conf:
AddDefaultCharset UTF-8
Виртуальные хосты
Файлы настроек виртуальных хостов хранятся в /etc/apache2/sites-available/*.conf. По умолчанию в Apache уже настроен один виртуальный хост. Его настройки лежат в файле 000-default.conf.
Настройки виртуального хоста:
#Имя хоста ServerName host1.server1 #Корневая папка хоста DocumentRoot /var/www/host1.server1 #Разрешение на перезапись всех директив при помощи .htaccess AllowOverride All
Назовём файл настройки именем хоста erud.server1.conf и сохраним.
После создания файла настроек необходимо дописать в /etc/hosts имя хоста: 127.0.0.1 host1.server1
Для включения созданного виртуального хоста воспользуемся утилитой a2ensite:
sudo a2ensite host1.server1
Отключается хост аналогично утилитой a2dissite:
sudo a2dissite host1.server1
Node
В стандартных репозиториях Ubuntu 18.04 уже есть версия Node.js, которую удобно использовать для обеспечения однородной среды выполнения сетевых приложений сразу на нескольких серверах [23].
Для установки последней версии воспользуемся пакетным менеджером apt. Сначала обновим локальный индекс пакетов:
sudo apt update
Теперь установим Node.js из репозиториев утилы apt:
sudo apt install nodejs
На этом установка Node.js закончена. Однако для полноценной работы с Node.js требуется уще установить npm - менеджер зависимостей. Это можно сделать при помощи следующей команды:
sudo apt install npm
Это позволит легко устанавливать модули и пакеты для Node.js.
Для проверки установленной версии Node.js выполним команду:
nodejs -v
npm -v
MySQL
По умолчанию в репозиторий пакетов APT в Ubuntu 18.04 включена только последняя версия MySQL. На момент написания этой диссертации это MySQL 5.7 [24].
Установка пакета MySQL осуществляется с помощью следующей команды:
sudo apt install mysql-server
Эта команда установит MySQL, но при этом не предлагается задавать пароль или вносить какие-либо правки в конфигурацию.
Начиная с 2010 года в MySQL 5.5 движок хранения данных InnoDB является основным движком в MySQL, и используется по умолчанию. Отличительными особенностями этого движка являются:
– высокая производительность;
– автоматическое восстановление данных после сбоя;
– автоинкрементируемые значения;
– данные сохраняются в одном или нескольких файлах.
PHP
Язык программирования PHP поставляется вместе с операционной системой Xubuntu. Для обновления PHP можно воспользоваться следующей командой [25].
apt-get install php
Кроме того, в процессе разработки нам потребуются некоторые модули для PHP, которые необходимо установить отдельно. Наличие этих модулей обязательно, так как без них не установится Laravel.
apt-get install php-pear php7.0-dev php7.0-zip php7.0-curl php7.0-gd php7.0-mysql php7.0-mcrypt php7.0-xml libapache2-mod-php7.0
Composer
Composer - менеджер зависимостей для PHP [26].
Сперва необходимо скачать установщик composer. Для этого можно воспользоватся диррективой wget
:
sudo wget -O composer-setup.php https://getcomposer.org/installer
После чего, запускаем команду установки:
sudo php composer-setup.php --install-dir=/usr/local/bin --filename=composer
Composer установлен. В чём можно убедиться, запустив команду
composer
PHPMyAdmin
Кроме сервера базы данных MySQL, нам потребуется система управления базой данных phpMyAdmin [27].
Для установки обновим наш локальный индекс пакетов, а затем используем систему управления пакетами apt для загрузки и установки необходимых файлов:
sudo apt-get update
sudo apt-get install phpmyadmin php-mbstring php-gettext
В процессе установки будет задано несколько вопросов по конфигурации.
При выборе сервера, выбераем apache2.
На вопрос, хотим ли мы использовать dbconfig-common для настройки базы данных отвечаем утвердительно.
Далее будет запрошен пароль администратора базы данных и повтор пароля.
В процессе установки в директорию /etc/apache2/conf-enabled/ будет добавлен файл конфигурации phpMyAdmin для Apache.
Единственное, что мы должны сделать вручную, так это включить расширения PHP mcrypt и mbstring следующими командами:
sudo phpenmod mcrypt
sudo phpenmod mbstring
Далее перезапустим Apache для применения изменений:
sudo systemctl restart apache2
Теперь мы можем осуществить доступ к веб-интерфейсу phpMyAdmin, введя имя домена или публичного статического IP адреса сервера и строки /phpmyadmin:
https://localhost/phpmyadmin
Установка Laravel, Nginx и MySQL с помощью Docker Compose
В последнее время Docker часто используется для развертывания приложений, поскольку он упрощает этот процесс с помощью виртуальных контейнеров. Например, при использовании стека LEMP (который состоит из PHP, Nginx и MySQL) и Laravel, Docker может значительно ускорить процедуру настройки приложения.
Docker Compose упрощает разработку, поскольку дает возможность определять инфраструктуру, — включая сервисы приложений, сети и тома, — в едином файле. Docker Compose легко заменяет собой сразу несколько команд, в том числе docker container create
и docker container run
.
1. Загрузка Laravel и установка зависимостей
Для начала загрузим последнюю версию Laravel и установим зависимости программы, в том числе и Composer, менеджер пакетов PHP уровня приложения. Используем Docker, чтобы не устанавливать Composer глобально. Перейдем в домашний каталог и клонируем последнюю версию Laravel в каталог laravel-app:
cd ~ git clone https://github.com/laravel/laravel.git laravel-app
Затем переходим в появившийся каталог:
cd ~/laravel-app
Теперь через образ composer смонтируем каталоги проекта Laravel:
docker run --rm -v $(pwd):/app composer install
Опции -v
и –rm
в команде docker run
создают контейнер, который привязывается к текущему каталогу до тех пор, пока он не будет удален. Содержимое каталога ~/laravel-app
скопируется в контейнер, а содержимое папки vendor, которую Composer создает внутри контейнера, будет скопировано в текущий каталог.
Теперь отредактируем привилегии каталога, все права на него нужно передать пользователю sudo:
sudo chown -R $USER:$USER ~/laravel-app
Это нужно для того, чтобы запускать процессы в контейнере через пользователя sudo.
2. Создание конфигурационного файла Docker Compose
Сборка приложений с помощью Docker Compose упрощает настройку и контроль версий в инфраструктуре. Настройка приложения осуществляется в файле docker-compose, в котором определяются сервисы веб-сервера, базы данных и приложения.
Откроем файл в редакторе gedit
sudo gedit ~/laravel-app/docker-compose.yml
В файле docker-compose нужно определить три сервиса: app, webserver и db:
version: '3' services: #PHP Service app: build: context: . dockerfile: Dockerfile image: digitalocean.com/php container_name: app restart: unless-stopped tty: true environment: SERVICE_NAME: app SERVICE_TAGS: dev working_dir: /var/www networks: - app-network #Nginx Service webserver: image: nginx:alpine container_name: webserver restart: unless-stopped tty: true ports: - "80:80" - "443:443" networks: - app-network #MySQL Service db: image: mysql:5.7.22 container_name: db restart: unless-stopped tty: true ports: - "3306:3306" environment: MYSQL_DATABASE: laravel MYSQL_ROOT_PASSWORD: your_mysql_root_password SERVICE_TAGS: dev SERVICE_NAME: mysql networks: - app-network #Docker Networks networks: app-network: driver: bridge
В файл входят следующие сервисы:
Свойство container_name определяет имя контейнера, которое должно совпадать с именем сервиса. Если вы не определите это свойство, Docker будет присваивать контейнерам случайные имена (по умолчанию он выбирает имя исторической личности и случайное слово, разделяя их символом подчеркивания).
Для простоты взаимодействия между контейнерами сервисы подключаются к соединительной сети app-network. Соединительная сеть использует программный мост, который позволяет подключенным к этой сети контейнерам взаимодействовать друг с другом. Драйвер устанавливает правила хоста автоматически, чтобы контейнеры из разных соединительных сетей не могли взаимодействовать напрямую. Это повышает безопасность приложений, поскольку взаимодействовать в таких условиях смогут только связанные сервисы.
3. Постоянное хранение данных
Docker предоставляет удобные средства для постоянного хранения данных. Для этого в данном тестовом приложении мы будем использовать тома и монтируемые образы. Тома очень гибкие в резервном копировании, их легко сохранять по истечении жизненного цикла контейнера, а монтируемые образы упрощают редактирование кода во время разработки и немедленно отражают изменения файлов или каталогов хоста в контейнерах. Используем оба варианта.
Монтируемые образы позволяют менять файловую систему хоста через работающие в контейнерах процессы, в том числе создавать, изменять и удалять важные системные файлы и каталоги. Это может повлиять на процессы в системе хоста, не связанные с Docker.
Для постоянного сохранения базы данных MySQL определим том dbdata в файле docker-compose
, в определении сервиса db:
... #MySQL Service db: ... volumes: - dbdata:/var/lib/mysql networks: - app-network ...
Том dbdata используется для постоянного сохранения содержимого /var/lib/mysql
в контейнере. Это позволяет останавливать и перезапускать сервис db, не теряя данных. В конеце файла определим том dbdata:
... #Volumes volumes: dbdata: driver: local
Теперь можно использовать этот том для разных сервисов. Затем добавим к сервису db привязку монтируемого образа для конфигурационных файлов MySQL.
... #MySQL Service db: ... volumes: - dbdata:/var/lib/mysql - ./mysql/my.cnf:/etc/mysql/my.cnf ...
Это привяжет файл ~/laravel-app/mysql/my.cnf
к каталогу /etc/mysql/my.cnf
в контейнере.
А теперь добавим монтируемые образы в сервис webserver. Образов будет два: один для кода приложения, а второй — для определения настроек Nginx.
#Nginx Service webserver: ... volumes: - ./:/var/www - ./nginx/conf.d/:/etc/nginx/conf.d/ networks: - app-network
Первый образ привязывает код приложения из каталога ~/laravel-app
к каталогу /var/www
внутри контейнера. Конфигурационный файл, добавляемый в ~/laravel-app/nginx/conf.d/
, также монтируется в папку /etc/nginx/conf.d/
в контейнере, что позволяет менять содержимое каталога по мере необходимости.
Теперь добавим следующие привязки образов в сервис app для кода приложения и конфигурационных файлов:
#PHP Service app: ... volumes: - ./:/var/www - ./php/local.ini:/usr/local/etc/php/conf.d/local.ini networks: - app-network
Сервис app привязывает монтируемый образ каталога ~/laravel-app, который содержит код приложения, к /var/www. Это позволяет ускорить разработку, поскольку все изменения в локальном каталоге приложения сразу же отразятся в контейнере. Также конфигурационный файл PHP ~/laravel-app/php/local.ini привязывается к файлу /usr/local/etc/php/conf.d/local.ini в контейнере. Мы создадим локальный конфигурационный файл PHP в разделе 5.
Теперь файл docker-compose имеет такой вид:
version: '3' services: #PHP Service app: build: context: . dockerfile: Dockerfile image: digitalocean.com/php container_name: app restart: unless-stopped tty: true environment: SERVICE_NAME: app SERVICE_TAGS: dev working_dir: /var/www volumes: - ./:/var/www - ./php/local.ini:/usr/local/etc/php/conf.d/local.ini networks: - app-network #Nginx Service webserver: image: nginx:alpine container_name: webserver restart: unless-stopped tty: true ports: - "80:80" - "443:443" volumes: - ./:/var/www - ./nginx/conf.d/:/etc/nginx/conf.d/ networks: - app-network #MySQL Service db: image: mysql:5.7.22 container_name: db restart: unless-stopped tty: true ports: - "3306:3306" environment: MYSQL_DATABASE: laravel MYSQL_ROOT_PASSWORD: your_mysql_root_password SERVICE_TAGS: dev SERVICE_NAME: mysql volumes: - dbdata:/var/lib/mysql/ - ./mysql/my.cnf:/etc/mysql/my.cnf networks: - app-network #Docker Networks networks: app-network: driver: bridge #Volumes volumes: dbdata: driver: local
Теперь пора создать пользовательский образ приложения.
4. Создание Dockerfile
Docker позволяет настраивать среду внутри отдельных контейнеров с помощью файла Dockerfile, который создает пользовательские образы.
Файл Dockerfile должен находиться в каталоге ~/laravel-app
. Создадим этот файл:
sudo gedit ~/laravel-app/Dockerfile
Этот Dockerfile будет определять базовый образ и необходимые команды для сборки образа приложения Laravel. Добавим в файл следующие строки:
FROM php:7.2-fpm # Copy composer.lock and composer.json COPY composer.lock composer.json /var/www/ # Set working directory WORKDIR /var/www # Install dependencies RUN apt-get update && apt-get install -y \ build-essential \ mysql-client \ libpng-dev \ libjpeg62-turbo-dev \ libfreetype6-dev \ locales \ zip \ jpegoptim optipng pngquant gifsicle \ vim \ unzip \ git \ curl # Clear cache RUN apt-get clean && rm -rf /var/lib/apt/lists/* # Install extensions RUN docker-php-ext-install pdo_mysql mbstring zip exif pcntl RUN docker-php-ext-configure gd --with-gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ --with-png-dir=/usr/include/ RUN docker-php-ext-install gd # Install composer RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer # Add user for laravel application RUN groupadd -g 1000 www RUN useradd -u 1000 -ms /bin/bash -g www www # Copy existing application directory contents COPY . /var/www # Copy existing application directory permissions COPY --chown=www:www . /var/www # Change current user to www USER www # Expose port 9000 and start php-fpm server EXPOSE 9000 CMD ["php-fpm"]
Сначала Dockerfile создает образ на основе образа php:7.2-fpm. Это образ Debian с предустановленным экземпляром PHP FastCGI PHP-FPM. Также этот файл устанавливает необходимые пакеты для Laravel: mcrypt, pdo_mysql, mbstring, imagick и composer.
Директива RUN определяет команды для обновления, установки и настройки параметров внутри контейнера, включая выделенного пользователя и группу www. Директива WORKDIR указывает рабочий каталог приложения (в данном случае это каталог /var/www
).
Используя отдельного пользователя и группу с ограниченными правами доступа, вы снижаете уязвимости при запуске контейнеров Docker (по умолчанию они запускаются с правами root). Чтобы не запускать контейнер с привилегиями root, мы создали пользователя www с правами на чтение и запись для каталога /var/www.
Это делается с помощью команды COPY и флага —chown для копирования прав каталога приложения.
Команда EXPOSE открывает в контейнере порт 9000 для сервера php-fpm. CMD определяет команду, которая будет запускаться после создания контейнера. Здесь CMD содержит команду php-fpm, которая запускает сервер.
Теперь пора определить конфигурации PHP.
5. Настройка PHP
Итак, мы определили инфраструктуру в файле docker-compose. Теперь можно настроить сервис PHP в качестве процессора для входящих запросов Nginx.
Для настройки PHP нужно создать файл local.ini в каталоге php. Это файл, который в разделе 2 мы привязали к файлу /usr/local/etc/php/conf.d/local.ini в контейнере. Имея этот файл, мы можем игнорировать файл по умолчанию php.ini, который PHP считывает при запуске.
mkdir ~/laravel-app/php
Открываем для редактирования:
sudo gedit ~/laravel-app/php/local.ini
Чтобы продемонстрировать настройку PHP, добавим следующий код для установки ограничений размера выгруженных файлов:
upload_max_filesize=40M post_max_size=40M
Директивы upload_max_filesize и post_max_size задают максимальный допустимый размер выгружаемых файлов и показывают, как задавать конфигурации php.ini из local.ini. Все параметры конфигурации PHP, которые можно игнорировать, поместим в файл local.ini.
Теперь пора настроить веб-сервер.
6. Настройка Nginx
Теперь можно настроить Nginx на поддержку PHP-FPM в качестве сервера FastCGI для обслуживания динамического контента. Для Nginx нужно создать в папке ~/laravel-app/nginx/conf.d/
файл app.conf с конфигурацией сервисов.
Сперва создадим каталог nginx/conf.d/
:
mkdir -p ~/laravel-app/nginx/conf.d
Затем создадим файл app.conf:
sudo gedit ~/laravel-app/nginx/conf.d/app.conf
Код, для определения конфигурации Nginx:
server { listen 80; index index.php index.html; error_log /var/log/nginx/error.log; access_log /var/log/nginx/access.log; root /var/www/public; location ~ \.php$ { try_files $uri =404; fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_pass app:9000; fastcgi_index index.php; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param PATH_INFO $fastcgi_path_info; } location / { try_files $uri $uri/ /index.php?$query_string; gzip_static on; } }
В блоке location для php директива fastcgi_pass указывает, что сервис app прослушивает сокет TCP по порту 9000. Благодаря этому сервер PHP-FPM прослушивает запросы через сеть, а не через сокет Unix. Сокет Unix имеет небольшое преимущество в скорости по сравнению с сокетом TCP, однако у него нет сетевого протокола и он пропускает сетевой стек. Если хосты находятся в одной системе, использование сокета Unix может иметь смысл, но если сервисы работают на разных хостах, сокет TCP гораздо лучше, поскольку позволяет подключаться к распределенным сервисам. Поскольку контейнеры app и webserver работают на разных хостах, в данной конфигурации сокет TCP будет эффективнее.
Все изменения в каталоге nginx/conf.d/ прямо отразятся в контейнере webserver.
7. Настройка MySQL
Настроив PHP и Nginx,можем включить MySQL как базу данных для приложения.
Для MySQL нужно создать файл my.cnf в каталоге mysql. Этот файл мы привязали к файлу /etc/mysql/my.cnf внутри контейнера. Привязка монтируемого образа позволяет игнорировать все параметры my.cnf, когда это потребуется.
Чтобы посмотреть, как это работает, давайте добавим в файл my.cnf параметры, которые включают лог общих запросов и задают лог-файл.
Создадим каталог mysql и пустой файл my.cnf:
mkdir ~/laravel-app/mysql nano ~/laravel-app/mysql/my.cnf
Поместим в файл следующий код, чтобы включить лог запросов и задать расположение лога:
[mysqld] general_log = 1 general_log_file = /var/lib/mysql/general.log
Файл my.cnf включает лог, задавая параметру general_log значение 1. Параметр general_log_file указывает, где будут храниться логи.
Все готово, пора запускать контейнеры.
8. Запуск контейнеров и изменение настроек среды
Итак, мы определили все сервисы в файле docker-compose и создали конфигурации для всех сервисов. Теперь можно запустить контейнеры. В качестве последнего шага мы создадим копию файла .env.example, который Laravel включает по умолчанию, и назовем ее .env, поскольку именно такой файл Laravel использует для определения среды:
cp .env.example .env
После запуска контейнеров мы вставим в этот файл параметры.
Теперь все сервисы определены в файле docker-compose. Запустим одну команду, которая запустит все контейнеры, создаст тома и настройки и подключит сети:
docker-compose up -d
При первом запуске команда docker-compose up загрузит все необходимые образы Docker, что может занять некоторое время. После загрузки образов на локальный компьютер Compose создаст контейнеры. Флаг -d преобразует процесс в демон, что позволяет поддерживать работу контейнеров в фоновом режиме.
После завершения этого процесса используем следующую команду, чтобы запросить список всех запущенных контейнеров:
docker ps
Увидим следующий вывод с данными о контейнерах app, webserver и db:
CONTAINER ID NAMES IMAGE STATUS PORTS c31b7b3251e0 db mysql:5.7.22 Up 2 seconds 0.0.0.0:3306->3306/tcp ed5a69704580 app digitalocean.com/php Up 2 seconds 9000/tcp 5ce4ee31d7c0 webserver nginx:alpine Up 2 seconds 0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp
В этом выводе CONTAINER ID — это уникальный идентификатор контейнера, а NAMES перечисляет имена сервисов. IMAGE определяет имя образа каждого контейнера, а STATUS предоставляет данные о состоянии.
Теперь отредактируем файл .env в контейнере app, чтобы добавить необходимые параметры.
Откроем файл с помощью docker-compose exec, что позволяет запускать определенные команды в контейнерах. В данном случае команда откроет файл для редактирования:
docker-compose exec app nano .env
В блоке DB_CONNECTION и определим особенности настройки системы.
DB_CONNECTION=mysql DB_HOST=db DB_PORT=3306 DB_DATABASE=laravel DB_USERNAME=laraveluser DB_PASSWORD=your_laravel_db_password
Затем настраиваем ключ для приложения Laravel с помощью команды php artisan key:generate. Команда сгенерирует ключ и скопирует его в файл .env, что защитит сессии пользователя и шифрованные данные:
docker-compose exec app php artisan key:generate
Теперь для запуска приложения имеются все необходимые настройки среды. Чтобы кэшировать эти настройки для ускорения загрузки приложения, запустим команду:
docker-compose exec app php artisan config:cache
Конфигурации будут загружены в /var/www/bootstrap/cache/config.php в контейнере.
На этом этапе можно проверить. Откроем в браузере сайт http://your_server_ip. На экране появится главная страница приложения Laravel.
Теперь можно перейти к настройке данных пользователя БД laravel в контейнере db.
9. Создание пользователя MySQL
Установка MySQL по умолчанию предоставляет только учетную запись root с неограниченными привилегиями доступа к серверу СУБД. Обычно при работе с базой данных лучше не использовать администратора, root. Вместо этого лучше создать специального пользователя для базы данных нашего приложения.
Чтобы создать его, запустим интерактивную оболочку bash в контейнере db с помощью команды docker-compose exec:
docker-compose exec db bash
Внутри контейнера входим в MySQL как root:
mysql -u root -p
Для начала проверим наличие базы данных laravel, которая была определена в файле docker-compose. Запустим show databases для проверки существующих баз данных:
show databases;
В выводе увидим БД laravel:
+--------------------+ | Database | +--------------------+ | information_schema | | laravel | | mysql | | performance_schema | | sys | +--------------------+ 5 rows in set (0.00 sec)
Затем создаем пользователя, у которого будет доступ к этой базе данных. Используем имя laraveluser. Имя пользователя и пароль должны соответствовать учетным данным, указанным ранее в файле .env.
GRANT ALL ON laravel.* TO 'laraveluser'@'%' IDENTIFIED BY 'your_laravel_db_password';
Чтобы сообщить серверу MySQL об изменениях, сбросим привелегии:
FLUSH PRIVILEGES;
Закроем MySQL:
EXIT;
И выходим из контейнера:
exit
10. Миграция данных
Теперь приложение запущено. Осталось произвести миграцию данных:
docker-compose exec app php artisan migrate
В случае успешной миграции, увидим следующее:
Migration table created successfully. Migrating: 2014_10_12_000000_create_users_table Migrated: 2014_10_12_000000_create_users_table Migrating: 2014_10_12_100000_create_password_resets_table Migrated: 2014_10_12_100000_create_password_resets_table
Теперь приложение Laravel работает! Убедимся в этом перейдя в браузере по адресу:
http://your_server_ip