﻿# 如何成功刪除 Amazon S3 Glacier 的 Vaults 備份資料

我大概在多年前開始把公司重要資料備份到 [Amazon S3 Glacier](https://aws.amazon.com/tw/glacier/) 雲端儲存空間，由於價格真的非常便宜，我大概備份了 500GB 左右的資料在上面，每個月的儲存成本僅僅 2 美元（約 60 新台幣），非常划算的異地備份方案。不過最近由於改變了備份策略，想把**儲存庫**(Vault)刪除，但卻意外遇到許多阻礙！

### 無法刪除有資料的 Vault 儲存庫

我原本以為刪除 Vault 很簡單，我資料都不要了，直接刪除不行嗎？沒錯，就是不行！

> AWS 的 `Vault` 可以想像成「保險箱」的概念，把要封存的檔案(archives)鎖在保險箱裡面。

從[官方文件](https://docs.aws.amazon.com/amazonglacier/latest/dev/deleting-vaults.html)得知，只要 Vault (保險箱) 還有任何 archives (檔案) 存在，就無法被刪除！

> 這裡的 `inventory` (庫存清單) 是告訴你保險箱中有多少檔案明細，預設是沒有**庫存清單**的，你必須建立一個**庫存清單取回作業** (inventory retrieval job)，才能得知有多少檔案在裡面，而每次執行一次大約要花 1 ~ 4 小時，看你的檔案數量多寡而定。

這裡判斷可刪除 Vault 的判斷標準，就是最後一次建立的 `inventory` (庫存清單) 是否還有任何 archives (檔案) 在清單裡面。

如果你試圖刪除 Vault，就會出現以下錯誤：

![Vault not empty or recently written to: arn:aws:glacier:us-west-2:111122223333:vaults/MyValutName](https://stwillblogassets.blob.core.windows.net/files/images/external/user-images.githubusercontent.com/5b2f6b3e9f49cdc5ca62-86036162-0cad2a00-ba70-11ea-933d-dbe789f1b4b7.png)

如果用 [AWS 命令列界面](https://aws.amazon.com/tw/cli/) 來執行刪除動作，也會出現以下錯誤：

```bash
$ aws glacier delete-vault --vault-name MyValutName --account-id 111122223333

An error occurred (InvalidParameterValueException) when calling the DeleteVault operation: Vault not empty or recently written to: arn:aws:glacier:us-west-2:111122223333:vaults/MyValutName
```

> 上述命令的 `--account-id` ([AWS 帳戶 ID](https://docs.aws.amazon.com/general/latest/gr/acct-identifiers.html)) 可以透過 `aws s3api list-buckets --query "Owner.ID"` 來取得。或是透過 [IAM Management Console](https://console.aws.amazon.com/iam/home?region=us-west-2#/security_credentials) 中「您的安全登入資料」的「帳戶識別符」頁籤展開後得知。你可以從 **AWS 管理控制台** 右上角點選姓名、點選「**我的安全登入資料**」進入查詢。

> 以下所有 CLI 命令，請將 `MyValutName` 修改成你的 **Vault 名稱**，將 `111122223333` 修改為你的 [AWS 帳戶 ID](https://docs.aws.amazon.com/general/latest/gr/acct-identifiers.html)！

### 如何成功刪除還有資料在內的 Vault 儲存庫

要能成功刪除**還有資料在內**的 Vault 儲存庫，基本上有三個步驟：

1.  啟動一個庫存清單取回作業 (inventory retrieval job)
    
    ```bash
    aws glacier initiate-job --vault-name MyValutName --account-id 111122223333 --job-parameters '{"Type": "inventory-retrieval"}'
    ```
    
    上述命令會取得一個 Job ID，預期結果如下：
    
    ```json
    {
        "location": "/111122223333/vaults/{ValutName}/jobs/{JobId}",
        "jobId": "{JobId}"
    }
    ```
    
    取得目前已經有的庫存清單取回作業與狀態 (取得 `JobId` 欄位)
    
    ```bash
    aws glacier list-jobs --vault-name MyValutName --account-id 111122223333
    ```
    
    取得庫存清單取回作業狀態，如果 `StatusCode` 出現 `InProgress` 就代表還沒完成！
    
    ```bash
    aws glacier describe-job --vault-name MyValutName --account-id 111122223333 --job-id "XOmFI3ySrMXe_SQqul_HQ2IgMNIXhZnBMXolaqI1gm6cHsebFk207hMkdd4im9rLeAzy-_uKhQoBU_WNl9ftEzz1-9xy"
    ```
    
    ```json
    {
        "InventoryRetrievalParameters": {
            "Format": "JSON"
        },
        "VaultARN": "VaultARN",
        "Completed": false,
        "JobId": "JOB_ID",
        "Action": "InventoryRetrieval",
        "CreationDate": "JOB建立日期",
        "StatusCode": "InProgress"
    }
    ```
    
2.  刪除每一個庫存清單中的檔案 (deleting each archive)
    
    先取得所有檔案清單 (從 `output.json` 檔案中取得每一個 `ArchiveId` 欄位)
    
    ```bash
    aws glacier get-job-output --vault-name MyValutName --account-id 111122223333 --job-id "XOmFI3ySrMXe_SQqul_HQ2IgMNIXhZnBMXolaqI1gm6cHsebFk207hMkdd4im9rLeAzy-_uKhQoBU_WNl9ftEzz1-9xy" output.json
    cat output.json | jq -r '(.ArchiveList[]).ArchiveId' > all_archive_ids.txt
    ```
    
    再利用以下命令透過 AWS CLI「逐筆刪除」每個封存檔案
    
    ```bash
    while read line; do echo "$line" && aws glacier delete-archive --vault-name MyValutName --account-id 111122223333 --archive-id "$line"; done < all_archive_ids.txt
    ```
    
3.  刪除儲存庫 (deleting the vault)
    
    你必須再次啟動一個庫存清單取回作業 (inventory retrieval job)
    
    ```bash
    aws glacier initiate-job --vault-name MyValutName --account-id 111122223333 --job-parameters '{"Type": "inventory-retrieval"}'
    ```
    
    然後還「不一定」可以刪除，我的經驗是再等個數個小時後，才能真正刪除 Vault 儲存庫！
    
    ```bash
    aws glacier delete-vault --vault-name MyValutName --account-id 111122223333
    ```
    

### 使用 [glacier-vault-remove](https://github.com/leeroybrun/glacier-vault-remove) 工具刪除 Vault 儲存庫

國外有個老兄用 Python 寫了一個小工具叫做 [glacier-vault-remove](https://github.com/leeroybrun/glacier-vault-remove)，作者算是十分佛心，把執行檔包成 Docker Image 了，因此你可以很輕易的透過 Docker 來執行，以下我就以 Docker 執行當作範例！

1.  先到 AWS Console 建立一組 Access Key / Secret Key
    
    [您的安全登入資料](https://console.aws.amazon.com/iam/home?region=us-west-2#/security_credentials)
    
2.  建立 `credentials.json` 認證檔，把你在 AWS 的 Access Key / Secret Key 寫成以下 JSON 檔案格式
    
    ```json
    {
        "AWSAccessKeyId": "AAAAAAAAAAAAAAAAAAAA",
        "AWSSecretKey":   "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
    }
    ```
    
3.  透過 Windows PowerShell 或 Bash 列出目前有幾個 Valuts
    
    ```powershell
    docker run --rm -v "$(pwd)\credentials.json:/app/credentials.json" leeroyb/glacier-vault-remove us-west-2 LIST
    ```
    
    > 請注意：**命令提示字元**不支援 `$(pwd)` 變數語法。
    
4.  要能成功刪除還有資料在內的 Vault 儲存庫，一樣也是三個步驟
    
    1.  啟動一個庫存清單取回作業 (inventory retrieval job)
        
        預設會先建立一個 Inventory Retrieving Job 取回所有檔案清單（會跑很久）
        
        ```bash
        docker run --rm -v G:\Projects\credentials.json:/app/credentials.json leeroyb/glacier-vault-remove us-west-2 MyValutName
        ```
        
        > 執行後可以按下 `Ctrl-C` 結束，因為你需要等待 inventory retrieval job 執行完畢，可能要等上幾個小時！
        
    2.  刪除每一個儲存庫中的檔案 (deleting each archive)
        
        設定一次跑 4 個 Process 執行刪除動作 (預設會自動找到最新的取回作業，並接續這份作業進行刪除工作)
        
        ```bash
        docker run --rm -v G:\Projects\credentials.json:/app/credentials.json leeroyb/glacier-vault-remove us-west-2 MyValutName 4
        ```
        
        你也可以先查詢目前正在執行的 Job ID
        
        ```bash
        docker run --rm -v G:\Projects\credentials.json:/app/credentials.json leeroyb/glacier-vault-remove us-west-2 MyValutName LIST
        ```
        
        也可以指定 Jobs ID 執行刪除動作
        
        ```bash
        docker run --rm -v G:\Projects\credentials.json:/app/credentials.json leeroyb/glacier-vault-remove us-west-2 MyValutName DEBUG 4 MOmFI3ySrMXa_Sqqul_HQ2IgMNIXhZnaMXolaqI1gm6cHsebFk307hMkdd4im9rLeAzy-_uKhQoBU_WNl9ftEzz1-9xy
        ```
        
    3.  刪除儲存庫 (deleting the vault)
        
        你必須再次啟動一個庫存清單取回作業 (inventory retrieval job)
        
        ```bash
        docker run --rm -v G:\Projects\credentials.json:/app/credentials.json leeroyb/glacier-vault-remove us-west-2 MyValutName NEW
        ```
        
        等 inventory retrieval job 完成後，上述命令也「不一定」可以立即刪除 Vault 儲存庫！建議等個半天左右，重新再跑一次，再不行就再等等！
        

### 總結

[Amazon S3 Glacier](https://aws.amazon.com/tw/glacier/) 雲端儲存空間確實是一個成本相當低廉的儲存服務，但是除了成本低之外，檔案取得的成本極高，取回的時間越短，成本就越高。因此，只有當你想把這份資料當成「異地備份」用途，而且取回的機會很低，才適合用這個服務來備份檔案。像我從 2014 年開始備份進去，至今沒有取出過一次，因為公司的 NAS 並沒有損毀，所以整體來講，我在「異地儲存」這件事情上，確實省了不少錢。

我最近就是覺得，如果要臨時取出一些重要檔案時，光是取得 `inventory` (庫存清單) 就要花上 1 ~ 4 小時，這個等待的時間確實讓人蠻焦慮的，所以決定將備份資料移到其他雲端平台(Azure)。

### 相關連結

-   [雲端資料存檔 | 長期物件儲存 | Amazon S3 Glacier](https://aws.amazon.com/tw/glacier/)
-   [Amazon S3 Simple Storage Service 定價 - Amazon Web Services](https://aws.amazon.com/tw/s3/pricing/)
-   [Deleting a Vault in Amazon S3 Glacier](https://docs.aws.amazon.com/amazonglacier/latest/dev/deleting-vaults.html)
-   [Deleting a Vault in Amazon S3 Glacier Using the AWS Command Line Interface - Amazon S3 Glacier](https://docs.aws.amazon.com/amazonglacier/latest/dev/deleting-vaults-cli.html)
-   [How to read file line by line in Bash script](https://linuxhint.com/read_file_line_by_line_bash/)
