Сегодняшняя статья будет посвящена подготовке рабочего места разработчика. А именно — установке и настройке LNMP-окружения на виртуальной машине в windows-среде. Всех заинтересовавшихся приглашаю под кат!

Сразу же возникает пара вопросов:

  1. Почему не взять готовый Denwer, XAMPP, Open Server?
  2. Зачем всё делать руками? Есть же Vagrant

Прежде, чем начать описание процесса, отвечу на эти вопросы по порядку.
1. В сети есть много готовых сборок веб-серверов, предназначенных для локальной разработки в windows-средах. Для начинающих разработчиков полезно использовать такие сборки, чтобы овладеть языком программирования и не заморачиваться с системным администрированием. Но очень скоро начинаются первые трудности:

  • Большинство хостингов сейчас предлагают linux-среды для размещения сайтов. Соответственно, тут же начинаются проблемы с кодировкой, адресацией и прочим при переносе своего проекта из среды разработки на продакшн.
  • Если первые проблемы устранены, то со временем возникает необходимость установки модулей PHP/Apache/NGINX для решения прикладных задач. При портировании с windows на *nix могут возникнуть проблемы с настройкой и поведением приложения.
  • PHP 5.5 и выше не умеет работать с Windows XP и ниже

2. Автоматическая сборка сервера — это здорово, удобно, круто и даёт профит при указании в резюме 😉 Разумеется, гораздо легче собрать сервер при помощи того же Vagrant. Но проблема в том, что любая автоматизация без понимания всех стадий технического процесса очень быстро разваливается. Поэтому я расскажу о пошаговой сборке сервера руками, чтобы потом уже можно было говорить об автоматизации.

Итак, начнём.

Шаг №0. Скачиваем дистрибутивы.

  • VirtualBox для Windows [ скачать ] — выбирайте соответствующую разрядность для вашей операционной системы. Если Вы используете х64 систему и хотите установить x64 виртуальную машину, то необходимо проверить, что Ваш процессор имеет аппаратную поддержку виртуализации. Если это так, то в BIOS необходимо перейти к секции Processor и включить Intel Virtualization Technology (в некоторых системах может называться Virtualization Extensions) или AMD-V. После этого VirtualBox сможет работать с 64-разрядными виртуалками
  • Дистрибутив Linux [ скачать ] — начнём с наиболее простого для изучения дистрибутива : Ubuntu Server 18.04 LTS. Вообще, я предпочитаю CentOS для работы, но его преимущества безопасности и строгости становятся его же минусами для изучения с нуля.
  • Клиент для подключения через SSH — PuTTY

Шаг №1. Создаём виртуальную машину и устанавливаем ОС
После запуска программа VirtualBox встретит нас примерно таким окном:
Главное окно
В нём Вам нужно выбрать кнопку «Создать», по нажатию которой система выдаст диалоговое окно мастера создания виртуальных машин
Мастер создания виртуальной машины
Здесь нужно указать:

  • Имя виртуалки (какое больше нравится)
  • Выбрать тип ОС (нам нужна Linux)
  • Выбрать тип серверного linux-дистрибутива (у наc Ubuntu Server)
  • Указать объём доступной оперативной памяти. Тут нужно отталкиваться от требований проектов. Для скриптов при обучении с лихвой хватит 2 ГБ. Если на стадии обучения скрипты будут вылезать за лимит памяти, то что-то точно идёт не так. Можно указать и меньше, но для нашего проекта будет использоваться база данных MySQL, а она довольно прожорливая в плане RAM.
  • Дать инструкцию на создание виртуального жёсткого диска. Нам вполне хватит 20 гигабайт для стартовых проектов.

По нажатию на кнопку «Создать», система выдаст ещё одно диалоговое окно — создания виртуального жёсткого диска

Указываем расположение, размер, тип (VDI) по умолчанию. Формату хранения я бы уделил больше внимания.

  • Динамический жёсткий диск изначально занимает мало места на физическом носителе, но растёт по мере добавления файлов на виртуальную машину. Растёт он ровно до указанного при создании лимита.
  • Фиксированный жёсткий диск на физическом носителе сразу занимает примерно тот же объём памяти, который выделяется ему при установке.

В чём же разница? В целях обеспечения стабильности работы системы лучше использовать виртуальные жёсткие диски фиксированного размера, т.к.

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

Итак, мы создали наш жёсткий диск и вернулись в первое диалоговое окно. Наша виртуальная машина готова к запуску. Но не торопитесь стартовать! Нужно внести ещё несколько настроек. Выбираем нашу виртуальную машину и переходим в меню её настроек.
Настройка виртуальной машины
Нам нужно настроить подключение виртуалки к сети, а также указать ей доступ к скачанному образу операционной системы.

  1. Переходим в меню «Сеть» и выбираем тип подключения «Сетевой мост». При необходимости нужно указать сетевую карту, если у Вас их несколько. В данном варианте соединения адаптер выступает в роли моста между виртуальной и физической сетями. Со стороны внешней сети имеется возможность напрямую соединяться с гостевой операционной системой — это очень удобная опция.
  2. Переходим в меню «Носители -> Атрибуты -> Оптический привод» и кликаем на иконку компакт диска, чтобы выбрать наш скачанный образ на 0-вом шаге.
    Выбор образа

Вот теперь можно нажимать «ОК» и запускать виртуальную машину. При первом запуске начнётся установка ОС Ubuntu. Выбираем язык English, а на следующем шаге — Install Ubuntu Server.

На заметку: для того, чтобы вывести курсор из консоли виртуальной машины используйте комбинацию клавиш "Ctrl" + "Alt" справа.

Далее Вам будет предложено

  • Выбрать язык ОС и временную зону.
    199329c56f
    Россия находится в Other -> Europe -> Russian Federation.
    На шаге configure locales выбираем United States — en_US.UTF-8.
    Раскладку клавиатуры (Keyboard layout) можно определить автоматически.
  • Указать сетевое имя (Hostname) — поставим UbuntuDev.
  • Создать вторичного (после root — администратора) пользователя. Создадим пользователя developer с удобным паролем.
  • Зашифровать пользовательский раздел — мы пока этого делать не будем
  • Подтвердить временную зону сервера
  • Разбить диск на разделы. О том, как Linux работает с диском можно прочитать здесь. А мы подробнее рассмотрим процесс разбиения на диски.

Сначала нам нужно выбрать ручное разбиение дисков — Manual
Ручное разбиение дисков

На скриншоте ниже система предлагает выбрать диск, на котором будет создана таблица разделов. Выбираем SCSI3 и на следующем шаге подтверждаем создание новой таблицы разделов.

В следующем диалоге выбираем пункт FREE SPACE
FREE SPACE
и создаём загрузочный раздел:

  1. Выбираем Create a new partition
    Create a new partition
  2. Указываем new partition size (например, 512 MB)
    Partition size
  3. Задаём type Primary
    Primary
  4. Задаём location Beginning
    Beginning
  5. Выбираем опцию Mount point и нажимаем Enter -> Указываем mount point /boot
    Mount point
    ce215845b4
  6. Устанавливаем bootable flag в on
  7. Выбираем Done setting up the partition

Теперь создадим логические разделы для данных и файл подкачки (swap). Я рекомендую создать следующие логические разделы:

  • /data — здесь у нас будут лежать скрипты и база данных. В больших системах стоит разделять эти вещи.
  • /tmp — здесь будут накапливаться временные файлы
  • /var/log — для больших систем стоит отделить раздел логов, чтобы он не мешал основной системе, но для нас это некритично.

Выбираем снова FREE SPACE -> Create a new partition. Укажем для раздела данных размер в 8 GB

Тип указываем Logical, location — beginning, Mount point -> Enter manually -> /data . В остальном всё по умолчанию. Заканчиваем создание, выбрав Done setting up the partition.
Точно также создаём раздел /tmp на 512 MB, указав в конце mount point /tmp.
Ещё 10 гигабайт выделим под корневой раздел ( «/» ) — там у нас будут храниться файлы БД.

У нас осталось около немного места. Выделим его под раздел подкачки. Для того, чтобы сделать это, на последнем шаге в пункте Use as выбираем значение swap area
swap area

В итоге должна получиться примерно следующая картинка
Disk parts

Разметка завершена, выбираем Finish partitioning and write changes to disk

Далее установщик предложит Вам указать настройки прокси-соединения. Если Вы не используете прокси, то просто пропустите этот шаг (Continue). Если же прокси есть, то заполните поля согласно настройкам Вашего прокси-соединения.

На следующем шаге Вам предложат выбрать автоматическую установку обновлений безопасности. Рекомендую оставить этот пункт в значении «No automatic updates», если Вы не уверены, что обновления не будут мешать Вам жить. Ведь каждое обновление потенциально приносит несовместимость с текущей системой.
No updates, please!

Теперь установщик попросит указать пакеты, которые потребуется установить. Выбираем только OpenSSH Server (с помощью пробела). Остальное установим сами в нужных версиях.
Перед завершением установки Ubuntu попросит добавить системный загрузчик GRUB в главную загрузочную запись. Выбираем Yes и продолжаем.

На этом установка закончена. Система предупредит о том, что нужно вынуть (для нас — размонтировать) установочный диск. Это можно сделать из меню VitrualBox -> Настройки -> Носители -> Атрибуты -> Оптический привод.
Нажимаем Continue.
После перезагрузки в том самом окне GRUB выбираем Ubuntu для загрузки.
GRUB

Система запросит логин и пароль. Входим под пользователем developer, которого мы создали в процессе установки. Мы внутри! 🙂
Ubuntu

Шаг №2. Оптимизируем взаимодействие с ОС и устанавливаем пакеты.
Посмотрим, какой IP нам был присвоен с помощью команды

ifconfig

В моём случае это 172.20.4.14
IP адрес

Запустим ssh (если он не запускался) и добавим его к автозапуску

service ssh start

update-rc.d ssh defaults

Теперь можно запустить скачанную на шаге №0 утилиту PuTTy и использовать её для соединения с сервером. Её интерфейс гораздо удобнее работы через VirtualBox.
PuTTy выглядит следующим образом:
PuTTy

Вводим полученный IP в поле Host Name (or IP address). Используем порт 22 — это порт для соединений SSH по умолчанию. Нажимаем Open.
Логинимся под пользователем developer. Теперь можно заниматься установкой нужных нам пакетов.

Нам потребуются:

  1. NGINX — веб-сервер для обработки пользовательских запросов
  2. PHP-FPM. Для установленной мной версии Ubuntu 18.04 LTS актуальной версией PHP является 7.2.3
  3. Percona DB — форк движка MySQL. Более стабильный и производительный
На заметку: более "классической" является сборка Apache + NGINX + PHP, где все запросы принимаются с помощью NGINX как реверс-прокси (он либо отдаёт статические файлы, либо направляет запрос к Apache, который взаимодействует с PHP). Связка более ресурсоёмкая, но и менее уязвима за счёт добавления слоя Apache. NGINX менее требователен к ресурсам.

Устанавливаем NGINX
В Ubuntu 18.04 LTS по умолчанию доступен NGINX версии 1.14 (на момент написания статьи), что вполне себе нормально по функционалу, поэтому можно ставить сразу из репозитория.

apt-get install nginx

После установки можем проверить версию

nginx -v

Должно получиться что-то вроде
nginx version: nginx/1.14.0

Устанавливаем Percona DB
Мы будем устанавливать Percona версии 5.7, где InnoDB, как известно, уже умеет делать FullText индексы, а MyISAM уже и не нужен.
Скачиваем пакет репозиториев Percona

wget https://repo.percona.com/apt/percona-release_0.1-6.$(lsb_release -sc)_all.deb

И устанавливаем его

dpkg -i percona-release_0.1-6.$(lsb_release -sc)_all.deb

Обновляем список репозиториев. Не забываем, что находимся под root-ом!

apt-get update

Теперь можно поставить пакет БД

apt-get install percona-server-server-5.7

В процессе установки система запросит пароль root-пользователя для БД

Устанавливаем PHP-FPM
Тут всё достаточно просто

apt-get install php-fpm php-cli php-mysqli php-gd

Проверяем установку

php -v

Должно получиться
PHP 7.2.3-1ubuntu4.18 (cli) (built: Oct 28 2015 01:34:46)
Copyright (c) 1997-2018 The PHP Group

Заодно добавим PHP-FPM в автозагрузку командой

update-rc.d php-fpm defaults

Обратите внимание на то, что в новых пакетах php-fpm может называться, например, php7.2-fpm.

Шаг №3. Настраиваем пакеты и их взаимодействие
Теперь мы можем приступить непосредственно к настройке установленных пакетов для того, чтобы начать работу.
Настраиваем NGINX
Веб-сервер NGINX многопоточен. Согласно документации NGINX количество дочерних процессов веб-сервера должно быть равно количеству ядер процессора в системе.
Давайте узнаем, сколько ядер процессоров есть у нас

cat /proc/cpuinfo | grep processor | wc -l

На моей виртуальной машине доступно 1 ядро. Я отредактирую файл nginx.conf

nano nginx.conf

И выставлю значение

worker_processes  1;

В конец файла добавим ссылку на подключение директории виртуальных хостов

include /etc/nginx/sites-enabled/*;

Сохраняем файл через Ctrl + O.

Если проект должен быть доступен через браузер, то нужно создать виртуальный хост, который будет обслуживать наш проект.
Создадим директорию проекта в разделе /data

mkdir /data/myproject.com

mkdir /data/myproject.com/docs

mkdir /data/myproject.com/logs

chown -R nginx:nginx /data/myproject.com

chmod -R 0775 /data/myproject.com

Обратите внимание на то, какой пользователь выполняет процесс nginx. Это может быть и www-data (посмотреть можно в nginx.conf). Поэтому в командах выше можно заменить nginx на актуального пользователя.

Теперь создаём настройку для нашего виртуального хоста

nano /etc/nginx/sites-available/myproject.com

Ниже я привожу код конфигурации виртуального хоста.

server {

    # слушаем стандартный порт HTTP

    listen  80;

    # здесь нужно указать наш домен

    server_name myproject.com www.myproject.com;

    # кодировка по умолчанию

    charset utf-8;

    # для разработки потребуются логи

    access_log  /data/myproject.com/logs/access.log combined;

    error_log   /data/myproject.com/logs/error.log;

    # корневая директория логики

    root /data/myproject.com/docs;

    # установим сжатие данных

    gzip on;

    gzip_disable "msie6";

    gzip_comp_level 6;

    gzip_min_length  1100;

    gzip_buffers 16 8k;

    gzip_proxied any;

    gzip_types text/plain application/xml

      application/javascript

      text/css

      text/js

      text/xml

      application/x-javascript

      text/javascript

      application/json

      application/xml+rss;

    # настройки размеров и таймаутов

    client_max_body_size            100m;

    client_body_buffer_size         128k;

    client_header_timeout           3m;

    client_body_timeout             3m;

    send_timeout                    3m;

    client_header_buffer_size       1k;

    large_client_header_buffers     4 16k;

    # правила обработки запросов к домену

    location / {

        # корневая директория

        root /data/myproject.com/docs;

        # стартовый скрипт

        index index.php;

        # правило автозагрузки в порядке следования: файл, папка, скрипт

        try_files $uri $uri/ @fallback;

    }

    # правило для того, чтобы отдавать статические файлы

    location ~* \.(jpeg|ico|jpg|gif|png|css|js|pdf|txt|tar|gz|wof|csv|zip|xml|yml) {

        access_log off;

        try_files $uri @statics;

        expires 14d;

        add_header Access-Control-Allow-Origin *;

        add_header Cache-Control public;

        root /data/myproject.com/docs;

    }

    location @statics {

        rewrite ^/(\w+)/(.*)$ /$2 break;

        access_log off;

        rewrite_log off;

        expires 14d;

        add_header Cache-Control public;

        add_header Access-Control-Allow-Origin *;

        root /data/myproject.com/docs;

    }

    # правила обработки PHP-скриптов

    location ~ \.php$ {

        root /data/myproject.com/docs;

        proxy_read_timeout 120;

        fastcgi_read_timeout 120;

        try_files $uri $uri/ =404;



        # внимательно смотрите на то, какое имя задано у сокета

        # это можно узнать в настройках php-fpm

        fastcgi_pass unix:/var/run/php-fpm.sock;

        fastcgi_index index.php;

        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

        include fastcgi_params;

    }

}

Активируем виртуальный хост

ln -s ../sites-available/myproject.com

Если пользователь nginx существует, добавим пользователя nginx в группу www-data, чтобы NGINX мог получить доступ к сокету PHP

usermod -a -G www-data nginx

Перезапустим NGINX

service nginx restart

Настраиваем Percona
Любой MySQL движок использует файл /etc/my.cnf , но у Percona его по умолчанию нет.
Создадим его

touch /etc/my.cnf

Внесём настройки

[client]

# порт и сокет для клиента

port            = 3306

socket          = /var/run/mysqld/mysqld.sock

[mysqld]

user            = mysql

pid-file        = /var/run/mysqld/mysqld.pid

socket          = /var/run/mysqld/mysqld.sock

port            = 3306

datadir         = /var/lib/mysql

# Отдадим под innodb четверть памяти. Настройка эта зависит от объёма данных в формате innodb

# Но в идеале нужно всегда устанавливать значение больше, чем полный объём данных в innodb

innodb_buffer_pool_size = 512M  

# У нас один процессор, поэтому будет только 1 поток кэша

innodb_buffer_pool_instances = 1

# Чтобы innodb работал быстрее, отключаем запись резервных данных на жёсткий диск

innodb_flush_log_at_trx_commit = 2

innodb_flush_method = 'O_DIRECT'

Сохраним файл. Для того, чтобы применить более тонкие настройки, можно воспользоваться утилитой mysqltuner.

Зайдём на сервер MySQL. Пароль рута был задан при установке.

mysql -uroot -p

Посмотрим, что за пользователи созданы по умолчанию

use mysql;

select * from user;

Как видим, полно всего ненужного.
MySQL users

Удаляем пользователей, оставляя только root@localhost

drop user 'root'@'ubuntudev';

drop user 'root'@'127.0.0.1';

drop user 'root'@'::1';

drop user ''@'localhost';

drop user ''@'ubuntudev';

flush privileges;

Теперь создадим базу данных и пользователя для нашего PHP-приложения. Разумеется, пароль задаём свой.

create database application_db;

create user 'connect'@'localhost' identified by 'password';

grant SELECT, INSERT, UPDATE, DELETE on application_db.* to 'connect'@'localhost';

flush privileges;

Пользователь создан, права обновлены. Теперь можно будет спокойно соединяться с базой и работать с данными.

Настраиваем PHP
Зачастую нужно использовать PHP не только для генерации страниц, но и для выполнения команд из консоли.
При этом используются разные модули PHP и, как следствие, разные конфигурационные файлы. Для удобства сделаем один файл конфигурации и ссылки на него из модулей. Выполним поочерёдно команды

mv /etc/php/fpm/php.ini ../

rm /etc/php/fpm/php.ini

rm /etc/php/cgi/php.ini

cd /etc/php/fpm

ln -s /etc/php/php.ini

cd /etc/php/cgi

ln -s /etc/php/php.ini

В файле /etc/php/fpm/php.ini находим параметры и вносим изменения

short_open_tag = On

cgi.fix_pathinfo = 0

date.timezone = Europe/Moscow

Перезагружаем PHP-FPM

service php-fpm restart

Шаг №4. Взлетит?
Теперь попробуем написать что-нибудь на PHP и проверить работоспособность результата нашей долгой работы.
Перейдём в директорию проекта

cd /data/myproject.com/docs

Создадим там простенький файл index.php с содержимым

Теперь пропишем привязку хоста к доменному имени. Если Вы работаете под Windows, то с правами администратора откройте файл
C:\Windows\System32\drivers\etc\hosts
и добавьте туда строчку

172.20.4.14 myproject.com

Разумеется, нужно заменить 172.20.4.14 на IP, выданный Вам сетью.
Сохраняем файл, открываем браузер и набираем myproject.com.

Если всё сделано правильно, то результатом будет вот такая картинка
Результат

Шаг №5. Работа с файлами
А какой прок от виртуалки, если нет возможности удобно работать с файлами на ней?
Можно подключить общий диск или смонтировать USB устройство. Но гораздо удобнее использовать Samba для коннекта между win и *nix.

Установим пакет

apt-get install samba

Теперь нужно произвести настройки в файле /etc/samba/smb.conf . Добавляем в конец файла строки

[my-projects]

comment = My projects

path = /data/

valid users = developer

create mask = 0775

force create mode = 0775

directory mask = 0775

writable = yes

force group = www-data

Теперь нужно создать пользователя samba командой

smbpasswd -a developer

указав при этом отдельный пароль для него.

Перезагружаем процессы samba

service smbd restart

service nmbd restart

Теперь уже в windows в адресной строке проводника можно вбить наш IP
Адресная строка

В результате мы увидим вот такую сетевую директорию
Сетевая папка

Кликнув по ней правой клавишей, мы сможем подключить её как сетевой диск и работать напрямую через любимый редактор файлов или IDE.

За сим всё!

С радостью отвечу на возникшие вопросы в комментариях!