• Skip to primary navigation
  • Skip to main content
  • Skip to footer
Bluetab

Bluetab

an IBM Company

  • SOLUTIONS
    • DATA STRATEGY
    • Data Readiness
    • Data Products AI
  • Assets
    • TRUEDAT
    • FASTCAPTURE
    • Spark Tune
  • About Us
  • Our Offices
    • Spain
    • Mexico
    • Peru
    • Colombia
  • talent
    • Spain
    • TALENT HUB BARCELONA
    • TALENT HUB BIZKAIA
    • TALENT HUB ALICANTE
    • TALENT HUB MALAGA
  • Blog
  • EN
    • ES

Bluetab

Mi experiencia en el mundo de Big Data – Parte I

October 14, 2021 by Bluetab

Mi experiencia en el mundo de Big Data - Parte I

David Emmanuel Reyes Núñez

Senior Data Engineer

Hace casi dos años (septiembre 2019), no sabía absolutamente nada de tecnologías Big Data, hoy sé que, aunque ya conozco y he interactuado con algunas de ellas, me falta mucho camino por recorrer y muchas cosas por aprender. Todo empieza confiando en ti y a veces necesitas el impulso de alguien que confíe en ti.

 Me ha gustado tanto este tipo de tecnologías que incluso en este par de años, he podido tomar un par de diplomados y concluir recientemente una Maestría en Análisis y Visualización de Datos.

En esta ocasión quiero compartir una de las experiencias que he tenido con estas tecnologías, la cual se trata de realizar ingestas de archivos hacia Google BigQuery  y Google Cloud Storage, utilizando Google Drive como repositorio fuente.

¿Qué necesitamos?

En este primer articulo haremos uso de Google Drive API y Python 2.6 o superior.

Necesitamos también tener proyecto de Google Cloud Platform con la API habilitada. Para crear un proyecto y habilitar una API, haz clic aquí

Debemos ver una pantalla similar a esta:

Seleccionamos el tipo de aplicación que necesitamos y se nos genera un archivo JSON, llamado credentials.json, que podemos descargar y ubicar en algún directorio que sea sencillo de identificar:

También necesitamos credenciales de autorización para una aplicación de escritorio. Para aprender a crear credenciales para una aplicación de escritorio, haz clic aquí.

Ejecutamos el siguiente comando en la consola para instalar las librerías que utilizaremos:

pip3 install google-api-python-client google-auth-httplib2 google-auth-oauthlib 

Una vez que tenemos configurado nuestro ambiente, comenzamos a crear los módulos de nuestra aplicación.

  • Archivo de Configuración

Para nuestro primer script de Python, necesitamos un archivo que guarde nuestra configuración de rutas y elementos, podemos llamarlo config.ini, y lo llenaremos como muestra el ejemplo que sigue:

[GENERAL_CONFIG] —secciónKey_file_location= path del archive json de credencialesscopes = https://www.googleapis.com/auth/drive https://www.googleapis.com/auth/cloud-platform (los scopes sirven para identificar las APIs que usaremos) CompressionLevel = 9ForwardX11 = yes [DRIVE_API] — en esta sección colocamos los id’s del Drive al que nos queremos conectar, se encuentra ubicado después del ultimo slash (/) de la URL:

drive_id = 1-jrMx9oDTOO9eN7ZGcU5tSZVTKtD
folder_2 = 1i70h0VBdL0Gzw9xR9FiO2gfBFxSQz 

Creamos el script de Python que leera nuestro archive config.ini mediante el siguiente código de Python:

import configparser
import datetime

def readConfig():
    #Obtenemos la fecha del sistema, que nos servirá para nombrar el archivo de Log
    fileDate = datetime.datetime.today().strftime('%Y-%m-%d')

    config = configparser.ConfigParser()
    config.read('./config/Config.ini') #path donde se ubica el archivo.ini
    general_config=config['GENERAL_CONFIG'] #referencia a la seccion
    drive_api=config['DRIVE_API']
    
    readConfig.conf_key_file=general_config['key_file_location']
    readConfig.scopes=general_config['scopes']
    
    readConfig.team_drive=drive_api['team_drive']
    
    #generamos el nombre para el archivo de log, con la fecha de sistema
    fileLogMain='MyLogFile.log'
    sfileLogMain =fileLogMain.split('.')
    fileLogMain=sfileLogMain[0]+'_'+fileDate+'.'+sfileLogMain[1]
 
  • Archivo de parámetros

Aquí es donde comenzamos a hablar de GCP.  Si queremos descargar archivos específicos de nuestro drive, tenemos que crear un archivo en donde le indiquemos el nombre, la extensión, el proyecto GCP de destino, el bucket de GS donde lo subiremos y el nombre de la tabla que creará en bigQuery.

Creamos un archivo parameters.csv con la siguiente estructura:

Nombre_Archivo|Proyecto_GCP|Bucket_GCP|DataSet_BQ|Tabla_BQ|Path_GS|Status

Archivo1.csv|mi_proyecto|mi_bucket|raw_data|mi_tabla_bq|path/gs|1
Archivo2.csv|mi_proyecto|mi_bucket2|raw_data|mi_tabla_bq|path2/gs|0
 

La última columna, indica el status para saber si se descargará o no el archivo (1=Activo, 0=Inactivo)

Nota: Debemos contar con permisos y credenciales para los proyectos a los que queramos subir el archivo, este tema lo iremos abordando en siguientes entregas. Ahora solo nos limitaremos a la parte de descargar un archivo desde Google Drive hacia nuestro disco local.

El script de Python para leer y descargar los archivos es el siguiente:

#Librerias para lectura del archivo, autenticación de servicios y lectura de archivos csv
from modules.readConfig import readConfig
from modules.auth import get_service
from modules.processDriveFiles import get_FoldersList
import logging
import csv
import os,glob

def readProperties():
    #Configuramos los niveles de logging
    logging.basicConfig(level=logging.INFO,
                        format='%(asctime)s %(name)-8s %(levelname)-8s %(message)s',
                        datefmt='%m-%d %H:%M',
                        filename=readConfig.log_file_main,
                        filemode='a')
    logging.info('Inicio...')
    #Comienza la lectura del archivo de parámetros
   For row in reader:
    with open(readConfig.params_file, 'r', encoding='utf-8') as f:
   
        reader = csv.DictReader(f, delimiter='|')
        props = {}
        api_name='drive'
        api_version='v3'
        scopes=[readConfig.scopes]
        key_file_location=readConfig.conf_key_file 


        #Validamos que el archivo que queremos esté activo para poder descargarlo y guardamos los parámetros en un arreglo.
        if row['Status']==1:
                props ={'nombre_archivo':row['Nombre_Archivo],'proyecto':row['Proyecto_GCP'],'DataSet_BQ':row['DataSet_BQ'],'Tabla':row['Tabla_BQ'],'Bucket_GCP':row['Bucket_GCP'],'Path_GS':row['Path_GS'], 'Status':row['Status'

#Funciones para autenticación y obtener el listado de folders
creds = get_service(api_name,api_version,scopes,key_file_location,
                        props) 
        get_FoldersList(creds,props)
        f.close()



#funcion get_service (ubicada en el módulo auth.py) Esta función nos servirá para seleccionar el archivo de credenciales que usaremos para autenticarnos en Google drive
def get_service(api_name, api_version, scopes, key_file_location,props):
        key_file_location='./auth/driveCredentials.json'
        
credentialsDrive = service_account.Credentials.from_service_account_file(
            key_file_location, scopes=scopes)


#funcion get_FoldersList. Una vez autenticados, nos permitirá listar los folders de nuestro Google Drive de acuerdo con los parámetros que le enviemos. Esta contenida en el script processDriveFiles.py



def get_FoldersList(creds,props):

    # Obtiene un listado de los Google Drive folders
        done = True
        logging.info('Ejecutando consulta: ')
        query = "mimeType!='application/vnd.google-apps.folder' and name='"+props.get('archivo_origen')+"' and parents!='"+readConfig.success_drive+"' and parents!='"+readConfig.failure_drive+"' and starred=False and trashed=False"
        response = creds.get('service').files().list(
                q=query,            
                fields='*',  
                orderBy='folder',
                driveId=readConfig.team_drive,                
                corpora='drive',
                includeItemsFromAllDrives=True,
                supportsAllDrives=True).execute()

        items = response.get('files', [])

        if not items:
                logging.info('No se han encontrado archivos')
        else:
                for item in items:
                #descargamos el archivo
                    file_id = item['id']
                    fh = io.FileIO(item['name'],'w')
                    
                    request = creds.get('service').files().get_media(fileId=file_id)
                    fh = io.FileIO(item['name'],'w')
                    downloader = MediaIoBaseDownload(fh, request)
                    done = False
				
       statusdw=0 
       #Se realiza la descarga
       while (done is False and int(statusdw)<100):
              file_ext=''
              status, done = downloader.next_chunk()
              logging.info('status: ')
              statusdw=(int(status.progress())*100)  
              logging.info(int(statusdw))
              logging.info('Descargando: '+item['name']+" %d%%." % int(statusdw))                      
              logging.info('Descargando: '+item['name']+" %d%%." % int(status.progress() * 100))
 

En esta parte, ya debemos ver nuestro archive descargado en el directorio local de la aplicación.      

Esta es la primera entrega de nuestro administrador de archivos, el objetivo de este desarrollo es llevar archivos desde Google Drive hacia Google Cloud. En la siguiente entrega continuaremos con el código de la función processDriveFiles.py y crearemos los scripts para hacer la carga de archivos hacia Google Cloud.

¿Quieres saber más de lo que ofrecemos y ver otros casos de éxito?
DESCUBRE BLUETAB

SOLUCIONES, SOMOS EXPERTOS

DATA STRATEGY
DATA FABRIC
AUGMENTED ANALYTICS

Te puede interesar

LakeHouse Streaming on AWS with Apache Flink and Hudi (Part 2)

October 4, 2023
LEER MÁS

Using Large Language Models on Private Information

March 11, 2024
LEER MÁS

MDM as a Competitive Advantage in Organizations

June 18, 2024
LEER MÁS

5 common errors in Redshift

December 15, 2020
LEER MÁS

Introduction to HashiCorp products

August 25, 2020
LEER MÁS

Oscar Hernández, new CEO of Bluetab LATAM.

May 16, 2024
LEER MÁS

Filed Under: Blog, Tech

Serverless Microservices

October 14, 2021 by Bluetab

Serverless Microservices

Francisco Linaje

AWS Solutions Architect

En esta práctica cloud veremos como construir microservicios dentro de AWS siguiendo el paradigma serverless. Este tipo de solución permite disponer de sistemas completamente administrados por AWS donde nosotros no deberemos preocuparnos por disponibilizar los recursos o administrarlos, simplemente especificaremos dentro de su configuración las políticas de ejecucion y escalado si se necesitasen, el pago por tanto es exclusivamente por uso.

Requisitos

Para poder realizar esta práctica deberemos disponer de las siguientes instalaciones que nos permitirán poder desarrollar los microservicios y operar el entorno.

  • Terraform: Desde donde crearemos y desplegaremos nuestro backend e infra.
  • Un usuario con credenciales de acceso al cli de AWS (access_key y secret_key) y permisos necesarios para operar los servicios empleados en la práctica.
  • AWS cli v2: Permitirá configurar los credenciales con aws configure, opcionalmente se pueden realizar exports del access_key y secret_key.
  • Un IDE en este caso visual-studio-code
  • Conocimiento básico en Api Rest y AWS
  • Postman

Overview

Se plantea un escenario donde diferentes usuarios mediante aplicaciones multiplataforma acceden a diferentes recursos dentro de la app consumiendo una API Rest segura desplegada en AWS, para ello deberán en primer lugar autenticarse contra el pool de cognito para obtener un token JWT que les permitirá consumir los diferentes endpoints de la API.

Autenticación y Autorización

Para poder ofrecer seguridad a la API y que sus servicios solo sean consumidos por usuarios autorizados, debemos emplear un broker que nos pueda ofrecer autenticacion y a su vez autorizacion sobre estos servicios.

En primer lugar deberemos de entender el flujo que sigue un usuario para poder ser autenticado y autorizado para consumir un microservicio. En el siguiente diagrama se pueden ver como en una serie de pasos un usuario puede consumir el endpoint.

  1. El usuario se autentica mediante sus credenciales contra el broker.
  2. Si los credenciales son correctos, el broker generara un token JWT que serviría como mecanismo de autorización al poder validar que el usuario es quien dice ser.
  3. Este token deberá emplearse en todas las llamadas a la API Rest, para ello se enviará en el header de Authorization junto a un prefijo Bearer.
  4. Si el token es validado por el broker, se le otorgará acceso al consumo del microservicio.

¿Que es JWT o Json Web Token?

Json Web Token es un estándar abierto (RFC 7519) donde se define una forma autónoma de asegurar integridad en los datos enviados gracias a que va firmada digitalmente con HMAC (firma simetrica – misma clave) o RSA(firma asimetrica – par de claves privada/publica) y asi asegurar que el peticionario es una entidad segura. Además estos tokens pueden estar encriptados adicionalmente para ocultar los reclamos a terceros. El escenario principal donde se emplea es en la autorización del peticionario.

Cada token JWT consta de tres partes separados por un “.” de la forma header.payload.signed codificados en base64 por separado.

Header: Indica el tipo de token y su firma, en el caso de RS256 incluirá el kid que identifica la clave pública del emisor del Token que se empleara para verificar la firma. Mediante el iss del payload y el kid de la clave podremos ver en el navegador https:///.well-known/jwks.json la firma publica empleada para descifrar el token.

Payload: Contiene los claims que permiten verificar los atributos de la generación del token: iss (emisor), aud (audience), exp( expiracion) y otros campos que se puedan añadir adicionalmente para enviar información.

AWS Cognito

Cognito es un servicio completamente administrado que ofrece autenticación de usuarios para aplicaciones multiplataforma, además permite el empleo de identidades federadas como Google, Amazon, Facebook, etc para su registro.

Los usuarios podrán registrarse y loguearse contra un pool de usuarios que funciona como directorio de usuarios donde serán albergados los parámetros de autenticación: email, password, número de teléfono, etc. Se ofrecen además opciones de confirmación de usuario mediante código o enlace vía email o sms. Los usuarios autenticados recibirán un token de acceso que podrán emplear para recibir autorización a los microservicios de Api Gateway. Mediante otras opciones no planteadas en este caso de uso, se podrá recibir credenciales temporales STS de AWS para acceder directamente a servicios AWS mediante la función AssumeRoleWithWebIdentity mediante los pool de identidades.

Los usuarios emplearan la interfaz web de Cognito para autenticarse y recibir un código que podrán intercambiar por un token JWT que emplearán como autorización en los microservicios desplegados en Api Gateway.

Empezaremos creando nuestro pool de usuarios.

  • Asignaremos un nombre al pool.
  • Añadiremos el atributo de acceso y verificación, en este caso email.
  • Definiremos como se realiza la verificación: via enlace. Además si se hará por defecto via SNS “COGNITO_DEFAULT” o via SES “DEVELOPER”.
  • La recuperación de la cuenta se hará vía el email verificado, siendo la prioridad 1, como maxima prioridad en caso de añadir futuros métodos opcionales.
resource "aws_cognito_user_pool" "user_pool" {
        name = var.cognito.user_pool_name
        auto_verified_attributes = ["email"]
        username_attributes = ["email"]

        verification_message_template {
            email_subject_by_link = "APP Notification - Account Verification"
            email_message_by_link = "Please click the link to verify your email address: {##VERIFY EMAIL##}\n<br><br>\n"
            default_email_option = "CONFIRM_WITH_LINK"
        }

        email_configuration {
                email_sending_account = "COGNITO_DEFAULT"
        }

        account_recovery_setting {
            recovery_mechanism {
            name = "verified_email"
            priority =  1
            }
        }
    } 

Por otro lado, activaremos la interfaz propia de AWS a modo de pruebas para poder realizar el proceso de autenticado contra cognito sin tener que realizar un desarrollo del frontend propio.

  • Indicamos que recibiremos un code para poder intercambiarlo por un token JWT, teniendo como scope el email.
  • El proveedor de identidad sera por default Cognito.
  • Indicaremos una url callback de prueba desde donde nos indicarán el code dentro de la url de la forma “?code=”.
resource "aws_cognito_user_pool_client" "client" {
  name = var.cognito.app_client_name
  user_pool_id = aws_cognito_user_pool.user_pool.id
  supported_identity_providers = ["COGNITO"]
  callback_urls = var.cognito.callback_urls
  allowed_oauth_flows_user_pool_client = var.cognito.user_pool_client
  allowed_oauth_flows = ["code"]
  allowed_oauth_scopes = ["openid","email"]
}

resource "aws_cognito_user_pool_domain" "main" {
  domain       = var.cognito.domain_name
  user_pool_id = aws_cognito_user_pool.user_pool.id
} 

Autorización de los microservicios

En Api Gateway configuraremos un autorizador que recibirá el token JWT y comprobará la firma del token, como el emisor y audiencia añadidos en los propios scopes. El proceso de comprobación es automático permitiendo el acceso directo al servicio si este es válido o denegando mediante un unauthorized la request.

resource "aws_apigatewayv2_authorizer" "jwtAuth" {
  api_id           = aws_apigatewayv2_api.api.id
  authorizer_type  = "JWT"
  identity_sources = ["$request.header.Authorization"]
  name             = var.api.jwt_authorizer_name

  jwt_configuration {
    audience = [aws_cognito_user_pool_client.client.id]
    issuer   = "https://${aws_cognito_user_pool.user_pool.endpoint}"
  }
} 

Obtención del token JWT

En primer lugar, deberemos abrir la interfaz proporcionada por Cognito para realizar los procesos de sign-up, sign-in. La podremos encontrar dentro de la consola de AWS, en el servicio de Cognito, dentro de la configuración del cliente de aplicación “Lanzar interfaz de usuario alojada”.

    1. Procederemos a registrar un usuario

    2. Nos pedira que confirmemos el usuario a través del enlace enviado a nuestra cuenta de correo introducida.

    3. El correo recibido tendrá la siguiente forma:

    4. Accedemos al enlace de VERIFY EMAIL

    5. Finalmente tendremos ya nuestro usuario confirmado en nuestro pool de usuarios de Cognito.

    6. Procedemos a logearnos en la interfaz de AWs empleada anteriormente y si los credenciales son correctos nos devolverá a la url de callback configurada cuando hemos creado el pool de usuarios con Terraform junto a un code que emplearemos posteriormente para obtener el token.

  1. Por último, para poder obtener el token debemos realizar una llamada al endpoint de Cognito /oauth2/token con los siguientes atributos en el body.
  • Metodo POST
  • Body
    • Aplicacion x-www-form-urlencoded.
    • Grant_type: authorization_code.
    • Client_id: tu id de la aplicación de Cognito.
    • Redirect_uri: url callback.
    • Code: código obtenido en la url de callback después de “?code=”

Obtendremos como respuesta el identity token (id_token) que contiene toda la informacion personal del usuario y es el que generalmente se empleará para la autorización y el access_token empleado principalmente para llamar a servicios externos sin incluir informacion personal del usuario. Dependiendo del caso de uso, si realmente la información aportada por el identity token no es necesaria, es recomendable emplear el access_token. Por ultimo el refresh token, se emplea principalmente para obtener un identity o access tokens nuevos.

Microservicios

Los microservicios seran desplegados en Api Gateway y tendrán como backend Lambda integrada como proxy y DynamoDB como bbdd. Todos estos servicios funcionan de forma completamente administrada siguiendo los objetivos serverless de esta práctica.

Vamos a definir brevemente estos servicios y ver cual es su papel dentro de la arquitectura.


AWS Api Gateway v2

Mediante Api Gateway podremos desarrollar APIs de una forma sencilla, segura y escalable, ademas de ofrecernos la integración con Lambda para poder operar sin aprovisionamiento. Solo funciona con HTTPs

Dispone de integración proxy para exponer por completo el request como input al backend. En el caso concreto del workshop se empleará Lambda Proxy Integration para poder consumir los parámetros desde el handler de la función vía el evento, para ello deberemos definir en la etapa de implementación POST como tipo, independientemente de que definamos el metodo HTTP del endpoint como GET.

Api Gateway es compatible con CloudFront como de CDN de la API, además es posible incorporar WAF como servicio de mitigación de ataques DDoS.

AWS actualmente dispone de dos versiones de Api Gateway, nosotros desplegaremos la última version, v2.

¿Que diferencias podemos encontrar? Los principales cambios introducidos con la version 2, se basan en:

  • Reducción de costes: 70%, 3.5$ vs 1$ por millón de peticiones.
  • Reducción de la latencia sobre el 50%.
  • Soporte a referencias cruzadas CORS.
  • JWT Authorizers a traves de OIDC y OAuth 2.0.
  • Disponible ruta y stages predeterminadas.
  • Integrado con SAM y CloudFormation.

Para poder crear nuestra Api deberemos crear los siguientes recursos

  • Api: indicando el nombre y su protocolo.
  • Un grupo de registros de Cloudwatch para la Api.
  • Un stage de implementación donde indicaremos el nombre de la implementacion y los parametros de los logs.
resource "aws_apigatewayv2_api" "api" {
  name          = var.api.api_name
  protocol_type = "HTTP"
}

resource "aws_cloudwatch_log_group" "api_gw" {
  name = "/aws/api_gw/${aws_apigatewayv2_api.api.name}"
  retention_in_days = 30
}

resource "aws_apigatewayv2_stage" "stage" {
  api_id = aws_apigatewayv2_api.api.id

  name        = var.api.stage_name
  auto_deploy = true


  access_log_settings {
    destination_arn = aws_cloudwatch_log_group.api_gw.arn

    format = jsonencode({
      requestId               = "$context.requestId"
      sourceIp                = "$context.identity.sourceIp"
      requestTime             = "$context.requestTime"
      protocol                = "$context.protocol"
      httpMethod              = "$context.httpMethod"
      resourcePath            = "$context.resourcePath"
      routeKey                = "$context.routeKey"
      status                  = "$context.status"
      responseLength          = "$context.responseLength"
      integrationErrorMessage = "$context.integrationErrorMessage"
    }
    )
  }
} 

Una vez creada la Api, crearemos los dos endpoints que emplearemos en este workshop: GET,POST

Para ambos indicaremos:

  • Tipo de autorización: JWT
  • Tipo de integración AWS_PROXY, para recibir integramente en la funcion lambda la request
  • Metodo de integración sera POST aun cuando el método del endpoint se declare como GET, ya que Lambda solo se activa a traves de peticiones POST.
  • Id del autorizador creado jwtAuth que tendrá como audience el id de la aplicación Cognito creada y su endpoint como issuer.
  • Se creará además un permiso de ejecución de la función Lambda especificada para su ejecución por parte de ApiGateway

 

GET

#### GET
resource "aws_apigatewayv2_integration" "get_item_app_integration" {
  api_id           = aws_apigatewayv2_api.api.id
  integration_type = "AWS_PROXY"
  description               = "Lambda GET example"
  integration_method        = "POST"
  integration_uri           = aws_lambda_function.get_item_app.invoke_arn
}

resource "aws_apigatewayv2_route" "get_item_app_route" {
  api_id = aws_apigatewayv2_api.api.id

  route_key = "GET /user"
  target    = "integrations/${aws_apigatewayv2_integration.get_item_app_integration.id}"
  authorization_type = "JWT"
  authorizer_id = aws_apigatewayv2_authorizer.jwtAuth.id
}

resource "aws_lambda_permission" "get_item_app_execution" {
  statement_id  = "AllowExecutionFromAPIGateway"
  action        = "lambda:InvokeFunction"
  function_name = aws_lambda_function.get_item_app.function_name
  principal     = "apigateway.amazonaws.com"
  source_arn = "${aws_apigatewayv2_api.api.execution_arn}/*/*"
} 

POST

resource "aws_apigatewayv2_integration" "create_item_app_integration" {
  api_id           = aws_apigatewayv2_api.api.id
  integration_type = "AWS_PROXY"
  description               = "Lambda example"
  integration_method        = "POST"
  integration_uri           = aws_lambda_function.create_item_app.invoke_arn
}

resource "aws_apigatewayv2_route" "create_item_app_route" {
  api_id = aws_apigatewayv2_api.api.id

  route_key = "POST /user"
  target    = "integrations/${aws_apigatewayv2_integration.create_item_app_integration.id}"
  authorization_type = "JWT"
  authorizer_id = aws_apigatewayv2_authorizer.jwtAuth.id
}

resource "aws_lambda_permission" "create_item_app_execution" {
  statement_id  = "AllowExecutionFromAPIGateway"
  action        = "lambda:InvokeFunction"
  function_name = aws_lambda_function.create_item_app.function_name
  principal     = "apigateway.amazonaws.com"
  source_arn = "${aws_apigatewayv2_api.api.execution_arn}/*/*"
} 

AWS Lambda

Para el desarrollo de la lógica de nuestros microservicios emplearemos AWS Lambda, servicio de computación completamente administrado con escalado automático. Donde se definen unos recursos de memoria y CPU para realizar la ejecución de cada función. Soporta de forma nativa lenguajes como Java, NodeJS, Python, etc. Las funciones son albergadas en un paquete de implementación del tipo zip alojadas en un bucket de S3 interno o creado por nosotros.

 

En primer lugar, crearemos el rol de ejecución, donde además definiremos que acciones se pueden ejecutar dentro de estas y sobre que servicios, en nuestro caso simplemente permitiremos acciones CRUD sobre la tabla DynamoDB especifica que crearemos posteriormente.

  • Crearemos el rol de Lambda y daremos permisos al servicio de Lambda para asumirlo
  • Crearemos dos políticas básicas que serán asignadas a este rol: ejecución (permiso para cargar registros en CloudWatch) y una segunda política custom donde estarán definidas las acciones que podrán realizarse sobre la tabla de DynamoDb empleada para el workshop.
resource "aws_iam_role" "lambda_exec_dev" {
  name = "serverless_lambda_dev"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Action = "sts:AssumeRole"
      Effect = "Allow"
      Sid    = ""
      Principal = {
        Service = "lambda.amazonaws.com"
      }
    }
    ]
  })
}

resource "aws_iam_role_policy_attachment" "lambda_policy_attachment_dev" {
  role       = aws_iam_role.lambda_exec_dev.name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}

resource "aws_iam_role_policy_attachment" "lambda_dynamodb_policy_attachment_dev" {
  role       = aws_iam_role.lambda_exec_dev.name
  policy_arn = aws_iam_policy.lambda_dynamodb_policy_dev.arn
}

resource "aws_iam_policy" "lambda_dynamodb_policy_dev" {
  name        = "lambda_dynamodb_policy_dev"
  description = "Lambda DynamoDB access"

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = [
          "dynamodb:Query",
          "dynamodb:GetItem",
          "dynamodb:PutItem",
          "dynamodb:UpdateItem",
          "dynamodb:BatchWriteItem",
          "dynamodb:BatchGetItem",
        ]
        Effect   = "Allow"
        Resource = [aws_dynamodb_table.app_table.arn]
      },
    ]
  })
  depends_on = [aws_dynamodb_table.app_table]
} 

Las funciones irán recogidas en un zip con el codigo Python que será subido a un bucket interno que crearemos expresamente para albergarlas. En cada función indicaremos su handler, runtime, rol de ejecución y el bucket/objeto donde poder encontrar el paquete de funciones.

data "archive_file" "lambda_functions_package" {
  type = "zip"

  source_dir  = "${path.module}/scripts/"
  output_path = "${path.module}/scripts/crud_lambdas.zip"
}

resource "aws_s3_bucket_object" "lambda_functions_package_object" {
  bucket = aws_s3_bucket.internal_dev.bucket
  key    = "crud_lambdas.zip"
  source = data.archive_file.lambda_functions_package.output_path
  etag = filemd5(data.archive_file.lambda_functions_package.output_path)
}

resource "aws_s3_bucket" "internal_dev" {
  bucket = var.bucket_name
  acl    = "private"
} 

Para su creacion simplemente indicaremos el nombre de la función Lambda, rol de ejecución, runtime, la función de ejecucíon, el bucket y el zip donde estan alojadas.

  • La función get_item_app nos devolvera el usuario buscado por id.
  • La función create_item_app permitira guardar el usuario en bbdd.
resource "aws_lambda_function" "get_item_app" {
  function_name = "get_user"
  handler       = "get_user.lambda_handler"
  runtime       = "python3.6"

  s3_bucket = aws_s3_bucket.internal_dev.bucket
  s3_key    = aws_s3_bucket_object.lambda_functions_package_object.key
  source_code_hash = data.archive_file.lambda_functions_package.output_base64sha256
  role = aws_iam_role.lambda_exec_dev.arn
}

resource "aws_lambda_function" "create_item_app" {
  function_name = "create_user"
  handler       = "create_user.lambda_handler"
  runtime       = "python3.6"

  s3_bucket = aws_s3_bucket.internal_dev.bucket
  s3_key    = aws_s3_bucket_object.lambda_functions_package_object.key
  source_code_hash = data.archive_file.lambda_functions_package.output_base64sha256
  role = aws_iam_role.lambda_exec_dev.arn
} 

Estas funciones Python emplearán la librería boto3 para poder realizar de una forma sencilla y rápida el conector contra la tabla de DynamoDB, estarán alojadas bajo el directorio de /scrips.

dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('AppDummy') 

A partir de este conector mediante table.get_item() o table.create_item() podremos realizar nuestras operaciones GET y POST respectivamente. Si la acción se ejecuta correctamente lanzaremos un codigo 200 y devolveremos el objeto añadido/obtenido.

  • En la funcion create_user obtendremos los atributos del body de la request que se encontrarán en el propio evento al ser una función integrada como AWS_PROXY.
item = json.loads(event["body"])
user = item["User"]
...
table = dynamodb.Table('AppDummy')
    response = table.put_item(
        Item=user
    )
... 
  • En la función get_user encontraremos el atributo de búsqueda UserId dentro del evento en “queryStringParameters”.
id = str(event["queryStringParameters"]['UserId'])
...
response = table.get_item(
        Key={
            'UserId': id
        }
    )
... 

AWS DynamoDB

Por último, crearemos una tabla básica para albergar los atributos de nuestros usuarios, tendrá simplemente como PK el id de usuario en string para soportar alfanuméricos.

resource "aws_dynamodb_table" "app_table" {
  name           = "AppDummy"
  billing_mode   = "PAY_PER_REQUEST"
  hash_key       = "UserId"

  attribute {
    name = "UserId"
    type = "S"
  }
} 

Despliegue

Mediante Terraform desplegaremos nuestra infraestructura, primero deberemos lanzar un init que descargará los plugins y inicializará nuestro directorio de trabajo con los archivos de configuración de AWS, para posteriormente ejecutar un plan, si el despliegue de recursos planificado por el plan concuerda con lo que buscamos finalmente ejecutaremos un apply para desplegar toda nuestra infra y realizar las pruebas.

En esta práctica, nuestro estado permanecera en local, no configuraremos AWS como backend para los estados de Terraform

terraform init

terraform plan -var-file="env/dev.tfvars"

terraform apply -var-file="env/dev.tfvars" 

Pruebas

Una vez desplegado nuestro proyecto, comprobaremos el correcto funcionamiento de los microservicios que hemos programado. Además en ambos casos, deberemos añadir el campo authorization en el header de la peticion HTTP con el prefijo Bearer y el access_token obtenido anteriormente para poder ser autorizados a consumir el microservicio.

POST /user

En primer lugar, probaremos la peticion HTTP POST /user, debería añadir el usuario a la tabla de DynamoDB creada y enviando como respuesta un codigo 200 y el usuario añadido a la tabla.

GET /user

De la misma forma, consumiremos el microservicio que a partir del UserId nos devolverá la información del usuario, recordando que al ser una petición GET la información relativa a la consulta irá en el Query Params (propia url)

Conclusión

En esta práctica hemos podido aprender a como desarrollar una API Rest segura y completamente administrada dentro del entorno de AWS, sirviendonos de la última versión de Api Gateway que facilita la integración nativa con autorizadores de JWT.

La integración de Cognito con Api Gateway nos torga la capa de seguridad y administración de los usuarios. Respectivamente con Lambda y DynamoDB disponemos de la capa de lógica/persistencia de nuestra API. La integración nativa de todos estos servicios nos facilita el desarrollo de estas aplicaciones al disminuir la carga de trabajo dedicada tanto al desarrollo puro, como a la integracion de los distintos servicios involucrados y su administración, además gracias a Terraform disponemos de toda la infraestructura como código facilitando su futura evolución y disponibilización en otros entornos de una forma mucho más rápida y comprensible.

En futuras entradas veremos como desarrollar otros escenarios típicos que podemos encontrar en nuestro día a día dentro de AWS, con el fin de tener unas primeras herramientas para poder solventar futuros escenarios que se nos planteen.

Espero que la práctica haya sido de vuestro agrado e interés, os espero en futuras entregas!

Enlaces de interés

  • Documentación general de Cognito.
  • Documentación general de Api Gateway.
  • Documentación general de Lambda.
  • Documentación general de DynamoDB.
  • Documentación general de JWT.
Navegación

Introducción

Requisitos

Overview

Autenticación y Autorización

Microservicios

Despliegue

Pruebas

Conclusión

Enlaces de interés

¿Quieres saber más de lo que ofrecemos y ver otros casos de éxito?
DESCUBRE BLUETAB

Francisco Linaje

AWS Solutions Architect

SOLUCIONES, SOMOS EXPERTOS

DATA STRATEGY
DATA FABRIC
AUGMENTED ANALYTICS

Te puede interesar

Bank Fraud detection with automatic learning II

September 17, 2020
LEER MÁS

De documentos en papel a datos digitales con Fastcapture y Generative AI

June 7, 2023
LEER MÁS

Some of the capabilities of Matillion ETL on Google Cloud

July 11, 2022
LEER MÁS

Azure Data Studio y Copilot

October 11, 2023
LEER MÁS

CLOUD SERVICE DELIVERY MODELS

June 27, 2022
LEER MÁS

Bluetab is certified under the AWS Well-Architected Partner Program

October 19, 2020
LEER MÁS

Filed Under: Blog, Practices, Tech

Desplegando una plataforma CI/CD escalable con Jenkins y Kubernetes

September 22, 2021 by Bluetab

Desplegando una plataforma CI/CD escalable con Jenkins y Kubernetes

Lucas Calvo Berlanga

Cloud Engineer

En este artículo de la práctica cloud veremos cómo crear una plataforma de CI/CD de una forma totalmente automatizada. Para ello nos apoyaremos en una metodología GitOps para así realizar nuestros despliegues de una forma más sencilla, escalable e industrializada.

La idea de este taller es crearnos un cluster de GKE donde tengamos desplegado Jenkins como nuestra pieza central de CI/CD y que este vaya escalando en agentes de una forma totalmente automatizada para que según la demanda de ejecuciones de jobs nuestro cluster crezca o decrezca de una forma transparente para nosotros. Otra de las grandes ventajas de esta arquitectura es que podemos crear diferentes tipos de slaves para así cubrir todo tipo de ejecuciones dentro de nuestra compañía.

Los componentes/herramientas que usaremos en este proyecto serán los siguientes:

  • Terraform.
  • GKE.
  • Jenkins.
  • Prometheus.
  • Grafana.
  • Slack.

Objetivos

  1. Creación de la las vpcs donde se desplegará la infraestructura.
  2. Creación de la infraestructura, en nuestro caso GKE, de una forma totalmente automatizada.
  3. Despliegue de Jenkins como componente principal haciendo uso del provider del helm.
  4. Configuración de Jenkins usando el plugin de Jcasc.
  5. Despliegue de prometheus para la monitorización de nuestro sistema haciendo uso de helm.
  6. Despliegue de grafana para la monitorización de nuestro sistema haciendo uso de helm.
  7. Configuración de grafana haciendo uso de prometheus y unos dashboard configurados automáticamente.
  8. Revisión de toda la infraestructura levantada y chequeo de la monitorización.
  9. Ejecución de un Job de ejemplo para ver el flujo completo.
  10. Comprobar sistema de alertas tanto caída de sistemas como de jobs completos.
  11. Comprobar el escalado de nuestra infraestructura y de los componentes desplegados.

Introducción a Terraform

Terraform es una herramienta open-source para automatizar la creación de infraestructura como código. Para nuestro caso de uso usaremos Terraform tanto para desplegar la infraestructura, tanto GKE como la VPC donde se hará el despliegue de este último.

Terraform no solo nos permite desplegar infraestructura como código sino que también nos da la opción de usar otros provedores como el de Kubernetes para la creación de namespace (entre otras muchas cosas) o la posibilidad de realizar despliegues de otros componentes dentro del cluster de GKE con el proveedor de Helm. Terraform.

Introducción a GKE

GKE es el servicio de Kubernetes gestionado y autoescalado por GCP. Es donde se realizarán todos los despliegues tanto de Jenkins como de la monitorización que tendrá nuestra plataforma. GKE. Este componente lo vamos a automatizar haciendo uso de Terraform donde se harán las implementaciones necesarias para realizar el despliegue correctamente.

Introducción a Jenkins

Jenkins es nuestra pieza central de la plataforma de CI/CD. Jenkins es una herramienta de construcción, implementación y automatización de proyectos software. Para el despliegue de este componente nos apoyaremos en el provider de Helm de Terraform. Jenkins.

Introducción a Prometheus

Prometheus es un sistema de monitorización que usaremos para comprobar el estado de todos los pods desplegados en nuestra plataforma, así como para monitorizar el estado de nuestra infraestructura como tal(Picos de consumo, nodos caídos…). Para el despliegue de este componente nos apoyaremos en el provider de Helm de Terraform. Prometheus.

Introducción a Grafana

Grafana es nuestra herramienta de visualización de la monitorización. Nos decantamos por esta herramienta ya que se integra perfectamente con prometheus y nos permite crear dashboards personalizados de nuestra infraestructura. Para el despliegue de este componente nos apoyaremos en el provider de Helm de Terraform. Grafana.

Introducción a Slack

Por último, haremos uso de slack como herramienta de envió de alertas tanto en la ejecución de los jobs de Jenkins como alertas de monitorización de caídas en alguno de los pods desplegados. Slack

Preparación de entorno

Para la ejecución de la plataforma CI/CD será necesario hacer la instalación de estas herramientas:
  1. Terraform. Aquí se usa la versión v1.0. Se puede descargar aquí. Para instalar una versión anterior consultar aquí.
  2. Helm. Guía de instalación de Helm.
  3. GCP. Para la realización del taller será necesario la creación de una cuenta en GCP.

Clonación de repositorio

El código fuente está disponible en github.

git clone https://github.com/lucasberlang/gitops-kubernetes-jenkins/
cd gitops-kubernetes-jenkins 

Índice de ficheros

Dentro de la carpeta src tendremos todo el código necesario para hacer el despliegue de nuestra infraestructura de una forma automatizada. En este apartado haremos un breve resumen de lo que contienen cada uno de los ficheros para que nuestro proyecto funcione.

  • providers.tf

Definición de los proveedores que usaremos para hacer el despliegue con Terraform. En nuestro caso haremos uso del provider de Google, helm y kubernetes.

  • terraform.tfvars

Variables que usaremos dentro de los módulos de Terraform.

  • variables.tf

Definición de las variables que usaremos tanto en los módulos de Terraform como en los secretos que desplegaremos para hacer uso en Jenkins.

  • outputs.tf

Información del proyecto que nos interesa conocer.

  • main.tf

Contendrá la lógica realizada en Terraform para hacer el despliegue de los componentes de infraestructura que necesitamos, en este caso VPC y GKE. Para ello haremos uso de dos módulos desarrollados por Bluetab.

  • kubernetes_secrets.tf

Archivo que contendrá toda la información sensible que usaremos dentro de los despliegues que realizaremos (Grafana, Prometheus, Jenkins), como contraseñas, tokens, etc… .

  • helm_monitoring.tf

Archivo que contiene la configuración que se realizará con el proveedor de Helm para el despliegue de los componentes Prometheus y Grafana.

  • helm_jenkins.tf

Archivo que contiene la configuración que se realizará con el proveedor de Helm para el despliegue de los componentes Jenkins.

  • grafana.yaml

En este archivo contiene la configuración inicial que usará Helm cuando realicemos el despliegue de grafana.

  • prometheus.yaml

En este archivo contiene la configuración inicial que usará Helm cuando realicemos el despliegue de prometheus.

  • jenkins.yaml

En este archivo contiene la configuración inicial que usará Helm cuando realicemos el despliegue de jenkins.

  • vars.example.env

Archivo que contendrá todas las variables de entorno que usaremos en nuestro proyecto. En este caso le pasaremos las variables más sensibles como pueden ser la contraseña de grafana o el token usado en slack para no tener que subirlo al repositorio.

Configuración de Slack

  1. Primero de todo nos tendremos que registrar en Slack.
  2. Una vez que tengamos el registro se deberá seguir la guían de configuración de Slack con Jenkins. De este paso solo necesitaremos el token de Slack que pasaremos como variable de entorno TF_VAR_slack_token.
  3. Además, nos crearemos un canal en Slack el cual pasaremos posteriormente como variable de entorno, TF_VAR_slack_channel.
  4. Será necesario también coger el nombre del dominio de slack y pasárselo como variable,TF_VAR_team_domain.
  5. Por último nos crearemos un webhook para el envío de alertas desde Prometheus. Para ello podemos seguir esta guía. De este paso solo necesitaremos el endpoint para luego pasarlo como variable de entorno, TF_VAR_slack_api_url.

Configuración de variables

Antes de realizar la ejecución de nuestro proyecto debemos definir una serie de variables de entorno que necesitaremos para el correcto funcionamiento de este. Para ello haremos uso del archivo vars.example.env donde tenemos ya definidas las variables más importantes. Para efectos de la demo las únicas variables que se tendrán que modificar son las siguientes:

  • TF_VAR_project_id: el id del proyecto de la cuenta de GCP donde se realizará el despliegue de la infraestructura.
  • TF_VAR_vault_addr: se añadirá la dirección de vault para la autenticación en el despliegue del proyecto de prueba. En caso de no tener vault se podrá también configurar con las credenciales de Azure o crearse otra proyecto de ejemplo.
  • TF_VAR_vault_token: token de vault que se utilizará para autenticarse contra Azure en nuestro proyecto de demo. Igual que con la variable de arriba no será necesaria si se configura el proyecto demo para autenticarse con las credenciales de Azure.
  • TF_VAR_slack_api_url: El endpoint de Slack que configuraremos para que se envíen las alertas de nuestra plataforma.
  • TF_VAR_slack_channel: Canal de Slack donde se publicarán los mensajes de las alertas.
  • TF_VAR_slack_token: El token de Slack que configuraremos para que se envíen las alertas de nuestra plataforma.
  • TF_VAR_team_domain: El dominio de Slack que configuraremos para que se envíen las alertas de nuestra plataforma.
  • TF_VAR_user_grafana: Nombre del usuario de Grafana que usaremos para loguearnos.
  • TF_VAR_password_grafana: Contraseña para el usuario de Grafana definido anteriormente para hacer el login.

Una vez se hayan configuradas todas estas variables se tendrá que lanzar el siguiente comando para que queden como variables de entornos.

source vars.example.env 

Configuración VPC

Para la configuración de la VPC haremos uso del módulo corporativo desarrollado por Bluetab. Este módulo está publica en el repositorio de github y está totalmente documentado por si se tiene alguna duda de su funcionamiento o se quiere realizar alguna modificación en la VPC. Para que esto funcione lo único que debemos de hacer es instanciar nuestro módulo con las variables necesarias para hacerle funcionar:

module "network" {
  source = "git@github.com:lucasberlang/gcp-network.git"

  project_id         = var.project_id
  description        = var.description
  enable_nat_gateway = true

  intra_subnets = [
    {
      subnet_name           = "private-subnet01"
      subnet_ip_cidr        = "10.0.0.0/16"
      subnet_private_access = false
      subnet_region         = var.region
    }
  ]

  secondary_ranges = {
    private-subnet01 = [
      {
        range_name    = "private-subnet01-01"
        ip_cidr_range = var.ip_range_pods
      },
      {
        range_name    = "private-subnet01-02"
        ip_cidr_range = var.ip_range_services
      },
    ]
  }

  labels = var.labels
} 

Lo único necesario para ejecutar la creación de la VPC será el id del proyecto de GCP. Además de desplegar la VPC con la creación del módulo se realizará el despliegue de una subred a nuestra VPC con el direccionamiento 10.0.0.0/16. Este es totalmente modificable así como el nombre de la subred o la región en donde se despliega. También se han creado dos rangos secundarios de direccionamientos para los pods y servicios que desplegaremos en GKE.

Configuración GKE

Para el despliegue del proyecto se ha optado por usar infraestructura totalmente gestionado, en este caso haremos uso de GKE. Para automatizar el despliegue usaremos el módulo corporativo desarrollado por Bluetab. Este módulo está publica en el repositorio de github y esta totalmente documentado por si se tiene alguna duda de su funcionamiento o se quiere realizar alguna modificación en el GKE. Para que esto funcione lo único que debemos de hacer es instanciar nuestro módulo con las variables necesarias para hacerle funcionar:

module "gke" {
  source = "git@github.com:lucasberlang/gcp-gke.git"

  project_id              = var.project_id
  name                    = "gitops"
  regional                = true
  region                  = var.region
  network                 = module.network.network_name
  subnetwork              = module.network.intra_subnet_names.0
  ip_range_pods           = "private-subnet01-01"
  ip_range_services       = "private-subnet01-02"
  enable_private_endpoint = false
  enable_private_nodes    = false
  master_ipv4_cidr_block  = "172.16.0.0/28"
  kubernetes_version      = "latest"

  master_authorized_networks = [
    {
      cidr_block   = module.network.intra_subnet_ips.0
      display_name = "VPC"
    },
    {
      cidr_block   = "0.0.0.0/0"
      display_name = "shell"
    }
  ]

  node_pools = [
    {
      name         = "default-node-pool"
      machine_type = "n1-standard-4"
    },
  ]

  istio     = var.istio
  dns_cache = var.dns_cache
  labels    = var.labels
} 

Como se puede observar en el código la implementación del módulo es bastante intuitivo solo será necesario declarar algunas variables. Las variables más importantes son el tipo de instancia que usaran los node-pools y la subred donde se desplegará el cluster de GKE que para esto usaremos la red declarada anteriormente.

El funcionamiento de GKE en nuestra VPC será este:

Configuración Jenkins

Para la configuración de Jenkins como hemos dicho anteriormente usaremos el provider de Helm. Para hacer el despliegue nos crearemos un namespace llamado gitops donde se desplegará Jenkins. El fichero donde se hará esta configuración es helm_jenkins.tf.

resource "kubernetes_namespace" "jenkins" {
  metadata {
    name = "gitops"
  }
} 

Luego procederemos a hacer el despliegue de Jenkins con Helm.

data "local_file" "helm_chart_values" {
  filename = "${path.module}/templates/jenkins.yaml"
}

resource "helm_release" "jenkins" {
  name       = "jenkins"
  repository = "https://charts.jenkins.io"
  chart      = "jenkins"
  version    = "3.5.3"
  namespace  = kubernetes_namespace.jenkins.metadata.0.name
  timeout    = 180

  values = [data.local_file.helm_chart_values.content]

} 

Cogeremos el chart del repositorio oficial de Jenkins y le pasaremos el fichero jenkins.yaml como configuración inicial.

De este fichero vamos a destacar algunos puntos que hemos modificado para realizar una automatización de nuestro servicio.

 image: "jenkins/jenkins"
  imagePullPolicy: "Always"
  adminSecret: true
  adminUser: "admin"
  jenkinsUrl: "http://${kubernetes_endpoint}:80" 

Se ha realizado la modificación del nombre del adminUser para que sea más genérico y jenkinsUrl para que nos de el endpoint de Jenkins en el GKE cuando nos leguen las alertas de los jobs en el slack.

  containerEnv:
    - name: kubernetes_endpoint
      valueFrom:
        secretKeyRef:
            name: jenkins-k8s-config
            key: kubernetes_endpoint
    - name: gitlab_username
      valueFrom:
        secretKeyRef:
            name: gitlab-credentials
            key: gitlab_username
    - name: gitlab_ssh_key
      valueFrom:
        secretKeyRef:
            name: gitlab-credentials
            key: gitlab_ssh_key
    - name: vault_token
      valueFrom:
        secretKeyRef:
            name: vault-credentials
            key: vault_token
    - name: vault_addr
      valueFrom:
        secretKeyRef:
            name: vault-credentials
            key: vault_addr
    - name: arm_access_key
      valueFrom:
        secretKeyRef:
            name: azure-credentials
            key: arm_access_key
    - name: slack_token
      valueFrom:
        secretKeyRef:
            name: slack-credentials
            key: slack_token
    - name: team_domain
      valueFrom:
        secretKeyRef:
            name: slack-credentials
            key: team_domain            
  servicePort: 80
  serviceType: LoadBalancer 

Estos son todos los secrets que hemos definido en el archivo kubernetes_secrets.tf. Estos son necesarios para el correcto funcionamiento de Jenkins ya que haremos uso de muchos de estos secretos cuando realicemos la configuración con el plugin de Jcasc de Jenkins.

Además, en la configuración del serviceType lo hemos definido como LoadBalancer externo y el servicePort 80 para que sea visible desde el exterior.

installPlugins:
    - kubernetes
    - docker-custom-build-environment
    - ansicolor
    - aws-credentials
    - azure-credentials
    - gitlab-api
    - gitlab-branch-source
    - docker-java-api
    - github-branch-source
    - pipeline-graph-analysis
    ... 

Listado de todos los plugins que se instalarán por defecto en la configuración inicial de Jenkins.

Configuración inicial de Jcasc

Jcasc es el plugin de configuración como código de Jenkins. Lo usaremos para hacer una serie de configuraciones previas como pueden ser:

  • Configuración de los slaves:

Esta parte es donde definiremos la creación de un cloud de kubernetes donde se desplegarán todos nuestros pods cada vez que se lance una ejecución de un job en nuestro Jenkins. También configuraremos la imagen que se usará para desplegar cada slave y el namespace donde se realizará dicho despliegue. Para nuestro ejemplo usaremos una imagen ya modificada con la instalación de Terraform y algunos componentes como el SDK de Google o el cli de azure (lucasbluetab/jnlp-agent-Terraform-gcloud:latest). Además, se configurará los recursos que gaste cada vez que se levante un pod en cualquier ejecución de nuestros, consiguiendo así una infraestructura totalmente escalable.

      cloud: |
        jenkins:
          clouds:
            - kubernetes:
                name: "Terraform-executors"
                serverUrl: "https://kubernetes.default"
                jenkinsTunnel: "jenkins-agent:50000"
                jenkinsUrl: "http://jenkins:80"
                skipTlsVerify: true
                namespace: "gitops"
                templates:
                    - name: "jenkins-jnlp"
                      namespace: "gitops"
                      nodeUsageMode: NORMAL
                      label: "jnlp-exec"
                      containers:
                        - name: "jnlp"
                          image: "jenkins/jnlp-slave"
                          alwaysPullImage: false
                          workingDir: "/home/jenkins/agent"
                          ttyEnabled: true
                          command: ""
                          args: ""
                          resourceRequestCpu: "500m"
                          resourceLimitCpu: "1000m"
                          resourceRequestMemory: "1Gi"
                          resourceLimitMemory: "2Gi"
                      volumes:
                        - emptyDirVolume:
                            memory: false
                            mountPath: "/tmp"
                      idleMinutes: "1"
                      activeDeadlineSeconds: "120"
                      slaveConnectTimeout: "1000"
                    - name: "Terraform"
                      namespace: "gitops"
                      nodeUsageMode: NORMAL
                      label: "Terraform-exec"
                      containers:
                        - name: "Terraform"
                          image: "lucasbluetab/jnlp-agent-Terraform-gcloud:latest"
                          alwaysPullImage: false
                          workingDir: "/home/jenkins/agent"
                          ttyEnabled: true
                          command: "/bin/sh -c"
                          args: "cat"
                          resourceRequestCpu: "100m"
                          resourceLimitCpu: "500m"
                          resourceRequestMemory: "500Mi"
                          resourceLimitMemory: "1Gi"
                      volumes:
                        - emptyDirVolume:
                            memory: false
                            mountPath: "/tmp"
                      podRetention: "never"
                      activeDeadlineSeconds: "900"
                      slaveConnectTimeout: "1000" 
  • Configuración de las credenciales: se definirán todas las credenciales que se usarán dentro de Jenkins, en nuestro caso las credenciales de Jenkins y alguna extras que podremos configurar si es necesario.
      credentials: |
          credentials:
              system:
                  domainCredentials:
                  - credentials:
                    - basicSSHUserPrivateKey:
                        scope: GLOBAL
                        id: "GitLab"
                        username: ${gitlab_username}
                        passphrase: ""
                        privateKeySource:
                          directEntry:
                            privateKey: ${gitlab_ssh_key}
                    - string:
                        scope: GLOBAL
                        id: vaultUrl
                        secret: ${vault_addr}
                    - string:
                        scope: GLOBAL
                        id: vaultToken
                        secret: "${vault_token}"
                    - string:
                        scope: GLOBAL
                        id: azureARMKey
                        secret: "${arm_access_key}"
                    - string:
                        scope: GLOBAL
                        id: slackToken
                        secret: "${slack_token}"
                    - string:
                        scope: GLOBAL
                        id: teamDomain
                        secret: "${team_domain}" 
  • Configuración de Slack: Aquí se pasará la configuración que realizamos en Jenkins en nuestro caso le indicaremos el dominio de nuestro Slack, para nuestras pruebas Bluetabmundo, y la sala por defecto donde se escribirán todas las alertas que manden nuestros jobs. Para terminar, le pasaremos el token de Slack para que haga correctamente la conexión. Tanto el teamDomain como la room deberán ser modificados con los que habéis creado en el apartado de configuración de Slack.
      unclassified: |
        unclassified:
          slackNotifier:
            teamDomain: bluetabmundo
            room: "#jenkins"
            tokenCredentialId: slackToken 
  • Arreglo de bugs: Una de las ventajas más interesantes de Jcasc es que nos permite la utilización de scripts de groovy dentro de su configuración. En nuestro caso crearemos un script para quitar un bug que salta en Jenkins al usar el plugin de de env-injector. Básicamente este script nos quita un warning que no debería salir pero por un bug de la versión del plugin salta.
      scriptgroovy: |
        groovy:
          - script: >
              import jenkins.model.*;
              import jenkins.security.*;
              import hudson.security.*;
              import hudson.model.*;
              import static groovy.json.JsonOutput.*;
              import hudson.ExtensionList;

              ExtensionList<UpdateSiteWarningsConfiguration> configurations = ExtensionList.lookup(UpdateSiteWarningsConfiguration.class);
              println configurations;
              
              UpdateSiteWarningsConfiguration configuration = configurations.get(0);
              HashSet<UpdateSite.Warning> activeWarnings = new HashSet<>();
              
              activeWarnings.add('SECURITY-248');
              
              configuration.ignoredWarnings = activeWarnings;
              
              configuration.save(); 
  • Uso de Jobdsl: Con el plugin de Jcasc también lo podemos combinar con el plugin de Jobdsl que nos permite definirnos jobs cuando arranque nuestro Jenkins y así tener ya creado todos nuestros principales jobs.
      init-jobs: |
            jobs:
              - script: >
                  folder('Terraform')
              - script: >
                  multibranchPipelineJob('Terraform/azure-test') {
                      branchSources {
                          branchSource {
                              source {
                                  git {
                                      remote('https://github.com/lucasberlang/Terraform-azure-test.git')
                                  }
                              }
                              strategy {
                                  defaultBranchPropertyStrategy {
                                      props {
                                          noTriggerBranchProperty()
                                      }
                                  }
                              }
                          }
                      }
                      configure {
                          it / sources / data / 'jenkins.branch.BranchSource' / source / traits {
                            'jenkins.plugins.git.traits.BranchDiscoveryTrait'()
                          }
                      }
                      triggers {
                          periodic(60)
                      }
                  } 

Configuración Prometheus

Para la configuración de Prometheus como hemos dicho anteriormente usaremos el provider de Helm. Para hacer el despliegue nos crearemos un namespace llamado monitoring donde se desplegará Prometheus. El fichero donde se hará esta configuración es helm_monitoring.tf.

resource "kubernetes_namespace" "jenkins" {
  metadata {
    name = "gitops"
  }
} 

Luego procederemos a hacer el despliegue de Prometheus con Helm.

data "template_file" "file" {
  template = "${file("${path.module}/templates/prometheus.yaml")}"
  vars = {
    slack_api_url = "${var.slack_api_url}"
    slack_channel = "${var.slack_channel}"
  }
}

resource "helm_release" "prometheus" {
  chart      = "prometheus"
  name       = "prometheus"
  namespace  = kubernetes_namespace.monitoring.metadata.0.name
  repository = "https://charts.helm.sh/stable"

  values = [data.template_file.file.rendered]
} 

Para ello haremos unas modificaciones en el template prometheus.yaml sustituyendo las variables slack_api_url y slack_channel dentro del fichero por las variables de entorno que le pasamos en el paso inicial. Estas variables serán usadas para el envío de alertas si existiera algún problema en el cluster de GKE o el algún pod desplegado en este.

De este fichero vamos a destacar algunos puntos que hemos modificado para realizar una automatización de nuestro servicio.

  • Configuración del servicio: Configuraremos el servicio de Prometheus como un ClusterIP para que solo sea visible dentro del cluster.
    type: ClusterIP 
  • Configuración de las alertas de Slack: Añadiremos la configuración de envío de alertas por si se cae algún nodo de la infraestructura o hay alguna caída de servicio como puede ser un fallo en el pod de Jenkins. Si sucede alguno de los dos puntos anteriores nos llegará una alerta a nuestro canal de Slack.
alertmanagerFiles:
  alertmanager.yml:
    global:
      slack_api_url: ${slack_api_url}

    receivers:
      - name: slack-notifications
        slack_configs:
          - channel: ${slack_channel}
            send_resolved: true
            icon_url: https://avatars3.githubusercontent.com/u/3380462
            title: |
              [{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}] {{ .CommonLabels.alertname }} for {{ .CommonLabels.job }}
              {{- if gt (len .CommonLabels) (len .GroupLabels) -}}
                {{" "}}(
                {{- with .CommonLabels.Remove .GroupLabels.Names }}
                  {{- range $index, $label := .SortedPairs -}}
                    {{ if $index }}, {{ end }}
                    {{- $label.Name }}="{{ $label.Value -}}"
                  {{- end }}
                {{- end -}}
                )
              {{- end }}
            text: |
              {{ range .Alerts -}}
              *Alert:* {{ .Annotations.title }}{{ if .Labels.severity }} - `{{ .Labels.severity }}`{{ end }}
          
              *Description:* {{ .Annotations.description }}
          
              *Details:*
                {{ range .Labels.SortedPairs }} • *{{ .Name }}:* `{{ .Value }}`
                {{ end }}
              {{ end }}

    route:
      group_wait: 10s
      group_interval: 5m
      receiver: slack-notifications
      repeat_interval: 3h 

Aquí es donde se hará la sustitución de nuestro webhook de Slack(slack_api_url) configurado en el paso inicial y también la sustitución del canal donde va a escribir las alertas(slack_channel).

  • Configuración de recolección de logs: por defecto Prometheus hace la recolección de todos los logs de kubernetes al nivel de infraestructura. La única modificación que haremos es que recopile los logs del pod de Jenkins (Este funciona ya que hemos instalado el plugin de prometheus en Jenkins) así recibiremos información valiosa de Jenkins que luego explotaremos con Grafana.
      - job_name: 'jenkins'
        metrics_path: /prometheus
        static_configs:
          - targets: [jenkins.gitops.svc.cluster.local:80] 

Configuración Grafana

Como último paso realizaremos la configuración de Grafana. Para esto como hemos dicho anteriormente usaremos el provider de Helm. Para hacer el despliegue usaremos el namespace creado anteriormente llamado monitoring donde se desplegará Grafana. El fichero donde se realizaremos el despliegue de Grafana es helm_monitoring.tf.

Para realizar el despliegue con helm usaremos el siguiente código:

data "local_file" "helm_chart_grafana" {
  filename = "${path.module}/templates/grafana.yaml"
}

resource "helm_release" "grafana" {
  chart      = "grafana"
  name       = "grafana"
  namespace  = kubernetes_namespace.monitoring.metadata.0.name
  repository = "https://charts.helm.sh/stable"

  values = [data.local_file.helm_chart_grafana.content]
} 

Todas las configuraciones iniciales de Grafana vienen modificadas en el template grafana.yaml.

Las configuraciones más importantes que hemos realizado en dicho archivo son las siguientes:

  • Configuración del servicio: el servicio de Grafana se levantará como LoadBalancer para que sea visible desde el exterior y lo podamos revisar desde nuestro navegador web.
service:
  type: LoadBalancer
  port: 80
  targetPort: 3000
    # targetPort: 4181 To be used with a proxy extraContainer
  annotations: {}
  labels: {}
  portName: service 
  • Configuración de credenciales: para loguearnos en Grafana haremos uso del secret de kubernetes grafana-secrets que hemos definido con usuario y contraseña.
admin:
  existingSecret: grafana-credentials
  userKey: adminUser
  passwordKey: adminPassword 
  • Configuración del datasource: como configuración inicial también añadiremos Prometheus como fuente de datos en Grafana. Para ello tendremos que añadir la url interna de prometheus, en nuestro caso será el nombre del servicio (prometheus-server) más el namespace donde esta desplegado (monitoring) y por último la resolución del DNS en GKE.
datasources:
 datasources.yaml:
   apiVersion: 1
   datasources:
   - name: Prometheus
     type: prometheus
     url: http://prometheus-server.monitoring.svc.cluster.local
     isDefault: true 
  • Configuración de los dashboards: añadiremos unos dashboards inciales cuando se levante el servicio de Grafana. Estos dashboards serán los siguientes:

    • Jenkins-Performance: Encargado de la monitorización del servicio de Jenkins a nivel de ejecución de jobs y de uso de recursos.

    • Kubernetes-nodes: Monitorización de la infraestructura de GKE.

    • kubernetes-cluster: Monitorización de la infraestructura de GKE.

dashboards:
  default:
    Jenkins-Performance:
      gnetId: 14764
      revision: 1
      datasource: Prometheus
    Kubernetes-nodes:
      gnetId: 315
      revision: 3
      datasource: Prometheus
    Kubernetes-cluster:
      gnetId: 6417
      revision: 1
      datasource: Prometheus 

Despliegue de la infraestructura

Una vez realizado el paso de configuración de variables pasaremos a realizar el despliegue de toda la infraestructura (GKE y VPC) y de todos los componentes que vamos a desplegar (Jenkins, Prometheus y Grafana).

Para ello al tenerlo todo configurado con Terraform solo debemos ejecutar los siguientes comandos dentro de nuestro proyecto:

cd src/
terraform init
terraform apply 

Una vez ejecutado los comandos anteriores la salida esperada de nuestro proyecto será la siguiente:

Revisión de la infraestructura

Una vez desplegado toda la infraestructura empezaremos a hacer la revisión de todo lo desplegado para ello nos conectaremos al cluster de GKE de la siguiente forma:

gcloud container clusters get-credentials $cluster_name --region $region --project $project_id 

Para ello se deberá sustituir las variables cluster_name por el nombre del cluster, region por la región donde se ha desplegado el cluster de GKE y la variable project_id haciendo referencia al proyecto donde se desplegará.

Una vez conectado al cluster de GKE haremos una revisión de los nodos desplegados:

kubectl get nodes 

Esto nos devolverá:

Así como pods y servicios que tenemos disponibles:Así como pods y servicios que tenemos disponibles:

kubectl get pods,svc -n gitops
kubectl get pods,svc -n monitoring 

Con la siguiente salida:

Revisión de Jenkins

Una vez hemos verificado nuestra infraestructura vamos a realizar la revisión de Jenkins.

Con la información sacada del servicio de jenkins accederemos a la interfaz web donde nos tendremos que loguear, en nuestro caso 34.79.219.11.

Para sacar la contraseña de nuestro usuario de Jenkins tendremos que ejecutar el siguiente comando:

JENKINS_PASSWORD=$(kubectl get secret jenkins -n gitops -o jsonpath="{.data.jenkins-admin-password}" | base64 --decode);echo
echo $JENKINS_PASSWORD 

Esto nos devolverá la contraseña y ya podremos hacer el login:

Como se puede observar una vez dentro de Jenkins tenemos la configuración que hemos realizado con Jcasc y la creación del job que hemos definido con JobDsl.

Revisión de Grafana

Para la revisión de Grafana debemos acceder también a la interfaz web y loguearnos. La dirección de la interfaz web la podemos observar con el comando de kubectl lanzado anteriormente en nuestro caso es 34.140.61.173.

Para loguearnos usaremos la información que pasamos por nuestras variables de entorno, TF_VAR_password_grafana y TF_VAR_user_grafana.

Podemos comprobar que la configuración que hemos añadido al template de creación de datasources y dashboards es correcta.

  • Datasources:
  • Dashboards:

Job de prueba

Para nuestro ejemplo usaremos un proyecto que desplegará un resource group en Azure, se encuentra en el siguiente repositorio.

Lo más destacable de este proyecto es la definición de nuestro pipeline de ejecución:

pipeline {
  agent {
      label "terraform-exec"
  }
 stages {
  stage('checkout') {
   steps {
    container('terraform') {
     echo "La rama de la que se va a hacer el checkout es: master"
     git branch: "master", url: 'https://github.com/lucasberlang/terraform-azure-test.git'
    }
   }
  }
   stage('Review Terraform version') {
    steps {
    container('terraform') {
      sh 'terraform --version'
    }
    }
   }
   stage('Terraform init') {
    steps {
    container('terraform') {
      sh 'terraform init -upgrade'
    }
   }
   }
  stage('Terraform plan infrastructure') {
    steps {
    container('terraform') {
     withCredentials([string(credentialsId: 'vaultUrl', variable: 'VAULT_ADDR'),
     string(credentialsId: 'vaultToken', variable: 'VAULT_TOKEN')
     ]) {
      sh 'export VAULT_ADDR=${VAULT_ADDR} && export VAULT_TOKEN=${VAULT_TOKEN} && terraform plan'
     slackSend channel: "#practica-cloud-deployments",color: '#BADA55', message: "Plan completed! Do you approve deployment? ${env.RUN_DISPLAY_URL}"
     }
    }
   }
  }
  stage('Approval') {
   when{
    not {
     branch 'poc'
    }
   }
   steps {
    container('terraform') {
     script {
     def userInput = input(id: 'confirm', message: 'Apply Terraform?', parameters: [ [$class: 'BooleanParameterDefinition', defaultValue: false, description: 'Apply terraform', name: 'confirm'] ])
     }
    }
   }
  }
  stage('Terraform Apply') {
    steps {
    container('terraform') {
     withCredentials([string(credentialsId: 'vaultUrl', variable: 'VAULT_ADDR'),
     string(credentialsId: 'vaultToken', variable: 'VAULT_TOKEN')
     ]) {
      sh 'export VAULT_ADDR=${VAULT_ADDR} && export VAULT_TOKEN=${VAULT_TOKEN} && terraform apply -auto-approve'
     slackSend color: '#BADA55', message: "Apply completed! Build logs from jenkins server ${env.RUN_DISPLAY_URL}"
     }
    }
    }
  }
  stage('Waiting to review the infrastructure') {
    steps {
    container('terraform') {
     slackSend channel: "#practica-cloud-deployments", color: '#BADA55', message: "Waiting 5 minutes before destroy the infrastructure!"
     sh 'sleep 300'
    }
   }
    
  }
  stage('Destroy Infra') {
    steps {
    container('terraform') {
     withCredentials([string(credentialsId: 'vaultUrl', variable: 'VAULT_ADDR'),
     string(credentialsId: 'vaultToken', variable: 'VAULT_TOKEN')
     ]) {
      sh 'export VAULT_ADDR=${VAULT_ADDR} && export VAULT_TOKEN=${VAULT_TOKEN} && terraform destroy -auto-approve'
     slackSend channel: "#practica-cloud-deployments", color: '#BADA55', message: "Destroy completed! Build logs from jenkins server ${env.RUN_DISPLAY_URL}"
     }
    }
    }
  }
 }
} 

En el pipeline se han definido las siguientes stages:

  • Checkout: se hará el checkoput de nuestro proyecto.
  • Review Terraform Version: revisaremos la versión de terraform desplegada en nuestro contenedor agente.
  • Terraform init: stage de inicialización de entorno.
  • Terraform plan infrastructure: revisión de la infraestructura a desplegar en nuestro caso un resource group de Azure.
  • Approval: paso de aprobación manual para comprobar si la infraestructura a desplegar es correcta.
  • Terraform Apply: ejecución y creación de toda la infraestructura que vamos a realizar en Azure.
  • Waiting to review the infrastructure: solo como para la demo haremos una espera de 5 minutos para revisar toda nuestra infraestructura.
  • Destroy Infra: destrucción de toda la infraestructura una vez esperado estos 5 minutos.

Ejecución end to end

Una vez realizado todas las comprobaciones pertinentes vamos a ejecutar el job de prueba en Jenkins para ver como escala nuestra plataforma así como el envío de alertas y la monitorización que realizaremos en Grafana.

Accederemos a nuestro job de prueba y lo ejecutaremos, dándole al botón construir ahora:

Una vez que el job esta lanzado podemos comprobar cómo se instancia un nuevo agente de Jenkins para nuestra ejecución teniendo así una infraestructura totalmente escalable:

watch -n 1 kubectl get pods -n gitops 

Cuando el job este ejecutando podemos observar cómo nos van a ir llegando alertas automáticamente al Slack. La primera alerta llegará en el stage de approval donde nos pedirá Jenkins que revisemos la infraestructura que vamos a desplegar y si es correcta aprobaremos el cambio:

Y una vez que entremos en el link de slack nos saltará directamente la interfaz de blueocean:

Una vez aprobado el cambio se realizará el despliegue en nuestra cuenta de Azure, donde podemos revisar que esta todo correcto y el resource group, en nuestro caso example-test, se ha desplegado correctamente:

Por último podemos observar todas las alertas que hemos recibido en nuestro canal de Slack las cuales nos avisaran en el stage de approval, waiting y destroy de la infra:

Escalabilidad de la solución

Una vez que hemos comprobado que nuestra solución está totalmente automatizada y que funciona correctamente vamos a comprobar la escalabilidad de nuestra plataforma.

Para ello lo que vamos a hacer es lanzar múltiples ejecuciones de nuestro job de Jenkins para ver cómo va desplegando la solución tanto a nivel de agentes (Levantará un pod por ejecución) como la escalabilidad de nuestros nodos cuando tengamos más peticiones y consumo de memoria y CPU.

El primer paso será ejecutar múltiples jobs desde la interfaz web:

Como se puede observar nuestros pods irán escalando según el número de ejecuciones que tenemos:

Para ver como escala a nivel de máquinas usaremos el siguiente comando:

watch -n 1 kubectl get nodes 

Como se puede observar se ha agregado un nuevo nodo hace 23 segundos por lo que nuestra plataforma es capaz de ir escalando según las peticiones que se realicen desde nuestro Jenkins.

Monitorización de la plataforma

Dado que hemos realizado tanto la configuración de Grafana como la de Prometheus vamos a hacer uso de nuestros dashboards predefinidos para monitorizar la plataforma en Grafana y el envío de alertas con Prometheus.

Dashboards

Estos dashboards que hemos creado son los siguientes:

  • Jenkins: Performance and Health Overview: dashboard con información relativa a nuestras ejecuciones en Jenkins, número de jobs, memoria utilizada… .
  • Kubernetes Cluster: en este dahsboard tendremos información del cluster de kubernetes a nivel de pods corriendo en el cluster memoria consumida por pod etc.
  • Kubernetes Cluster: en el último dahsboard se mostrará gráficos de picos de red, CPUs y memoria a nivel de máquinas en el Cluster de GKE.

Alertas del sistema

Además de alertas cuando se ejecuta un job también se hemos realizado la configuración de alertas de la infraestructura vía Prometheus. Este nos enviará alertas cuando el pod de Jenkins este caído o alguno de los nodos del cluster se caiga.

Para hacerlo un poco más ilustrativo usaremos el ejemplo de autoescalado que hemos realizado antes para que Prometheus nos envíe una alerta cuando empiece a quitarse máquinas porque el cluster de GKE detecte que no se está consumiendo tanta memoria.

Cuando hicimos la prueba de autoescalado vimos como se había agregado un nuevo nodo llamado gke-go-euw2-bk-sca-g-default-node-poo-c9c04d95-sgmz. Este después del pico de ejecuciones de jobs en Jenkins se auto eliminará ya que no tendremos tanta actividad en nuestro GKE.

Prometheus al detectar que nuestro nodo ha desaparecido nos enviará una alerta de nodo caído automáticamente a Slack.

Así sabremos de una forma totalmente automatizada si nuestra infraestructura ha sufrido algún problema.

Para el caso de los pods lo que vamos a provocar es una caída del componente de Jenkins para que así Prometheus nos envíe una alerta de pod caído.

Para ello lo que vamos a realizar es la destrucción del pod de Jenkins para provocar una caída del servicio:

kubectl delete pod jenkins-0 -n gitops 

Una vez que el pod este eliminado se arrancará automáticamente:

Y pasado un minuto Prometheus nos avisará de la caída del servicio de Jenkins y de su posterior recuperación:

Eliminación de recursos

El último paso que haremos tras probar el éxito de nuestra plataforma CI/CD será realizar la destrucción de todos sus componentes para ello ejecutaremos el siguiente comando:

cd src/
terraform destroy 

Conclusión

Es de sobra conocido la importancia de automatizar el ciclo de vida del desarrollo software pero en este artículo le hemos querido dar una vuelta más a esta automatización consiguiendo industrializar toda nuestra plataforma CI/CD. Para ello hemos automatizado tanto el levantamiento de toda nuestra infraestructura con Terraform así como la configuración inicial de nuestras tres piezas centrales Jenkins, Prometheus y Grafana.

Haciendo uso de plugin como Jcasc conseguimos automatizar toda la configuración inicial de Jenkins que muchas veces es bastante tediosa de hacer de arranque. Esto nos permite que si alguna vez hay una caída de nuestro servicio siempre mantendremos una configuración base en este, evitando tiempos de configuración que al final se reduce en tiempo de espera para el usuario.

Además, añadiendo una monitorización inicial tanto con Prometheus como con Grafana somos capaces de crear un sistema de avisos que nos ayude a comprobar y chequear el correcto comportamiento de toda nuestra plataforma para así poder evitar futuras incidencias.

Por último el uso de GKE en este tipo de plataformas consiguen un ahorro de costes bastante importante en nuestra organización ya que nos da la posibilidad de tener un autoescalado dependiendo del uso que se esté realizando sobre nuestra plataforma, pudiendo así tener picos de gran carga sin tener un deterioro del servicio.

Navegación

Introducción

Objetivos

Introducción a Terraform

Introducción a GKE

Introducción a Jenkins

Introducción a Prometheus

Introducción a Grafana

Introducción a Slack

Preparación de entorno

Clonación de repositorio

Índice de ficheros

Configuración de Slack

Configuración de variables

Configuración VPC

Configuración GKE

Configuración Jenkins

Configuración Prometheus

Configuración Grafana

Despliegue de la infraestructura

Job de prueba

Ejecución end to end

Escalabilidad de la solución

Monitorización de la plataforma

Eliminación de recursos

Conclusión

Autor

¿Quieres saber más de lo que ofrecemos y ver otros casos de éxito?
DESCUBRE BLUETAB

Lucas Calvo Berlanga

Cloud Engineer

SOLUCIONES, SOMOS EXPERTOS

DATA STRATEGY
DATA FABRIC
AUGMENTED ANALYTICS

Te puede interesar

Cómo depurar una Lambda de AWS en local

October 8, 2020
LEER MÁS

Gobierno del Dato: Una mirada en la realidad y el futuro

May 18, 2022
LEER MÁS

Bank Fraud detection with automatic learning

September 17, 2020
LEER MÁS

Snowflake Advanced Storage Guide

October 3, 2022
LEER MÁS

Essential features to consider when adopting a cloud paradigm

September 12, 2022
LEER MÁS

Big Data and loT

February 10, 2021
LEER MÁS

Filed Under: Blog, Practices, Tech

Optimización de los procesos de la Cadena de Suministro.

July 26, 2021 by Bluetab

Optimización de los procesos de la Cadena de Suministro.

Nuestro cliente es una multinacional mexicana referente del sector de la venta y distribución de bebidas, con importante implantación en América y recientemente adquirida por un grupo estadounidense líder en el sector. En su proceso de optimización continua ha emprendido un importante proceso de transformación digital dirigido a dotar de inteligencia y automatización a su cadena de suministro (SCM), desde la gestión de los proveedores, a la cadena de logística y hasta la entrega a sus clientes.

En Bluetab México hemos colaborando con ellos en la definición de los KPIs de las diferentes áreas implicadas (ventas, compras, inventario, gestión de activos, transporte y producción), la construcción del DWH y el diseño de la capa de consumo/visualización sobre Microstrategy. Estamos en el proceso de la generación de parámetros de gestión que además de la descripción de lo que está pasando (KPIs), puedan anticipar de forma predictiva lo que es esperable ante una situación de contexto (KPPs indicadores predictivos clave).

brown cardboard boxes on white metal rack

Nuestro cliente ha logrado dotar a sus usuarios de negocio de herramientas de toma de decisión flexibles y automatizadas que permiten la evolución hacia el autoconsumo de información. Actualmente seguimos colaborando con ellos realizando mejoras y nuevos proyectos en el área y está previsto que extendamos el modelo a varias geografías.

SUCCESS STORIES

Filed Under: Casos Tagged With: data-fabric

Integración con plataformas externas

July 26, 2021 by Bluetab

Integración con plataformas externas

El cambio del mercado retail desde la situación pre-COVID ha supuesto la necesidad de transformar los negocios de venta física, a negocios on-line. Muchos de los líderes han tenido que cambiar modelos de distribución y venta en tienda por modelos con venta eCommerce y logística de entrega a domicilio. Líderes como Amazon han supuesto una vía eficiente para utilizar su posición y experiencia logística para completar la ultima milla en la cadena de suministro.

Nuestro cliente es la compañía líder en la representación de marcas de ropa, accesorios y artículos de hogar en la región y opera más de 2,000 puntos de venta en departamentales y más de 600 boutiques. Siendo una referencia para todas las marcas de éxito en Latinoamérica.

cardboard robot toy on wooden tree

El proyecto tiene como objetivo implementar dentro de su canal de ventas de e-commerce el portal de Amazon, para lo que cargamos el inventario en la plataforma del partners, a fin de que el producto, se venda y cobre al cliente final a través de su infraestructura, mientras que su despacho se realice a través de la infraestructura del cliente con las guías generadas por Amazon.

Derivado del volumen de operaciones, el control manual no es viable, por lo que deben implementarse adecuaciones tecnológicas y de proceso a fin de garantizar la correcta operación con dicha plataforma, generándose diversas interfaces que permiten el intercambio de información entre nuestro cliente y los portales, a través de las diversas APIS que dicha plataforma pone a disposición para dicho fin, asegurando la correcta operación y administración del proceso de comercialización. Además, mediante los servicios generados se asegura la monitorización del nivel de satisfacción de los clientes.

SUCCESS STORIES

Filed Under: Casos Tagged With: data-fabric

Elixir & React Senior Software Engineer

July 14, 2021 by Bluetab

Elixir & React Senior Software Engineer

/MADRID

En Bluetab comenzamos el año con muchas ganas, creciendo en nuestro desarrollo de negocio y sin duda, con nuevos retos que vienen cargados de fuerza, buenas sensaciones, optimismo y sobre todo buscando aportar calidad humana.

Nuestro producto Truedat (una distribución open-source orientada a la gestión de los datos), busca incorporar a un profesional de software, basado en Elixir y React para involucrarse en el desarrollo y arquitectura de la solución.

Así es Truedat: https://www.truedat.io/

¡Si crees que puedes ser tú y te interesa, continúa leyendo!


Somos fieles a nuestra cultura y queremos compartir contigo nuestra filosofía, siendo cooperativa, de trabajo en equipo y formativa. Descubrir que se puede trabajar bajo pasión y que desde este lado te podemos aportar muchas cosas tanto en lo profesional como en lo personal.

Ningún mar en calma hizo experto a un marinero. ¿Somos exigentes? Sí, porque nuestros retos los son, pero te desarrollarás entre cracks, en entornos innovadores y con las últimas tendencias tecnológicas.

Como Desarrollador en Elixir & React sólo pedimos que, más allá de las ganas de afrontar todo tipo de misiones, ser una persona curiosa y un poco geek por qué no, tengas alta experiencia técnica en las tecnologías remarcadas, lo demás lo ponemos nosotros:


Tecnologías servicios backend (API Rest): 

  • Elixir –> Phoenix (Framework web)
  • Python–> Flask (Framework web)

Tecnologías frontend: 

  • React-Redux.

 

Otros productos externos:

  • Postgresql –> Base de datos relacional
  • ElasticSearch –> motor de búsquedas
  • Redis –> Cache
  • Kong –> Api Gateway
  • Vault –> almacenamiento de credenciales
  • Neo4j –> Base de datos de grafos
  • Prometheus –> Base de datos de series temporales
  • Grafana –> Visualización de gráficos
  • AWS –> Infraestructura
  • Terraform –> Despliegue de infraestructura
  • Docker –> Contenedores de los servicios

 

Tu comodidad nos importa. Trabajamos en un entorno confortable, con mucha conciliación, sin dress-code (ni en su momento en la oficina) y con proyección de carrera. Esto es lo que te aporta ser bluetaber:

  • Contrato indefinido con un salario competitivo. El salario se relaciona en función de los conocimientos técnicos detectados en el proceso y rol asignado, procurando respetar el sentido de equidad con la plantilla y siendo susceptible de mejora tras las evaluaciones continuas de desempeño.
  • Horarios flexibles de entrada/salida adaptado a tus necesidades personales.
  • Jornada intensiva (viernes, julio y agosto).
  • Smartworking (Actualmente 100% remoto debido a la situación sanitaria).
  • ¡Formación continua gamificada con rewards!
  • Plan de carrera individual definido ya sea técnica, funcional o management (decide tu camino pero, ¡sigue formándote!).
  • Dispondrás a tu disposición de un Career Coach para que te guíe en tu desarrollo profesional.
  • Beneficios sociales más allá de tu salario: Seguro médico & póliza dental con una amplia cobertura para ti + Tarjeta restaurant (11€ por jornada completa).
  • Retribución flexible: Tarjeta transporte y cheque guardería.
  • Tu regalo de cumpleaños.

 

Además de todo esto, en Bluetab lo importante son las personas.

Consideramos que la diversidad es enriquecedora y queremos velar por la inclusión e igualdad de oportunidades, por lo que contamos con un Plan de Igualdad y un Código Ético que recoge estos principios para garantizar la no discriminación de nuestras colaboradoras y colaboradores por cuestión de raza, color, nacionalidad, origen social, edad, sexo, estado civil, orientación sexual, ideología, opiniones políticas, religión o cualquier otra condición personal, física o social.

Si te gusta la tecnología y la resolución de problemas tanto como a nosotros, ¡nos encantaría conocerte!

Nivel de experiencia
  • Algo de responsabilidad
Sector
  • Servicios y tecnologías de la información
Tipo de empleo
  • Jornada completa
Funciones laborales
  • Tecnología de la información 
  • Ingeniería
¿Quieres saber más de lo que ofrecemos y ver otros casos de éxito?
DESCUBRE BLUETAB

Queremos Conocerte

    Al cumplimentar el formulario das tu consentimiento para el tratamiento de los datos que nos faciltas de forma voluntaria, libre e informada a efectos de participar en los procesos de selección de la Bluetab Solutions S.L.U. No cedemos ni cederemos tus datos a terceros, salvo autorización expresa u obligación legal. Tampoco están previstas transferencias internacionales a terceros países. Podrás ejercer tus derechos con relación a tus datos por escrito en la dirección Edificio Ruiz Picasso, Pl. Pablo Ruiz Picasso, 11, 2ª Planta, Tetuán, 28020 Madrid España o mediante el envío de un correo electrónico a dpo@beta.bluetab.net. Para más información al respecto consultar Política de Privacidad.

      I accept the  Privacy Policy

    Filed Under: Talent Tagged With: Madrid

    • « Go to Previous Page
    • Page 1
    • Interim pages omitted …
    • Page 5
    • Page 6
    • Page 7
    • Page 8
    • Page 9
    • Interim pages omitted …
    • Page 18
    • Go to Next Page »

    Footer

    LegalPrivacy Cookies policy

    Patron

    Sponsor

    © 2025 Bluetab Solutions Group, SL. All rights reserved.