定期從Docker上部署的MySQL備份數據

前段時間公司停電,正巧趕上周一領導要開會要過一遍項目,然而項目所依賴的MySQL數據庫是直接部署在宿主機,且因為各人部署方式不同的原因,花了很久才在開會前啟動起來。于是開完會后,我第一件事就是把原先依賴的MySQL數據庫遷移到Docker上,又另外寫了一個腳本定時將Docker上部署的MySQL數據庫備份出來,而且我們的腳本不單單可以指定要備份的數據庫,還要將備份出來的SQL文件打包成壓縮文件,并以一定的規范來命名,比如:test_2019-10-11-17.zip,test是前綴,2019-10-11-17代表是2019年10月11日17點的時候備份的。再來就是定期刪除5個小時或10個小時之前的備份文件,當然這些都是錦上添花的事了,文末會附上備份文件腳本。

現在,我們先在Docker上部署一個MySQL實例,再創建幾個用于測試的數據庫,將其導出。

首先,我們創建一個MySQL實例:

?  ~ docker run --name mysql-test -v /usr/local/mysql-test:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -p 8706:3306 -d mysql
1a70e86992bddb493db69da55cf8bf08863ce0b59d2f5931e782125adb900d71
?  ~ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                               NAMES
1a70e86992bd        mysql               "docker-entrypoint..."   4 seconds ago       Up 2 seconds        33060/tcp, 0.0.0.0:8706->3306/tcp   mysql-test

  

然后我們進入到容器后,再進入到MySQL修改一下加密方式,以便部分版本較舊的Navicat可以連接我們的MySQL實例(此操作可以不做):

?  ~ docker exec -it mysql-test /bin/bash  
[email protected]:/# mysql -uroot -p
Enter password: 
……

mysql> ALTER user 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
Query OK, 0 rows affected (0.03 sec)

mysql> FLUSH PRIVILEGES; 
Query OK, 0 rows affected (0.00 sec)

  

我們創建一個test1數據庫,再創建一張表admin,并插入兩條數據:

mysql> CREATE DATABASE `test1`CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
Query OK, 1 row affected (0.02 sec)

mysql> CREATE TABLE `test1`.`admin` ( `id` INT NOT NULL AUTO_INCREMENT, `account` CHAR ( 32 ) NOT NULL, PRIMARY KEY ( `id` ) );
Query OK, 0 rows affected (0.27 sec)

mysql> INSERT INTO `test1`.`admin`(`account`) VALUES ('admin1');
Query OK, 1 row affected (0.02 sec)

mysql> INSERT INTO `test1`.`admin`(`account`) VALUES ('admin2');
Query OK, 1 row affected (0.01 sec)

mysql> SELECT * FROM `test1`.`admin`;
+----+---------+
| id | account |
+----+---------+
|  1 | admin1  |
|  2 | admin2  |
+----+---------+
2 rows in set (0.00 sec)

  

我們再來創建第二個數據庫test2,并創建一張表user,再插入兩條測試數據:

mysql> CREATE DATABASE `test2`CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
Query OK, 1 row affected (0.01 sec)

mysql> CREATE TABLE `test2`.`user` ( `id` INT NOT NULL AUTO_INCREMENT, `name` CHAR ( 32 ) NOT NULL, `age` TINYINT NOT NULL, PRIMARY KEY ( `id` ) );
Query OK, 0 rows affected (0.04 sec)

mysql> INSERT INTO `test2`.`user`(`name`,`age`) VALUES ('Amy','16');
Query OK, 1 row affected (0.03 sec)

mysql> INSERT INTO `test2`.`user`(`name`,`age`) VALUES ('Tom','20');
Query OK, 1 row affected (0.00 sec)

mysql> SELECT * FROM `test2`.`user`;
+----+------+-----+
| id | name | age |
+----+------+-----+
|  1 | Amy  |  16 |
|  2 | Tom  |  20 |
+----+------+-----+
2 rows in set (0.00 sec)

  

至此,我們的測試庫和測試數據已經創建好了。現在,我們先嘗試著用命令行備份數據庫test1。

?  ~ docker exec -it mysql-test mysqldump -uroot -p123456 test1 > test1.sql
?  ~ cat test1.sql  
……
CREATE TABLE `admin` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `account` char(32) COLLATE utf8mb4_general_ci NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
……
INSERT INTO `admin` VALUES (1,'admin1'),(2,'admin2');

  

這里簡單介紹一下導出命令:

docker exec -it {container_name} mysqldump -u{db_user} -p{db_password} {database} > {file_path}

 

  • container_name:容器名稱,此處也可填容器ID。
  • db_user:數據庫賬號。
  • db_password:數據庫密碼。
  • database:要備份的數據庫。
  • file_path:備份出來的文件名。

因此,如果我們要備份多個數據庫,比如test1和test2,則循環執行上面的命令,替換database即可。

讓我們用Python3來執行下面這個腳本:

#!/usr/bin/env python
# encoding: utf-8
import datetime
import os
import shutil
import subprocess
import time
import zipfile

# 數據庫用戶名
db_user = "root"
# 數據庫密碼
db_password = "123456"
# 備份目錄
backup_dir = "/var/test_backup"
# backup_prefix和backup_suffix分別為備份文件的前綴和后綴,如test_backup_2019-09-19-11則代表該文件是在2019年9月19日的11點時備份的
backup_prefix = "test_backup"
backup_suffix = "%Y-%m-%d-%H"
# 備份數據庫列表
backup_databases = [
    "test1",
    "test2",
]
# 容器名
container_name = "mysql-test"
# 過期小時,定期刪除5個小時前的備份文件
expire_hour = 5


# 獲取備份文件名
def get_backup_filename():
    t = time.strftime(backup_suffix, time.localtime())
    return "%s_%s" % (backup_prefix, t)


def get_backup_path():
    return "%s%s%s" % (backup_dir, os.sep, get_backup_filename())


# 獲取過期時間戳
def get_expire_time():
    t = datetime.datetime.now() - datetime.timedelta(hours=expire_hour)
    return int(time.mktime(t.timetuple()))


def create_dir(dir_path):
    # 如果目錄存在則退出
    if os.path.exists(dir_path):
        return
    os.mkdir(dir_path)


cmd_template = "docker exec -it {container_name} mysqldump -u{db_user} -p{db_password} {database} > {file_path}"


# 備份指定數據庫
def backup_database(backup_path, database):
    file_path = os.sep.join([backup_path, "%s.sql" % database])
    d = {
        "container_name": container_name,
        "db_user": db_user,
        "db_password": db_password,
        "database": database,
        "file_path": file_path,
    }
    cmd = cmd_template.format(**d)
    subprocess.call(cmd, shell=True)


def zip_dir(dir_path):
    file_path = '.'.join([dir_path, "zip"])
    if os.path.exists(file_path):
        os.remove(file_path)
    z = zipfile.ZipFile(file_path, 'w', zipfile.ZIP_DEFLATED)
    for root, directories, files in os.walk(dir_path):
        fpath = root.replace(dir_path, '')
        fpath = fpath and fpath + os.sep or ''
        for filename in files:
            z.write(os.path.join(root, filename), fpath + filename)
    z.close()


# 備份數據庫
def backup():
    backup_path = get_backup_path()
    try:
        create_dir(backup_path)
        for database in backup_databases:
            backup_database(backup_path, database)
        zip_dir(backup_path)
    finally:
        shutil.rmtree(backup_path)


# 清理過期備份文件
def clean():
    expire_time = get_expire_time()
    for root, directories, files in os.walk(backup_dir):
        for file in files:
            if not file.startswith(backup_prefix):
                continue
            if not file.endswith(".zip"):
                continue
            file_path = os.sep.join([root, file])
            t = os.path.getctime(file_path)
            if t < expire_time:
                os.remove(file_path)


if __name__ == "__main__":
    try:
        backup()
    finally:
        clean()

  

執行完畢后,我們會發現備份目錄下多了一個zip文件,我們可以用unzip命令來查看下zip文件的內容:

?  ~ python36 backup.py 
?  ~ ll /var/test_backup 
total 4.0K
-rw-r--r-- 1 root root 1.8K Oct 12 09:55 test_backup_2019-10-12-09.zip
?  ~ unzip -v /var/test_backup/test_backup_2019-10-12-09.zip 
Archive:  /var/test_backup/test_backup_2019-10-12-09.zip
 Length   Method    Size  Cmpr    Date    Time   CRC-32   Name
--------  ------  ------- ---- ---------- ----- --------  ----
    2085  Defl:N      784  62% 10-12-2019 09:55 e42329a0  test1.sql
    2104  Defl:N      801  62% 10-12-2019 09:55 046297a6  test2.sql
--------          -------  ---                            -------
    4189             1585  62%                            2 files

  

測試腳本可以正常備份Docker上的MySQL實例的多個數據庫,我們就可以用Linux自帶的crontab命令來自動執行腳本。

posted @ 2019-10-12 10:06  北洛  閱讀(2374)  評論(3編輯  收藏
七乐彩2011年走势图南方双彩