En este proyecto vamos a realizar un despliegue de pipelines CI/CD usando jenkins y ArgoCD, automatizado mediante webhooks de github y jobs creados en Jenkins, se desplegara internamente de forma automatica el helm contenedor de los microservicios y a su vez se realizara monitoreo de estos mediante prometheus y grafana
Se debe tener instalado
Docker Kubernetes helm
Podemos ver el flujo de la aplicacion en el que se comienza realizando un cambio en algun archivo del repositorio de CI, luego esto acciona el webhook asociado a jenkins el cual acciona el pipeline que añade un nuevo versionamiento a la imagen de Docker y la sube a un container registry el cual es Dockerhub, luego se invoca automaticamente el pipeline de CD el cual actualiza este versionamiento en el pod asociado al helm y luego, argoCD verifica dichos cambios y actualiza el servicio con la nueva imagen para añadir dicha version al pod de forma local.
Para la instalacion de Jenkins se procedera primero a realizar una clonacion del siguiente repo
git clone https://github.com/DanielRamirez1901/CD-Jenkins-Kubernetes.git
Este repo descargara una carpeta llamada JenkinsConfig, en jenkins-ns.yaml cambiaremos el nombre del namespace a nuestro gusto y luego ejecutamos los siguientes comandos
cd CD-Jenkins-Kubernetes
cd JenkinsConfig
kubectl apply -f jenkins-ns.yaml
kubectl apply -f jenkins-pv.yaml
kubectl apply -f jenkins-pvc.yaml
helm repo add jenkins https://charts.jenkins.io
helm repo update
helm install jenkins --namespace parcial3 --values values.yaml jenkins/jenkins
Con el comando
kbc get all -n parcial3
Podremos ver la lista de los servicios asociados a ese namespace el cual en este caso son los de jenkins
Ahora ejecutamos el comando para hacer accesible el servicio de jenkins localmente
minikube service jenkins --url -n parcial3
Ahora, debemos crear un tunel para exponer jenkins que se encuentra ejecutandose localmente a traves de internet para poder luego configurarlo con los webhooks de github, para ello se puede realizar con ngrok o con serveo, el comando para serveo es el siguiente
ssh -R jenkins-example.serveo.net:80:<Localhost>
:<Puerto-del-Jenkins>
serveo.net
Localhost:
Se puede reemplazar con la ip asociada al servicio, en este caso 127.0.0.1Puerto-del-Jenkins:
Se reemplaza con el puerto expuesto por el servicio, en este caso 39965
O en este caso, con Ngrok, para ello se procede primero a instalar ngrok
wget https://bin.equinox.io/c/bNyj1mQVY4c/ngrok-v3-stable-linux-amd64.tgz
tar -xvzf ngrok-v3-stable-linux-amd64.tgz
sudo mv ngrok /usr/local/bin
Ahora, procedemos a ir a la pagina oficial de registro de ngrok Iniciamos sesion y copiamos el token para posteriormente usar en el siguiente comando
ngrok config add-authtoken YOUR_AUTHTOKEN
Y luego creamos el tunel para acceder a jenkins por medio de internet
ngrok http 127.0.0.1:39965
Ahora que Jenkins es accesible en internet, procedemos a configurarlo para su posterior uso, para obtener la contraseña y acceder al login se usa el siguiente comando
printf$(kubectl get secret --namespace parcial3 jenkins -o jsonpath="{.data.jenkins-admin-password}"| base64 --decode);echo
Se deben primero actualizar los plugins necesarios, para ello vamos a
Administrar Jenkins>Plugins>Updates y actualizamos todo lo que aqui se encuentre
Y en esa misma pagina, en Available Plugins vamos a buscar los plugins relacionados a Github y relacionados a Docker y luego se reinicia jenkins
Ahora, se agregaran credenciales que se usaran en el pipeline de jenkins para por ejemplo logearse en github y hacer pull o en dockerhub para subir la imagen creada con su nuevo tag
Para ello se accede a Panel de control>Administrar Jenkins>Credentials>system>Global credentials>Add credentials
Se deben usar los tokens generados por dockerhub y github ya que estas plataformas ya no permiten el uso de contraseñas para el inicio de sesion por consola, cabe recalcar que el token de github se debe crear permitiendole el uso de webhooks
Para este ejercicio se debe tener como minimo estos dos credentials
Ahora creamos las tareas necesarias, en este caso se crearan dos, una para el CI y otra para el CD
CI
Para este caso, creamos una tarea de tipo pipeline
Habilitamos el trigger github trigger for GITScm polling
Seleccionamos en el apartado de pipelines para que se use el de Jenkins que se encuentra en el repo
Guardamos todo este Job
Ahora cremos una nueva tarea, esta se nombrara updatemanifest
Se selecciona en la configuracion general, esta opcion debe parametrizarse, se añade un parametro de cadena titulado DOCKERTAG con value latest
Se habilita el trigger como en el Job pasado
Ahora configuramos el pipeline de la misma forma que el Job anterior pero usando como url de github contenedor del Jenkins, el repo creado para CD
En este apartado se mostrara el pipeline creado para CI/CD con lo que tendra mas sentido lo configurado anteriormente en Jenkins
CI
En este apartado se usara el repo siguiente que se puede clonar con el comando
git clone https://github.com/DanielRamirez1901/CI-Jenkins-Docker.git
En este repo se encuentra un proyecto de ejemplo, un proyecto n-tier, y se configurara el jenkins en base a dicho proyecto usando un dockerfile como base de imagen
podTemplate(label: 'build', containers: [
containerTemplate(name: 'docker', image: 'docker', command: 'cat', ttyEnabled: true)
],
volumes: [
hostPathVolume(mountPath: '/var/run/docker.sock', hostPath: '/var/run/docker.sock'),
]
) {
node('build') {
container('docker') {
def app
stage('Clone repository') {
checkout scm
}
stage('Build image') {
app = docker.build("ventana1901/knote")
}
stage('Test image') {
app.inside {
sh 'echo "Tests passed"'
}
}
stage('Push image') {
docker.withRegistry('https://registry.hub.docker.com', 'dockerhub') {
app.push("${env.BUILD_NUMBER}")
}
}
stage('Trigger ManifestUpdate') {
echo "triggering updatemanifestjob"
build job: 'updatemanifest', parameters: [string(name: 'DOCKERTAG', value: env.BUILD_NUMBER)]
}
}
}
}
El pipeline utiliza un pod template para ejecutar contenedores Docker en Jenkins. A continuación se detallan las etapas del pipeline:
El podTemplate
define un pod con un contenedor Docker y un volumen montado para Docker socket.
podTemplate(label: 'build', containers: [
containerTemplate(name: 'docker', image: 'docker', command: 'cat', ttyEnabled: true)
],
volumes: [
hostPathVolume(mountPath: '/var/run/docker.sock', hostPath: '/var/run/docker.sock'),
]
)
El pipeline se ejecuta en un nodo etiquetado como build
y dentro de un contenedor Docker.
node('build') {
container('docker') {
Esta etapa clona el repositorio especificado en la configuración de Jenkins.
stage('Clone repository') {
checkout scm
}
Esta etapa construye la imagen Docker utilizando el Dockerfile presente en el repositorio. La imagen se etiqueta como ventana1901/knote
.
stage('Build image') {
app = docker.build("ventana1901/knote")
}
En esta etapa, la imagen Docker construida se ejecuta y se ejecutan las pruebas dentro del contenedor.
stage('Test image') {
app.inside {
sh 'echo "Tests passed"'
}
}
La imagen Docker se empuja a Docker Hub. La imagen se etiqueta con el número de build de Jenkins.
stage('Push image') {
docker.withRegistry('https://registry.hub.docker.com', 'dockerhub') {
app.push("${env
Finalmente, se dispara otro trabajo de Jenkins (updatemanifest
) para actualizar el manifiesto de despliegue con el nuevo tag de la imagen.
stage('Trigger ManifestUpdate') {
echo "triggering updatemanifestjob"
build job: 'updatemanifest', parameters: [string(name: 'DOCKERTAG', value: env.BUILD_NUMBER)]
}
CD
Para este apartado, se usa otro repo que se puede obtener mediante el comando
git clone https://github.com/DanielRamirez1901/CD-Jenkins-Kubernetes.git
En este repo se encuentra el Jenkinsfile asociado al CD
node {
def app
stage('Clone repository') {
checkout scm
}
stage('Update GIT') {
script {
catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') {
withCredentials([usernamePassword(credentialsId: 'github', passwordVariable: 'GIT_PASSWORD', usernameVariable: 'GIT_USERNAME')]) {
sh "git config user.email danirg2222@outlook.es"
sh "git config user.name DanielRamirez1901"
// Path to the values.yaml in the Helm chart directory
def valuesYamlPath = 'helm-project/values.yaml'
// Update the image tag in the values.yaml file
sh "sed -i 's+tag:.*+tag: ${DOCKERTAG}+g' ${valuesYamlPath}"
// Display the updated file for verification
sh "cat ${valuesYamlPath}"
// Commit and push the changes to the repository
sh "git add ${valuesYamlPath}"
sh "git commit -m 'Update image tag to ${DOCKERTAG} by Jenkins Job: ${env.BUILD_NUMBER}'"
sh "git push https://${GIT_USERNAME}:${GIT_PASSWORD}@github.com/${GIT_USERNAME}/CD-Jenkins-Kubernetes.git HEAD:main"
}
}
}
}
}
Este Jenkins Pipeline está diseñado para clonar un repositorio, actualizar el archivo values.yaml
de un chart de Helm con un nuevo tag de imagen, y luego hacer commit y push de los cambios al repositorio.
El pipeline consta de varias etapas para realizar estas tareas:
El pipeline se ejecuta en un nodo Jenkins.
node {
def app
Esta etapa clona el repositorio especificado en la configuración de Jenkins.
stage('Clone repository') {
checkout scm
}
En esta etapa, se actualiza el archivo values.yaml
en el chart de Helm con el nuevo tag de imagen proporcionado en la variable DOCKERTAG
.
Se configura el usuario de Git con el correo electrónico y el nombre proporcionados.
sh "git config user.email danirg2222@outlook.es"
sh "git config user.name DanielRamirez1901"
Se define la ruta al archivo values.yaml
en el directorio del chart de Helm.
def valuesYamlPath = 'helm-project/values.yaml'
Se utiliza sed
para buscar y reemplazar la línea que contiene el tag de la imagen con el nuevo tag proporcionado en DOCKERTAG
.
sh "sed -i 's+tag:.*+tag: ${DOCKERTAG}+g' ${valuesYamlPath}"
Se muestra el contenido del archivo values.yaml
actualizado para verificar los cambios.
sh "cat ${valuesYamlPath}"
Se hacen commit y push de los cambios al repositorio utilizando las credenciales de Git configuradas en Jenkins.
sh "git add ${valuesYamlPath}"
sh "git commit -m 'Update image tag to ${DOCKERTAG} by Jenkins Job: ${env.BUILD_NUMBER}'"
sh "git push https://${GIT_USERNAME}:${GIT_PASSWORD}@github.com/${GIT_USERNAME}/CD-Jenkins-Kubernetes.git HEAD:main"
Ahora, para añadir webhooks en el CI para que cualquier cambio, genere un nuevo tag y este se actualice en el deploy, se debe hacer lo siguiente
Vamos a settings del repo CI, luego en webhooks y creamos uno, este webhook se configurade la siguiente manera
se debe añadir en payload URL /github-webhook, dicha url es la dada por ngrok o serveo
Ahora, si hacemos cualquier cambio en el repo del CI, se ejecutaran los Jobs
Podemos ver que el CI invoco el Job del CD y ambos se ejecutaron correctamente
Ahora vamos a usar argoCD el cual se conectara al repositorio contenedor del CD e instalara localmente los recursos asociados al helm en nuestro cluster
Se instala el ArgoCD con los siguientes comandos
kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd stable/manifests/install.yaml
Luego, se accede a él con el siguiente comando
kubectl port-forward svc/argocd-server -n argocd 8080:443
Y para acceder se usa este comando para obtener la contraseña
kbc -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d; echo
En argo cd ejecutamos el siguiente .yaml para que cree un proyecto asociado al repositorio de CD y utilice los valores del Helm para levantar el proyecto
se ejecuta el comando
kubectl apply -f argocd.yaml
Luego se da click en sync para sincronizar los servicios de forma local con el argoCD
Y ahora podemos ver la aplicacion en ejecucion y con su estado de salud asociado a cada microservicio