Close

Implementar ImageLabeller con GitLab

Foto de Warren Marusiak
Warren Marusiak

Divulgador técnico sénior

Para demostrar cómo desarrollar, implementar y gestionar aplicaciones con Jira Software y varias herramientas conectadas, nuestro equipo creó ImageLabeller, una sencilla aplicación de demostración creada con AWS que utiliza el aprendizaje automático para aplicar etiquetas a las imágenes.

Esta página explica cómo implementar ImageLabeller con GitLab. Antes de empezar, te recomendamos que leas las páginas de arquitectura de ImageLabeller y de configuración de AWS SageMaker para ver el contexto.

Requisitos previos

Si aún no tienes una organización de GitHub, sigue los pasos de esta guía de GitLab para crear una desde cero.

Repositorios de GitHub orientados al público con código ImageLabeller

https://github.com/AtlassianOpenDevOpsGuides

Vídeo de demostración de la integración de Jira con GitLab

Integrar Jira y GitLab

En Jira, haz clic en Tablero, Aplicaciones y GitLab.

Captura de pantalla del menú desplegable de Jira Software para ir a GitLab

Haz clic en Obtener ahora.

Modal de la aplicación GitLab en Jira Software

Haz clic en Aplicaciones y, a continuación, en Gestionar tus aplicaciones.

Modal de la aplicación GitLab en Jira Software con menú desplegable

Expande GitLab for Jira.

Expande GitLab cuando estés en la pantalla de gestión de aplicaciones de Jira Software

Haz clic en Añadir espacio de nombres.

Pantalla para añadir un espacio de nombres a la configuración de GitLab en Jira Software

Selecciona tu espacio de nombres actual y haz clic en Vincular. Esta guía supone que ya tienes una cuenta de GitLab y un grupo de GitLab.

Enlazar un espacio de nombres de GitLab en Jira Software

Añadir la clave SSH a GitLab

Haz clic en el icono de tu perfil en la esquina superior derecha y haz clic en Preferencias.

Navegar a las preferencias mediante el menú desplegable de GitLab

Haz clic en Claves SSH y sigue las instrucciones para generar una nueva clave SSH o utilizar una clave SSH existente.

Crear un repositorio para la infraestructura de AWS S3

Un ciclo de desarrollo estándar normalmente hace que un desarrollador retome una tarea de Jira, la mueva al trabajo en curso y, a continuación, realice el trabajo de desarrollo. El identificador de la incidencia de Jira es la clave que vincula el trabajo de desarrollo con la incidencia de Jira. Es el componente principal de integración entre los dos sistemas.

Ve a Jira y crea una nueva incidencia para añadir un repositorio de infraestructura de AWS S3 a GitLab. Toma nota del identificador de la incidencia. En este ejemplo, es IM-5.

Crear una nueva incidencia para tu tablero en Jira Software

Ve a GitLab y haz clic en New project (Nuevo proyecto).

Navegar para crear un "Nuevo proyecto" en GitLab

Haz clic en Create blank project (Crear proyecto en blanco).

Crear un nuevo proyecto en GitLab

Añade un Project name (Nombre de proyecto) y elige el grupo adecuado en Project URL (URL del proyecto). Haz clic en Create project (Crear proyecto) para continuar.

Crear un nuevo proyecto: pantalla detallada en GitLab

En tu terminal, ve al repositorio de s3_infra y ejecuta lo siguiente para enviar tu archivo AWS CloudFormation template.yml a GitLab.

git add --all
git commit -m "IM-5 add s3_infra repository to gitlab"
git remote add origin git@gitlab.com:pmmquickstartguides/s3_infra.git
git branch -m mainline
git push -u origin mainline

Añadir la clave de acceso de AWS

Haz clic en Settings (Configuración) y, a continuación, en CI/CD. Desplázate hacia abajo y expande Variables. Haz clic en Add variable (Añadir variable).

Página de configuración de CI/CD en GitLab

Crea dos variables. Una para el identificador de la clave de acceso de AWS y otra para la clave de acceso secreta de AWS.

Modal "añadir una variable" para añadir tus claves de AWS en GitLab

Protege las variables para que solo las usen las canalizaciones que se ejecutan en las ramas protegidas y en las etiquetas. Da acceso de administrador al usuario de IAM asociado a la clave de acceso de AWS. También puedes optar por utilizar un control de acceso más detallado eligiendo políticas de acceso de AWS individuales.

Las claves de AWS figuran en la" sección" Variables de la página de configuración de CI/CD de GitLab

Configurar las ramas protegidas para acceder a las variables protegidas

Haz clic en Settings (Configuración) y, a continuación, en Repository (Repositorio). Desplázate hacia abajo y expande Protected Branches (Ramas protegidas).

Introduce el prefijo del identificador de tu incidencia de Jira y un *.

Los identificadores de incidencias de Jira son como IM-5 e IM-6 en este ejemplo; el prefijo es IM-.

Introduce IM-* y haz clic en Protect (Proteger).

Configurar ramas protegidas en GitLab

Verás la rama principal e IM-* como ramas protegidas.

Configurar entornos de implementación

Haz clic en Deployments (Implementaciones) y, luego, en Environments (Entornos). Haz clic en New environment (Nuevo entorno) para añadir nuevos entornos. Hay un entorno de pruebas en US-WEST-1 y US-EAST-2 y entornos de producción en US-WEST-2, US-EAST-1 y CA-CENTRAL-1 en este ejemplo.

Configurar entornos de implementación en GitLab

.gitlab-ci.yml para implementar en AWS

Ve al repositorio de s3_infra de tu terminal y crea una rama con el nombre de tu identificador de incidencia de Jira.

git checkout -b IM-5

Crea un archivo .gitlab-ci.yml con el siguiente yaml. Esto define un flujo de trabajo de implementación para tus entornos de pruebas, ensayo y producción.

stages:
  - merge-request
  - test-us-west-1
  - test-us-east-2
  - production-us-west-2
  - production-us-east-1
  - production-ca-central-1

merge-request-pipeline-job:
  stage: merge-request
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
  script:
    - echo "This pipeline always succeeds and enables merges during merge requests"
    - echo true

deploy-test-us-west-1:
  stage: test-us-west-1
  environment: test-us-west-1
  rules:
    - if: $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE != "merge_request_event"
  image: registry.gitlab.com/gitlab-org/cloud-deploy/aws-base:latest
  script:
    - export AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID
    - export AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY
    - aws cloudformation deploy --region us-west-1 --template-file template.yml --stack-name OpenDevOpsS3Infra

deploy-test-us-east-2:
  stage: test-us-east-2
  environment: test-us-east-2
  rules:
    - if: $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE != "merge_request_event"
  image: registry.gitlab.com/gitlab-org/cloud-deploy/aws-base:latest
  script:
    - export AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID
    - export AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY
    - aws cloudformation deploy --region us-east-2 --template-file template.yml --stack-name OpenDevOpsS3Infra

deploy-production-us-west-2:
  stage: production-us-west-2
  environment: production-us-west-2
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE != "merge_request_event"
  image: registry.gitlab.com/gitlab-org/cloud-deploy/aws-base:latest
  script:
    - export AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID
    - export AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY
    - aws cloudformation deploy --region us-west-2 --template-file template.yml --stack-name OpenDevOpsS3Infra

deploy-production-us-east-1:
  stage: production-us-east-1
  environment: production-us-east-1
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE != "merge_request_event"
  image: registry.gitlab.com/gitlab-org/cloud-deploy/aws-base:latest
  script:
    - export AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID
    - export AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY
    - aws cloudformation deploy --region us-east-1 --template-file template.yml --stack-name OpenDevOpsS3Infra

deploy-production-ca-central-1:
  stage: production-ca-central-1
  environment: production-ca-central-1
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE != "merge_request_event"
  image: registry.gitlab.com/gitlab-org/cloud-deploy/aws-base:latest
  script:
    - export AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID
    - export AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY
    - aws cloudformation deploy --region ca-central-1 --template-file template.yml --stack-name OpenDevOpsS3Infra

Entender un archivo .gitlab-ci.yml

Fases

Añade un bloque de etapas para definir el orden de ejecución de tu canalización de GitLab. Las etapas se ejecutan en el orden en que se definen en el bloque de etapas. Los trabajos asociados a una etapa se ejecutan en paralelo.

stages:
  - merge-request
  - test-us-west-1
  - test-us-east-2
  - production-us-west-2
  - production-us-east-1
  - production-ca-central-1

Empleo

Los trabajos están asociados a una etapa y pueden asociarse a un entorno. Las reglas controlan si una tarea en particular se ejecutará o no. La regla de este ejemplo comprueba si la rama de canalización no es la rama predeterminada y si la canalización no se ejecuta automáticamente como parte de una solicitud de fusión.

Puedes especificar una imagen diferente para cada trabajo. Puedes configurar las imágenes con las herramientas necesarias para tus scripts de trabajo. La sección de script define el conjunto de pasos que se ejecutan cuando se ejecuta la tarea.

deploy-test-us-west-1:
  stage: test-us-west-1
  environment: test-us-west-1
  rules:
    - if: $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE != "merge_request_event"
  image: registry.gitlab.com/gitlab-org/cloud-deploy/aws-base:latest
  script:
    - export AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID
    - export AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY
    - aws cloudformation deploy --region us-west-1 --template-file template.yml --stack-name OpenDevOpsS3Infra

Canalización de solicitudes de fusión

GitLab ejecuta automáticamente una canalización cuando se aprueba una solicitud de fusión. Puedes crear un trabajo para esta canalización añadiendo una regla. El trabajo siempre se completa con éxito en este ejemplo.

merge-request-pipeline-job:
  stage: merge-request
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
  script:
    - echo "This pipeline always succeeds and enables merges during merge requests"
    - echo true

Enviar a una rama de funciones

Ejecuta lo siguiente desde la línea de comandos para enviar los cambios a la rama IM-5 del repositorio de s3_infra. Incluye el identificador de incidencia de Jira en los mensajes de confirmación y los nombres de las ramas para que la integración de Jira con GitLab pueda hacer un seguimiento de lo que ocurre en tu proyecto.

git add --all
git commit -m "IM-5 add .gitlab-ci.yml to s3_infra"
git push -u origin IM-5

Haz clic en CI/CD y, a continuación, en Pipelines (Canalizaciones) para ver cómo se ejecuta la canalización.

Pantalla de canalizaciones de CI/CD en GitLab

Haz clic en el identificador de la canalización en ejecución.

ID de canalización de la canalización en ejecución en GitLab

Haz clic en un trabajo para ver más detalles.

Pantalla de tarea detallada para ejecutar la canalización en GitLab

Crear una solicitud de fusión

Para crear una solicitud de fusión, haz clic en Merge Requests (Solicitudes de fusión) y, a continuación, en Create merge request (Crear solicitud de fusión).

Pantalla de solicitudes de fusión en GitLab

Elige tu rama de funciones como rama de origen y, a continuación, haz clic en Compare branches and continue (Comparar ramas y continuar).

Comparar la rama de origen y la de destino en GitLab

Elige una Persona asignada y un Revisor.

Elegir un revisor para tu solicitud de fusión en GitLab

Haz clic en Create merge request (Crear solicitud de fusión).

Seleccionar el botón "Crear solicitud de fusión" en GitLab

Revisa los cambios en el código y, a continuación, haz clic en Approve (Aprobar).

Pantalla detallada de solicitudes de fusión en la que puedes revisar los cambios en GitLab

Haz clic en CI/CD y, a continuación, en Pipelines (Canalizaciones) para ver cómo se ejecuta la canalización de solicitudes de fusión.

Navegar a la pantalla "canalizaciones" de GitLab para ver las solicitudes de fusión ejecutadas

Haz clic en el identificador de la canalización. Ten en cuenta que el trabajo de canalización de solicitudes de fusión es el único trabajo que se ha ejecutado.

Página detallada de "canalización" que muestra que solo merge-request-pipeline-job se ejecutaba en GitLab

Vuelve a la solicitud de fusión haciendo clic en Solicitudes de fusión, luego en la solicitud de fusión activa y, a continuación, en Fusionar. Esto pone en marcha otra canalización.

Fusionar la solicitud de fusión activa en GitLab

Haz clic en CI/CD y, a continuación, en Pipelines (Canalizaciones). Haz clic en el identificador de la canalización.

Página detallada de la canalización en GitLab que muestra "fusionar 'IM-5' con 'rama principal'"

Crear un repositorio para SystemTests

Ve a Jira y crea una incidencia de Jira para añadir un repositorio de SystemTests a GitLab. Toma nota del identificador de la incidencia de Jira. En este ejemplo, es IM-7.

Crea una incidencia en Jira Software para "añadir el repositorio de GitLab para la AWS Lambda de SubmitImage"

Añade un Project name (Nombre de proyecto) y elige el grupo adecuado en Project URL (URL del proyecto). Haz clic en Create project (Crear proyecto) para continuar.

Rellenar los detalles del proyecto al crear un nuevo proyecto en GitLab

En tu terminal, ve al repositorio de SystemTests y ejecuta lo siguiente para enviar tu código a GitLab.

git add --all
git commit -m "IM-7 add SystemTests repository to gitlab"
git remote add origin git@gitlab.com:pmmquickstartguides/systemtests.git
git branch -m mainline
git push -u origin mainline

El repositorio de SystemTests no necesita un archivo .gitlab-ci.yml. No tiene una canalización propia, ya que proporciona pruebas para que se ejecuten otras canalizaciones. Toma nota de la URL remota de tu SystemTests. Las canalizaciones de CI/CD de SubmitImage, GetImageLabel e InvokeLabeller clonarán el repositorio de SystemTests durante las etapas de prueba. Tendrás que actualizar el gitlab-ci.yml de los repositorios posteriores con la URL correcta.

Añadir un token de implementación

Tienes que añadir un token de implementación para clonar este repositorio durante la ejecución de otras canalizaciones. Haz clic en Settings (Configuración) y, a continuación, en Repository (Repositorio). Desplázate hacia abajo y expande Deploy Tokens (Tokens de implementación).

Introducir un nombre de ejemplo "CloneMe" en "Implementar tokens" en GitLab

Introduce un nombre, marca read_repository y haz clic en Create deploy token (Crear token de implementación).

Seleccionar la casilla de verificación "read_repository" en la página de configuración de "Implementar tokens" en GitLab

El nombre de usuario del token de implementación se genera automáticamente. La contraseña del token de implementación se proporciona una vez en el momento de la creación. Añádelo a una herramienta de gestión de secretos para poder consultarlo más adelante. Más adelante en esta guía, se hace referencia al nombre de usuario del token de implementación como gitlab_deploy_token y a la contraseña del token de implementación como gitlab_deploy_password.

Pantalla de implementación de tokens en GitLab, que muestra el nombre de usuario y la contraseña de Implementar tokens

Crear un repositorio para la AWS Lambda de SubmitImage

Ve a Jira y crea una nueva incidencia para añadir un repositorio de la AWS Lambda de SubmitImage a GitLab. Toma nota del identificador de la incidencia. En este ejemplo es IM-8.

Tablero de ImageLabeller en Jira Software: destacando la incidencia "IM-8, añadir el repositorio de GitLab para la AWS Lambda de SubmitImage"

Ve a GitLab y haz clic en New Project (Nuevo proyecto) y, a continuación, en Create blank project (Crear proyecto en blanco). Añade un Project name (Nombre de proyecto) y elige el grupo adecuado en Project URL (URL del proyecto). Haz clic en Create project (Crear proyecto) para continuar.

captura de pantalla de la creación de un nuevo proyecto "SubmitImage" en GitLab

En tu terminal, ve al repositorio de SubmitImage y ejecuta lo siguiente para enviar tu código a GitLab.

git add --all
git commit -m "IM-8 add SubmitImage to gitlab"
git remote add origin git@gitlab.com:pmmquickstartguides/submitimage.git
git branch -m mainline
git push -u origin mainline

Debes añadir las claves de acceso de AWS, configurar las ramas protegidas y configurar los entornos de implementación.

A continuación, añade las claves de implementación de tu repositorio de SystemTests para permitir que la canalización SubmitImage de GitLab se descargue y ejecuta SystemTests.

Por último, añade el identificador de tu cuenta de AWS como variable de CI/CD.

captura de pantalla de variables en gitlab

.gitlab-ci.yml para implementar en AWS

Ve al repositorio de SubmitImage de tu terminal y crea una rama con el nombre de tu identificador de incidencia de Jira.

git checkout -b IM-8

Crea un archivo .gitlab-ci.yml con el siguiente yaml. Esto define un flujo de trabajo de implementación para tus entornos de pruebas, ensayo y producción. Debes actualizar la línea de clonación de git para que SystemTests sea tu repositorio de SystemTests.

stages:
  - merge-request
  - run-unit-tests
#US-WEST-1
  - deploy-us-west-1
  - test-us-west-1
#US-EAST-2
  - deploy-us-east-2
  - test-us-east-2
#US-WEST-2
  - deploy-us-west-2
  - test-us-west-2
#US-EAST-1
  - deploy-us-east-1
  - test-us-east-1
#CA-CENTRAL-1
  - deploy-ca-central-1
  - test-ca-central-1

merge-request-pipeline-job:
  stage: merge-request
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
  script:
    - echo "This pipeline always succeeds and enables merge"
    - echo true

run-unit-tests:
  stage: run-unit-tests
  image: golang:buster
  rules:
    - if: $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE != "merge_request_event"
  script:
    - cd submitImage
    - go test ./opendevopslambda/...

#US-WEST-1
deploy-us-west-1:
  stage: deploy-us-west-1
  environment: test-us-west-1
  image: python:latest
  rules:
    - if: $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE != "merge_request_event"
  before_script:
    - pip3 install awscli --upgrade
    - pip3 install aws-sam-cli --upgrade
    - wget https://golang.org/dl/go1.16.6.linux-amd64.tar.gz
    - rm -rf /usr/local/go && tar -C /usr/local -xzf go1.16.6.linux-amd64.tar.gz
    - export PATH=$PATH:/usr/local/go/bin
    - go version
  script:
    - export AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID
    - export AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY
    - sam build
    - sam package --output-template-file submit-image-packaged.yaml --s3-bucket open-devops-code-us-west-1-$AWS_ACCOUNT_ID --region us-west-1
    - sam deploy --template-file submit-image-packaged.yaml --stack-name OpenDevOpsSubmitImage  --s3-bucket open-devops-code-us-west-1-$AWS_ACCOUNT_ID --capabilities CAPABILITY_IAM --region us-west-1 --no-fail-on-empty-changeset

      #test-us-west-1:
      #  stage: test-us-west-1
      #  environment: test-us-west-1
      #  image: golang:buster
      #  rules:
      #    - if: $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE != "merge_request_event"
      #  script:
      #    - export AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID
      #    - export AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY
      #    - git clone https://${gitlab_deploy_token}:${gitlab_deploy_password}@gitlab.com/pmmquickstartguides/systemtests.git
      #    - cd systemtests
      #    - go test -v ./... -aws_region=us-west-1

#US-EAST-2
deploy-us-east-2:
  stage: deploy-us-east-2
  environment: test-us-east-2
  image: python:latest
  rules:
    - if: $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE != "merge_request_event"
  before_script:
    - pip3 install awscli --upgrade
    - pip3 install aws-sam-cli --upgrade
    - wget https://golang.org/dl/go1.16.6.linux-amd64.tar.gz
    - rm -rf /usr/local/go && tar -C /usr/local -xzf go1.16.6.linux-amd64.tar.gz
    - export PATH=$PATH:/usr/local/go/bin
    - go version
  script:
    - export AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID
    - export AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY
    - sam build
    - sam package --output-template-file submit-image-packaged.yaml --s3-bucket open-devops-code-us-east-2-$AWS_ACCOUNT_ID --region us-east-2
    - sam deploy --template-file submit-image-packaged.yaml --stack-name OpenDevOpsSubmitImage  --s3-bucket open-devops-code-us-east-2-$AWS_ACCOUNT_ID --capabilities CAPABILITY_IAM --region us-east-2 --no-fail-on-empty-changeset

      #test-us-east-2:
      #  stage: test-us-east-2
      #  environment: test-us-east-2
      #  image: golang:buster
      #  rules:
      #    - if: $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE != "merge_request_event"
      #  script:
      #    - export AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID
      #    - export AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY
      #    - git clone https://${gitlab_deploy_token}:${gitlab_deploy_password}@gitlab.com/pmmquickstartguides/systemtests.git
      #    - cd systemtests
      #    - go test -v ./... -aws_region=us-east-2

#US-WEST-2
deploy-us-west-2:
  stage: deploy-us-west-2
  environment: production-us-west-2
  image: python:latest
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE != "merge_request_event"
  before_script:
    - pip3 install awscli --upgrade
    - pip3 install aws-sam-cli --upgrade
    - wget https://golang.org/dl/go1.16.6.linux-amd64.tar.gz
    - rm -rf /usr/local/go && tar -C /usr/local -xzf go1.16.6.linux-amd64.tar.gz
    - export PATH=$PATH:/usr/local/go/bin
    - go version
  script:
    - export AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID
    - export AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY
    - sam build
    - sam package --output-template-file submit-image-packaged.yaml --s3-bucket open-devops-code-us-west-2-$AWS_ACCOUNT_ID --region us-west-2
    - sam deploy --template-file submit-image-packaged.yaml --stack-name OpenDevOpsSubmitImage  --s3-bucket open-devops-code-us-west-2-$AWS_ACCOUNT_ID --capabilities CAPABILITY_IAM --region us-west-2 --no-fail-on-empty-changeset

      #test-us-west-2:
      #  stage: test-us-west-2
      #  environment: production-us-west-2
      #  image: golang:buster
      #  rules:
      #    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE != "merge_request_event"
      #  script:
      #    - export AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID
      #    - export AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY
      #    - git clone https://${gitlab_deploy_token}:${gitlab_deploy_password}@gitlab.com/pmmquickstartguides/systemtests.git
      #    - cd systemtests
      #    - go test -v ./... -aws_region=us-west-2

#US-EAST-1
deploy-us-east-1:
  stage: deploy-us-east-1
  environment: production-us-east-1
  image: python:latest
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE != "merge_request_event"
  before_script:
    - pip3 install awscli --upgrade
    - pip3 install aws-sam-cli --upgrade
    - wget https://golang.org/dl/go1.16.6.linux-amd64.tar.gz
    - rm -rf /usr/local/go && tar -C /usr/local -xzf go1.16.6.linux-amd64.tar.gz
    - export PATH=$PATH:/usr/local/go/bin
    - go version
  script:
    - export AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID
    - export AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY
    - sam build
    - sam package --output-template-file submit-image-packaged.yaml --s3-bucket open-devops-code-us-east-1-$AWS_ACCOUNT_ID --region us-east-1
    - sam deploy --template-file submit-image-packaged.yaml --stack-name OpenDevOpsSubmitImage  --s3-bucket open-devops-code-us-east-1-$AWS_ACCOUNT_ID --capabilities CAPABILITY_IAM --region us-east-1 --no-fail-on-empty-changeset

      #test-us-east-1:
      #  stage: test-us-east-1
      #  environment: production-us-east-1
      #  image: golang:buster
      #  rules:
      #    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE != "merge_request_event"
      #  script:
      #    - export AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID
      #    - export AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY
      #    - git clone https://${gitlab_deploy_token}:${gitlab_deploy_password}@gitlab.com/pmmquickstartguides/systemtests.git
      #    - cd systemtests
      #    - go test -v ./... -aws_region=us-east-1

#CA-CENTRAL-1
deploy-central-1:
  stage: deploy-ca-central-1
  environment: production-ca-central-1
  image: python:latest
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE != "merge_request_event"
  before_script:
    - pip3 install awscli --upgrade
    - pip3 install aws-sam-cli --upgrade
    - wget https://golang.org/dl/go1.16.6.linux-amd64.tar.gz
    - rm -rf /usr/local/go && tar -C /usr/local -xzf go1.16.6.linux-amd64.tar.gz
    - export PATH=$PATH:/usr/local/go/bin
    - go version
  script:
    - export AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID
    - export AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY
    - sam build
    - sam package --output-template-file submit-image-packaged.yaml --s3-bucket open-devops-code-ca-central-1-$AWS_ACCOUNT_ID --region ca-central-1
    - sam deploy --template-file submit-image-packaged.yaml --stack-name OpenDevOpsSubmitImage  --s3-bucket open-devops-code-ca-central-1-$AWS_ACCOUNT_ID --capabilities CAPABILITY_IAM --region ca-central-1 --no-fail-on-empty-changeset

      #test-central-1:
      #  stage: test-ca-central-1
      #  environment: production-ca-central-1
      #  image: golang:buster
      #  rules:
      #    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE != "merge_request_event"
      #  script:
      #    - export AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID
      #    - export AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY
      #    - git clone https://${gitlab_deploy_token}:${gitlab_deploy_password}@gitlab.com/pmmquickstartguides/systemtests.git
      #    - cd systemtests
      #    - go test -v ./... -aws_region=ca-central-1

La ejecución de las pruebas de integración no admite comentarios por ahora. Las pruebas del sistema solo pasarán cuando se implemente toda la aplicación. Descomenta los pasos de la prueba de integración en tu repositorio y realiza otro envío para ejecutar la canalización de implementación una vez que se hayan implementado todos los componentes de ImageLabeller. Debes actualizar la línea de clonación de git para que SystemTests sea tu repositorio de SystemTests.

Entender un archivo .gitlab-ci.yml

Este paso ejecuta las pruebas unitarias que forman parte del repositorio de SubmitImage.

unit-test-us-west-1:
  stage: unit-test-us-west-1
  environment: test-us-west-1
  image: golang:buster
  rules:
    - if: $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE != "merge_request_event"
  script:
    - cd submitImage
    - go test ./opendevopslambda/...

Este paso implementa la AWS Lambda de SubmitImage mediante AWS SAM. Fíjate en la sección before_script. Este paso se ejecuta antes de la sección de scripts y se puede utilizar para instalar dependencias y configurar varias herramientas.

deploy-us-west-1:
  stage: deploy-us-west-1
  environment: test-us-west-1
  image: python:latest
  rules:
    - if: $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE != "merge_request_event"
  before_script:
    - pip3 install awscli --upgrade
    - pip3 install aws-sam-cli --upgrade
    - wget https://golang.org/dl/go1.16.6.linux-amd64.tar.gz
    - rm -rf /usr/local/go && tar -C /usr/local -xzf go1.16.6.linux-amd64.tar.gz
    - export PATH=$PATH:/usr/local/go/bin
    - go version
  script:
    - export AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID
    - export AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY
    - sam build
    - sam package --output-template-file submit-image-packaged.yaml --s3-bucket open-devops-code-us-west-1-$AWS_ACCOUNT_ID --region us-west-1
    - sam deploy --template-file submit-image-packaged.yaml --stack-name OpenDevOpsSubmitImage  --s3-bucket open-devops-code-us-west-1-$AWS_ACCOUNT_ID --capabilities CAPABILITY_IAM --region us-west-1 --no-fail-on-empty-changeset

Este paso descarga y ejecuta las pruebas de integración en el repositorio de SystemTests. Debes actualizar la línea de clonación de git para que SystemTests sea tu repositorio de SystemTests.

test-us-west-1:
  stage: test-us-west-1
  environment: test-us-west-1
  image: golang:buster
  rules:
    - if: $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE != "merge_request_event"
  script:
    - export AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID
    - export AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY
    - git clone https://${gitlab_deploy_token}:${gitlab_deploy_password}@gitlab.com/pmmquickstartguides/systemtests.git
    - cd systemtests
    - go test -v ./... -aws_region=us-west-1

Se hace referencia al token de implementación creado anteriormente en la línea de clonación de git.

- git clone https://${gitlab_deploy_token}:${gitlab_deploy_password}@gitlab.com/pmmquickstartguides/systemtests.git

Enviar a una rama de funciones

Ejecuta lo siguiente desde la línea de comandos para enviar los cambios a la rama IM-8 del repositorio de SubmitImage. Incluye el identificador de incidencia de Jira en los mensajes de confirmación y los nombres de las ramas para que la integración de Jira con GitLab pueda hacer un seguimiento de lo que ocurre en tu proyecto.

git add --all
git commit -m "IM-8 add .gitlab-ci.yml to SubmitImage"
git push -u origin IM-8

Haz clic en CI/CD y, a continuación, en Pipelines (Canalizaciones) para ver cómo se ejecuta la canalización.

captura de pantalla de una canalización ejecutada en gitlab

Crear una solicitud de fusión

Crea una solicitud de fusión para implementarla en tus entornos de producción después de que GitLab se implemente en tus entornos de pruebas. Elige la rama IM-8.

captura de pantalla de solicitudes de fusión en gitlab

Haz clic en CI/CD y, a continuación, en Pipelines (Canalizaciones) para ver la canalización de solicitudes de fusión en ejecución.

captura de pantalla de la ejecución de una solicitud de fusión en gitlab

Fusiona los cambios en la rama principal una vez que se complete el proceso de canalización de fusión. Haz clic en CI/CD y, a continuación, en Pipelines (Canalizaciones) para ver la canalización del entorno de producción en ejecución.

captura de pantalla de la ejecución de una canalización del entorno de producción en gitlab

Crear un repositorio para la AWS Lambda de InvokeLabeller

Ve a Jira y crea una nueva incidencia para añadir un repositorio de la AWS Lambda de InvokeLabeller a GitLab. Toma nota del identificador de la incidencia. En este ejemplo es IM-10.

captura de pantalla de una incidencia de jira para crear un repositorio" invokelabeller" en gitlab

Ve a GitLab y haz clic en New Project (Nuevo proyecto) y, a continuación, en Create blank project (Crear proyecto en blanco). Añade un Project name (Nombre de proyecto) y elige el grupo adecuado en Project URL (URL del proyecto). Haz clic en Create project (Crear proyecto) para continuar.

captura de pantalla de la creación de un nuevo proyecto "invokelabeller" en gitlab

En tu terminal, ve al repositorio de InvokeLabeller y ejecuta lo siguiente para enviar tu código a GitLab.

git add --all
git commit -m "IM-10 add InvokeLabeller to gitlab"
git remote add origin git@gitlab.com:pmmquickstartguides/invokelabeller.git
git branch -m mainline
git push -u origin mainline

Debes añadir las claves de acceso de AWS, configurar las ramas protegidas y configurar los entornos de implementación.

A continuación, añade las claves de implementación de tu repositorio de SystemTests para permitir que la canalización InvokeLabeller de GitLab se descargue y ejecuta SystemTests.

Por último, añade el identificador de tu cuenta de AWS como variable de CI/CD.

captura de pantalla de la página de variables en gitlab

.gitlab-ci.yml para implementar en AWS

Ve al repositorio de InvokeLabeller de tu terminal y crea una rama con el nombre de tu identificador de incidencia de Jira.

git checkout -b IM-10

Crea un archivo .gitlab-ci.yml con el siguiente yaml. Esto define un flujo de trabajo de implementación para tus entornos de pruebas, ensayo y producción. Debes actualizar la línea de clonación de git para que SystemTests sea tu repositorio de SystemTests.

stages:
  - merge-request
  - run-unit-tests
#US-WEST-1
  - deploy-us-west-1
  - test-us-west-1
#US-EAST-2
  - deploy-us-east-2
  - test-us-east-2
#US-WEST-2
  - deploy-us-west-2
  - test-us-west-2
#US-EAST-1
  - deploy-us-east-1
  - test-us-east-1
#CA-CENTRAL-1
  - deploy-ca-central-1
  - test-ca-central-1

merge-request-pipeline-job:
  stage: merge-request
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
  script:
    - echo "This pipeline always succeeds and enables merge"
    - echo true

run-unit-tests:
  stage: run-unit-tests
  image: python:3.8-buster
  rules:
    - if: $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE != "merge_request_event"
  before_script:
    - pip3 install pytest
    - pip3 install moto
    - pip3 install -r tst/requirements.txt --user
  script:
    - python3 -m pytest -v tst/unit --junitxml=test-reports/report.xml

#US-WEST-1
deploy-us-west-1:
  stage: deploy-us-west-1
  environment: test-us-west-1
  image: python:3.8-buster
  rules:
    - if: $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE != "merge_request_event"
  before_script:
    - pip3 install awscli --upgrade
    - pip3 install aws-sam-cli --upgrade
  script:
    - export AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID
    - export AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY
    - sam build
    - sam package --output-template-file invoke-labeller-packaged.yaml --s3-bucket open-devops-code-us-west-1-$AWS_ACCOUNT_ID --region us-west-1
    - sam deploy --template-file invoke-labeller-packaged.yaml --stack-name OpenDevOpsInvokeLabeller  --s3-bucket open-devops-code-us-west-1-$AWS_ACCOUNT_ID --capabilities CAPABILITY_IAM --region us-west-1 --no-fail-on-empty-changeset

      #test-us-west-1:
      #  stage: test-us-west-1
      #  environment: test-us-west-1
      #  image: golang:buster
      #  rules:
      #    - if: $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE != "merge_request_event"
      #  script:
      #    - export AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID
      #    - export AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY
      #    - git clone https://${gitlab_deploy_token}:${gitlab_deploy_password}@gitlab.com/pmmquickstartguides/systemtests.git
      #    - cd systemtests
      #    - go test -v ./... -aws_region=us-west-1

#US-EAST-2
deploy-us-east-2:
  stage: deploy-us-east-2
  environment: test-us-east-2
  image: python:3.8-buster
  rules:
    - if: $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE != "merge_request_event"
  before_script:
    - pip3 install awscli --upgrade
    - pip3 install aws-sam-cli --upgrade
  script:
    - export AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID
    - export AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY
    - sam build
    - sam package --output-template-file invoke-labeller-packaged.yaml --s3-bucket open-devops-code-us-east-2-$AWS_ACCOUNT_ID --region us-east-2
    - sam deploy --template-file invoke-labeller-packaged.yaml --stack-name OpenDevOpsInvokeLabeller  --s3-bucket open-devops-code-us-east-2-$AWS_ACCOUNT_ID --capabilities CAPABILITY_IAM --region us-east-2 --no-fail-on-empty-changeset

      #test-us-east-2:
      #  stage: test-us-east-2
      #  environment: test-us-east-2
      #  image: golang:buster
      #  rules:
      #    - if: $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE != "merge_request_event"
      #  script:
      #    - export AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID
      #    - export AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY
      #    - git clone https://${gitlab_deploy_token}:${gitlab_deploy_password}@gitlab.com/pmmquickstartguides/systemtests.git
      #    - cd systemtests
      #    - go test -v ./... -aws_region=us-east-2

#US-WEST-2
deploy-us-west-2:
  stage: deploy-us-west-2
  environment: production-us-west-2
  image: python:3.8-buster
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE != "merge_request_event"
  before_script:
    - pip3 install awscli --upgrade
    - pip3 install aws-sam-cli --upgrade
  script:
    - export AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID
    - export AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY
    - sam build
    - sam package --output-template-file invoke-labeller-packaged.yaml --s3-bucket open-devops-code-us-west-2-$AWS_ACCOUNT_ID --region us-west-2
    - sam deploy --template-file invoke-labeller-packaged.yaml --stack-name OpenDevOpsInvokeLabeller  --s3-bucket open-devops-code-us-west-2-$AWS_ACCOUNT_ID --capabilities CAPABILITY_IAM --region us-west-2 --no-fail-on-empty-changeset

      #test-us-west-2:
      #  stage: test-us-west-2
      #  environment: production-us-west-2
      #  image: golang:buster
      #  rules:
      #    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE != "merge_request_event"
      #  script:
      #    - export AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID
      #    - export AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY
      #    - git clone https://${gitlab_deploy_token}:${gitlab_deploy_password}@gitlab.com/pmmquickstartguides/systemtests.git
      #    - cd systemtests
      #    - go test -v ./... -aws_region=us-west-2

#US-EAST-1
deploy-us-east-1:
  stage: deploy-us-east-1
  environment: production-us-east-1
  image: python:3.8-buster
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE != "merge_request_event"
  before_script:
    - pip3 install awscli --upgrade
    - pip3 install aws-sam-cli --upgrade
  script:
    - export AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID
    - export AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY
    - sam build
    - sam package --output-template-file invoke-labeller-packaged.yaml --s3-bucket open-devops-code-us-east-1-$AWS_ACCOUNT_ID --region us-east-1
    - sam deploy --template-file invoke-labeller-packaged.yaml --stack-name OpenDevOpsInvokeLabeller  --s3-bucket open-devops-code-us-east-1-$AWS_ACCOUNT_ID --capabilities CAPABILITY_IAM --region us-east-1 --no-fail-on-empty-changeset

      #test-us-east-1:
      #  stage: test-us-east-1
      #  environment: production-us-east-1
      #  image: golang:buster
      #  rules:
      #    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE != "merge_request_event"
      #  script:
      #    - export AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID
      #    - export AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY
      #    - git clone https://${gitlab_deploy_token}:${gitlab_deploy_password}@gitlab.com/pmmquickstartguides/systemtests.git
      #    - cd systemtests
      #    - go test -v ./... -aws_region=us-east-1

#CA-CENTRAL-1
deploy-central-1:
  stage: deploy-ca-central-1
  environment: production-ca-central-1
  image: python:3.8-buster
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE != "merge_request_event"
  before_script:
    - pip3 install awscli --upgrade
    - pip3 install aws-sam-cli --upgrade
  script:
    - export AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID
    - export AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY
    - sam build
    - sam package --output-template-file invoke-labeller-packaged.yaml --s3-bucket open-devops-code-ca-central-1-$AWS_ACCOUNT_ID --region ca-central-1
    - sam deploy --template-file invoke-labeller-packaged.yaml --stack-name OpenDevOpsInvokeLabeller  --s3-bucket open-devops-code-ca-central-1-$AWS_ACCOUNT_ID --capabilities CAPABILITY_IAM --region ca-central-1 --no-fail-on-empty-changeset

      #test-central-1:
      #  stage: test-ca-central-1
      #  environment: production-ca-central-1
      #  image: golang:buster
      #  rules:
      #    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE != "merge_request_event"
      #  script:
      #    - export AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID
      #    - export AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY
      #    - git clone https://${gitlab_deploy_token}:${gitlab_deploy_password}@gitlab.com/pmmquickstartguides/systemtests.git
      #    - cd systemtests
      #    - go test -v ./... -aws_region=ca-central-1

La ejecución de las pruebas de integración no admite comentarios por ahora. Las pruebas del sistema solo pasarán cuando se implemente toda la aplicación. Descomenta los pasos de la prueba de integración en tu repositorio y realiza otro envío para ejecutar la canalización de implementación una vez que se hayan implementado todos los componentes de ImageLabeller. Debes actualizar la línea de clonación de git para que SystemTests sea tu repositorio de SystemTests.

Actualizar src/app.py con el endpoint de AWS SageMaker

Abre el archivo src/app.py de InvokeLabeller y busca query_endpoint. Cambia el endpoint_name y el region_name del cliente para que coincidan con tu bloc de notas de AWS SageMaker.

def query_endpoint(img):
  endpoint_name = 'jumpstart-dft-image-labeller-endpoint'
  client = boto3.client(service_name='runtime.sagemaker', region_name='us-west-1')
  response = client.invoke_endpoint(EndpointName=endpoint_name, ContentType='application/x-image', Body=img)
  model_predictions = json.loads(response['Body'].read())['predictions'][0]
  return model_predictions

Enviar a una rama de funciones

Ejecuta lo siguiente desde la línea de comandos para enviar los cambios a la rama IM-10 del repositorio de InvokeLabeller. Incluye el identificador de incidencia de Jira en los mensajes de confirmación y los nombres de las ramas para que la integración de Jira con GitLab pueda hacer un seguimiento de lo que ocurre en tu proyecto.

git add --all
git commit -m "IM-10 add .gitlab-ci.yml to InvokeLabeller"
git push -u origin IM-10

Haz clic en CI/CD y, a continuación, en Pipelines (Canalizaciones) para ver cómo se ejecuta la canalización.

captura de pantalla de una canalización ejecutada en gitlab

Crear una solicitud de fusión

Crea una solicitud de fusión para implementarla en tus entornos de producción después de que GitLab se implemente en tus entornos de pruebas. Elige la rama IM-10.

captura de pantalla de la creación de una solicitud de fusión en gitlab

Fusiona los cambios en la rama principal una vez que se complete el proceso de canalización de fusión. Haz clic en CI/CD y, a continuación, en Pipelines (Canalizaciones) para ver la canalización del entorno de producción en ejecución.

captura de pantalla de la ejecución de una canalización del entorno de producción en gitlab

Si has llegado hasta aquí, ¡enhorabuena! Acabas de implementar ImageLabeller. El siguiente paso es configurar la monitorización de ImageLabeller con Opsgenie.

Warren Marusiak
Warren Marusiak

Warren is a Canadian developer from Vancouver, BC with over 10 years of experience. He came to Atlassian from AWS in January of 2021.


Compartir este artículo

Lecturas recomendadas

Consulta estos recursos para conocer los tipos de equipos de DevOps o para estar al tanto de las novedades sobre DevOps en Atlassian.

Ilustración de Devops

La comunidad de DevOps

Ilustración de Devops

Ruta de aprendizaje de DevOps

Ilustración de un mapa

Pruébalo gratis

Suscríbete para recibir el boletín de DevOps

Thank you for signing up