Merhaba arkadaşlar, bu yazımda heybooster‘ın veritabanı yedeği için yazdığımız bir koddan bahsedeceğim. Bu kod veritabanımızı Google Cloud Storageda açtığımız bir bucketa yolluyor. Detayları aşağıda konuşalım :)



Neden Mongodb?


mongodb-logo

Bu yazı tek tek neyin ne olduğunu açıklayan bir yazı olmayacak ancak kısaca yazıda geçen teknolojilerden bahsetmek istiyorum. Mongodb birçok programlama diline destek veren, ölçeklenebilir açık kaynak kodlu bir NoSQL veritabanıdır. Verileri SQL veritabanlarından farklı olarak JSON formatta tutmaktadır. Bu da bizim istediğimiz bir özellik. Mongodb NoSQL yapısıyla Heybooster’ın tüm ihtiyaçlarına çözüm sunuyordu. Bu yüzden veritabanı olarak Mongodb’yi tercih ettik.



Neden Google Cloud Storage?

gcs-logo

Yazının kalanında “Google Cloud Storage” yerine “GCS” yazacağım. Öncelikle ne olduğundan bahsedelim. Wikipedia’a göre aşağıdaki gibi bir tanımı var. Bu tanım ne olduğunu anlamaya yetecektir diye düşünüyorum.

Google Cloud Storage, Google Cloud Platform altyapısındaki verileri depolamak ve bunlara erişmek için RESTful bir çevrimiçi dosya depolama web hizmetidir. Hizmet, Google’ın bulutunun performansını ve ölçeklenebilirliğini gelişmiş güvenlik ve paylaşım yetenekleriyle birleştirir

Peki Biz Ne İstiyorduk?

who-we-are-meme

Gelelim asıl konuya, biz ne istiyorduk? Bizim isteğimiz gün içinde oluşan veritabanındaki bazı verileri yedeklemek. Yani günlük veritabanı yedeği diyebiliriz. Peki bunu nasıl yapacağız? Mongodb database tool içinde bulunan mongodump programını kullanarak veritabanını export edebileceğiz. Sonra? Sonrası için ilk planımız Digital Ocean‘dan bir ubuntu makine açıp orda yedeklemekti ancak GCS’a göre çok daha pahalıya geliyordu. Bizim kararımız bu noktada netleşti.



Operasyon Başlasın 👊

who-we-are-meme

Kararlarımızı verdikten sonra gereklilikleri sağlayıp kod yazmaya başladık. Kodu yazı sonunda paylaşacağım ama önce gerekliliklerden kısaca bahsedeyim. Verilerini elde edeceğimiz veritabanının username, password gibi bilgilerine ihtiyacımız var. Sonrasında GCS için credential işlemleri ve gerekli GCS kütüphanesini yüklememiz gerekiyor. Bunları yaptıktan sonra kodumuzu yazabiliriz.

Gelelim koda.

import os
from dotenv import load_dotenv
from google.cloud import storage
from bson.objectid import ObjectId
from datetime import datetime
import time
from os import listdir
from os.path import isfile, join

load_dotenv()

DB_USER = os.environ['DB_USER']
DB_PASSWORD = os.environ['DB_PASSWORD']
DB_NAME = os.environ['DB_NAME']
BUCKET_NAME = os.environ['BUCKET_NAME']


def mongodump_reports(archive_file):
    today = datetime.today()
    date = datetime(today.year, today.month, today.day, 0, 0, 0)
    # Required for ignoring the documents older than 1 day
    idMin = ObjectId(format(int(date.timestamp()), 'x') + "0000000000000000")

    # Using --out options to fix the file name
    DB_DUMP_COMMAND = """
    mongodump --uri "mongodb://{}:{}@heybooster-shard-00-00-yue91.mongodb.net:27017,heybooster-shard-00-01-yue91.mongodb.net:27017,heybooster-shard-00-02-yue91.mongodb.net:27017/{}?authSource=admin&replicaSet=heybooster-shard-0&retryWrites=true&ssl=true&w=majority" --collection reports --out {} --query""".format(
        DB_USER, DB_PASSWORD, DB_NAME, archive_file) + """ '{"_id": {"$gte": ObjectId("'""" + str(idMin) + """'")}}'"""
    os.system(DB_DUMP_COMMAND)


def list_blobs(bucket_name):
    """Lists all the blobs in the bucket."""

    storage_client = storage.Client.from_service_account_json(
        'credentials.json')

    # Note: Client.list_blobs requires at least package version 1.17.0.
    return storage_client.list_blobs(bucket_name)


def upload_backup_file(bucket_name, archive_file, destination_blob):
    """Uploads a file to the bucket."""

    storage_client = storage.Client.from_service_account_json(
        'credentials.json')
    bucket = storage_client.bucket(bucket_name)

    current = os.getcwd()
    a = listdir(archive_file)[0]
    path = current + "/" + archive_file + "/" + a
    files = [f for f in listdir(path) if isfile(join(path, f))]
    for file in files:
        archive_file = path + '/' + file
        destination_blob = destination_blob + '_' + file
        blob = bucket.blob(destination_blob)
        blob.upload_from_filename(archive_file)

    print("File {} uploaded to {}.".format(archive_file, destination_blob))


def remove_old_reports(bucket_name, archive_file):
    """Deletes a blob from the bucket."""

    storage_client = storage.Client.from_service_account_json(
        'credentials.json')

    bucket = storage_client.bucket(bucket_name)
    blob = bucket.blob(archive_file)
    blob.delete()

    print("Blob {} deleted.".format(archive_file))


def start():
    archive_file_name = "mongo_backup_" + time.strftime('%x').replace('/', '_')
    destination_blob_name = archive_file_name
    mongodump_reports(archive_file_name)
    upload_backup_file(BUCKET_NAME, archive_file_name, destination_blob_name)


if __name__ == '__main__':
    start()

Kod karmaşık olmadığı için anlaşılır diye düşünüyorum. Kısaca bahsedeyim, veritabanımızı export eden fonksiyon “mongodump_export”un yaptığı şey mongodump komutunu çalıştırmak. Başka bir görevi yok. “list_blobs” isimli fonksiyon ise bucket içindeki dosyaları listelememizi sağlıyor. “upload_backup_file” ise istediğimiz dosyanın bucketa yüklenmesini sağlıyor. “remove_old_file” isminden anlaşıldığı gibi eski dosyaları silmemizi sağlıyor. Tabi bunu dosya ismini parametre olarak alarak yapıyor.

Kodumuz bundan ibaret. Serverda bu kodun günlük olarak çalışmasını sağlamamız yeterli. Anlamadığınız bir yer varsa sorabilirsiniz. Umarım işinize yaramıştır. İyi günler dilerim :)