iT邦幫忙

2023 iThome 鐵人賽

DAY 18
0

今天開始進入 Part 2 的內容——進階 Pulumi 技巧。第一篇文章就要來介紹狀態 (State) 管理。
在 Pulumi 中,所有被建立資源 (Resource) 的資訊都會被儲存到 state 中。下一次執行 pulumi up 時,就會直接根據這個 state 的內容去規劃有哪些資源需要變更。

外部狀態變更,同步回 Pulumi

但是有時難免會直接修改到 Provider 的 Resource,例如直接到 AWS 上進行一些資源的調整。在這個狀況下,Pulumi 對這些變更是完全一無所知的。縱使我們在 Pulumi 專案裡加上一樣的變更,Pulumi 還是會認為這些變更尚未做過。

例如我們直接在 EC2 Instance 上更改 Tag,新增了一個 Tag,接著又回到 Pulumi 專案中為 EC2 Instance 加上這個 Tag。在執行 pulumi up 時,仍然會看到 Pulumi 會想要對 EC2 新增 Tag。

這種重複更改資源的行為是很危險的,並不是任何資源的操作都支援重複的修改值。因此就需要有個方式可以將 Cloud Provider 的變更同步至 State 中了。

同步 State 指令

使用 pulumi refresh 指令就可以滿足這個需求。pulumi refresh 會將目前 Cloud Provider 最新的狀態內容與 Pulumi State 進行同步。

pulumi refresh 指令執行完後,會列出所有有變更的資源,這時可以透過選擇 details 讓 Pulumi 顯示異動的內容。例如以下範例就是直接在 AWS 上變更了 VPC 的 Tag,因此 Pulumi 要將這個變更 (新增新的 Tag) 同步回 State 中。

     Type                                    Name                                    Plan       Info
     pulumi:pulumi:Stack                     aws-vpc-ts-dev
     └─ pulumi-practice:vpc:Vpc              project-vpc
 ~      └─ aws:ec2:Vpc                       project-vpc-vpc                         update     [diff: ~tags,tagsAll]


Resources:
    ~ 1 to update
    13 unchanged

Do you want to perform this refresh?
No resources will be modified as part of this refresh; just your stack's state will be.
 details
  pulumi:pulumi:Stack: (same)
    [urn=urn:pulumi:dev::aws-vpc-ts::pulumi:pulumi:Stack::aws-vpc-ts-dev]
        ~ aws:ec2/vpc:Vpc: (update)
            [id=vpc-01fd78fa1db88b084]
            [urn=urn:pulumi:dev::aws-vpc-ts::pulumi-practice:vpc:Vpc$aws:ec2/vpc:Vpc::project-vpc-vpc]
            [provider=urn:pulumi:dev::aws-vpc-ts::pulumi:providers:aws::default_5_42_0::c8fcc026-699b-4164-bc25-2b578c6273c2]
            --outputs:--
          ~ tags                            : {
              + Group: "state-change-outside"
                Name : "aws-vpc-ts-dev-vpc"
            }
          ~ tagsAll                         : {
              + Group: "state-change-outside"
                Name : "aws-vpc-ts-dev-vpc"
            }

Do you want to perform this refresh?
No resources will be modified as part of this refresh; just your stack's state will be.
  [Use arrows to move, type to filter]
  yes
> no
  details

同步完後,可以再執行一次 pulumi up,就會看到 Pulumi 想要刪除剛剛同步的 Tag。這是因為在我們的 Pulumi 專案中,VPC 並沒有這個 Tag。此時我們可以修改專案中的 VPC,增加這個手動變更的 Tag 值;或是不修改專案,讓 Pulumi 把雲端資源中手動變更的部分移除。

Pulumi 的資源名稱取錯了

接著討論一個很常發生的問題,也就是 Pulumi 中的資源名稱取錯,或是想變更要怎麼辦。

在 Pulumi 中,新建資源時,第一個參數即是 Pulumi 的 Resource name,Pulumi 內部是使用這個名字與 Resource Type 等資訊來辨識是否為相同資源的。

以下範例中,我將原本的 my-vpc-a 更名為 my-vpc-b,這就會導致 Pulumi 認為我刪除了 my-vpc-a 並想重新建立 my-vpc-b

如果這是可以接受重建資源的專案 (例如 k8s 的 deployment 等,stateless 的 pod),那問題可能不大。但如果是像範例中的 VPC 重建,可能就茲事體大了。

     Type                 Name            Plan
     pulumi:pulumi:Stack  aws-vpc-ts-dev
 +   ├─ aws:ec2:Vpc       my-vpc-b        create
 -   └─ aws:ec2:Vpc       my-vpc-a        delete


Resources:
    + 1 to create
    - 1 to delete
    2 changes. 1 unchanged

因此需要有個辦法來將原本的 my-vpc-a 變成 my-vpc-b,或是讓 Pulumi 認知到,這是在做更名,而不是重建。

使用 pulumi state rename

Pulumi 內提供了 state 操作的 CLI command,以下是 pulumi state rename 的使用方式。需要指定一個 resource URN 與新的名字。

Usage:
  pulumi state rename [resource URN] [new name] [flags]

新的名字不是問題,但這個 resource URN 是什麼?要從哪邊得到這個資訊呢?

URN

前面提到 Pulumi 是透過資源的 name 來追蹤是否為同一個資源這句話可能不是如此精確,實際上應該是透過 Resource URN 來辨識資源。URN 的組成包含了 stack name、project name、parent resource name、resource type、resource name 等合併為一個 URN。

大部分的狀況下都是用不到 URN 的,但要用的時候要怎麼找到這個值呢?總不可能要自己組合吧。

可以透過之前提到的 pulumi stack 指令來查看資源的 URN。在這個指令後加上 --show-urns 或是 -u 參數,即可顯示所有資源的 URN。

Current stack resources (3):
    TYPE                     NAME
    pulumi:pulumi:Stack      aws-vpc-ts-dev
    │  URN: urn:pulumi:dev::aws-vpc-ts::pulumi:pulumi:Stack::aws-vpc-ts-dev
    ├─ aws:ec2/vpc:Vpc       my-vpc-a
    │     URN: urn:pulumi:dev::aws-vpc-ts::aws:ec2/vpc:Vpc::my-vpc-a
    └─ pulumi:providers:aws  default_5_42_0
          URN: urn:pulumi:dev::aws-vpc-ts::pulumi:providers:aws::default_5_42_0

從上面的輸出中可以看到,my-vpc-a 的 URN 為:urn:pulumi:dev::aws-vpc-ts::aws:ec2/vpc:Vpc::my-vpc-a,其中就包含了前述的資訊。

有了 URN 後,就可以執行 pulumi state rename 重新命名 my-vpc-amy-vpc-b

$ pulumi state rename "urn:pulumi:dev::aws-vpc-ts::aws:ec2/vpc:Vpc::my-vpc-a" my-vpc-b
warning: This command will edit your stack's state directly. Confirm? Yes
Resource renamed

指令下去後,會有個警告,這個指令相當於直接編輯 state 檔案,如果沒搞好可能會讓 Pulumi 的狀態大亂。

不透過 CLI 變更資源的名字

透過 CLI 變更資源有時比較危險且麻煩,有沒有其他方式可以變更資源的名稱呢?

有的,可以直接透過建立資源時的第三個參數——opts 中的別名 (aliases)。

例如以下範例,再度變更 VPC 的名字為 my-vpc-c,並且在 aliases 中設定別名為 my-vpc-b。這邊可以注意到,是直接將想要修改的名字設定為資源的名子,並在別名中指定舊的名字

  new aws.ec2.Vpc('my-vpc-c', {
    cidrBlock: '10.100.0.0/16',
    tags: {
      Name: 'my-vpc-a',
    }
  }, {
    aliases: [
      {
        name: "my-vpc-b"
      }
    ]
  });

當完成這樣的設定後,執行 pulumi up 會看不到任何的資源變更,這時選擇 yes 讓 Pulumi 更新狀態。更新完後透過 pulumi stack 指令,就可以看到資源顯示為新的名稱了!

aliases 為一個陣列,如果要再更名的話,可以直接在陣列中增加新的資料即可。

匯入既有的資源

如果需要將已經存在於雲端的資源匯入至 Pulumi 中操作,則可以透過 pulumi import 的功能完成這個任務。

pulumi import 的指令用法如下,由於每個 Provider 的 Type 都不同,最好的方式就是直接到 Provider 的文件中,每個資源的最下方都會有如何 import 的資訊。

Usage:
  pulumi import [type] [name] [id] [flags]

例如 AWS VPC 的 import 方式為使用 vpc-id,type 為 aws:ec2/vpc:Vpc摘錄文件如下:

Import
Using pulumi import, import VPCs using the VPC id. For example:

pulumi import aws:ec2/vpc:Vpc test_vpc vpc-a01106c2

這時就可以來嘗試 import 看看了!

$ pulumi import aws:ec2/vpc:Vpc imported_vpc vpc-01c472b175539534e
Previewing import (dev)

     Type                 Name            Plan
     pulumi:pulumi:Stack  aws-vpc-ts-dev
 =   └─ aws:ec2:Vpc       imported_vpc    import


Resources:
    = 1 to import
    1 unchanged

Do you want to perform this import? details
  pulumi:pulumi:Stack: (same)
    [urn=urn:pulumi:dev::aws-vpc-ts::pulumi:pulumi:Stack::aws-vpc-ts-dev]
    = aws:ec2/vpc:Vpc: (import) 🔒
        [id=vpc-01c472b175539534e]
        [urn=urn:pulumi:dev::aws-vpc-ts::aws:ec2/vpc:Vpc::imported_vpc]
        [provider=urn:pulumi:dev::aws-vpc-ts::pulumi:providers:aws::default_5_42_0::e2f7e354-5261-4bbe-8bef-34d993f55670]
        cidrBlock : "10.100.0.0/16"
        tags      : {
            Name      : "my-vpc-a"
        }

Do you want to perform this import? yes
Importing (dev)

     Type                 Name            Status
     pulumi:pulumi:Stack  aws-vpc-ts-dev
 =   └─ aws:ec2:Vpc       imported_vpc    imported (1s)


Resources:
    = 1 imported
    1 unchanged

Duration: 2s

Please copy the following code into your Pulumi application. Not doing so
will cause Pulumi to report that an update will happen on the next update command.

Please note that the imported resources are marked as protected. To destroy them
you will need to remove the `protect` option and run `pulumi update` *before*
the destroy will take effect.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const imported_vpc = new aws.ec2.Vpc("imported_vpc", {
    cidrBlock: "10.100.0.0/16",
    tags: {
        Name: "my-vpc-a",
    },
}, {
    protect: true,
});

在 Pulumi 中,最貼心的大概就是 import 完,還會提供程式碼,因為 import 完的資源如果沒有對應的程式碼的話,下次執行 pulumi up 就會被認為這個資源需要被刪除。

透過程式碼匯入資源

如果覺得每次用 CLI 匯入資源都要查詢文件,才能知道匯入的 Type 很麻煩的話,還有另一個方式。就是透過程式碼來匯入資源。

在資源的 opts 參數中,有一個 import 的選項,可以填入要匯入資源的 id。例如以下範例,在執行 pulumi up 時,就會變成 import 模式,將資源匯入 state。但使用這個方法要特別注意,程式碼的所有 argument 都必須與雲端中的資源相符,如果有差異的話,就會匯入失敗。

  new aws.ec2.Vpc('imported_vpc', {
    cidrBlock: '10.100.0.0/16'
  }, {
    import: 'vpc-01c472b175539534e'
  });

從 state 中刪除資源

可以透過 pulumi state delete [resource URN] 從 state 刪除資源,這邊要特別注意,是從 state 中刪除。這僅是用來改變 Pulumi 的狀態用的,並不會動到雲端的資源。

通常會用到這個功能都是使用了 pulumi import 指令將既有的資源放入 Pulumi state 中,後來又發現放錯了,或是這資源不需要放入 Pulumi state。希望將資源從 state 中刪除時使用。


上一篇
[Day 17] 實戰練習(4) - 在 EKS 中部署應用程式
下一篇
[Day 19] Pulumi 的 Backend 與 migration
系列文
30 天學習 Pulumi:用各種程式語言控制雲端資源30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言