Скрипт для автоматического резервного копирования
Задача: реализовать резервное копирование в S3 исходя из типов данных с помощью ansible
Для достижения цели нужно:
- mc client(minio)
- наличие пакета cron в системе
- наличие созданных бакетов и настроенными правами доступа
Ручной метод
Для реализации первых двух пунктов предлагаю использовать в качестве примера ansible роль, которая автоматически установит пакет mc client
на любую архитектуру.
Также продублирую код для установки пакета:
- name : Check arm architecture
set_fact:
DEFAULT_ARCHITECTURE: arm64
when : ("'armv7l' in ansible_architecture") or ("'aarch64' in ansible_architecture")
- name : Check amd64 architecture
set_fact:
DEFAULT_ARCHITECTURE: amd64
when : "'x86_64' in ansible_architecture"
- name: Debug
debug:
msg: "{{ ansible_architecture }}"
- name: Install depends
ansible.builtin.apt:
name:
- cron
state: latest
update_cache: true
- name: Download MinIO Client
get_url:
url: https://dl.min.io/client/mc/release/linux-{{ DEFAULT_ARCHITECTURE }}/mc
dest: /usr/local/bin/mc
mode: '0755'
- name: Create a symbolic link
ansible.builtin.file:
src: /usr/local/bin/mc
dest: /usr/bin/mc
state: link
mode: 0755
- name: Check version MinIO Client
command: "mc --version"
register: mc_version
changed_when: false
- name: Output version MinIO Client
debug:
msg: "{{ mc_version.stdout }}"
Настраиваем alias для подключения к бакету:
mc alias set myminio https://myminio.example.net minioadminuser minioadminpassword
Создаем bash + python скрипт, в дириктории root (можно в любом другом месте с минимальнонеобходимыми правами):
#!/bin/bash
echo "НАЗВАНИЕ НОДЫ backup result" > /var/log/synk.log
echo "" >> /var/log/synk.log
mc cp --recursive ~/mydata/ myminio/mydata/
if [ $? -ne 0 ]; then
echo "ERROR" >> /var/log/synk.log
else
echo "SUCCESS" >> /var/log/synk.log
fi
python3 sendemail.py /var/log/synk.log
rm /var/log/synk.log
Данный скрипт, делает копию определенной дириктории, заносит результат выполнения в файл /var/log/synk.log
после чего запускает python в который передается путь данного фала.
sendemail.py
import requests
import sys
import smtplib, ssl
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
file_path = sys.argv[1]
with open(file_path, 'r') as file:
message_content = file.read()
token = '<TELEGRAM TOKEN>'
chat_id = '<CHAT ID>'
url = f'https://api.telegram.org/bot{token}/sendMessage'
# Отправка сообщения
data = {'chat_id': chat_id, 'text': message_content}
response = requests.post(url, data=data)
Данный скрипт пересылает содержание файла в телеграм бота. Также можно использовать EMAIL:
# Отправка сообщения
data = {'chat_id': chat_id, 'text': message_content}
response = requests.post(url, data=data)
message = MIMEMultipart("alternative")
message["Subject"] = "Backup status"
message["From"] = "[email protected]"
message["To"] = "[email protected]"
part1 = MIMEText(message_content, "plain")
message.attach(part1)
context = ssl.create_default_context()
with smtplib.SMTP("smtp.example.com", "587") as server:
server.ehlo() # Can be omitted
server.starttls(context=context)
server.ehlo() # Can be omitted
server.login("[email protected]", "my_super_pass")
server.sendmail("[email protected]", "[email protected]", message.as_string())
Вносим скприпт в crontab
:
0 3 * * * /root/bucket-sync.sh
Теперь каждый день в 3 часа ночи будет совершаться резервное копирование данных.
Метод через Ansible
Файл инвентори:
ssh.example.com SERVERNAME=example MC_NODE=true
Файл group_vars
:
MC_SYNCPAIR:
- {name: "<название БД>", src: "<адрес до БД, нужен pgclient>", dest: "<путь назначения используя mc синтаксис>", target: <Должно совпадать с SERVERNAME>, type: "pgsql", state: present}
#!/bin/bash
echo "{{ SERVERNAME }}-node backup result" > /var/log/synk.log
echo "" >> /var/log/synk.log
{% for chain in MC_SYNCPAIR %}
{% if chain.target == SERVERNAME %}
{% if chain.state == "present" %}
{% if chain.type == "tar" %}
tar cz {{ chain.src }} | mc pipe {{ chain.dest }}/backup-{{ chain.name }}-$(date +%Y-%m-%d).tar.gz
if [ $? -ne 0 ]; then
echo "Synk error {{ chain.name }}" >> /var/log/synk.log
else
echo "Sync success {{ chain.name }}" >> /var/log/synk.log
fi
{% elif chain.type == "sync" %}
mc mirror --remove --overwrite {{ chain.src }} {{ chain.dest }}
if [ $? -ne 0 ]; then
echo "Synk error {{ chain.name }}" >> /var/log/synk.log
else
echo "Sync success {{ chain.name }}" >> /var/log/synk.log
fi
{% elif chain.type == "file" %}
mc cp {{ chain.src }} {{ chain.dest }}/file-{{ chain.name }}-$(date +%Y-%m-%d)
if [ $? -ne 0 ]; then
echo "Synk error {{ chain.name }}" >> /var/log/synk.log
else
echo "Sync success {{ chain.name }}" >> /var/log/synk.log
fi
{% elif chain.type == "pgsql" %}
export PGPASSWORD='{{ DOCKER_COMPOSE_DB_MASTER_PASS }}'
pg_dump -U {{ DOCKER_COMPOSE_DB_MASTER_USER }} -d {{ chain.name }} -h {{ chain.src }} > /tmp/database-{{ chain.name }}-$(date +%Y-%m-%d).pgsql.sql # | mc pipe {{ chain.dest }}/backup-{{ chain.name }}-$(date +%Y-%m-%d).pgsql.sql
mc mv /tmp/database-{{ chain.name }}-$(date +%Y-%m-%d).pgsql.sql {{ chain.dest }}/
if [ $? -ne 0 ]; then
echo "Synk error {{ chain.name }}" >> /var/log/synk.log
else
echo "Sync success {{ chain.name }}" >> /var/log/synk.log
fi
{% endif %}
{% endif %}
{% endif %}
{% endfor %}
python3 sendemail.py /var/log/synk.log
rm /var/log/synk.log
import requests
import sys
import smtplib, ssl
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
file_path = sys.argv[1]
with open(file_path, 'r') as file:
message_content = file.read()
token = '{{ TELEGRAM_BOT_TOKEN }}'
chat_id = '{{ TELEGRAM_ADMIN_ID }}'
url = f'https://api.telegram.org/bot{token}/sendMessage'
# Отправка сообщения
data = {'chat_id': chat_id, 'text': message_content}
response = requests.post(url, data=data)
# message = MIMEMultipart("alternative")
# message["Subject"] = "Backup status"
# message["From"] = "{{ SERVERNAME }}@{{ SENDEMAIL_DOMAIN }}"
# message["To"] = "{{ SENDEMAIL_RECEIVER }}"
# part1 = MIMEText(message_content, "plain")
# message.attach(part1)
# context = ssl.create_default_context()
# with smtplib.SMTP("{{ SENDEMAIL_HOST }}", "{{ SENDEMAIL_PORT }}") as server:
# server.ehlo() # Can be omitted
# server.starttls(context=context)
# server.ehlo() # Can be omitted
# server.login("{{ SENDEMAIL_LOGIN }}", "{{ SENDEMAIL_PASSWORD }}")
# server.sendmail("{{ SENDEMAIL_LOGIN }}", "{{ SENDEMAIL_RECEIVER }}", message.as_string())
И всё это дело теперь развертываем
tasks:
- name: Install cron
ansible.builtin.apt:
name:
- cron
state: latest
update_cache: true
- name: Create scrypt for sync buckets
template:
src : ./files/configs/scripts/bucket-sync.sh.j2
dest: "/root/bucket-sync.sh"
mode: 0500
- name: Copy sent email script
template:
src : ./files/configs/scripts/sendemail.py
dest: "/root/sendemail.py"
mode: 0500
- name: Schedule script execution
ansible.builtin.cron:
name: "Run my script"
hour: "3"
minute: "0"
job: /root/bucket-sync.sh
state: present
Данный метод в зависимости от типа в переменной type
будет создавать разные части кода для резервного копирования.