Portfolio-Webseite
In diesem Artikel geht es um die Entstehung und Entwicklung meiner Portfolio-Webseite.
Die ersten Entwürfe habe ich in canva gestaltet um zu schauen, wie ich die relativ einfache Webseite designe. Es war schnell klar, dass einfache HTML-Seiten, CSS und vielleicht etwas Javascript ausreichen sollten. Ich entschied mich die Umsetzung mit Parcel.js und TailWindCSS zu entwickeln.
Als Feature möchte ich gerne für ausgewählte Besucher (Identifizierung über spezielle Route) die Credentials für den Read-Only-User meiner gitea-Instanz anzeigen oder besser gleich Links mit automatisiertem Gast-Login. Der Content der HTML-Seite müsste also schon serverseitig mit diesen besonderen Informationen angereichert werden.
Um serverseitig Webseiten auszuliefern boten sich für mich jetzt folgende Möglichkeiten:
- Variante 1: auf meinem Server den vorhandenen nginx-php-Container-Stack nehmen. Hier müsste ich aber die fertige HTML-Seite von Parcel dann zusätzlich mit PHP-Code ausstatten und manuell in .php umwandeln... wird aufwendig und ist nicht sehr elegant.
- Variante 2: AWS Amplify und Nuxt.js... hier wäre die CI/CD-Pipeline einfacher, müsste aber erst lokal Nuxt einrichten und mich in das Thema einarbeiten... auch sehr aufwendig.
- Variante 3: AWS CloudFront mit Lambda@Edge, wo ich den Request/Response modifziere - ich "injiziere" meinen Code über eine Lambda-Function in den HTML-Body ein. Diese Variante ist sehr einfach, da ich die HTML-Website lokal erstellen kann und einfach über CloudFront verteile. Der dynamische Part wird über eine eigene Lambda-Function geregelt, diese muss ich auch selten ändern (die Credentials stehen fest) und der Code ist dafür auch sehr simpel.
Als angehender Cloud-Engineer reizte mich Variante 3 am meisten und diese sollte auch "einfach" und robust umsetzbar sein. Zunächst erstellte ich die notwendigen Resourcen in der AWS Console:
- S3-Bucket als Speichertort für die HTML-,CSS-,JS-Dateien
- CloudFront-Distribution
- Lambda-Function als Lambda@Edge in Python

Dabei gab es folgende Dinge zu beachten:
- Die CloudFront (CF) -Distribution sollte an meine in Route53 gehostete Domain the-cloud-engineer.de angebunden werden, dafür war ein Zertifikate in ACM in der Region us-east-1 notwendig, welches ich für die Root-Domain als auch die www-Subdomain ausstellte. In CF wurden dann bei "Alternate domain names" beide Adressen angegeben.
- In CF unbedingt "Default root object" mit
index.htmlangeben, sonst wird bei http://domain.de nicht direkt auf /index.html umgeleitet, sondern es wird eine leere Seite/Fehlerseite angezeigt. - Die Lambda-Edge-Function muss in us-east-1 sein, es ist nur X86 erlaubt und in der Execution-Role muss zusätzlich edgelambda.amazonaws.com als Principal angegeben werden. Außerdem muss eine explizite Version erstellt werden auf die direkt von CF verwiesen wird.
Doch wo muss die Lambda Edge Function ansetzen? Im Viewer Response ?
- ❌ Limited body access - Can only modify response body in specific scenarios
- ❌ Body size limit: Only 40 KB
- ❌ Runs after caching - Less efficient for content modification
- ❌ Can modify headers and status codes
Im Origin Response!
- ✅ Can modify body
- ✅ Runs after origin responds - Perfect for modifying content before caching
- ✅ Body size limit: Up to 1 MB
- ✅ Modified content gets cached - Improves performance for subsequent requests
- ✅ Can modify headers and status codes along with body
ABER: Origin Response enthält im Body nicht den HTML-Code von der S3-Bucket HTML-Datei! "You can only generate static content or remove the body, but you cannot modify the existing body content."
Daher sieht meine Lösung so aus, dass die Lambda Function selber die HTML-Seite aus dem Bucket einliest und die gewünschten Modifikationen am HTML-Code vornimmt. Und da mein modifizierter HTML-Code sich nicht sehr oft ändert, lohnt sich das Caching der generierten Antwort umso mehr.
Nach dem das Grundgerüst in der AWS Console stand und auch ausgetestet war, ging es nun darum es in eine wartbare Lösung zu übertragen. Hierzu nutze ich das AWS CDK: definiere Infrastructure as Code (IaC) in Python und lasse es über mein Terminal in die AWS Cloud deployen. Mit Hilfe von Amazon Q ging dies recht schnell, Probleme bereitet dann zum Schluss eher die Verknüpfung meines existierenden A/Alias-Records meiner Route53-HostedZone mit der CloudFront Distribution: CDK managet vornehmlich nur die eigene erstellte Infrastruktur. Die Lösung war dann eine CustomResource zu definieren.
Ein weiteres Problem waren Code-Updates für die Lambda-Function, wo laut Amazon Q ein einfaches
cdk deploy --hotswap
ausreichen sollte, jedoch die Versionsnummer für die Lambda-Function im Origin Request der CF-Distribution nicht aktualisiert wurde. Amazon Q vermutete eine "Race Condition: The hotswap might update the distribution configuration before the new Lambda version is fully available, [...]" - was für mich persönlich als Problemursache am meisten Sinn ergibt.
--hotswap kurz erklärt: "Hotswap deployments for faster development. This option attempts to perform a faster, hotswap deployment if possible. For example, if you modify the code of a Lambda function in your CDK app, the CDK CLI will update the resource directly through service APIs instead of performing a CloudFormation deployment."
Es werden also nicht regulär wie bei cdk deploy über ein CloudFormation-Template die Resourcen aktualisiert, sondern es wird versucht mittels direkter API-Aufrufe schneller die Resourcen zu aktualisieren.
Die Lösung jetzt lag darin, einfach das reguläre ´cdk deploy´ zu verwenden.
Um die Aktualisierung aber auch im Edge-Netzwerk (von CloudFront) aktuell bekannt zu machen, müssen wir noch eine Invalidation setzen:
aws cloudfront create-invalidation --distribution-id DISTRIBUTION_ID --paths "/index.html*"
Das Sternchen am Ende der index.html umfasst dabei alle Querystrings.
Kurze Zusammenfassung
Ich habe 2 Projekte erstellt:
- CDK-Stack für die Infrastruktur und die Lambda-Function
- HTMl-Webseite mit Parcel.js, über ein gitea Action-Workflow werden die gebundelten Dateien in S3 kopiert Nach jeder Code-Änderung nehme ich eine Invalidation vor, um die Caches in den Edge-Locations "zu leeren" und den aktuellen Code "zu verteilen"