WEBサイト制作

シェルスクリプトを利用したWordPressのバックアップ

WordPressのバックアップのために、よく利用されている「BackWPup」プラグインがあります。
簡単な設定で利用できますので大変便利なのですが、ファイル容量やDB容量が増えてくると時々止まることがあることと、wp-cronで実行しますので、サーバ負荷が心配になってきます。

wp-cronを止めてサーバのcronで動作させれば良いのですが、プラグインだと中身がよくわかっておらず、やはり動作が気になりますので、シェルスクリプト(bash)で実現してみました。

事前作業:SSH認証用の鍵を作成

バックアップ用サーバにはSSH接続することを想定しています。鍵認証であればパスワードを入力する必要はありませんので、まず事前にSSH認証用の鍵を作成します。

鍵作成

ssh-keygenコマンドを実行すると、.sshディレクトリの下に秘密鍵(id_rsa)と公開鍵(id_rsa.pub)のペアが作成されます。
秘密鍵(id_rsa)は機密情報ですので、ファイルパーミッションは最初から600になっています。

$ cd ~
$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/user/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):何も入力せずリターン
Enter same passphrase again:何も入力せずリターン
Your identification has been saved in /home/user/.ssh/id_rsa.
Your public key has been saved in /home/user/.ssh/id_rsa.pub.
The key fingerprint is:
2b:46:fb:9f:d9:ec:3d:84:c8:54:8d:5a:34:ca:xx:xx user@hostname
The key's randomart image is:
$ ls -l .ssh
-rw------- 1 user user 1675 Sep 2 19:03 id_rsa
-rw-r--r-- 1 user user  412 Sep 2 19:03 id_rsa.pub

バックアップサーバに公開鍵を配置

バックアップ先サーバの「.ssh/authorized_keys」ファイルに「id_rsa.pub」の内容を追加します。

バックアップサーバにSSH接続

初めてSSH接続する際には、プロンプト画面が出ますので、一回だけ手動でSSH接続します。
そうすると、「.ssh/known_hosts」に、バックアップサーバの情報が書かれますので、次回からは自動的にアクセスできるようになります。

$ ssh -i ~/.ssh/id_rsa backupuser@bkup.domain
The authenticity of host 'bkup.domain (xxx.xxx.xxx.xxx)' can't be established.
RSA key fingerprint is 74:36:0f:2b:4f:e0:2b:1b:96:c5:54:9a:e8:b4:30:9e.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'bkup.domain,xxx.xxx.xxx.xxx' (RSA) to the list of known hosts.

バックアップスクリプト作成

設定

まずは、スクリプト中で利用する変数の設定です。

#!/bin/bash

# settings

wordpress_name=my_wordpress # バックアップファイルの名前の先頭文字列
wordpress_dir=/var/www/html/wordpress # WordPressが置かれているディレクトリ

dest_user=backupuser # バックアップ先のログインユーザー名
dest_ip=xxx.xxx.xxx.xxx # バックアップ先のIPアドレス
dest_dir=./backup_dir/ # バックアップ先のディレクトリ名
mairaddr=mailaddress@domain # エラー送信用メールアドレス

number_of_file_to_keep=15 # 残しておく過去のファイル数
keep_in_local=FALSE # ローカルに保存するか

datestr=`date +%Y%m%d-%H%M%S` # バックアップファイルの名前に付与する時刻
STATUS=0 # エラーステータス(0:エラーなし、1:エラーあり)

それぞれの環境に合わせてください。「dest_ip」はホスト名でも設定可能です。

WordPressファイルのコピー

作業用に、「workdir_{日付}」というディレクトリを作成します。さらに、その下に「wordpress」というディレクトリを作成します。
WordPressのファイル一式をコピーします。

普通にコピーするとファイルの属性を引き継げなかったため、「shopt -s dotglob」を実行しています。

コピーしたファイルのうち、バックアップ不要なファイル/ディレクトリは削除します。

# copy wordpress files and remove unnecessary files
mkdir workdir_${datestr}
cd workdir_${datestr}
mkdir wordpress
shopt -s dotglob
cp -pr ${wordpress_dir}/* wordpress/
cd wordpress
rm -rf ./wp-content/cache/*
rm -rf ./wp-content/upgrade/*
rm -rf ./wp-content/uploads/backwpup*
cd ..

データベースのエクスポート

WordPressのデータベースをエクスポートし圧縮します。
データベースのホスト、名前、ユーザー名、パスワードは、wp-config.phpファイルを参照しています。

# export database
wordpress_config=${wordpress_dir}/wp-config.php
wordpress_db_name=`grep 'DB_NAME' ${wordpress_config} | cut -d "'" -f4`
wordpress_db_user=`grep 'DB_USER' ${wordpress_config} | cut -d "'" -f4`
wordpress_db_password=`grep 'DB_PASSWORD' ${wordpress_config} | cut -d "'" -f4`
eval "mysqldump -u ${wordpress_db_user} -p${wordpress_db_password} ${wordpress_db_name} | gzip -c > wordpress_${datestr}.sql.gz"
if [ $? -ne 0 ]
then
    echo "[ERROR]mysqldump error."
    STATUS=1
fi

ファイルとデーターベースを一まとめにする

WordPressファイル一式とデータベースを一まとめにして、backupディレクトリに、.tar.gz形式で出力します。

# create wordpress file
cd ..
if [ ! -e backup ]
then
    mkdir backup
fi
source_filename=./backup/${wordpress_name}_${datestr}.tar.gz
eval "tar czf ${source_filename} workdir_${datestr}/*"
if [ $? -ne 0 ]
then
    echo "[ERROR]tar error."
    STATUS=1
fi

バックアップサーバにコピー

バックアップサーバに接続してコピーします。他でも使いまわせるように、関数化しています。
事前に準備しておいた鍵を利用して、鍵認証でSSH接続しますので、パスワード入力は必要ありません。

# copy backup file to backup server
scpfile ${source_filename} ${dest_dir}/

# function : execute scp
# $1 copy source file name
# $2 copy destination file name
function scpfile(){
    eval "scp -i ~/.ssh/id_rsa $1 ${dest_user}@${dest_ip}:$2"
    if [ $? -ne 0 ]
    then
        echo "[ERROR]scp error. file=$1"
        STATUS=1
    fi
    return
}

作業用ディレクトリの削除

作業用ディレクトリを削除します。
ローカルにバックアップファイルを残さない場合は、そのファイルも削除します。

# delete backup file
if [ ${keep_in_local} = FALSE ]
then
    rm -f ${source_filename}
fi

# remove work directory
rm -rf workdir_${datestr}

ローカルの過去のファイルを削除

ローカルにバックアップファイルを残す設定をしている場合、残すファイル数を超えた分は日付が古いものから削除します。他でも使いまわせるように、関数化しています。

# delete old files
if [ ${keep_in_local} = TRUE ]
then
    deletefile "backup/${wordpress_name}_"
fi

# function : delete old files
# $1: file path to delete
function deletefile(){
    CNT=0
    for file in `ls -1t ${1}*`   # make file list by date order
    do
        CNT=$((CNT+1))

        if [ ${CNT} -le ${number_of_file_to_keep} ]  # delete files which older than 10 generations
        then
            continue
        fi
        eval "rm ${file}"
    done
    return
}

リモートの過去のバックアップファイルを削除

バックアップサーバにSSH接続して、ローカルと同様に、残すファイル数を超えた分は日付が古いものから削除します。

リモートサーバで実行するスクリプトを、いったん「delete_remote_files.sh」というファイルに出力したうえで、そのスクリプトをSSH接続にインプットしています。

# delete remote old files
echo '
    cd backup_dir
    CNT=0
    for file in `ls -1t '${wordpress_name}'_*`   # 日付順にファイルリスト作成
    do
        CNT=$((CNT+1))
        if [ ${CNT} -le '${number_of_file_to_keep}' ]  # 指定された数より古いファイルを削除
        then
            continue
        fi
        eval "rm ${file}"
    done
' > delete_remote_files.sh
ssh ${dest_user}@${dest_ip} 'bash -s' < delete_remote_files.sh

エラーメールを送信

処理途中でエラーが発生した場合は、指定されたメールアドレスにエラーメールを送信します。

# send result
if [ ${STATUS} -ne 0 ]
then
    SUBJECT="[ERROR]wordpress backup report"
    echo "" | mail -s "${SUBJECT}" "${mailaddr}"
else
    SUBJECT="[SUCCESS]wordpress backup report"
fi

exit ${STATUS}

全体

スクリプト全体です。
「/home/user/scripts」ディレクトリに、「wordpress_backup.sh」というファイル名で作成しました。

#!/bin/bash

# settings

wordpress_name=my_wordpress
wordpress_dir=/var/www/html/wordpress

dest_user=backupuser
dest_ip=xxx.xxx.xxx.xxx
dest_dir=./backup_dir/
mairaddr=mailaddress@domain

number_of_file_to_keep=15
keep_in_local=FALSE

datestr=`date +%Y%m%d-%H%M%S`
STATUS=0

# function : execute scp
# $1 copy source file name
# $2 copy destination file name
function scpfile(){
    eval "scp -i ~/.ssh/id_rsa $1 ${dest_user}@${dest_ip}:$2"
    if [ $? -ne 0 ]
    then
        echo "[ERROR]scp error. file=$1"
        STATUS=1
    fi
    return
}

# function : delete old files
# $1: file path to delete
function deletefile(){
    CNT=0
    for file in `ls -1t ${1}*`   # make file list by date order
    do
        CNT=$((CNT+1))

        if [ ${CNT} -le ${number_of_file_to_keep} ]  # delete files which older than 10 generations
        then
            continue
        fi
        eval "rm ${file}"
    done
    return
}

# MAIN

# copy wordpress files and remove unnecessary files
mkdir workdir_${datestr}
cd workdir_${datestr}
mkdir wordpress
shopt -s dotglob
cp -pr ${wordpress_dir}/* wordpress/
cd wordpress
rm -rf ./wp-content/cache/*
rm -rf ./wp-content/upgrade/*
rm -rf ./wp-content/uploads/backwpup*
cd ..

# export database
wordpress_config=${wordpress_dir}/wp-config.php
wordpress_db_name=`grep 'DB_NAME' ${wordpress_config} | cut -d "'" -f4`
wordpress_db_user=`grep 'DB_USER' ${wordpress_config} | cut -d "'" -f4`
wordpress_db_password=`grep 'DB_PASSWORD' ${wordpress_config} | cut -d "'" -f4`
eval "mysqldump -u ${wordpress_db_user} -p${wordpress_db_password} ${wordpress_db_name} | gzip -c > wordpress_${datestr}.sql.gz"
if [ $? -ne 0 ]
then
    echo "[ERROR]mysqldump error."
    STATUS=1
fi

# create wordpress file
cd ..
if [ ! -e backup ]
then
    mkdir backup
fi
source_filename=./backup/${wordpress_name}_${datestr}.tar.gz
eval "tar czf ${source_filename} workdir_${datestr}/*"
if [ $? -ne 0 ]
then
    echo "[ERROR]tar error."
    STATUS=1
fi

# copy backup file to backup server
scpfile ${source_filename} ${dest_dir}/

# delete backup file
if [ ${keep_in_local} = FALSE ]
then
    rm -f ${source_filename}
fi

# remove work directory
rm -rf workdir_${datestr}

# delete old files
if [ ${keep_in_local} = TRUE ]
then
    deletefile "backup/${wordpress_name}_"
fi

# delete remote old files
echo '
    cd WEB/effata
    CNT=0
    for file in `ls -1t '${wordpress_name}'_*`
    do
        CNT=$((CNT+1))
        if [ ${CNT} -le '${number_of_file_to_keep}' ]
        then
            continue
        fi
        eval "rm ${file}"
    done
' > delete_remote_files.sh
ssh ${dest_user}@${dest_ip} 'bash -s' < delete_remote_files.sh

# send result
if [ ${STATUS} -ne 0 ]
then
    SUBJECT="[ERROR]wordpress backup report"
    echo "" | mail -s "${SUBJECT}" "${mailaddr}"
else
    SUBJECT="[SUCCESS]wordpress backup report"
fi

exit ${STATUS}

cron設定

最後に、cronで自動起動する設定をします。

$ crontab -e

毎週日曜日の午前3時にバックアップする設定です。
また、wp-cron.phpを完全に止めてしまうと、悪影響がある可能性がありますので、10分おきに動かすようにしておきます。

0 3 * * 0 cd /home/user/scripts; ./wordpress_backup.sh > /dev/null 2>&1
*/10 * * * * /usr/bin/php /var/www/html/wordpress/wp-cron.php > /dev/null 2>&1

wp-config.phpファイルを編集して、wp-cronを止めておきます。
「define(‘DB_COLLATE’, ”);」の下あたりに記述すると良いでしょう。

...
/** データベースの照合順序 (ほとんどの場合変更する必要はありません) */
define('DB_COLLATE', '');

define('DISABLE_WP_CRON', 'true');
...

カテゴリー