使用 Ansible 执行剧本,对服务器的目录进行备份,支持 Docker、Systemd 管理的程序备份前后关闭重启,支持通过 API 调用执行剧本。
备份的文件,目前只支持上传到 MinIO,因此,需要有自己的 MinIO 服务端,并且需要手动到 MinIO 创建存放备份文件的主 bucket,比如 backup。
目前每次运行都会删除上一次的备份文件
工作目录下:
- backup-example.yaml,样例 playbook
- inventory-example.yaml,样例 主机信息
- role/
- check_storage_space,检查剩余空间,如果小于备份目录,则停止备份
- my_minio_client_role,确保被控端装有 mc,并且有设置的 MinIO 服务端配置
- docker_backup,通用 Docker 程序备份
- systemd_backup, 通用 Systemd 管理的程序备份
- matrix_backup,针对 matrix-docker-ansible-deploy 这个项目编写的备份,与官方默认设置一致就能使用
- my_minio_client_role
备份统一步骤
- 检查剩余空间大小,确保 mc 安装
- 压缩前,关闭程序,压缩后,恢复程序。
- 压缩时可以排除子目录
- 上传导出文件到 minio,删除压缩包
- 删除 mc 的配置
mkdir -p ~/pythonServe/ && cd ~/pythonServe/
git clone https://git.ahfei.blog/ahfei/Ansible4backup && cd Ansible4backup
sudo apt update && sudo apt upgrade && sudo apt install python3 python3-venv -y
python3 -m venv .env
source .env/bin/activate && pip install -r requirements.txt
开启 ansible 的命令补全
activate-global-python-argcomplete --user
首先复制 inventory-example.yaml 文件,比如新文件名称为 inventory.yaml
vim inventory.yaml
放入被控端的信息
backup_hosts:
vars:
backup_date: "{{ lookup('pipe', 'date +%Y%m%d') }}"
minio_alias: ansible4bak
bucket_name: backup
hosts:
MatrixServer:
ansible_host: <MatrixServer_IP> # 替换为 MatrixServer 的 IP 地址
ansible_port: <MatrixServer_Port> # 替换为 MatrixServer 的 SSH 端口
ansible_user: <your_username> # 替换为登录 MatrixServer 的用户名
ansible_ssh_pass: <your_password> # 替换为登录密码
ansible_become_method: sudo
ansible_become_password: <your_password> # 替换为登录密码,用于使用 sudo 权限
然后配置密钥登录
- 在管理节点生成密钥对
ssh-keygen -t rsa -b 2048
当提示保存密钥的位置时,可以接受默认位置(通常是 ~/.ssh/id_rsa
),也可以指定一个新位置。
Your identification has been saved in /home/vfly2/.ssh/id_rsa
Your public key has been saved in /home/vfly2/.ssh/id_rsa.pub
The key fingerprint is:
...
- 将公钥复制到被控端
可以将生成的公钥(.pub
文件)复制到所有被控端服务器的 ~/.ssh/authorized_keys
文件中。可以手动做这件事,也可以使用 ssh-copy-id
命令来自动复制。
如果你的公钥文件是默认的 ~/.ssh/id_rsa.pub
,使用以下命令:
ssh-copy-id -p 22 vfly2@50.60.77.88
- 测试密钥登录,看看是否不需要密码即可登录
ssh -p 22 vfly2@50.60.77.88
- 配置 Ansible
如果密钥不在默认位置,需要在 Ansible 的配置文件中指定密钥的路径,或者在运行 ansible 命令时使用 --private-key
选项。在默认位置则可以直接使用。
在 ansible.cfg
文件中指定非默认密钥的路径:
[defaults]
private_key_file = /path/to/your/private/key
在命令行中使用参数:
ansible-playbook myplaybook.yml --private-key=/path/to/your/private/key
测试能否连接上被控端
测试属于 backup_hosts 的机器
ansible -i inventory.yaml -m ping backup_hosts
单独测试一个机器
ansible -i inventory.yaml -m ping TechniqueServer
如果希望用密码登录被控端,需要增加 ansible_ssh_pass 变量
backup_hosts:
vars:
backup_date: "{{ lookup('pipe', 'date +%Y%m%d') }}"
minio_alias: ansible4bak
bucket_name: backup
hosts:
MatrixServer:
ansible_host: <MatrixServer_IP>
ansible_port: <MatrixServer_Port>
ansible_user: <your_username>
ansible_ssh_pass: <your_password> # 增加这个,放入密码
ansible_become_method: sudo
ansible_become_password: <your_password>
但是光有密码还不能正常连接,为了能通过密码登陆,还需要安装 sshpass:
sudo apt install sshpass
并且要先手动登陆一次,记录被控端的公钥才行
ssh -p 22 username@1.2.3.4
这样之后,调用 ansible 执行 playbook 时,就能顺利登录被控端了
首先复制 backup-example.yaml 文件,比如新文件名称为 backup.yaml
vim backup.yaml
参考样例和下面的 role 的介绍,根据需要指定剧本的“剧情”
检查 playbook 本身有无问题
ansible-playbook -i inventory.yaml backup.yaml --check
运行整个 playbook:
ansible-playbook -i inventory.yaml backup.yaml
运行 playbook 中有某个 tag 的任务:
ansible-playbook -i inventory.yaml backup.yaml --tags=backup_matrix # --tags=backup_matrix, backup_technique 多个则用冒号隔开
运行 playbook 中某个主机的任务:
ansible-playbook -i inventory.yaml backup.yaml --limit "MatrixServer"
创建一个 role,需要在项目目录下的 roles 目录下新建 role 的目录。
mkdir -p roles/check_storage_space
然后,role 的 task 部分,应该放在 tasks 文件中
mkdir roles/check_storage_space/tasks
一般,只用创建一个 main.yaml,代码都放在里面就行
vim roles/check_storage_space/tasks/main.yaml
如果需要 handler 部分,那应该在 handlers 文件中,新建 main.yaml;其他类同。
确保在运行 playbook 时定义了下面两个变量
large_directory
: 要备份的目录temporary_store_directory
: 备份文件临时存放的目录,如果不存在,则不比较
如果 large_directory
的大小超过了 temporary_store_directory
所在硬盘的剩余空间,这个备份任务终止。
在 playbook 中调用 role
vim backup.yaml
- name: Backup Matrix
tags:
- backup_matrix
hosts: MatrixServer
vars:
large_directory: /matrix/synapse/storage/media-store # 如果存在,则比较硬盘剩余空间与该目录大小
temporary_store_directory: /tmp/matrix_bak/ # 必须带 /
specify_directory_in_bucket: MatrixServer
roles:
- check_storage_space
- my_minio_client_role
- matrix_backup
tasks:
- name: delete mc config
shell: "mc alias rm {{ minio_alias }}"
检查 playbook 本身有无问题
ansible-playbook -i inventory.yaml backup.yaml --tags=backup_matrix --check
运行 playbook :
ansible-playbook -i inventory.yaml backup.yaml --tags=backup_matrix
适用对象,程序是由 systemd 管理的,并且要备份的全在一个目录内。
可以实现,备份前关闭程序,备份后重启;忽略一些子目录。
在 playbook 中调用 role
vim backup.yaml
以 minio 图床为例
- name: Backup Imagebed
tags:
- backup_imagebed
hosts: ImagebedServer
vars:
temporary_store_directory: /tmp/imagebed_bak # 不能带 /
imagebed_directory: /mnt/minio
excluded_directory: --exclude=/mnt/minio/updatefetch --exclude=/mnt/minio/upload # 目前只会这样,以后完善
specify_directory_in_bucket: ImagebedServer
systemdservice_name_list: [minio]
large_directory: "{{ imagebed_directory }}"
roles:
- check_storage_space
- my_minio_client_role
- systemd_backup
tasks:
- name: delete mc config
shell: "mc alias rm {{ minio_alias }}"
检查 playbook 本身有无问题
ansible-playbook -i inventory.yaml backup.yaml --tags=backup_imagebed --check
运行 playbook :
ansible-playbook -i inventory.yaml backup.yaml --tags=backup_imagebed
类似 systemd,自动关闭重启,docker 一般只有一个目录,只支持备份一个目录。
在 playbook 中调用 role
vim backup.yaml
Docker 版的 WordPress
- name: Backup FlightBorne
tags:
- backup_flight_borne
hosts: FlightBorneServer
vars:
backup_file_name_prefix: wordpress_flight_borne_bak
temporary_store_directory: /tmp/flight_borne_bak # 不能带 /
specify_directory_in_bucket: FlightBorneServer
container_name_list: [wordpress, wp_mysql, wp_redis]
container_directory: "{{ ansible_env.HOME }}/myserve/wordpress"
large_directory: "{{ container_directory }}"
roles:
- check_storage_space
- my_minio_client_role
- docker_backup
tasks:
- name: delete mc config
shell: "mc alias rm {{ minio_alias }}"
ansible-playbook -i inventory.yaml backup.yaml --tags=backup_flight_borne --check
原生安装的,使用 MySQL 系的数据库。
备份前会开启维护模式。
WordPress 备份的内容只有两个,一个网页目录,还有数据库的备份。因此需要提供目录路径和数据库的用户、密码、数据库名称。
在 playbook 中调用 role
vim backup.yaml
- name: Backup Technique
tags:
- backup_technique
hosts: TechniqueServer
vars:
temporary_store_directory: /tmp/technique_bak # 不能带 /
specify_directory_in_bucket: TechniqueServer
systemdservice_name_list: [lsws]
mysql_user: wp_user
mysql_password: password
mysql_dbname: wp_db
wordpress_directory: /usr/local/lsws/wordpress
large_directory: "{{ wordpress_directory }}"
roles:
- check_storage_space
- my_minio_client_role
- wordpress_backup
tasks:
- name: delete mc config
shell: "mc alias rm {{ minio_alias }}"
ansible-playbook -i inventory.yaml backup.yaml --tags=backup_technique --check
原生安装的,使用 MySQL 系的数据库。
备份前会开启维护模式。
同为 PHP 项目,NextCloud 备份的内容也是只有两个,一个网页目录,还有数据库的备份。因此需要提供目录路径和数据库的用户、密码、数据库名称。
在 playbook 中调用 role
vim backup.yaml
- name: Backup NextCloud
tags:
- backup_nextcloud
hosts: NextCloudServer
vars:
temporary_store_directory: /tmp/nextcloud_bak # 不能带 /
specify_directory_in_bucket: NextCloudServer
nextcloud_directory: /var/www/nextcloud.ahfei.blog
mysql_user: nextcloud
mysql_password: password
mysql_dbname: nextcloud_db
large_directory: "{{ nextcloud_directory }}"
roles:
- check_storage_space
- my_minio_client_role
- nextcloud_backup
tasks:
- name: delete mc config
shell: "mc alias rm {{ minio_alias }}"
ansible-playbook -i inventory.yaml backup.yaml --tags=backup_nextcloud --check
sudo ufw allow 5123 comment ansible_api
运行
export ANSIBLE_API_TOKEN="dwSR3bXYXcLGNiGV"
export ANSIBLE_VENV_PATH="./.env"
python ansible_api.py
程序内的测试
# 虚拟环境的路径
ansible_virtual_env_path = '/home/vfly2/pythonServe/Ansible4backup/.env'
inventory = "/home/vfly2/pythonServe/Ansible4backup/inventory.yaml"
playbook = "/home/vfly2/pythonServe/Ansible4backup/backup.yaml"
tags = 'backup_matrix'
# 调用函数执行 ansible-playbook 命令
run_ansible_playbook(inventory, playbook, tags, ansible_virtual_env_path)
调用 API 测试
curl -X POST -H "Content-Type: application/json" -H "Authorization: dwSR3bXYXcLGNiGV" -d '{"inventory":"/home/vfly2/pythonServe/Ansible4backup/inventory.yaml", "playbook":"/home/vfly2/pythonServe/Ansible4backup/backup.yaml", "tags":"backup_matrix"}' http://ip:5123/ansible_bak_api
sudo vim /lib/systemd/system/ansible_api.service
[Unit]
Description=ansible API Server
After=network.target
[Service]
WorkingDirectory=/home/vfly2/pythonServe/Ansible4backup
User=vfly2
Group=vfly2
Type=simple
ExecStart=/home/vfly2/pythonServe/Ansible4backup/.env/bin/python /home/vfly2/pythonServe/Ansible4backup/ansible_api.py
ExecStop=/bin/kill -s HUP $MAINPID
Environment=PYTHONUNBUFFERED=1
Environment=ANSIBLE_API_TOKEN="dwSR3bXYXcLGNiGV"
Environment=ANSIBLE_VENV_PATH="/home/vfly2/pythonServe/Ansible4backup/.env"
RestartSec=15
Restart=on-failure
[Install]
WantedBy=default.target
sudo systemctl daemon-reload
sudo systemctl enable --now ansible_api
sudo systemctl status ansible_api
sudo systemctl stop ansible_api
sudo systemctl start ansible_api