Elastic Webstack mit CloudFormation

Mit AWS kann man relativ einfach einen elastischen (hochverfügbareren und skallierbaren) Webstack deployen. Als Tool wollen wir dazu CloudFormation einsetzen. Der Fokus soll dabei auf Elastic Load Balancing (ELB) und Auto Scaling liegen.

Webstack Overview

HA Webstack

Elastizität

Elastic oder auch elastisch ist ein Setup dann, wenn es selbstständig auf eine steigende oder sinkende Last reagierten kann. Bei hoher Last werden zusätzliche Ressourcen angefordert während bei geringer Last Ressourcen abgebaut werden. Liegt die Reaktionszeit innerhalb weniger Minuten kann man von einem elastischen Setup sprechen. Zusäzlich werden ausfallende Server automatisch ersetzt.

Ein elastisches Setup ist daher skalierbar und ausfallsicher.

Der kurzlebige Server

Ein neuer Server wird beschafft, physikalisch angeschlossen, installiert und konfiguriert und läuft dann über Jahre hinweg. Dieser Ansatz ist wenig flexibel. Der Server wird sehr unterschiedlich ausgelastet, so dass Benutzer manchmal länger und manchmal kürzer auf eine Antwort warten müssen. Werden die Antwortzeiten über einen längeren Zeitraum zu schlecht, wird ein zweiter Server angeschafft. Generell ist der Server eigentlich nur werktags zwischen 8 und 18 Uhr ausgelastet, da er eine interne Applikation im Unternehmen anbietet.

Der kurzlebige Server dagegen wird nur gestartet wenn er benötigt wird. So wird die typische Lebenszeit eines virtuellen Servers eher in Tagen gemessen als in Jahren. Kurzlebige Server benötigen Automatisierung. Vom Beschaffen bis zur Konfiguration muss alles automatisch geschehen.































Schritt langlebig kurzlebig
Beschaffung Telefonat mit Lieferant AWS API Request
Verkabeln System-Administrator -
Installieren System-Administrator Booten von Image (AMI)
Konfigurieren System-Administrator Puppet / Chef / Skript

Auto Scaling

Eine Auto Scaling Gruppe ist eine Liste von EC2 Instanzen die nach einer Schablone (Launch Configuration) erzeugt werden. Eine Auto Scaling Gruppe kennt ihre minimale und maximale Größe. Außerdem weiß sie über denn IST und SOLL Zustand der aktuellen Instanzen bescheid. Ein Beispiel:


































Minimum Maximum SOLL IST Interne Aktion
1 3 2 1 Neue Instanz nach Schablone starten
1 3 2 2 Nichts passiert
1 3 1 2 Eine Instanz wird terminiert

Ein CloudWatch Alarm kann über eine Aktion SOLL um 1 erhöhen oder SOLL um 1 reduzieren wodurch wir automatisch nach z.B. CPU Load die Anzahl der Instanzen regeln können.

Elastic Load Balancer (ELB)

Wenn sich die Anzahl der Server in einer Auto Scaling Gruppe ständig ändert, wie können wir dann eine Anfrage von einem Kunde überhaupt an den richtigen Server durchreichen? Dabei helfen Load Balancer. Instanzen aus der Auto Scaling Gruppe melden sich beim Load Balancer an. Der Load Balancer führ alle t Sekunden einen Healthcheck durch und prüft ob die Instanz noch verfügbar ist. Erreicht jetzt eine Anfrage den Load Balancer schaut dieser nach, welche Instanzen verfügbar sind und leitet die Anfrage an einen der Server weiter. ELB ist ein von AWS angebotener und hochverfügbarer Service.

Vereinfachtes Setup

Auto Scaling

CloudFormation Template

{
    "AWSTemplateFormatVersion": "2010-09-09",
    "Description": "Elastic Webstack",
    "Parameters": {
        "AutoScalingDesiredCapacityParameter": {
            "Description": "Desired Capacity for Auto Scaling",
            "Type": "Number",
            "Default": "2"
        },
        "AutoScalingMaxSizeParameter": {
            "Description": "Max Size for Auto Scaling",
            "Type": "Number",
            "Default": "3"
        },
        "AutoScalingMinSizeParameter": {
            "Description": "Min Size for Auto Scaling",
            "Type": "Number",
            "Default": "1"
        },
        "KeyParameter": {
            "Description": "Key name",
            "Type": "String"
        },
        "AMIParameter": {
            "Description": "AMI id (default works only in eu-west-1)",
            "Type": "String",
            "Default": "ami-6e7bd919"
        },
        "VPCParameter": {
            "Description": "VPC id",
            "Type": "String"
        },
        "SubnetsParameter": {
            "Description": "Subnet ids (separated by commas)",
            "Type": "CommaDelimitedList"
        }
    },
    "Resources": {
        "ELBSecurityGroup": {
            "Type": "AWS::EC2::SecurityGroup",
            "Properties": {
                "GroupDescription": "elastic-loadbalancer",
                "SecurityGroupEgress": [{
                    "IpProtocol": "-1",
                    "CidrIp": "0.0.0.0/0"
                }],
                "SecurityGroupIngress": [{
                    "CidrIp": "0.0.0.0/0",
                    "FromPort": 80,
                    "IpProtocol": "tcp",
                    "ToPort": 80
                }],
                "VpcId": {"Ref": "VPCParameter"}
            }
        },
        "LoadBalancer": {
            "Type": "AWS::ElasticLoadBalancing::LoadBalancer",
            "Properties": {
                "CrossZone": true,
                "HealthCheck": {
                    "HealthyThreshold": "2",
                    "Interval": "15",
                    "Target": "HTTP:8888/",
                    "Timeout": "10",
                    "UnhealthyThreshold": "2"
                },
                "LoadBalancerName": "elastic-loadbalancer",
                "Listeners": [{
                    "InstancePort": "8888",
                    "InstanceProtocol": "HTTP",
                    "LoadBalancerPort": "80",
                    "Protocol": "HTTP"
                }],
                "Scheme": "internet-facing",
                "SecurityGroups": [{ "Ref": "ELBSecurityGroup" }],
                "Subnets": {"Ref": "SubnetsParameter"}
            }
        },
        "IAMRole": {
            "Type": "AWS::IAM::Role",
            "Properties": {
                "AssumeRolePolicyDocument": {
                    "Version": "2012-10-17",
                    "Statement": [{
                        "Effect": "Allow",
                        "Principal": {
                            "Service": [ "ec2.amazonaws.com" ]
                        },
                        "Action": [ "sts:AssumeRole" ]
                    }]
                },
                "Path": "/",
                "Policies": []
            }
        },
        "InstanceProfile": {
            "Type": "AWS::IAM::InstanceProfile",
            "Properties": {
                "Path": "/",
                "Roles": [{"Ref": "IAMRole"}
                ]
            }
        },
        "EC2SecurityGroup": {
            "Type": "AWS::EC2::SecurityGroup",
            "Properties": {
                "GroupDescription": "elastic-instances",
                "SecurityGroupEgress": [{
                    "IpProtocol": "-1",
                    "CidrIp": "0.0.0.0/0"
                }],
                "SecurityGroupIngress": [{
                    "CidrIp": "0.0.0.0/0",
                    "FromPort": 22,
                    "IpProtocol": "tcp",
                    "ToPort": 22
                }, {
                    "FromPort": 8888,
                    "IpProtocol": "tcp",
                    "SourceSecurityGroupId": {"Ref": "ELBSecurityGroup"},
                    "ToPort": 8888
                }],
                "VpcId": {"Ref": "VPCParameter"}
            }
        },
        "LaunchConfiguration": {
            "Type": "AWS::AutoScaling::LaunchConfiguration",
            "Properties": {
                "AssociatePublicIpAddress": true,
                "EbsOptimized": false,
                "IamInstanceProfile": {"Ref": "InstanceProfile"},
                "ImageId": {"Ref": "AMIParameter"},
                "InstanceType": "t2.micro",
                "KeyName": {"Ref": "KeyParameter"},
                "SecurityGroups": [{ "Ref": "EC2SecurityGroup" }],
                "UserData": { "Fn::Base64": { "Fn::Join": ["", [
                    "#!/bin/bash -ex\n",
                    "mkdir -p /srv\n",
                    "cd /srv\n",
                    "python -m SimpleHTTPServer 8888 &\n"
                ] ] } }
            }
        },
        "AutoScalingGroup": {
            "Type": "AWS::AutoScaling::AutoScalingGroup",
            "Properties": {
                "AvailabilityZones": { "Fn::GetAZs": { "Ref": "AWS::Region" } },
                "DesiredCapacity": {"Ref": "AutoScalingDesiredCapacityParameter"},
                "LaunchConfigurationName": { "Ref": "LaunchConfiguration" },
                "MaxSize": {"Ref": "AutoScalingMaxSizeParameter"},
                "MinSize": {"Ref": "AutoScalingMinSizeParameter"},
                "VPCZoneIdentifier": {"Ref": "SubnetsParameter"},
                "LoadBalancerNames": [{"Ref": "LoadBalancer"}]
            }
        }
    }
}

Ergebnis

  1. Eine Instanz ist bei der Auto Scaling Gruppe angemeldet.

Auto Scaling Group

  1. Der Load Balancer meldet ebenfalls eine Instanz. Der Healthcheck war erfolgreich und die Instanz ist somit InService. (Es kann einige Zeit dauern bis die Instanz bei dir als InService angezeigt wird).

Load Balancer

Fazit

Mit AWS kann man mit wenig Aufwandeinen elastischen Webstack erzeugen. Dieser ist nicht nur per se ausfallsicher sondern auch noch skallierbar. Mit Hilfe von CloudWatch Alarmen kann man den SOLL Zustand (Desired Capacity) der Auto Scaling Gruppe dynamisch z.B. in Abhängigkeit der CPU Load der Gruppe anpassen.

Der gezeigte Stack eignet sich nicht nur für Web-Applikationen sondern generell für alle Request / Response Applikationen. Vorraussetzung ist, dass die Server selbst keinen Zustand halten. Zustand gehört in eine Datenbank.

Ähnliche Tags

AutoScaling CloudFormation EC2 ELB Template aws

Teilen

           

RSS

  RSS

Newsletter


Michael Wittig

Michael Wittig

Ich bin Autor von Author of Amazon Web Services in Action. Ich arbeite als Software Engineer und unabhängiger Berater mit dem Fokus auf AWS und DevOps. Engagiere mich!

Fehlt etwas in meinem Artikel? Ich freue mich auf dein Feedback! @hellomichibye oder michael@widdix.de.


Veröffentlicht am