Canalización de Jenkins de varias ramas en GKE

1. Descripción general

Jenkins es una de las soluciones de integración continua más populares disponibles. Se usa para automatizar las partes esenciales no humanas del proceso de desarrollo de software. Si implementamos Jenkins en Kubernetes en Google Cloud y utilizamos el complemento de GKE, podemos escalar de forma rápida y automática los ejecutores de compilación según sea necesario. En combinación con Cloud Storage, podemos compilar y probar una aplicación con un esfuerzo mínimo.

Actividades

  • Implementa Jenkins en un clúster de Kubernetes
  • Implementa y configura el complemento Jenkins de GKE para permitir que Jenkins cree y destruya pods como nodos ejecutores.
  • Compila y prueba una aplicación de muestra de SpringBoot
  • Compila y publica un contenedor en Google Container Registry
  • Implementa la aplicación de ejemplo en un entorno de producción y de pruebas de GKE

Requisitos

  • Un proyecto de Google Cloud con la facturación configurada. Si no tienes una, deberás crearla.

2. Cómo prepararte

Este codelab se puede ejecutar completamente en Google Cloud Platform sin necesidad de instalación ni configuración local.

Cloud Shell

A lo largo de este codelab, aprovisionaremos y administraremos diferentes recursos y servicios de la nube con la línea de comandos a través de Cloud Shell.

Habilita las APIs

Estas son las APIs que deberemos habilitar en nuestro proyecto:

  • API de Compute Engine: Crea y ejecuta máquinas virtuales
  • API de Kubernetes Engine: Compila y administra aplicaciones basadas en contenedores
  • API de Cloud Build: Plataforma de integración continua y entrega continua de Google Cloud
  • API de Service Management: Permite a los productores de servicios publicar servicios en Google Cloud Platform
  • API de Cloud Resource Manager: Crea, lee y actualiza metadatos para contenedores de recursos de Google Cloud

Habilita las APIs requeridas con el siguiente comando de gcloud:

gcloud services enable compute.googleapis.com \
container.googleapis.com \
cloudbuild.googleapis.com \
servicemanagement.googleapis.com \
cloudresourcemanager.googleapis.com \
--project ${GOOGLE_CLOUD_PROJECT}

Crea un bucket de GCS

Necesitaremos un bucket de GCS para subir nuestro trabajo de prueba. Creemos un bucket con nuestro ID del proyecto en el nombre para garantizar la unicidad:

gsutil mb gs://${GOOGLE_CLOUD_PROJECT}-jenkins-test-bucket/ 

3. Cómo crear clústeres de Kubernetes

Crea el clúster

A continuación, crearemos un clúster de GKE que alojará nuestro sistema de Jenkins, incluidos los Pods que se enviarán como nodos trabajadores. El alcance adicional indicado por la marca --scopes permitirá que Jenkins acceda a Cloud Source Repositories y a Container Registry. En la consola de Cloud, ejecuta lo siguiente:

gcloud container clusters create jenkins-cd \
--machine-type n1-standard-2 --num-nodes 1 \
--zone us-east1-d \
--scopes "https://www.googleapis.com/auth/source.read_write,cloud-platform" \
--cluster-version latest

También implementaremos 2 clústeres para alojar nuestras compilaciones de etapa de pruebas y producción de la aplicación de ejemplo:

gcloud container clusters create staging \
--machine-type n1-standard-2 --num-nodes 1 \
--zone us-east1-d \
--cluster-version latest
gcloud container clusters create prod \
--machine-type n1-standard-2 --num-nodes 2 \
--zone us-east1-d \
--cluster-version latest

28b45298e1e82748.png Verificar

Una vez que se hayan creado los clústeres, podemos confirmar que se están ejecutando con gcloud container clusters list.

El resultado debería tener RUNNING en la columna STATUS:

NAME        LOCATION    MASTER_VERSION  MASTER_IP     MACHINE_TYPE   NODE_VERSION  NUM_NODES  STATUS
jenkins-cd  us-east1-d  1.15.9-gke.9    34.74.77.124  n1-standard-2  1.15.9-gke.9  2          RUNNING
prod        us-east1-d  1.15.9-gke.9    35.229.98.12  n1-standard-2  1.15.9-gke.9  2          RUNNING
staging     us-east1-d  1.15.9-gke.9    34.73.92.228  n1-standard-2  1.15.9-gke.9  2          RUNNING

4. Implementa Jenkins con Helm

Instala Helm

Usaremos Helm, un administrador de paquetes de aplicaciones para Kubernetes, para instalar Jenkins en nuestro clúster. Para comenzar, descarga el proyecto que incluye los manifiestos de Kubernetes que usaremos para implementar Jenkins:

git clone https://github.com/GoogleCloudPlatform/continuous-deployment-on-kubernetes.git ~/continuous-deployment-on-kubernetes

Cambia tu directorio de trabajo actual al directorio del proyecto:

cd ~/continuous-deployment-on-kubernetes/

Crea una vinculación de rol de clúster para otorgarte permisos de rol de cluster-admin:

kubectl create clusterrolebinding cluster-admin-binding --clusterrole=cluster-admin --user=$(gcloud config get-value account)

Para conectarte a tu clúster de Jenkins, obtén sus credenciales:

gcloud container clusters get-credentials jenkins-cd --zone us-east1-d --project ${GOOGLE_CLOUD_PROJECT}

Descarga el objeto binario de Helm en tu consola de Cloud:

wget https://storage.googleapis.com/kubernetes-helm/helm-v2.14.1-linux-amd64.tar.gz

Descomprime el archivo y copia el archivo de Helm incluido en tu directorio de trabajo actual:

tar zxfv helm-v2.14.1-linux-amd64.tar.gz && \
cp linux-amd64/helm .

Tiller es el lado del servidor de Helm que se ejecuta en el clúster de Kubernetes. Creemos una cuenta de servicio llamada tiller:

kubectl create serviceaccount tiller \
--namespace kube-system

Y vincúlalo al rol de clúster cluster-admin para que pueda realizar cambios:

kubectl create clusterrolebinding tiller-admin-binding \
--clusterrole=cluster-admin \
--serviceaccount=kube-system:tiller

Ahora podemos inicializar Helm y actualizar el repo:

./helm init --service-account=tiller && \
./helm repo update

28b45298e1e82748.png Verificar

Confirma que Helm esté listo con ./helm version. Esto debería mostrar los números de versión del cliente y el servidor:

Client: &version.Version{SemVer:"v2.14.1", GitCommit:"5270352a09c7e8b6e8c9593002a73535276507c0", GitTreeState:"clean"}
Server: &version.Version{SemVer:"v2.14.1", GitCommit:"5270352a09c7e8b6e8c9593002a73535276507c0", GitTreeState:"clean"}

Instala Jenkins

Ahora que Helm está instalado en nuestro clúster, podemos instalar Jenkins:

./helm install stable/jenkins -n cd \
-f jenkins/values.yaml \
--version 1.2.2 --wait

28b45298e1e82748.png Verificar

Verifiquemos los Pods:

kubectl get pods

El resultado debería mostrar nuestro Pod de Jenkins con el estado RUNNING:

NAME                          READY     STATUS    RESTARTS   AGE
cd-jenkins-7c786475dd-vbhg4   1/1       Running   0          1m

Confirma que el servicio de Jenkins se haya creado correctamente:

kubectl get svc

El resultado debería ser similar a este:

NAME               TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)     AGE
cd-jenkins         ClusterIP   10.35.241.170   <none>        8080/TCP    2m27s
cd-jenkins-agent   ClusterIP   10.35.250.57    <none>        50000/TCP   2m27s
kubernetes         ClusterIP   10.35.240.1     <none>        443/TCP     75m

La instalación de Jenkins utilizará el complemento de Kubernetes para crear agentes de compilación. La instancia principal de Jenkins los iniciará automáticamente según sea necesario. Una vez finalizado su trabajo, se terminarán automáticamente y los recursos se agregarán de nuevo al grupo de recursos del clúster.

Conéctate a Jenkins

Jenkins se ejecuta en nuestro clúster, pero, para acceder a la IU, configuremos la redirección de puertos desde Cloud Shell:

export POD_NAME=$(kubectl get pods --namespace default -l "app.kubernetes.io/component=jenkins-master" -l "app.kubernetes.io/instance=cd" -o jsonpath="{.items[0].metadata.name}") &&
kubectl port-forward $POD_NAME 8080:8080 >> /dev/null &

Se generó una contraseña de administrador durante la instalación. Recuperémoslo:

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

En la parte superior de Cloud Shell, haz clic en el ícono de vista previa en la Web 7ddf5a65fd556dd6.png y selecciona “Vista previa en el puerto 8080”.

1d614c831a621cff.png

Deberíamos ver una pantalla de acceso para Jenkins en la que podemos ingresar admin para el nombre de usuario y la contraseña que se devolvió en el paso anterior:

9cba23e856cbc84f.png

Cuando hagamos clic en Acceder, se nos dirigirá a la página principal de Jenkins.

9261f3e914829137.png

5. Instala y configura el complemento de GKE

El complemento de Google Kubernetes Engine nos permite publicar implementaciones creadas en Jenkins en nuestros clústeres de Kubernetes que se ejecutan en GKE. Debes realizar algunos ajustes de configuración con los permisos de IAM en tu proyecto. Implementaremos esa configuración con Terraform.

Primero, descarga el proyecto del complemento de GKE:

git clone https://github.com/jenkinsci/google-kubernetes-engine-plugin.git ~/google-kubernetes-engine-plugin

Configuración automatizada de permisos de IAM

Cambia tu directorio de trabajo actual al directorio rbac del proyecto de GKE que clonamos antes:

cd ~/google-kubernetes-engine-plugin/docs/rbac/

gcp-sa-setup.tf es un archivo de configuración de Terraform que creará un rol personalizado de IAM de GCP con permisos restringidos junto con una cuenta de servicio de GCP para otorgar ese rol. El archivo requiere valores para las variables del proyecto, la región y el nombre de la cuenta de servicio. Para proporcionar esos valores, primero declaramos las siguientes variables de entorno:

export TF_VAR_project=${GOOGLE_CLOUD_PROJECT}
export TF_VAR_region=us-east1-d
export TF_VAR_sa_name=kaniko-role

Inicializa Terraform, genera un plan y aplícalo:

terraform init
terraform plan -out /tmp/tf.plan
terraform apply /tmp/tf.plan && rm /tmp/tf.plan

La cuenta de servicio necesitará permisos de administrador de almacenamiento para guardar en nuestro bucket de Cloud Storage:

gcloud projects add-iam-policy-binding ${GOOGLE_CLOUD_PROJECT} \
--member serviceAccount:kaniko-role@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com \
--role 'roles/storage.admin'

También necesitará permisos de contenedor para las etapas de implementación de nuestra canalización:

gcloud projects add-iam-policy-binding ${GOOGLE_CLOUD_PROJECT} --member \
serviceAccount:kaniko-role@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com --role 'roles/container.developer'

Ahora podemos usar Helm para configurar los permisos del clúster para el complemento de GKE con el robot de implementación de GKE. Cambia tu directorio de trabajo al directorio helm del proyecto de GKE:

cd ~/google-kubernetes-engine-plugin/docs/helm/

E instala con el gráfico de Helm proporcionado:

export TARGET_NAMESPACE=kube-system && \
envsubst < gke-robot-deployer/values.yaml | helm install ./gke-robot-deployer --name gke-robot-deployer -f -

6. Configura Jenkins

Claves de cuentas de servicio

Para que la cuenta de servicio funcione correctamente, deberemos generar un archivo de claves privadas y agregarlo como un secreto de Kubernetes. Primero, genera el archivo con el siguiente comando de gcloud:

gcloud iam service-accounts keys create /tmp/kaniko-secret.json --iam-account kaniko-role@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com

Crearemos una clave secreta en el almacén de secretos de Kubernetes con ese archivo:

kubectl create secret generic jenkins-int-samples-kaniko-secret --from-file=/tmp/kaniko-secret.json 

Descarga el archivo JSON en tu disco local. Para ello, accede al elemento Download File desde el menú de 3 puntos de Cloud Shell:

c40378e72013b843.png

Ingresa la ruta de acceso al archivo /tmp/kaniko-secret.json y haz clic en Descargar.

De vuelta en la página de Jenkins, en el panel lateral izquierdo, haz clic en Credenciales y, luego, en Sistema.

6c140f7e6bb82f8.png

3b874912cdc8019b.png

En la sección de la página titulada Sistema, haz clic en Credenciales globales y, luego, en Agregar credenciales a la izquierda:

4350c0e68561119b.png

3d3526551cdae8b.png

En el menú desplegable Tipo, selecciona Cuenta de servicio de Google desde una clave privada. Ingresa “kaniko-role” como el nombre, sube la clave JSON que se creó en los pasos anteriores y haz clic en Aceptar.

b0502213408e730e.png

Variables de entorno

Hay algunas variables de entorno que deberemos definir en Jenkins antes de crear la canalización de varias ramas. Son los siguientes:

  • JENK_INT_IT_ZONE: Es la zona del clúster de Kubernetes. En nuestro caso, us-east1-d
  • JENK_INT_IT_PROJECT_ID: Hace referencia al ID del proyecto de GCP que aloja esta instancia de Jenkins.
  • JENK_INT_IT_STAGING: Es el nombre de nuestro clúster de "etapa de pruebas". Para fines de demostración, es staging.
  • JENK_INT_IT_PROD: Es el nombre de nuestro clúster de producción. A modo de demostración, es prod.
  • JENK_INT_IT_BUCKET: Es el bucket de Google Cloud Storage que se creó en el paso anterior.
  • JENK_INT_IT_CRED_ID: Hace referencia a las credenciales creadas con el archivo JSON del paso anterior. El valor debe coincidir con el nombre que le asignamos, kaniko-role.

Para agregarlos, ve a Administrar Jenkins:

d54f279190a07878.png

A continuación, Configurar sistema:

ce79d218b2799640.png

Habrá una sección llamada Propiedades globales, y cuando marquemos la casilla de Variables de entorno, aparecerá un botón Agregar en el que haremos clic para agregar las variables anteriores como pares clave-valor:

81aa222a2b17b2cc.png

Haz clic en el botón Guardar que se encuentra en la parte inferior de la página para aplicar los cambios.

7. Configura una canalización

En Jenkins, haz clic en “New Item”:

8d1270ce4d7b6a8a.png

Ingresa "jenkins-integration-sample" como nombre, selecciona "Multibranch Pipeline" como el tipo de proyecto y haz clic en Aceptar:

eb071ecfbb4d775b.png

Se nos redireccionará a la página de configuración de la canalización. En Branch Sources, ingresa https://github.com/GoogleCloudPlatform/jenkins-integration-samples.git como el repositorio del proyecto. En Configuración de compilación, ingresa "gke/Jenkinsfile" como la Ruta de secuencia de comandos.

5135bd6b0374508c.png

Haz clic en Guardar para aplicar esta configuración. Cuando guardes los cambios, Jenkins iniciará un análisis del repositorio y una compilación posterior para cada rama. A medida que avanza, verás que se crean, ejecutan y destruyen pods en la página Cargas de trabajo de Kubernetes a medida que avanzan las compilaciones.

Cuando finalicen las compilaciones, encontrarás dos elementos en la página Cargas de trabajo de Kubernetes llamados jenkins-integration-samples-gke, cada uno correspondiente al clúster de producción o de prueba. El estado se mostrará como OK:

bdec6b1753d1ba07.png

Con el siguiente comando de gcloud, veremos que subimos una imagen de contenedor a Google Container Registry que corresponde a nuestra canalización:

gcloud container images list

Para ver la carga de trabajo en tu navegador, obtén las credenciales del clúster de producción:

gcloud container clusters get-credentials prod --zone us-east1-d --project ${GOOGLE_CLOUD_PROJECT}

Ejecuta el siguiente comando para configurar un reenvío de puertos desde el puerto 8081 de tu shell al puerto 8080 de tu carga de trabajo:

export POD_NAME=$(kubectl get pods -o jsonpath="{.items[0].metadata.name}") &&
kubectl port-forward $POD_NAME 8081:8080 >> /dev/null &

En la parte superior de Cloud Shell, haz clic en el ícono de vista previa en la Web y selecciona “Vista previa en el puerto 8081”.

1b19b5b56f1bae7.png

e80e995e71763bb2.png

8. Limpieza

Exploramos cómo implementar una canalización de Jenkins y una canalización de varias ramas de ejemplo en Kubernetes. Ahora es momento de limpiar nuestro proyecto de todos los recursos que creamos.

Borra el proyecto

Si lo prefieres, puedes borrar todo el proyecto. En GCP Console, ve a la página Cloud Resource Manager.

En la lista de proyectos, selecciona el proyecto en el que hemos estado trabajando y haz clic en Borrar. Se te pedirá que ingreses el ID del proyecto. Ingrésalo y haz clic en Cerrar.

Como alternativa, puedes borrar todo el proyecto directamente desde Cloud Shell con gcloud:

gcloud projects delete $GOOGLE_CLOUD_PROJECT

Si prefieres borrar los diferentes componentes facturables uno por uno, continúa con la siguiente sección.

Clúster de Kubernetes

Borra todo el clúster de Kubernetes con gcloud:

gcloud container clusters delete jenkins-cd --zone=us-east1-d

Buckets de almacenamiento

Quita todos los archivos subidos y borra nuestro bucket con gsutil:

gsutil rm -r gs://${GOOGLE_CLOUD_PROJECT}-jenkins-test-bucket

Imágenes de Google Container Registry

Borraremos las imágenes de Google Container Registry con los resúmenes de imágenes. Primero, recupera los resúmenes con el siguiente comando:

gcloud container images list-tags gcr.io/${GOOGLE_CLOUD_PROJECT}/jenkins-integration-samples-gke --format="value(digest)"

Luego, para cada resumen que se muestre, haz lo siguiente:

gcloud container images delete gcr.io/${GOOGLE_CLOUD_PROJECT}/jenkins-integration-samples-gke@sha256:<DIGEST>

9. ¡Felicitaciones!

¡Hurra! Lo lograste. Aprendiste a implementar Jenkins en GKE y a enviar trabajos a clústeres de Kubernetes.

Temas abordados

  • Implementamos un clúster de Kubernetes y usamos Helm para instalar Jenkins.
  • Instalamos y configuramos el complemento de GKE para permitir que Jenkins implemente artefactos de compilación en clústeres de Kubernetes.
  • Configuramos Jenkins para que establezca una canalización de varias ramas que envíe trabajo a los clústeres de GKE.