Laravel Vapor

Com desplegar aplicacions Laravel de manera serverless amb Laravel Vapor: AWS Lambda, configuració, bases de dades, cues, emmagatzematge i escalat automàtic.

Què és Laravel Vapor?#

Laravel Vapor és una plataforma de desplegament serverless dissenyada específicament per a aplicacions Laravel. Creada per Taylor Otwell i l'equip de Laravel, Vapor permet desplegar aplicacions Laravel sobre la infraestructura d'Amazon Web Services (AWS) sense necessitat de gestionar servidors, configurar escalat ni preocupar-se pel manteniment de la infraestructura. En lloc de tenir un servidor (o diversos) executant-se permanentment esperant peticions, Vapor converteix la teva aplicació Laravel en funcions que s'executen sota demanda a AWS Lambda.

La proposta de valor de Vapor és clara: tu escrius codi Laravel com sempre, i Vapor s'encarrega de tot el que passa entre el teu codi i els usuaris. Aprovisiona la infraestructura a AWS, configura les xarxes, gestiona els certificats SSL, desplega el codi, escala automàticament segons el tràfic i et proporciona eines per monitoritzar-ho tot. Tot això sense que hagis de tocar mai la consola d'AWS directament.

El paradigma serverless#

El terme "serverless" pot portar a confusió perquè, evidentment, hi ha servidors executant el teu codi. El que realment significa és que tu no has de gestionar-los. No aprovisiones instàncies EC2, no configures Nginx ni PHP-FPM, no apliques actualitzacions de seguretat al sistema operatiu ni et lleves a les 3 de la matinada perquè un servidor s'ha quedat sense memòria.

En una arquitectura serverless, l'execució funciona així: quan arriba una petició HTTP, AWS Lambda crea un entorn d'execució, carrega el teu codi Laravel, processa la petició i retorna la resposta. Si arriben 10 peticions simultànies, Lambda crea 10 entorns. Si en arriben 10.000, en crea 10.000. Si no arriba cap petició, no hi ha res en execució i no pagues res. Aquesta elasticitat automàtica és el gran avantatge del serverless: l'aplicació escala de zero a milers de peticions concurrents sense cap intervenció manual i sense cap configuració d'escalat.

El model de costos també canvia radicalment. En lloc de pagar un servidor 24/7 (independentment de si rep peticions o no), pagues exactament pel temps d'execució consumit. Si la teva aplicació processa 100.000 peticions al mes amb un temps mitjà de 200ms, pagues per 20.000 segons de computació. Si un dia tens un pic de tràfic amb 10 vegades més peticions, pagues 10 vegades més aquell dia, però l'endemà tornes al cost habitual. No cal sobredimensionar un servidor "per si de cas".

Com funciona Vapor per sota#

Vapor orquestra una sèrie de serveis AWS que treballen conjuntament per servir la teva aplicació Laravel. Entendre aquesta arquitectura t'ajuda a prendre millors decisions de configuració i a diagnosticar problemes.

AWS Lambda és el motor d'execució. Cada petició HTTP que rep la teva aplicació es processa dins d'una funció Lambda. Vapor empaqueta el teu codi Laravel juntament amb una versió compilada de PHP i el puja a Lambda. Quan arriba una petició, Lambda crea un entorn d'execució (o reutilitza un d'existent si n'hi ha un disponible), carrega el codi i executa l'aplicació Laravel. Lambda gestiona automàticament la concurrència: si arriben 100 peticions simultànies, tindrà 100 entorns executant-se en paral·lel.

API Gateway és el punt d'entrada HTTP. Totes les peticions dels usuaris arriben primer a API Gateway, que les valida, les transforma al format que Lambda espera i les envia a la funció Lambda corresponent. API Gateway també gestiona el throttling, la compressió de respostes i altres aspectes del transport HTTP.

Amazon S3 serveix per a múltiples propòsits. Emmagatzema el codi desplegat (els artefactes de desplegament), els fitxers pujats pels usuaris i els assets estàtics de l'aplicació. Com que el sistema de fitxers de Lambda és efímer i de només lectura (excepte /tmp), qualsevol fitxer que l'aplicació necessiti persistir ha d'anar a S3.

Amazon SQS (Simple Queue Service) gestiona les cues de treball. Quan la teva aplicació dispatxa un job, aquest es publica a una cua SQS. Vapor configura automàticament funcions Lambda separades que escolten la cua i processen els jobs. Això significa que els workers de cues també són serverless i escalen automàticament.

Amazon CloudFront és la CDN que serveix els assets estàtics (CSS, JavaScript, imatges) des de ubicacions properes als usuaris arreu del món. Vapor configura automàticament una distribució CloudFront per a cada entorn, cosa que millora significativament el temps de càrrega per a usuaris geogràficament distants del datacenter principal.

Amazon RDS i Aurora Serverless proporcionen bases de dades gestionades (MySQL o PostgreSQL). Amazon ElastiCache proporciona Redis gestionat per a cache i sessions. Amazon Route 53 gestiona els DNS per als dominis personalitzats.

Prerequisits#

Abans de començar amb Vapor, necessites dues coses: un compte d'AWS i una subscripció a Vapor.

El compte d'AWS ha de tenir permisos suficients per crear i gestionar els serveis que Vapor necessita (Lambda, API Gateway, S3, SQS, CloudFront, RDS, ElastiCache, IAM, CloudFormation, etc.). La manera més senzilla és utilitzar un usuari IAM amb accés d'administrador, encara que en entorns corporatius pot ser necessari definir polítiques més restrictives. Vapor proporciona la llista exacta de permisos IAM necessaris a la seva documentació.

La subscripció a Vapor es gestiona a vapor.laravel.com. Hi ha diferents plans que varien en el nombre de projectes, entorns i membres d'equip. La subscripció a Vapor cobreix l'ús de la plataforma (dashboard, CLI, gestió d'infraestructura), però els costos d'AWS es facturen directament al teu compte d'AWS. Això és important: el que pagues a Vapor i el que pagues a AWS són coses separades.

Instal·lació i configuració inicial#

El primer pas és instal·lar el paquet vapor-core a la teva aplicació Laravel. Aquest paquet adapta Laravel per funcionar correctament en un entorn Lambda, gestionant aspectes com el sistema de fitxers efímer, les sessions, la cache i les rutes d'assets:

composer require laravel/vapor-core

A continuació, instal·la el CLI de Vapor globalment. El CLI és l'eina principal per interactuar amb Vapor des de la línia de comandes: desplegar, gestionar entorns, veure logs i configurar la infraestructura:

npm install -g laravel-vapor

Un cop instal·lat, inicia sessió amb el teu compte de Vapor:

vapor login

Això obrirà el navegador per autenticar-te. Després d'iniciar sessió, vincula el projecte al teu equip de Vapor. Si és la primera vegada, hauràs de crear un equip i connectar el teu compte d'AWS. Vapor necessita credencials AWS (Access Key ID i Secret Access Key) per gestionar els serveis al teu compte:

vapor team:current
vapor team:switch

Finalment, inicialitza el projecte Vapor:

vapor init

Aquesta comanda crea el fitxer vapor.yml a l'arrel del teu projecte, que és el fitxer de configuració central de Vapor. Vapor et farà algunes preguntes sobre el nom del projecte i la regió AWS preferida.

El fitxer vapor.yml en profunditat#

El fitxer vapor.yml és el cor de la configuració de Vapor. Defineix com es desplega l'aplicació, quins recursos necessita cada entorn i com es comporta la infraestructura. Un fitxer complet pot assemblar-se a això:

id: 42
name: laravel-andorra
 
environments:
    production:
        memory: 1024
        cli-memory: 512
        runtime: 'php-8.3:al2'
        build:
            - 'COMPOSER_MIRROR_PATH_REPOS=1 composer install --no-dev --no-interaction --prefer-dist --optimize-autoloader'
            - 'npm ci && npm run build && rm -rf node_modules'
        deploy:
            - 'php artisan migrate --force'
            - 'php artisan config:cache'
            - 'php artisan route:cache'
            - 'php artisan view:cache'
        warm: 10
        timeout: 30
        concurrency: 200
        queues:
            - emails
            - default
            - low
        storage: laravel-andorra-production
        domain: laravelandorra.com
        environment:
            APP_ENV: production
            APP_DEBUG: 'false'
 
    staging:
        memory: 512
        cli-memory: 512
        runtime: 'php-8.3:al2'
        build:
            - 'composer install --no-interaction --prefer-dist --optimize-autoloader'
            - 'npm ci && npm run build && rm -rf node_modules'
        deploy:
            - 'php artisan migrate --force'
        warm: 2
        timeout: 30
        domain: staging.laravelandorra.com
        environment:
            APP_ENV: staging
            APP_DEBUG: 'true'

Identificador i nom del projecte#

Les claus id i name a l'arrel del fitxer identifiquen el projecte a la plataforma Vapor. L'id s'assigna automàticament quan crees el projecte i és l'identificador únic a la base de dades de Vapor. El name és el nom llegible que apareix al dashboard. No canviïs l'id manualment; el name pot canviar-se però afectarà els noms dels recursos AWS creats.

Entorns#

Cada clau dins d'environments defineix un entorn independent amb la seva pròpia configuració, infraestructura i URL. Els entorns més habituals són production i staging, però pots crear-ne tants com necessitis (development, testing, demo, etc.). Cada entorn es desplega de manera independent i té els seus propis recursos AWS (funcions Lambda, distribucions CloudFront, cues SQS).

Memòria#

La clau memory defineix quanta memòria RAM s'assigna a cada invocació Lambda per a les peticions web. Amb AWS Lambda, la memòria també determina proporcionalment la potència de CPU assignada: més memòria implica més CPU. Per exemple, 1024 MB proporciona el doble de CPU que 512 MB. Això significa que augmentar la memòria no només dóna més RAM sinó que també fa que el codi s'executi més ràpid.

La clau cli-memory és equivalent però per a les invocacions CLI (comandes Artisan, migracions, workers de cues). Normalment es pot configurar amb menys memòria que les peticions web, a menys que les tasques CLI facin operacions intensives.

Els valors típics oscil·len entre 512 MB i 3008 MB. Per a la majoria d'aplicacions Laravel, 1024 MB és un bon punt de partida per a producció. Monitoritza l'ús real de memòria dels teus Lambda a CloudWatch i ajusta segons les necessitats.

Runtime#

La clau runtime especifica quina versió de PHP utilitza l'entorn. Vapor proporciona runtimes oficials que inclouen PHP amb extensions comunes precompilades i optimitzades per a Lambda. El format és php-X.Y:al2, on X.Y és la versió de PHP i al2 indica Amazon Linux 2 com a sistema base:

# PHP 8.3 sobre Amazon Linux 2 (recomanat)
runtime: 'php-8.3:al2'
 
# PHP 8.2 si necessites compatibilitat
runtime: 'php-8.2:al2'

Mantenir el runtime actualitzat és important per seguretat i rendiment. Quan Laravel deixi de suportar una versió de PHP, assegura't d'actualitzar el runtime al vapor.yml.

Build commands#

Les build commands s'executen durant el procés de construcció, abans del desplegament. Aquí és on instal·les les dependències de Composer i construeixes els assets frontend. Aquestes comandes s'executen en un entorn de construcció temporal, no a Lambda directament:

build:
    - 'COMPOSER_MIRROR_PATH_REPOS=1 composer install --no-dev --no-interaction --prefer-dist --optimize-autoloader'
    - 'npm ci && npm run build && rm -rf node_modules'

La variable COMPOSER_MIRROR_PATH_REPOS=1 és important per a Vapor: fa que Composer copiï el contingut dels repositoris de tipus path en lloc de crear enllaços simbòlics, que no funcionen bé amb el procés d'empaquetament de Lambda. L'opció --no-dev exclou les dependències de desenvolupament, reduint la mida del paquet. El rm -rf node_modules al final elimina els mòduls de Node.js després de construir els assets, ja que no són necessaris en producció i ocupen molt espai.

Deploy commands#

Les deploy commands s'executen després de cada desplegament, dins de l'entorn Lambda. Aquí és on executes les migracions, generes les caches de configuració i rutes, i qualsevol altra tasca post-desplegament:

deploy:
    - 'php artisan migrate --force'
    - 'php artisan config:cache'
    - 'php artisan route:cache'
    - 'php artisan view:cache'
    - 'php artisan event:cache'

L'opció --force a migrate és necessària perquè Laravel demana confirmació en entorns de producció per defecte. Les comandes de cache (config:cache, route:cache, view:cache) milloren el rendiment perquè Laravel no ha de parsejar fitxers de configuració, rutes o vistes a cada petició.

Warm (pre-escalfament)#

La clau warm controla el pre-escalfament (pre-warming) de funcions Lambda. Un dels inconvenients del serverless són els "cold starts" (arrencades en fred): quan no hi ha cap entorn Lambda disponible, en crear-ne un de nou es produeix un retard addicional (típicament entre 200ms i 1 segon per a aplicacions Laravel). Vapor pot mantenir un nombre determinat d'entorns Lambda "calents" (pre-escalfats) que estiguin preparats per respondre immediatament:

# Mantenir 10 entorns Lambda pre-escalfats
warm: 10

Amb warm: 10, Vapor fa pings periòdics a 10 instàncies Lambda per mantenir-les actives. Això significa que les primeres 10 peticions concurrents no patiran cold start. La onzena petició concurrent sí que podria patir-lo si no hi ha entorns disponibles. Ajusta aquest valor segons el tràfic esperat: prou alt per cobrir la concurrència habitual, però no tant que generi costos innecessaris.

Timeout#

La clau timeout defineix el temps màxim d'execució (en segons) d'una funció Lambda. Si una petició o tasca supera aquest temps, Lambda la mata automàticament:

# Màxim 30 segons per petició
timeout: 30

El límit màxim absolut d'AWS Lambda és de 900 segons (15 minuts). Per a peticions web, 30 segons és un valor raonable. Si tens processos CLI llargs (per exemple, importacions massives), pots augmentar-lo per a la Lambda CLI. Tingues en compte que API Gateway té el seu propi timeout de 30 segons que no es pot canviar, de manera que les peticions HTTP mai podran trigar més de 30 segons tot i que Lambda permeti més.

Concurrència#

La clau concurrency limita el nombre màxim d'execucions Lambda concurrents per a l'entorn. Això és útil per protegir la base de dades de massa connexions simultànies:

# Màxim 200 execucions simultànies
concurrency: 200

Si no estableixes un límit, Lambda escalarà fins al límit del teu compte AWS (per defecte, 1.000 concurrents). Amb una base de dades RDS que pot gestionar 100 connexions, 1.000 Lambdas intentant connectar-se simultàniament saturarien la base de dades. Configura la concurrència d'acord amb els límits de connexió de la base de dades o utilitza proxies de base de dades (més endavant).

Cues#

La clau queues defineix les cues SQS que Vapor ha de crear i monitoritzar per a l'entorn. Cada cua tindrà el seu propi Lambda worker que processa els jobs:

queues:
    - emails
    - default
    - low

Si no especifiques cues, Vapor crea una cua default. Cada cua addicional consumeix una mica més de recursos AWS, però permet separar i prioritzar els jobs.

Storage#

La clau storage especifica el nom del bucket S3 que utilitza l'aplicació per emmagatzemar fitxers. Vapor crea el bucket automàticament si no existeix:

storage: laravel-andorra-production

Aquest bucket s'utilitza per a tot l'emmagatzematge de fitxers de l'aplicació (avatars, documents, PDFs generats, etc.). El nom ha de ser globalment únic a S3, de manera que és bona pràctica incloure el nom del projecte i l'entorn.

Domini personalitzat#

La clau domain assigna un domini personalitzat a l'entorn:

domain: laravelandorra.com

Perquè funcioni, el domini ha d'estar gestionat a Route 53 al mateix compte AWS, o bé has de crear manualment els registres DNS que Vapor t'indicarà. Vapor genera automàticament un certificat SSL gratuït a través d'AWS Certificate Manager.

Variables d'entorn inline#

Pots definir variables d'entorn directament al vapor.yml dins de la clau environment:

environment:
    APP_ENV: production
    APP_DEBUG: 'false'
    CACHE_DRIVER: dynamodb
    SESSION_DRIVER: cookie
    QUEUE_CONNECTION: sqs

Tanmateix, per a valors sensibles com claus d'API, secrets de base de dades o tokens, és millor gestionar-les a través del CLI o el dashboard de Vapor (veure la secció de variables d'entorn més endavant).

Desplegar amb Vapor#

El desplegament amb Vapor és un procés d'una sola comanda:

vapor deploy production

Aquesta comanda inicia un procés que inclou diversos passos orquestrats automàticament. Primer, Vapor executa les comandes de build al teu entorn local (o en un CI/CD). Això instal·la les dependències de Composer, compila els assets frontend i prepara l'artefacte de desplegament. Després, empaqueta tot el codi en un fitxer ZIP i el puja a S3. A continuació, actualitza la funció Lambda amb el codi nou, configura les variables d'entorn, actualitza API Gateway si cal, invalida la cache de CloudFront per als assets i finalment executa les comandes de deploy (migracions, caches). Si tot funciona correctament, el nou desplegament es publica i comença a rebre tràfic.

Per a l'entorn de staging:

vapor deploy staging

Rollback#

Si un desplegament introdueix un error, pots tornar a la versió anterior ràpidament:

# Tornar al desplegament anterior
vapor rollback production
 
# Tornar a un desplegament específic (per ID)
vapor rollback production 42

El rollback és quasi instantani perquè simplement canvia la versió de codi que Lambda utilitza a la versió anterior, que ja està pujada a S3. Les migracions de base de dades no es reverteixen automàticament: si necessites revertir migracions, ho hauràs de fer manualment amb vapor command production --command="php artisan migrate:rollback".

Historial de desplegaments#

Pots consultar l'historial de desplegaments per veure què s'ha desplegat i quan:

vapor deploy:list production

Cada desplegament té un identificador únic, la data, l'estat (exitós o fallit) i l'usuari que l'ha iniciat. Aquesta informació és útil per auditar canvis i correlacionar desplegaments amb incidències.

Executar comandes Artisan#

Pots executar qualsevol comanda Artisan a l'entorn desplegat:

vapor command production
# S'obrirà un prompt on pots escriure la comanda
 
vapor command production --command="php artisan tinker"
vapor command production --command="php artisan db:seed --class=ProductionSeeder"
vapor command production --command="php artisan cache:clear"

Aquestes comandes s'executen dins d'un Lambda separat amb la configuració cli-memory, no dins de la funció web. Tingues en compte el timeout de Lambda: si una comanda triga més del timeout configurat, Lambda la matarà.

Bases de dades#

RDS gestionat a través de Vapor#

Vapor pot crear i gestionar instàncies Amazon RDS directament des del CLI o el dashboard. RDS és el servei de bases de dades gestionades d'AWS que suporta MySQL, PostgreSQL, MariaDB i altres motors. "Gestionat" significa que AWS s'encarrega dels backups automàtics, les actualitzacions del motor, la replicació i la monitorització:

# Crear una base de dades MySQL
vapor database laravel-andorra-db --type=mysql
 
# Crear una base de dades PostgreSQL
vapor database laravel-andorra-db --type=pgsql
 
# Llistar bases de dades
vapor database:list
 
# Veure detalls d'una base de dades
vapor database:show laravel-andorra-db

Vapor et proporcionarà el host, el port i les credencials de la base de dades, que configurarà automàticament com a variables d'entorn al teu entorn Vapor. Pots canviar el tipus d'instància RDS (des de db.t3.micro per a desenvolupament fins a db.r5.4xlarge per a producció amb alta càrrega) directament des del dashboard de Vapor.

Aurora Serverless#

Per a aplicacions amb patrons de tràfic variables, Aurora Serverless és una opció excel·lent. Aurora Serverless és una versió de la base de dades Aurora d'AWS que escala automàticament la capacitat de computació segons la demanda, de manera similar a com Lambda escala les funcions. Si la teva aplicació no rep tràfic durant la nit, Aurora Serverless pot escalar fins a zero unitats de capacitat, reduint els costos dràsticament:

vapor database laravel-andorra-db --type=aurora-serverless-mysql
vapor database laravel-andorra-db --type=aurora-serverless-pgsql

Aurora Serverless és especialment adequada per a entorns de staging o desenvolupament on el tràfic és esporàdic. Per a producció amb tràfic constant, una instància RDS tradicional pot ser més previsible en rendiment i cost.

Proxies de base de dades#

Un dels reptes del serverless amb bases de dades és la gestió de connexions. Cada invocació Lambda obre la seva pròpia connexió a la base de dades. Amb 200 Lambdas concurrents, tens 200 connexions obertes simultàniament. Les bases de dades relacionals no estan dissenyades per gestionar centenars o milers de connexions simultànies; cada connexió consumeix memòria i recursos.

Vapor ofereix proxies de base de dades (basats en Amazon RDS Proxy) que actuen com a intermediaris entre les funcions Lambda i la base de dades. El proxy manté un pool de connexions i les reutilitza, de manera que 200 Lambdas poden compartir un pool de 20-50 connexions reals a la base de dades:

vapor database:proxy laravel-andorra-db

Un cop creat el proxy, Vapor actualitza automàticament la configuració de l'entorn per connectar-se a través del proxy en lloc de directament a la base de dades. Això millora significativament l'estabilitat sota càrrega alta i permet escalar el nombre de Lambdas sense preocupar-se pels límits de connexió de la base de dades.

Configuració de la connexió#

Vapor configura automàticament les variables d'entorn de la base de dades quan crees una base de dades a través del CLI o el dashboard. No obstant, pots configurar-les manualment si és necessari:

vapor env:pull production

Això descarregarà el fitxer .env de l'entorn. Edita'l i puja'l de nou:

DB_CONNECTION=mysql
DB_HOST=laravel-andorra-db-proxy.proxy-xxxxxxxxxxxx.eu-west-1.rds.amazonaws.com
DB_PORT=3306
DB_DATABASE=laravel_andorra
DB_USERNAME=vapor
DB_PASSWORD=el-teu-secret-segur
vapor env:push production

Cues amb SQS#

Vapor integra les cues de Laravel amb Amazon SQS de manera transparent. Quan configures cues al vapor.yml, Vapor crea automàticament les cues SQS a AWS i configura funcions Lambda separades que actuen com a workers. Aquests workers serverless escalen automàticament: si hi ha molts jobs pendents, AWS crearà més workers; si no hi ha jobs, no hi haurà cap worker executant-se.

La configuració és mínima. Al vapor.yml, simplement llista les cues que necessites:

environments:
    production:
        queues:
            - high
            - default
            - low

I al fitxer .env de l'entorn, assegura't que el driver de cues sigui SQS:

QUEUE_CONNECTION=sqs

El codi de dispatching de jobs no canvia en absolut. Continua dispatxant jobs exactament com ho faries amb qualsevol altre driver:

// Dispatxar a la cua per defecte
ProcessPodcast::dispatch($podcast);
 
// Dispatxar a una cua específica
SendWelcomeEmail::dispatch($user)->onQueue('high');
GenerateReport::dispatch($report)->onQueue('low');

Concurrència dels workers#

Per defecte, cada cua pot tenir fins a 1.000 invocacions Lambda concurrents processant jobs. Pots limitar-ho al vapor.yml per evitar sobrecarregar serveis externs o la base de dades:

environments:
    production:
        queues:
            - high: 50
            - default: 20
            - low: 5

Amb aquesta configuració, la cua high pot tenir fins a 50 workers concurrents, default fins a 20, i low fins a 5. Això és especialment important si els teus jobs accedeixen a la base de dades: cada worker obre la seva pròpia connexió, i sense límits podries saturar la base de dades ràpidament.

Timeout dels jobs#

Recorda que els jobs a Lambda tenen el mateix timeout que qualsevol altra invocació Lambda. Si un job necessita més de 15 minuts (el límit absolut de Lambda), hauràs de dividir-lo en trossos més petits o considerar un enfocament diferent. Per a la majoria de jobs, però, el timeout per defecte és més que suficient.

Emmagatzematge amb S3#

En un entorn serverless, el sistema de fitxers local és efímer i de només lectura (excepte /tmp, que es buida entre invocacions). Això significa que no pots emmagatzemar fitxers pujats al disc local com ho faries en un servidor tradicional. Tots els fitxers han d'anar a Amazon S3.

Vapor configura automàticament Laravel per utilitzar S3 com a disc d'emmagatzematge. Al fitxer .env, estableix:

FILESYSTEM_DISK=s3

Amb això, totes les operacions d'emmagatzematge de Laravel (Storage::put(), Storage::get(), etc.) funcionen transparentment amb S3:

use Illuminate\Support\Facades\Storage;
 
// Pujar un fitxer
Storage::put('avatars/user-42.jpg', $contingut);
 
// Obtenir una URL temporal (signada) per a un fitxer privat
$url = Storage::temporaryUrl('documents/factura.pdf', now()->addMinutes(30));
 
// Pujar des d'un formulari
$path = $request->file('avatar')->store('avatars', 's3');

Pujada directa a S3#

Per a fitxers grans, Vapor ofereix pujada directa a S3 des del navegador de l'usuari, sense passar per Lambda. Això és important perquè Lambda té un límit de 6 MB per al cos de la petició HTTP (a través d'API Gateway). Si un usuari intenta pujar un fitxer de 50 MB, la petició fallarà.

Amb la pujada directa, el navegador obté una URL signada de S3 i puja el fitxer directament a S3, sense passar per la teva aplicació Laravel. Vapor inclou un component JavaScript per facilitar-ho:

npm install laravel-vapor
import Vapor from 'laravel-vapor';
 
// Al component de pujada (per exemple, Vue o React)
async function uploadFile(file) {
    const response = await Vapor.store(file, {
        progress: (progress) => {
            console.log(`Progrés: ${Math.round(progress * 100)}%`);
        }
    });
 
    // Enviar la clau S3 al backend
    await axios.post('/api/profile/avatar', {
        uuid: response.uuid,
        key: response.key,
        bucket: response.bucket,
    });
}

Al controlador de Laravel, mou el fitxer temporal a la seva ubicació final:

public function updateAvatar(Request $request)
{
    $request->validate(['key' => 'required|string']);
 
    Storage::copy(
        $request->key,
        'avatars/' . auth()->id() . '.jpg'
    );
 
    auth()->user()->update([
        'avatar_path' => 'avatars/' . auth()->id() . '.jpg',
    ]);
}

Cache i sessions#

ElastiCache (Redis)#

Per a la cache de l'aplicació, Vapor pot crear una instància ElastiCache Redis gestionada:

vapor cache laravel-andorra-cache

Vapor configura automàticament la connexió Redis a l'entorn. Al .env:

CACHE_STORE=redis
REDIS_HOST=laravel-andorra-cache.xxxxx.euw1.cache.amazonaws.com
REDIS_PORT=6379

Redis via ElastiCache és l'opció recomanada per a la cache perquè és extremadament ràpid i suporta operacions atòmiques. Com que les funcions Lambda són efímeres, la cache local (fitxer o array) no té sentit: cada invocació Lambda comença amb una cache buida. Redis, en canvi, és un servei separat persistent al qual totes les Lambdas es connecten.

DynamoDB per a sessions#

Per a les sessions, Vapor recomana Amazon DynamoDB en lloc de Redis. DynamoDB és un servei de base de dades NoSQL completament gestionat que escala automàticament i té una latència molt baixa. La raó principal per preferir DynamoDB sobre Redis per a sessions és que DynamoDB escala de manera il·limitada sense configuració, mentre que Redis té un límit de memòria:

SESSION_DRIVER=dynamodb
DYNAMODB_CACHE_TABLE=sessions

Laravel inclou suport natiu per a DynamoDB com a driver de sessions. Vapor configura la taula DynamoDB automàticament. El cost de DynamoDB per a sessions és generalment molt baix, ja que les operacions de lectura i escriptura de sessions són petites.

Una alternativa senzilla és utilitzar sessions basades en cookies (SESSION_DRIVER=cookie), que no requereixen cap infraestructura addicional. Tanmateix, les sessions de cookie tenen un límit de 4 KB i les dades s'envien amb cada petició, cosa que pot ser un inconvenient si emmagatzemes molta informació a la sessió.

Dominis personalitzats i SSL#

Vapor gestiona automàticament els certificats SSL i la configuració de dominis. Quan afegeixes un domini al vapor.yml, Vapor:

  1. Crea un certificat SSL gratuït a AWS Certificate Manager
  2. Configura CloudFront per servir el domini amb HTTPS
  3. Et proporciona els registres DNS que has de crear
# Veure l'estat del domini
vapor domain:list
 
# Afegir un domini
vapor domain laravelandorra.com

Si el teu domini està gestionat a Route 53 (dins del mateix compte AWS), Vapor pot crear els registres DNS automàticament. Si el domini està a un altre registrador (Cloudflare, Namecheap, etc.), Vapor et mostrarà els registres CNAME que has de crear manualment.

Vapor també configura automàticament CloudFront com a CDN davant de l'aplicació. Això significa que els assets estàtics (CSS, JS, imatges) es serveixen des de la ubicació CloudFront més propera a l'usuari, millorant significativament el temps de càrrega.

Variables d'entorn#

Les variables d'entorn a Vapor es gestionen de manera segura i separada del codi. Hi ha dues maneres de gestionar-les: a través del CLI i a través del dashboard web.

Amb el CLI#

# Descarregar el fitxer .env de l'entorn
vapor env:pull production
 
# Editar-lo localment
# (Obre .env.production amb el teu editor)
 
# Pujar els canvis
vapor env:push production

Quan fas env:pull, Vapor descarrega les variables d'entorn actuals de l'entorn i les guarda en un fitxer local temporal. Després d'editar-les, env:push les puja de nou i les xifra. Cada desplegament posterior utilitzarà les variables actualitzades.

Variables individuals#

Per canviar una sola variable sense descarregar tot el fitxer:

vapor env production APP_DEBUG false

Variables sensibles#

Totes les variables d'entorn a Vapor s'emmagatzemen xifrades. Vapor utilitza AWS Systems Manager Parameter Store per emmagatzemar les variables de manera segura, xifrades amb claus KMS d'AWS. Això és significativament més segur que emmagatzemar secrets en fitxers .env al servidor.

Mai incloguis el fitxer .env.production o qualsevol fitxer amb secrets al repositori Git. Gestiona sempre les variables d'entorn a través del CLI de Vapor o del dashboard.

Assets amb CloudFront CDN#

En un entorn Vapor, els assets estàtics (CSS, JavaScript, imatges, fonts) no es serveixen des de Lambda sinó des de CloudFront, la CDN global d'AWS. Això és important per dues raons: primer, servir fitxers estàtics des de Lambda seria extremadament ineficient i costós (cada petició d'un fitxer CSS consumiria una invocació Lambda); segon, CloudFront serveix els fitxers des de ubicacions properes als usuaris, reduint la latència.

Vapor configura automàticament la pujada d'assets a S3 i la seva distribució a través de CloudFront durant el procés de desplegament. L'únic que has de fer és utilitzar la funció asset() de Laravel a les teves vistes:

{{-- En comptes d'un path local, retorna una URL de CloudFront --}}
<link rel="stylesheet" href="{{ asset('build/assets/app.css') }}">
<script src="{{ asset('build/assets/app.js') }}"></script>
<img src="{{ asset('images/logo.png') }}" alt="Logo">

Vapor sobreescriu automàticament la funció asset() perquè retorni URLs de CloudFront en lloc de paths locals. No has de canviar res al teu codi.

Mix i Vite#

Si utilitzes Vite (recomanat a partir de Laravel 9), els assets compilats funcionen automàticament amb Vapor. Les directives @vite generen les URLs correctes de CloudFront:

@vite(['resources/css/app.css', 'resources/js/app.js'])

Monitorització i logs#

CloudWatch#

Totes les invocacions Lambda generen logs a AWS CloudWatch. Vapor integra l'accés a aquests logs a través del CLI:

# Veure els logs recents de producció
vapor log production
 
# Seguir els logs en temps real
vapor log production --follow
 
# Filtrar per text
vapor log production --filter="Error"
 
# Veure els logs del CLI (comandes Artisan, workers)
vapor log production --cli

La comanda vapor log production --follow és especialment útil per depurar problemes en temps real: mostra els logs a mesura que es generen, similar a tail -f en un servidor tradicional.

Mètriques#

Al dashboard de Vapor pots veure mètriques detallades de cada entorn: nombre d'invocacions, temps de resposta mitjà, errors, durada de les funcions Lambda, ús de memòria, throttling i molt més. Aquestes mètriques provenen directament de CloudWatch i s'actualitzen en temps gairebé real.

Alarmes#

Pots configurar alarmes per rebre notificacions quan alguna mètrica supera un llindar. Per exemple, pots rebre un correu quan el temps de resposta mitjà supera els 5 segons o quan el percentatge d'errors supera l'1%. Això et permet reaccionar ràpidament a problemes sense estar constantment vigilant el dashboard.

Vanity URLs i preview environments#

Cada desplegament a Vapor rep automàticament una "vanity URL" única, un subdomini de vapor.cloud que apunta al desplegament concret. Això és útil per compartir una versió específica de l'aplicació amb el teu equip sense afectar el domini principal:

# Desplegar i obtenir una vanity URL
vapor deploy staging
# URL: https://abc123.vapor.cloud

Els preview environments van un pas més enllà: pots configurar Vapor per crear automàticament un entorn temporal per a cada pull request de GitHub. Quan obres una PR, Vapor desplega automàticament una còpia de l'aplicació amb els canvis de la PR. Quan tanques o fusiones la PR, l'entorn es destrueix automàticament. Això facilita la revisió de codi perquè els revisors poden provar els canvis directament sense necessitat d'executar res localment.

Limitacions del serverless#

Tot i els avantatges del serverless, hi ha limitacions importants que cal tenir en compte abans d'adoptar Vapor:

Cold starts (arrencades en fred). Quan no hi ha cap Lambda disponible per processar una petició, AWS n'ha de crear una de nova. Aquest procés afegeix entre 200ms i 1 segon de latència a la primera petició. Per a aplicacions on la latència és crítica (per exemple, APIs de trading o jocs en temps real), els cold starts poden ser un problema. El pre-warming (warm) mitiga parcialment el problema, però no l'elimina completament.

Límit de temps d'execució. Lambda té un límit absolut de 15 minuts per invocació. Per a peticions web via API Gateway, el límit efectiu és de 30 segons. Si tens processos que triguen més (importacions massives, generació de vídeo, processament de fitxers grans), necessitaràs una estratègia alternativa: dividir la tasca en trossos més petits, utilitzar Step Functions d'AWS o processar-ho fora de Lambda.

Sistema de fitxers de només lectura. El sistema de fitxers de Lambda és de només lectura, excepte el directori /tmp que té un límit de 512 MB (ampliable fins a 10 GB). No pots escriure fitxers al disc i esperar que persisteixin entre invocacions. Tots els fitxers persistents han d'anar a S3. Això pot afectar paquets de tercers que assumeixen un sistema de fitxers writable.

Límit de mida del payload. API Gateway limita el cos de la petició HTTP a 6 MB. Per a pujades de fitxers grans, has d'utilitzar la pujada directa a S3 (explicada anteriorment). Això afecta també les respostes: si la teva aplicació retorna respostes molt grans (per exemple, exportacions de dades), pot ser necessari un enfocament alternatiu.

Websockets. Lambda no suporta connexions persistents com websockets de manera nativa. Si la teva aplicació necessita websockets per a funcionalitats en temps real (xats, notificacions push, actualitzacions en directe), necessitaràs un servei separat com AWS API Gateway WebSocket o un servei de tercers com Pusher o Ably. Laravel Echo funciona perfectament amb Pusher des de Vapor.

Límit de concurrència. Cada compte AWS té un límit de concurrència Lambda per defecte de 1.000 invocacions simultànies per regió. Si necessites més, has de sol·licitar un augment a AWS. Tingues en compte que la concurrència es comparteix entre totes les funcions Lambda del compte, no només les de Vapor.

Quan triar Vapor vs Forge#

La decisió entre Vapor i Forge depèn de les necessitats específiques de cada projecte. Aquí tens una guia per ajudar-te a triar.

Tria Vapor si:

  • El tràfic és molt variable o impredictible (pics puntuals, campanyes de màrqueting, events virals)
  • No vols gestionar servidors ni preocupar-te pel manteniment d'infraestructura
  • Necessites escalat automàtic sense cap configuració
  • L'aplicació pot funcionar amb les limitacions del serverless (no necessita websockets natius, fitxers locals persistents o processos de més de 15 minuts)
  • Prefereixes un model de costos basat en el consum real

Tria Forge si:

  • El tràfic és estable i previsible
  • Necessites control total sobre el servidor (configuració de PHP, extensions específiques, processos de llarga durada)
  • L'aplicació utilitza websockets natius o processos que triguen més de 15 minuts
  • Vols un cost mensual fix i previsible
  • Necessites accés SSH al servidor per a depuració o operacions específiques

Consideracions addicionals. Vapor tendeix a ser més econòmic per a aplicacions amb tràfic baix (perquè no pagues quan no hi ha peticions) però pot ser més car per a aplicacions amb tràfic constant i alt (perquè el cost per invocació s'acumula). Forge amb un servidor potent pot ser més econòmic si el servidor està constantment ocupat processant peticions.

Estructura de costos#

El cost total d'utilitzar Vapor té dos components: la subscripció a Vapor i els costos d'AWS.

Subscripció a Vapor. Vapor té plans mensuals que inclouen un nombre determinat de projectes, entorns i membres d'equip. Consulta vapor.laravel.com per als preus actualitzats.

Costos d'AWS. Aquests es facturen directament al teu compte AWS i depenen de l'ús real. Els principals serveis que generen cost són:

  • Lambda: Factura per nombre d'invocacions i temps d'execució. El nivell gratuït inclou 1 milió d'invocacions i 400.000 GB-segons al mes.
  • API Gateway: Factura per nombre de peticions HTTP. Aproximadament 3.50$ per milió de peticions.
  • S3: Factura per emmagatzematge i transferència de dades. Molt econòmic per a la majoria d'aplicacions.
  • SQS: Factura per nombre de missatges. El primer milió de missatges al mes és gratuït.
  • CloudFront: Factura per transferència de dades i peticions. Inclou un nivell gratuït generós.
  • RDS/Aurora: Factura per tipus d'instància i hores d'execució. Pot ser el cost més gran si necessite una instància potent.
  • ElastiCache: Factura per tipus de node i hores d'execució.

Per a una aplicació petita o mitjana amb tràfic moderat, el cost total d'AWS pot oscil·lar entre 30$ i 200$ al mes, sumats a la subscripció de Vapor. Per a aplicacions amb molt tràfic, els costos d'AWS poden ser significativament més alts, però també ho seria el cost d'un servidor o clúster de servidors equivalent amb Forge.

AWS ofereix una calculadora de preus (AWS Pricing Calculator) que et permet estimar els costos abans de comprometre't. És recomanable fer una estimació basada en el tràfic esperat de l'aplicació per evitar sorpreses a la factura.