上一篇『初探 Pulumi 上傳靜態網站到 AWS S3 (一)』主要介紹 Pulumi 基本使用方式,而本篇會延續上一篇教學把剩下的章節教完,底下是本篇會涵蓋的章節內容:
- 設定 Pulumi Stack 環境變數
- 建立第二個 Pulumi Stack 環境
- 刪除 Pulumi Stack 環境
讓開發者可以自由新增各種不同環境,像是 Testing 或 Develop 環境,以及該如何動態帶入不同環境的變數內容,最後可以透過單一指令將全部資源刪除。
設定 Pulumi Stack 環境變數
大家可以看到,現在所有 main.go
的程式碼,都是直接 hardcode 的,那怎麼透過一些環境變數來動態改變設定呢?這時候可以透過 pulumi config 指令來調整喔,底下來看看怎麼實作,假設我們要讀取的 index.html 放在其他目錄底下,該怎麼動態調整?
步驟一: 撰寫讀取 Config 函式
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
pulumi config set s3:siteDir production
打開 Pulumi.dev.yaml
可以看到
config:
aws:profile: demo
aws:region: ap-northeast-1
s3:siteDir: production
接著將程式碼改成如下:
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
$ 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 專案肯定不止一個檔案,所以再來改一下原本的讀取檔案列表流程
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
}
}
執行部署
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
完整程式碼如下:
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 可以建立全新環境
$ pulumi stack ls
NAME LAST UPDATE RESOURCE COUNT URL
dev* 1 minute ago 5 https://app.pulumi.com/appleboy/demo/dev
建立 stack
$ 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
設定參數
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
<html>
<body>
<h1>Hello Pulumi S3 Bucket From New Stack</h1>
</body>
</html>
about.html
<html>
<body>
<h1>About us From New Stack</h1>
</body>
</html>
步驟三: 部署 New Stack
先看看 Preview 結果
$ 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 }
如果看起來沒問題,就可以直接執行了
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 執行看看
$ 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
指令可以刪除全部資源
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
移除所以資源
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 操作,請看
$ 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
要整個完整移除,請務必要執行底下指令
pulumi stack rm
最後的確認
$ 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:
$ pulumi stack ls
NAME LAST UPDATE RESOURCE COUNT URL
dev 24 minutes ago 5 https://app.pulumi.com/appleboy/demo/dev
選擇 Stack
pulumi stack select dev
接著重複上面一跟二步驟即可
心得
本篇跟上一篇教學剛好涵蓋了整個 Pulumi 的基本使用方式,如果你還在選擇要用 Terraform 還是 Pulumi,甚至 AWS 所推出的 CDK,很推薦你嘗試看看 Pulumi,未來會介紹更多 Pulumi 進階的使用方式,或者像是部署 Kubernetes .. 等,能使用自己喜歡的語言寫 Infra 是一件令人舒服的事情。