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
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
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
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
y selecciona “Vista previa en el puerto 8080”.

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:

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

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:

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.


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


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.

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:

A continuación, Configurar sistema:

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:

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”:

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

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.

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:

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”.


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.