Implementación continua en Google Kubernetes Engine mediante Jenkins

En este instructivo, se muestra cómo configurar una canalización de entrega continua mediante Jenkins y Google Kubernetes Engine (GKE), como se describe en el siguiente diagrama.

Arquitectura de entrega continua de Jenkins.

Objetivos

  • Comprender una aplicación de muestra
  • Implementar una aplicación en GKE
  • Subir el código a Cloud Source Repositories
  • Crear canalizaciones de implementación en Jenkins
  • Implementar entornos de desarrollo
  • Implementar un lanzamiento Canary
  • Implementar entornos de producción

Costos

En este instructivo, se usan componentes facturables de Google Cloud, que incluyen los siguientes:

  • Compute Engine
  • Google Kubernetes Engine
  • Cloud Build

Usa la calculadora de precios a fin de generar una estimación de costos según el uso previsto para este instructivo. Los usuarios nuevos de GCP pueden optar por una prueba gratuita.

Antes de comenzar

  1. Accede a tu Cuenta de Google.

    Si todavía no tienes una cuenta, regístrate para obtener una nueva.

  2. En la página de selección de proyectos de Cloud Console, selecciona o crea un proyecto de Cloud.

    Ir a la página Selector de proyectos

  3. Comprueba que la facturación esté habilitada en tu proyecto.

    Descubre cómo puedes habilitar la facturación

  4. Habilita las API de Compute Engine, GKE, and Cloud Build.

    Habilita las API

Prepara el entorno

  1. Completa el instructivo Configura Jenkins en GKE. Asegúrate de tener una instalación de Jenkins en ejecución en GKE.

  2. En Cloud Shell, clona el código de muestra:

    git clone https://github.com/GoogleCloudPlatform/continuous-deployment-on-kubernetes
    cd continuous-deployment-on-kubernetes/sample-app
    
  3. Aplica la función cluster-admin a la cuenta de servicio de Jenkins:

    kubectl create clusterrolebinding jenkins-deploy \
        --clusterrole=cluster-admin --serviceaccount=default:cd-jenkins
    

    En este instructivo, la cuenta de servicio de Jenkins necesita permisos cluster-admin para crear espacios de nombres de Kubernetes y cualquier otro recurso que requiera la app. Para el uso en producción, debes catalogar los permisos necesarios y aplicarlos a la cuenta de servicio de forma individual.

Características de la aplicación

Implementarás la aplicación de muestra, gceme, en la canalización de implementación continua. La aplicación está escrita en lenguaje Go y se encuentra en el directorio sample-app del repositorio. Cuando ejecutas el objeto binario gceme en una instancia de Compute Engine, la app muestra los metadatos de la instancia en una tarjeta de información.

tarjeta de información de gceme

Para imitar a un microservicio, la aplicación admite dos modos de operación:

  • En el modo de backend, gceme escucha en el puerto 8080 y muestra los metadatos de la instancia de Compute Engine en formato JSON.

  • En el modo de frontend, gceme consulta el servicio de backend gceme y procesa el JSON resultante en la interfaz de usuario.

    arquitectura de gceme

Los modos de frontend y backend admiten las siguientes dos URL adicionales:

  • /version imprime la versión en ejecución.
  • /healthz informa el estado de la aplicación. En el modo de frontend, el estado se muestra como OK si se puede acceder al backend.

Implementa la misma app de muestra en Kubernetes

Implementa el frontend y el backend de gceme en Kubernetes mediante archivos de manifiesto que describan el entorno de implementación. Los archivos usan una imagen predeterminada que se actualiza más adelante en este instructivo.

Implementa las aplicaciones en dos entornos.

  • Producción. El sitio en vivo al que tus usuarios acceden.

  • Canary. Un sitio de menor capacidad que recibe solo un porcentaje del tráfico de usuarios. Usa este entorno para verificar el estado del software con tráfico en vivo antes de que se envíe al entorno en vivo.

Primero, implementa la aplicación en el entorno de producción para propagar códigos en la canalización.

  1. Crea el espacio de nombres de Kubernetes para aislar de forma lógica la implementación de producción:

    kubectl create ns production
    
  2. Crea los servicios y las implementaciones de producción y Canary:

    kubectl --namespace=production apply -f k8s/production
    kubectl --namespace=production apply -f k8s/canary
    kubectl --namespace=production apply -f k8s/services
    
  3. Escala verticalmente los frontends del entorno de producción:

    kubectl --namespace=production scale deployment gceme-frontend-production --replicas=4
    
  4. Recupera la dirección IP externa para los servicios de producción. Pueden pasar varios minutos hasta que veas la dirección IP del balanceador de cargas.

    kubectl --namespace=production get service gceme-frontend
    

    Cuando se completa el proceso, se muestra una dirección IP en la columna EXTERNAL-IP.

    NAME             TYPE           CLUSTER-IP     EXTERNAL-IP    PORT(S)        AGE
    gceme-frontend   LoadBalancer   10.35.254.91   35.196.48.78   80:31088/TCP   1m
    
  5. Almacena la IP del balanceador de cargas del servicio de frontend en una variable de entorno:

    export FRONTEND_SERVICE_IP=$(kubectl get -o jsonpath="{.status.loadBalancer.ingress[0].ip}"  --namespace=production services gceme-frontend)
    
  6. Abre la dirección IP externa del frontend en tu navegador para confirmar que ambos servicios están funcionando.

  7. Abre una terminal diferente y sondea la URL /version del extremo de producción para que puedas observar actualizaciones progresivas en la siguiente sección:

    while true; do curl http://$FRONTEND_SERVICE_IP/version; sleep 1;  done
    

Crea un repositorio para alojar el código fuente de la app de muestra

Luego, crea una copia de la app de muestra gceme y envíala a Cloud Source Repositories.

  1. Crea el repositorio en Cloud Source Repositories:

    gcloud source repos create gceme
    
  2. Inicializa el repositorio de Git local:

    git init
    git config credential.helper gcloud.sh
    export PROJECT_ID=$(gcloud config get-value project)
    git remote add origin https://source.developers.google.com/p/$PROJECT_ID/r/gceme
    
  3. Configura el nombre de usuario y la dirección de correo electrónico para tu confirmación de Git en este repositorio. Reemplaza [EMAIL_ADDRESS] por tu dirección de correo electrónico de Git y [USERNAME] por tu nombre de usuario de Git.

    git config --global user.email "[EMAIL_ADDRESS]"
    git config --global user.name "[USERNAME]"
    
  4. Agrega, confirma y envía los archivos:

    git add .
    git commit -m "Initial commit"
    git push origin master
    

Crea una canalización

Usa Jenkins a fin de definir y ejecutar una canalización para probar, compilar y, luego, implementar la copia de gceme en el clúster de Kubernetes.

Agrega las credenciales de tu cuenta de servicio

Configura tus credenciales para permitir que Jenkins acceda al repositorio de códigos.

  1. En la interfaz de usuario de Jenkins, haz clic en Credenciales en la barra de navegación izquierda.
  2. Haz clic en el vínculo de Jenkins en la tabla Credentials.

    Grupos de credenciales de Jenkins

  3. Haz clic en Global credentials.

  4. Haz clic en Agregar credenciales en la barra de navegación izquierda.

  5. Selecciona Cuenta de servicios de Google de metadatos en la lista desplegable Tipo.

  6. Haz clic en Aceptar.

Ahora hay dos credenciales globales. Anota el nombre de la segunda credencial para usarlo más adelante en este instructivo.

Credenciales de Jenkins.

Crea un trabajo de Jenkins

Luego, usa la función de canalización de Jenkins para configurar la canalización de compilación. Los archivos de canalización de Jenkins se escriben mediante una sintaxis similar a la de Groovy.

Navega a la interfaz de usuario de Jenkins y sigue estos pasos para configurar un trabajo de canalización.

  1. Haz clic en el vínculo de Jenkins en la parte superior izquierda de la interfaz.
  2. Haz clic en el vínculo Elemento nuevo en la barra de navegación izquierda.
  3. Asigna un nombre a la app de muestra del proyecto; luego, elige la opción Multibranch Pipeline y haz clic en OK.
  4. En la página siguiente, haz clic en Add Source y selecciona Git.
  5. Pega la HTTPS clone URL del repositorio de sample-app en Cloud Source Repositories en el campo Project Repository. Reemplaza [PROJECT_ID] por el ID del proyecto.

    https://source.developers.google.com/p/[PROJECT_ID]/r/gceme
    
  6. En la lista desplegable Credentials, selecciona el nombre de las credenciales que creaste cuando agregaste la cuenta de servicio.

  7. En la sección Scan Multibranch Pipeline, selecciona el cuadro Periodically if not otherwise run. Establece el valor de Interval en “1 minute”.

  8. Haz clic en Save.

    Crea una configuración de trabajo de Jenkins.

Después de completar estos pasos, se ejecuta un trabajo denominado “Branch indexing” (indexación de ramas). Este metatrabajo identifica las ramas en el repositorio y garantiza que no se hayan producido cambios en las ramas existentes. Si actualizas Jenkins, la rama master muestra este trabajo.

La primera ejecución del trabajo fallará hasta que realices algunos cambios de código en el siguiente paso.

Modifica la definición de la canalización

Crea una rama para el entorno Canary llamada canary.

git checkout -b canary

El contenedor Jenkinsfile que define esa canalización se escribe con la sintaxis de Groovy para canalizaciones de Jenkins. El uso de un Jenkinsfile permite que una canalización de compilación completa se exprese en un solo archivo, que convive con el código fuente. Las canalizaciones admiten características potentes como la paralelización y requieren la aprobación manual del usuario.

Modifica el Jenkinsfile para que contenga tu ID del proyecto en la línea 1.

Implementa un lanzamiento Canary

Ahora que la canalización está configurada de manera correcta, puedes realizar un cambio en la aplicación gceme y dejar que la canalización la pruebe, la empaquete y la implemente.

El entorno Canary se configura como un lanzamiento Canary. Como tal, tu cambio se envía a un pequeño porcentaje de los pods detrás del balanceador de cargas de producción. Esto se logra en Kubernetes; para ello, se deben mantener varias implementaciones que compartan las mismas etiquetas. Para esta aplicación, los servicios de gceme-frontend realizan el balanceo de cargas en todos los pods que tienen las etiquetas app: gceme y role: frontend. El archivo de manifiesto de Canary k8s/frontend-canary.yaml establece las réplicas en 1 y, también, incluye las etiquetas necesarias para el servicio de gceme-frontend.

En la actualidad, tienes 1 de cada 5 pods de frontend ejecutando el código de Canary, mientras que los otros 4 ejecutan el código de producción. Esto ayuda a garantizar que el código de Canary no afecte de forma negativa a los usuarios antes del lanzamiento en todo el conjunto de pods.

  1. Abre html.go y reemplaza las dos instancias de blue por orange.
  2. Abre main.go y cambia el número de versión de 1.0.0 a 2.0.0:

    const version string = "2.0.0"
    
  3. Luego, agrega esos archivos al repositorio local y confírmalos.

    git add Jenkinsfile html.go main.go
    git commit -m "Version 2"
    
  4. Por último, envía los cambios al servidor remoto de Git:

    git push origin canary
    
  5. Después de enviar el cambio al repositorio de Git, navega a la interfaz de usuario de Jenkins, en la que podrás ver que se inició la compilación.

    Pantalla de la primera compilación de Jenkins.

  6. Una vez que la compilación se esté ejecutando, haz clic en la flecha hacia abajo que se encuentra junto a la compilación en la barra de navegación izquierda y selecciona Console Output:

    Consola de navegación de Jenkins.

  7. Realiza un seguimiento del resultado de la compilación durante unos minutos y espera a que aparezcan los mensajes kubectl --namespace=production apply... para comenzar. Cuando comiencen, vuelve a verificar en la terminal que sondeaba la URL /version de producción y observa cómo empiezan a cambiar en algunas de las solicitudes. Ahora extendiste ese cambio a un subconjunto de usuarios.

  8. Después de implementar el cambio en el entorno de Canary, puedes extenderlo al resto de los usuarios; para hacerlo, debes combinar el código con la rama master y enviar esta combinación al servidor de Git:

    git checkout master
    git merge canary
    git push origin master
    
  9. En alrededor de 1 minuto, se inicia el trabajo master en la carpeta sample-app.

    Trabajo principal de Jenkins.

  10. Haz clic en el vínculo master para mostrar las etapas de la canalización, la información de procesos correctos y con fallas, y las características de tiempo.

    Producción de canalización de Jenkins.

  11. Consulta la URL de producción para verificar que la nueva versión 2.0.0 se haya lanzado y esté entregando las solicitudes de todos los usuarios.

    export FRONTEND_SERVICE_IP=$(kubectl get -o jsonpath="{.status.loadBalancer.ingress[0].ip}" --namespace=production services gceme-frontend)
    while true; do curl http://$FRONTEND_SERVICE_IP/version; sleep 1;  done
    

Puedes ver el Jenkinsfile en el proyecto para ver el flujo de trabajo.

Implementa una rama de desarrollo

A veces, es necesario trabajar con cambios no triviales que no se puedan enviar directamente al entorno de Canary. Una rama de desarrollo es un conjunto de entornos que usan los desarrolladores para probar los cambios de código antes de enviarlos a la integración en el sitio en vivo. Estos entornos son una versión reducida de la aplicación, pero se implementan mediante los mismos mecanismos que el entorno en vivo.

Para crear un entorno de desarrollo a partir de una rama de funciones, puedes enviar la rama al servidor Git y dejar que Jenkins implemente el entorno. En una situación de desarrollo, no usarías un balanceador de cargas público. Para ayudar a proteger la aplicación, puedes usar el proxy de kubectl. El proxy se autentica por sí solo con la API de Kubernetes y procesa las solicitudes de la máquina local al servicio en el clúster sin exponer tu servicio a Internet.

  1. Crea otra rama y envíala al servidor de Git:

    git checkout -b new-feature
    git push origin new-feature
    

    Se crea un trabajo nuevo, y el entorno de desarrollo está en proceso de creación. En la parte inferior del resultado de la consola del trabajo, hay instrucciones para acceder al entorno.

  2. Inicia el proxy en segundo plano:

    kubectl proxy &
    
  3. Verifica que se pueda acceder a la aplicación mediante el host local:

    curl http://localhost:8001/api/v1/namespaces/new-feature/services/gceme-frontend:80/proxy/
    
  4. Ahora puedes enviar el código a esta rama para actualizar el entorno de desarrollo. Cuando termines, vuelve a combinar la rama en canary para implementar ese código en el entorno de Canary.

    git checkout canary
    git merge new-feature
    git push origin canary
    
  5. Cuando estés seguro de que el código no causará problemas en el entorno de producción, combina la rama canary con la rama master para iniciar la implementación:

    git checkout master
    git merge canary
    git push origin master
    
  6. Cuando termines con la rama de desarrollo, bórrala del servidor y borra el entorno del clúster de Kubernetes:

    git push origin :new-feature
    kubectl delete ns new-feature
    

Realiza una limpieza

Sigue estos pasos para evitar que se apliquen cargos a tu cuenta de Google Cloud Platform por los recursos que usaste en este instructivo:

Borra el proyecto

La manera más fácil de eliminar la facturación es borrar el proyecto que creaste para el instructivo.

Para borrar el proyecto, sigue estos pasos:

  1. En Cloud Console, ve a la página Administrar recursos.

    Ir a la página Administrar recursos

  2. En la lista de proyectos, selecciona el proyecto que deseas borrar y haz clic en Borrar .
  3. En el cuadro de diálogo, escribe el ID del proyecto y haz clic en Cerrar para borrar el proyecto.

Próximos pasos