Slide 1

Slide 1 text

Infrastructure as Code (IaC) Bo-Yi Wu 2021/11/16 Pulumi vs Terraform

Slide 2

Slide 2 text

About me • Software Engineer in Mediatek • Member of Drone CI/CD Platform • Member of Gitea Platform • Member of Gin Golang Framework • Maintain Some GitHub Actions Plugins.

Slide 3

Slide 3 text

What is Infrastructure As Code (IaC)?

Slide 4

Slide 4 text

Infrastructure as code is about bringing software engineering principles and approaches into the cloud infrastructure space. IUUQTXXXQVMVNJDPNXIBUJTXIBUJTJOGSBTUSVDUVSFBTDPEF

Slide 5

Slide 5 text

6QMPBE%BUBUP"844TUPSBHF

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

"84$MPVE%BTICPBSE

Slide 8

Slide 8 text

5FTUJOH 4UBHJOH 1SPEVDUJPO

Slide 9

Slide 9 text

*OUFHSBUJPO5FTUJOHPS4NPLF5FTUJOH

Slide 10

Slide 10 text

Move to GCP or Azure?

Slide 11

Slide 11 text

為什麼需要 IaC (避免⼈為操作失敗?)

Slide 12

Slide 12 text

IaC 帶來的好處? • 建置 CI/CD ⾃動化 (不⽤依賴 UI 操作) • 版本控制 (審核避免錯誤) • 重複使⽤ (減少建置時間) • 環境⼀⾄性 (測試及正式) • 團隊成長 (分享學習資源)

Slide 13

Slide 13 text

IUUQTHJUIVCDPNEBUBTUBDLUWEBUBFOHJOFFSSPBENBQ

Slide 14

Slide 14 text

⼯具選擇?

Slide 15

Slide 15 text

Terraform • Domain Speci fi c Language of IaC Tool • HCL for Terraform FBTZUPSFBEBOEXSJUF

Slide 16

Slide 16 text

data "aws_ami" "ubuntu_16_04_docker" { f i lter { name = "name" values = ["app - base - docker - image - *"] } f i lter { name = "virtualization - type" values = ["hvm"] } owners = ["161654634948"] # Canonical } resource "aws_instance" "foobar_api_01" { ami = data.aws_ami.ubuntu_16_04_docker.id instance_type = "t2.small" vpc_security_group_ids = [aws_security_group.foobar_api.id] key_name = aws_key_pair.foobar_deploy.key_name subnet_id = aws_subnet.foobar_a.id iam_instance_prof i le = aws_iam_instance_prof i le.ec2_role.id tags = { Name = "foobar api 01" Project = "foobar" Environment = var.environment["production"] } } HCL 語法

Slide 17

Slide 17 text

What if you need more logic? • Conditionals • If / else & logical operators • Loops • Functions • re-use logic

Slide 18

Slide 18 text

_, err : = s3.NewBucket(ctx, "wms - bucket", &s3.BucketArgs{ Acl: pulumi.String("private"), Bucket: pulumi.String("objects.wms"), ForceDestroy: pulumi.Bool(false), LifecycleRules: s3.BucketLifecycleRuleArray{ &s3.BucketLifecycleRuleArgs{ Enabled: pulumi.Bool(true), Expiration: &s3.BucketLifecycleRuleExpirationArgs{ Days: pulumi.Int(7), }, Pref i x: pulumi.String("backup"), Tags: pulumi.StringMap{ "autoclean": pulumi.String("true"), "name": pulumi.String("database"), }, }, }, }, pulumi.Protect(true)) AWS S3 using Golang

Slide 19

Slide 19 text

Con fi guration by ENV (AWS Global and China)

Slide 20

Slide 20 text

No content

Slide 21

Slide 21 text

Amazon FSx for Lustre (Don’t support in China)

Slide 22

Slide 22 text

Database struct { Driver string `envconf i g:"APP_DATABASE_DRIVER"` Username string `envconf i g:"APP_DATABASE_USERNAME"` Password string `envconf i g:"APP_DATABASE_PASSWORD"` Name string `envconf i g:"APP_DATABASE_NAME"` } Conf i g struct { Logging Logging Server Server Database Database Redis Redis Storage Storage Queue Queue }

Slide 23

Slide 23 text

Pulumi Support Language • Golang • C# • Python • JavaScript • TypeScript • F# • VB

Slide 24

Slide 24 text

No content

Slide 25

Slide 25 text

Terraform 就不能⽤程式語⾔來寫?

Slide 26

Slide 26 text

No content

Slide 27

Slide 27 text

No content

Slide 28

Slide 28 text

https: / / github.com/hashicorp/terraform - cdk

Slide 29

Slide 29 text

Pulumi Cloud Provider

Slide 30

Slide 30 text

No content

Slide 31

Slide 31 text

Converting From Terraform Using tf2pulumi Tool IUUQTXXXQVMVNJDPNEPDTHVJEFTBEPQUJOHGSPN@UFSSBGPSN

Slide 32

Slide 32 text

No content

Slide 33

Slide 33 text

Using Pulumi and Terraform Side-by-Side from Terraform state import * as pulumi from "@pulumi/pulumi" ; import * as terraform from "@pulumi/terraform" ; const con fi g = new pulumi.Con fi g() ; const tfeToken = con fi g.requireSecret("tfeToken") ; const networkState = new terraform.state.RemoteStateReference("network", { backendType: "remote" , token: tfeToken , organization: "acme" , workspaces: { name: "production-network" } , }) ;

Slide 34

Slide 34 text

⾃⾏開發整合⼯具? (Automation API)

Slide 35

Slide 35 text

https: / / w w w .pulumi.com/blog/automation - api/

Slide 36

Slide 36 text

YA! 不⽤使⽤ CLI Tool (pulumi command)

Slide 37

Slide 37 text

Automation API allows you to embed Pulumi within your application code

Slide 38

Slide 38 text

1. Database Migration

Slide 39

Slide 39 text

_, err = rds.NewClusterInstance(ctx, "dbInstance", &rds.ClusterInstanceArgs{ ClusterIdentif i er: cluster.ClusterIdentif i er, InstanceClass: rds.InstanceType_T3_Small, Engine: rds.EngineTypeAuroraMysql, EngineVersion: pulumi.String("5.7.mysql_aurora.2.03.2"), PubliclyAccessible: pulumi.Bool(true), DbSubnetGroupName: subnetGroup.Name, }) if err ! = nil { return err } ctx.Export("host", cluster.Endpoint) ctx.Export("dbName", dbName) ctx.Export("dbUser", dbUser) ctx.Export("dbPass", dbPass)

Slide 40

Slide 40 text

/ / create our stack with an "inline" Pulumi program (deployFunc) stack : = auto.UpsertStackInlineSource(ctx, stackName, projectName, deployFunc) / / run the update to deploy our database res, err : = stack.Up(ctx, stdoutStreamer) fmt.Println("Update succeeded!") / / get the connection info host : = res.Outputs["host"].Value dbName : = res.Outputs["dbName"].Value dbUser : = res.Outputs["dbUser"].Value dbPass : = res.Outputs["dbPass"].Value / / establish db connection db : = sql.Open("mysql", fmt.Sprintf("%s:%s@tcp(%s:3306)/%s", dbUser, dbPass, host, dbName)) defer db.Close() / / run our database "migration" fmt.Println("creating table . . . ") db.Query(` CREATE TABLE IF NOT EXISTS hello_pulumi( id int(9) NOT NULL, color varchar(14) NOT NULL, PRIMARY KEY(id) ); `)

Slide 41

Slide 41 text

2. RESTful API Service

Slide 42

Slide 42 text

ctx : = context.Background() stackName : = createReq.ID program : = createPulumiProgram(createReq.Content) s, err : = auto.NewStackInlineSource(ctx, stackName, project, program) if err ! = nil { / / if stack already exists, 409 if auto.IsCreateStack409Error(err) { w.WriteHeader(409) fmt.Fprintf(w, fmt.Sprintf("stack %q already exists", stackName)) return } w.WriteHeader(500) fmt.Fprintf(w, err.Error()) return } s.SetConf i g(ctx, "aws:region", auto.Conf i gValue{Value: "us - west-2"}) / / deploy the stack / / we'll write all of the update logs to st out so we can watch requests get processed upRes, err : = s.Up(ctx, optup.ProgressStreams(os.Stdout)) if err ! = nil { w.WriteHeader(500) fmt.Fprintf(w, err.Error()) return }

Slide 43

Slide 43 text

State Management

Slide 44

Slide 44 text

%FGBVMUJO$MPVE %FGBVMUJO-PDBM IUUQTFOXJLJQFEJBPSHXJLJ5FBN@DPO fl JDU

Slide 45

Slide 45 text

Why I Choose Pulumi?

Slide 46

Slide 46 text

開發者 • 可以使⽤熟悉的語⾔, 不⽤學習新語法 • 模組及跨平台 (AWS, GCP, Azure) • 減少複製流程, ⾃由建立模組清單

Slide 47

Slide 47 text

DevOps 團隊 • Infrastructure as Code (可審核檢視) • Multi-Cloud DevOps (⽤⼀種⽅式管理多個雲平台) • Deploy Continuously (⾃動化部署)

Slide 48

Slide 48 text

資安團隊 • Policy as Code • Built-In Secrets (加密特定資料: DB 密碼 ..) • Enforce Standards (同⼀套流程管理系統架構)

Slide 49

Slide 49 text

Pulumi Architecture

Slide 50

Slide 50 text

Pulumi Overview IUUQTXXXQVMVNJDPNEPDTJOUSPDPODFQUT

Slide 51

Slide 51 text

Pulumi Overview 3VOUIF1VMVNJ$-*DPNNBOE QVMVNJVQ

Slide 52

Slide 52 text

name: footer runtime: go description: A minimal AWS Go Pulumi program template: conf i g: aws:region: description: AWS region default: ap - southeast-1 conf i g: aws:prof i le: footer aws:region: ap - southeast-1 project:environment: production project:name: footer pulumi:template: aws - go 1SPKFDU :".- 4UBDL :".-

Slide 53

Slide 53 text

package main import ( "github.com/pulumi/pulumi - aws/sdk/v4/go/aws/s3" "github.com/pulumi/pulumi/sdk/v3/go/pulumi" ) func main() { pulumi.Run(func(ctx * pulumi.Context) error { / / Create an AWS resource (S3 Bucket) bucket, err : = s3.NewBucket(ctx, "my - bucket", nil) if err ! = nil { return err } / / Export the name of the bucket ctx.Export("bucketName", bucket.ID()) return nil }) } NBJOQSPHSBN

Slide 54

Slide 54 text

Pulumi CLI Console

Slide 55

Slide 55 text

Pulumi Cloud Console

Slide 56

Slide 56 text

Pulumi Cloud Console

Slide 57

Slide 57 text

Pulumi Cloud Console

Slide 58

Slide 58 text

Pulumi Cloud Console

Slide 59

Slide 59 text

快速建置簡單網⾴ 使⽤ AWS S3 + Static Website

Slide 60

Slide 60 text

pulumi.Run(func(ctx * pulumi.Context) error { / / Create an AWS resource (S3 Bucket) bucket, err : = s3.NewBucket(ctx, "my - bucket", &s3.BucketArgs{ Website: s3.BucketWebsiteArgs{ IndexDocument: pulumi.String("index.html"), }, }) if err ! = nil { return err } site : = getEnv(ctx, "s3:siteDir", "content") f i les, err : = ioutil.ReadDir(site) if err ! = nil { return err } for _, item : = range f i les { name : = item.Name() if _, err = s3.NewBucketObject(ctx, name, &s3.BucketObjectArgs{ Bucket: bucket.Bucket, Source: pulumi.NewFileAsset(f i lepath.Join(site, name)), Acl: pulumi.String("public - read"), ContentType: pulumi.String(mime.TypeByExtension(path.Ext(f i lepath.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 })

Slide 61

Slide 61 text

Pulumi Pricing

Slide 62

Slide 62 text

IUUQTXXXQVMVNJDPNQSJDJOH

Slide 63

Slide 63 text

免費⽅案 IUUQTXXXQVMVNJDPNQSJDJOHGBRQSPEVDU

Slide 64

Slide 64 text

Convince Your Boss IUUQTXXXQVMVNJDPNQSJDJOHGBRQSPEVDU

Slide 65

Slide 65 text

Thank You