Voici un script de déploiement pragmatique pour servir une SPA avec CloudFront + S3.
Hypothèses
- Le cloud utilisé est AWS
- Les artefacts de build frontend sont stockés dans S3 et servis via CloudFront
Architecture de l’infrastructure
S3
- Versioning du bucket : désactivé
- Chiffrement : SSE-S3
- Block Public Access : activé
- Hébergement statique de site web : désactivé
CloudFront
- Classe tarifaire : toutes les edge locations
- Versions HTTP supportées : HTTP/3, 2, 1.1, 1.0
Origin
- Nom de l’origin : S3-frontend-app
- Domaine d’origine :
<bucket_name>.s3.ap-northeast-1.amazonaws.com - Chemin d’origine :
/current - Accès à l’origine : Origin access control settings
Behavior
Valeur par défaut (*)
- Origin : S3-frontend-app
- Viewer protocol policy : HTTP to HTTPS
- Cache policy : Managed-CacheOptimized
- Viewer request : CloudFront Functions
Script de déploiement
Je laisse de côté la partie credentials AWS, car elle dépend du contexte d’exécution.
# global varsreadonly S3_BUCKET=""readonly DIST_DIR=""
function main() { # BUILD_ID peut aussi être un hash de commit Git 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"
# Déployer sous 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"
# sauf 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"
# Sauvegarder les assets actuellement servis de current vers previous aws s3 sync "$current_uri" "$previous_uri" --delete --exact-timestamp
# Copier ce build vers current et le publier aws s3 sync "$deploy_uri" "$current_uri" --delete --exact-timestamp}Pourquoi définir Cache-Control dans cet ordre
Dans l’idéal, j’aimerais définir le cache-control à la toute dernière étape, aws s3 sync "$deploy_uri" "$current_uri", mais lors d’une synchronisation S3 vers S3 avec --metadata-directive "REPLACE", AWS cesse d’inférer le Content-Type, et tout finit en binary/octet-stream.
Lors d’une synchronisation depuis le local vers S3, l’inférence du Content-Type est conservée. Cette organisation est donc un compromis pratique.
Script de rollback
En cas de problème, il suffit de restaurer previous vers current.
# 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}Résumé
Pour servir une SPA avec CloudFront + S3, combiner des réglages de cache-control adaptés avec une structure de déploiement de type blue-green permet un flux de déploiement plus sûr et plus efficace.
En conservant l’historique sous builds et en gérant la version active et la précédente avec current et previous, on peut revenir en arrière rapidement en cas de problème.
hsb.horse