falecci.dev 🥃

NextJS Serverless Component y AWS CloudFront

April 23, 2020 📖 5 min read

Also in English

serverless component

Intro

Queríamos usar NextJS para una aplicación, y para ello, sentíamos la necesidad de poder deployearla fácilmente en AWS. Después de investigar un poco, llegamos a Serverless NextJS Component por Daniel Conde. Como el nombre dice, components son una nueva manera de construir aplicaciones Serverless. Básicamente el concepto es el mismo que un componente de React, construir y compartir partes utilizables de serverless (como S3, APIGateways, etc).

Al comienzo, estaba TOTALMENTE impresionado. Asi es como se veía nuestro serverless.yml:

# serverless.yml

myApp:

 component: serverless-next.js

WOW. O sea. WOW. Eso es ultra simple, y amamos las cosas simples. Si ejecutamos serverless, deployeará todo lo que necesitamos a AWS, incluyendo:

  • S3 Bucket: para los assets y el build de la aplicación.
  • Lambdas: para hacer nuestro trabajo de server side rendering.
  • CloudFront: para usar nuestras lambdas anteriores y darnos una url para acceder al sitio.

De todas maneras, nos dimos cuenta que habia un issue con esto. Cada vez que ejecutábamos serverless, terminaba creando recursos nuevos en AWS. ¿Cómo podríamos resolver esto?

Muy simple, sin dudas. Vamos a nombrar nuestros recursos, usando el parámetro inputs en nuestro component.

# serverless.yml

nextApp:
  component: serverless-next.js
  inputs:
    bucketName: my-name-of-choice-deployment-bucket
    name: my-name-of-choice-default-lambda

Okay, ¿qué hemos hecho? Hasta ahora, hemos nombrado nuestro deployment bucket, nuestras lambdas y… Esperen, ¿dónde esta nuestra distribución de CloudFront?

Tristemente, ahora mismo no hay manera de reutilizar la distribución de CloudFront creada previamente. Esto no es un issue del componente de NextJS Serverless; es un issue del componete de CloudFront, y está aclarado en el repo de NextJS Component:

[CI/CD] A new CloudFront distribution is created 
on every CI build. I wasn't expecting that:

You need to commit your application state in source control. 
That is the files under the .serverless directory. 
The serverless team is currently working on remote state 
storage so this won't be necessary in the future.

No podemos simplemente commitear el estado de nuestra aplicación en nuestro source control. Al menos, esa no era una opción para nosotros. Entonces, ¿cómo podríamos resolver esto?

Workaround

Como hemos configurado Github Actions para deployear nuestra aplicación, nos hemos dado cuenta que podríamos subir la carpeta .serverless al deployment bucket en S3. Nuestro nuevo pipeline se vería de la siguiente manera:

pipeline

Vamos en detalle paso por paso, e incluso aunque no estuviéses usando Github Actions, podrías hacer lo mismo para el pipeline que estés usando.

NOTA: Tendrás que instalar AWS CLI. Si estás usando Github Actions como nosotros, podes usar aws-actions.

Downloading .serverless folder from S3

Nuestra carpeta .serverless contiene un archivo json llamado Template.nextApp.CloudFront. Ahí mismo, encontraremos la información de nuestra distribución de CloudFront. Acá está el script bash que usaremos para descargar el archivo.

CLOUDFRONT_FILE=.serverless/Template.nextApp.CloudFront.json

aws s3 ls s3://${{ secrets.S3_DEPLOYMENT_BUCKET }}/$CLOUDFRONT_FILE
if [[ $? -ne 0 ]]; 
then
  echo 'Serverless folder does not exist in S3. This is the first deploy in this environment.'
else
  aws s3 sync s3://${{ secrets.S3_DEPLOYMENT_BUCKET }}/.serverless ./.serverless
  echo 'Finshed download Serverless folder from S3'
fi

El script es casi auto explicable. Vamos a listar los items de nuestro S3 Deployment Bucket. Si no tenemos nuestro archivo .serverless/Template.nextApp.CloudFront.json, significa que es nuestro primer deploy. Si ese es nuestro caso, ignoraremos este paso.

Si el archivo existe, lo descargaremos asi nuestro deploy de serverless puede utilizarlo.

Invalidando la cache de la distribución de CloudFront

Después de ejecutar nuestro paso de deploy (solo debería ser ejecutar serverless), procederemos a invalidar la cache de nuestra distribución de CloudFront.

CLOUDFRONT_FILE=.serverless/Template.nextApp.CloudFront.json

# read "id" attribute from Template.nextApp.CloudFront.json
CLOUDFRONT_DISTRIBUTION_ID="$(grep -Po '"id": *\K"[^"]*"' $CLOUDFRONT_FILE | sed "s/\"//g")" 

echo "Creating invalidation for CloudFront for distribution: $CLOUDFRONT_DISTRIBUTION_ID"
aws cloudfront create-invalidation --distribution-id $CLOUDFRONT_DISTRIBUTION_ID --paths '/*';
echo 'Finished invalidation for CloudFront'

Vamos a buscar en nuestro archivo de CloudFront su id, y crear una invalidación para nuestra distribución.

Sincronizando la carpeta Serverless con S3

Por último, sincronizaremos nuestra carpeta .serverless en S3. Chequearemos si el archivo existe en S3. Si existe, no haremos nada. Caso contrario, subiremos nuestra carpeta .serverless a S3.

echo 'Checking if Serverless folder is missing in S3'

aws s3 ls s3://${{ secrets.S3_DEPLOYMENT_BUCKET }}/$CLOUDFRONT_FILE
if [[ $? -ne 0 ]]; 
then
  echo 'Syncing Serverless folder with S3'
  aws s3 sync ./.serverless s3://${{ secrets.S3_DEPLOYMENT_BUCKET }}/.serverless
else
  echo 'Serverless folder already exists on S3'
fi

Conclusión

A pesar de que ésta no es la solución ideal, creemos que es la mejor solución para esta situación en este momento. El componente de NextJS Component ya soporta reutilizar un id de una distribución de CloudFront, pero es el componente de AWS CloudFront Component que no soporta usar un id existente, y parece que no lo tendremos pronto.

De todas maneras, amo la idea de Serverless Components y qué FÁCIL la configuración y deployment de aplicaciones son. Creo firmemente que es un punto de inflexión. Es triste que no reciban la atención que se merecen.

Espero les haya gustado el artículo y lo hayan encontrado útil. I hope you’ve liked the article and found it helpful. Por favor siéntanse libres de contactarme por Twitter!