laravel – データベースと画像の自動バックアップ

万が一に備えて laravel のデータベースと画像ファイルを自動的にバックアップします。

xserver でシェルスクリプトを作成して cron 設定

以下のようなシェルスクリプトを作成します。

内容は、laravel のデータベース全体を日付名でエクスポートして、古いものは削除するというものです。

dbdump.sh

#!/bin/sh
period=7
dirpath='~/moheno.site/mysql_bak'
filename=`date +%y%m%d`
mysqldump -uheno -pmoheno henomemo > $dirpath/$filename.sql
chmod 700 $dirpath/$filename.sql
oldfile=`date --date "$period days ago" +%y%m%d`
rm -f $dirpath/$oldfile.sql

そうして、xserver のコントロールパネルの cron 設定から1日1回このシェルスクリプトを実行するようにします。

NAS の python プログラムでバックアップをコピー

ローカルマシンから xserver に ssh ログインすると、パスフレーズを要求されます。

vps の ubuntu に django をインストールしていたときは秘密のキーを .ssh に置いて、パスフレーズを入力することなく以下のようにしてファイルを scp していました。


scp -P 11123 -i ~/.ssh/kagoya.key -r root@133.18.111.11:/home/heno/testdjango ~/

xserver ではこれを許してくれません。

ssh-add を使えばローカルマシンでは可能ですが、NAS には ssh-add がありません。バックアップはローカルマシンじゃなくて NAS にやらせたいのです。

そこで python を使うことにしました。
以下のような python を作成。

以下のプログラムのコアな部分は、ここから頂きました。
実はよく理解していないので途中からちょっと変な感じになっていますが、これでちゃんと動きます。

ftp_tls.py

import os
import shutil
from ftplib import FTP_TLS
from pathlib import Path

ENCODING = 'utf8'

class FtpDown():
    
    def __init__( self ):    

        self.config = {
            'host': 'xxxx.xserver.jp',
            'user': 'heno',
            'passwd': 'pass',
        }
        self.remote_mdeia_dir = 'moheno.site/sample1/storage/app/public'
        self.local_media_dir = '/home/user/laravel_bak/media'
        self.remote_sql_dir = 'moheno.site/mysql_bak'
        self.local_sql_dir = '/home/user/laravel_bak/sql/'
        self.resetDir()
        
    def resetDir(self):
        os.makedirs(self.local_media_dir, exist_ok=True)
        os.makedirs(self.local_sql_dir, exist_ok=True)
        shutil.rmtree(self.local_media_dir)
        shutil.rmtree(self.local_sql_dir)
        os.makedirs(self.local_media_dir, exist_ok=True)
        os.makedirs(self.local_sql_dir, exist_ok=True) 

    def media_down(self, ftp: FTP_TLS, remote_dir: str, local_root: Path) -> None:
        ftp.cwd(remote_dir)
        def get_full_path(path, filename):
            return '{}/{}'.format(path, filename)
        paths = ['.']
        while paths:
            path = paths.pop(0)
            for filename, info in ftp.mlsd(path):
                if info['type'] in ('cdir', 'pdir'):
                    continue
                if info['type'] == 'dir':
                    paths.append(get_full_path(path, filename))
                if info['type'] == 'file':
                    full_path = get_full_path(path, filename)
                    print(full_path)
                    local_path = local_root / full_path
                    local_path.parent.mkdir(parents=True, exist_ok=True)
                    with local_path.open('wb') as fp:
                        ftp.retrbinary('RETR {}'.format(full_path), fp.write)
  
    def sql_down(self):
        with FTP_TLS(**self.config) as ftp:
            ftp.encoding = ENCODING
            ftp.cwd(self.remote_sql_dir) 
            files = ftp.nlst(".")
            for filename in files:
                if filename == '.' or filename == '..':
                    pass
                else:
                    localfn = self.local_sql_dir + filename
                    print(localfn)
                    with open(localfn, 'wb') as fp:
                        ftp.retrbinary("RETR %s" % filename, fp.write)
         
    def filedown(self):
        with FTP_TLS(**self.config) as ftp:
            ftp.encoding = ENCODING
            local_media = Path(self.local_media_dir).resolve()
            self.media_down(ftp, self.remote_mdeia_dir, local_media)
            self.sql_down()

if __name__ == '__main__':
    ftpdn = FtpDown()
    ftpdn.filedown() 

NAS (synology)のタイムスケジューラで、この python を1日1回実行します。

そうすると、xserver にバックアップされている mysql のダンプファイルと画像ファイルが NAS に ftp ダウンロードされます。

バックアップ体制の全体像

以下のような感じになっています。

自作のカード型データベースは xserver 上の laravel で動いています。

xserver 上で1日1回、データベースのダンプファイルを作成します。
1週間を過ぎたら古いものから削除します。

我が家の NAS DS718+ は1日に1回、xserver 上のダンプファイル7個と画像ファイルを ftp ダウンロードします。

ダウンロードされたデータは rsync で直ちに DS 118 へコピーされます。

DS118 では hyperbackup により、1日1回データが外付けHDDへとバックアップされます。

同じデータが4ヶ所で共有できるので、何かあったとしても1日前までは戻れると思っています。

ファイルのバックアップを scp で

NAS が変になってしまったので工場出荷時に戻して、rsync でバックアップしていた NAS から逆に rsync を設定して復活したのですが、データベースや media のバックアップ用の python プログラムがどこかに行ってしまってバックアップができなくなりました。

上に示したプログラムはもともとよくわからずに使っていて、そもそも scp でできないかと考えて、ちょっと検索してみたところ、さすがは python すぐに解答が見つかりました。

xserver に ssh ログインできるように設定しておけば、以下のような python で簡単にバックアップを取ることができます。


import os
import shutil
import paramiko
import scp

class ScpBykey():

    def __init__( self ):

        self.HOST = 'henoheno.xsrv.jp'
        self.PORT = 10022
        self.USER = 'henoheno'
        self.KEY_FILE = '/home/user/.ssh/henoheno.key'
        self.PASSPHRASE = 'pass'  
        self.remote_mdeia_dir = 'moheno.site/sample1/storage/app/public'
        self.remote_sql_dir = 'moheno.site/mysql_bak'
        self.local_dir = '/home/user/laravel'
        self.ssh = ''
        self.resetDir()

    def resetDir(self):
        os.makedirs(self.local_dir, exist_ok=True)
        shutil.rmtree(self.local_dir)
        os.makedirs(self.local_dir, exist_ok=True)

    def sshcon(self):
        rsa_key = paramiko.RSAKey.from_private_key_file(self.KEY_FILE, self.PASSPHRASE)
        ssh = paramiko.SSHClient()
        ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        ssh.connect(self.HOST, self.PORT, self.USER, pkey=rsa_key)
        self.ssh = ssh
        self.file_down( self.remote_sql_dir )
        self.file_down( self.remote_mdeia_dir )

    def file_down( self, remotedir ):
        with scp.SCPClient(self.ssh.get_transport()) as scpc:          
            try:         
                scpc.get( remote_path = remotedir, local_path = self.local_dir, recursive = True)
            except:
                print('そのディレクトリは存在しません。')

if __name__ == '__main__':
    scpk = ScpBykey()
    scpk.sshcon()

こちらの方が遥かに簡単です。

python て本当にすごいですね。