====== 3. Despliegue Automatizado ======
===== Despliegue Automatizado =====
En el tema anterior hemos visto como desplegar manualmente en los servidores Apache y Nginx. En este tema vamos a ver como hacer lo mismo pero de forma automatizada. Automatizar tareas es fundamental ya que se minimizan los errores que se cometen. Por otro lado el despliegue puede llegar a hacerse decenas de veces al día, por lo que obviamente debe estar automatizado.
Podemos pensar que instalar (desplegar) decenas de veces al día una aplicación es un poco exagerado pero en sitios web como Amazon ésto si que tiene sentido.
Si en la aplicación de amazón hay un error que suele afectar al 0,1% de sus ventas. ¿Cuanto dinero pierden diariamente por tener ese error? Y en este tipo de aplicaciones se suelen estar constantemente realizando cambios y corrección de error , así que ¿porque no desplegarla en cuanto está corregido un error o añadida la nueva funcionalidad? Esto en la literatura informática se llama **Continuous delivery**
===== Estructura de carpetas =====
Para poder automatizar el despliegue , lo primero es tener una estructura definida de nuestro proyecto y de nuestro servidor. Durante el curso vamos a usar este esquema tanto en este módulo como en los otros módulos. El esquema he intentado que sea lo mas fiel posible a la realidad de las empresas aunque he intentado simplificarlo. Sin embargo cada empresa tendrá la estructura que se adecue al proyecto que estén desarrollando.
* Estructura de carpetas.
/mi-proyecto
/src //Donde está todo el código fuente de la aplicación
/main
/java
/com
/daw
/peliculas
Main.java
/resources
/static
index.html
/assets
/img
/fonts
/media //Audios y Videos
/vendor //librerías bajadas de internet (también se puede llamar lib)
/jquery
/boostrap
/scss //Código en SASS
/css //código en CSS
/ts //Código en TypeScript
/js //código en JavaScript
/docs //Documentación de la aplicación. Es la documentación con los diagramas UML, el JavaDoc, etc.
/disenyo //Donde están las imágenes en formato Photshop o similares que estamos visualmente diseñando
/db //Ficheros ".sql" de creación de la base de datos, etc. Es todo lo necesario para tener la base de datos preparada desde cero. Incluye insertar datos que siemrpe deben estar , como por ejemplo códigos de provincias. Pero NO las inserciones de los datos del usuario.
/scripts //Scripts varios. Normalmente están relacionados con el despliegue de la aplicación. Es lo mas importante en este módulo de despliegue.
deploy.sh
build.sh
/node_modules //Carpeta que usa node para guardar lo que instalamos de NodeJS
/target //El código "compilado" de la aplicación. Eso incluye transformar de TypeScript a JavaScript o de SASS a CSS,etc.
AppPeliculas-0.0.1-SNAPSHOT.jar
package.json
Hay que fijarse que en git no se debe guardar únicamente la carpeta "src" del código fuente sino que debe guardarse la carpeta "mi-proyecto" completa. Aunque se puede hacer alguna excepción con "dist" o "node-modules". El motivo de guardar la carpeta "mi-proyecto" es debido a que realmente todas esas carpetas son las que forman el proyecto, y no únicamente la carpeta "src".
===== Carpeta target =====
La carpeta ''target'' es la mas importante para el módulo de despliegue ya que en ella estará el proyecto tal y como se va a desplegar.
En otros IDE's como NetBeans , esta carpeta puede llamarse ''dist''
Podemos pensar que debe ser únicamente una copia de la carpeta "src" ya que el código JavaScript pero si que se pueden hacer muchos cambios en esta carpeta. Veamos algunos ejemplos:
* Compilar el código fuente Java a el compilado en los ficheros ''.class''
* Minimizar el código: Hacer que ocupe menos el código JavaScript o CSS. Se hace para que las páginas web se carguen antes. Y suele consistir en quitar los comentarios, los retornos de carro, etc.
* Ofuscar el código: Para evitar que alguien pueda copiar nuestro código se suele, por ejemplo , cambiar el nombre de las variables por nombres sin sentido. Normalmente se minimiza y se ofusca a la vez.
* Transpilar: Es transformar el código fuente de un lenguaje en otro. Se usa:
* TypeScript a JavaScript: TypeScript es un lenguaje con mayor tipado que JavaScript y se suele usar en vez de JavaScript. Como los navegadores no soportan TypeScript hay que convertirlo (transpilarlo) de TypeScript a JavaScript.
* SASS a CSS: SASS es un lenguaje similar a CSS pero con mayores funcionalidades. Como los navegadores no soportan SASS hay que convertirlo (transpilarlo) de SASS a CSS. En SASS también se suele decir transformar el código de SASS a CSS en vez de decir "transpilar"
* Etc.
Por ello , por ejemplo, en la carpeta "src" estaría el código en TypeScript y en la carpeta "target" estaría en JavaScript correspondiente al TypeScript.
===== Tareas de Automatización del despliegue =====
Veamos ahora un ejemplo de como desplegar una aplicación web en Java con SASS , TypeScript y que se despliega en Apache.
* Build (en maven se llama package)
* Copiar el código de "src" en "target"
* Compilar el código Java
* Transpilar el código SASS en CSS dentro de la carpeta "target"
* Transpilar el código TypeScript en JavaScript dentro de la carpeta "target"
* Borrar el código SASS de la carpeta "target"
* Borrar el código TypeScript de la carpeta "target"
* Generar el ''.jar''
* Desplegar
* Copiar el ''.jar'' de la carpeta "target" al servidor donde se va a ejecutar o copiar el ''.war'' en el Tomcat
* Ejecutar
* Ejecutar el ''.jar''
Para hacer todas esta tareas vamos a usar Scripts del sistema operativo , por ejemplo en Linux se usará "Bash" y los Script de npm que vamos a ver mas adelante.
==== Transformar SASS en CSS ====
* Instalar SASS
npm install sass -g
* Transpilar SASS en CSS
sass ./scss/main.scss ./css/main.css
==== Transformar TypeScript en JavaScript ====
* Instalar TypeScript
npm install typescript -g
* Transpilar TypeScript en JavaScript
tsc --outDir ./js ./ts/*.ts
=== Generar HTML con los issues ===
Para generar un HTML a partir de los los de git se puede usar este script llamado ''generar_html_from_log.sh''
#!/bin/bash
# Verificar que se pasó un archivo como argumento
if [ $# -ne 3 ]; then
echo "Uso: $0 usuario_de_git repositorio_de_git archivo_de_salida"
exit 1
fi
# Usuario y repositorio de Git para el enlace de los commits e issues
git_user="$1"
git_repository="$2"
# Archivo de salida
output_file="$3"
# Crear el encabezado HTML
echo "" > "$output_file"
echo "Tabla generada" >> "$output_file"
echo "" >> "$output_file"
echo "" >> "$output_file"
echo "Hash | Fecha | Autor | Tipo | Issue | Descripción |
" >> "$output_file"
# Leer el archivo de texto línea por línea
git --no-pager log --pretty=tformat:"%h|%cd|%cn|%s" --date=format:"%Y-%m-%d" | grep -E "\(\#[0-9]*\)\:" | while IFS='|' read -r campo1 campo2 campo3 campo4; do
# Separar el cuarto campo en tres partes: prefijo, número y texto
prefijo=$(echo "$campo4" | sed -E 's/^([^:]+)\(#[0-9]+\):.+/\1/') # Extrae 'fix o feat, etc'
numero=$(echo "$campo4" | sed -E 's/^[^(]+\(#([0-9]+)\):.+/\1/') # Extrae 'número del issue'
descripcion=$(echo "$campo4" | sed -E 's/^[^:]+:\s*(.+)/\1/') # Extrae 'nombre del issue'
# Añadir una fila a la tabla HTML
echo " " >> "$output_file"
echo " $campo1 | " >> "$output_file"
echo " $campo2 | " >> "$output_file"
echo " $campo3 | " >> "$output_file"
echo " $prefijo | " >> "$output_file"
echo " #$numero | " >> "$output_file"
echo " $descripcion | " >> "$output_file"
echo "
" >> "$output_file"
done
# Cerrar la tabla y el HTML
echo "
" >> "$output_file"
echo "" >> "$output_file"
echo "" >> "$output_file"
echo "Archivo HTML generado: $output_file"
* Y se ejecuta así:
./generar_html_from_log.sh my_usuario_git mi_repositio log_git.html
Un ejemplo similar del servidor Tomcat se puede ver en [[https://tomcat.apache.org/tomcat-10.1-doc/changelog.html|Apache Tomcat 10 Changelog]]
====== Scripts npm ======
Para hacer las tareas que acabamos de indicar, se harán scripts de Bash que se dejarán en la carpeta "/scripts" sin embargo ahí puede haber gran cantidad de Script o podemos querer usar otra carpeta u otro lenguaje. Para unificar todo esto en los proyectos web, vamos a homogeneizar la ejecución de estos scripts mediante los Scripts de npm.
En los siguientes artículos se explican los scripts npm:
* [[https://docs.npmjs.com/cli/v9/using-npm/scripts|npm scripts]]
* [[https://css-tricks.com/why-npm-scripts/|Why npm scripts]]
* [[http://lucasmreis.github.io/blog/npm-is-an-amazing-build-tool/|NPM is an amazing build tool]]
Veamos ahora un ejemplo de Script de transformar SASS a CSS.
* Crear un Script para node dentro del **packaje.json**
"scripts": {
"compile-scss": "sass ./scss/main.scss ./css/main.css"
}
* Ejecutar un Script de node.
npm run compile-scss
¿Que hemos ganado con ésto? Que ahora todas las "tareas" que tenemos que hacer en el proyecto se pueden ver en el "packaje.json" y siempre se ejecutarán como "**npm run nombre-tarea**"
==== Ejecutar un Script cuando algo cambia ====
[[https://www.npmjs.com/package/onchange|onchange]] permite ejecutar un comando cuando cambia algún fichero.
* Instalar onchange
npm install onchange -g
* Mostrar un mensaje cada vez que cambia algo en un fichero JavaScript
onchange '**/*.js' -- echo Ha cambiado un fichero JavaScript
===== Maven =====
Ahora deberemos llamar a nuestro script de ''npm run build'' desde maven. Usaremos el plugin de ''exec-maven-plugin''.
Para ello añadiremos el siguiente XML al ''pom.xml''
org.codehaus.mojo
exec-maven-plugin
3.0.0
build-cliente
compile
exec
npm
${project.basedir}
run
build
* Para ejecutar Maven usaremos:
mvn clean install
Más información:
* [[https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html|Introduction to the Build Lifecycle]]
===== Copiar ficheros por scp =====
Para copiar ficheros locales a un servidor remoto se usa la sintaxis:
scp /mi_carpeta_local/mi_fichero mi_usuario_remoto@ip_remota:/mi_carpeta_remota
* Ejemplo para copiar el fichero local ''/home/logongas/app.jar'' en la máquina remota ''192.168.1.1'' en la carpeta ''/opt'' usando el usuario remoto ''logongas'':
scp /home/logongas/app.jar logongas@192.168.1.1:/opt
Para copiar ficheros remoto en una carpeta local se usa la sintaxis:
scp mi_usuario_remoto@ip_remota:/mi_carpeta_remota/mi_fichero /mi_carpeta_local
* Ejemplo para copiar el fichero remoto ''app.jar'' que está en ''/opt'' en la máquina remota ''192.168.1.1'' y guardarlo en nuestra carpeta local ''/home/logongas'' usando el usuario remoto ''logongas'':
scp logongas@192.168.1.1:/opt/app.jar /home/logongas
===== Ejecutar ordenes en servidores remotos =====
Para ejecutar ordenes en ordenadores remoto se usa la sintaxis:
ssh mi_usuario_remoto@ip_remota "comando"
* Ejemplo de crear el directorio ''/home/logongas/miapp'' en la máquina remota ''192.168.1.1'' usando el usuario remoto ''logongas'':
ssh logongas@192.168.1.1 "mkdir /home/logongas/miapp"
===== Scripts necesarios =====
Veamos ahora los script que deberíamos siempre tener en cualquier proyecto.
"scripts": {
"build": ".\\scripts\\build.sh",
"deploy": "mvn clean install && .\\scripts\\deploy.sh"
}
* build: Compila el código de **cliente** de ''src'' y lo deja en ''tarjet''. Eso incluye compilar TypeScript, el SASS, etc.
npm run build
* deploy: Despliega la aplicación en un servidor
npm run deploy
Por lo tanto todo queda de la siguiente forma:
* Para compilar **TODO** el código:
mvn clean install
* Para desplegara la aplicación:
npm run deploy
===== Variables de entorno =====
Es posible personalizar los scripts en Bash usando variables de entorno.
* Ejemplo de declaración de variables
USER_NAME=logongas
IP_SERVER=192.168.1.1
Para usar las variables se usa ''${}'' y dentro de las llaves el nombre de la variable. Tambien se puede usar solo ''$'' delante del nombre de la variable.
* Ejemplo de uso de las variables
scp /home/logongas/app.jar ${USER_NAME}@$IP_SERVER:/opt
Podemos tener en un fichero externo las variables.
* Ejemplo de ''config.env''
USER_NAME=logongas
IP_SERVER=192.168.1.1
Para cargar un fichero de variables se usa el comando ''source''.
* Ejemplo de cargar las variables de ''config.env''
source config.env
===== Parámetros del Script =====
Los Script de bash pueden tener parámetros, para acceder a ellos se usa ''$1'', ''$2'', ''$3'', etc. siendo cada uno de ellos la posición del parámetro.
* Ejemplo de script con 1 parámetro
./deploy.sh config.env
* Continuando con el ejemplo se usar ''$1'' para acceder al parámetro
echo El primera parámetro es $1
source $1
===== Interpretar las Variables de Bash =====
Al pasar argumentos a un script se puede hacer de varias maneras.
* Imaginemos el script ''test.sh'', que muestra por pantalla el primer argumento
#!/bin/bash
echo $1
Y ahora lo vamos a ejecutar de 3 formas distintas:
a=Hola
b=Adios
./test.sh $a $b
./test.sh '$a $b'
./test.sh "$a $b"
./test.sh "$a \$b"
¿Va a mostrar siempre ''Hola''?
Veamos el resultado:
Hola
$a $b
Hola Adios
Hola $b
¿Qué es lo que ha pasado?
^ Parametros ^ Resultados ^ Explicación ^
| ''$a $b'' | ''Hola'' | Lo toma como 2 argumentos separados y solo muestra ''Hola'' porque solo se muestra el primer argumento |
| ''%%'%%$a $b%%'%%'' | ''$a $b'' | Todo ahora es un único argumento, pero con la peculiaridad de que **no** interpreta las variables (y otras cosas) de BASH |
| ''%%"%%$a $b%%"%%'' | ''Hola Adios'' | Todo ahora es un único argumento, pero si que se interpretan las variables de bash |
| ''%%"%%$a \$b%%"%%'' | ''Hola $b'' | Todo ahora es un único argumento, pero si que se interpretan las variables de bash aunque no se evalúa la variable ''$b'' ya que está escapada con ''\''. |
===== Instalar Java JRE =====
Vamos a ver como instalar y usar el JRE:
Para instalar el JRE:
* Descargar el fichero tar.gz desde [[https://adoptium.net/es/temurin/releases/?os=linux&arch=x64&package=jre|Temurin download]]
* Descomprime el fichero ''tar.gz'' en la carpeta actual y se creará una carpeta con el JRE:
tar -xf OpenJDK21U-jre_x64_linux_hotspot_21.0.5_11.tar -C .
Comprobar que todo está instalado:
* Crea la variable de entorno ''JAVA_HOME'':
export JAVA_HOME=$(pwd)/jdk-21.0.5+11-jre
* Muestra la versión de java con:
$JAVA_HOME/bin/java -version
Ejecutar un jar:
export JAVA_HOME=$(pwd)/jdk-21.0.5+11-jre
$JAVA_HOME/bin/java -jar mi_app.jar
El nombre del fichero ''OpenJDK21U-jre_x64_linux_hotspot_21.0.5_11.tar.gz'' y de la carpeta ''jdk-21.0.5+11-jre'' será distintas según la versión de java que estés usando.
Si en GitHub no deja subir el ''OpenJDK21U-jre_x64_linux_hotspot_21.0.5_11.tar.gz'' porque es un fichero demasiado grande, se puede partir en pequeños ficheros.
* Para dividir un fichero en trozos se usa esta orden:
split --bytes=45m nombre_fichero_grande nombre_fichero_grande.trozo_
Que generará una lista de ficheros del estilo:
nombre_fichero_grande.trozo_aa
nombre_fichero_grande.trozo_ab
nombre_fichero_grande.trozo_ac
...
Etc.
* Para unir los trozos se usará la orden:
cat nombre_fichero_grande.trozo_* > nombre_fichero_grande
===== Cortafuegos =====
Para que funcione la aplicación, es necesario abrir los puertos. Para ello se usa la orden ''ufw allow numero_puerto''.
* Ejemplo para abrir el puerto ''80''
ufw allow 80
Para permitir que un usuario pueda gestionar el cortafuegos se debe ejecutar la orden:
sudo visudo
Y añadir una linea.
* Ejemplo a añadir para el usuario ''logongas''
logongas ALL=(ALL) NOPASSWD: /usr/sbin/ufw
===== Conexión con claves pública/privada =====
Para conectarse a un servidor, ya no se usan contraseñas sino que se crear pares de claves (una pública y otra privada).
Para generar el par de claves se usa la orden ''ssh-keygen''.
* Generar el par de clave con formato RSA con una logitud de 4096 bits, con el correo ''juan@alumno.fpmislata.com'' y que se genera el par de claves en los ficheros ''$HOME/.ssh/id_rsa'' y ''$HOME/.ssh/id_rsa.pub''
ssh-keygen -f $HOME/.ssh/id_rsa -t rsa -b 4096 -C "juan@alumno.fpmislata.com"
Esa orden genera 2 ficheros:
* ''$HOME/.ssh/id_rsa'': Es la clave privada
* ''$HOME/.ssh/id_rsa.pub'': Es la clave pública que hay que copiar en el servidor al que nos queremos conectar
* Ejemplo de Contenido de los ficheros
-----BEGIN OPENSSH PRIVATE KEY-----
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
-----END OPENSSH PRIVATE KEY-----
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC9aN2Srvty5Yc+GmV7JBTxDS4OYuqCjsJc0i09u58ULxlnmWOn2awvEv3HQfCNEukhzQl0kdvN/lNHmrswE8SqnbRuqbqCOE4Hntzx3aMeypc2SHN/v88VzUnnRrz++3EDq90d0WAStRRb2IH2k1WT56UdSr32ovf1Wd3JdMvoI/nojEHJl/j9DsT4jXkp/iYSknLlvpJvqH0pjcKD1NlkZQAO+5n/pVPVJtJFwd0wvaWhlugdv0JnzXKNiuTvpdvbpVx6ynPVn59XhGW6wOne6ou6umDG4AdEfi8QGgMCrjibw8uIs6qTCOKOuw0N6+Hiw3TmeB3rLtsGCqwPTvhcmi6NzV/9v1NPUrBZ+MauoJKw4FPNi3YNZ1a4MLb9mF/KOlwQn9/5a7omYkxGpPMIbFQcQVN6JsBE7Fr+6qT/LCGdqEasCU5hTIkF0CqZJ5SxTtjhQ3N3lmgZoBHDktQKM6LElO/w/Bt1pio4OZqcQoW71gpNQ5r8fJvwN/XpajEeXVqFDqZYzB8UQ74zaplUJMk77ixUzChZq8FL0zBuie4Bht+vDPMJBUbo+YG6zSdauVnb+QzRGeRqLx9j336tKRpIIWDkQ78CQ1tGYsxEWEtVEVLl3EasT+v+/Y2IpfwAaaCiqwGqITG9lQ4gS1Q571J7c6J8/1Y21HhyGBiHnQ== juan@alumno.fpmislata.com
Ahora tenemos que ir al **servidor remoto** al que nos queremos conectar e ir a la carpeta del usuario al que nos queremos conectar.
Y añadir al fichero ''$HOME/.ssh/authorized_keys'' el contenido de nuestra clave **publica**
* En el siguiente ejemplo vemos que ya existe una clave y añadimos la nueva que acabamos de crear.
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDEq0zGyX+fP1av8Si9ywOhX2XaWKp/cUaLuFSp0mz0u8mAYoiKojOygBmwJgxwXIMtyHZBCV3ijJlyRaXOLcq+ii3NrPu1hZ7NQGqJRkMXiYcm8BUaJSajHzguOA01a9mE8rOcklmUtroFH9TcgH6BUSfNNnQJ7wZQ4EgQG6sdYODbd299FL8FC9xnfrCpNiuq0nt4qEUEoNeRHSDh8KQt02j0DqNYhKikeEMobATjuwTJQjNLACNWh+VXvv1hX12xV79voMoDwUVhPI4GlnOVUdz11ot3RK/j5kNT0IQtjU+iS4DfXeZ6UHvcPikH4zS/007bMf24TB2t/9sz7CXhd78JSpplLbh28EVR7YnqeKfzMqTGWMHpYCvPupjQpCwOAcdhPox5QU0yWu07QRU+efjH2GpUaIzB+RqcgV8ENgAj0Ua5RmjkAz0j4OrzttcIyEsdpJoYGiFw8g+6mR44gSLZ2d5nN+9B3x80pJwzc12UG5Pt/wPn0YI0s5YAJoHsV9dXd+q8xvc9m4M70pCLFfDL94k08Q+0WYQFQhpTl/n/0b4HyvwMK1pAA3CmCcXgfkRppjtsbHkF8nyBDhhXGa4ESEUUk0PlxEDyfmYUtfOjNvUSVZ/LByCZOqds0XWmyuDJq+EQAMwBAcwQtKAnhYnB59OHua+hHIw13suOaw== pedro@alumno.fpmislata.com
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC9aN2Srvty5Yc+GmV7JBTxDS4OYuqCjsJc0i09u58ULxlnmWOn2awvEv3HQfCNEukhzQl0kdvN/lNHmrswE8SqnbRuqbqCOE4Hntzx3aMeypc2SHN/v88VzUnnRrz++3EDq90d0WAStRRb2IH2k1WT56UdSr32ovf1Wd3JdMvoI/nojEHJl/j9DsT4jXkp/iYSknLlvpJvqH0pjcKD1NlkZQAO+5n/pVPVJtJFwd0wvaWhlugdv0JnzXKNiuTvpdvbpVx6ynPVn59XhGW6wOne6ou6umDG4AdEfi8QGgMCrjibw8uIs6qTCOKOuw0N6+Hiw3TmeB3rLtsGCqwPTvhcmi6NzV/9v1NPUrBZ+MauoJKw4FPNi3YNZ1a4MLb9mF/KOlwQn9/5a7omYkxGpPMIbFQcQVN6JsBE7Fr+6qT/LCGdqEasCU5hTIkF0CqZJ5SxTtjhQ3N3lmgZoBHDktQKM6LElO/w/Bt1pio4OZqcQoW71gpNQ5r8fJvwN/XpajEeXVqFDqZYzB8UQ74zaplUJMk77ixUzChZq8FL0zBuie4Bht+vDPMJBUbo+YG6zSdauVnb+QzRGeRqLx9j336tKRpIIWDkQ78CQ1tGYsxEWEtVEVLl3EasT+v+/Y2IpfwAaaCiqwGqITG9lQ4gS1Q571J7c6J8/1Y21HhyGBiHnQ== juan@alumno.fpmislata.com
Por último para conectarnos al servidor con ''ssh'' o ''scp'' deberemos indicar el fichero de la clave privada y para ello se usará el parámetro ''-i''.
* Ejemplo con ''ssh'' indicado que la clave privada a usar está en ''$HOME/.ssh/id_rsa''
ssh -i $HOME/.ssh/id_rsa logongas@192.168.1.1 "mkdir /home/logongas/miapp"
* Ejemplo con ''scp'' indicado que la clave privada a usar está en ''$HOME/.ssh/id_rsa''
scp -i $HOME/.ssh/id_rsa /home/logongas/app.jar admin@192.168.1.1:/opt
Si guardamos nuestro par de claves con los nombres de ''$HOME/.ssh/id_rsa'' y ''$HOME/.ssh/id_rsa.pub''
se usarán por defecto al conectarnos a cualquier servidor y no hará falta indicar que las usemos.
Por otro lado en el fichero ''$HOME/.ssh/config'' podemos especificar para cada servidor la clave que queremos usar.
Host servidor.ejemplo.com
User usuario
IdentityFile /ruta/de/mi_clave_privada
Por lo tanto las 3 reglas (en orden de prioridad) para saber que clave privada usar son:
- Se indica en el propio comando ''ssh'' o ''scp'' con ''-i''
- Se indica en el fichero ''$HOME/.ssh/config'' para cada servidor
- Se usa ''$HOME/.ssh/id_rsa''
- La clave añadida con ''ssh-agent'' (No explicada)
===== Ejercicios =====
==== Ejercicio 1.A ====
Crea un Script en bash llamado "build.sh" que:
* Compile el SASS de ''src'' en la carpeta ''css'' de ''src''
* Compile el SASS de ''tarjet'' en la carpeta ''css'' de ''tarjet'' pero sin poner espacio
npm run build
Se ejecute el Script "build.sh"
Haz que se ejecute ''npm run build'' cuando se lance ''install'' en Maven. Es decir al ejecutar
mvn clean install
==== Ejercicio 1.B ====
Modifica el proyecto anterior , añadiendo un Script en bash llamado "deploy.sh" que:
* Ejecute ''mvn clean install''
* Copie el ''jar'' a una máquina remota en Linux
* Ejecute el jar en dicha máquina remota en Linux
npm run deploy
==== Ejercicio 1.C ====
Modifica el proyecto anterior , crean en el script ''deploy.sh'' las siguientes variables y úsalas:
* ''IP_DEPLOY_SERVER''
* ''USER_NAME_DEPLOY_SERVER''
* ''APP_PORT''
Puedes crear más variables si las necesitas.
Ahora extrae esas variables al fichero ''pruebas.env'' y carga esas variables usando:
source pruebas.env
Para indicar en Spring el puerto a usar en la aplicación se pasa el valor por la línea de comandos con la opción ''-D'' y definiendo la variable ''server.port''
java -Dserver.port=${APP_PORT} -jar mi_app.jar
==== Ejercicio 1.D ====
Teniendo 3 servidores distintos donde desplegar:
* pruebas
* preproduccion
* produccion
Cada entorno tiene una IP, un usuario y un puerto distinto.
Crea 3 ficheros de variables para cada entorno en la carpeta ''entornos'':
* ''pruebas.env''
* ''preproduccion.env''
* ''produccion.env''
Ahora crea los siguientes scripts en el ''package.json''
{
"scripts": {
"deploy:pruebas": "mvn clean install && ./deploy.sh pruebas.env",
"deploy:preproduccion": "mvn clean install && ./deploy.sh preproduccion.env",
"deploy:produccion": "mvn clean install && ./deploy.sh produccion.env"
}
}
Y modifica ''deploy.sh'' de forma que el nombre del fichero de configuración sea el primer argumento del script
source $1
Por último despliega la aplicación en cada uno de los entornos ejecutando
* En el entorno de pruebas:
npm run deploy:pruebas
* En el entorno de preproducción:
npm run deploy:preproduccion
* En el entorno de producción:
npm run deploy:produccion
==== Ejercicio 1.E ====
Modifica el script de despliegue de forma que antes de ejecutar ''mvn clean build'' se cambie a la rama correspondiente de git para que se compile la versión correcta.
^ Entorno ^ Rama de Git a compilar ^
| Pruebas | ''develop'' |
| Preproduccion | ''release'' |
| Produccion | ''master'' |
Para ello modifica los ficheros ''.env'' añadiendo la varaible ''RAMA_GIT'' para indicar a que rama de debe mover el script
==== Ejercicio 1.F ====
Cambia ahora el script de despliegue para que en la máquina destino se copie tambien el JDK y se use ese JDK de has copiado.
Para ello tendrás que:
* Tener en local el JDK comprimido en la carpeta ''dependencies''
* Copiar el fichero del JDK en la máquina desino
* Descomprimir el fichero en la máquina desino
* Ejecutar el jar con el jdk que has copiado.
==== Ejercicio 1.G ====
Cambia ahora el script de despliegue para que en la máquina destino el cortafuegos abra el puerto que esté usando la app.
==== Ejercicio 1.H ====
Crea un par de claves publica/privada para acceder al servidor con esas claves en vez de con una contraseña.
Es decir que tanto las órdenes de ''ssh'' como las de ''scp'' usen la clave privada en vez de una contraseña.
==== Ejercicio 1.I ====
Ahora vamos a separar el proyecto en 2 repositorios.
* El proyecto de Java con todo el código excepto la parte de deploy. Este proyecto lo vamos a llamar ''Facturas''
* Otro proyecto con solo la parte necesaria para desplegar. Este otro proyecto lo vamos a llamar ''DesplegarFacturas''
Ahora deberás hacer lo siguiente:
* Crear 2 repositorios en GitHub para guardar los 2 proyectos.
* ''Facturas''
* ''DesplegarFacturas''
* En el proyecto de desplegar:
* Crea un nuevo fichero de variables de entorno con la URL del proyecto de ''Facturas''. Este fichero lo llamarás ''app.env'' y la variable se llamará ''URL_SOURCE_GIT''
* En el script de deploy.sh:
* Borrar la carpeta ''../Facturas''
* Descargar el código fuente de la app en ''../Facturas'' desde la URL ''${URL_SOURCE_GIT}''
* Cambiar a la rama de git adecuada
* Generar el ''AppFacturas-0.0.1-SNAPSHOT.jar'' (incluyendo generar el CSS)
* Copiar los siguientes ficheros en el servidor de despliegue
* ''OpenJDK21U-jre_x64_linux_hotspot_21.0.5_11.tar.gz''
* ''AppFacturas-0.0.1-SNAPSHOT.jar''
* ''server.sh''
* Borrar la carpeta ''../Facturas''
* Ejecutar en el servidor el script ''server.sh'' el cual hace lo siguiente:
* Descomprimir el ''OpenJDK21U-jre_x64_linux_hotspot_21.0.5_11.tar.gz''
* Abrir el puerto del cortafuegos
* Crear la variable de entorno ''JAVA_HOME''
* Ejecutar el ''AppFacturas-0.0.1-SNAPSHOT.jar'' indicando el puerto con la variable ''server.port''
Con todo ésto lo que hemos conseguido es que descargando únicamente el proyecto ''DesplegarFacturas'' y ejecutando ''npm run deploy:nombre_entorno'' podamos desplegar la aplicación.
Es decir que estará todo totalmente automatizado a excepción de las 2 siguiente órdenes:
* ''git clone http://github.com/usuariogit/DesplegarFacturas.git''
* ''npm run deploy:nombre_entorno''.
Aunque realmente aun no lo hemos automatizado todo ya que:
* Para ejecutar ''npm'' nos haría falta NodeJS
* Para compilar el código Java nos haría falta Maven
* Para compilar el código Java nos haría falta el JDK
* Para SASS nos haría falta el compilador de SASS
* Para TypeScript nos haría falta ek compilador de TypeScript
* Etc.
Veamos ahora un esquema de como quedarían los directorios más importante de cada máquina.
* Máquina local desde la que se despliega
/Facturas
/src
/main
/java
/resources
/static
index.html
/scss
main.scss
/css
main.css
/scripts
build.sh
/target
AppFacturas-0.0.1-SNAPSHOT.jar
package.json
pom.xml
/DesplegarFacturas
/certificados
id_rsa
id_rsa.pub
/global
app.env
/entornos
pruebas.env
preproduccion.env
produccion.env
/dependencies
OpenJDK21U-jre_x64_linux_hotspot_21.0.5_11.tar.gz
/scripts
deploy.sh
server.sh
package.json
* Maquina remota en la que se despliega
/home/alumno
/.ssh
authorized_keys
/nombreAlumno
/jdk-21.0.5+11-jre
/bin
java
OpenJDK21U-jre_x64_linux_hotspot_21.0.5_11.tar.gz
AppFacturas-0.0.1-SNAPSHOT.jar