Привет, дорогой читатель!
Сегодня я хочу поделиться своим опытом настройки персонального окружения для работы с различными PHP-based проектами. В данной статье описывается опыт ручной настройки окружения.
Данная статья написана
Автоматизацию процесса развертывания среды я напишу в одной из следующих статей.
Статья будет очень длинной с уклоном в техническую сторону. Прошу под «кат».
Предыстория
Прежде чем начать, хочу поделиться небольшой историей о том, как все начиналось.
Долгое время, процесс разработки велся в основном на одном проекте, с упором на долгосрочные перспективы (работы было много: на 1 год вперед). И так было от компании к компании. Так что мне не составляло особого труда добавить пару «конфигов» и перезагрузить сервисы на локальной машине, чтобы добавить новый проект. При смене компании я просто настраивал все с нуля подстраиваясь под проект.
В конце 2016 года мне пришлось перейти с одного проекта на другой в связи с чередой очень неприятных событий. Перейдя на новый проект столкнулся с тем, что архитектура проекта была построена так, что нужно одновременно поддерживать минимум 5 web-сервисов, работающих в связке. Так же мне все еще приходится время от времени поддерживать предыдущие проекты. Да и, не забывать об R&D проектах, Open Source проектах, “халтурке”, семье и детях.
Такое стечение обстоятельств повлияло на дальнейшую историю моей среды разработки и все что с этим связано.
Цель: гибкая среда разработки для PHP-based проектов
Мне нужно получить рабочее окружение, позволяющее вести разработку, отладку и исследования во множественных PHP-based проектах с привязкой к инфраструктуре компании.
Примечание: дальнейшее описание относится к двум проектам, которые сейчас находятся в моей зоне ответственности: Warface Hub — то, что от него осталось, и CryEngine Hub — результат молниеносной реализации, “удачной” архитектуры и “правильного” планирования.
Таким образом я получил следующее
- Windows 10 — Host с VMWare или VitrualBox
- Ubuntu — Guest c:
- Samba — для работы с файлами проектов
- Bind9 — локальный DNS сервер
- Nginx, PHP FPM, MySQL и все остальное
- Другие виртуальные машины
Ресурсы
В крупных компаниях очень часто используют решения от компании Microsoft для обеспечения работы внутренней связи между сотрудниками (Outlook, Lync, AD) и еще кучи «самописных» приложений. Так как лень мне не позволяет искать и настраивать клиенты под Linux или использовать Wine, то я успешно начал использовать “гибрид” двух OS и виртуализацию:
- Windows 10 (Host OS)
- Ubuntu 16.04 (Guest OS — VM)
- SSD — максимально быстрый. При данной реализации очень важно иметь быстрый диск.
- 16Gb ram — Чем больше, тем лучше. Мне приходится выделять до 12Gb для всех гостевых OS, так как приходится решать cross-browser проблемы, которые на эмуляторах не воспроизводятся.
Установка сервисов, приложений и компонентов.
На этом этапе устанавливаем все (Guest OS), что может нам понадобится для того чтобы поднять простой стек из Nginx, PHP и MySQL, а также все остальное, что может пригодится в проектах (NPM, Yarn, Composer). Не забываем про нашу среду разработки и добавляем туда Samba и Bind9.
$ sudo add-apt-repository ppa:ondrej/php
$ sudo apt-get install php5.6 php7.0 php7.1 php7.1-fpm
$ sudo apt-get install php-bcmath php-curl php-dev php-fpm php-gd php-intl php-json php-mbstring php-mcrypt php-mysql php-readline php-soap php-sqlite3 php-tidy php-xml php-zip php-codecoverage php-codesniffer php-common php-geoip php-igbinary php-imagick php-memcache php-memcached php-redis php-ssh2 php-xdebug php-xhrpof
$ sudo apt-get install php5.6-bcmath php5.6-curl php5.6-dev php5.6-fpm php5.6-gd php5.6-intl php5.6-json php5.6-mbstring php5.6-mcrypt php5.6-mysql php5.6-opcache php5.6-readline php5.6-soap php5.6-sqlite3 php5.6-tidy php5.6-xml php5.6-zip
$ sudo apt-get install php7.0-bcmath php7.0-curl php7.0-dev php7.0-fpm php7.0-gd php7.0-intl php7.0-json php7.0-mbstring php7.0-mcrypt php7.0-mysql php7.0-opcache php7.0-readline php7.0-soap php7.0-sqlite3 php7.0-tidy php7.0-xml php7.0-zip
$ sudo apt-get install php7.1-opcache
$ sudo apt-get install memcached redis-server redis-tools nginx mysql-server-5.7 mysql-client-5.7 composer npm curl nodejs nodejs-legacy ruby-full
$ curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
$ echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
$ sudo apt-get update && sudo apt-get install yarn
$ sudo gem update --system
$ sudo gem install compass
$ composer global require hirak/prestissimo
$ sudo npm install -g grunt-cli
$ sudo apt-get install bind9 samba
Samba-сервер
Первое, к чему я потянулся — Samba-сервер, который настроил для того, чтобы моя IDE могла работать непосредственно с файловой системой гостевой OS.
Все мои проекты будут находится по пути /var/www/{project-name}.
Так же в процессе настройки гостевой ОС я принудительно настроил DHCP сервер так, чтобы у нее был статический IP и пряталось все за NAT: 172.16.126.130.
[global]
workgroup = WORKGROUP
server string = %h server (Samba, Ubuntu)
dns proxy = no
# few changes to speed up smbd
strict allocate = Yes
read raw = Yes
write raw = Yes
strict locking = No
socket options = TCP_NODELAY IPTOS_LOWDELAY SO_RCVBUF=131072 SO_SNDBUF=131072
min receivefile size = 16384
use sendfile = true
aio read size = 16384
aio write size = 16384
max log size = 1000
syslog only = no
syslog = 0
server role = standalone server
usershare allow guests = yes
[www]
path = /var/www
available = yes
valid users = oxcom
read only = no
browsable = yes
public = yes
writable = yes
executable = yes
Чтобы IDE могла работать с этим ресурсом я монтирую эту Netbios папку как удаленный локальный диск.
@echo off
@title Local UBUNTU Network Drive
net use q: \\172.16.126.130\www {user-password} /USER:oxcom
Unmount local drive
@echo off
@title Local UBUNTU Network Drive
net use q: /delete
Почему я стал использовать Samba, а не Shared Folders?
На то время, когда я этим вопросом задался получалось так, что Shared Folders являлись монтируемой директорией, а NGINX не хотел их использовать как root директорию проекта. Второй причиной стало то, что время от времени у меня Shared Folders отваливались без видимой на то причины и без возможности быстро все восстановить. А также нет возможности работать с симлинками, которые необходимы для некоторых пакетов npm и не только.
Есть ли какие-то проблемы с IDE?
Да и разные, но SSD спасает:
- иногда индексирование проектов происходит долго
- при быстром изменении большого количества файлов (merge, rebase, смена бранча) не сразу подхватываются изменения. Смотрю в сторону rsync.
- Если отключить диск раньше, чем закрыть IDE, то, при закрытии проекта, IDE зависнет
- Если отвалится сеть, то проект скорее всего нужно будет открыть еще раз
DNS-сервер
Дорогой читатель может представить насколько трудно в большой компании запросить небольшие изменения в настройках сети. И даже если вы успешно пройдете первый этап объяснения для чего вам это нужно, то второй этап (реализация) может затянуться на очень большой срок, что проще все сделать самому.
Так как я очень ленивый, то через какое-то время мне надоело добавлять в hosts файл записи вида:
172.16.126.130 project1.lo
172.16.126.130 project2.lo
Было решено, что нужен локальный DNS-сервер, который можно настроить быстрее, чем я смогу выпить бутылку немецкого крепкого пива. После чего был послан запрос в лигу лени. Результат не заставил себя долго ждать: BIND9
Настройка очень проста: описываем конфигурации прямого и обратного пула доменов, а также не забываем их подключить:
;
; BIND data file for local loopback interface
;
$TTL 604800
@ IN SOA lo. root.lo. (
0000119 ; Serial
604800 ; Refresh
86400 ; Retry
2419200 ; Expire
604800 ) ; Negative Cache TTL
;
@ IN NS lo.
@ IN A 172.16.126.130
@ IN AAAA ::1
* IN CNAME @
;
; BIND data file for local loopback interface
;
$TTL 604800
;
; BIND reverse data file for local loopback interface
;
$TTL 604800
@ IN SOA ns.lo. root.lo. (
0000112 ; Serial
604800 ; Refresh
86400 ; Retry
2419200 ; Expire
604800 ) ; Negative Cache TTL
;
@ IN NS lo.
zone "lo" {
type master;
file "/etc/bind/db.lo";
check-names ignore;
allow-query { any; };
allow-transfer { any; };
};
zone "126.16.172.in-addr.arpa" {
type master;
file "/etc/bind/db.126.16.172";
allow-query { any; };
allow-transfer { any; };
};
options {
directory "/var/cache/bind";
forwarders {
8.8.8.8;
8.8.4.4;
};
recursion yes;
listen-on { any; };
auth-nxdomain no; # conform to RFC1035
listen-on-v6 { any; };
};
Сервер настроен. Теперь, для полной радости осталось решить еще несколько задач:
- заставить Host систему использовать наш DNS-сервер для домена .lo. Поэтому меняем на Host машине параметры виртуальной сети так, чтобы первым был наш DNS-сервер, и чистим DNS-кеш.
- заставить Guest систему тоже использовать наш DNS-сервер. Тут проще: редактируем сетевой интерфейс и прописываем туда наш DNS-сервер
Результат не заставляет себя ждать:
; <<>> DiG 9.10.3-P4-Ubuntu <<>> example.lo
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 32204
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 1, ADDITIONAL: 2
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;example.lo. IN A
;; ANSWER SECTION:
example.lo. 604800 IN CNAME lo.
lo. 604800 IN A 172.16.171.130
;; AUTHORITY SECTION:
lo. 604800 IN NS lo.
;; ADDITIONAL SECTION:
lo. 604800 IN AAAA ::1
;; Query time: 0 msec
;; SERVER: 172.16.171.130#53(172.16.171.130)
;; WHEN: Mon Jul 17 11:09:52 CEST 2017
;; MSG SIZE rcvd: 111
Pinging lo [172.16.171.130] with 32 bytes of data:
Reply from 172.16.171.130: bytes=32 time<1ms TTL=64
Reply from 172.16.171.130: bytes=32 time<1ms TTL=64
Вот только «nslookup example.lo» не работает. Почему — не знаю, но любой браузер на Host машине будет нормально ходить на соответствующий сервер.
PHP-FPM и Pools
Вроде все работает. Теперь необходимо приступить к настройке PHP для каждого из проектов.
Работая над последними проектами у меня получилось так, что один проект может состоять из различных сервисов или собирать в себе многие другие проекты. По-этому у меня получилась следующая структура:
- warface.com
- cryengine.com
- shop.cryengine.com
- forum.cryengine.com
В процессе работы приходится использовать API вызовы между проектами и было бы очень хорошо уметь «дебажить» это все с помощью XDebug и любимой IDE. При попытках использовать один PHP Pool я сталкивался со следующими проблемами:
- при отладке на одном домене с общей сессией PHP-FPM блокирует исполнение остальных запросов с такой же сессий (как смог так и описал :)). Таким образом останавливаясь где-то на cryengine.com я не смогу работать с forum.cryengine.com, пока не завершится предыдущий запрос.
- неудобно проводить отладку двух проектов одновременно с разными ключами XDebug. Да-да, я стараюсь максимально избегать изменения настроек среды из PHP
Чтобы это решить, я создал свой PHP Pool по шаблону для каждого проекта под каждую версию PHP. И подключаю их 'include=/etc/php/7.0/fpm/pool.d/*.d/*.conf'
; Start a new pool
; the variable $pool can we used in any directive and will be replaced by the
; pool name ('www' here)
[{project-name}-{project-subname}]
user = www-data
group = www-data
listen = /run/php/php7.0-$pool-fpm.sock
listen.owner = www-data
listen.group = www-data
pm = dynamic
pm.max_children = 20
pm.start_servers = 5
pm.min_spare_servers = 3
pm.max_spare_servers = 7
php_admin_value[xdebug.idekey] = key-$pool
Выглядит это примерно так:
cryengine.d/www.conf
cryengine.d/shop.conf
cryengine.d/forum.conf
warface.d/www.conf
www.conf
php5.6-cryengine-www-fpm.sock
php5.6-cryengine-shop-fpm.sock
php5.6-cryengine-forum-fpm.sock
php5.6-warface-www-fpm.sock
php5.6-fpm.sock
php7.0-cryengine-www-fpm.sock
php7.0-cryengine-shop-fpm.sock
php7.0-cryengine-forum-fpm.sock
php7.0-warface-www-fpm.sock
php7.0-fpm.sock
Теперь каждый проект имеет свой PHP Pool и проставленным xdebug.idekey, что позволяет проводить непрерывную отладку между несколькими проектами (не забывайте настроить php.ini)
NGINX
Настройка сервера довольно проста. Для каждого проекта создается отдельный файл конфигурации в 'sites-available', который описывает конфигурацию vhost. В конфигурации мы подключаем соответствующие файлы конфигурации для каждого проекта.
Основываясь на указанной выше архитектуре я реализовал вот такую структуру:
cryengine.d/
- www.conf
- forum.conf
- shop.conf
- ssl.conf
warface.d/
- www.conf
- ssl.conf
sites-available/
- www.cryengine.conf
- forum.cryengine.conf
- shop.cryengine.conf
- www.warface.conf
nginx.conf
server {
listen 443 ssl http2;
server_name {project-subname}.{project-name}.lo;
root /var/www/{project-subname}.{project-name}.com/wwwroot;
index index.php;
error_log /var/www/{project-subname}.{project-name}.com/log/nginx.error.log error;
access_log /var/www/{project-subname}.{project-name}.com/log/nginx.access.log;
# configuration related to sub-project
include {project-name}.d/www.conf;
# global configuration for projects
include {project-name}.d/ssl.conf;
}
Таким образом в /etc/nginx/{project-name}.d/ хранятся все конфигурации, которые относятся к проекту, а также сопутствующим под проектам. Каждый проект имеет свой PHP Pool пока каждую версию PHP, что позволяет быстро переключаться между версиями и только для одного проекта.
Итоги
Спасибо, что дочитали до конца. Я искренне надеюсь, что ручной труд еще не вымер, ибо разработчик должен уметь настроить среду сам и без автоматизации.
В итоге я получил гибрид между двумя ОС, который позволяет полностью использовать все внутренние сервисы компании, а также вести разработку и отладку в среде, максимально приближенной к LIVE.
Я понимаю, что это не идеальное решение, но это один из способов, как можно за короткий промежуток времени подготовить среду разработки.
Минусы:
- Долгая индексация в IDE
- При переустановке/переносе виртуальной машины нужно изменять конфигурацию для Bind9 и скрипт подключения диска
- Это все не автоматизировано
Плюсы:
- рабочую среду можно переносить с машина на машину
- разработчик остается в Windows окружении и есть возможность использовать MS сервисы, которые установлены в компании.
- разработчик работает с Linux окружением, которое может быть легко настроено как LIVE среда.
Комментарии (11)
smple
17.07.2017 15:32используйте vagrant например для подобных историй.
настройку машины можно сложить в provision
OxCom
17.07.2017 15:48smple
Спасибо за совет! Планирую все это завернуть в Vagrant + Chef, и возможно что-то завернуть Docker.smple
17.07.2017 19:10docker прикольная вещь и она довольно быстро стартует, по сравнению со связкой vagrant + virtualbox только там всякие забавы идут с windows и mac и есть подход в котором докер крутится внутри виртуалки.
Поэтому все же проще vagrant + virtualbox
У него есть минусы:
- Потребляет лишнюю память и cpu (в среднем 1gb ram и проц)
- Разворачивается не быстро (в среднем 1 минута).
- На больших проектах синхронизация файлов по умолчанию тормозит (обычно используют rsync или nfs что выливается в пляски с windows хорошо если лицензия корпоративная или проффесиональная)
Но есть и огромное количество плюсов
по поводу provision, не обязательно использовать что то типо chef, ansible или puppet, можно просто использовать bash script в простых случаях как вы описали выше.
Вообщем если есть на это спрос могу написать конечно статью на хабр на эту тему как я разворачиваю окружение ну и конфиг соответственный.
OxCom
18.07.2017 10:14Мне будет очень интересно посмотреть, как это реализовали другие люди, так как от DevOps я относительно далеко, но уж очень интересно посмотреть.
smple
18.07.2017 19:40дело в том что я не devops а обычный разработчик, просто люблю комфортную работу и хорошо умею настрайвать всякие штуки.
artarn
20.07.2017 14:24я например использую docker так:
1) в системе крутится DNS docker контейнер tonistiigi/dnsdock
2) через docker-compose.yml описываю проект
например
version: '2' services: project_db: image: mysql:{версия} .... environment: DNSDOCK_NAME: project DNSDOCK_IMAGE: db project_php: build: ./php{версия} .... project_nginx: depends_on: - project_php image: nginx .... environment: DNSDOCK_NAME: project DNSDOCK_IMAGE: site
3) кастую docker-compose up -d
получаю проект mysql + php-fpm + nginx
в итоге на хост машине
mysql доступен по адресу project.db.docker например для workbench
nginx доступен по адресу http://project.site.docker
могу остановить в любой момент, поднять несколько разных проектов с разными версиями БД и php.
на самой хост машине mysql снес, php только версии 7.1
65536
26.07.2017 19:33Про Shared Folders хочу добавить — адски тормозят на hdd. То что должно происходить 30 мс может длиться 2.5 сек, зависит от количества операций с файлами. Года полтора-два промучился, выяснилось что это из-за них, проблема известна и нерешаема. Решил уйти от этой схемы и первым делом попробовал через самбу примонтировать. PhpStorm впадает в практически бесконечную переиндексацию. Переключение ветки может всё повесить на несколько минут, поиск по всем файлам невозможен, хоть на ночь оставляй. Да и просто тормозит при навигации по проекту, выдаче подсказок и т. д. Начал искать еще варианты и нашел вот это: https://habrahabr.ru/post/139348/. Очень хорошо работает, за время перехода в браузер и нажатие ф5 на сервере уже будет всё как надо. Единственное неудобство это то, что синхронизация односторонняя и сгенерированные на сервере файлы приходится скачивать вручную.
OxCom
26.07.2017 22:34Интересный подход. Несколько раз смотрел в эту сторону, но нет. Мне действительно нужна двусторонняя синхронизация. Одна из причин — как ни крути, но в unix консоль более удобна и приятная в использовании чем cmd и windows решение для GIT.
Еще я рассматривал монтирование с использованием SSHFS for Win, но PHPStorm постоянно засыпал меня уведомления, что GIT «упал» и все. Попытки решить проблему не увенчались успехом. Возможно нужно ждать обновлений.OxCom
26.07.2017 23:06Решение проблемы нашел сегодня:
неправильный права доступа$ find <somerepo>/.git -ls | grep 'r--r--r--' $ chmod 0644 {file-path}
script88
Какой смысл использовать ВМ, в то время как в самой винде есть ubuntu bash на которой можно развернуть все тоже самое?!
OxCom
Так уж случилось, что у меня еще есть Win7, в которой этого нет, но я работаю в ней с тем же подходом.
Я сомневаюсь, что до текущего момента все компании поголовно перешли на Win10.