fewit.ru :: Заметки недоайтишника

Ещё один криворукий админ

Задача — на минимальных ресурсах VPS развернуть хостинг нескольких не нагруженных сайтов. Сделать это быстро и удобно с минимальными проблемами в будущем и не падать на пиковых нагрузках.

Основные принципы:

1. ОС — Centos-6 86_x64 потому что стабильно, удобно и легко обновляемо.
2. Никакого самосборного софта. А то как говорится «командой make && make install любой дистрибутив превращается в Slackware.»

Маленькое уточнение, на данный момент я использую тарифный план v256 у хостинг провайдера flynet.pro (256Мб оперативки) и не рассчитываю на большую нагрузку так что большая часть относится именно к такому количеству оперативной памяти, но в целом решения легко переносимы фактически на все тарифные планы разных хостинг-провайдеров.
И еще одно уточнение — хостинг делается «для себя». Тут недостаточно описаны моменты, которые стоит учитывать, если вы даете доступ к администрированию сайтов посторонним людям.

Поехали.

1. Проверяем обновления.
Установочный образ у хостинг провайдера может оказаться не особо свежим.
[root@test ~]# yum update

Есть что обновлять — обновляем. Нет — радуемся.

2. Подключаем репозитарий EPEL (http://fedoraproject.org/wiki/EPEL) из которого будем ставить недостающий софт.
[root@test ~]# rpm -ihv download.fedora.redhat.com/pub/epel/6/x86_64/epel-release-6-5.noarch.rpm

3. Ставим нужный нам софт
[root@test ~]# yum install httpd mysql-server php vsftpd mc phpMyAdmin php-eaccelerator sysstat crontabs tmpwatch

Кратко о софте:
httpd — Apache штатная версия для Centos-6 — 2.2.15
mysql-server — Mysql 5.1.52
php — PHP 5.3.2
vsftpd — довольно удобный FTP сервер vsftpd 2.2.2
mc — некоторые вещи все-таки удобнее делать в mc чем в командной строке.
phpMyAdmin — аналогично с mc. управлять mysql базами в phpMyAdmin всетаки удобнее.
php-eaccelerator — акселератор для PHP. Заметно увеличивает скорость выполнения скриптов и снижает нагрузку на процессор. Да и на память.
sysstat — на случай если нам захочется посмотреть как поживает система.
crontabs — для выполнения заданий по расписанию.
tmpwatch — утилита для удаления устаревших файлов.

На самом деле установится несколько больше пакетов, к тем пакетам что мы попросили установить добавится все необходимое для их функционирования.
В результате получается:
Install 44 Package(s)
Upgrade 0 Package(s)

Total download size: 37 M
Installed size: 118 M

4. Командой free смотрим, есть ли у нас своп и если нет, то создаем его и подключаем. Если есть – радуемся и пропускаем этот пункт.
Тут важный момент — активное использование свопа — очень плохо. Если идет активный свопинг — значит нужно что-то оптимизировать или урезать. Если оптимизировать и урезать не получается — придется переходить на более дорогой тарифный план. Еще стоит учитывать что хостинг-провайдер может обидиться на слишком активное использование свопа.
Но совсем без свопа тоже не очень хорошо — oom killer — штука страшная. Может ненароком убить mysqld и вместо того чтобы просто тормозить ваши сайты будут совсем лежать.
Замечание — делать своп больше имеющейся оперативной памяти не нужно. Пользы от него не будет, а место он скушает.

Создаем своп следующим образом:
[root@test /]# dd if=/dev/zero of=/swap bs=1M count=256
[root@test /]# mkswap /swap

подключаем
[root@test /]# swapon /swap

ну и для того чтобы оно подключалось автоматически записываем эту команду в /etc/rc.local
Проверить наличие и занятость свопа можно командами top или free

5. Включаем и запускаем демонов
[root@test /]# chkconfig httpd on
[root@test /]# chkconfig mysqld on
[root@test /]# chkconfig crond on

[root@test /]# service httpd restart
[root@test /]# service mysqld restart
[root@test /]# service crond restart

6. Создаем пользователей для сайтов. Я предпочитаю чтобы имя пользователя было аналогично домену сайта.
[root@test /]# adduser testsite.ru
[root@test /]# adduser mysite.ru
[root@test /]# adduser cfg.testsite.ru

Далее создаем дополнительные в каталогах пользователей. html (в котором будет основное содержимое сайтов) и log в которую будут писаться логи для этого сайта и выставляем права. Права ставим: пользователю – полный доступ, группе apache чтение и листинг директорий, остальным – фикус.
Права можно выставить и руками, а можно и воспользоваться небольшим скриптиком:
cd /home
for dir in `ls -1 `; do
mkdir /home/$dir/log
mkdir /home/$dir/html
chown -R $dir:apache $dir
chmod ug+rX $dir
done;

7. Настраиваем веб сервер. Правим /etc/httpd/conf/httpd.conf
Из действительно нуждающегося в изменении — настраиваем модуль prefork так чтобы он изначально кушал поменьше памяти и ограничивал свои аппетиты.
Дело в том что Apache изначально настроен на запуск до 256 своих рабочих процессов, при том что один рабочий процесс запросто занимает 20-40Мб (256*20=5Гб) это запросто может привести к проблемам, особенно на скромных VPS где есть всего 256Мб оперативки.
Поэтому мы ограничиваем их количество разумными цифрами изсходя из доступной нам оперативной памяти. Например 5 процессов апача при среднем размере 30Мб займут около 150Мб — что уже терпимо.
Было:
<IfModule prefork.c>
StartServers 8
MinSpareServers 5
MaxSpareServers 20
ServerLimit 256
MaxClients 256
MaxRequestsPerChild 4000

Стало:
<IfModule prefork.c>
StartServers 2
MinSpareServers 2
MaxSpareServers 3
ServerLimit 5
MaxClients 5
MaxRequestsPerChild 1000

Такая настройка не даст апачу расплодиться сверх меры и скушать всю оперативку. В зависимости от реальной нагрузки параметры, возможно, стоит пересмотреть.
Ну и раскомментируем строку
NameVirtualHost *:80

Для того чтобы на одном ip адресе иметь много сайтов.

Далее переходим в директорию /etc/httpd/conf.d/ и настраиваем наши сайты.
Там можно удалить welcome.conf который выключает индексы и выдает вместо нее страничку «Apache 2 Test Page».
Следует учесть, что конфиги виртуальных хостов в этой директории применяются по очереди в алфавитном порядке.
Для того чтобы пользователь зайдя по IP адресу на какой либо из наших сайтов не попал на совершенно другой (который будет первым по списку) в директорию conf.d стоит положить файл с именем например 000-default.conf и таким содержимым:
<VirtualHost *:80>
ServerName localhost.local
DocumentRoot "/var/www/html"

и положить в директорию /var/www/html/ файлик index.html с пожеланиями.

Далее для каждого из наших виртуальных хостов создаем конфиг файл по примерно такому шаблону:
<VirtualHost *:80>

ServerName testsite.ru
ServerAlias www.testsite.ru
ServerAdmin webmaster@testsite.ru
ErrorLog /home/testsite.ru/log/error.log
CustomLog /home/testsite.ru/log/access.log combined
DocumentRoot /home/testsite.ru/html/

<Directory "/home/testsite.ru/html">
Order allow,deny
Allow from all

В эти же файлы, по вкусу можно добавить индивидуальные настройки каких либо модулей.

Перезапускаем apache и смотрим все ли работает.
[root@test /]# service httpd restart

apache должен запуститься нормально. В директориях log сайтов должно создаться по 2 файла логов.
При обращении к серверу по IP адресу должен выводиться файл который вы положили в /var/www/html/, а при обращениях по именам сайтов вы должны видеть содержимое директории html (пустое скорее всего) и записи в файле access.log соответствующего сайта.

8. Настраиваем mysql. Первым делом удаляем базу test и задаем пароль пользователя root на mysql
[root@test /]# mysql

mysql> DROP DATABASE test;
mysql> USE mysql;
mysql> UPDATE user SET Password=PASSWORD('MyMysqlPassword') WHERE user='root';
mysql> FLUSH PRIVILEGES;
mysql> quit

С MySql проблема примерно такая же как с Apache — требовательность к оперативной памяти которая на VPS весьма дорога.
Для уменьшения объема используемой памяти sql сервером правим /etc/my.cnf следующим образом:
в секцию [mysqld] добавляем следующее:
key_buffer = 16M
max_allowed_packet = 10M
table_cache = 400
sort_buffer_size = 1M
read_buffer_size = 4M
read_rnd_buffer_size = 2M
net_buffer_length = 20K
thread_stack = 640K
tmp_table_size = 10M
query_cache_limit = 1M
query_cache_size = 32M
skip-locking
skip-innodb
skip-networking

и в конец файла добавляем эти строки:
[mysqldump]
quick
max_allowed_packet = 16M

[mysql]
no-auto-rehash

[isamchk]
key_buffer = 8M
sort_buffer_size = 8M

[myisamchk]
key_buffer = 8M
sort_buffer_size = 8M

[mysqlhotcopy]
interactive-timeout

перезапускаем mysqld чтобы убедиться что все нормально:
[root@test ]# service mysqld restart

Так же нужно заменить что опция «skip-networking» делает возможным обращение к серверу только с локальной машины через сокет. Если требуется сетевой доступ — эту опцию включать не нужно.
Такие настройки позволят минимизировать память используемую процессом mysql и нормально работать на незагруженном сайте. Но конечно же нужно смотреть на статистику работы mysql и в зависимости от потребностей увеличивать данные здесь лимиты.

Дальнейшее администрирование mysql удобнее производить через phpMyAdmin.
Теперь один нюанс — по умолчанию phpMyAdmin доступен по пути /phpMyAdmin на всех наших сайтах.
Чтобы этого не было создаем специализированный сайт для управления (например cfg.testsite.ru) и настраиваем его аналогично остальным.
Потом переносим все содержимое файла /etc/httpd/conf.d/phpMyAdmin.conf в конфиг этого сайта, а сам файл phpMyAdmin.conf удаляем или переносим куда-нибудь из дириктории conf.d.
После таких действий phpMyAdmin будет доступен по пути /phpMyAdmin/ только на специально выделенном сайте.
Ну и для того чтобы в него можно было войти в файле конфигурации сайта меняем
<Directory /usr/share/phpMyAdmin/>
Order Deny,Allow
Deny from All
Allow from 127.0.0.1
Allow from ::1

<Directory /usr/share/phpMyAdmin/setup/>
Order Deny,Allow
Deny from All
Allow from 127.0.0.1
Allow from ::1

на
<Directory /usr/share/phpMyAdmin/>
Order Deny,Allow
Deny from All
Allow from 127.0.0.1
Allow from ваш.ип.адрес.
Allow from ::1

<Directory /usr/share/phpMyAdmin/setup/>
Order Deny,Allow
Deny from All
Allow from 127.0.0.1
Allow from ваш.ип.адрес.
Allow from ::1

После этого phpMyAdmin будет доступен с вашего ip адреса.

Авторизуемся в нем как пользователь root с тем паролем что установили.
Чтобы создать пользователя идем в «Привелегии» — «Добавить нового пользователя»
имя пользователя — произвольное, я предпочитаю использовать имя сайта чтобы уменьшить путаницу.
Хост — локальный (мы же делаем его для сайта который будет крутиться тут же?)
Пароль — генерировать. (не забываем копировать пароль)
Ставим галочку — «Создать базу данных с именем пользователя в названии и предоставить на нее полные привилегии»
Применяем.
В итоге получаем пользователя с выбранными вами именем, паролем и базой данных с аналогичным названием.

9. Часто заливать файлы на хостинг удобнее через фтп. для этого мы и установили vsftpd
редактируем его конфиг /etc/vsftpd/vsftpd.conf
выключаем анонимный логин, меняем
anonymous_enable=YES

на
anonymous_enable=NO

и раскоментируем
chroot_local_user=YES

Теперь чтобы можно было зайти на фтп определенного сайта соответствующему пользователю нужно задать пароль
[root@test /]# passwd testsite.ru

И не забываем что по умолчанию этот пользователь с установленным паролем может зайти по SSH. Чтобы отключить эту возможность проще всего сменить шелл пользователя
[root@test etc]# chsh -s /sbin/nologin testsite.ru

Включаем и запускаем vsftpd
[root@test /]# chkconfig vsftpd on
[root@test /]# service vsftpd start

Проверяем, все ли работает.

Ну и на последок совсем простенький «оперативный бекап». По принципу «бекапов много не бывает».
Лучше бы использовать что-то более правильное, но плохой бекап все-таки лучше полного отсутствия.
Такой бекап может служить неплохим дополнением полному бекапу виртуальной машины у хостинг-провайдера. Но, ни в коем случае не его заменой.
Бекапим содержимое сайтов и баз данных, а так же настройки в каталоге /etc/.
Создаем директорию /backup/ и ставим на нее права «700»

[root@test /]# mkdir /backup/
[root@test /]# chmod 700 /backup/

В директории /etc/cron.daily/ создаем файл backup.sh и так же ставим на него права «700».
[root@test /]# touch /etc/cron.daily/backup.sh
[root@test /]# chmod 700 /etc/cron.daily/backup.sh

Файл имеет следующее содержание:

#!/bin/sh

#Бекапим все директории html наших сайтов
tar -cf - /home/*/html/ | gzip > /backup/sites-`date +%Y-%m-%d`.tar.gz

#Бекапим все базы даных в один файл
mysqldump -u root --password=MyMysqlPassword --all-databases | gzip > /backup/mysql-`date +%Y-%m-%d`.dump.gz

#Бекапим конфигурационные файлы
tar -cf - /etc/ | gzip > /backup/etc-`date +%Y-%m-%d`.tar.gz

#Удаляем файлы бекапов старше 7 дней
tmpwatch -t -m 7d /backup/

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

#!/bin/sh
for dir in `ls -1 /home/ `; do
tar -cf - /home/$dir/html/ | gzip > /backup/sites-$dir-`date +%Y-%m-%d`.tar.gz
mysqldump -u root --password=MyMysqlPassword $dir | gzip > /backup/mysql-$dir-`date +%Y-%m-%d`.dump.gz
done;

#Бекапим конфигурационные файлы
tar -cf - /etc/ | gzip > /backup/etc-`date +%Y-%m-%d`.tar.gz

#Удаляем файлы бекапов старше 7 дней
tmpwatch -t -m 7d /backup/

10. Обновления.
Не забываем время от времени обновлять систему.
[root@test ~]# yum update

Благодаря политике RHEL/Centos в отношении к софту версии софта после обновления останутся теми же и нечаяно положить сервер из-за того что в конфиге что-то поменялось шансов очень мало.
Правда в этом подходе есть и минус — через три года в Centos-6 будут теже версии софта что и сейчас. Но если наша цель стабильность — это нам подходит.

11. Тестирование.
Очень рекомендую провести после настройки тестирование сайта.
Первый пункт тестирования — перезагрузка сервера и проверка того что все нужные демоны стартанули и все работает как ожидалось. Я бы вообще порекомендовал не гнаться за циферками аптайма а перезагружаться после установки или изменения версий любого серверного софта стартующего автоматом.
Лучше узнать, что Apache не стартует в автозапуске после собственноручного планового ребута, чем узнать, что у хостера были проблемы и в следствии ребута вашей виртуалки сайты на ней уже полдня как не работают.
Далее — нагрузочное тестирование при помощи утилиты ab (Apache HTTP server benchmarking tool).
В данном тестировании нас интересует не столько количество попугаев сколько поведение сервера под нагрузкой. На нем не должно быть умирающих процессов и активного свопинга.
Для тестирования нам потребуется сайт размещенный на этом сервере в рабочем состоянии. И «типичная» страница с этого сайта. Ну или можно использовать не типичную, а наиболее тяжелую.

Я для примера провожу тестирование на свежеустановленном Drupal 7.9

Из всего многообразия командной строки ab нам потребуются всего 2 параметра -n — количество http запросов -c — количество одновременных запросов (потоков).
Во время выполнения теста во второй ssh сессии с помощью top наблюдаем за тем как поживает сервер.

100 запросов в 2 потока.

[root@test ~]# ab -n 100 -c 2 testsite.ru/

Из вывода ab мне особо интересны «Requests per second», «Time per request» и «Failed requests» которые дают общее представление о производительности сервера.

Failed requests: 0
Requests per second: 6.20 [#/sec] (mean)
Time per request: 322.788 [ms] (mean)

Видно, что сервер обрабатывает 6 с копейками запросов в секунду и тратит 322 миллисекунды на генерацию одной страницы.

Из вывода top интересно распределение памяти и загрузка процессора.

Tasks: 62 total, 3 running, 59 sleeping, 0 stopped, 0 zombie
Cpu(s): 19.9%us, 5.3%sy, 0.0%ni, 0.0%id, 0.0%wa, 0.0%hi, 0.4%si, 74.5%st
Mem: 244856k total, 151624k used, 93232k free, 3752k buffers
Swap: 262136k total, 0k used, 262136k free, 76604k cached

Swap: 0k used — ооочень хорошо.
93232k free + 76604k cached — фактически 170 мегабайт свободной памяти.

100 запросов 5 потоков.

[root@test ~]# ab -n 100 -c 5 testsite.ru/

Failed requests: 0
Requests per second: 6.21 [#/sec] (mean)
Time per request: 804.513 [ms] (mean)

Tasks: 63 total, 5 running, 58 sleeping, 0 stopped, 0 zombie
Cpu(s): 17.5%us, 6.2%sy, 0.0%ni, 0.0%id, 0.0%wa, 0.0%hi, 0.0%si, 76.3%st
Mem: 244856k total, 159756k used, 85100k free, 3812k buffers
Swap: 262136k total, 0k used, 262136k free, 76660k cached

Количество запросов в секунду осталось тем же а вот время генерации выросло больше чем в 2 раза — уперлись в процессор.

Ну и наконец, хабраэффект или что-то близкое 🙂

[root@test ~]# ab -n 500 -c 50 testsite.ru/

Failed requests: 0
Requests per second: 6.45 [#/sec] (mean)
Time per request: 7749.972 [ms] (mean)

Tasks: 63 total, 6 running, 57 sleeping, 0 stopped, 0 zombie
Cpu(s): 19.1%us, 5.3%sy, 0.0%ni, 0.0%id, 0.0%wa, 0.0%hi, 0.0%si, 75.6%st
Mem: 244856k total, 162740k used, 82116k free, 3884k buffers
Swap: 262136k total, 0k used, 262136k free, 76672k cached

Опять-таки количество запросов в секунду относительно стабильно, а вот время генерации стало уже совсем грустным. Но в тоже время Failed requests — нулевое. Что значит что хоть и медленно, но все работает.
Ну и по поводу памяти — на данный момент Swap: 0k used, 82116k free, 76672k cached — потребление практически не выросло и в принципе можно увеличить некоторые лимиты, но учитывая отсутствие у меня наполнения сайта на данный момент, думаю, этого делать не стоит. А вот позже стоит прогнать тесты на заполненом сайте и в зависимости от результатов уже откорректировать настройки.

12. Установка nginx в качестве фронтенда.

Почему это нужно.
Основная проблема кроется в том как apache обрабатывает входящие соединение. На каждое входящее соединение создается новый процесс или берется один из запущенных и соединение передается ему на обслуживание. До тех пор пока соединение не закрыто этот процесс занимается только им.
В принципе все выглядит хорошо пока у нас много оперативки и/или очень быстрые клиенты (ab запущенный с локалхоста один из таких вариантов), но все становится куда грустнее если клиент сидит на медленном канале или просто никуда не спешит. В таком случае он на время забора реквеста фактически блокирует один из процессов который на это время выключается из работы сервера.
Таким образом в теории имея сервер на 100мбит канале и одного настойчивого клиента на диалапе с регетом мы можем получить нечто-вроде DOS — клиент в несколько потоков заблокирует фактически все наши процессы apache которых у нас в виду малого количества оперативной памяти весьма не много.
Решается данная проблема установкой какого-либо легкого http сервера в виде фронтенда. При наличии фронтенда все входящие соединения принимаются им, затем запрос передается apache и быстро получается ответ тем самым освобождая процесс apache для новых запросов. Фронтенд же не спеша и не растрачивая лишних ресурсов отдает полученный ответ уже запросившему ему клиенту.
Дополнительным бонусом фронтенд может сам отдавать статическое содержимое — например картинки, css и т.п. снимая нагрузку с тяжелого апача.

[root@test ~]# rpm -ihv centos.alt.ru/pub/repository/centos/6/x86_64/centalt-release-6-1.noarch.rpm
[root@test ~]# yum install mod_realip2 nginx-stable

Для того чтобы apache и наши скрипты в запросах видели реальный ip адрес клиента а не адрес фронтенда у нас будет установлен mod_realip2.
редактируем /etc/httpd/conf.d/mod_realip2.conf, раскоментируем
RealIP On
RealIPProxy 127.0.0.1
RealIPHeader X-Real-IP

редактируем httpd.conf и файлы в /etc/httpd/conf.d/
меняем все указания на порт 80 на порт 8080
Всего менять нужно три директивы:
Listen 127.0.0.1:8080
NameVirtualHost *:8080
<VirtualHost *:8080>

редактируем /etc/nginx/nginx.conf
user apache;
worker_processes 2;

Я использую запуск nginx из под пользователя apache поскольку изначально мы давали все права с расчетом именно на него.
Так же не лишним будет закомментировать директиву access_log в nginx.conf чтобы избежать двойного ведения лога.
error_log лучше не трогать — ошибки у апача и nginx все-таки разные.

В секции server правим директиву listen и ставим:
listen 80 default

меняем:
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}

на
location / {
proxy_pass 127.0.0.1:8080/;
}

В директории /etc/nginx/conf.d/ создаем файл proxy.conf со следующим содержанием
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
client_max_body_size 10m;
client_body_buffer_size 128k;
proxy_connect_timeout 90;
proxy_send_timeout 90;
proxy_read_timeout 90;
proxy_buffer_size 4k;
proxy_buffers 4 32k;
proxy_busy_buffers_size 64k;
proxy_temp_file_write_size 64k;

перезапускаем apache и nginx
service httpd restart
service nginx restart

и проверяем все ли работает.

В целом все. Теперь nginx стоит фронтендом, принимает все входящие соединения и проксирует их апачу который их обрабатывает и быстро передает ответ обратно в nginx освобождая процесс для новых запросов.

Следующим шагом по увеличению быстродействия и снижения потребляемых ресурсов будет отдача статического содержимого напрямую через nginx.

Для этого придется в дополнение к виртуальным хостам apache завести виртуальные хосты nginx и указать что раздавать.
Для этого в каталоге /etc/nginx/conf.d/ создаем файл с именем нашего сайта и расширением .conf со следующим содержимым:
server {
listen 80;
server_name testsite.ru www.testsite.ru;
location / {
proxy_pass 127.0.0.1:8080/;
}

location ~ /\.ht {
deny all;
}

location /sites/default/files {
root /home/testsite.ru/html;
access_log /home/testsite.ru/log/access_static.log combined;
}
}

В этом примере для сайта на CMS Drupal статическое содержимое каталога /sites/default/files раздается через nginx, а за всем остальным мы уже идем к апачу.
Еще один варинт — заменить директиву location на:
location ~ \.(jpg|gif|png|css|js|ico)$ {
root /home/testsite.ru/html;
access_log /home/testsite.ru/log/access_static.log combined;
}

В таком случае все файлы с соответствующими расширениями будут отдаваться nginx’ом. Но в данном варианте есть маленький минус — nginx не умеет работать с файлами .htaccess поэтому если у вас там есть какое либо содержимое, закрытое от просмотра .htaccess’ом — от использования такого варианта стоит воздержаться.

Еще стоит заметить, что в данной ситуации мы получаем два лога на один сайт. Отдельно лог запросов, по которым отработал апач и отдельно лог содержимого отданного nginx.
Как вариант — перенести директиву access_log из секции location в секцию server и отключить access_log в виртуальном хосте апача. В таком случае лог будет вести только nginx.
Но для посмотреть «как же это работает» двойной лог может оказаться интересным — по ним сразу видно какая часть нагрузки на кого приходится.

Для проведения дальнейшей оптимизации стоит читать уже мануалы по оптимизации конкретных компонентов и делать ее с оглядкой на сложившуюся ситуацию.

UPD: Исправленно несколько опечаток
UPD: Исправлено подключение swap, спасибо AngryAnonymous
UPD: Добавленно описание установки и настройки nginx, спасибо masterbo за пинок в правильную сторону.
Еще вариант бекап скрипта от odmin4eg: habrahabr.ru/blogs/s_admin/132302/#comment_4391784

Жду критики.

Источник: http://habrahabr.ru/post/132302/

11 апреля, 2013

Posted In: Linux