КАК Я ПЕРЕСТАЛ БОЯТЬСЯ И ПОЛЮБИЛ BASH

дамп / восстановление / миграция web-сервера

 

Я уже выкладывал образцы простеньких bash-скриптов, например, для сборки cubic проектов или программы автозапуска, но здесь будет немного более сложная задачка, а именно создание дампа и восстановление рабочего веб-сервера Apache. Суть сводится к тому, чтобы писать как можно меньше букав и не компостировать себе мозги в таком случае, например, если сервер упадет и не очнется. Проект будет состоять из двух этапов. На первом мы запускаем скрипт dump.sh, он проверяет, какие есть настройки, папки, базы и т. д., затем все это дело копирует в некий специально-заготовленный  каталог и тут же автоматически создает другой скрипт, deploy.sh. На втором этапе deploy.sh установит apache, mysql и восстановит всю прежнюю конфигурацию, не задавая попутно идиотских вопросов. Полагаю, это может понадобиться не только на аварийный случай, когда система поломается в результате каких-то неудачных экспериментов, но так же при миграции, скажем, на другое железо.

 

ЭТАП 1-Й ДАМП

 

Ох, намучился, пока разбирался, за то победил! Победил с первой половиной. Чтобы не писать самому, естественно, решил сначала нагуглить готовое решение. Нашелся интересный вариант на этом сайте.

nano dump.sh

#!/usr/local/bin/bash
# MySQL backup script
#ulimit -t 3600
#user name
USER="madmentat"
#user password
PASSWORD="Password666"
#MYSQL host address ("localhost" by default)
HOST="localhost"
#dirrectory for backups
DIR="/data/fileserver/share/mysqldump"

GZIP="$(which gzip)"
MYSQL="$(which mysql)"
MYSQLDUMP="$(which mysqldump)"
NOW=$(date +"%Y_%m_%d_%H-%M")

echo "Choose database number:"
i=1
for db in $($MYSQL -u$USER -h$HOST -p$PASSWORD -Bse 'show databases')
do
echo "$i -  $db"
DATABASES[$i]=$db
i=$((i+1))
done

read RESULT
if [ "$RESULT" -ge 1 ] && [ "$RESULT" -lt ${#DATABASES[@]} ]
then
eval "DB=${DATABASES[$RESULT]}"
eval "FILE=$DIR$DB-$NOW.sql.gz"
$MYSQLDUMP -u$USER -h$HOST -p$PASSWORD $DB | $GZIP -9 > $FILE
else
echo "Wrong number!"
fi

Однако, скрипт не заработал, ругался нехорошими словами на строку 23.

mysqldump.sh: 23: DATABASES[1]=base: not found

По аналогии с C++, я сразу понял что там какой-то массив и всякое вот это, и вроде бы все сходится, но что значит "not found"?! Пытался распросить народ на  /ru.stackoverflow.com/, там никто толком не помог. Пришлось разбираться самому. В итоге нашел похожий кейс на английском stackoverflow и там мужик в конце объяснил одну прикольную штуку, дескать, bash-скрипты следует запускать не sh командой, типа

sh dump.sh

а через точку, т. е., так:

./dump.sh

Я не очень силен в разных языках, тем более в английском... "Alice in wonderland" осилил, а вот "Brave new world" не смог, как и "Orange Clockwork"... Поэтому хз чего он там балакал. Дословно следующее:

This can happen if you override the intended interpreter. E.g this will run with sh regardless of what hash bang is used (handy when not using hash bang):
> sh run.sh
OR to run bash:
> bash run.sh
To let it use the script defined hash bang value, use this:
> ./run.sh

Еще один крендель написал об этом по-русски:

"На счет запуска, правильно и так и так, просто если в первом случае вы явно указываете обработчик, то во втором случае используется обработчик прописаный в шебанге. Кроме того, если в первом случае скрипт будет выполнен в любом случае, то во втором только если пользователь обладает правом на запуск этого файла. И да, в вашем первом варианте вы пытаетесь запустит bash (как я понимаю) скрипт в sh"

Сразу ясно: оба сказали что-то очень умное и по делу. Но что я из этого извлек? За меня ответит Сергей Демихов:

Похоже, bash и sh - не совсем одно и то же. В общем, доперев, как работать с массивами, я убедился, что изначальный вариант все равно не... не... Как бы это сказать-то, чтобы без мата? Ну, вот конструкции "if - then - fi" логически еще как-то можно осознать, но... Эти "read, eval" и всякое такое - по-моему, какая-то хрень. Вообще, при том что у меня реально мало опыта программирования, я уже понимаю одну вещь: чем более простой и тупой твой код, тем лучше - во всяком случае, так меньше вероятности что ты там навертишь чего-нибудь не того. "eval", в теории, можно понять, но к чему тут "read" - решил уже даже не разбираться - ну нахрен. Точнее, подумал что, в принципе, не понимаю зачем нужен оператор read в bash, а уж тем более в данном примере, и решил все упростить, запихав необходимые операции в цикл "for":

#!/bin/bash
# MySQL backup script, writen by genious supermind-programmer madmentat
#ulimit -t 3600
#user name
USER="madmentat"
#user password
PASSWORD="Password666"
#MYSQL host address ("localhost" by default)

HOST="localhost"
#dirrectory for backups
mkdir -p /data/fileserver/dump/mysql
mkdir -p /data/fileserver/dump/etc
SQLDUMPDIR="/data/fileserver/dump/mysql"
DUMPDIR="/data/fileserver/dump"


GZIP="$(which gzip)"
MYSQL="$(which mysql)"
MYSQLDUMP="$(which mysqldump)"
NOW=$(date +"%Y_%m_%d_%H-%M")

echo "Choose database number:"
i=1
for db in $($MYSQL -u$USER -h$HOST -p$PASSWORD -Bse 'show databases')
do
DATABASES[$i]=$db
FILE[$i]=$SQLDUMPDIR/${DATABASES[$i]}-$NOW.sql
echo "${FILE[$i]}"
mysqldump -u$USER -h$HOST -p$PASSWORD ${DATABASES[$i]} > ${FILE[$i]}
i=$((i+1))
done

echo "MySQL dumps are done! Look for your files in $MYSQLDIR"

cp -r /etc/php $DUMPDIR/etc/php
cp -r /etc/apache2 $DUMPDIR/etc/apache2
echo "Apache server files are saved in $DUMPDIR"

И вот этот вариант уже реально выкладывает дампы sql в директорию, прописанную в $DIR! 

mysqldump

 

 ЭТАП 2-Й ВОССТАНОВЛЕНИЕ

 

Здесь сложность упирается в то, чтобы установить LAMP без идиотских вопросов. Отмечу, что идиотскими у нас считаются любые вопросы, скрипт должен работать тихо.

 Продолжение следует...