Slide 1

Slide 1 text

Инфраструктура как TypeScript

Slide 2

Slide 2 text

План

Slide 3

Slide 3 text

План

Slide 4

Slide 4 text

План

Slide 5

Slide 5 text

Автоматизация Cloud 3

Slide 6

Slide 6 text

API управления ресурсами

Slide 7

Slide 7 text

Сервисы HTTP/REST Content-Type: application/x-www-form-urlencoded; charset=UTF-8 X-Amz-Date: 20130813T150211Z Host: ec2.amazonaws.com Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20130813/us-east- 1/ec2/aws4_request, SignedHeaders=content-type;host;x-amz- date, Signature=ced6826de92d2bdeed8f846f0bf508e8559e98e4b0194b84example54174deb456c http://ec2.amazonaws.com/?Action=RunInstances ImageId=ami-2bb65342 &MaxCount=3 &MinCount=1 &Monitoring.Enabled=true &Placement.AvailabilityZone=us-east-1a &Version=2016-11-15

Slide 8

Slide 8 text

var launchRequest = new RunInstancesRequest() { ImageId = amiID, InstanceType = "t1.micro", MinCount = 1, MaxCount = 1, KeyName = keyPairName, SecurityGroupIds = groups }; var launchResponse = ec2Client.RunInstances(launchRequest); var instances = launchResponse.Reservation.Instances; SDK для вызова REST сервисов

Slide 9

Slide 9 text

az storage account create \ --location westus \ --name samplesa \ --resource-group myrg \ --sku LRS Командная строка

Slide 10

Slide 10 text

Сложности со скриптами

Slide 11

Slide 11 text

Desired State Configuration 6

Slide 12

Slide 12 text

Описание целевого состояния

Slide 13

Slide 13 text

Управление графами ресурсов

Slide 14

Slide 14 text

Характеристики

Slide 15

Slide 15 text

Инструменты от вендоров

Slide 16

Slide 16 text

Языки разметки 9 Yet Another Markup Language

Slide 17

Slide 17 text

Azure ARM Templates: JSON "resources": [ { "apiVersion": "2016-01-01", "type": "Microsoft.Storage/storageAccounts", "name": "mystorageaccount", "location": "westus", "sku": { "name": "Standard_LRS" }, "kind": "Storage", "properties": { } } ]

Slide 18

Slide 18 text

AWS CloudFormation: YAML Resources: S3BucketForURLs: Type: "AWS::S3::Bucket" DeletionPolicy: Delete Properties: BucketName: !If [ "CreateNewBucket", !Ref "AWS::NoValue", !Ref S3BucketName ] WebsiteConfiguration: IndexDocument: "index.html" LifecycleConfiguration: Rules: - Id: DisposeShortUrls ExpirationInDays: !Ref URLExpiration Prefix: "u" Status: Enabled

Slide 19

Slide 19 text

resource "azurerm_storage_account" "sa" { name = "mysa" location = azurerm_resource_group.rg.location resource_group_name = azurerm_resource_group.rg.name account_tier = "Standard" account_replication_type = "LRS" } Terraform: HashiCorp Configuration Language

Slide 20

Slide 20 text

Языки разметки

Slide 21

Slide 21 text

Пример: Соотношение кода и разметки KV Store Table “URLs” Function “Add URL” Function “Open URL”

Slide 22

Slide 22 text

{ "Resources": { "InstanceSecurityGroup": { "Type": "AWS::EC2::SecurityGroup", "Properties": { "SecurityGroupIngress": [ ... ] } }, "EC2Instance": { "Type": "AWS::EC2::Instance", "Properties": { "InstanceType": "t2.micro", "SecurityGroups": [ { "Ref": "InstanceSecurityGroup" } ], "ImageId": "ami-0080e4c5bc078760e" } } } } Ссылки на ресурсы

Slide 23

Slide 23 text

{ "condition": "[equals(parameters('production'), 'Yes')]", "type": "Microsoft.Compute/availabilitySets", "apiVersion": "2017-03-30", "name": "[variables('availabilitySetName')]", "location": "[resourceGroup().location]", "properties": { "platformFaultDomainCount": 2, "platformUpdateDomainCount": 3 } } Условия

Slide 24

Slide 24 text

locals { files = ["index.php","main.php"] } resource "aws_s3_bucket_object" "files" { count = "${length(local.files)}" key = "${local.files[count.index]}" bucket = "${aws_s3_bucket.examplebucket.id}" source = "./src/${local.files[count.index]}" } Циклы

Slide 25

Slide 25 text

Повторное использование

Slide 26

Slide 26 text

Тестирование

Slide 27

Slide 27 text

Кажется, мы это уже когда-то делали?

Slide 28

Slide 28 text

IDEs Code completion Unit tests Types Functions Package managers Versioning Classes Code reviews Contracts SDKs Workflows Refactoring

Slide 29

Slide 29 text

Pulumi 14

Slide 30

Slide 30 text

Универсальные языки программирования

Slide 31

Slide 31 text

• • • • • Провайдеры • • • • •

Slide 32

Slide 32 text

const resourceGroup = new azure.core.ResourceGroup("rg"); const storageAccount = new azure.storage.Account("storage", { resourceGroupName: resourceGroup.name, accountReplicationType: "LRS", accountTier: "Standard", }); Пример TypeScript

Slide 33

Slide 33 text

const resourceGroup = new azure.core.ResourceGroup("rg"); const storageAccount = new azure.storage.Account("storage", { resourceGroupName: resourceGroup.name, accountReplicationType: "LRS", accountTier: "Standard", }); Desired State

Slide 34

Slide 34 text

Как работает Pulumi

Slide 35

Slide 35 text

Static Website on AWS 17

Slide 36

Slide 36 text

N провайдеров, M языков

Slide 37

Slide 37 text

N провайдеров, M языков

Slide 38

Slide 38 text

Сложность O(N*M)

Slide 39

Slide 39 text

Сложность O(N+M)

Slide 40

Slide 40 text

Сложность O(N+M)

Slide 41

Slide 41 text

Управление состоянием

Slide 42

Slide 42 text

Serverless Functions в Azure 22

Slide 43

Slide 43 text

Ресурсы, нужные для Azure Function

Slide 44

Slide 44 text

// A resource group to contain our Azure Functions const resourceGroup = new azure.core.ResourceGroup("rg"); // A Node.js Azure Function const nodeApp = new azure.appservice.ArchiveFunctionApp("http", { resourceGroup, archive: new pulumi.asset.FileArchive("./javascript"), }); export const nodeEndpoint = nodeApp.endpoint; Node.js Azure Function

Slide 45

Slide 45 text

Запустим pulumi up … Type Name Plan + pulumi:pulumi:Stack functions-dev create + ├─ azure:appservice:ArchiveFunctionApp http create + │ ├─ azure:storage:Account http create + │ ├─ azure:appservice:Plan http create + │ ├─ azure:storage:Container http create + │ ├─ azure:storage:ZipBlob http create + │ └─ azure:appservice:FunctionApp http create + └─ azure:core:ResourceGroup rg create

Slide 46

Slide 46 text

// A .NET (C#) Azure Function const dotnetApp = new azure.appservice.ArchiveFunctionApp("dotnet", { resourceGroup, archive: new pulumi.asset.FileArchive("./dotnet/publish/"), appSettings: { runtime: "dotnet", }, }); .NET Azure Function

Slide 47

Slide 47 text

const linuxResourceGroup = new azure.core.ResourceGroup("linuxrg"); const linuxPlan = new azure.appservice.Plan("linux-asp", { resourceGroupName: linuxResourceGroup.name, kind: "Linux", sku: { tier: "Dynamic", size: "Y1" }, reserved: true, }); const pythonApp = new azure.appservice.ArchiveFunctionApp(...); Python Azure Function on Linux

Slide 48

Slide 48 text

const linuxResourceGroup = new azure.core.ResourceGroup("linuxrg"); const linuxPlan = new azure.appservice.Plan("linux-asp", {...}); const pythonApp = new azure.appservice.ArchiveFunctionApp("http", { resourceGroup: linuxResourceGroup, plan: linuxPlan, archive: new pulumi.asset.FileArchive("./python"), appSettings: { runtime: "python", }, }); Python Azure Function on Linux

Slide 49

Slide 49 text

const f = new azure.appservice.HttpEventSubscription("greeting", { resourceGroup, callback: async (context, req) => { return { status: 200, body: `Hello ${req.params.name}!`, }; }, }); Callbacks => Serverless Functions

Slide 50

Slide 50 text

const queue = new azure.storage.Queue("myqueue", { storageAccountName: storageAccount.name, }); queue.onEvent("MyMessageHandler", { callback: async (context, msg) => { doWork(msg); }, }); Callbacks => Serverless Functions

Slide 51

Slide 51 text

Containers 27

Slide 52

Slide 52 text

Crosswalk for AWS

Slide 53

Slide 53 text

Load- balanced container instances Load Balancer Container Container

Slide 54

Slide 54 text

const listener = new awsx.elasticloadbalancingv2.NetworkListener("lb",{ port: 80 }); const service = new awsx.ecs.FargateService("nginx", { desiredCount: 2, taskDefinitionArgs: { containers: { nginx: { image: "jonashackt/spring-boot-vuejs", memory: 512, portMappings: [listener], }}}}); Nginx + Fargate + Load Balancer

Slide 55

Slide 55 text

Запустим pulumi up … Type Name Status + pulumi:pulumi:Stack pulumi-typescript-aws-fargate-dev created + ├─ awsx:x:ec2:SecurityGroup nginx created + │ └─ aws:ec2:SecurityGroup nginx created + ├─ awsx:x:ec2:Vpc default-vpc-accd9bc5 created + │ ├─ awsx:x:ec2:Subnet default-vpc-accd9bc5-public-1 created + │ └─ awsx:x:ec2:Subnet default-vpc-accd9bc5-public-0 created + ├─ awsx:x:ecs:FargateService spring-boot-vuejs created + │ └─ aws:ecs:Service spring-boot-vuejs created + ├─ awsx:x:ecs:FargateTaskDefinition spring-boot-vuejs created + │ ├─ aws:cloudwatch:LogGroup spring-boot-vuejs created + │ ├─ aws:iam:Role spring-boot-vuejs-execution created + │ ├─ aws:iam:Role spring-boot-vuejs-task created + │ ├─ aws:iam:RolePolicyAttachment spring-boot-vuejs-execution-9a42f520 created + │ ├─ aws:iam:RolePolicyAttachment spring-boot-vuejs-task-32be53a2 created + │ ├─ aws:iam:RolePolicyAttachment spring-boot-vuejs-task-fd1a00e5 created + │ └─ aws:ecs:TaskDefinition spring-boot-vuejs created + ├─ awsx:x:ecs:Cluster default-cluster created + │ ├─ awsx:x:ec2:SecurityGroup default-cluster created + │ │ ├─ awsx:x:ec2:IngressSecurityGroupRule default-cluster-ssh created + │ │ │ └─ aws:ec2:SecurityGroupRule default-cluster-ssh created + │ │ ├─ awsx:x:ec2:IngressSecurityGroupRule default-cluster-containers created + │ │ │ └─ aws:ec2:SecurityGroupRule default-cluster-containers created + │ │ ├─ awsx:x:ec2:EgressSecurityGroupRule default-cluster-egress created + │ │ │ └─ aws:ec2:SecurityGroupRule default-cluster-egress created + │ │ └─ aws:ec2:SecurityGroup default-cluster created + │ └─ aws:ecs:Cluster default-cluster created + └─ aws:lb:ApplicationLoadBalancer nginx created + ├─ awsx:lb:ApplicationTargetGroup nginx created + │ └─ aws:lb:TargetGroup nginx created + ├─ awsx:lb:ApplicationListener nginx created + │ ├─ awsx:x:ec2:IngressSecurityGroupRule nginx-external-0-ingress created + │ │ └─ aws:ec2:SecurityGroupRule nginx-external-0-ingress created + │ ├─ awsx:x:ec2:EgressSecurityGroupRule nginx-external-0-egress created + │ │ └─ aws:ec2:SecurityGroupRule nginx-external-0-egress created + │ └─ aws:lb:Listener nginx created + └─ aws:lb:LoadBalancer nginx created

Slide 56

Slide 56 text

Cosmos App 30

Slide 57

Slide 57 text

Cosmos App (1)

Slide 58

Slide 58 text

Cosmos App (2)

Slide 59

Slide 59 text

Cosmos App (3)

Slide 60

Slide 60 text

export const functions = new CosmosApp("urls", { resourceGroup, locations: ["WestEurope", "WestUS", "SouthEastAsia"], factory: ({location, cosmosdb}) => { const app = new azure.appservice.ArchiveFunctionApp("app", { location, archive: new pulumi.asset.FileArchive("./app"), appSettings: { COSMOSDB_ENDPOINT: cosmosdb.endpoint, }, }); return { id: app.functionApp.id }; }}); Глобальные приложения

Slide 61

Slide 61 text

export const functions = new CosmosApp("urls", { resourceGroup, locations: ["WestEurope", "WestUS", "SouthEastAsia"], factory: ({location, cosmosdb}) => { const app = new azure.appservice.ArchiveFunctionApp("app", { location, archive: new pulumi.asset.FileArchive("./app"), appSettings: { COSMOSDB_ENDPOINT: cosmosdb.endpoint, }, }); return { id: app.functionApp.id }; }}); Глобальные приложения

Slide 62

Slide 62 text

Kubernetes 33

Slide 63

Slide 63 text

class AksCluster extends pulumi.ComponentResource { constructor(name: string, args: AksClusterArgs, opts: pulumi.ComponentResourceOptions = {}) { super("examples:aks:AksCluster", name, args, opts); const password = new random.RandomPassword("password"/*, {...}*/); const sshPublicKey = new tls.PrivateKey("key"/*, {...}*/); const adApp = new azuread.Application("aks"/*, {...}*/); const adSp = new azuread.ServicePrincipal("aksSp"/*, {...}*/); const vnet = new azure.network.VirtualNetwork("vnet"/*, {...}*/); const subnet = new azure.network.Subnet("subnet"/*, {...}*/); const cluster = new azure.containerservice.KubernetesCluster("aksCluster"/*, {...}*/) } } Managed Clusters

Slide 64

Slide 64 text

Crosswalk for Kubernetes

Slide 65

Slide 65 text

// Define the application Pod. const appPod = new kx.PodBuilder({ containers: [{ image: "app:1.0.0", env: { "APP_CONFIG_PATH": "/app/config" }, volumeMounts: [configs.mount("/app/config")], }], }); // Create a Kubernetes Deployment using the previous Pod definition. const deployment = new kx.Deployment("nginx", { spec: appPod.asDeploymentSpec({ replicas: 3 }), }); // Expose the Deployment with a load balancer using a Kubernetes Service. const service = deployment.createService({ type: kx.types.ServiceType.LoadBalancer, }); Библиотека Kx

Slide 66

Slide 66 text

// Find all distinct versions of MySQL running in your cluster. const mySqlVersions = kq .list("v1", "Pod") .flatMap(pod => pod.spec.containers) .map(container => container.image) .filter(imageName => imageName.includes("mysql")) .distinct(); mySqlVersions.forEach(console.log); Запросы

Slide 67

Slide 67 text

Контроль качества 36

Slide 68

Slide 68 text

Языки программирования для инфраструктуры? Точно?

Slide 69

Slide 69 text

const group = require("./index").group; // check: Instances must not have SSH open to the Internet. it("must not open port 22 (SSH) to the Internet", done => { pulumi.all([ group.urn, group.ingress ]).apply(([ urn, ingress ]) => { if (ingress.find(rule => rule.fromPort == 22 && rule.cidrBlocks.find(block => block === "0.0.0.0/0"))) { done(new Error(`Illegal SSH port 22 open to the Internet on group ${urn}`)); } else { done(); } }); }); Unit Testing

Slide 70

Slide 70 text

const policies = new PolicyPack("aws", { policies: [ { name: "prohibited-public-internet", description: "Ingress rules with public internet access are prohibited.", enforcementLevel: "mandatory", validateResource: validateTypedResource(aws.ec2.SecurityGroup, (sg, args, report) => { const publicInternetRules = (sg.ingress || []).find(ingressRule => (ingressRule.cidrBlocks || []).find(cidr => cidr === "0.0.0.0/0")); if (publicInternetRules) { report("Ingress rules with public internet access are prohibited."); } }), }, ], }); Policy as Code

Slide 71

Slide 71 text

AWS Cloud Development Kit (CDK)? 39

Slide 72

Slide 72 text

м TypeScript Python Java .NET Транслируется в CloudFormation

Slide 73

Slide 73 text

const hello = new lambda.Function(this, 'HelloHandler', { runtime: lambda.Runtime.NODEJS_8_10, code: lambda.Code.asset('lambda'), handler: 'hello.handler' }); new apigw.LambdaRestApi(this, 'Endpoint', { handler: hello }); Пример AWS CDK

Slide 74

Slide 74 text

Примеры из моей презентации ✓ ✓ ✓  ✓ ✓ ✓  ✓  ✓ 

Slide 75

Slide 75 text

Выводы 40

Slide 76

Slide 76 text

Используйте Инфраструктуру как код Создаёте приложения для cloud?

Slide 77

Slide 77 text

Меньшие компоненты Лучшая автоматизация

Slide 78

Slide 78 text

Преимущества кода

Slide 79

Slide 79 text

м Инфраструктура как код Архитектура как код

Slide 80

Slide 80 text

No content