Hier ist ein praxistaugliches Deployment-Skript für die Auslieferung einer SPA über CloudFront + S3.
Voraussetzungen
- Als Cloud-Anbieter wird AWS verwendet
- Die Frontend-Build-Artefakte liegen in S3 und werden über CloudFront ausgeliefert
Infrastruktur
S3
- Bucket-Versionierung: deaktiviert
- Verschlüsselung: SSE-S3
- Block Public Access: aktiviert
- Statisches Website-Hosting: deaktiviert
CloudFront
- Preis-Klasse: alle Edge-Standorte verwenden
- Unterstützte HTTP-Versionen: HTTP/3, 2, 1.1, 1.0
Origin
- Origin-Name: S3-frontend-app
- Origin-Domain:
<bucket_name>.s3.ap-northeast-1.amazonaws.com - Origin-Pfad:
/current - Origin-Zugriff: Origin access control settings
Behavior
Standard (*)
- Origin: S3-frontend-app
- Viewer Protocol Policy: HTTP to HTTPS
- Cache Policy: Managed-CacheOptimized
- Viewer Request: CloudFront Functions
Deployment-Skript
Die AWS-Credentials lasse ich aus, weil sie vom jeweiligen Ausführungskontext abhängen.
# global varsreadonly S3_BUCKET=""readonly DIST_DIR=""
function main() { # BUILD_ID kann auch ein Git-Commit-Hash sein local -r build_id=$(date -Iseconds)
local -r base_uri="s3://$S3_BUCKET" local -r deploy_uri="$base_uri/builds/$build_id" local -r previous_uri="$base_uri/previous" local -r current_uri="$base_uri/current"
# Deployment unter builds/
# js,css aws s3 sync "$DIST_DIR" "$deploy_uri" \ --exclude "*" \ --include "*.js" \ --include "*.css" \ --metadata-directive "REPLACE" --cache-control "public,max-age=31536000,immutable"
# alles außer js,css,html aws s3 sync "$DIST_DIR" "$deploy_uri" \ --exclude "*.js" \ --exclude "*.css" \ --exclude "*.html" \ --metadata-directive "REPLACE" \ --cache-control "public,max-age=1,stale-while-revalidate=604800"
# html aws s3 sync "$DIST_DIR" "$deploy_uri" \ --exclude "*" \ --include "*.html" \ --metadata-directive "REPLACE" \ --cache-control "no-cache"
# Aktuell ausgelieferte Assets von current nach previous sichern aws s3 sync "$current_uri" "$previous_uri" --delete --exact-timestamp
# Diesen Build nach current kopieren und veröffentlichen aws s3 sync "$deploy_uri" "$current_uri" --delete --exact-timestamp}Warum Cache-Control in dieser Reihenfolge gesetzt wird
Eigentlich würde ich Cache-Control gern im letzten Schritt setzen, also bei aws s3 sync "$deploy_uri" "$current_uri". Wenn man aber bei einem S3-zu-S3-Sync --metadata-directive "REPLACE" verwendet, hört AWS auf, den Content-Type abzuleiten, und alles landet als binary/octet-stream.
Bei einem Sync vom lokalen Dateisystem nach S3 bleibt die Content-Type-Erkennung erhalten. Daher ist diese Reihenfolge ein praktikabler Kompromiss.
Rollback-Skript
Wenn ein Problem auftritt, wird previous nach current zurückkopiert.
# global varsreadonly S3_BUCKET=""
function revert() { local -r base_uri="s3://$S3_BUCKET" local -r previous_uri="$base_uri/previous" local -r current_uri="$base_uri/current"
# copy previous to current aws s3 sync "$previous_uri" "$current_uri" --delete --exact-timestamp}Zusammenfassung
Wenn man eine SPA mit CloudFront + S3 ausliefert, ergibt die Kombination aus passenden Cache-Control-Einstellungen und einer Blue-Green-ähnlichen Deployment-Struktur einen sicheren und effizienten Deployment-Ablauf.
Mit Build-Historie unter builds sowie current und previous für aktive und vorherige Versionen lässt sich bei Problemen schnell zurückrollen.
hsb.horse