上一篇『初探 Pulumi 上傳靜態網站到 AWS S3 (一) 』主要介紹 Pulumi 基本使用方式,而本篇會延續上一篇教學把剩下的章節教完,底下是本篇會涵蓋的章節內容:
設定 Pulumi Stack 環境變數 建立第二個 Pulumi Stack 環境 刪除 Pulumi Stack 環境 讓開發者可以自由新增各種不同環境,像是 Testing 或 Develop 環境,以及該如何動態帶入不同環境的變數內容,最後可以透過單一指令將全部資源刪除。
教學影片 VIDEO
00:00 Pulumi 教學內容簡介 (Stack 介紹) 01:04 設定 Pulumi Stack 環境變數 05:04 用 Go 語言讀取多個檔案上傳到 AWS S3 08:04 用 Pulumi 建立多個開發環境 09:00 用 pulumi config 設定環境參數 12:35 如何清除單一 Pulumi Stack 環境 15:30 Pulumi 管理 AWS 心得 設定 Pulumi Stack 環境變數 大家可以看到,現在所有 main.go
的程式碼,都是直接 hardcode 的,那怎麼透過一些環境變數來動態改變設定呢?這時候可以透過 pulumi config 指令來調整喔,底下來看看怎麼實作,假設我們要讀取的 index.html 放在其他目錄底下,該怎麼動態調整?
步驟一: 撰寫讀取 Config 函式 1
2
3
4
5
6
7
8
9
10
11
func getEnv (ctx *pulumi.Context, key string , fallback ...string ) string {
if value, ok := ctx.GetConfig (key); ok {
return value
}
if len (fallback) > 0 {
return fallback[0 ]
}
return ""
}
pulumi 的 context 內有一個讀取環境變數函式叫 GetConfig
,接著我們在設計一個 fallback 當作 default 回傳值。底下設定一個變數 s3:siteDir
1
pulumi config set s3:siteDir production
打開 Pulumi.dev.yaml
可以看到
1
2
3
4
config :
aws:profile : demo
aws:region : ap-northeast-1
s3:siteDir : production
接著將程式碼改成如下:
1
2
3
4
5
6
7
8
site := getEnv (ctx, "s3:siteDir" , "content" )
index := path.Join (site, "index.html" )
_, err = s3.NewBucketObject (ctx, "index.html" , &s3.BucketObjectArgs{
Bucket: bucket.Bucket,
Source: pulumi.NewFileAsset (index),
Acl: pulumi.String ("public-read" ),
ContentType: pulumi.String (mime.TypeByExtension (path.Ext (index))),
})
步驟二: 更新 Infrastructure 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ pulumi up
Previewing update (dev)
View Live: https://app.pulumi.com/appleboy/demo/dev/previews/d76d2f9b-16c8-4bfd-820d-d5368d29f592
Type Name Plan Info
pulumi:pulumi:Stack demo-dev
~ └─ aws:s3:BucketObject index.html update [diff: ~source]
Resources:
~ 1 to update
2 unchanged
Do you want to perform this update? details
pulumi:pulumi:Stack: (same)
[urn =urn:pulumi:dev::demo::pulumi:pulumi:Stack::demo-dev]
~ aws:s3/bucketObject:BucketObject: (update)
[id =index.html]
[urn =urn:pulumi:dev::demo::aws:s3/bucketObject:BucketObject::index.html]
- source: asset(file:77aab46) { content/index.html }
+ source: asset(file:01c09f4) { production/index.html }
可以看到 source 會被換成 production/index.html
步驟三: 讀取更多檔案 整個 Web 專案肯定不止一個檔案,所以再來改一下原本的讀取檔案列表流程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
site := getEnv (ctx, "s3:siteDir" , "content" )
files, err := ioutil.ReadDir (site)
if err != nil {
return err
}
for _, item := range files {
name := item.Name ()
if _, err = s3.NewBucketObject (ctx, name, &s3.BucketObjectArgs{
Bucket: bucket.Bucket,
Source: pulumi.NewFileAsset (filepath.Join (site, name)),
Acl: pulumi.String ("public-read" ),
ContentType: pulumi.String (mime.TypeByExtension (path.Ext (filepath.Join (site, name)))),
}); err != nil {
return err
}
}
執行部署
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Type Name Status Info
pulumi:pulumi:Stack demo-dev
+ ├─ aws:s3:BucketObject about.html created
~ └─ aws:s3:BucketObject index.html updated [diff: ~source]
Outputs:
bucketEndpoint: "foobar-1234.s3-website-ap-northeast-1.amazonaws.com"
bucketID : "foobar-1234"
bucketName : "foobar-1234"
Resources:
+ 1 created
~ 1 updated
2 changes. 2 unchanged
Duration: 9s
完整程式碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
package main
import (
"io/ioutil"
"mime"
"path"
"path/filepath"
"github.com/pulumi/pulumi-aws/sdk/v3/go/aws/s3"
"github.com/pulumi/pulumi/sdk/v2/go/pulumi"
)
func main () {
pulumi.Run (func (ctx *pulumi.Context) error {
// Create an AWS resource (S3 Bucket)
bucket, err := s3.NewBucket (ctx, "my-bucket" , &s3.BucketArgs{
Bucket: pulumi.String ("foobar-1234" ),
Website: s3.BucketWebsiteArgs{
IndexDocument: pulumi.String ("index.html" ),
},
})
if err != nil {
return err
}
site := getEnv (ctx, "s3:siteDir" , "content" )
files, err := ioutil.ReadDir (site)
if err != nil {
return err
}
for _, item := range files {
name := item.Name ()
if _, err = s3.NewBucketObject (ctx, name, &s3.BucketObjectArgs{
Bucket: bucket.Bucket,
Source: pulumi.NewFileAsset (filepath.Join (site, name)),
Acl: pulumi.String ("public-read" ),
ContentType: pulumi.String (mime.TypeByExtension (path.Ext (filepath.Join (site, name)))),
}); err != nil {
return err
}
}
// Export the name of the bucket
ctx.Export ("bucketID" , bucket.ID ())
ctx.Export ("bucketName" , bucket.Bucket)
ctx.Export ("bucketEndpoint" , bucket.WebsiteEndpoint)
return nil
})
}
func getEnv (ctx *pulumi.Context, key string , fallback ...string ) string {
if value, ok := ctx.GetConfig (key); ok {
return value
}
if len (fallback) > 0 {
return fallback[0 ]
}
return ""
}
建立第二個 Pulumi Stack 環境 在 Pulumi 可以很簡單的建立多種環境,像是 Testing 或 Production,只要將動態變數抽出來設定成 config 即可。底下來看看怎麼建立全先的環境,這步驟在 Pulumi 叫做 Stack。前面已經建立一個 dev 環境,現在我們要建立一個全新環境來部署 Testing 或 Production 該如何做呢?
步驟一: 建立全新 Stack 環境 透過 pulumi stack 可以建立全新環境
1
2
3
$ pulumi stack ls
NAME LAST UPDATE RESOURCE COUNT URL
dev* 1 minute ago 5 https://app.pulumi.com/appleboy/demo/dev
建立 stack
1
2
3
4
5
6
$ pulumi stack init prod
Created stack 'prod'
$ pulumi stack ls
NAME LAST UPDATE RESOURCE COUNT URL
dev 1 minute ago 5 https://app.pulumi.com/appleboy/demo/dev
prod* n/a n/a https://app.pulumi.com/appleboy/demo/prod
設定參數
1
2
3
pulumi config set s3:siteDir www
pulumi config set aws:profile demo
pulumi config set aws:region ap-northeast-1
步驟二: 建立 www 內容 建立 content/www
目錄,一樣放上 index.htm + about.html
1
2
3
4
5
<html >
<body >
<h1 >Hello Pulumi S3 Bucket From New Stack</h1 >
</body >
</html >
about.html
1
2
3
4
5
<html >
<body >
<h1 >About us From New Stack</h1 >
</body >
</html >
步驟三: 部署 New Stack 先看看 Preview 結果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
$ pulumi up
Previewing update (prod)
View Live: https://app.pulumi.com/appleboy/demo/prod/previews/3b85a340-0e71-455e-9b96-48dc38538d18
Type Name Plan
+ pulumi:pulumi:Stack demo-prod create
+ ├─ aws:s3:Bucket my-bucket create
+ ├─ aws:s3:BucketObject index.html create
+ └─ aws:s3:BucketObject about.html create
Resources:
+ 4 to create
Do you want to perform this update? details
+ pulumi:pulumi:Stack: (create)
[urn =urn:pulumi:prod::demo::pulumi:pulumi:Stack::demo-prod]
+ aws:s3/bucket:Bucket: (create)
[urn =urn:pulumi:prod::demo::aws:s3/bucket:Bucket::my-bucket]
acl : "private"
bucket : "my-bucket-ba8088c"
forceDestroy: false
website : {
indexDocument: "index.html"
}
+ aws:s3/bucketObject:BucketObject: (create)
[urn =urn:pulumi:prod::demo::aws:s3/bucketObject:BucketObject::index.html]
acl : "public-read"
bucket : "my-bucket-ba8088c"
contentType : "text/html; charset=utf-8"
forceDestroy: false
key : "index.html"
source : asset(file:460188b) { www/index.html }
+ aws:s3/bucketObject:BucketObject: (create)
[urn =urn:pulumi:prod::demo::aws:s3/bucketObject:BucketObject::about.html]
acl : "public-read"
bucket : "my-bucket-ba8088c"
contentType : "text/html; charset=utf-8"
forceDestroy: false
key : "about.html"
source : asset(file:376c42a) { www/about.html }
如果看起來沒問題,就可以直接執行了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Updating (prod)
View Live: https://app.pulumi.com/appleboy/demo/prod/updates/1
Type Name Status
+ pulumi:pulumi:Stack demo-prod created
+ ├─ aws:s3:Bucket my-bucket created
+ ├─ aws:s3:BucketObject about.html created
+ └─ aws:s3:BucketObject index.html created
Outputs:
bucketEndpoint: "my-bucket-a7044ab.s3-website-ap-northeast-1.amazonaws.com"
bucketID : "my-bucket-a7044ab"
bucketName : "my-bucket-a7044ab"
Resources:
+ 4 created
Duration: 18s
最後用 curl 執行看看
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
$ curl -v $( pulumi stack output bucketEndpoint)
* Trying 52.219.8.20...
* TCP_NODELAY set
* Connected to my-bucket-a7044ab.s3-website-ap-northeast-1.amazonaws.com (52.219.8.20) port 80 (#0)
> GET / HTTP/1.1
> Host: my-bucket-a7044ab.s3-website-ap-northeast-1.amazonaws.com
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 200 OK
< x-amz-id-2: oGxc+rLPi3kLOZslMsOmJqPY/WGeMoxX9sXJDRj4wlJlGVq+7pMx3ers71jxnDiDkeM9JRrd+T8=
< x-amz-request-id: 528235DDFF40F365
< Date: Thu, 11 Feb 2021 04:49:21 GMT
< Last-Modified: Thu, 11 Feb 2021 04:48:41 GMT
< ETag: "ae41d1b3f0aeef6a490e1b2edc74d2b5"
< Content-Type: text/html; charset =utf-8
< Content-Length: 85
< Server: AmazonS3
<
<html>
<body>
<h1>Hello Pulumi S3 Bucket From New Stack</h1>
</body>
</html>
* Connection #0 to host my-bucket-a7044ab.s3-website-ap-northeast-1.amazonaws.com left intact
* Closing connection 0
刪除 Pulumi Stack 環境 最後步驟就是要學習怎麼一鍵刪除整個 Infrastructure 環境。現在我們已經建立兩個 Stack 環境,該怎麼移除?
步驟一: 刪除所有資源 用 pulumi destroy
指令可以刪除全部資源
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
Previewing destroy (prod)
View Live: https://app.pulumi.com/appleboy/demo/prod/previews/92f9c4a4-f4a9-464d-be27-5040aff295ae
Type Name Plan
- pulumi:pulumi:Stack demo-prod delete
- ├─ aws:s3:BucketObject about.html delete
- ├─ aws:s3:BucketObject index.html delete
- └─ aws:s3:Bucket my-bucket delete
Outputs:
- bucketEndpoint: "my-bucket-a7044ab.s3-website-ap-northeast-1.amazonaws.com"
- bucketID : "my-bucket-a7044ab"
- bucketName : "my-bucket-a7044ab"
Resources:
- 4 to delete
Do you want to perform this destroy? details
- aws:s3/bucketObject:BucketObject: (delete)
[id =about.html]
[urn =urn:pulumi:prod::demo::aws:s3/bucketObject:BucketObject::about.html]
- aws:s3/bucketObject:BucketObject: (delete)
[id =index.html]
[urn =urn:pulumi:prod::demo::aws:s3/bucketObject:BucketObject::index.html]
- aws:s3/bucket:Bucket: (delete)
[id =my-bucket-a7044ab]
[urn =urn:pulumi:prod::demo::aws:s3/bucket:Bucket::my-bucket]
- pulumi:pulumi:Stack: (delete)
[urn =urn:pulumi:prod::demo::pulumi:pulumi:Stack::demo-prod]
--outputs:--
- bucketEndpoint: "my-bucket-a7044ab.s3-website-ap-northeast-1.amazonaws.com"
- bucketID : "my-bucket-a7044ab"
- bucketName : "my-bucket-a7044ab"
選擇 yse
移除所以資源
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Destroying (prod)
View Live: https://app.pulumi.com/appleboy/demo/prod/updates/2
Type Name Status
- pulumi:pulumi:Stack demo-prod deleted
- ├─ aws:s3:BucketObject index.html deleted
- ├─ aws:s3:BucketObject about.html deleted
- └─ aws:s3:Bucket my-bucket deleted
Outputs:
- bucketEndpoint: "my-bucket-a7044ab.s3-website-ap-northeast-1.amazonaws.com"
- bucketID : "my-bucket-a7044ab"
- bucketName : "my-bucket-a7044ab"
Resources:
- 4 deleted
Duration: 7s
步驟二: 移除 Stack 設定 上面步驟只是把所有資源移除,但是你還是保留了所以 stack history 操作,請看
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
$ pulumi stack history
Version: 2
UpdateKind: destroy
Status: succeeded
Message: chore(pulumi): 設定 Pulumi Stack 環境變數
+0-4~0 0 Updated 1 minute ago took 8s
exec.kind: cli
git.author: Bo-Yi Wu
git.author.email: xxxxxxxx@gmail.com
git.committer: Bo-Yi Wu
git.committer.email: xxxxxxxx@gmail.com
git.dirty: true
git.head: 9d9f8182abefb0e90656ca45065bc07a8a3431f4
git.headName: refs/heads/main
vcs.kind: github.com
vcs.owner: go-training
vcs.repo: infrastructure-as-code-workshop
Version: 1
UpdateKind: update
Status: succeeded
Message: chore(pulumi): 設定 Pulumi Stack 環境變數
+4-0~0 0 Updated 8 minutes ago took 18s
exec.kind: cli
git.author: Bo-Yi Wu
git.author.email: xxxxxxxx@gmail.com
git.committer: Bo-Yi Wu
git.committer.email: xxxxxxxx@gmail.com
git.dirty: true
git.head: 437e94e130ee3d31eb80075dd237cc17d09255d1
git.headName: refs/heads/main
vcs.kind: github.com
vcs.owner: go-training
vcs.repo: infrastructure-as-code-workshop
要整個完整移除,請務必要執行底下指令
最後的確認
1
2
3
$ pulumi stack rm
This will permanently remove the 'prod' stack!
Please confirm that this is what you' d like to do by typing ("prod" ):
移除其他的 Stack 按照上面的步驟重新移除其他的 Stack,先使用底下指令列出還有哪些 Stack:
1
2
3
$ pulumi stack ls
NAME LAST UPDATE RESOURCE COUNT URL
dev 24 minutes ago 5 https://app.pulumi.com/appleboy/demo/dev
選擇 Stack
1
pulumi stack select dev
接著重複上面一跟二步驟即可
心得 本篇跟上一篇 教學剛好涵蓋了整個 Pulumi 的基本使用方式,如果你還在選擇要用 Terraform 還是 Pulumi ,甚至 AWS 所推出的 CDK ,很推薦你嘗試看看 Pulumi,未來會介紹更多 Pulumi 進階的使用方式,或者像是部署 Kubernetes .. 等,能使用自己喜歡的語言寫 Infra 是一件令人舒服的事情。
See also