Bei meiner Arbeit mit Cloudfromation habe ich viel nützliche Tipps gesammelt, die ich gern teilen will.
1. Eine IDE mit Autocomplete verwenden
Für die Jetbrains IDEs wie PHPStorm, Webstorm oder IntelliJ gibt es ein sehr gutes AWS Cloudfromation Plugin namens AWS Cloudfromation von Leonid Shalupov:
Dieses biete Autocomplete für die IDE an und eine automatische Formatüberprüfung, ob Attribute gesetzt werden, die dort nicht erlaubt sind. Auf diesem weg kann man viele Fehler schon vor dem validieren finden und spart viel Zeit.
2. AWS-CLI Befehle verwenden
Um die Templates in Cloudfromation zu testen und auch zu deployen sollte man am besten die AWS CLI verwenden um viel Zeit zu sparen.
Die wichtigsten Befehle für Cloudformation sind:
### stack anlegen: #https://docs.aws.amazon.com/cli/latest/reference/cloudformation/create-stack.html aws cloudformation create-stack --template-body file://infrastructure/cloudformation.yml --stack-name myteststack #### template validieren: aws cloudformation validate-template --template-body file://infrastructure/cloudformation.yml ### status des stacks abrufen: aws cloudformation describe-stacks --stack-name myteststack ### update stack aws cloudformation update-stack --template-body file://infrastructure/cloudformation.yml --stack-name myteststack ### delete stack aws cloudformation delete-stack --stack-name myteststack ## output ändern: # json, table, text möglich aws cloudformation describe-stacks --stack-name myteststack --output=text
3. mehrzeiligen Text lesbar schreiben
files: /var/www/html/public/index.php: content: >- <html> <body> <h1>Welcome to the AWS CloudFormation PHP Sample</h1> <p/> <?php // Print out the current data and time print "The Current Date and Time is: <br/>"; print date("g:i A l, F j Y."); ?> <?php phpinfo(); ?> </body> </html> mode: '000600' owner: apache group: apache
4. Variablen in Strings verwenden
/etc/httpd/conf.d/http-vhost.conf: content: !Sub - >- <Directory "/var/www/html/public"> Options Indexes FollowSymLinks AllowOverride All Require all granted </Directory> <VirtualHost *:80> DocumentRoot "/var/www/html/public" ServerName "${Domain}" </VirtualHost> - { Domain: !Ref Domain }
5. Zwei Stacks zum Testen verwenden
Um Zeit zu sparen verwende ich immer 2 Stacks zum Entwickeln, diese werden abwechseln erstellt und gelöscht, weil sonst auf das Löschen des Stacks gewartet werden muss beim Entwickeln.
CLI Befehle:
aws cloudformation create-stack --template-body file://infrastructure/cloudformation.yml --stack-name myteststack aws cloudformation delete-stack --stack-name myteststack aws cloudformation create-stack --template-body file://infrastructure/cloudformation.yml --stack-name myteststack2 aws cloudformation delete-stack --stack-name myteststack2 aws cloudformation create-stack --template-body file://infrastructure/cloudformation.yml --stack-name myteststack ....
6. Der Aufbau eines Cloudfromation Templates
Die wichtigste Seite der Cloudformation Dokumentation ist die für den Aufbau eines Cloudformation Templates.
"Resources": {
"MyInstance": {
"Type": "AWS::EC2::Instance",
"Metadata" : {
"AWS::CloudFormation::Init" : {
"config" : {
"packages" : {
:
},
"groups" : {
:
},
"users" : {
:
},
"sources" : {
:
},
"files" : {
:
},
"commands" : {
:
},
"services" : {
:
}
}
}
},
"Properties": {
:
}
}
}
Hier wird u.a. beschrieben, in welcher Reihenfolge die Konfigurationsabschnitte abgearbeitet werden:
- packages
- groups
- users
- sources
- files
- commands
- services
In dieser Reihenfolge sollten auch im Template die Konfigurationsabschnitte definiert werden.
7. Cronjob anlegen in Amazon Linux 2
Hier sind 2 Beispiele, wie man für verschiedene User (root und ec2-user) Crontabs anlegt:
InstallCrontab: files: /var/spool/cron/root: content: !Sub | # m h dom mon dow command 39 1,13 * * * certbot renew --no-self-upgrade > /dev/null 2>&1 mode: '000600' owner: root group: root /var/spool/cron/ec2-user: content: !Sub | # m h dom mon dow command */5 * * * * /usr/local/bin/aws-scripts-mon/mon-put-instance-data.pl --mem-avail --swap-used --disk-space-avail --disk-path=/ --from-cron > /dev/null 2>&1 mode: '000600' owner: ec2-user group: ec2-user
8. yaml oder json
Cloudformation bietet 2 Formate an, die sich tatsächlich sehr unterscheiden, json und yaml. Ich habe mich für yaml entscheiden, weil da weniger zu schreiben ist und der Syntax besser zu lesen. Allerdings habe ich beim Entiwcklen gemerkt, dass fast alle Fragen auf Stackoverflow in json sind, was einen Nachteil dargestellt hat.
Noch besser ist es allerdings:
9. ein Framework verwenden
Leider endet man immer mit einer riesigen Datei in yaml oder json ohne eine Möglichkeit den Inhalt aufzuteilen oder andere Dateien zu inkludieren.
Deswegn würde ich ein Framework wie lono empfehlen, um den Code einfacher zu halten und übersichtlich.
Die Möglichkeit ein Porjekt auf mehrere Dateien aufzusplitten erleichtert auch die Arbeit mit einem VCS wie GIT.
10. Debugging von EC2 Instanz Fehlern
Leider bekommt man oft die wenig sagende Fehlermeldung:
"Failed to receive 1 resource signal(s) within the specified duration"
Hier sollte man dann das EC2 System Log aufrufen und nach der richtigen Fehlermeldung suchen nach der Stelle „Cloud-init“:
11. Cloudwatch Alarm erstellen mit CloudWatchMonitoringScripts
Um seine Infrastruktur abzusichern, kann man Cloudwatch Alarme erstellen, die einem z.B. per Email (SNS) benachrichten, wenn die CPU Auslastung zu stark ist oder zu wenig freier RAM-Speicher vorhanden ist oder die Festplatte voll läuft.
Um die Daten für die Festplatte un den RAM zu erhalten, muss man die CloudWatchMonitoringScripts installieren und einen Cronjob einrichten.
Dies kann man mit Cloudfromation wie folgt machen:
InstallAwsCloudwatchAgent: commands: downloadCloudWatchMonitoringScripts: command: "sudo curl -o /tmp/CloudWatchMonitoringScripts-1.2.2.zip https://aws-cloudwatch.s3.amazonaws.com/downloads/CloudWatchMonitoringScripts-1.2.2.zip" unzipCloudWatchMonitoringScripts: command: "sudo unzip /tmp/CloudWatchMonitoringScripts-1.2.2.zip -d /usr/local/bin" packages: yum: perl-Switch: [] perl-DateTime: [] perl-Sys-Syslog: [] perl-LWP-Protocol-https: [] perl-Digest-SHA.x86_64: [] files: /var/spool/cron/root: content: !Sub | # m h dom mon dow command */5 * * * * /usr/local/bin/aws-scripts-mon/mon-put-instance-data.pl --mem-avail --swap-used --disk-space-avail --disk-path=/ --from-cron > /dev/null 2>&1 mode: '000644' owner: root group: root
Und man kann dann einen Alarm anlegen:
CPUAlarm: Type: AWS::CloudWatch::Alarm Properties: AlarmName: MyCPUAlarm AlarmDescription: CPU alarm for ec2 instance AlarmActions: - arn:aws:sns:xxx MetricName: CPUUtilization Namespace: AWS/EC2 Statistic: Average Period: '300' EvaluationPeriods: '3' Threshold: '50' ComparisonOperator: GreaterThanThreshold TreatMissingData: missing Dimensions: - Name: InstanceId Value: Ref: WebServerInstance MemoryAlarm: Type: AWS::CloudWatch::Alarm Properties: AlarmName: MyMemoryAlarm AlarmDescription: Memory alarm for ec2 instance AlarmActions: - arn:aws:sns:xxx MetricName: MemoryAvailable Namespace: System/Linux Statistic: Average Period: '300' EvaluationPeriods: '3' Threshold: '200' ComparisonOperator: LessThanOrEqualToThreshold TreatMissingData: missing Dimensions: - Name: InstanceId Value: Ref: WebServerInstance DiskSpaceAlarm: Type: AWS::CloudWatch::Alarm Properties: AlarmName: MyDiskSpaceAlarm AlarmDescription: Free Disk Space alarm for ec2 instance AlarmActions: - arn:aws:sns:xxxx MetricName: DiskSpaceAvailable Namespace: System/Linux Statistic: Average Period: '300' EvaluationPeriods: '3' Threshold: '5' ComparisonOperator: LessThanOrEqualToThreshold TreatMissingData: missing Dimensions: - Name: InstanceId Value: Ref: WebServerInstance - Name: Filesystem Value: /dev/xvda1 - Name: MountPath Value: /