Upgrade to Pro — share decks privately, control downloads, hide ads and more …

初探 Infrastructure as Code 工具 Pulumi

Bo-Yi Wu
November 16, 2021

初探 Infrastructure as Code 工具 Pulumi

想必大家對於 Infrastructure as Code 簡稱 (IaC) 並不陌生,而這個名詞在很早以前就很火熱,本篇最主要介紹為什麼我們要導入 IaC,以及該選擇哪些工具來管理雲平台 (AWS, GCP, Azure 等…)。觀看現在很火紅的 Terraform 及後起之秀 Pulumi 是大家可以作為選擇的參考,而底下會來歸納優缺點及技術比較,以及為什麼我最後會選擇 Pulumi。這兩套都是由 Go 語言所開發,現在選擇工具前,都要先考慮看看什麼語言寫的,以及整合進團隊自動化部署流程難易度。

Bo-Yi Wu

November 16, 2021
Tweet

More Decks by Bo-Yi Wu

Other Decks in Programming

Transcript

  1. 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.
  2. Infrastructure as code is about bringing software engineering principles and

    approaches into the cloud infrastructure space. IUUQTXXXQVMVNJDPNXIBUJTXIBUJTJOGSBTUSVDUVSFBTDPEF
  3. IaC 帶來的好處? • 建置 CI/CD ⾃動化 (不⽤依賴 UI 操作) •

    版本控制 (審核避免錯誤) • 重複使⽤ (減少建置時間) • 環境⼀⾄性 (測試及正式) • 團隊成長 (分享學習資源)
  4. Terraform • Domain Speci fi c Language of IaC Tool

    • HCL for Terraform FBTZUPSFBEBOEXSJUF
  5. 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 語法
  6. What if you need more logic? • Conditionals • If

    / else & logical operators • Loops • Functions • re-use logic
  7. _, 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
  8. 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 }
  9. Pulumi Support Language • Golang • C# • Python •

    JavaScript • TypeScript • F# • VB
  10. 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" } , }) ;
  11. _, 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)
  12. / / 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) ); `)
  13. 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 }
  14. DevOps 團隊 • Infrastructure as Code (可審核檢視) • Multi-Cloud DevOps

    (⽤⼀種⽅式管理多個雲平台) • Deploy Continuously (⾃動化部署)
  15. 資安團隊 • Policy as Code • Built-In Secrets (加密特定資料: DB

    密碼 ..) • Enforce Standards (同⼀套流程管理系統架構)
  16. 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 :".-
  17. 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
  18. 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 })