Infrastructure as Code ist in der Cloud Welt in aller Munde. Sei es bei Azure via ARM Templates oder bei AWS mit CloudFormation. AWS geht nun aber einen Schritt weiter und bietet ein Cloud Development Kit an.
Mit AWS CDK können Cloud Ressourcen mithilfe von TypeScript, Python, Java (Entwicklervorschau) und .NET (Entwicklervorschau) modelliert werden. Dabei können die Entwickler ihre bevorzugten IDEs und Testtools verwenden. Dank Tools wie automatische Vervollständigung und Inline Dokumentation verschwendet man weniger Zeit damit, zwischen Service Dokumentation und dem Code zu wechseln.
Ein super Einstieg in die Thematik bietet der Online Workshop von AWS an. Der Workshop hilft bei der Installation und bietet ein geführtes Beispiel an. Die CDK Reference Dokumentation findet ihr hier. Ich gehe nicht tief in die einzelnen Funktionen von CDK ein. Dies sind sehr gut in den Online Dokumentationen beschrieben.
Das Development Kit ist sehr mächtig, ich möchte dies anhand einem kleinen Beispiel aufzeigen. Vor allem der Unterschied zwischen der Anzahl Codezeilen und der daraus resultierenden CloudFormation Deklaration. Dazu verwende ich Python als Programmiersprache.
Ein kleiner Webserver soll auf einem Container in der AWS Cloud laufen. Für diesen Zweck verwende ich den Fargate Service von AWS. Der Fargate Service sitzt hinter einem Loadbalancer, alle Anfragen werden via Loadbalancer an den Container weiter geleitet. Für das Setup braucht es noch ein VPC mit zwei publik und zwei privaten Subnetzen, denn das ganze soll auch redundant sein.
Der Code in der clxs_fargate_service_stack.py Datei sieht folgendermassen aus:
001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041 042 043 044 045 046 047 048 049 050 051 052 053 054 055 056 057 058 059 060 061 062 063 064 065 066 067 068 069 070 071 072 073 074 075 076 077 078 079 080 081 082 083 084 085 086 087 088 089 090 091 092 093 094 095 096 097 098 099 100 101 102 103 104 105 106 107 108 109 | from aws_cdk import ( core, aws_ec2 as ec2, aws_ecs as ecs, aws_ecs_patterns as ecs_patterns, aws_logs as logs, aws_iam as iam, ) class ClxsFargateServiceStack(core.Stack): def __init__( self , scope: core.Construct, id : str , * * kwargs) - > None : super ().__init__(scope, id , * * kwargs) # The code that defines your stack goes here #Create Subnet for the VPC subnets = [] subnets.append(ec2.SubnetConfiguration( name = "euw1-pr-sub-fargate01" , subnet_type = ec2.SubnetType.PRIVATE, cidr_mask = 24 )) subnets.append(ec2.SubnetConfiguration( name = "euw1-pr-sub-public01" , subnet_type = ec2.SubnetType.PUBLIC, cidr_mask = 24 )) #Create the VPC vpc = ec2.Vpc( self , "Vpc" , max_azs = 2 , cidr = "192.168.0.0/16" , subnet_configuration = subnets, ) # Create the Fargate Cluster cluster = ecs.Cluster( self , 'Ec2Cluster' , vpc = vpc, cluster_name = 'clxs-pr-cluster-fgservice' ) ##Create VPC Flow Log # Setup IAM Policy Statement flow_log_policy_statement = iam.PolicyStatement( effect = iam.Effect.ALLOW, resources = [ "*" ], actions = [ "logs:CreateLogGroup" , "logs:CreateLogStream" , "logs:PutLogEvents" , "logs:DescribeLogGroups" , "logs:DescribeLogStreams" ], ) #Create Policy with the Statement flow_log_policy = iam.ManagedPolicy( self , "MyPolicy" , managed_policy_name = "clxs-pr-pol-vpc_flow_log" , statements = [flow_log_policy_statement]) #Create a IAM Role and attache the Policy vpc_flow_role = iam.Role( self , 'vpc-flow-log' , assumed_by = iam.ServicePrincipal( 'vpc-flow-logs.amazonaws.com' ), role_name = "clxs-pr-role-vpc_flow_log" , managed_policies = [flow_log_policy] ) # Create CloudWatch log group log_group = logs.LogGroup( self , 'log-group' , log_group_name = 'clxs-pr-log-vpc_flow_log' , retention = logs.RetentionDays( 'ONE_YEAR' ) ) # Setup VPC flow logs and give the just created Role vpc_log = ec2.CfnFlowLog( self , 'FlowLogs' , resource_id = vpc.vpc_id, resource_type = 'VPC' , traffic_type = 'ALL' , deliver_logs_permission_arn = vpc_flow_role.role_arn, log_destination_type = 'cloud-watch-logs' , log_group_name = log_group.log_group_name ) #Create the FargateService with the Docker Image saved in the DockerImage Folder fargate_service = ecs_patterns.NetworkLoadBalancedFargateService( self , "FargateService" , cluster = cluster, task_image_options = { 'image' : ecs.ContainerImage.from_asset( "DockerImage" ) }, service_name = "clxs-pr-fgse-website" , memory_limit_mib = 512 , cpu = 256 , desired_count = 1 , ) #Add Port 80 to Security Group fargate_sg = fargate_service.service.connections.security_groups[ 0 ] fargate_sg.add_ingress_rule(ec2.Peer.any_ipv4(), ec2.Port.tcp( 80 )) #Give the Loadbalancer URL back to the CLI core.CfnOutput( self , "LoadBalancerDNS" , value = "http://" + fargate_service.load_balancer.load_balancer_dns_name ) |
In der app.py Datei wird dann die Klasse aus der clxs_fargate_service_stack.py Datei verwendet. In diesem Beispiel werden die AWS Credentials aus der AWS CLI Konfigurationsdatei verwendet. Durch Vererbung und mehrfacher Deklaration könnten wir diesen Stack auch auf andere AWS Regionen anwenden.
01 02 03 04 05 06 07 08 09 10 11 | from aws_cdk import core from clxs_fargate_service.clxs_fargate_service_stack import ClxsFargateServiceStack app = core.App() ClxsFargateServiceStack(app, "clxs-fargate-service" , env = { 'region' : 'eu-west-1' } ) app.synth() |
Das Dockerfile ist auch sehr einfach gehalten, so sieht das Dockerfile aus. Wir nehmen das nginx:alpine Image, kopieren ein index.html in das nginx Verzeichnis und stellen noch den http Port bereit.
1 2 3 | FROM nginx:alpine COPY index.html /usr/share/nginx/html/ EXPOSE 80 |
Nun kann mit «cdk diff» geschaut werden was alles CloudFormation im Hintergrund erstellt, jedoch wird der Stack noch nicht ausgeführt. Mit «cdk deploy» wird dann der Stack in CloudFormation ausgeführt. Vor dem ersten Deploy muss noch ein «cdk bootstrap» ausgeführt werden. Das Bootstrap erstellt ein S3 Bucket um das Docker Image hoch zu laden, auch kreiert das Bootstrap einen eigenen Stack in CloudFormation. Mit «cdk synth» kann der CloudFormation Code angeschaut werden der bei einem Deploy ausgeführt wird. Dies sind einige Zeilen mehr als der Python Code. Ich möchte niemand langweilen mit dem CloudFormation Code, der ist 665 Zeilen lang.
[happy c0ding]!