Blog
Ein Tutorial zur Automatisierung von Betriebsaufgaben in AWS Lambda

In diesem Blogbeitrag möchte ich Ihnen einen Ansatz vorstellen, mit dem Sie ganz einfach eine operative Aufgabe in AWS entwickeln, testen und bereitstellen können. Zum Beispiel, um AWS-Ressourcen außerhalb der Geschäftszeiten auszusetzen. Um Backups von AWS-Services zu planen, die nicht Ihren Anforderungen entsprechen. Oder um den Missbrauch der Befugnisse, die Benutzer normalerweise erhalten, zu entdecken und sofortige Maßnahmen zu ergreifen.
Lambci hat eine großartige Lösung entwickelt, um eine Lambda-Funktion lokal mit Docker zu testen. Operative Lambda-Funktionen nutzen auch andere AWS-Services. Nachdem Sie Ihre Lambda-Funktion geschrieben haben, müssen Sie ein Skript erstellen, um die Funktion bereitzustellen, und dazu gehört das Schreiben einer Richtlinie und einer Rolle. Es dauert auch einige Zeit, die Funktion zu aktualisieren und aufzurufen, nur um sie zu testen. Wäre es nicht toll, wenn die Lambda-Funktion in Docker mit der gleichen Rolle ausgeführt würde? Sogar die Bereitstellung von Rollen und Richtlinien wird zu einer einfachen Aufgabe, und Sie sind kurz davor, Ihre erste operative Aufgabe bereitzustellen. Die meisten DevOps-Ingenieure verstehen Python oder sind zumindest in der Lage, Python-Skripte zu kopieren, einzufügen und umzuschreiben. Deshalb habe ich mich für Python und nicht für eine andere unterstützte Sprache entschieden.
Da botocore in der AWS Lambda-Produktionsumgebung manchmal veraltet ist und lambci docker-lambda logischerweise auch, wurde ein weiterer Bonus hinzugefügt, um Ihnen eine einfache Möglichkeit zu geben, botocore als Teil Ihrer Funktion zu installieren, unabhängig vom Betriebssystem, das Sie verwenden. Warum hat mich das interessiert? Zum Zeitpunkt des Schreibens wollte ich meine Entwicklungs-RDS-Instanzen stoppen, und die rds.stop_instances() ist Teil einer neueren botocore-Bibliothek.
Ich hätte einfach eine Cloudformation- oder Terraform-Vorlage verteilen können, aber dieser Blogpost ist nicht nur dazu da, Ihre Funktion in der Produktion einzusetzen. Vielmehr hat er die Absicht, mehr über AWS zu erfahren als nur die Bereitstellung einer Lambda-Funktion. Wenn Sie durch die Verwendung der AWS CLI entdeckt:
- Zeitersparnis, da Sie nicht viel Dokumentation lesen müssen
- Erhalten Sie direktes Feedback beim Lernen
- Einfacher Rückgriff auf die Verwaltungskonsole (UI)
- Stapeln verschiedener Ressourcen, anstatt den gesamten Stapel auf einmal zu verteilen
- Schließlich wird die Erstellung von Terraform oder Cloudformation zu einer einfachen Aufgabe
Voraussetzungen
Bevor wir beginnen, müssen wir einige Variablen festlegen. Ändern Sie sie hier, wenn Sie sie später in dieser Anleitung nicht ersetzen wollen. Oder legen Sie sie in einer Datei settings.cfg ab und initialisieren Sie sie mit dem Befehl source settings.cfg.
ACCOUNT_ID=1234567890
AWS_PROFILE=yourprofile
AWS_REGION=eu-west-1
FUNCTIONNAME=MyOpsFunction
ROLENAME=LambdaExecutionRole
STACKNAME=RolePolicyForMyDocker
Andere Voraussetzungen: Überprüfen Sie, ob die folgenden Befehle auf Ihrer Kommandozeile funktionieren: jq, docker, zip, aws. Andernfalls verwenden Sie Google, um zu erfahren, wie Sie diese Tools installieren und verwenden. Außerdem sollten Sie über ein grundlegendes Verständnis von Docker, AWS im Allgemeinen und der AWS CLI mit mehreren Profilen verfügen.
Legen Sie außerdem Ihr Standard-Aws-Profil mit dem folgenden Befehl fest, andernfalls müssen Sie in jedem aws-Befehl -profile hinzufügen. Sie können diesen Schritt überspringen, wenn Sie nur das Standardprofil haben.
export AWS_DEFAULT_PROFILE=<yourprofilename>
Stellen Sie die Rolle in AWS bereit
Für das Richtliniendokument assume_role.json kopieren Sie den folgenden Code und fügen ihn in die Datei assume_role.json ein. Ersetzen Sie ihn durch Ihre account_id (ähnlich wie: 111122223333) und durch Ihren Benutzernamen, der dem Profil entspricht, das Sie in der CLI verwenden. Dies ermöglicht es Diensten wie Lambda und Ihrem Benutzernamen, diese Rolle zu übernehmen. Übernehmen bedeutet: temporäre Anmeldeinformationen generieren und sie in diesem Fall für den Zugriff auf AWS-Ressourcen verwenden. Es erlaubt auch Lambda, die gleiche Rolle zu übernehmen.
{
"Version" : "2012-10-17",
"Statement": [ {
"Effect": "Allow",
"Principal": {
"Service": [ "lambda.amazonaws.com" ]
},
"Action": [ "sts:AssumeRole" ]
},
{
"Effect": "Allow",
"Principal": {
"AWS": [ "arn:aws:iam::<account_id>:user/<username>" ]
},
"Action": [ "sts:AssumeRole" ]
} ]
}
Die Richtlinie selbst wird in die Datei lambda_policy.json kopiert, die Lambda derzeit Zugriff auf das Schreiben von Protokollen und das Auflisten aller Buckets in Ihrem Konto gibt. Sie sollten bei der Entwicklung immer das Prinzip des "Least Privilege" im Hinterkopf haben. Wenn Sie also nur ec2-Instanzen stoppen wollen, geben Sie als Aktion nicht
{
"Version": "2012-10-17",
"Statement": [ {
"Sid": "BaseLambdaPolicyForLogs",
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:*:*:*"
},
{
"Sid": "YourLambdaPolicy",
"Effect": "Allow",
"Action": [
"s3:ListAllMyBuckets"
],
"Resource": "*"
} ]
}
Wenn Sie die Dateien lambda_policy.json und assume_role.json mit den entsprechenden Berechtigungen erstellt haben, können Sie die folgenden Befehle ausführen, um die Rolle und die Richtlinie bei AWS bereitzustellen. Stellen Sie sicher, dass die Variablen korrekt gesetzt sind.
aws iam create-role
--role-name ${ROLENAME}
--assume-role-policy-document file://assume_role.json
aws iam put-role-policy
--role-name ${ROLENAME}
--policy-name ${ROLENAME}-policy
--policy-document file://lambda_policy.json
Entwickeln Sie eine grundlegende Lambda-Funktion
Erstellen Sie einen neuen Ordner und eine neue Datei: lambda_function/lambda_function.py, und kopieren Sie den folgenden Inhalt. Er enthält keine wirkliche Funktionalität, sondern gibt vorerst nur die aktuelle boto3- und botocore-Version aus. Auch die Protokollierung ist enthalten, da dies eine sehr nützliche Bibliothek für die strukturierte Protokollierung ist.
import boto3
import json
import botocore
import logging
log = logging.getLogger(__name__)
log.setLevel(logging.INFO)
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(name)s (%(lineno)s) - %(levelname)s: %(message)s",
datefmt='%Y.%m.%d %H:%M:%S')
def lambda_handler(event, context):
log.info("Boto3 Version: " + boto3.__version__)
log.info("Botocore Version: " + botocore.__version__)
Testen Sie die Lambda-Funktion
Lassen Sie uns zunächst testen, ob die Lambda-Funktion funktioniert, so wie Lambci es vorgesehen hat. Führen Sie den Befehl aus dem Projektverzeichnis aus. Wenn Sie diesen Befehl nicht aus dem Projektverzeichnis ausführen, erhalten Sie die Fehlermeldung: "Unable to import module 'lambda_function': Kein Modul namens lambda_function"
docker run -v "$PWD"/lambda_function:/var/task lambci/lambda:python3.6
Ausgabe:
START RequestId: ... Version: $LATEST
[INFO] 2017-09-28T18:55:22.891Z 2e29785e-c4ef-41cb-ab8c-2fbe56aec341 Boto3 Version: 1.4.4
[INFO] 2017-09-28T18:55:22.891Z 2e29785e-c4ef-41cb-ab8c-2fbe56aec341 Botocore Version: 1.5.19
END RequestId: ...
REPORT RequestId: ... Duration: 217 ms Billed Duration: 300 ms
Memory Size: 1536 MB Max Memory Used: 20 MB
null
Übrigens, die Null am Ende kommt daher, dass die lambda_function nichts zurückgibt.
Botocore installieren und erneut ausführen
Gehen Sie in der Befehlszeile in den Ordner lambda_function und führen Sie dann den folgenden Befehl aus. Mit -v "$PWD":/localdir mountet Docker das aktuelle Verzeichnis nach /localdir im Container. python:3.6-alpine ist das Docker-Image auf Dockerhub, das wir verwenden werden. pip install botocore -t /localdir ist der Befehl, der im Container ausgeführt wird. Er lädt botocore herunter und installiert es mit -t in den angegebenen Zielordner.
docker run -v "$PWD"/lambda_function:/localdir python:3.6-alpine
pip install botocore boto3 -t /localdir
Wenn Sie viele Pakete zu installieren haben oder die Paketliste vom Befehl trennen möchten, ist die Datei requirements.txt eine Option.
Legen Sie es in das Stammverzeichnis des Ordners und ersetzen Sie die Paketnamen im obigen Befehl durch
-r /localdir/requirements.txt.Nachdem Sie den vorherigen Docker-Befehl ausgeführt haben, rufen Sie nun erneut die Lambda-Funktion auf und beachten Sie die Änderung der Versionen. Natürlich werden diese für Sie unterschiedlich sein, da sie oft aktualisiert werden.
docker run -v "$PWD"/lambda_function:/var/task lambci/lambda:python3.6
Ausgabe:
START RequestId: ... Version: $LATEST
[INFO] 2017-09-28T18:57:22.891Z 2e29785e-c4ef-41cb-ab8c-2fbe56aec341 Boto3 Version: 1.4.7
[INFO] 2017-09-28T18:57:22.891Z 2e29785e-c4ef-41cb-ab8c-2fbe56aec341 Botocore Version: 1.7.19
END RequestId: ...
REPORT RequestId: ... Duration: 217 ms Billed Duration: 300 ms
Memory Size: 1536 MB Max Memory Used: 20 MB
Lambda mit Rolle ausführen
Stellen Sie sicher, dass Sie die Rolle bei AWS bereitgestellt und die Variablen ${ACCOUNT_ID}, ${ROLENAME}, ${AWS_REGION} ersetzt oder gesetzt haben. Der wichtigste Teil des Skripts ist die aws sts assume-role, die neue API-Zugriffsschlüssel generiert, die an Docker gesendet werden, wenn es den Container mit Ihrer Lambda-Funktion ausführt.
temp_role=$( aws sts assume-role
--role-arn "arn:aws:iam::${ACCOUNT_ID}:role/${ROLENAME}"
--role-session-name "justsomerandomcharacters" )
export A_ACCESS_KEY_ID=$(echo $temp_role | jq .Credentials.AccessKeyId | xargs)
export A_SECRET_ACCESS_KEY=$(echo $temp_role | jq .Credentials.SecretAccessKey | xargs)
export A_SESSION_TOKEN=$(echo $temp_role | jq .Credentials.SessionToken | xargs)
env | grep -i A_
docker run -e AWS_ACCESS_KEY_ID=${A_ACCESS_KEY_ID}
-e AWS_SECRET_ACCESS_KEY=${A_SECRET_ACCESS_KEY}
-e AWS_REGION=${AWS_REGION}
-e AWS_SESSION_TOKEN=${A_SESSION_TOKEN}
-v "$PWD"/lambda_function:/var/task lambci/lambda:python3.6
Jetzt können Sie Ihrer Lambda-Funktion einige weitere Zeilen hinzufügen und überprüfen, ob Ihre Lösung funktioniert. Der folgende Befehl liest alle S3-Buckets und listet sie in der Ausgabe auf. Wenn Sie keine S3-Buckets haben, sollten Sie zunächst eines erstellen. Bonuspunkte erhalten Sie, wenn Sie die Berechtigung zu Ihrer Richtlinie hinzufügen und boto3 verwenden, um diesen Bucket zu erstellen.
...
def lambda_handler(event, context):
log.info("Boto3 Version: " + boto3.__version__)
log.info("Botocore Version: " + botocore.__version__)
s3r = boto3.resource('s3')
r = []
for bucket in s3r.buckets.all():
r.append(bucket.name)
return r
Bereitstellen auf AWS
Sie können Ihre Funktion nun über die Verwaltungskonsole, Cloudformation oder andere Tools oder über die Befehlszeilenschnittstelle (CLI) bereitstellen, die im Folgenden verwendet wird. Da die Rolle, die wir zuvor erstellt haben, auch Lambda erlaubt, die Rolle zu übernehmen, erstellen wir einfach die Funktion und rufen sie manuell auf.
Zunächst müssen wir die Funktion wegen der Bibliotheken, die wir einbinden müssen, zippen. Nachdem wir die ZIP-Datei erstellt haben, können wir die Funktion erstellen.
cd lambda_function && zip -r9 ../lambda_function.zip * && cd ..
aws lambda create-function
--function-name ${FUNCTIONNAME}
--runtime python3.6
--role arn:aws:iam::${ACCOUNT_ID}:role/${ROLENAME}
--handler lambda_function.lambda_handler
--zip-file fileb://lambda_function.zip
Um die Lambda-Funktion manuell aufzurufen und die Ergebnisse zu überprüfen:
aws lambda invoke
--function-name ${FUNCTIONNAME}
output.txt
cat output.txt
Ausgabe:
["some", "of", "your", "buckets"]
Zur Aktualisierung der Lambda-Funktion in AWS, wenn Sie Änderungen vorgenommen und lokal getestet haben:
cd lambda_function && zip -r9 ../lambda_function.zip * && cd ..
aws lambda update-function-code
--function-name ${FUNCTIONNAME}
--zip-file fileb://lambda_function.zip
In der Verwaltungskonsole können Sie Ihre Lambda-Funktion finden und alle Details überprüfen. Dort können Sie die Funktion auch aufrufen und die Protokolle einsehen.
Planen Sie die Lambda-Funktion
Jetzt, wo die Funktion vorhanden und korrekt getestet ist, können wir sie planen. Es sind 2 Funktionen verfügbar: cron() und rate(). cron() ist vor allem dann nützlich, wenn etwas zu einer bestimmten Zeit jede Stunde, jeden Tag, jede Woche geschehen muss, während rate() dazu da ist, alle x Minuten oder x Stunden auszulösen. Einige Beispiele und weitere Informationen finden Sie in der AWS-Dokumentation über geplante Ereignisse. Da wir nichts Besonderes tun und ich gerne einen Beweis dafür hätte, dass alles läuft, empfehle ich rate(5 Minuten) zu verwenden.
aws events put-rule
--name ${FUNCTIONNAME}Trigger
--schedule-expression "rate(5 minutes)"
aws lambda add-permission
--function-name ${FUNCTIONNAME}
--statement-id ${FUNCTIONNAME}Trigger
--action 'lambda:InvokeFunction'
--principal events.amazonaws.com
--source-arn arn:aws:events:${AWS_REGION}:${ACCOUNT_ID}:rule/${FUNCTIONNAME}Trigger
aws events put-targets
--rule ${FUNCTIONNAME}Trigger
--targets "Id"="1","Arn"="arn:aws:lambda:${AWS_REGION}:${ACCOUNT_ID}:function:${FUNCTIONNAME}"
Gehen Sie zur Management-Konsole, gehen Sie zu CloudWatch, gehen Sie zu Ereignisse und dann zu Regeln. Klicken Sie auf das Ereignis, das Sie gerade erstellt haben, und überprüfen Sie die Metriken und andere Details.
Aufräumen
Vergessen Sie nicht, aufzuräumen, wenn Sie mit Ihrer Funktion fertig sind, oder die Richtlinie der Rolle zu aktualisieren, indem Sie Ihr Prinzip der Zugriffsübernahme aufheben. Diese Befehle sind auch nützlich, wenn Sie einen Fehler gemacht haben und etwas rückgängig machen müssen.
aws events remove-targets
--rule ${FUNCTIONNAME}Trigger
--ids 1
aws events delete-rule
--name ${FUNCTIONNAME}Trigger
aws lambda remove-permission
--function-name ${FUNCTIONNAME}
--statement-id ${FUNCTIONNAME}Trigger
aws lambda delete-function
--function ${FUNCTIONNAME}
aws iam delete-role-policy
--role-name ${ROLENAME}
--policy-name ${ROLENAME}-policy
aws iam delete-role
--role-name ${ROLENAME}
Einpacken
Fühlt es sich nützlich an? Ich denke schon. Fühlt es sich komplex an für eine einfache Funktion? Ja, das tut es sicherlich. Aber mit dem Wissen, das Sie jetzt haben, könnten Sie leicht ein Terraform- oder Cloudformation-Skript schreiben und eine automatisierte Bereitstellung mit einem beliebigen Bereitstellungstool vornehmen. Wenn Sie dies gut strukturieren und eine Menge operativer Aufgaben hochladen und pflegen, wird es zu einer leichten Aufgabe.
Bild von lcarissimi aus Pixabay
Verfasst von
Martijn van Dongen
Contact