[Azure][DevOps]針對 Azure DevOps 的 Repository 來做備份 ( 2/2 )

當確認好基本可以回復的週期是一個月,那接下來我們會來嘗試每個月備份一次 Repository ,並且設法讓備份下來的可以放回到 Azure DevOps 上。

當我們要開始準備做備份 Azure DevOps Repository 的備份的時候,首先我們會先來思考幾個問題

  1. 如何定時執行相關腳本來進行 ?
  2. 備份的儲存媒體和方式 ?
  3. 要怎麼將整個備份下來而非單一分支 ?
  4. 備份後是否可以重新放回到 Azure DevOps 上 ?

幾個問題確認好之後,就開始了相關爬文和測試,最後我選擇比較經濟一點的方式,建立一個單獨的 Azure DevOps , 將不同的備份設定成為定時執行的 Pipeline,透過 Git clone --mirror ( 說明連結 ) 的方式將來源的 Repository 儲存在 local 成為 bare 的模式,接著利用 zip 的方式壓縮之後存在 Azure Storage 上面,並且設定 blob 的保留期限,這樣看起來應該是可以初步達到我想要的功能。


首先先申請一個 Azure DevOps 的組織,這個部分算是很容易,但比較麻煩就是目前預設是不提供 Pipeline 的免費時數,因此要花點時間申請一下,這個部分可能因為周末的關係,我等了一個星期才申請下來。接下來我會使用 YAML 來建置 Pipeline,主要會用他是剛好最近再把我所有專案從原本使用圖形介面設定的 Pipeline,全部轉成 YAML 放在程式碼內,且透過 YAML 可以很方便的執行相關腳本,因此我就採用 YAML 來進行

schedules:
- cron: '0 16 * * *'
  displayName: Daily midnight backup
  branches:
    include:
    - main
  always: true

variables:
- name: gitURL
  value: 'https://{{YOUR PAT}}@dev.azure.com/{{YOUR ORGANAZITION}}/{{YOUR PROJECT}}/_git/{{YOUR REPOSITORY}}'
- name: projectName
  value: '{{YOUR PROJECT}}'
- name: serviceConnection
  value: '{{SERVICE CONNECTION}}'
- name: azureStorageName
  value: '{{Azure Storage Name}}'  
- name: azureContainerName
  value: '{{Azure Container Name}}'   
- name: today

pool:
  vmImage: windows-latest  # 如果不使用 Windows 環境則無法使用 AzCopy

steps:
- powershell: |
    $date = (Get-Date).AddHours(8).ToString('yyyyMMdd')
    Write-Host "##vso[task.setvariable variable=today;]$date"
  displayName: '設定變數'

- task: CmdLine@2
  inputs:
    script: |
      git clone --mirror $(gitURL) 
  displayName: '取得要備份的 Repository'

- task: DeleteFiles@1
  inputs:
    SourceFolder: '$(System.DefaultWorkingDirectory)'
    Contents: |
      *.md
      *.yml
  displayName: '刪除不必要的檔案'


- task: ArchiveFiles@2
  inputs:
    rootFolderOrFile: '$(System.DefaultWorkingDirectory)'
    includeRootFolder: false
    archiveType: 'zip'
    archiveFile: '$(Build.ArtifactStagingDirectory)/Backup.zip'
    replaceExistingArchive: true
  displayName: '備份 Repository 成為獨立的檔案'

- task: AzureFileCopy@5
  inputs:
    SourcePath: '$(Build.ArtifactStagingDirectory)/Backup.zip'
    azureSubscription: '$(serviceConnection)'
    Destination: 'AzureBlob'
    storage: '$(azureStorageName)'
    ContainerName: '$(azureContainerName)'
    BlobPrefix: '$(projectName)/$(projectName)_$(today).zip'
  displayName: '上傳到 Azure Storage'

這裡我將主要要連接的 Git 位置和 Azure 相關資訊定義在 variables 裡面的幾個變數內,這樣我就可以拿這個腳本調整成為不同的 Repository 來做備份。而這個範本檔案我也做了一點簡化,因此在我的環境下,每個 Project 內,可能會有多個 Repository,像是放 前端的Angular 專案 , 後端的 .Net 專案 , 資料庫的 SQL Database 專案 , 排程執行的 Azure Function 專案等,但這裡我就先簡化相關程序,在一個 YAML 內就先只做一件事情。

在這個 YAML 有幾個地方要注意

  1. Schedule:由於 Azure 上的時間都是採用 UTC 時間為主,因此如果要排程設定時間,要注意一下 cron 這個部分,我剛開始設定這個就弄錯了。而另外一個要注意的就是要記得加上 always 的屬性要設定為 true,否則預設要是 Repository 沒有修改就不會執行。
  2. Pool:大部分時候我使用 YAML 都會儘量採用 ubuntu-latest 的 image,但是因為在後續的處理我會在 task 的部分採用到 AzureFileCopy,因此這裡就要改用 windows-latest ,否則就要自己用 AZ 或者是 Powershell 來進行上傳 Azure Storage 的相關處理了。
  3. Variable:大部分的變數我都是採用固定值,一開始定義就指定預設值給他了。但在這裡我建立一個 today 的變數,這個會在後續利用 powershell 的處理,取得今天日期轉換成為字串塞入到這個變數內,這樣我上傳檔案的時候就可以用這個變數來指定檔案名稱了。
  4. Task - DeleteFiles : 主要是預設會把我用來處理的 YAML 腳本也會壓縮到我們的備份檔案內,因為 YAML 檔案內會有相關的 PAT 的機密資料,所以這裡我會多增加這個 task 把 yml 和 md 檔案給刪除。
  5. Task - AzureFileCopy : 這個也是我卡關卡很久的一個地方,因為早期只要設定好這些,並且在專案內的 Service Connections 裡面設定好就可以有足夠權限,但因為我是採用 5 的版本,從 4 之後的還要另外設定權限 , 這個部分後續再來說明。

針對前面第 5 項的部分,一般我們在 Azure DevOps 的專案上面可以建立 Service Connection 
 

以往在這一步設定完成之後,就會在 Azure 的訂閱或該訂閱的資源群組( 看有沒有指定資源群組名稱 ) 的存取控制中,建立一個「應用程式」的名稱,並設定具有「參與者」角色,因此當我們要佈署 Web App 或相關處理的時候,就可以很容易的將程式佈署上去。但這次我們在使用 AzureFileCopy 這個 Task 來將檔案上傳到 Azure Storage,原本還以為這樣就可以了,但沒有想到一值會出現權限不足的狀況。因此在這裡必須要將我們的我們剛剛所產生的應用程式,在我們所要使用的 Storage 上的 IAM 設定,要再多增加指定「儲存體 Blob 資料參與者」的角色,這樣我們在 YAML 執行的時候才可以正常上傳。

 


當我們真的遇到狀況了,需要使用該備份檔案的時候,首先我們要先在 Azure DevOps 上面建立一個空的 Repository,這個 Repository 不一定要在哪個 Project 上面,只要新建立是空的就可以了。

接著我們可以從備份的 Storage 上面將檔案給抓下來,解開壓縮檔進入目錄後可以看到類似以下的檔案

開啟 CMD 的視窗後,因為這個備份檔案還記錄著原本的來源位置,所以我們使用 git remote remove 的指令將 origin 給刪除

git remote remove origin

接下來我們就可以將目前的 Git 資料給推回去 Azure DevOps 上面了,這裡我們會使用到兩個指令

git remote add origin {{新的 REPOSITORY 的位址}}

git push --mirror origin

當完成之後,我們回到 Azure DevOps 上,查看剛才建立的 Repository,就可以看到相關的分支和說明都可以查看到了。

 


 


 

 

 

 

 


正常來說這些 git 指令都很基本,但以往自己太懶惰了,多半使用 git 都是用 UI 介面來完成,忽然要完全自己打指令,就有點手忙腳亂,好在線上說明的資料蠻清楚的,除了 Azure FileCopy 那個部分卡關之外,另外一個就是遇到 Project 和 Repository 的名稱中間有空白,這個在 YAML 使用 Powershell 或者是 Cmd 去處理的時候要特別注意,雖然空白字串透過 URLEncode 出來是 %20,但是 PowerShell 和 Cmd 在處理的時候,要特別將 %20 換成 %%20,否則相關名稱會有問題,這一點也是自己耍白癡,沒事搞個空白在中間,害我自己浪費一堆時間在處理這個部分。