Ahorra con Amazon EKS
Tags: aws, eks, kubernetes, typescript
Published: 2024-03-28 08:21:38

Cuando nos planteamos el diseño de una nueva arquitectura para nuestra plataforma uno de los aspectos que suele influenciar en el diseño es el coste. Pero ya no solo es el coste, sino también el impacto medioambiental que supone el hecho de optimizar los recursos de los que disponemos. En muchas ocasiones tenemos distintos entornos que no tienen porque ser usados las 24 horas (desarrollo, qa, staging, ...). En este caso, os voy a mostrar cómo poder ahorrar costes haciendo un scale down de los nodos de vuestros clusters de kubernetes en AWS a través de una Lambda, de esta manera podréis optimizar costes con aquellos entornos que no necesitáis tener encendidos durante todo el día.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iscyppgc425wgrlyzzso.png)

Para realizar este proyecto usaremos [Serverless Framework](https://www.serverless.com/) para crear y desplegar nuestro pequeño sistema de apagado automático, el cual realizaremos en NodeJS con Typescript.

### Instalar serverless framework

Lo primero de todo es instalar serverless framework con el siguiente comando desde un terminal

```
npm install -g serverless
```
Una vez instalado creamos nuestro proyecto en un nuevo directorio ejecutamos el siguiente comando:

```
sls create -t aws-nodejs-typescript

```
Esto nos creará un proyecto serverless con typescript. Al abrirlo veréis como ha creado el serverless.ts y una función de ejemplo “hello”. En el fichero de serverless.ts debéis poner los datos referentes a vuestra cuenta de aws.

Ahora ya podéis probar que el proyecto se ha creado correctamente invocando la función hello desde local:

```
sls invoke local -f start
```

### Lambda
Una vez que comprobamos que el ejemplo funciona, ya podemos empezar a modificar nuestra lambda, o bien podemos crear una nueva. Lo primero que necesitamos es la sdk de aws:

```
npm i aws-sdk
```
y luego añadimos este código a nuestra lambda:


```typescript
import type { ValidatedEventAPIGatewayProxyEvent } from '@libs/api-gateway';
import { formatJSONResponse } from '@libs/api-gateway';
import { middyfy } from '@libs/lambda';
import { NodegroupScalingConfig, UpdateNodegroupConfigRequest, UpdateNodegroupConfigResponse } from 'aws-sdk/clients/eks';
import { AWSError, EKS } from 'aws-sdk';

import schema from './schema';
import { PromiseResult } from 'aws-sdk/lib/request';

const stop: ValidatedEventAPIGatewayProxyEvent<typeof schema> = async (event) => {

  const CLUSTER_NAME:string = '[CLUSTER_NAME]';
  const NODE_GROUP_NAME:string = '[NODE_GROUP_NAME]';

  let nodeGroupConfig:NodegroupScalingConfig = {
    desiredSize: 0,
    maxSize: 1,
    minSize: 0

  };

  let nodeGroupRequest:UpdateNodegroupConfigRequest = {
    clusterName: CLUSTER_NAME,
    nodegroupName: NODE_GROUP_NAME,
    scalingConfig: nodeGroupConfig
  };


  let response:PromiseResult<UpdateNodegroupConfigResponse, AWSError> = await new EKS().updateNodegroupConfig(nodeGroupRequest).promise();
  console.log(response);

  
  return formatJSONResponse({
    message: 'NodeGroupScaling updated to 0',
    event,
  });
};

export const main = middyfy(stop);
```
En el código podemos ver cómo modificamos la configuración de escalado de los nodos, poniéndola a 0. Antes debemos modificar los datos para indicar cual es nuestro cluster y nuestro node group name.

### Lambda scheduler
La lambda podemos ejecutarla manualmente, a través de una llamada a un endpoint de API Gateway o a través de una tarea periódica con CloudWatch Rules.

Para ello vamos al archivo index.ts y vemos que está configurado para llamar a la lambda a través de un endpoint de API Gateway, sustituimos ese código por el siguiente:

```typescript
import { handlerPath } from '@libs/handler-resolver';
export default {
  handler: `${handlerPath(__dirname)}/handler.main`,
  events: [
    {
      schedule: 'cron(0 19 ? * MON-FRI *)',
    }
  ],
};
```
De esta manera nuestra lambda hará un scale down cada día, de lunes a viernes a las 7 de la tarde.

### Lambda role (I)
Para que la lambda tenga acceso a nuestro cluster de eks debemos hacer que su rol tenga acceso a la policy que permite modificar la configuración de escalado de los nodos:

```json
provider: {
    name: 'aws',
    iamRoleStatements: [
      {
        'Effect': 'Allow',
        'Action': ['eks:UpdateNodegroupConfig'],
        'Resource': '*'
      }
    ],
```

### Deploy
Ahora tenemos todo nuestro código preparado para ralizar la función que queremos, solo queda desplegarlo, y es tan simple como hacer lo siguiente:

```
sls deploy
```


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d9o1mqcldam5y6q01wn5.png)

### Lambda role (II)

Una vez desplegados podemos ver que la lambda tiene asignado un rol

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/51yssrsssna74knpxwxg.png)

Debemos copiar el arn de ese rol, ya que debemos configurar nuestro cluster para permitir a ese rol realizar cambios, tal y como se explica en la documentación oficial https://docs.aws.amazon.com/eks/latest/userguide/add-user-role.html

```
eksctl create iamidentitymapping \
    --cluster cluster-name \
    --region=xx-xxxx-x \
    --arn arn:aws:iam::0000000000000:role/eks-management-dev-xx-xxxx-x-lambdaRole \
    --group group:example
```
Esto creará un registro nuevo en el mapa de identidades de nuestro cluster

```
eksctl get iamidentitymapping --cluster cluster-name --region=xx-xxxx-x
```
y con esa acción podremos comprobar que realmente se ha añadido. Con esto ya hemos conseguido dar acceso a nuestra lambda.

Os dejo la documentación oficial donde se detalla como funciona la API de la SDK de AWS: https://docs.aws.amazon.com/eks/latest/APIReference/API_UpdateNodegroupConfig.html

Os dejo también el repositorio GitHub donde está subido este ejemplo: https://github.com/fjaviermoradiaz/aws-eks-manager