Variables de entorno en serverless
Actualmente las aplicaciones que nacen nativamente cloud, tienden a comunicarse con muchos servicios de terceros, esto debido a que parte de su naturaleza consiste en dejar el trabajo pesado a servicios especializados, los cuales resuelven problemas puntuales, en ese contexto estos servicios necesitan credenciales para conectarse a ellos, por eso es importante gestionar las credenciales e inyectarlas como variables de entorno en el sistema, para ello serverless maneja algunos métodos que explicare a continuación
Variables de entorno
En los proyectos de desarrollo generalmente se gestionan las variables de entorno con librerías que leen la información de algún archivo .env y cargan estas variables a la memoria, lo cual se puede apreciar en la Figura 1 , cuando trabajamos con serverless como hablaba hace poco en un post de algunos dolores de cabeza y entre ellos mencionaba las variables de entorno, al final del día en arquitectura serverless y cualquier otra arquitectura la diferencia es muy poca, una lambda es un servicio efímero que se ejecuta en una maquina por un tiempo, la diferencia en este caso es el como hacerlo y para ello debemos soportarnos sobre el SDK de serverleess
Variables de consola
Las variables de CLI son las variables que utilizamos dentro del SDK cuando ejecutamos algún comando, estos parámetros son los que dispone el serverless como banderas, en el siguiente código se puede ver un ejemplo
service: mycognito-cognito-auth
frameworkVersion: '2'
variablesResolutionMode: 20210326
provider:
name: aws
runtime: python3.8
lambdaHashingVersion: 20201221
environment:
stage: ${opt:stage, 'dev'}
functions:
hello:
handler: handler.hello
events:
- httpApi:
method: POST
path: /upload
para ejecutar el despliegue ejecutamos el siguiente comando
serverless deploy --stage dev --aws-profile personal-account
como se aprecia en el código del archivo serverless.yml en la sección enviroment, la estructura para manejar variables de entorno proporcionados por medio de banderas de consola es
${opt:stage, 'dev'}
el valor después de los puntos es el nombre de la bandera, y el valor entre comillas simples es el valor por defecto si la bandera no se pasa, ya que si no se pone un valor por defecto el valor a tomar es nulo
Variables de archivos
Esta opción es usada cuando se necesita referenciar variables de archivos, serverless no soporta .env pero si soporta las dos principales estructuras de archivos usados JSON y YAML la sintaxis para referenciar una variable especifica es ${file(./myFile.json):someProperty} veamos un ejemplo
service: mycognito-cognito-auth
frameworkVersion: '2'
variablesResolutionMode: 20210326
provider:
name: aws
runtime: python3.8
lambdaHashingVersion: 20201221
environment:
stage: ${opt:stage, 'dev'}
httpApi:
authorizers:
MyProjectAuthorizer:
type: jwt
identitySource: $request.header.Authorization
issuerUrl: ${file(./config.${opt:stage, 'dev'}.json):ISSUER_URL}
audience:
- ${file(./config.${opt:stage, 'dev'}.json):AUDIENCE}
functions:
hello:
handler: handler.hello
events:
- httpApi:
method: POST
path: /upload
authorizer:
name: MyProjectAuthorizer
lo ejecutamos para desplegar con el siguiente comando
serverless deploy --aws-profile personal-account
como se aprecia en el archivo yml en la sección httpApi tenemos la siguiente linea
${file(./config.${opt:stage, 'dev'}.json):AUDIENCE}
aquí lo que se hace es abrir el archivo que se llame config.dev.json si el stage es dev, config.qa.json si el stage es qa y asi sucesivamente, es decir se pueden tener archivos de configuraron para diferentes ambientes usando la bandera stage, el archivo json tiene la siguiente estructura
{
"AUDIENCE": "https://test",
"ISSUER_URL": "test"
}
con el :AUDIENCE lo que sucede es que selecciona el atributo audience y asigna este valor, esta opción es bastante utilizada para gestionar nuestras variables de entorno por ambiente
Variables de AWS
Esta opción nos permite referenciar básicamente dos valores pertenecientes a identificadores de AWS, estos valores son el accountid y la region, estas variables son muy importantes sobre todo a la hora de construir identificadores de recursos, usando el ARN, veamos el siguiente ejemplo
service: mycognito-cognito-auth
frameworkVersion: '2'
variablesResolutionMode: 20210326
provider:
name: aws
runtime: python3.8
lambdaHashingVersion: 20201221
environment:
stage: ${opt:stage, 'dev'}
httpApi:
authorizers:
MyProjectAuthorizer:
type: jwt
identitySource: $request.header.Authorization
issuerUrl: ${file(./config.${opt:stage, 'dev'}.json):ISSUER_URL}
audience:
- ${file(./config.${opt:stage, 'dev'}.json):AUDIENCE}
functions:
update:
handler: handler.update
events:
- stream:
type: kinesis
arn: arn:aws:kinesis:${aws:region}:${aws:accountId}:stream/mystream
batchSize: 100
startingPosition: LATEST
maximumRetryAttempts: 10
enabled: true
ejecutamos el despliegue
serverless deploy --stage dev --region us-east-1 --aws-profile personal-account
observen que yo le digo la región por medio del CLI, así que por defecto tomara la región que le pase, ahora cree una función update de tipo stream con kinesis, algo importante es que este recurso requiere identificarlo por medio del arn, si especifico directamente el arn que genera el recurso, cuando haga un despliegue en otra cuenta al harcodear el ARN ocurrirá un error, en este caso lo que hago es referenciar un ARN "dinamico", de tal forma que si cambio de región o cuenta solo debo ponerle el mismo nombre (o podría cargar el nombre de una variable de archivo) con esto mantengo un despliegue dinámico
arn: arn:aws:kinesis:${aws:region}:${aws:accountId}:stream/mystream
Variables de parameter store
Este es un servicio de AWS que permite gestionar datos secretos de una forma fácil y sencilla, esto servicio bastante amplio e interesante pero yo solo me centrare en la conexión que presta al SDK, serverless tiene una integración con SSM, lo que permite acceder a este servicio de forma sencilla, aquí se guardaran variables o credenciales que contienen información sensible, debido que este servicio se conecta con KMS, para resumir, usaremos este servicio cuando se requieran guardar datos que no pueden ser visibles en el código ni en ningún archivo, creare una variable en KMS y luego la guardare en parameter store
Luego la relaciono con parameter store para poder accederla
ahora ya la puedo acceder desde serverless, como mencione es nativa por lo tanto sera bastante sencillo
service: mycognito-cognito-auth
frameworkVersion: '2'
variablesResolutionMode: 20210326
provider:
name: aws
runtime: python3.8
lambdaHashingVersion: 20201221
environment:
stage: ${opt:stage, 'dev'}
environment:
SECRET_VALUE: ${ssm:/secret/url}
httpApi:
authorizers:
MyProjectAuthorizer:
type: jwt
identitySource: $request.header.Authorization
issuerUrl: ${file(./config.${opt:stage, 'dev'}.json):ISSUER_URL}
audience:
- ${file(./config.${opt:stage, 'dev'}.json):AUDIENCE}
functions:
update:
handler: handler.update
events:
- stream:
type: kinesis
arn: arn:aws:kinesis:${aws:region}:${aws:accountId}:stream/mystream
batchSize: 10
startingPosition: LATEST
maximumRetryAttempts: 10
enabled: true
Como se puede apreciar en el código cree una variable en enviroment llamada SECRET_VALUE con el siguiente valor
${ssm:/secret/url}
aquí lo que hago es acceder al servicio de parameter store y obtener el valor en plano de esa variable, el nombre debe ser único por región, si quisiera hacerlo un poco mas dinamice y cambiarlo de región podría hacer lo siguiente
${ssm(us-west-2):/secret/url-${opt:stage, 'dev'}}
en este caso cambie la obtención del parámetro a otra región pero adicional obtiene las que el nombre lleven -stage, de esta forma puedo tener variables de producción, desarrollo, qa etc
Conclusiones
La gran cantidad de opciones para variables de entorno en serverless, no es exactamente para decidir por alguna en especifico, esto debido a que la mayor ventaja esta en mesclar su uso, por lo tanto si yo puedo utilizar varias formas de cargar variables, obtendré un mayor beneficio haciendo mi proyecto lo mas dinámico posible, logrando facilitar el despliegue de mi producto