Pull to refresh

Мой опыт настройки окружения для Web-разработки

Reading time10 min
Views21K
Речь пойдет не о настройке денвера и не о том, как поставить LAMP-стек. Я решил рассказать о том, какое мы в своей команде используем окружение для разработки. Мы разрабатываем Web-сервисы и ERP-системы, но всё это, в сущности, ничто иное, как сайты. Просто сложные внутри и порой не такие красивые снаружи.

Сразу хочу сказать, что я не претендую на описание идеального окружения для Web-разработки. С удовольствием послушаю критику, приглашаю всех поделиться своими подходами в комментариях. В общем, поехали.


Подождите, а чем плох денвер?


Основной недостаток денвера состоит в том, что проекты в production не работают на денвере. А значит мы не можем гарантировать, что тщательно отлаженные на компьютере разработчика скрипты не начнут «чудить», когда попадут в production. В production проекты обычно работают в Linux (в нашем случае это CentOS или Amazon Linux). Кроме того, работая на денвере, мы не сможем использовать различные утилиты и средства, которые нам нужны в проекте (например, catdoc, поиск sphinx и многое другое).

Ок, но ведь не все готовы работать в Linux!


Разрабатывать прямо в Linux, конечно круто, но правда жизни такова, что большинство разработчиков пользуются Windows в качестве основной OC на компьютере. Поэтому дальше я опишу наш рецепт, как работая в Windows, разрабатывать сайты в Linux.

Мы используем CentOS 7, но думаю, без существенных изменений все будет работать и на других дистрибутивах.

Создаем образ виртуальной машины


Ок. Первое, что нужно сделать — это поставить на компьютер гипервизор (VmWare Workstation, Oracle VirtualBox или может какой-то другой по вкусу). Мы используем VmWare. После этого создаем виртуальную машину и разворачиваем в ней тот образ Linux, на котором будут работать наши проекты в production. Устанавливаем Web-сервер, СУБД, в общем все, что нам нужно для запуска проекта. Кстати, если есть образ виртуалки для production, то еще лучше — можно его и взять за основу.

Виртуалку удобнее всего подключить через bridged-сетевой интерфейс. Так она будет полноценным участником сети и можно будет легко показывать результаты коллегам или заказчикам, если пробросить порт из внешнего мира.

Сетевая папка


Первый вопрос, который у нас встал на этом этапе. Нам теперь что, скрипты проекта через putty редактировать?! И очевидное решение, которое мы нашли, было удивительно простым. В Windows-машине нужно завести отдельную папку, в которой будут лежать все наши проекты. Эту папку нужно расшарить для доступа по сети и примонтировать в корневую директорию, с которой работает Web-сервер.

Чтобы все это работало надежнее, я написал скриптик cifs_mount.sh, буквально из 2 строчек кода, который поставил в виртуалке на запуск раз в 5 минут.
#!/bin/sh
if ! mount -t cifs | grep -q `cat /root/file_server`
then mount -t cifs -o uid=apache,gid=apache,iocharset=utf8,noserverino,credentials=/root/.cifscreds `cat /root/file_server` /var/www
fi

Скрипт проверяет, не отвалилась ли шара (простите мой французский). И если отвалилась, обратно ее монтирует.

Файл /root/file_server
//192.168.0.2/Projects

Файл /root/.cifscreds
username=developvm
password=secretpass


В целом, это решение работает надежно, как утюг.

Но тем не менее есть один небольшой нюанс.
Иногда бывает, что при копировании больших файлов, вылетает ошибка cifs. Как правило, это связано с тем, что в Windows не хватает выделенной памяти для шары. Вылеты происходят из-за того, что Win7 настроена по умолчанию на экономию памяти для сетевых подключений, в частности, размер выделяемого для этих целей пула памяти ограничен, и если для обработки файла требуется больше, то Win7 шлет Linux фатальную ошибку интерфейса и CIFS падает.

Также бывает наблюдаются периодические проблемы с неполной загрузкой страниц (не подгружаются CSS или вообще ошибки при попытке Apache прочитать файл). Причем это возникает время от времени без какой-либо системы.
При этом в логе ошибок Apache следующие ошибки:
[Tue Oct 20 10:44:28.417589 2015] [core:crit] [pid 9632] (5)Input/output error: [client 192.168.1.5:60666] AH00529: /var/www/project/.htaccess pcfg_openfile: unable to check htaccess file, ensure it is readable and that '/var/www/project/' is executable, referer: http://192.168.1.102/script.php
[Tue Oct 20 10:44:28.418762 2015] [core:error] [pid 9555] (5)Input/output error: [client 192.168.1.5:60670] AH00132: file permissions deny server access: /var/www/project/css/main/layout-main.css, referer: http://192.168.1.102/script.php


Чтобы ликвидировать все эти проблемы разом, нужно подредактировать реестр Win7, а именно:
1. У HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management\LargeSystemCache установить значение 1
2. У HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\lanmanserver\parameters\size установить значение 3

После этого перезапустить LanmanServer. “net stop srv”/“net start srv”. На виртуалке перемонтировать шару.


И еще один нюанс касается опции noserverino
В свое время он породил этот мой вопрос на stackoverflow. Если кратко, эта опция нужна. Подробнее можно почитать по ссылке.


Не стоит пугаться этих нюансов! Они накопились у нас почти за 10 лет разработки с использованием этого подхода.

Итак, что мы имеем. Теперь мы можем работать со скриптами в привычном нам редакторе кода в Windows. А результаты смотреть в браузере, заходя на нашу виртуальную машину. Идем дальше.

Подпапки для проектов.


А что если у нас больше 1 проекта. Каждый раз поднимать новую виртуалку!? Нам на помощь приходят виртуальные хосты. Придется опять написать небольшой скриптик flush_vhosts.sh (сугубо для удобства работы).
#!/bin/sh

rm /etc/httpd/conf.d/vhosts/*

rm /etc/hosts
echo '127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4' >> /etc/hosts
echo '::1         localhost localhost.localdomain localhost6 localhost6.localdomain6' >> /etc/hosts

for D in `find /var/www -maxdepth 1 -mindepth 1 -type d -printf '%f\n'`
do
    echo '<VirtualHost *:80>' >> /etc/httpd/conf.d/vhosts/$D.conf
    echo "ServerName $D.wde" >> /etc/httpd/conf.d/vhosts/$D.conf
    echo "ServerAlias *.$D.wde" >> /etc/httpd/conf.d/vhosts/$D.conf
    echo "DocumentRoot /var/www/$D" >> /etc/httpd/conf.d/vhosts/$D.conf
    if [[ $D == *"bitrix"* ]]
    then
      echo 'php_admin_value mbstring.func_overload 2'  >> /etc/httpd/conf.d/vhosts/$D.conf
      echo 'php_admin_value mbstring.internal_encoding UTF-8'  >> /etc/httpd/conf.d/vhosts/$D.conf
      echo 'php_admin_value max_input_vars 10001'  >> /etc/httpd/conf.d/vhosts/$D.conf
      echo 'php_admin_value pcre.recursion_limit 1000'  >> /etc/httpd/conf.d/vhosts/$D.conf
    fi
    echo "</VirtualHost>" >> /etc/httpd/conf.d/vhosts/$D.conf
    echo "127.0.0.1   $D.wde" >> /etc/hosts
done

systemctl restart httpd.service

Вот что он делает:
1. Очищает конфигурацию виртуальных хостов.
2. Очищает /etc/hosts.
3. Дальше он проходится по всем подкаталогам нашей примонтированной папки и создает для каждого каталога новый виртуальный хост Apache. Если в названии каталога находится страшное слово bitrix, то он добавляет в конфиг виртуального хоста несколько специфических настроек для этой замечательной CMS. Добавляет в /etc/hosts новые записи для созданных виртуальных хостов.
4. Перезапускает Apache.

Мы использует в адресах проектов для разработки условный домен первого уровня wde (web developer environment). Можно использовать local или кому что нравится. У нас разрабатываемые проекты доступны по адресам project1.wde, project2.wde и так далее. При этом виртуальные хосты создаются с директивой ServerAlias *.your-folder-name.wde, то есть все поддомены будут также обрабатываться созданным для папки виртуальным хостом.

Таким образом, если нам нужно начать работу над новым проектом, достаточно создать новую папку в общей папке проектов. Выполнить скрипт flush_vhosts.sh. А в Windows прописать адрес для нового проекта в файле C:\Windows\System32\drivers\etc\host.
192.168.1.166 wde
192.168.1.166 site1.wde
192.168.1.166 site2.wde
...и т.д.

Звездочки файл hosts к великому сожалению, не поддерживает. Надо прописывать каждый адрес, с которым будем работать. Вместо 192.168.1.166 нужно указать ip, который вы присвоили виртуальной машине. После этого соответствующий проект будет открываться в браузере по адресам site1.wde или site2.wde и так далее.

Для удобства, если вы пользуетесь, phpMyAdmin, его можно настроить дефолтным хостом. Тогда при обращении по любому адресу, когда Apache не будет находить соответствующую папку проекта, он будет открывать phpMyAdmin. Главное не забыть прописать этот адрес (например, просто wde) в файле hosts в Windows.

Организация загрузочного меню

Чтобы было совсем удобно. И не приходилось новым членам команды объяснять, как обновить конфигурацию виртуальных хостов, настроить сетевую папку и т.д. Я написал еще несколько скриптов для вывода и запуска частых действий через меню. В них нет ничего сверхестественного, прикладываю — может быть кому-то тоже пригодятся.

Собственно скрипт, выводящий меню. Его нужно прописать в файле .bash_profile домашней директории пользователя, под которым мы входим на виртуалку. Для виртуалки, на которой ведется разработка, я считаю можно входить под root, соответственно добавляем в файл /root/.bash_profile строчку ./menu.sh. Теперь сразу после входа в систему будет запускаться наше меню. При необходимости из него всего можно выйти, нажав Ctrl+C.

menu.sh
#!/bin/sh
SCRIPT_DIR=`dirname $0`
source $SCRIPT_DIR/utils.sh

#menu actions
act_net () {
  nmtui ;
  }

act_folder () {
  $SCRIPT_DIR/mount_cfg.sh ;
  }

act_flushvhosts () {
  $SCRIPT_DIR/flush_vhosts.sh ;
  }

act_reboot () {
  read -p "System is going to reboot, are u sure? (y/N) " key ;
  if [ $key = "y" ]; then
    systemctl reboot ;
    exit
  fi
  key=
  }

act_shutdown () {
  read -p "System is going down, are u sure? (y/N) " key ;
  if [ $key = "y" ]; then
    systemctl halt ;
    exit
  fi
  key=
  }

themenu () {
  clear
  server_uptime
  mnt_detect
  echo "===================================================================="
  echo "======================= WELCOME to CENTOS WDE!!! ==================="
  echo "===================================================================="
  echo "======================== wish you happy coding ====================="
  echo "===================================================================="
  echo -e "System time: "$curtime"\tUptime:"$uptime;
echo ;
  echo -e "Mounted folder: "$MNT;
echo ;
  echo "=========================== network info ==========================="
  echo "`ifconfig -a`"
echo ;
  echo `grep nameserver /etc/resolv.conf`
echo ;
  echo "`route -n`"
echo ;
  echo "====================== current vhosts configs ======================"
  echo "`ls -1 /etc/httpd/conf.d/vhosts/`"
echo ;
  echo "===================================================================="
  echo "========================= Available actions: ======================="
  echo -e "\t\tConfigure ${FG_UN}net${NORM}"
  echo -e "\t\tConfigure mounted ${FG_UN}folder${NORM}";
  echo -e "\t\t${FG_UN}Flush${NORM} virtual hosts";
  echo -e "\t\t${FG_UN}Reboot${NORM}";
  echo -e "\t\t${FG_UN}Shutdown${NORM}";

  echo
  echo "Type underlined chars(lowercase) and press ENTER or just ENTER to refresh";
  echo "Type Ctrl+C to exit to shell";
  echo "====================================================================";
}

while true
 do
  themenu
  read answer
  case $answer in
        "net")  act_net;;
        "folder")  act_folder;;
        "flush")  act_flushvhosts;;
        "reboot")  act_reboot;;
        "shutdown")  act_shutdown;;
        *)  echo 'No action found! Refreshing...'; sleep 1; continue;;
   esac
done


utils.sh - содержит функции и переменные, используемые в других скриптах
#!/bin/sh
set -o pipefail
mnt_dir="/var/www"

if [ "$interactive" != 'no' ]; then
  #cursor movements
  CU_RIGHT=$(tput hpa $(tput cols))$(tput cub 7)
  #background colors
  BG_BLACK=$(tput setab 1)
  BG_RED=$(tput setab 1)
  BG_GREEN=$(tput setab 2)
  BG_YELLOW=$(tput setab 3)
  BG_BLUE=$(tput setab 4)
  BG_PURPLE=$(tput setab 5)
  BG_CYAN=$(tput setab 6)
  BG_WHITE=$(tput setab 7)
  #foreground colors
  FG_RED=$(tput setaf 1)
  FG_GREEN=$(tput setaf 2)
  FG_YELLOW=$(tput setaf 3)
  FG_BLUE=$(tput setaf 4)
  FG_PURPLE=$(tput setaf 5)
  FG_CYAN=$(tput setaf 6)
  FG_WHITE=$(tput setaf 7)
  #text-decoration
  FG_BOLD=$(tput bold)
  FG_HB=$(tput dim)
  FG_UN=$(tput smul)
  FG_REVERSE=$(tput rev)
  #back to defaults
  NORM=$(tput sgr0)
  fi

#functions to display progress
dots () {
  if [ "$interactive" != 'no' ]; then
    while true; do
        echo -n "."; sleep 0.5
      done
    fi
}
estart(){
  if [ "$interactive" != 'no' ]; then
    echo -n "$1"
    dots &
    dots_pid=$!
    fi
}
efinish(){
  estatus=$?
  if [ "$interactive" != 'no' ]; then
    if [ "$estatus" -eq 0 ];then
      echo "[ ${FG_GREEN}OK${NORM} ]"
    else
      echo "[ ${FG_RED}FAIL${NORM} ]"
    fi
    kill $dots_pid
    wait $dots_pid 2>/dev/null
    fi
}


#detect server uptime
server_uptime () {
uptime=$(</proc/uptime)
uptime=${uptime%%.*}
s=$(( uptime%60 ))
m=$(( uptime/60%60 ))
h=$(( uptime/60/60%24 ))
d=$(( uptime/60/60/24 ))
uptime=$d'd '$h'h '$m'm '$s's '
curtime=$(date +'%Y-%m-%d %H:%M:%S')
}

#detect cifs mount
mnt_detect () {
  MNT=$(mount -l | grep $mnt_dir)
  if [ ! -z "$MNT" ]; then
    MNT=$FG_GREEN$MNT$NORM
  else
    MNT=$FG_RED"error(not found)"$NORM
  fi
}


mount_cfg.sh - настройка параметров сетевой папки
#!/bin/sh
SCRIPT_DIR=`dirname $0`
source $SCRIPT_DIR/utils.sh

clear
echo "========================================="
echo "  Mounted folder configuration"
echo "    (/var/www)"
echo "========================================="
echo

old_address=$(cat /root/file_server)
old_username=$(grep 'username=' /root/.cifscreds | awk -F '=' '{ print $2 }')
old_password=$(grep 'password=' /root/.cifscreds | awk -F '=' '{ print $2 }')

echo "Type new value and press ENTER or just press ENTER to leave current value.";
echo ;
read -p "Address of fileserver, type like //ip/folder (current value $FG_YELLOW$old_address$NORM): " address ;
read -p "Username (current value $FG_YELLOW$old_username$NORM): " username ;
read -p "Password (current value $FG_YELLOW$old_password$NORM): " password ;

if [ -z "$address" ]; then
  address=$old_address; fi
if [ -z "$username" ]; then
  username=$old_username; fi
if [ -z "$password" ]; then
  password=$old_password; fi

echo "======================================="
echo "  New parameters"
echo "======================================="
echo -e "IP address of fileserver: "$address
echo -e "Username: "$username
echo -e "Password: "$password
echo "======================================="
echo
read -p "Save changes? (y/N) " key ;

if [ $key == "Y" -o $key == "y" ]; then
  echo "username=$username
  password=$password" > /root/.cifscreds
  echo "$address" > /root/file_server
  estart "Unmounting..."
  umount /var/www
  efinish
  estart "Mounting..."
  /root/cifs_mount.sh
  efinish
  echo ;
  read -p "Done. Press any key" key ;
else
  echo ;
  read -p "Nothing was changed. Press any key" key ;
fi



Система контроля версий


Дальше остается настроить любимую систему контроля версий. Можно пользоваться desktop-приложением для Windows, можно работать через командную строку в putty прямо на нашей виртуальной машине. Кому, как удобнее.

PS. Кстати, одно из величайших открытий для меня было, что можно поставить git или svn непосредственно на production сервере. Когда я в свое время это понял, это ощущение было сравнимо, наверное, с тем чувством, которое испытал Архимед, когда сел в ванную. Ведь больше не надо мучиться с синхронизацией файлов по ftp\sftp, достаточно ввести svn up или git pull origin master!
Tags:
Hubs:
-8
Comments86

Articles