[Azure][SQL]如何定時將存放在 Storage 內的歷史備份給清除

Azure Storage 的 Container 下可以存放 Block Blobs 、Append Blobs 和 Page Blobs,這幾種不同的格式處理的方式也會有所差異。

前一陣子在處理資料庫和程式的備份,基本上都會利用 Azure Storage 相對便宜的存放空間來做資料的保存,因此只要設定好相關的設定,就可以存放過去了。而在處理的過程中,剛好手邊還有一些舊的系統,因為有採用到一些特殊的功能,因此還沒有辦法直接將這些轉換成為 Azure SQL Database,只能繼續存放在 Azure SQL VM 上面,因此就決定採用 SQL Server 的維護計畫,將資料庫用備份到 URL 的方式,將備份檔案存放在 Azure Storage 上面。

而有使用過維護計畫的朋友,相信對於「維護清除工作」來說都不陌生,以往我們在地端的時候,很長會利用這個 Task,將歷史的備份檔案或者是維護計畫的 Log 資料給清除。但是這個 Task 雖然功能不錯,但當我們將備份存放在 Azure Storage 上面的時候,因為這裡沒有辦法類似備份,可以指定一個 Azure Storage 的認證去連線,他只能指定主機所能連結的檔案路徑來做處理

那如果我們不使用維護計畫來做刪除,Azure 的 Storage 上面有相關服務可以來做處理嗎 ? 在 Azure Storage 上面,的確是有個類似的功能,稱為「生命週期管理」

在這個功能裡面看起來我們可以設定一些保存的規則,就可以利用這個服務來協助我們定時清除歷史的 Blob 了。正常的情況下,如果您設定好規則,則該服務會在每天 UTC 0:0:0 的時候去做檢查,因此如果您設定好之後,會需要等到隔天早上的 8:00,才有辦法看到資料有被處理。

但除了上述的執行時間外,這個生命週期管理只能用在 Block Blobs 和 Append Blobs 這兩種格式的 Blob 上面。但很不幸的,當我們使用 SQL Server 的維護計畫來做備份的時候,存放在 Storage 上面會採用 Page Blobs,就恰好是「生命週期管理」所不支援的,那我們該如何來處理呢 ?

基本上如果使用 Azure Automation 或者是 Azure Function,都可以排程來做相關處理。但因為我自己比較習慣用 Azure Function,因此就採用 Azure Function 來寫一段程式來進行處理。下面這段程式基本上我比較偷懶一點,先將 RunCleaner 的那段從其他處理中抽過來,改成用迴圈去將 Storage 內所有 Container 下面的檔案找出來,然後判斷建立日期是否小於一年前,如果是的話就將相關檔案給刪除。而至於時間我設定為 「0 0 16 * * * 」,則是希望是每天的凌晨來進行處理。

[FunctionName("DailyClean")]
public void Run([TimerTrigger("0 0 16 * * *")]TimerInfo myTimer, ILogger log)
{
    log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");
    RunCleaner(log);
}

private bool RunCleaner(ILogger log)
{
    string storageConnectionString = Environment.GetEnvironmentVariable("BackupAzureStorage");
    log.LogInformation($"Storage connection string is : {storageConnectionString}");
    BlobServiceClient blobServiceClient = new BlobServiceClient(storageConnectionString);
    foreach (BlobContainerItem container in blobServiceClient.GetBlobContainers())
    {
        BlobContainerClient containerClient = blobServiceClient.GetBlobContainerClient(container.Name);

        foreach (BlobItem blob in containerClient.GetBlobs())
        {
            if (blob.Properties.CreatedOn <= DateTimeOffset.UtcNow.AddDays(-365))
            {
                BlobClient blobClient = containerClient.GetBlobClient(blob.Name);
                blobClient.DeleteIfExists();
                log.LogInformation($"Deleted blob: {blob.Name}");
            }
        }
    }
    return true;
}

透過上述的程式,將這個編譯好上傳到 Azure Function,就可以有一個免費的服務來定時協助我來清除檔案。當然上述只是個簡單的範例,您也可以再多加上像是 LINE Notion,這樣就可以每天透過通知,知道服務是否有正常的執行了。

當然這個不見得一定是個好方法,再跟朋友討論的過程中,朋友提到那為什麼我們不直接把備份改成 Block Blobs,那不就連程式也都不用寫了嗎 ? 以目前來說因為維護計畫並沒有辦法做這個調整,但是您也可以改成先將備份存在 Local 的磁碟上面,等備份完畢產生出備份檔之後,再使用 AzCopy 的功用程式,或者是呼叫 PowerShell 使用 Set-AzStorageBlobContent 來上傳檔案,這樣就可以指定放上去的時候是採用 Block Blobs,也就可以順利地使用「生命週期管理」了。

反正不管黑貓白貓,會抓老鼠的就是好貓,因此就看您喜歡用哪個方式, 自己順手就好囉。