Saltar al contenido

Autor: Walberth

Integrando AWS Lambda a un Pipeline de Azure Devops

Hola, espero que estén bien. Este post es una continuación al post anterior, en el cual revisamos como podemos realizar el deploy de un api en un AWS Lambda. Ahora seguiremos con lo que es más lógico que suceda luego de hacer esto, automatizar este proceso. En post anteriores, he mostrado el uso de Azure Devops para los procesos de devops que uso, veamos como realizamos en este caso.

Primero, debemos tener creado un rol en IAM para que nuestro pipeline de devops se pueda authenticar. Con esto realizado, ingresar a azure devops y nos dirigimos a la siguientes opción.

Configuramos una nueva credencial
Elegimos AWS

Con esto hecho, vamos a AWS para crear un bucket, que es donde configuraremos luego para que nuestra pipeline deje los archivos.

Creamos un nuevo bucket

En nuestro pipeline, vamos a seleccionar la siguiente tarea.

Tarea a seleccionar
Información a completar

Para que funcione nuestro proceso de devops, es necesario crear una serie de archivos en nuestro proyecto, son dos los archivos, el primero es “serverless.template” y “aws-lambda-tools-defaults.json”. Les dejo el detalle de la información que va en cada archivo y les comento cual es la información que debe variar.

serverless.template

{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Transform": "AWS::Serverless-2016-10-31",
  "Description": "An AWS Serverless Application that uses the ASP.NET Core framework running in Amazon Lambda.",
  "Parameters": {},
  "Conditions": {},
  "Resources": {
    "AspNetCoreApi": {
      "Type": "AWS::Serverless::Api",
      "Properties": {
        "StageName": "Prod",
        "BinaryMediaTypes": [
          "*~1*"
        ]
      }
    },
    "AspNetCoreFunction": {
      "Type": "AWS::Serverless::Function",
      "Properties": {
        "Handler": "Exphadis.Services.WebApi::Exphadis.Services.WebApi.LambdaEntryPoint::FunctionHandlerAsync",
        "Runtime": "dotnetcore3.1",
        "CodeUri": "",
        "MemorySize": 512,
        "Timeout": 30,
        "Role": null,
        "Policies": [
          "AWSLambda_FullAccess"
        ],
        "Environment" : {
          "Variables" : {
            "ASPNETCORE_ENVIRONMENT" : "",
            "SENTRY_CONNECTION" : "",
            "DATABASE_CONNECTION" : "",
            "JWT_KEY" : "",
            "JWT_EXPIRATION" : ""
          }
        },
        "Events": {
          "ProxyResource": {
            "Type": "Api",
            "Properties": {
              "Path": "/{proxy+}",
              "Method": "ANY",
              "RestApiId": {
                "Ref": "AspNetCoreApi"
              }
            }
          },
          "RootResource": {
            "Type": "Api",
            "Properties": {
              "Path": "/",
              "Method": "ANY",
              "RestApiId": {
                "Ref": "AspNetCoreApi"
              }
            }
          }
        }
      }
    }
  },
  "Outputs": {
    "ApiURL": {
      "Description": "Site for Exphadis Support",
      "Value": {
        "Fn::Sub": "https://${AspNetCoreApi}.execute-api.${AWS::Region}.amazonaws.com/site/"
      }
    }
  }
}

Consideren, que deben modificar de esta plantilla:

  • Handler
  • Environment – Variables

aws-lambda-tools-defaults.json

{
    "Information" : [
        "All the command line options for the Lambda command can be specified in this file."
    ],
    "profile"     : "default",
    "region"      : "us-east-1",
    "configuration" : "Release",
    "framework"     : "netcoreapp3.1",
    "s3-prefix"     : "exphadis-service/",
    "template"      : "serverless.template",
    "template-parameters" : "",
    "s3-bucket"           : "exphadis-service",
    "stack-name"          : "Exphadis-Service-Serverless"
}

Consideren modificar la información que visualizan, de-acuerdo al escenario que tengan.

Veamos como tendría que quedar nuestro archivo “pipeline.yml”.

trigger:
  - feature/deploy-lambda

pool:
  vmImage: 'windows-latest'

variables:
  solution: '**/*.sln'
  buildPlatform: 'Any CPU'
  buildConfiguration: 'Release'

steps:
  - task: LambdaNETCoreDeploy@1
    name: 'Deploy_Lambda'
    displayName: 'Deploy to lambda'
    inputs:
      awsCredentials: 'AWS Admin'
      regionName: 'us-east-1'
      command: 'deployServerless'
      packageOnly: false
      lambdaProjectPath: '$(System.DefaultWorkingDirectory)\\Exphadis.Services.WebApi'
      stackName: 'Exphadis-Service-Serverless'
      s3Bucket: 'exphadis-service'
      s3Prefix: 'Exphadis.Services.WebApi/'


Notemos especificamente estas partes.

Información importante

Fuera de elegir las credenciales que ya hemos configurado previamente, la región, es necesario indicar:

  • lambdaProjectPath: La ruta en nuestro proyecto donde está nuestro entrypoint para lambda.
  • stackName: El nombre de nuestro lambda
  • s3Bucket: El nombre del bucket en S3 que hemos creado
  • s3Prefix: El prefijo de la carpeta en la que se va a desplegar nuestra proyecto compilado.

Probemos ahora toda nuestra configuración.

Despliegue en lambda desde azure devops

Finalmente, validamos en AWS este cambio.

Publicación visualizada desde AWS
Consumo del api

La demo de todo lo anteriormente explicado, pueden revisarla en el siguiente video.

Espero que les sea util, si tienen alguna duda, los espero en la sección de comentarios.

Saludos.

Deja un comentario

Publicando NetCore Api en AWS Lambda y AWS API Gateway

Hola, en un par de post anteriores les explicaba como dockerizar un entorno de trabajo:

Primera parte

Segunda parte

Hoy quiero explicarles como podemos desplegar nuestro api (para este ejercicio en NetCore 3.1) en AWS Lambda, usando un AWS API Gateway para poder consumir los recursos.

Empezamos en nuestra aplicación en visual studio.

Aplicación en visual studio

Notemos que en esta aplicación hemos modificado nuestro archivo “Program.cs” y los hemos renombrado “LocalEntryPoint.cs” y además hemos añadido un archivo “LambdaEntryPoint.cs”. Sobre el primer archivo no entrare en detale por que es el tipico archivo que tenemos cuando creamos un api en netcore. Pero el segundo si nos interesa, dado que ese es el handler que usará nuestro lambda. Veamos entonces, que nos encontramos.

Contenido de LambdaEntryPoint

Este archivo hereda de “API GatewayProxyFunction”, veremos más adelante que se usa un método dentro de esta clase para el handler, por lo demás estamos usando el overload de uno de los “Init”, especificamente el que tiene como entrada un “IWebHostBuilder” que es una interfaz ya conocida. Ahora vemos que tenemos que hacer en AWS. Como nota aparte, les comento que las demos las estoy usando la web de AWS. Sin embargo, todo esto puede ser reemplazada por la configuración desde la consola para crear los recursos y conectarlos entre si. Sin embargo, pienso que las demás son más dinámicas de este modo.

CREACIÓN DEL LAMBDA

Creación del lambda

Configuramos el handler

Handler por defecto
Nuevo hanlder

El handler que vamos a usar, se configura del siguiente modo.

Exphadis.Services.WebApi::Exphadis.Services.WebApi.LambdaEntryPoint::FunctionHandlerAsync

Donde, el orden es el siguiente:

Orden de elementos para el handler
  1. La ruta del proyecto en la cual esta ubicado
  2. El namespace de nuestra clase
  3. El nombre de la clase
  4. Un método dentro de la clase de la cual estamos heredando

Subimos nuestro código fuente

Realizamos un build de nuestras fuentes y comprimimos
Seleccionamos subir .zip
Seleccionamos nuestro archivo

CREACIÓN DEL API GATEWAY

Crear nueva api
Seleccionamos api rest
Finalizamos la creación

Vamos a configurar nuestro API Gateway para que consuma los recursos de nuestro lambda.

Creamos un nuevo recurso
Configuramos el recurso como un proxy
Indicamos el ARN de nuestro lambda

Mapeamos el ARN de nuestro lambda en el api gateway y aceptamos que nuestro api gateway pueda consumir nuestro lambda.

Brindamos permisos al lambda
Realizamos el deploy del api
Ponemos el nombre del stage que deseemos
Obtenemos el url de nuestro api

En este punto, para mi caso, es necesario configurar algunas variables de entorno, dado que sin esto va a fallar nuestra ejecución. Para poder hacer esto, debemos ir a nuestro lambda, a configuración y adicionar las variables de entorno.

Configuramos variables de entorno, de ser necesario

Finalmente, vamos a realizar la prueba de uno de los siguientes endpoint que tiene nuestro api. En mi caso es un get a un “health” que me permite obtener la fecha y hora del servidor y también de la base de datos, esto con la finalidad de determinar si todo esta funcionando correctamente. El endpoint a consumir sería el siguiente.

Respuesta del endpoint

Si quisieramos ver el log de nuestro lambda, tenemos que ir a la opción “Monitor”, y luego ir a cloudwatch a ver los LogGroups que se crean cada ves que consumimos nuestro api.

Opción de monitor en nuestro lambda
Cloudwatch logs
Ejecución de nuestro lambda

Finalmente, sólo voy a comentar que a este endpoint que hemos generado en AWS API Gateway, podemos mapearlo a otros domains, para esto tenemos una opción que se llama “Custom Domain Names”, en esta tenemos que indicar cual es el domain que queremos, además nos permite mapearlo a algún certificado que hayamos solicitado previamente para que nuestra api se pueda servir sobre https.

Por otro lado, como explico un poco en el video adjunto en este post, nuestro lambda es manejado enteramente por AWS, sin embargo, nosotros podemos agregar “layers” para poder realizar ajustes sobre nuestro ambiente, en tre los cuales se pueden manejar la instalación de otras librerías en nuestro ambiente de trabajo.

Finalmente, en un siguiente post, voy a mostrar como podemos integrar este flujo en un pipeline de devops con azure devops.

Espero que esten bien, cuidense.

Saludos.

Deja un comentario

Dockerizando una aplicación Net 5.0 – Part. II

Este post es una continuación de su versión anterior, en este nos enfocaremos en como configuramos nuestro ambiente de desarrollo con docker.

Retomemos en el código del proyecto.

Código del proyecto

Vamos a modificar nuestro “launchSetting.json”, eliminemos los profiles que tenemos actualmente, creemos uno nuevo para manejar nuestra instancia en docker que tendrá la siguiente estructura.

{
  "iisSettings": {
    "windowsAuthentication": false,
    "anonymousAuthentication": true,
    "iisExpress": {
      "applicationUrl": "http://localhost:61567/",
      "sslPort": 44304
    }
  },
  "$schema": "http://json.schemastore.org/launchsettings.json",
  "profiles": {
    "Docker": {
      "commandName": "Docker",
      "launchBrowser": true,
      "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}",
      "publishAllPorts": true,
      "useSSL": true,
      "httpPort": 7550,
      "sslPort": 7551,
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development",
        "SENTRY_CONNECTION": "",
        "DATABASE_CONNECTION": "",
        "JWT_KEY": "",
        "JWT_EXPIRATION": "",
        "URL_BASE_PATH": "",
        "AWS_ACCESS_KEY": "",
        "AWS_SECRET_KEY": "",
        "EMAIL_NO_REPLY": "",
        "ASPNETCORE_URLS": "http://+:8080;https://+:443"
      }
    }
  }
}
Nueva configuración en launchSettings.json

Lo que hemos hecho es crear un nuevo perfil, le indicamos el comando que va a ejecutar “docker”, que debe abrir un navegador, la url que se va a abrir (esta como vemos se obtiene de variables, no valores en duro), un poco de dolor de cabeza me llevo descubrir el uso de “httpPort” y “sslPort”, dado que estos dos nos permiten indicarle que puertos queremos que use. Esto ultimo parece algo menor, pero nos sirve cuando mapeamos en nuestro postman o en el front el puerto del backend, y que este sea consistente en distintos ambientes, de otro modo, nos asignarán un puerto aleatorio y esto nos generar tener que estar cambiando nuestra configuración en el front. Finalmente, acá también podemos indicarle las variables de entorno que vamos a usar dentro de nuestro dockerfile.

En nuestro ide, selecionamos “Docker” y ejecutamos el proyecto. Si es la primera ves que se ejecuta, se va a crear la imagen en base a nuestro dockerfile, luego de esto, el ide automaticamente va a crear una instancia en base a esta imagen. Veamos que comando es el que usa para ejecutar este comando.

Primero se hace el build:

docker build -f "C:\Users\walbe\Development\Simplicity\simplicity-api\dockerfile-dev" --force-rm -t simplicityserviceapi:dev --target base  --label "com.microsoft.created-by=visual-studio" --label "com.microsoft.visual-studio.project-name=Simplicity.Service.Api" "C:\Users\walbe\Development\Simplicity"
Docker build

Luego realizamos se realiza el run de nuestra imagen recién creada:

docker run -dt -v "C:\Users\walbe\vsdbg\vs2017u5:/remote_debugger:rw" -v "C:\Users\walbe\Development\Simplicity\simplicity-api\Simplicity.Service.Api:/app" -v "C:\Users\walbe\Development\Simplicity:/src/" -v "C:\Users\walbe\AppData\Roaming\Microsoft\UserSecrets:/root/.microsoft/usersecrets:ro" -v "C:\Users\walbe\AppData\Roaming\ASP.NET\Https:/root/.aspnet/https:ro" -v "C:\Users\walbe\.nuget\packages\:/root/.nuget/fallbackpackages2" -v "C:\Program Files\dotnet\sdk\NuGetFallbackFolder:/root/.nuget/fallbackpackages" -e "DOTNET_USE_POLLING_FILE_WATCHER=1" -e "ASPNETCORE_LOGGING__CONSOLE__DISABLECOLORS=true" -e "ASPNETCORE_ENVIRONMENT=Development" -e "ASPNETCORE_HTTPS_PORT=7551" -e "NUGET_PACKAGES=/root/.nuget/fallbackpackages2" -e "NUGET_FALLBACK_PACKAGES=/root/.nuget/fallbackpackages;/root/.nuget/fallbackpackages2" -p 7550:8080 -p 7551:443 -P --name Simplicity.Service.Api_1 --entrypoint tail simplicityserviceapi:dev -f /dev/null
Docker run
docker run -dt 
-v "C:\Users\walbe\vsdbg\vs2017u5:/remote_debugger:rw" 
-v "C:\Users\walbe\Development\WebApplication1:/app" 
-v "C:\Users\walbe\Development\WebApplication1:/src/" 
-v "C:\Users\walbe\AppData\Roaming\Microsoft\UserSecrets:/root/.microsoft/usersecrets:ro" 
-v "C:\Users\walbe\AppData\Roaming\ASP.NET\Https:/root/.aspnet/https:ro" 
-v "C:\Users\walbe\.nuget\packages\:/root/.nuget/fallbackpackages2" 
-v "C:\Program Files\dotnet\sdk\NuGetFallbackFolder:/root/.nuget/fallbackpackages" 
-e "DOTNET_USE_POLLING_FILE_WATCHER=1" 
-e "ASPNETCORE_LOGGING__CONSOLE__DISABLECOLORS=true" 
-e "ASPNETCORE_ENVIRONMENT=Development" 
-e "ASPNETCORE_URLS=https://+:443;http://+:80" 
-e "NUGET_PACKAGES=/root/.nuget/fallbackpackages2" 
-e "NUGET_FALLBACK_PACKAGES=/root/.nuget/fallbackpackages;/root/.nuget/fallbackpackages2" 

Con esto, ya tendriamos todo funcionando correctamente, les dejo un video de todo este proceso, donde podemos ver mejor como nuestra aplicación corre desde nuestra imagen docker y los cambios que realizamos se actualizan directamente.

Espero que les sirva, en el siguiente post explicare como realizar el deploy de una aplicación en NetCore o Net 5.0 en AWS Lambda.

Deja un comentario

Dockerizando una aplicación Net 5.0 – Part. I

Hola, espero que los que me lean a esta fecha estén bien. Hace ya un tiempo que no publicaba nada, principalmente se debe a la falta de tiempo que he tenido por el trabajo y otras complicaciones que empezaron a surgir como consecuencia de la pandemia. En estos meses he aprendido algunas cosas nuevas relacionadas con AWS, Google Cloud y otras cosas que por ahí alguien puede encontrar interesante.

Retomo las publicaciones con este post, por que les comento que en general, en AWS y GCloud no es tan común que sus servicios soporten la última versión de net que nos ofrece microsoft, en mi caso tenía que migrar algunos servicios de AWS a Google Cloud esta semana que pasó y me di con la sorpresa que app engine no soporta Net 5.0. Sin embargo, soportan docker así que fue la oportunidad perfecta para practicar un poco mis skills en docker que hace un tiempito no he podido jugar con esta herramienta.

Quiero que este sea el primer post, por que en algunos de los que tengo pensado más adelante, quiero usar net 5.0, y de este modo sabrán como manejarse con esta versión del framework. Tomemos como ejemplo una aplicación que tiene especial cariño para mi porque yo la inicie como un proyecto personal hace ya un par de años y poco a poco he ido añadiendole funcionalidades y cosas. Sobre la aplicación, puedo decir que usa EF, Clean architecture y que no voy a poder compartirles el código. Sin embargo, este post es para centrarnos en docker y como implementarlo en un ambiente de desarrollo y de producción.

Dicho esto, empecemos, en nuestro proyecto base, vamos a crear un archivo que llamaremos “dockerfile-dev” y otro “dockerfile-prod”. Veamos cual es la estructura de nuestro proyecto desde visual studio y desde el explorador de windows.

Explorador de windows
Visual studio

Para este ejemplo, hemos creado los archivos de correspondientes de docker en la raiz del proyecto. haciendo un build de nuestro proyecto para ver si todo esta funcionando correctamente.

Build del proyecto

Además, es importante revisar el archivo “launchSetting.json” dentro de la carpeta “Properties” como saben este archivo te permite configurar los distintos ambientes en los que quisieras levantar tu aplicación localmente, acá manejaremos las variables de entorno que necesitemos, entre otras configuraciones.

Archivo launchSetting.json

Notemos, que por defecto el visual estudio, nos esta proporcionando ya algunos perfiles. Para nuestro caso, queremos poder levantar nuestra aplicación localmente dentro de una imagen docker. Esto último es un salvavidas en algunos casos, al empezar algún proyecto, hay un tiempo que puede ser menor o mayor para poder levantar las aplicaciones que se tengan, si estuvieran dockerizadas, sólo tendrías que clonar el proyecto, instalar docker, correr tu “docker build .” dentro de la carpeta donde estar el dockerfile y listo, puede iniciar el trabajo. Tenganlo en cuenta por si más adelante les puede servir.

Empecemos trabajando con el archivo “dockerfile-prod”. Añadamos las siguientes lineas y luego detallaremos un poco que es lo que esperamos conseguir con estas.

FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base
WORKDIR /app
EXPOSE 8080

Estamos tomando como base de nuestra futura imagen docker la imagen del repositorio oficial de microsoft para Net 5.0. el “AS base” es un alias que le ponemos en nuestro dockerfile a esta imagen. La sentencia “WORKDIR /app” le indica docker cual es nuestro directorio de trabajo y finalmente el “EXPOSE 8080” le dice que vamos a exponer el puerto 8080 dentro de nuestro container.

ENV ASPNETCORE_ENVIRONMENT=${_ASPNETCORE_ENVIRONMENT}
ENV AWS_ACCESS_KEY=${_AWS_ACCESS_KEY}
ENV AWS_SECRET_KEY=${_AWS_SECRET_KEY}
ENV DATABASE_CONNECTION=${_DATABASE_CONNECTION}
ENV EMAIL_NO_REPLY=${_EMAIL_NO_REPLY}
ENV JWT_EXPIRATION=${_JWT_EXPIRATION}
ENV JWT_KEY=${_JWT_KEY}
ENV SENTRY_CONNECTION=${_SENTRY_CONNECTION}
ENV ASPNETCORE_URLS=${_ASPNETCORE_URLS}
ENV URL_BASE_PATH=${_URL_BASE_PATH}

Esta sección es basicamente para declarar variables de entorno, podemos ver que en este caso las variables hacen referencia a otras variables, esto lo dejo asi por que si esto va a ir a producción, hay un pipeline en el que se reemplazarán estas variables por motivos de seguridad y buenas prácticas.

RUN apt-get update
RUN apt-get install -y apt-utils
RUN apt-get install -y libgdiplus
RUN apt-get install -y libc6-dev 
RUN ln -s /usr/lib/libgdiplus.so/usr/lib/gdiplus.dll

Esta parte la añado por que es necesario para esta aplicación, basicamente instala en nuestro entorno una librería necesaria para generar archivos excel que por defecto no están en linux. Sin embargo, nos sirve para ver como correr comandos dentro de una imagen docker.

FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
WORKDIR /src
COPY ./ ./Simplicity/
RUN dotnet restore Simplicity\\Simplicity.Service.Api\\Simplicity.Service.Api.csproj

Acá vamos a tomar como base para hacer el build el sdk de microsoft de 5.0, le indicarmos cual va a ser nuestro directorio de trabajo para hacer nuestro build, copiamos todo el codigo que tengamos en la carpeta en la que esta ubicada nuestro archivo dockerfile con el siguiente comando “COPY ./ ./Simplicity/” en una carpeta que vamos a crear “Simplicity”. Finalmente, en esta sección vamos a ejecutar el comando “dotnet restore” que si recuerdan la imagen de los archivos esta dentro de “Simplicity.Service.Api\\Simplicity.Service.Api.csproj” en nuestro caso, como le estamos añadiendo una carpeta más, termina siendo “Simplicity\\Simplicity.Service.Api\\Simplicity.Service.Api.csproj”.

COPY . .
WORKDIR /src/Simplicity
RUN dotnet build Simplicity.Service.Api\\Simplicity.Service.Api.csproj -c Release -o /app/build

En esta parte, nuevamente copiamos los archivos al directorio actual, le indicamos a docker cual va a ser nuestro nuevo directorio de trabajo con “/src/Simplicity” y finalmente realizamos nuestro build, recordemos en este punto que estamos haciendo un release y le añadimos que el directorio del output “-o /app/build”, consideremos que ahora no es necesario ponerle “Simplicity” al inicio por que ya lo pusimos en el workdir.

FROM build AS publish
RUN dotnet publish Simplicity.Service.Api\\Simplicity.Service.Api.csproj -c Release -o /app/publish

Ya estamos cerca de terminar, en esta parte vamos a hacer el publish con el típico comando “dotnet publish”, tambien tenemos que indicar el output. Y finalmente, ya llegamos a la última parte de la configuración de nuestro dockerfile.

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "Simplicity.Service.Api.dll"]

En este punto, indicamos nuestro directorio de trabajo, copiamos lo que tenemos en nuestra carpeta publish en una carpeta publish que vamos a crear en la carpeta “app” y con el comando “ENTRYPOINT” le indicamos a docker que comando queremos que ejecute, para nuestro caso es “dotnet” al archivo “Simplicity.Service.Api.dll”. Realicemos el build de nuestro dockerfile.

docker build -t simplicity-service --file dockerfile-prod .
Realización del build del dockerfile
Realización del build
Listado de imágenes de docker

Ahora ejecutemos esta imagen docker y probemos el resultado. Notemos en el siguiente comando que le indicamos que corra en modo deamon con el “-dt” y también es necesario que le indiquemos el mapeo de los puertos de nuestra imagen docker con la máquina en la que se está ejecutando, esto lo conseguimos con “-p 4750:8080”.

docker run -dt -p 7551:8080 cb831
Ejecutamos nuestra imagen docker
Listado de imagenes docker ejecutandose

Finalmente, ejecutemos en nuestro browser el localhost para el puerto 7551.

Ejecución de nuestra aplicación dentro de la imagen docker

Les comparto el dockerfile que hemos desarrollado completo.

FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base
WORKDIR /app
EXPOSE 8080

ENV ASPNETCORE_ENVIRONMENT=${_ASPNETCORE_ENVIRONMENT}
ENV AWS_ACCESS_KEY=${_AWS_ACCESS_KEY}
ENV AWS_SECRET_KEY=${_AWS_SECRET_KEY}
ENV DATABASE_CONNECTION=${_DATABASE_CONNECTION}
ENV EMAIL_NO_REPLY=${_EMAIL_NO_REPLY}
ENV JWT_EXPIRATION=${_JWT_EXPIRATION}
ENV JWT_KEY=${_JWT_KEY}
ENV SENTRY_CONNECTION=${_SENTRY_CONNECTION}
ENV ASPNETCORE_URLS=${_ASPNETCORE_URLS}
ENV URL_BASE_PATH=${_URL_BASE_PATH}

RUN apt-get update
RUN apt-get install -y apt-utils
RUN apt-get install -y libgdiplus
RUN apt-get install -y libc6-dev 
RUN ln -s /usr/lib/libgdiplus.so/usr/lib/gdiplus.dll

FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
WORKDIR /src
COPY ./ ./Simplicity/
RUN dotnet restore Simplicity\\Simplicity.Service.Api\\Simplicity.Service.Api.csproj

COPY . .
WORKDIR /src/Simplicity
RUN dotnet build Simplicity.Service.Api\\Simplicity.Service.Api.csproj -c Release -o /app/build

FROM build AS publish
RUN dotnet publish Simplicity.Service.Api\\Simplicity.Service.Api.csproj -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "Simplicity.Service.Api.dll"]

La parte del post, en el que explico como levantar la aplicación para nuestro ambiente de desarrollo, debuggear y realizar cambios de nuestro desarrollor que se actualicen directamente en nuestra imagen docker la realizare en un siguiente post. Espero que lo que he desarrollado hasta acá les sirva. En unos días publico la segunda parte.

Deja un comentario

Descargar Archivo en Angular

Bueno, con este post espero complementar el post anterior:

Lo hago con angular, por que cuando me toco revisarlo en el trabajo use angular para hacerlo. Pero, me parece que encontrarán algunas funciones que les pueden servir, dado que son javascript puro.

Basicamente, tenemos una aplicación creada usando el tipico:

ng new app-name

Le he instalado algunas librerias para poder tener algo más decente que un alert, otras que las podrán revisar en el código fuente que como siempre dejo disponible. La demás lógica la he realizado en el “app.component” que se crea por defecto. Revisemos que hemos creado en nuestro “app.component.html”.

Adicion de botón de descarga

Al html inicial que se genera con nuestro nuevo proyecto ded angular, estoy adicionandole un botón de carga.

app.component.ts

Primer metodo, descarga el excel
Segundo método, le da formato como archivo y lo guarda en la pc

Acá, tenemos dos métodos, uno que nos permitirá descargar el array de bytes consumiendo el servicio, y el otro que obtendrá esta información, y lo descargará a nuestra máquina. Recordemos que si queremos que nuestro navegador abra una ventana para elegir donde guardar el archivo, se debe configurar en el mismo browser, de no ser así, el navegador lo descargará automáticamente en una carpeta (de descargas probablemente).

Finalmente, veamos que tenemos en nuestro servicio.

Consumo del servicio

Como se puede ver, es necesario en el header, indicar que esperamos un tipo “blob”. Hagamos ahora una prueba.

Abrimos nuestra aplicación, podemos ver nuestro botón, lo presionamos

Recordemos que tenemos que tener el servicio corriendo en el mismo puerto en el que se está poniendo en el servicio.

Petición al servicio.

Sale la petición a nuestro servicio

La petición, llega al endpoint.

Llega nuestra petición al servicio

Podemos ver acá la respuesta que estamos recibiendo de nuestro servicio.

Se descarga el archivo.

Descarga final del archivo

Bueno, así finalizamos esta serie de post de generar un archivo excel y descargarlo desde una web en angular. Espero que les sirva, dejo el video con la explicación y el codigo fuente en github por si les interesa.

REPO: GITHUB

Saludos.

6 comentarios

Crear Excel con Apache POI y Spring Boot

Bueno, tuve que dejar un par de semanas de hacer post por que me acumule de trabajo y quería tocar este tema por que es algo que me parece muy útil para quien le toque ver temas referidos a la generación de excel con Spring boot o en general con java. Y para serles franco el código en este post no va a ser pequeño, por lo que hacer una demo que yo considero útil para varios posibles escenarios con los que se van a encontrar en algún trabajo y proyecto me tomo unas cuantas horas por lo que me demoro el desarrollo del post.

Espero que lo que revisemos y el código y video que deje les sea de provecho por si llegan a tener algún escenario como el que voy a plantear para iniciar con la explicación.

Escenario

Digamos que en su trabajo/freelo/proyecto personal, quieres realizar la generación de un excel. El modo más sencillo sería empezar a definir las columnas que va a tener y así ir dándole la forma al excel. Sin embargo, si queremos que este sea dinámico. Digamos que necesitamos un componente al cual le pasamos una estructura de datos y este nos genera un excel formateado y demás. Claro que el nivel de customización dependen de lo que se busque, pero un caso similiar tuve hace unos días en el trabajo y fue divertido por que me permitió aprender más sobre la librería en la cual me baso para esta demo, Apache POI, es de seguro ya conocida por mucho en el mundo de Java, teniendo en mente lo que acabo de plantear, veamos como esta distribuida nuestra demo.

Distribución del Proyecto

Distribución del Proyecto
Distribución del Proyecto

Como pueden ver en la imagen, tenemos las siguientes capas:

  • Controller: Exponemos nuestro endpoint
  • Model: Nos permitirá manejar de manera genérica la información de nuestro excel.
  • Service: Ejecutamos la lógica para la generación del excel.
  • Transversal: Son funciones que podremos usar a lo largo de la aplicación.
  • Util: He creado varias funciones que nos permitán manejar mejor ciertos casos que se pueden presentar en la generación de un excel (formato de decimales, fechas, etc).
  • Resources; Parte propia de spring, acá de ubica el yml de configuración. Sin embargo, también estoy poniendo el json del cual vamos a partir para generar el excel.

Revisemos ahora que tenemos en cada uno de los componentes:

Controller

End-point para obtener el reporte
End-point para obtener el reporte

Como pueden ver, estamos devolviendo a la petición GET un objeto del tipo Resource. Esto lo hacemos por que nuestro end-point retorna un array de bytes e indicamos en el header el formato del excel, en el key “Content-Disposition”, además, de indicarlo en el content-type del response. En el siguiente post voy a explicar como se descargaría este excel desde una aplicación de angular sencilla.

Model

Información de las columnas
Información de las columnas
Información de las filas
Información de las filas

Sólo usaremos estas dos clases para manejar toda la información que queramos renderizar en nuestro excel, cuando revisemos las otras capas veremos como dinamicamente vamos generando el excel. Notese, que “Field” me permite mapear los campos que obtengo de base, por su nombre, descreipción y tipo de dato que como vemos es un enum. Estoy usando lombok para no llenarme de codigo de inicialización de las clases y demás.

Service

Método que se consume desde el controller
Método que se consume desde el controller

Este método es el que nos retorna el objeto Resource y por lo tanto es el se encarga de llamar a los otros métodos que nos permiten generar el excel. Antes de explicar que hace cada uno de estos métodos, comentar que “data” y “columns” básicamente están leyendo los json que tenemos en resources y los están parseando a un objeto con un método creado en utils que me permite hacer esto. Veamos que hacen los otros métodos:

generateFieldsReport:

Creación de la lista de Field
Creación de la lista de Field

Recorremos la lista de información de columnas para generar una lista de fields. Acá estamos usando el enum para determinar de que tipo de cada es cada columna de nuestro excel. Esto lo usaremos para el formato en un paso posterior.

buildCellTypes:

Obtenemos todos los tipos de datos que tendrá nuestro excel
Obtenemos todos los tipos de datos que tendrá nuestro excel

Obtenemos todos los tipos de datos que tendremos en nuestro excel.

buildDataLines:

Generamos un listado de array de string que es la información que ira en nuestro excel
Generamos un listado de array de string que es la información que ira en nuestro excel

En este método basicamente estamos obteniendo la información que va a llenar nuestro reporte de excel, en base al tipo de datos que ya obtuvimos estamos dándole el formato al cuerpo del excel en cada una de las columnas. Como se puede ver, estamos recibiendo la lista de maps que obtuvimos de la base de datos (nuestro json para nosotros).

createXlsxFileAsByteArray:

Generación del excel parte 1
Generación del excel parte 1
Generación del excel parte 2
Generación del excel parte 2

Finalmente, en esta capa, el método en el que ya trabajamos con Apache POI, acá generaremos el excel y lo convertiremos a un array de bytes. Empezamos creando un objeto del tipo “SXSSFWorkbook”, dentro de este definimos un “Sheet” que es la hoja sobre la que trabajaremos, le ponemos el nombre que queramos. Las siguientes lineas me sirven para darle el formato de fecha a los campos que correspondan, lo defino a este nivel por que si se van creando conforme se recorren las filas de excel, se genera una excepción si nuestro excel tiene mas de 10 mil filas, dado que si definimos más de 10 mil objetos del tipo “CellStyle” en un mismo excel apache poi lanza una excepción. Empezamos a recorrer nuestro “dataLines” que si mal no recuerdan es donde se pusimos todo el contenido de nuestro excel y uno a uno vamos a hacer validaciones sobre cada fila, en la primera iteración no hacemos mayores validaciones por que es la cabecera solo seteamos esta cabecera y le damos cierto formato, a partir de aqui si nos vamos a valer de un switch por el tipo de datos para darle el formato de corresponda. Para finalizar, escribimos toda la información de nuestro workbook en nuestro objecto “ByteArrayOutputStream” y retornamos nuestro método con un “toByteArray()”.

Ya en este punto, tenemos el bytes de array para devolver en el end-point. Las demás funciones utilitarias que se usan en el proyecto (leer el json, formatear decimales, fechas, etc) no las voy a explicar en el post por que sería extenderlo más y de por si ya a quedado muy amplio.

REPO GITHUB

Saludos

Deja un comentario

Spring Boot con Docker

Bueno, cada ves es más frecuente que los proyectos tengan todo un pipeline de devops para sus releases. He incluso, que en los lugares donde trabajan o en los proyectos que desarrollen tengan integrado ambientes con docker en los mismos proyectos. De manera tal, que puedan tener un ambiente de trabajo en el que se tenga la certeza que no se tiene algún factor adicional y que todos están trabajando en el mismo ambiente.

Tal ves resulte un poco dificil de comprender por que es esto importante. Pero creanme cuando les digo que puede ser un dolor de cabeza levantar algunas aplicaciones. En este post quería mostrar como podemos configurar nuestro ambiente de desarrollo con spring boot usando devtools.

Pensando en esto, es que me plantee la pregunta como integrar spring boot con docker. La meta de este post sería tener un proyecto web api con spring boot que que nos permita tener un ambiente configurado y desplegado en nuestro container. Y que al hacer cambios en nuestro proyecto, podamos ver los cambios en nuestra imagen docker sólo con la realización de un build.

Empezamos usando spring initializer para generar un proyecto base, sólo le pondremos las siguientes dependencias:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

Creamos un controlador en nuestro proyecto, sólo la demo.

Controlador para probar la actualización

Indicamos el puerto en el que queremos que nuestra aplicación funcione, esto lo hacemos poniendo en el “application.yml” la siguiente propiedad:

server:
  port: 8055

Sólo añadiendo la dependencia de devtools, tenemos acceso a ciertas funcionalidades en nuestra aplicación. Como por ejemplo, que nuestra aplicación mumestre los cambios que realizamos sin necesidad des volver a parar el debugging e iniciarlo, es algo parecido al live-realoading que tenemos con webpack. Provemos esto, y luego nos metemos de lleno a la configuración de con docker.

Probando el api inicialmente
Build al cambio que hemos realizado
Probamos el api nuevamente, visualizamos el cambio

Acá viene la magia de spring, para poder dockerizar nuestra aplicación sólo debemos de ejecutar un comando en la carpeta raiz de nuestro proyecto.

mvnw spring-boot:build-image

Esto, nos generará una imagen docker en nuestro equipo (obviamente antes debemos de tener instalado docker) con el nombre que hemos definido para nuestro proyecto. Para la demo sería del modo siguiente:

Nombre proyecto

De modo, que si ejecutamos el comando “docker ps”, tendríamos un resultado similar a este:

Imagen creada

Es posible que la primera ves que ejecutan este proceso tome un poco de tiempo, dado que esta bajando la imagen base por primera ves.

Para poder correr nuestro proyecto en la imagen docker, tenemos que ejecutar el siguiente comando.

docker run --tty --publish 8055:8055 demo:0.0.1-SNAPSHOT

Hasta este punto, tenemos nuestro proyecto dockerizado. Pero que pasa con el live reloading? Bueno, nos toca realizar algunos pasos adicionales. Primero, en nuestro archivo de configuración yml debemos añadir la siguiente configuración.

spring:
  devtools:
    remote:
      secret: wgutierrez

Luego de esto, debemos de añadir una configuración adiciona en nuestro POM, veamos como estaba antes, y como debería de quedar.

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

Tenemos que añadir una configuracion al plugin para que no excluya dev tools.

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <!--PARA PODER ACTUALIZAR EL PUBLICADO SOLO COMPILANDO-->
                <configuration>
                    <excludeDevtools>false</excludeDevtools>
                </configuration>
            </plugin>
        </plugins>
    </build>

Y finalmente, debemos añadir una nueva configuración para nuestro run/debug.

Configuración del run/debug

Es importante, mencionar que la clase:

org.springframework.boot.devtools.RemoteSpringApplication

La usamos por que nos permite tener un filewatcher, que detacta los cambios en nuestro proyecto y los replica en nuestro container. Además, tenemos también que indicar la url con el puerto que hemos configurado en nuestro proyecto.

De este modo, me parece que tenemos el panorama más claro sobre como podemos tener un proyecto spring boot con docker. Dejo el link y el repo del video.

REPO GITHUB

Saludos.

Deja un comentario

SQS en AWS con Spring Boot – Parte II

En el post pasado, revisamos que es una cola cuando hablamos de desarrollo, en que casos nos puede servir y que alternativas encontramos en el mercado para poder usarlas. Puntualmente, hicimos una demo de el uso de esta herramientas con SQS que es un servicio ofrecido por AWS. Sin embargo, buscando no extender el post y la demo demasiado conforme iba avanzando el post anterior, decidí que era mejor dividirlo en dos partes. En la primera, revisamos como crear desde AWS un usuario con los permisos necesarios para conectarse a una cola, en código con un proyecto hecho con spring boot conectarnos a esta y poder enviar mensajes.

En esta segunda parte, revisaremos como podemos suscribirnos a una cola, y estar a la escucha de nuevos mensajes.

Para poder conseguir esto, y no estar llenandonos de proyectos voy a usar el mismo proyecto en spring boot de la ves pasada, con la diferencia que creare algunos nuevos componentes, la idea basicamente es consumir una api desde un controlador, esta encola el mensaje y en un listener recibirlo e imprimirlo en consola.

Veamos que hemos añadido en nuestro proyecto inicial.

POM

Nuevas dependencias en el POM
Nuevas dependencias en el POM

JmsConfiguration

Clase de configuración del JMS
Clase de configuración del JMS

SqsListener

Clases del SqsListener
Clases del SqsListener

Con lo añadido en nuestro POM, tenemos acceso a clases de configuración que nos permitirán configurar un listener para las colas que ya tenemos definida en SQS. Si vemos a detalle la clase de configuración que hemos creado, tenemos lo siguiente:

Configuración en el JmsListener
Configuración en el JmsListener

En esta clase, como podemos ver, estamos creando un factory para las conexiones a AWS esto en el método “sqsConnectionFactory” y adicionalmente, configuramos nuestro listener factory para estar a la escucha de nuestra cola definida en AWS.

Como ven, tenemos que indicar que esta clase es de configuración con @Configuration, además de indicarse que habilite el JMS con @EnableJms. Sólo para aclarar por si hubiera alguna duda, JMS son las siglas de Java Message Service que es una clase definida en la librería JMS que nos permite realizar la creación y suscripción de mensajes a través de una cola.

Y finalmente, tenemos que crear nuestro listener, esto lo hacemos del siguiente modo:

SqsListener
SqsListener

Creamos una clase y dentro creamos un método void con las siguientes anotaciones “@JmsListeners” que tiene dentro un “@JmsListener”. Es necesario dentro de esta última anotación indicarle cual es el nombre de la cola que vamos a estar a la escucha en nuestro listener.

Hagamos una prueba de todo junto.

Primero realizamos el consumo de nuestro controlador con una mensaje cualquiera.

Consumiendo el API
Consumiendo el API

Recibimos el mensaje en nuestro controlador.

Recibiendo el mensaje en el controlador
Recibiendo el mensaje en el controlador

El mensaje es enviado a nuestra cola previamente configurada en SQS.

Se genera el mensaje correctamente
Se genera el mensaje correctamente

Mensaje encolado en SQS.

Mensaje en SQS
Mensaje en SQS

Ahora, segundos despues de que se encoló el mensaje, debería llegar a nuestro listener que esta escuchando la misma cola “SqsTest”.

Recibimos el mensaje
Recibimos el mensaje
Log del mensaje
Log del mensaje

Con esta prueba concluye este post. El código lo subire como una actualización al proyecto anterior. Espero que les sirva, como ven es sencillos tener un manejo de colas funcional con sólo un poco de código. Dejo el link del repo y el video.

REPO GITHUB

Saludos.

2 comentarios

SQS (Manejo de Colas) en AWS con Spring Boot – Parte I

Hola, hace ya algún tiempo desde el último post, la verdad es que la cuarentena nos a permitido a todos tener más tiempo con la familia, pero también a los que nos dedicamos al desarrollo de software nos hemos cargado de trabajo. En mi caso, especialmente por que mi familia tiene un colegio y he tenido que asumir la migración a la enseñanza virtual.

Bueno, vamos al tema que nos reclama este post, en el proyecto en el que me encuentro trabajando he tenido la oportunidad de meterme a bucear por distintos servicios de AWS que estamos usando en la arquitectura. Este proceso a sido desafiante y de mucho aprendizaje, y quiero plasmar en una serie de posts mi exploración a los servicios de AWS. Empezando por el servicio de SQS que es basicamente un encolador de mensajes, sólo que es la alternativa que nos ofrece AWS. Deben ya de conocer algún servicio similar a estos, que yo recuerde ahora uno con el que hice una pruebas era RabbitMQ, que era un software open source que tenías que instalar en tu pc o en el servidor en el que lo quisieras usar y te permitia generar las colas y suscribirte a ellas, y realizaba el manejo de los mensajes que ibas encolando.

Me parece importante detallar un poco bajo que condiciones es necesario usar este servicio. Usualmente, cuando tenemos varios microservicios (puede entendeser como varias APIs), entrar a detallar de las caraterísticas que la hacen un microservicio va a extender mucho el post así que dejemoslo ahí. Pero cuando queremos que estos se comuniquen, podemos directamente hacer una solicitud http entre ellas y de ese modo la solicitud sería instantanea. Sin embargo, que sucede si son miles de peticiones, si alguna se cae o si se satura el microservicio que estamos consumiendo, como tener un control en la llegada de estas peticiones.

Teniendo en cuenta esto, es que entran a tallar soluciones de encolación de mensajes. Dado que nos permiten enviar mensajes (peticiones) a una cola que tengamos definido (inclusive definirla en tiempo de ejecución) y luego el suscriptor a esa cola va a ir desencolando los mensajes y procesandolos uno a uno. Del modo más básico esto es lo que se consigue usando este tipo de herramientas y amazon nos provee este servicio con SQS.

Entonces, ya teniendo claro para que podemos usarlo, veamos como se implementó. Primero debemos de tener una cuenta de AWS creada, en este punto quiero aclarar que si bien todos los recursos que voy a crear puede ser hechos desde un json y luego deployados con cloudformation en AWS o desde el AWSCLI, yo voy a usar la web de amazon por que me parece que es la forma más simple de hacerlo y a propositos de esta demo me sirve más. Con la cuenta que debemos tener, es necesario ir a IAM y crear un usuario que tenga asignado el siguiente rol:

Creacion de usuario
Indicar permisos

Luego no olviden copiar el access-key y secret-key que nos va a brindar IAM. Ahora veamos el proyecto en spring que hemos creado.

Usaremos las siguiente dependencias, en el POM:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-aws-messaging</artifactId>
            <version>2.0.1.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
        </dependency>
Estructura del proyecto

He dividido de módo básico en capas el proyecto, quedando del siguiente modo:

  • Controller: Y sabemos para que es esta capa.
  • Service: Para ejecutar la lógica que necesitemos.
  • Configuration: Para realizar la configuracion inicial de aws.
  • Infraestructure: Acá ejectuamos las acciones en las librerias externas que usemos.

Nuestro controlador, es un endpoint que recibe una path variable que sera lo que encolaremos.

Controller

En nuestro service, llamamos a nuestra capa infraestructure para pasarle nuestro mensaje y que realice en encolado, estamos devolviendo un objeto anonimo, que indica si se registro correctamente, el mensaje que se a encolado y el uuid que nos retorna aws cuando generamos el encolado.Veamos que hace el método marcado.

Service

Como pueden ver, preparamos el objeto que vamos a encolar, estamos usando un ObjectMapper para convertir el objeto que recibimos (en nuestro caso un string) a un formato json y esto encolarlo. En este punto, también indicamos cual es la URL a la cual se dedbe encolar, la cual obtuvimos en el service por injección de dependencia directamente desde nuestro archivo yml.

Infraestructure

Revisemos que configuración tenemos en nuestro “AwsConfiguration”. Como pueden ver, por injección de dependencia obtenemos el accesskey y secretkey que hemos generado al iniciar todo y los seteamos en el “BasicAWSCredentials” que luego usamos para generar un objeto del tipo “AmazonSQS”, que es el que nos permite encolar la información entre otras cosas.

AwsConfiguration

Mientras realizaba el desarrollo del post, me di cuenta que hacer el listener de esta cola en este post iba a hacer que sea demasiado extenso. Por lo que termine decidiendo que en el próximo entrare a cubrir este detalle. Espero que les sriva y si tienen alguna duda pueden contactarme por alguno de los medios que he dejado, ver el video o revisar la documentacion oficial de AWS que es muy buena y completa.

Documentación Oficial

REPO GITHUB

Saludos

Deja un comentario

CI and CD con VSTS

Hola, aún en cuarentena aprovecho para hacer un breve post sobre integración y deploy continuo. Como deben de haber notado, luego de todo el proceso de desarrollo que hemos cubierto en algunos post que he realizado antes y con distintas tecnologías, siempre terminamos la demo realizando una prueba de nuestra aplicación mientras esta en debugging. Sin embargo, en un escenario real sabemos que esto es inviable. Dado, que necesitamos que nuestra aplicación este desplegada en un ambiente productivo. Cuando uno inicia en el mundo del desarrollo, probablemente no es quien se encargue de hacer los pases a producción y es muy probable dependiendo en que empresas estés trabajando, que este pase sea un simple “copiar y pegar” de una carpeta a otra manualmente, o que tengan una solución automatizada para llevar esto a cabo.

Esta última es la que me encargaré de desarrollar en este post. Yo he podido vivir ambos tipos de despliegue, siendo el más gratificante los últimos por que puedes ver que si bien es cierto la configuración inicial requiere más trabajo, además de tener ciertos conceptos claros, te permite automatizar los despliegues futuros eliminando el factor humado.

Primero, tengamos claro que para conseguir un proceso de CI y CD tenemos distintos stacks de tecnologías, entre las más grandes podríamos mencionar a azure, aws, jenkins, etc. Y también podemos encontrar variantes entre las que son libres y las que te ofrecen una serie de servicios adicionales como escalamiento y demás. Para la demo que voy a realizar, he decidido optar por hacerla usando VSTS (Visual Studio Team Services) que es basicamente un servicio ofrecido por microsoft que nos permite mapear todos los procesos del desarrollo de software, desde la gestión que se debe realizar hasta el despliegue. La estoy usando por sobre otras, por que es gratuita con sus limitaciones, y es accesible sólo con tener una cuenta de microsoft.

Mi idea, es generar un proyecto MVC con net core de 0, mapearlo a un repositorio como puede ser github, luego vincular nuestro repositorio a nuestro pipeline en VSTS y desde ahí configurar los archivos YAML indicandole los pasos que debe seguir. Para el hosting de nuestra aplicación demo, no estoy usando ninguna de las alternativas que ofrece Azure, por que quise hacerlo en un hosting propio en el que tengo una cuenta así que por medio de una tarea FTP, paso los archivos.

Primero, debo comentar que cuando queremos generar un proyecto con netcore desde la consola y no queremos usar la última versión que tengamos instalada, en cambio queremos indicarle una es necesario crear en la carpeta una archivo “global.json” indicandole esto al cli. Empecemos poniendo los comando que debemos usar para crear nuestra aplicación demo:

$ mkdir demo-mvc-ci-cd # creamos la carpeta donde alojaremos nuestra aplicación
$ cd demo-mvc-ci-cd # ingresamos a la carpeta
$ echo > global.json

En el archivo que hemos creado en necesario ingresar un json con la siguiente estructura.

{
    "sdk": {
      "version": "2.2.401"
    }
}

En el cual, como ven indicamos la versión del sdk queremos que se use. Una ves creado esto, podemos crear nuestra aplicación netcore.

$ dotnet new mvc # nuestra aplicación tendrá el mismo nombre de la carpeta
$ dotnet restore

Hasta aquí, tenemos nuestra aplicación en netcore creada, ahora inicializemos nuestra repositorio con git, previamente en mi caso he creado un repositorio en github para almacenar ahí nuestra aplicación.

$ git init
$ git add .
$ git commit -m "hola"
$ git remote add origin https://github.com/walberth/demo-mvc-ci-cd.git
$ git remote -v
$ git push origin master
$ git checkout -B "master" "origin/master"

Ya con nuestro código en github, es momento de ir a VSTS, les comento que con sólo tener una cuenta en microsoft (outlook, live, etc) ya tienes acceso a este servicio. Al inciar debemos crear un proyecto si es que aún no lo tenemos, en mi caso es un proyecto que he llamado demo.

Proyectos VSTS

En nuestro proyecto demo, podemos hacerle seguimiento a todos los procesos de desarrollo de software de nuestra aplicación. Sin embargo, nosotros vamos a centrarnos sólo en la integración y el deploy continuo. Vamos a la siguiente opción.

Opción del menú

Creamos un nuevo pipeline, y nos preguntará donde está nuestro código, en mi caso mi repositorio está en github así que le indicaré que lo obtenga de ahí señalándole el nombre del repositorio. Luego de esto nos pide que elijamos el tipo de tecnología que deseamos compilar, en nuestro caso elegimos net core y nos carga la siguiente plantilla:

Plantilla YAML

Acá debemos realizar varios cambios, veamos uno a los lo que debemos poner.

steps:
- task: DotNetCoreCLI@2
  inputs:
    command: 'restore'
    projects: '**/*.csproj'
    feedsToUse: 'select'

- task: DotNetCoreCLI@2
  inputs:
    command: 'build'
    projects: '**/*.csproj'
    arguments: '--configuration $(buildConfiguration)'

- task: DotNetCoreCLI@2
  inputs:
    command: 'publish'
    publishWebProjects: true
    arguments: '--configuration $(buildConfiguration) --output $(Build.ArtifactStagingDirectory)'
    zipAfterPublish: false

- task: PublishBuildArtifacts@1
  inputs:
    PathtoPublish: '$(Build.ArtifactStagingDirectory)'
    ArtifactName: 'drop'
    publishLocation: 'Container'

En steps, debemos añadir tareas de netcore, la primera es para restaurar los nuggets que necesitemos, la segunda es realizar un build de nuestro código indicando que es en release, la tercera es realizar la publicación de nuestro compilado con netcore, dejándolo en “$(Build.ArtifactStagingDirectory)” que es una variable de entorno de VSTS y finalmente, la última tarea es para copiar ese compilado a la carpeta final en la que la tendremos lista para poder obtenerla desde nuestra tarea de despliegue continuo. Grabamos todos los cambios realizados.

Grabamos cambios

Cuando empieza a correr, tenemos la siguiente vista.

Actividad en la cola
Realiza las actividades

Como podemos ver en la última imagen, el proceso se a llevado a cabo de-acuerdo a lo que le hemos indicado, ha realizado todos los pasos y al final a desplegado el artefacto donde le indicamos que lo haga. Ahora vamos a ver como realizar el despliegue continuo.

Para esto, vamos a la opción de releases y creamos uno nuevo.

Creando un nuevo release

Seleccionamos el artefacto que vamos a usar y se lo indicamos al release.

Luego de añadirlo, es necesario que le indiquemos los pasos que vamos a usar acá tenemos un sin fin de opciones y de las más variadas, en mi caso simplemente realizaré una tarea FTP para que se conecte al site donde debe dejar el compilado. Si intentamos entrar a este site ahora tendremos la siguiente respuesta.

Respuesta del site antes de publicar

Agregamos nuestra tarea FTP.

Tarea FTP
Información FTP
Grabamos nuestro release
Realizamos el deploy
Realiza el proceso de despliegue

Si en este punto, validamos nuestra web, debería de tener nuestra aplicación ya desplegada.

Aplicación

Finalmente, hagamos un cambio en la aplicación, luego realizar un commit y push a master y luego de unos minutos deberíamos de tener nuestra despliegue realizado.

Cambio en la app

Luego de unos minutos que todo nuestro proceso automatizado se lleva a cabo, vemos nuestra web nuevamente y el cambio esta reflejado.

Cambio reflejado

Bueno, con esto concluyo el post que al final no salio tan chico. Espero que les sea de utilidad, igual pueden dejarme algún comentario o comunicarse conmigo por alguna de las redes sociales. No dejo link de github en este caso por que esta demo no iba enfocada en código.

Saludos.

1 comentario