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: /
