Java en la nube: tutorial de configuración de integración continua
Publicado: 2022-03-11Año tras año, somos testigos de la evolución cada vez más rápida de la industria de TI. Han pasado más de dos décadas desde que el eslogan innovador "Escriba una vez, ejecute en cualquier lugar" estableció un nuevo nivel de expectativa para la comunidad de desarrollo de software. Y aquí estamos hoy, con un conjunto resultante de herramientas en constante expansión que colectivamente han llevado el desarrollo de Java en particular, y el desarrollo de software en general, a un nuevo universo de posibilidades.
Metodologías como Agile, DevOps y Continuous Integration and Deployment, junto con la evolución de los microservicios, han impulsado colectivamente la productividad del proceso de desarrollo de software hasta un punto en el que es un placer desarrollar software más que nunca. Utilizar la automatización y configurar el conjunto correcto de herramientas puede hacer que el desarrollo y la entrega de productos de software sean sorprendentemente sencillos.
Este artículo analizará este nuevo universo desde la perspectiva de un desarrollador de Java que cruza a DevOps y busca optimizar al máximo el desarrollo y la entrega de productos.
Hoy en día, términos como Spring Boot, Docker, Cloud, Amazon Web Services, Continuous Delivery son ampliamente utilizados pero menos comprendidos. Este artículo tomará la ruta más fácil posible para presentar todas estas tecnologías y explicar estos términos, y lo resumirá en forma de tutorial en el que desarrollaremos una pequeña pieza de software y la prepararemos para la entrega en producción utilizando todas las herramientas mencionadas.
¿Por qué estas herramientas?
Simplifique las implementaciones con Docker
"Escribir una vez, ejecutar en cualquier lugar" fue el avance conceptual que produjo tecnologías como Java Virtual Machine (JVM) que permitió que su código se ejecutara en cualquier lugar. Y ahora aquí estamos, un par de décadas después, con algo llamado Docker presentado a la comunidad de TI. Docker es una herramienta de contención en la que puede colocar su software y ejecutarlo sin problemas, casi en cualquier lugar que desee.
Sin embargo, un desarrollador de Java puede mirar a Docker y decir "¿Por qué necesitaríamos eso? Ya tenemos la JVM, que es reconocida como la solución portátil principal". ¿Pero es?
"Escribir una vez, ejecutar en cualquier lugar" suena bien y funciona bien... al menos la mayor parte del tiempo. Hasta que encuentre múltiples proveedores de JVM, múltiples versiones de Java, múltiples sistemas operativos y varias permutaciones y combinaciones de todo lo anterior. Luego se encuentra cambiando del elegante paradigma "Escribir una vez, ejecutar en cualquier lugar" al contraproducente escollo "Escribir una vez, depurar en todas partes".
Y ahí es donde entra Docker para ayudar a salvar el día.
Docker simplifica el desarrollo, las pruebas y el envío de software. Si tiene el software que desea probar, colóquelo en el contenedor Docker, se ejecutará y será fácil de instalar para todas las partes involucradas.
Acelere el desarrollo con Spring Boot
Menos de una década después de que se introdujera el lema "ejecutar en cualquier lugar", apareció en escena el marco Spring. Hoy en día, el ecosistema de Spring continúa floreciendo y ha producido muchos proyectos valiosos basados en Spring, quizás el más notable Spring Boot. Como se indica en el sitio web de Spring Boot:
Spring Boot facilita la creación de aplicaciones basadas en Spring independientes y de grado de producción que puede ejecutar.
Spring Boot le permite poner en marcha la aplicación en cuestión de minutos. Los desarrolladores de software pueden concentrarse en el desarrollo de software y luego pueden beneficiarse de una herramienta que hace toda la configuración por ellos.
En este tutorial, usaremos Spring Boot para desarrollar nuestro microservicio.
Integración Continua (CI) con Jenkins
DevOps es un movimiento de rápido crecimiento que está integrando estrechamente los equipos de desarrollo de software y administración de sistemas, con el objetivo de hacer que el ciclo de vida de desarrollo y entrega de software sea lo menos doloroso, fluido y productivo posible para todas las partes involucradas: desarrolladores, administradores de sistemas, evaluadores y, en última instancia, , los usuarios finales.
La integración continua (CI) es uno de los pilares de la revolución DevOps. La idea es que cada vez que un desarrollador envía código al repositorio de código, se prueba y empaqueta automáticamente para su entrega (implementación) a producción.
CI va de la mano con:
- Entrega continua: entrega automática del paquete preparado para las pruebas comerciales del usuario final con activación manual para la implementación de producción.
- Implementación continua: implementación automática del producto empaquetado directamente en producción.
Existen más de unas pocas herramientas que se pueden utilizar para implementar el proceso de IC. Una de las más populares es Jenkins, una herramienta de CI de código abierto. Con más de mil complementos y una gran comunidad detrás, Jenkins es una opción fácil cuando se comienza a pensar en implementar integración, entrega o implementación continuas.
En nuestro tutorial, usaremos Jenkins para enviar nuestro producto a la nube, más específicamente, a la nube de Amazon (AWS).
Computación en la nube con AWS
Si tiene algo de experiencia como administrador de sistemas, imagine quitarse algunas de las preocupaciones de la administración del sistema de sus hombros. Tienes algunas aplicaciones; tiene una idea de cuántos recursos requerirán, pero no sabe exactamente el tamaño del hardware que necesitará. Se hace la estimación, se compran los recursos y el sistema pasa a producción. Si tiene suerte, encontrará que sobreestimó y tiene más recursos de los que necesita. Pero dada la Ley de Murphy, es más probable que descubra que subestimó los requisitos de recursos y termine luchando para obtener un poco más de memoria o potencia de procesamiento bajo una tremenda presión de tiempo. Por el contrario, si está implementando en la nube, simplemente coloca su sistema y lo dimensiona según sea necesario, con la flexibilidad que ofrecen los proveedores de la nube. Con la nube, no necesita preocuparse por quedarse sin recursos del sistema, ni por tener el 90 por ciento de su memoria o CPU inactivos.
Por supuesto, existe el desafío de decidir qué proveedor elegir. Las guerras en la nube todavía están en progreso. El choque de Microsoft, Amazon y Google por el futuro de la informática es un título de ejemplo que puede encontrar últimamente en las noticias del mundo tecnológico. Para este blog, he elegido Amazon Web Services (AWS), en gran medida en función de su popularidad actual y participación de mercado.
Una de las ventajas de AWS es que Amazon ofrece muchos servicios después de registrarse:
En este tutorial, utilizaremos los siguientes dos servicios de AWS: Elastic Compute Cloud EC2 (más específicamente, Amazon EC2 Container Registry o Amazon ECR) y Amazon S3 (Simple Storage Services).
Amazon ECR
Tendremos que almacenar nuestras imágenes de Docker en algún lugar. Amazon ECR es un servicio de registro de AWS Docker administrado. Como se indica en el sitio web de Amazon ECR:
…facilita a los desarrolladores el almacenamiento, la gestión y la implementación de imágenes de contenedores de Docker. Amazon ECR está integrado con Amazon EC2 Container Service (ECS), lo que simplifica su flujo de trabajo de desarrollo a producción. Amazon ECR elimina la necesidad de operar sus propios repositorios de contenedores o preocuparse por escalar la infraestructura subyacente.
Amazonas S3
Como se mencionó, la aplicación que desarrollaremos será un microservicio Spring Boot que cargará archivos en Amazon S3. Como se indica en el sitio web de Amazon S3:
…proporciona a los desarrolladores y equipos de TI un almacenamiento en la nube seguro, duradero y altamente escalable. Amazon S3 es un almacenamiento de objetos fácil de usar, con una interfaz de servicio web simple para almacenar y recuperar cualquier cantidad de datos desde cualquier lugar de la web.
Un tutorial práctico de "cómo hacer"
El objetivo es prepararse para la implementación de un microservicio Spring Boot que cargará archivos en Amazon S3. Los pasos son los siguientes:
- Desarrollar el microservicio
- Definir el proceso de compilación en el que se dockerizará el servicio
- Use Bitbucket para alojar el repositorio de código Git
- Integre Bitbucket con Jenkins para empaquetar la aplicación usando Gradle
- Empújelo a un Amazon ECR remoto
Lo que sigue es un tutorial para configurar todos los componentes necesarios:
- Aplicación de ejemplo de Spring Boot: microservicio empaquetado y dockerizado con Gradle
- Instalación de Jenkins en un servidor Ubuntu nuevo
- Integración de Bitbucket con Jenkins mediante webhook
- Configuración del trabajo de Jenkins
- Amazon ECR para almacenar las imágenes de Docker que contienen nuestra aplicación
requisitos previos
Para poder utilizar los recursos de la nube de AWS, primero debemos registrarnos en Amazon. Al registrarnos, obtendremos una cuenta con beneficios inmediatos de uso de la capa gratuita, con el fin de habilitar la experiencia práctica durante los 12 meses posteriores al registro.
Como se mencionó, en este tutorial usaremos Amazon S3 y Amazon ECR. Para ambos, necesitaremos claves de acceso para conectarnos a los servicios.
Luego de registrarnos en AWS, vamos a nuestra cuenta Credenciales de seguridad , donde elegimos Claves de acceso y hacemos clic en “Crear nueva clave de acceso”. Después de hacer clic, se genera una clave junto con su ID. Debe almacenar esto en un lugar seguro, ya que lo usaremos más adelante cuando configuremos la integración de AWS Jenkins y desarrollemos nuestra carga de archivos S3.
El siguiente requisito previo es que necesitamos un depósito de Amazon S3 (contenedor de almacenamiento). Nuestro servicio Spring Boot cargará y descargará archivos hacia y desde el almacenamiento de Amazon S3. La creación de cubos es bastante simple y solo requiere unos pocos clics. Se proporciona una descripción completa de cómo hacerlo en la documentación Crear un depósito.
También usaremos Bitbucket para alojar nuestro código y activar solicitudes a Jenkins, por lo que también se necesita una cuenta de Bitbucket. Bitbucket es una excelente opción para los desarrolladores, y uno de sus principales beneficios es la cantidad ilimitada de repositorios privados que puede crear.
Desarrollo de aplicaciones
En lugar de entrar en todos los detalles de las anotaciones de Spring y cómo funcionan, me centraré, desde la perspectiva pura del desarrollador, en la parte más desafiante de toda la configuración; es decir, instalar y configurar Linux, Jenkins y otras herramientas necesarias para CI. Todos los ejemplos de código usados en este tutorial, incluida la aplicación de microservicio Spring Boot, están disponibles en el repositorio de Bickbucket para el proyecto.
La composición de nuestra aplicación es simple. Tenemos un punto de entrada de la aplicación Spring Boot en nuestro archivo StorageWebserviceApplication.java
. La lógica para cargar y descargar archivos está en StorageService.java
. StorageController.java
es un controlador Rest que contiene puntos finales de API que se utilizan para cargar y descargar archivos. Aquí está la jerarquía del proyecto:
Hemos elegido Gradle como herramienta de compilación, y empaquetará nuestra aplicación y compondrá la imagen de Docker. A continuación, analizaremos el archivo de compilación de Gradle, el componente de servicio y el Dockerfile.
Para poder usar la API de AWS, debemos incluir dependencias en nuestro archivo de compilación, tal como se define en la documentación de AWS para usar Gradle.
En resumen, la parte de configuración de dependencia de AWS de nuestro script de Gradle tendrá el siguiente aspecto:
buildscript { ... repositories { mavenCentral() } dependencies { ... classpath("io.spring.gradle:dependency-management-plugin:0.5.4.RELEASE") } } .. apply plugin: "io.spring.dependency-management" dependencyManagement { imports { mavenBom ('com.amazonaws:aws-java-sdk-bom:1.10.47') } } dependencies { .. compile ('com.amazonaws:aws-java-sdk-s3') }
Como se indicó anteriormente, cuando cargamos archivos en Amazon S3, lo hacemos cargando archivos en un depósito S3.
Para conectarse al depósito, nuestro cliente de Amazon S3 debe tener las credenciales proporcionadas. Las credenciales son las claves de acceso que creamos anteriormente. Definimos el ID y el valor de la clave de acceso en el archivo application.properties
; hemos nombrado nuestro cubo toptal-s3-example
.
Nuestro principal componente de servicio ahora es el siguiente:
@Service public class StorageService { @Value("${aws.accesKeyId}") private String awsAccessKeyId; @Value("${aws.secretKey}") private String awsSecretKey; @Value("${aws.bucketName}") private String awsBucketName; private AWSCredentials credentials; private AmazonS3 s3client;; @PostConstruct public void init(){ credentials = new BasicAWSCredentials(awsAccessKeyId, awsSecretKey); s3client = new AmazonS3Client(credentials); } public void uploadFile(MultipartFile file) throws IOException { File fileForUpload = transformMultipartToFile(file); s3client.putObject(new PutObjectRequest(awsBucketName, file.getOriginalFilename(), fileForUpload)); } public InputStream downloadFile(String amazonFileKey) throws IOException { S3Object fetchFile = s3client.getObject(new GetObjectRequest(awsBucketName, amazonFileKey)); InputStream objectData = fetchFile.getObjectContent(); return objectData; } …
StorageService
lee las credenciales del archivo application.properties
y las usa para crear instancias del objeto BasicAWSCredentials
y, posteriormente, del objeto AmazonS3Client
. Lo que sigue es una simple cuestión de invocar putObject
para la carga de archivos y getObject
para la descarga de archivos, en el objeto de cliente de Amazon S3.
Ejecutaremos el servicio dentro de un contenedor de Docker y, durante el proceso de compilación de Gradle, compilaremos la imagen de Docker. Haremos esto configurando adicionalmente el archivo build.gradle
, de la siguiente manera:
buildscript { ... dependencies { ... classpath('se.transmode.gradle:gradle-docker:1.2') } } ..... apply plugin: 'docker' ... task buildDocker(type: Docker, dependsOn: build) { push = false applicationName = "storageservice" dockerfile = file('src/main/docker/Dockerfile') doFirst { copy { from jar into stageDir } } }
La parte Buildscript
y el apply plugin
de aplicación son bastante estándar. También hemos definido una tarea buildDocker
que lee la configuración de Docker almacenada en src/main/docker/Dockerfile
y copia el archivo JAR en la compilación de Docker.
Dockerfile contiene una lista de comandos Docker puros con los que prepararemos nuestra imagen:
FROM frolvlad/alpine-oraclejdk8 ADD storageWebService-0.0.1-SNAPSHOT.jar storageService.jar EXPOSE 8080 CMD ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "/storageService.jar"]
Un requisito previo para ejecutar nuestra aplicación es tener instalada una Máquina Virtual Java (JVM). Docker proporciona una lista de imágenes con Java instalado, y elegiremos una de las más pequeñas, basada en un Alpine Linux mínimo de 5 MB. frolvlad/alpine-oraclejdk8
tiene todo lo que necesitamos y es bastante pequeña (solo 170 MB).
El comando FROM
establece la imagen mencionada como la base sobre la que se construirá la nuestra. ADD
el archivo JAR integrado al sistema de archivos del contenedor con el nombre storageService.jar
. A continuación, definimos que el contenedor Docker escuche en el puerto 8080
en tiempo de ejecución con el comando EXPOSE
. Sin embargo, esto no habilitará la comunicación al 8080
desde el host. Cuando la imagen esté lista y queramos ejecutarla, también necesitaremos publicar el puerto en el contenedor con el siguiente comando docker run -p 8080:8080 amazonRepository/storageservice
, donde amazonRepository
es un repositorio que configuraremos más adelante en este tutorial. Con CMD
, definimos qué comandos se ejecutarán cuando ejecutemos el contenedor. Los valores entre paréntesis del comando CMD
simplemente significan que se ejecutará lo siguiente cuando ejecutemos el contenedor:
java -Djava.security.egd=file:/dev/./urandom -jar /storageService.jar
La opción -Djava.security.egd=file:/dev/./urandom
es necesaria para ayudar a mitigar los retrasos de JVM durante el inicio. Si se omite, hará que el inicio de la aplicación sea extremadamente lento debido a un proceso de generación de números aleatorios que se necesita durante el proceso de inicio.
Esto resume la parte de "Desarrollo de aplicaciones". Una vez hecho esto, el servicio que hemos creado aquí se iniciará automáticamente cuando ejecutemos un contenedor Docker más adelante. Entonces, comencemos con la instalación y configuración de las otras herramientas necesarias para configurar el proceso de integración continua.
Operaciones de aplicaciones y sistemas
En primer lugar, necesitamos un servidor Linux limpio en el que configurar la herramienta Jenkins CI. Tenga en cuenta que las siguientes instrucciones son específicas para Ubuntu 14.04. Tenga en cuenta que las instrucciones pueden diferir ligeramente para otras distribuciones de Linux. La versión de Jenkins utilizada es la 2.7.1 y las pantallas y las instrucciones pueden diferir ligeramente según la versión de Jenkins que se utilice.

Entonces, vamos a la consola de nuestro servidor Linux y comenzamos a instalar los requisitos previos.
Requisito previo de JDK
Necesitamos tener un JDK instalado. Las siguientes son instrucciones para instalar JDK8.
sudo add-apt-repository ppa:webupd8team/java sudo apt-get install python-software-properties sudo apt-get update sudo apt-get install oracle-java8-installer java -version
Instalar ventana acoplable
Para que Jenkins pueda activar las compilaciones de Docker, debemos instalar docker-engine
de la siguiente manera:
sudo apt-get install apt-transport-https ca-certificates sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D #create a docker list file sudo vi /etc/apt/sources.list.d/docker.list #add the following entry in the docker.list file (change trusty to #the release you are running on if you are running on different, ie. #xenial, precise...): deb https://apt.dockerproject.org/repo ubuntu-trusty main #save and exit the file sudo apt-get update apt-cache policy docker-engine sudo apt-get install docker-engine
Como ahora hemos instalado el motor de Docker, con el siguiente comando iniciaremos una imagen de Docker hello-world
para confirmar que Docker funciona correctamente.
sudo docker run hello-world
La salida de la imagen Hello-world
tendrá el siguiente aspecto y, con esto, podemos confirmar que el motor funciona correctamente.
Instale la interfaz de línea de comandos (CLI) de AWS
A continuación, instalaremos la CLI de AWS. Más adelante, en la configuración del trabajo de Jenkins, usaremos la CLI para ejecutar comandos para la autenticación de AWS y la inserción de imágenes de Docker en el registro de contenedores de Amazon EC2.
Para instalar la CLI de AWS, seguimos las pautas descritas en los detalles de la documentación de la CLI de Amazon.
De las dos opciones de instalación, elegiremos la instalación mediante Pip, un sistema de gestión de paquetes utilizado para instalar y gestionar programas de Python. Instalaremos Pip y AWS CLI simplemente ejecutando los siguientes tres comandos:
#install Python version 2.7 if it was not already installed during the JDK #prerequisite installation sudo apt-get install python2.7 #install Pip package management for python sudo apt-get install python-pip #install AWS CLI sudo pip install awscli
ECR de AWS
Como último paso del proceso de compilación, enviaremos nuestra imagen de Docker al registro de contenedores de Amazon. En la consola de servicios web de Amazon, encontramos el Servicio de contenedor AWS EC2.
Seleccionamos el submenú Repositorios de la izquierda y pulsamos en Comenzar .
Luego se nos presenta la primera pantalla para configurar el repositorio donde ingresamos el nombre del repositorio y hacemos clic en el botón Siguiente paso .
Al hacer clic en Siguiente paso , se nos muestra una pantalla con instrucciones sobre cómo enviar imágenes al repositorio.
Se nos presenta un ejemplo de cómo crear y enviar una imagen de Docker al registro, pero no necesitamos preocuparnos por esto ahora. Con esto, hemos creado un repositorio.
Instalar y configurar Jenkins
Para instalar Jenkins, ingresamos los siguientes comandos en el shell:
#Download Jenkins key and pipe it to apt-key tool, apt-key command #add will read from input stream, as defined by „–„. When added #apt will be able to authenticate package to be installed. wget -q -O - https://jenkins-ci.org/debian/jenkins-ci.org.key | sudo apt-key add - #create a sources list for jenkins sudo sh -c 'echo deb http://pkg.jenkins-ci.org/debian-stable binary/ > /etc/apt/sources.list.d/jenkins.list' #update your local package list sudo apt-get update #install jenkins sudo apt-get install jenkins
Cuando finaliza la instalación, Jenkins se inicia automáticamente. Verifique el estado del servicio con el siguiente comando:
sudo service jenkins status
Jenkins se conectará al repositorio Bitbucket Git y, para hacerlo, debemos instalar Git.
#install Git sudo apt-get install git
Jenkins activará el proceso de compilación de Gradle, durante el cual se creará una imagen de Docker. Para poder hacer esto, el usuario de Jenkins debe agregarse al grupo de usuarios de docker
:
#add Jenkins user to docker user group sudo usermod -aG docker jenkins
Durante el proceso de compilación, Jenkins enviará imágenes de Docker a Amazon ECR. Para habilitar esto, necesitamos configurar AWS para el usuario de Jenkins.
Primero, necesitamos cambiar al usuario jenkins
. Para hacerlo, necesitamos establecer una contraseña.
#change Jenkins password sudo passwd jenkins #switch to Jenkins user su – jenkins #configure AWS aws configure
Después de ingresar el comando aws configure
, comenzamos a ingresar la clave de acceso secreta generada y el ID de la clave (estas son las credenciales que generamos anteriormente en el proceso). En mi caso, la región de la cuenta es us-west-2
, así que la ingreso. También establecemos que el formato de salida predeterminado para los comandos de AWS sea JSON.
Ahora podemos pasar a configurar Jenkins a través de la consola web accesible en el puerto 8080.
Cuando accedemos a la URL, se nos presenta la siguiente pantalla de Inicio .
Como se indica en la pantalla, necesitamos ingresar la contraseña. Una vez hecho esto, el asistente de configuración nos indica que hagamos lo siguiente:
- Elija qué complementos instalar; elegiremos Instalar complementos sugeridos .
- Cree el primer usuario administrador ingresando las credenciales de usuario
Cuando haya terminado, haga clic en Guardar y finalizar . Con esto, hemos terminado la configuración de configuración de Jenkins.
Antes de comenzar a definir el trabajo de compilación, debemos agregar algunos complementos adicionales. Iremos a Administrar Jenkins y pulsaremos en Administrar complementos . En la pestaña Disponible , primero encontramos el complemento de Bitbucket , marcamos la casilla y hacemos clic en Descargar e instalar después de reiniciar .
Luego se le presentará algo como la siguiente pantalla.
Después de instalar el complemento, repetimos el proceso para los siguientes complementos adicionales que se necesitarán para configurar el trabajo:
- Complemento Gradle
- Complemento de paso de compilación de Docker
- Complemento de entorno de compilación personalizado Cloudbees Docker
- Complemento Amazon ECR
El complemento de paso de compilación de Docker que usamos enviará solicitudes al demonio de Docker. Para ello, debemos habilitar el socket TCP en el puerto 2375. Para ello, ingresamos al archivo de configuración de Docker ubicado en etc/default/docker
.
sudo vi /etc/default/docker
Aquí añadimos la siguiente línea en la configuración:
DOCKER_OPTS='-H tcp://127.0.0.1:2375 -H unix:///var/run/docker.sock'
Guardamos y salimos del archivo y reiniciamos el servicio Docker y Jenkins.
sudo service docker restart sudo service jenkins restart
Luego de que Jenkins se reinicie, vamos a la consola de Jenkins y, desde Administrar Jenkins , elegimos Configurar Sistema .
Encontramos la sección del generador de Docker e ingresamos http://localhost:2375
para la URL de la API REST, hacemos clic en Aplicar para confirmar el cambio. Luego hacemos clic en Probar conexión para confirmar que todo está bien.
Guardamos la configuración y procedemos a la configuración del trabajo de Jenkins.
Configuración del trabajo
Vamos a la página de inicio de Jenkins y creamos un New Item .
Elegimos un proyecto de Freestyle e ingresamos el nombre del proyecto como se muestra en la siguiente pantalla:
Al hacer clic en Aceptar , se nos presenta la página de configuración del trabajo. Queremos que el proyecto se construya en cada envío a nuestro repositorio de Bitbucket Git. Para lograr esto, primero debemos definir el repositorio al que nos estamos conectando.
Paso 1: Gestión del código fuente
En administración de código fuente, elegimos Git e ingresamos la URL de nuestro repositorio de Bitbucket. La URL tiene la forma de https://bitbucket.org/bitbucketUsername/repositoryName
.
Después de ingresar la URL, Jenkins intentará probar la conexión automáticamente. Como aún no ingresamos las credenciales, mostrará un error que indica que no se puede conectar.
Abra la lista desplegable Agregar y haga clic en Credenciales del proveedor de Jenkins .
Se nos presenta la siguiente pantalla, donde ingresamos el usuario y contraseña de nuestra cuenta de Bitbucket.
Después de agregar el nuevo registro de credenciales, nos aseguramos de seleccionarlo en el menú desplegable de credenciales, y esto finaliza la configuración de la administración del código fuente .
Paso 2: Genera disparadores
Compruebe las compilaciones de Trigger de forma remota y defina un token de autenticación. Asegúrese de definir un token aleatorio y seguro.
Paso 3: Webhook de Bitbucket
Jenkins ya nos proporcionó la URL que usaremos en Bitbucket. Vamos a nuestra página de repositorio de Bitbucket y, en el menú de configuración, pulsamos en Webhooks . Posteriormente al hacer clic en Agregar webhook nos presenta la siguiente pantalla, la cual rellenamos de la siguiente manera:
La URL tiene la siguiente estructura: http://JENKINS_URL _HOST:PORT/job/JOB_NAME/build?token=TOKEN
.
Ingrese los valores anteriores respectivamente con la URL de Jenkins, el puerto en el que se ejecuta, el nombre del trabajo que ha creado y el token que ha definido previamente.
Después de guardar el Webhook, se le proporcionará la siguiente pantalla, que puede editar si es necesario, o ver las solicitudes generadas cada vez que insertamos código nuevo.
Con esta configuración, el webhook se activa en cada inserción de repositorio, independientemente de la rama. En el lado de Jenkins, podemos definir qué impulso de rama activará la compilación.
Para que Bitbucket pueda enviar código a Jenkins, debemos reconfigurar la seguridad global de Jenkins para permitir el acceso de lectura anónimo. Además, para nuestra configuración, tenemos que deshabilitar la opción predeterminada de Jenkins que evita la falsificación de solicitudes entre sitios. Para hacer esto, vaya a Administrar Jenkins y elija Configurar seguridad global . Marque Permitir acceso de lectura anónimo y marque Evitar ataques de falsificación entre sitios . Luego guarde la configuración.
Tenga en cuenta que esto solo se hace por razones de simplicidad. La configuración completa supera la cobertura de este tutorial e incluiría una mayor seguridad de Jenkins detrás de un proxy inverso, en una conexión TLS y la habilitación de la prevención de CSRF.
Paso 4: Compilación de Gradle
Ahora podemos volver al trabajo de Jenkins y continuar configurándolo. En la sección de compilación, agregamos un paso de compilación: Invoke gradle script .
En este formulario ingresamos lo siguiente:
Como se muestra en la pantalla, usaremos el envoltorio de Gradle, una característica conveniente de Gradle que no requiere que tenga Gradle instalado en el host. Asegúrese de marcar la casilla Hacer ejecutable gradlew .
En las tareas, especificamos build
y buildDocker
.
Paso 5: Imagen de etiqueta de Docker
Esta parte de la compilación etiqueta una imagen de Docker preparada previamente por la tarea dockerBuild de dockerBuild
. Para esto, agregamos un nuevo paso de compilación al trabajo: Execute Docker command . Elegimos el comando Etiquetar imagen y establecemos el nombre de la imagen, el repositorio de destino donde enviaremos la imagen y la etiqueta:
Paso 6: Docker Push a Amazon ECR
Por último, debemos definir cómo enviar nuestra imagen a Amazon ECR. Para esto, agregamos un nuevo paso de compilación Ejecutar shell y configuramos los comandos para autenticar en AWS y enviar la imagen a Amazon ECR:
#region for our account is us-west-2 aws ecr get-login --region us-west-2 | bash #push the previously tagged image docker push 058432294874.dkr.ecr.us-west-2.amazonaws.com/springbootdocker:${BUILD_NUMBER}
Con esto, hemos terminado nuestro proceso de construcción. Después de insertar un nuevo código en el repositorio, este trabajo se activará y tendremos una nueva imagen de Docker cargada en el registro de Docker "automágicamente".
Luego, la imagen se puede llevar a cualquier lugar donde tengamos docker-engine
instalado y se puede ejecutar con el siguiente comando:
docker run -p 8080:8080 amazonRepository/springbootdocker
Este comando iniciará nuestro microservicio Spring Boot, con los siguientes puntos finales para cargar y descargar nuestros archivos al depósito S3:
-
http://hostnameURL:8080/api/storage/upload
-
http://hostnameURL:8080/api/storage/download?fileName=xyz
Más pasos con Java y la integración continua
Siempre hay más cosas que hacer. Se ha cubierto mucho terreno en este tutorial, pero lo consideraría solo un punto de partida desde el cual aprender más. Poner a Jenkins detrás de un servidor proxy web, como Nginx, y establecer una conexión TLS, son solo dos ejemplos de lo que se podría y podría decirse que se debería hacer más.
Nuestra imagen de Docker está disponible en Amazon ECR y lista para su implementación. Ahora podemos tomarlo y desplegarlo manualmente. Sin embargo, una mejor solución sería automatizarlo aún más. CI es solo el primer paso, y el próximo paso es la entrega continua. ¿Qué pasa con una alta disponibilidad? Amazon AWS EC2 proporciona funciones para registrar contenedores en la nube en un entorno agrupado que es obligatorio para el servicio basado en producción. Puede encontrar un buen ejemplo práctico de desarrollo de un proceso de entrega continua en la siguiente publicación de blog de AWS.
Conclusión
En general, hemos puesto en marcha un proceso de desarrollo de software fluido y limpio. Utilizando las herramientas disponibles, hemos creado una infraestructura que ayuda a maximizar nuestra productividad. Ahora, no tenemos que preocuparnos por la configuración de nuestro servicio Java, que es un servicio web simple con un extremo REST. Dejamos que la convención Spring Boot se encargue de todo y nos centremos solo en la lógica del servicio. Utilizamos Jenkins para crear una nueva imagen de Docker cada vez que insertamos nuestro código en nuestro repositorio Bitbucket Git y, al final, configuramos la nube para que se encargue de almacenar nuestras imágenes y archivos de Docker. Cuando implementamos nuestro servicio contenido dentro de una imagen de Docker, estaremos libres de cualquier restricción del sistema operativo (siempre y cuando el sistema operativo tenga instalado un docker-engine
).