Desplegando una aplicación de Astro con Vue.js y Tailwind en AWS App Runner

Recientemente trabajé en un proyecto en el que tuve que desplegar un sitio web hecho con Astro, Tailwind y Vue.js. Este proyecto era para uso interno y se podría decir que era un prototipo más que algo que saldría a producción, por lo que no vi caso en montar algo complejo con Amplify, ECS, Lambdas o Lambda@Edge, como se puede encontrar en otras guías de internet.
Por si no conoces AWS App Runner, es un servicio completamente administrado que simplifica el despliegue y la ejecución de aplicaciones basadas en contenedores. Con App Runner, puedes tomar tu código o imagen de contenedor, construir, desplegar y escalar tu aplicación sin tener que preocuparte por la infraestructura subyacente.
Entre sus ventajas destacan:
Facilidad para aplicaciones containerizadas: App Runner está diseñado para desplegar aplicaciones server-side y basadas en contenedores de forma directa, sin requerir configuraciones complejas.
Despliegue y escalado automático: Permite configurar despliegues automáticos y escalar la aplicación en función de la carga de tráfico, lo cual resulta ideal para proyectos que puedan experimentar picos de usuarios.
Facilidad en la configuración: Si bien se puede utilizar para aplicaciones productivas con gran volumen de tráfico, si solo quieres prototipar o mostrar avances de tu aplicación, App Runner te permite tener tu aplicación corriendo en minutos, con CI/CD y toda la cosa.
Bueno, vamos directo al grano. Estos son los pasos que necesitarás seguir para desplegar tu aplicación con AWS App Runner.
Inicialización del Proyecto
El primer paso es inicializar tu proyecto de Astro. Puedes configurarlo según las necesidades de tu aplicación. En mi caso, lo inicialicé con Tailwind y Vue.js mediante el siguiente comando:
npm create astro@latest -- --add vue --add tailwind
Una vez que el proyecto esté inicializado, al ejecutar npm run dev
deberías poder ver la página de bienvenida de Astro.
A partir de este punto, omito la parte en la que modificas o agregas los componentes necesarios, ya que esta guía se centra en el despliegue del proyecto en App Runner.
Instalación y Configuración del Adaptador
Hasta el momento de escribir esta guía, no hay un adaptador que se pueda utilizar para desplegar en App Runner como sí lo hay para AWS Amplify, ECS, Lambdas o Lambda@Edge. La mayoría de estos adaptadores funcionan con el SDK de AWS y, si bien funcionan de maravilla, por lo general son algo lentos.
En este caso, utilizaremos el adaptador nativo @astrojs/node en modo standalone y, para el despliegue, utilizaremos el proceso CI/CD que nos proporciona App Runner para detectar los cambios de nuestra rama main
y realizar los despliegues.
Si aún no has instalado el adaptador @astrojs/node, ejecútalo en la raíz del proyecto:
npx astro add node
Este comando instalará las dependencias necesarias y modificará el archivo astro.config.mjs
. Al finalizar, deberías ver algo similar a lo siguiente:
// @ts-check
import { defineConfig } from 'astro/config';
import tailwindcss from '@tailwindcss/vite';
import node from "@astrojs/node"; // <-- new
import vue from '@astrojs/vue';
// https://astro.build/config
export default defineConfig({
vite: {
plugins: [tailwindcss()]
},
adapter: node(), // <-- new
integrations: [
vue()
]
});
Si no aparecen estas líneas, agrégalas manualmente. Luego, para especificar el modo del adaptador, modifica la configuración de la siguiente manera:
export default defineConfig({
vite: {
plugins: [tailwindcss()]
},
adapter: node({
mode: 'standalone', // <-- new
}),
integrations: [
vue()
]
});
Configuración de Variables de Entorno (Opcional)
Si tu aplicación utiliza variables de entorno, puedes definir un esquema en la configuración de Astro. Para ello, agrega el parámetro env
a la función defineConfig
:
import { defineConfig, envField } from 'astro/config'; // <-- new
export default defineConfig({
// ... otras configuraciones
env: { // <-- new
schema: {
API_HOST: envField.string({ context: "server", access: "public", optional: false }),
API_KEY_VALUE: envField.string({ context: "server", access: "secret", optional: false }),
// ... otras variables
}
}
});
Consulta la documentación oficial para más detalles.
Creación del Archivo de Configuración apprunner.yaml
Este archivo nos permitirá configurar App Runner, indicándole qué debe hacer en cada una de las etapas de despliegue.
App Runner cuenta con dos etapas de despliegue: build
y run
. En la etapa build
, normalmente nos va a interesar construir nuestra aplicación; en el caso de Astro, instalar las dependencias y ejecutar el comando npm run build
. Opcionalmente, también puedes ejecutar tus pruebas en caso de tenerlas o cualquier otro proceso que quieras realizar antes de que tu aplicación esté disponible para tus usuarios.
En la etapa run
, normalmente nos va a interesar correr nuestra aplicación; en el caso de Astro, levantar el servidor integrado para que nuestra aplicación esté disponible.
Este es mi archivo de configuración básico para correr Astro en App Runner:
version: 1.0
runtime: nodejs18
build:
commands:
pre-build:
- npm install
build:
- npm run build
env:
- name: NODE_ENV
value: "development"
run:
runtime-version: 18.20.5
pre-run:
- npm install --omit=dev
command: node dist/server/entry.mjs
network:
port: 80
env: PORT
env:
- name: NODE_ENV
value: "production"
- name: HOST
value: "0.0.0.0"
Expicación de la configuración
version: Versión del archivo de configuración.
runtime: Entorno de ejecución; en este caso usamos Node.js 18 debido a que hasta ahora App Runner soporta hasta esa versión.
build: Define la etapa de construcción, donde normalmente se instalan las dependencias y se construye el proyecto.
run: Especifica la etapa de ejecución, indicando el comando para levantar el servidor, el puerto y las variables necesarias. En el caso de Astro, debemos agregar la variable
HOST
con el valor "0.0.0.0" para que el Health Check de App Runner funcione.
Consulta la documentación oficial para más detalles.
Variables de entorno
Si tu aplicación utiliza variables de entorno, debes incluirlas tanto en la sección build
como en run
:
build:
env:
- name: NODE_ENV
value: "development"
- name: API_HOST
value: "https://api.myawsomewebsite.com"
- name: API_KEY_NAME
value: "API-Key"
run:
env:
- name: NODE_ENV
value: "production"
- name: HOST
value: "0.0.0.0"
- name: API_HOST
value: "https://api.myawsomewebsite.com"
- name: API_KEY_NAME
value: "API-Key"
El archivo apprunner.yaml debe subirse al repositorio Git. Si manejas variables sensibles, utiliza AWS Secrets Manager o AWS Systems Manager Parameter Store. Por ejemplo:
run:
env:
- name: MY_VAR_EXAMPLE
value: "example"
secrets:
- name: my-secret
value-from: "arn:aws:secretsmanager:us-east-1:123456789012:secret:testingstackAppRunnerConstr-kJFXde2ULKbT-S7t8xR:username::"
- name: my-parameter
value-from: "arn:aws:ssm:us-east-1:123456789012:parameter/parameter-name"
- name: my-parameter-only-name
value-from: "parameter-name"
Por alguna razón, Astro no detecta correctamente las variables de entorno inyectadas por App Runner. La solución más sencilla que encontré es crear un script que extraiga las variables del sistema y las escriba en un archivo .env
. Este es el script que utilizo:
#!/usr/bin/env bash
# Declarar un array asociativo con las variables y sus valores por defecto
declare -A default_variables=(
[API_HOST]="https://api.myawsomewebsite.com"
[API_KEY_VALUE]="something"
# ... otras variables
)
# Obtener el directorio donde se encuentra el script
script_dir="$(dirname "$(realpath "$0")")"
env_file="$script_dir/.env"
# Crear o vaciar el archivo .env
> "$env_file"
# Recorrer el array y escribir cada variable en el archivo .env
for key in "${!default_variables[@]}"; do
# Si la variable de entorno existe, se utiliza; de lo contrario, se toma el valor por defecto
value="${!key}"
if [ -z "$value" ]; then
value="${default_variables[$key]}"
fi
# Si el valor contiene espacios y no está entre comillas, se envuelve en comillas dobles
if [[ "$value" =~ \ && ! "$value" =~ ^\".*\"$ ]]; then
value="\"$value\""
fi
echo "$key=$value" >> "$env_file"
done
echo "Archivo .env generado en $env_file"
cat $env_file
Guarda este archivo en la raíz del proyecto como generate_env.sh
y asígnale permisos de ejecución:
chmod +x generate_env.sh
Luego, asegúrate de llamar a este script en las etapas de build
y run
de tu archivo apprunner.yaml
:
build:
commands:
pre-build:
- ./generate_env.sh # <-- nuevo: genera el archivo .env
- npm install
env:
...
run:
...
pre-run:
- ./generate_env.sh # <-- nuevo: genera el archivo .env
- npm install --omit=dev
command: node dist/server/entry.mjs
network:
...
env:
...
Configuración en AWS App Runner
Con el proyecto configurado y el archivo apprunner.yaml
listo, solo queda subir tu proyecto a GitHub o Bitbucket y configurar App Runner en la consola de AWS.
Estos son los pasos que necesitas seguir:
Ingresa a App Runner y haz clic en “Crear servicio”.
Selecciona “Repositorio de código fuente” y elige el proveedor (GitHub o Bitbucket).
Selecciona la conexión, el repositorio y la rama. En caso de trabajar en un monorepo o que tu código fuente no se encuentre en la raíz del repositorio, especifica el directorio donde se encuentra tu proyecto de Astro (por ejemplo, “/” para la raíz).
Configura el despliegue automático para que, cada vez que se haga push a la rama main (o la que hayas elegido), se inicie un nuevo despliegue.
En la sección de compilación, selecciona “Usar un archivo de configuración” para que App Runner utilice el archivo
apprunner.yaml
que acabamos de crear.Define la cantidad de vCPU y memoria asignada. Para proyectos ligeros como prototipos o landings estaticas, 0.25 vCPU y 0.5GB pueden ser suficientes; puedes ajustar esta configuración según el uso que tu aplicación va a tener.
App Runner viene con una configuración para el Auto Scaling por default, básicamente permite 100 peticiones en simultaneo por instancia y conforme este uso aumenta va incrementando de 1 instancia (el minímo) hasta 25 instancias (el máximo). Haciendo un calculo básico tenemos que esta configuración es capaz de responder a 2,500 peticiones al mismo tiempo. Puedes crear o modificar esta configuración para adaptarla a las necesidades de tu aplicación.
App Runner hace una comprobación health check TCP con un timeout de 5 segundos. Si prefieres, puedes cambiar a HTTP y especificar una ruta (por ejemplo,
/health
).Ahora toca seleccionar un rol de instancia previamente creado que tenga permisos para acceder a los secretos (obligatorio si usas Secrets Manager o SSM).
Asegúrate de que el rol tenga la siguiente relación de confianza:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "tasks.apprunner.amazonaws.com" }, "Action": "sts:AssumeRole" } ] }
Si deseas que tu aplicación sea pública, puedes dejar la configuración de red por defecto. En caso de que quieras que tu aplicación sea privada, deberás configurar un punto de conexión privado y personalizar la VPC. En esta guía no abarcaremos este tema.
Opcionalmente, habilita AWS WAF (ten en cuenta que puede generar costos adicionales) o configura AWS X-Ray para monitoreo.
Revisa la configuración y haz clic en “Crear e implementar”. Si todo está correcto, verás cómo se despliega tu aplicación.
¡Y eso es todo! Hasta el momento, esta ha sido la forma más sencilla que he encontrado de desplegar aplicaciones con una experiencia similar a la que ofrece Vercel o Netlify. Si tienes alguna duda o presentas algún error siguiendo esta guía, no dudes en ponerte en contacto conmigo a través de Twitter en @yosoydev.
Subscribe to my newsletter
Read articles from Jesús Magallón directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
