Kopia zapasowa to rzecz święta!

Python wrz 04, 2020

Zdarzyło Ci się poznać osobę, która totalnie olewa kopie zapasowe? Ja poznałem ich zbyt wiele, usłyszałem też mnóstwo historii o skutkach takich decyzji, dlatego mam chyba drobną obsesję na tym punkcie.

Faceci też czasem płaczą

Jeżeli jesteś osobą, która nie zwraca uwagi na robienie backupów i sposób ich przechowywania, to może kilka historii z życia wziętych zmieni Twoje podejście :)

Nie będę się rozpisywać i wchodzić w szczegóły, ale dwie historie zapadły mi dość dobrze w pamięć. Klient posiadał dwie bazy danych - jedna była produkcyjna, a druga testowa. Różniły się one tym, że baza testowa w nazwie na końcu miała kropkę.

Po pewnym czasie stwierdzili, że nie potrzebują już bazy testowej, więc poprosili firmę, która ich wdrażała, aby ją usunęli. Efekt był taki, że przez pomyłkę zniknęła baza produkcyjna. Fuckup można byłoby wybaczyć, bo bazy w nazwach różniły się głupią kropką, ale od momentu wdrożenia nie wykonano nawet jednej kopii. Osoba odpowiedzialna za usunięcie pliku niemalże płakała mi do słuchawki.

Drugi przypadek dotyczył ofiary Ransomware, było to biuro rachunkowe, posiadające bazy danych setki swoich klientów, łącznie kilkaset GB danych. Tutaj firma pokazała klasę, bo co kilka dni wykonywana była kopia wszystkich baz - brawo! Jest tylko jedno ale - zapisywali je na tym samym dysku co instancja SQL, dosłownie folder obok... Wiem, że zdecydowali zapłacić za odszyfrowanie plików, nie chcę sobie nawet wyobrażać ile ich to kosztowało...

Ghost a kopia zapasowa

Blog jest jeszcze w totalnych powijakach i strata jednego czy kilku wpisów nie brzmi tragicznie, natomiast jestem na tyle leniwą osobą, że nie chciałoby mi się kolejny raz pisać postu na dany temat - nie ma to jak pierwsze natchnienie twórcze :)

Wracając do tematu kopii zapasowych - musiałem zbadać temat jak wygląda dokładnie sprawa kopii zapasowych platformy Ghost. Niestety jestem trochę zawiedziony, bo nie jest to takie kolorowe jakbym chciał.

Ghost wykorzystuje bazę danych MySQL - wszystko byłoby w porządku, jakby korzystał tylko z niej... Poza tym, dochodzi jeszcze plik Json, który zawiera ustawienia platformy oraz posty. Nie wiem kto wpadł na pomysł tego rozwiązania - ja w nim sensu nie widzę, no ale cóż.

Założyłem temat na forum Ghost z pytaniem o najlepszą praktykę do przetrzymywania kopii zapasowych. W odpowiedzi dostałem scenariusz, którego chyba najbardziej się obawiałem - kopia bazy MySQL oraz pliki samej platformy... Link do tego tematu możesz zobaczyć tutaj.

W sumie wpadłem na jeden pomysł jak można to zrobić i niewiele się zastanawiając przystąpiłem do działania. Doświadczenie z Bash już mam, a chciałem zaczerpnąć czegoś nowego, także mój wybór padł na napisanie skryptu w Python który będzie się łączył do VPS, wykonywał kopię bazy MySQL oraz plików platformy.

Może teraz wielkość tych plików nie jest jakaś ogromna, ale z czasem może rosnąć, więc skrypt dodatkowo pakuje wszystko w tar.gz. Szybki research wskazał Paramiko, jako odpowiednią bibliotekę do połączenia się z VPS z wykorzystaniem Pythona. Wiem co i jak chcę zrobić, więc czas przystąpić do pracy.

Nigdy z Python nie miałem żadnego kontaktu poza pip3 install, dlatego pierwsze 30 minut spędziłem na nauce podstaw Pythona - w sumie mogę polecić ich dokumentację. Napisanie skryptu zajęło mi jakieś 10 minut z przetestowaniem - nic dziwnego, bo robi bardzo proste operacje, niemniej jednak jestem zadowolony, bo dotknąłem Pythona, a skrypt robi to co chciałem.

Dodatkowo, wrzuciłem go CRON'a na dwóch maszynach, aby wykonywał te kopie co X czasu, także nie muszę go odpalać ręcznie. Poniżej wrzucam skrypt, wiem że wygląda brzydko, ale zapiernicz w pracy skutecznie odwiódł mnie od próby refactoru. Póki co ważne, że działa, a na dniach postaram się go odrobinę upiększyć :)

import paramiko


def start_connection():
    username = 'root'
    password = ''
    port = 22
    vps_ip = '127.0.0.1'
    ssh_key = '/home/user/.ssh/id_rsa'

    ssh = paramiko.SSHClient()
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    my_rsa_key = paramiko.RSAKey.from_private_key_file(ssh_key)

    session = ssh.connect(vps_ip, port, username, password, pkey=my_rsa_key)

    remote_cmd_db = 'mysqldump -pPassword --databases blog_db | gzip -c > /home/ghostblog/backup_blog/blog_db.sql.gz'
    remote_cmd_app = 'cd /var/www && tar -zcvf ghostblog.tar.gz ghostblog/'
    ssh.exec_command(remote_cmd_db)
    ssh.exec_command(remote_cmd_app)

    sftp = ssh.open_sftp()
    localpathdb = '/home/user/Backups/Ghost/blog_db.sql.gz'
    remotepathdb = '/home/ghostblog/backup_blog/blog_db.sql.gz'
    sftp.get(remotepathdb, localpathdb)

    localpathapp = '/home/user/Backups/Ghost/ghostblog.tar.gz'
    remotepathapp = '/var/www/ghostblog.tar.gz'
    sftp.get(remotepathapp, localpathapp)

    print("Ghost database backup downloaded!".format(sftp.close()))
    print("Connection to VPS closed!".format(ssh.close()))


if __name__ == '__main__':
    start_connection()

I co dalej?

W poprzednim poście pisałem, że postawię aplikację PostHog i dorzucę do niej kilka innych aplikacji. Plan mi się odrobinę zmienił, gdyż statystyki będę póki co ciągnąć z Google Analytics oraz chcę przećwiczyć pisanie w Go.

Chcę napisać aplikację, która za pośrednictwem API będzie pobierać pewne informacje, a następnie wyświetli je za pośrednictwem Grafana. Aplikacja składa się dopiero z kilku linijek testowych, dlatego nie będę póki co pokazywać kodu, ale mogę zdradzić rąbek tajemnicy, że dane będą dotyczyć EVE Online. Oczywiście nie zapominam o Jenkins oraz Prometheus :) Plany planami, zobaczymy co czas pokaże... Do następnego!

Tagi

Bartłomiej Komendarczuk

Newbie DevOps :)

Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.