A estas alturas de la película ya esta más que demostrado que los contenedores llegaron al mundo del desarrollo para quedarse.
En este artículo y en los que vendrán NO pretendo enseñar a usar contenedores "desde 0" junto con sus ventajas e inconvenientes, para eso ya existen mogollón de artículos, cursos y demás recursos en Internet que están geniales...y sino me creéis probar a buscar :-)
La idea es enfocar el artículo un poco diferente y tratar de ayudar a encontrar utilidades un poco más raras del uso de contenedores. Pensando que si a mi me han ayudado mejorar mi día a día en el trabajo quizás a vosotros también os puedan ayudar.
Por otro lado, se enseñarán / recordarán unas cuantas estrategias para conocer realmente lo que estamos utilizando dentro de un contenedor
Este artículo está dividido en 4 partes:
- 1. Introducción
- 2. Stack Tecnológico
- 3. Soporte de Análisis Básico de Contenedores
- 4. Ejemplo de Uso con Docker
- 5. Conclusiones
1. Introducción
Estos son los puntos que se tratarán en la introducción:
- 1.1. ¿Para qué podemos usar los contenedores?
- 1.2. Aclaración sobre el desarrollo con contenedores
1.1. ¿Para qué podemos usar los contenedores?
Con la aparición de los contenedores el mundo del desarrollo ha cambiado en muchos aspectos
Algunos de los aspectos que han cambiado:
Aspectos de Sistemas
- Disponer de productos "comerciales" instalados como contenedores
- Facilitar un laboratorio de pruebas rápidas
- Proporcionar un soporte y ayuda a la hora de instalar productos : plataformas, distribuciones, versiones, etc.
- Proporcionar un soporte y ayuda a la hora de realizar la configuración de productos
- Proporcionar un soporte y ayuda a la hora de integrar diferentes productos para que funcionen juntos
- Proporcionar un soporte y ayuda a la hora de integrar productos con aspectos del desarrollo: frameworks, etc.
- Facilitar la generación de scripts para automatizar lo anterior : instalaciones, configuración, uso , etc
- Proporcionar plataformas completas para CI / CD: instalaciones, configuraciones, integraciones, pipelines, plugins, etc.
- ...
Aspectos de Aprendizaje
- Proporcionar la infraestructura para ayudar o ser utilizados en los cursos
- Proporcionar un mecanismo para crear/destruir componentes de forma fácil y rápida durante el proceso de aprendizaje
- Proporcionar la infraestructura necesaria como "cajas negras" para poder centrar el aprendizaje en lo que realmente es necesario -> Aprendizaje "incisivo"
- Facilitar centrarse en un único punto de aprendizaje
- Facilita poder formarte en casi cualquier cosa
- Existen muchos recursos en Internet con proyectos oficiales y realizados por la comunidad
- Facilita diferentes niveles de aprendizaje : básico (existen ya piezas medio montadas) y avanzado (montando desde 0)
- ...
Aspectos de Desarrollo
- Proporcionar instalaciones de productos en un entorno local : servidor web, contenedor de servlet, base de datos , etc.
- Facilitar las pruebas de despliegue y uso de los desarrollos en entornos lo más parecidos que sean posibles a los entornos productivos
- Proporcionar plataformas de servicios/microservicios para desarrollas/probar las funcionalidades en un entorno local (Mockear capa de servicios para ser usados por front o por back para diseñar servicios complejos
- Facilitar el desarrollo y despliegue en entornos productivos
- ...
Aspectos Testing/QA
- Proporcionar instalaciones de productos para testing y QA
- Facilitar mecanismos para reducir los problemas que se puedan encontrar en otros entornos, con otros juegos de datos, con otras configuraciones, con otras tipologías de pruebas, etc.
- Mockear aquellas piezas o partes que pueden ser interesantes: productos, bases de datos, servicios, etc.
- ...
Existen muchas tecnologías de contenedores en el mercado y cada una tiene sus características particulares:
Por lo tanto, antes de trabajar con una de ellas conviene que tengas unos conocimientos mínimos.
1.2. Aclaración sobre el desarrollo con contenedores
Cuando desarrollamos con contenedores normalmente hacemos uso de imágenes que ya existen (oficiales o no oficiales) en los repositorios públicos de imágenes (Docker Hub, etc.) o nos basamos en el código que hacen otros desarrolladores en sus repositorios de código.
Lo que casi nunca se explica es la "letra pequeña" que tiene esta utilización y que obliga al cumplimiento de una serie de imposiciones :-)
Para ayudar a entender a que me refiero aclararé una serie de puntos :
Diagrama de generación de un Container para Docker
¿Qué es un archivo de configuración?
Un archivo de configuración para este contexto es un archivo que proporciona el contenido con el que queremos generar la imagen.
Cada tecnología de contenedor tiene su propia manera de entender este punto.
Suele tener una forma de codificación particular dependiendo de cada tecnología.
Para el caso de Docker este fichero se denomina Docker File.
Ejemplo de fichero Docker File para crear un contenedor de Tomcat :
FROM tomcat:8.5.56-jdk8-openjdk
RUN rm -rf /usr/local/tomcat/webapps/*
ADD tomcat-users.xml /usr/local/tomcat/conf/
ADD context.xml /usr/local/tomcat/webapps/manager/META-INF/
CMD ["catalina.sh","run"]
¿Qué es una imagen?
Una imagen es una "receta" o "plantilla" que representa la realidad del estado de un contenedor.
Se genera a partir de un archivo de configuración.
La realidad representada en la imagen de un contenedor puede representar diferentes casos :
- Representar el sistema operativo que se quiere utilizar
- Normalmente utilizado como imagen base para construir otras imágenes
- Incluye: librerías y paquetes necesarios
- Representar un entorno de desarrollo o despliegue (Java, .Net, etc.) y que funciona sobre un sistema operativo
- Incluye: librerías y configuraciones
- Represenar un producto específico (base de datos, servidor de aplicaciones, microservicio, etc.) que funciona sobre los anteriores
- Incluye : configuraciones
Se utilizan para crear nuevos contenedores.
¿Qué es un contenedor?
Un contenedor sería una instancia en ejecución de una imagen. Por lo tanto, se pueden crear varios contenedores a partir de una única imagen y conviene saber que todos los contenedores derivados de una imagen son idénticos -> Principio de Inmutabildiad
¿De qué se compone una imagen?
Cuando se crea una imagen normalmente se usa otra imagen como base / padre y sobre la imagen que se tiene acceso se pueden incorporar ciertos elementos de definición.
Aclaración
He considerado como elementos de definición : variables de entorno, configuraciones específicas, dependencias, instalación de componentes específicas, persistencia, comandos de ejecución, parámetros, volúmenes, etc.
Normalmente la primera capa se suele corresponder con un sistema operativo "básico" (Base OS Layer).
A la definición de una imagen por composición de otras es lo que denominamos arquitectura basada en capas o de "cebolla" (layered architecture), donde cada imagen engloba a las anteriores y, por tanto, tú defines lo de tu capa y heredas lo de las capas anteriores.
¿Cómo puedo "usar" una imagen?
Existen varios enfoques de uso de una imagen :
- Usar una imagen ya definida que cubra tus necesidades asumiendo las imposiciones que "alguien" ya ha establecido a nivel de elementos de definición y que habilitará una serie de opciones de customización permitidas.
- Crear una imagen extra (capa) que corrija, adapte y/o evolucione una imagen ya definida y así incorporar ciertos elementos de definición propios aportando la funcionalidad específica requerida.
- Crear una imagen "casi desde 0" partiendo de la imagen base y en la que se deciden los elementos de definición desde su inicio.
¿Problemas que aparecen al usar imágenes ya definidas?
El principal problema es que alguien ha tomado ciertas decisiones de desarrollo al definir ya los elementos de definición, por lo que puede existir problemas a nivel de configuración, funcionalidad no permitida / bloqueada, uso de dependencias antiguas, uso de versiones no estables de las cosas, problemas de seguridad, etc.
Propuestas para minimizar los problemas identificando y entendiendo:
- La documentación existente sobre la imagen "utilizada" donde se detalla información como : descripción, tag, uso, configuración, ejecución, etc. (por ejemplo la página que proporciona Docker Hub para cada imagen)
- Cada página tiene su propio formato de estructura de presentación de información (por lo que en algunos casos falta información)
- El archivo de configuración que representa la imagen a utilizar (si se puede y esta definido)
- Las variables de entorno utilizadas y definidas
- Los posibles scripts de ejecución utilizados
- Directorios utilizados (instalación, configuración, logs, etc.)
- Etc.
Se aconseja investigar previamente TODO lo anterior sobre todo cuando se tiene el contenedor en ejecución :-)
2. Stack Tecnológico
Este es stack tecnológico elegido para implementar el ejemplo de este artículo:
- Java 8
- Docker - Tecnología de Contenedores/Containers
- Docker Hub - Repositorio de Docker Público donde se ubican las imágenes oficiales
3. Soporte de Análisis Básico de Contenedores
Cuando se ejecuta un contenedor basado en una imagen ya creada se ha podido ver al principio del artículo qué consideraciones y qué problemáticas podemos llegar a encontrar si queremos cambiar alguna configuración etc.
Por regla general las tecnologías de contenedores proporcionan unas ciertas herramientas / utilidades básicas de base que permiten realizar comprobaciones o bien gestionar los propios componentes utilizados.
IMPORTANTE
Se aconseja que compruebes cuales son las herramientas / utilidades proporcionadas con tu tecnología de contenedores. Dedícalas un tiempecito hasta que las entiendas "bien" si luego las quieres sacar el máximo partido :-)
Además hay que pensar que algunas veces los comandos se pueden encadenar para dar otra respuesta (Por ejemplo: contar el número de procesos que se encuentran levantados)
Para el caso de Docker que es el que trataremos en el ejemplo se han definido los siguientes puntos :
- 3.1. Comandos de Análisis Básico de Contenedores Docker
- 3.2. Scripts de Soporte
3.1. Comandos de Análisis Básico de Contenedores Docker
Algunos de los comandos de Docker que pueden ayudar a esta tarea:
Mostrar la información de los procesos asociados a los contenedores en ejecución
docker ps
Además del identificador del contenedor, de los puertos habilitados y demás información se aconseja prestar atención al valor de "Command" ya que dará información sobre el comando que inició el contenedor.
Un ejemplo de composición podría ser el siguiente : Nº de procesos Docker activos con estado "Up" eliminando espacios por la izquierda
docker ps | grep Up | wc -l | sed -e 's/^[ \t]*//'
Mostrar cierta información del contenedor
docker inspect <CONTAINER-ID>
Acceder a los logs del contenedor
docker logs <CONTAINER-ID>
Acceder al interior del contenedor
docker exec -it <CONTAINER-ID> bash
Una vez dentro podemos realizar acciones de investigación como :
- "cat /etc/*release" : para determinar la versión e Linux utilizada
- "set" : para visualizar las variables de entorno declaradas
- "find / -name "XXX" " : para buscar las localizaciones de una determinada cadena de texto
- El lugar en el que se encuentra instalada una aplicación
- Los permisos de un determinado directorio
- ...
3.2 Scripts de Soporte
Se pude utilizar scripting junto con la ejecución de las herramientas proporcionadas / los comandos anteriores para realizar ciertas funcionalidades extras o de cierta complejidad, para ello se necesita tener cierto conocimiento sobre lo que estamos utilizando y un poquito de programación tipo "Shell Scripting".
Siempre se pueden diseñar estos scripts y añadirlos junto con tus desarrollos, así tardarás menos tiempo a la hora de hacer las cosas.
Dependiendo de la funcionalidad que necesites se puede establecer ciertas tipologías "típicas" :
- Scripts que muestren cierta información
- Scripts que aplican cierta configuración
- Scripts que inicializan información
- Scripts que comprueban cierta información / estados
- Scripts que prueban partes del desarrollo
- ...
La opción de mezclar funcionalidad entre ellos también es posibles -> Scripts Híbridos
A partir de ahí te toca aplicar creatividad :-)
4. Ejemplo de Uso con Docker
Para enseñar a utilizarlo y así practicar se hará uso de la imagen Tomcat de Docker Hub
En este apartado trataremos los siguientes puntos :
- 4.1. Preparación del contenedor Tomcat de Docker
- 4.2. Análisis Básico del Contenedor Tomcat de Docker
4.1. Preparación del contenedor Tomcat de Docker
Ejecutar el siguiente comando para traer la imagen
docker pull tomcat:8.5.56-jdk8-openjdk
Se detalla una versión concreta de todas las disponibles para Tomcat
Resultado del comando
Verificar que la imagen esta disponible en local
docker images
En mi caso tengo otras imágenes disponibles pero se puede verificar que se encuentra disponible "tomcat" con el TAG de la versión solicitada
IMPORTANTE: En este punto todavía NO se ha generado un contenedor asociado
Ejecutar el siguiente comando para crear el contenedor
docker run -it --rm -p 8888:8080 tomcat:8.5.56-jdk8-openjdk
- -it : Se ejecuta de forma interactiva
- -rm : Elimina el contenedor cuando este termina
- -p : Se mapea un puerto
Nota : Existen muchos mas parámetros para su construcción
Resultado del comando
Resultado desde el navegador invocando a : http://localhost:8888/
Es correcto que devuelva un 404 y si nos fijamos es del Tomcat que acabamos de utilizar (No muestra la consola porque esta se encuentra desactivada por defecto)
4.2. Análisis Básico del Contenedor Tomcat de Docker
Mostrar la información de los procesos asociados a los contenedores en ejecución
docker ps
Se identifica ID, Estado, Puerto y Commando que lo ejecuto
Mostrar cierta información del contenedor
docker inspect 011772a502d6
La respuesta se corresponde con el siguiente código
[
{
"Id": "011772a502d68462c3e91535b9d4b0ed3ed7b931c683a1aa98cfa75895bf00fd",
"Created": "2020-06-12T17:11:35.41406142Z",
"Path": "catalina.sh",
"Args": [
"run"
],
"State": {
"Status": "running",
"Running": true,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 4876,
"ExitCode": 0,
"Error": "",
"StartedAt": "2020-06-12T17:11:36.145964542Z",
"FinishedAt": "0001-01-01T00:00:00Z"
},
"Image": "sha256:e010d327a9043b1be5c313294ae43149c78566131f02bc03de3087e3275dc73f",
"ResolvConfPath": "/var/lib/docker/containers/011772a502d68462c3e91535b9d4b0ed3ed7b931c683a1aa98cfa75895bf00fd/resolv.conf",
"HostnamePath": "/var/lib/docker/containers/011772a502d68462c3e91535b9d4b0ed3ed7b931c683a1aa98cfa75895bf00fd/hostname",
"HostsPath": "/var/lib/docker/containers/011772a502d68462c3e91535b9d4b0ed3ed7b931c683a1aa98cfa75895bf00fd/hosts",
"LogPath": "/var/lib/docker/containers/011772a502d68462c3e91535b9d4b0ed3ed7b931c683a1aa98cfa75895bf00fd/011772a502d68462c3e91535b9d4b0ed3ed7b931c683a1aa98cfa75895bf00fd-json.log",
"Name": "/priceless_feistel",
"RestartCount": 0,
"Driver": "overlay2",
"Platform": "linux",
"MountLabel": "",
"ProcessLabel": "",
"AppArmorProfile": "",
"ExecIDs": null,
"HostConfig": {
"Binds": null,
"ContainerIDFile": "",
"LogConfig": {
"Type": "json-file",
"Config": {}
},
"NetworkMode": "default",
"PortBindings": {
"8080/tcp": [
{
"HostIp": "",
"HostPort": "8888"
}
]
},
"RestartPolicy": {
"Name": "no",
"MaximumRetryCount": 0
},
"AutoRemove": true,
"VolumeDriver": "",
"VolumesFrom": null,
"CapAdd": null,
"CapDrop": null,
"Capabilities": null,
"Dns": [],
"DnsOptions": [],
"DnsSearch": [],
"ExtraHosts": null,
"GroupAdd": null,
"IpcMode": "private",
"Cgroup": "",
"Links": null,
"OomScoreAdj": 0,
"PidMode": "",
"Privileged": false,
"PublishAllPorts": false,
"ReadonlyRootfs": false,
"SecurityOpt": null,
"UTSMode": "",
"UsernsMode": "",
"ShmSize": 67108864,
"Runtime": "runc",
"ConsoleSize": [
0,
0
],
"Isolation": "",
"CpuShares": 0,
"Memory": 0,
"NanoCpus": 0,
"CgroupParent": "",
"BlkioWeight": 0,
"BlkioWeightDevice": [],
"BlkioDeviceReadBps": null,
"BlkioDeviceWriteBps": null,
"BlkioDeviceReadIOps": null,
"BlkioDeviceWriteIOps": null,
"CpuPeriod": 0,
"CpuQuota": 0,
"CpuRealtimePeriod": 0,
"CpuRealtimeRuntime": 0,
"CpusetCpus": "",
"CpusetMems": "",
"Devices": [],
"DeviceCgroupRules": null,
"DeviceRequests": null,
"KernelMemory": 0,
"KernelMemoryTCP": 0,
"MemoryReservation": 0,
"MemorySwap": 0,
"MemorySwappiness": null,
"OomKillDisable": false,
"PidsLimit": null,
"Ulimits": null,
"CpuCount": 0,
"CpuPercent": 0,
"IOMaximumIOps": 0,
"IOMaximumBandwidth": 0,
"MaskedPaths": [
"/proc/asound",
"/proc/acpi",
"/proc/kcore",
"/proc/keys",
"/proc/latency_stats",
"/proc/timer_list",
"/proc/timer_stats",
"/proc/sched_debug",
"/proc/scsi",
"/sys/firmware"
],
"ReadonlyPaths": [
"/proc/bus",
"/proc/fs",
"/proc/irq",
"/proc/sys",
"/proc/sysrq-trigger"
]
},
"GraphDriver": {
"Data": {
"LowerDir": "/var/lib/docker/overlay2/a5ee210e69807fa8cc84633c2ba9f03cb1360b25b32eb366fdb926e6f5346e81-init/diff:/var/lib/docker/overlay2/e81e84e00a680bf471037f2720f6a2060710642325480d6930719a365b7edfdf/diff:/var/lib/docker/overlay2/28f421f1c2f3192154cfb59403f6640f581417bf4a5f7bef92a2095d83a276a9/diff:/var/lib/docker/overlay2/41721652d8c5dae79090bef56a78fa53145f5cde71952770ef22761b6e02b6b9/diff:/var/lib/docker/overlay2/48e667863ecbd5fe03ef6b89b827140bfdb7b40c5acdcf29ad840e0501b1a0ce/diff:/var/lib/docker/overlay2/4b23884126598d8332f5fc6c2e0ebc8220a1d7161efc91ab4074b82661b8bc3b/diff:/var/lib/docker/overlay2/4343864afa99e978dfad646de6e34a457ebafba79b2e51687294d47b8cabde9d/diff:/var/lib/docker/overlay2/de0796891cc893193a81b187973a4eae82d0bf042095f15b65e7fa3bf102e2db/diff:/var/lib/docker/overlay2/86bf0cd3d2a767ea04662fa74e5ea55180d31af86d68d0ee1278d062d1e8ceb3/diff:/var/lib/docker/overlay2/ea6c0aee7e9c9e22d0466a3693802fa963604d292493ea4b064dc497e09c982e/diff:/var/lib/docker/overlay2/63508bf8f19fe389ce6e3ca30a0f2d8d3c1f2c60a1de905b735e81916fdfa734/diff",
"MergedDir": "/var/lib/docker/overlay2/a5ee210e69807fa8cc84633c2ba9f03cb1360b25b32eb366fdb926e6f5346e81/merged",
"UpperDir": "/var/lib/docker/overlay2/a5ee210e69807fa8cc84633c2ba9f03cb1360b25b32eb366fdb926e6f5346e81/diff",
"WorkDir": "/var/lib/docker/overlay2/a5ee210e69807fa8cc84633c2ba9f03cb1360b25b32eb366fdb926e6f5346e81/work"
},
"Name": "overlay2"
},
"Mounts": [],
"Config": {
"Hostname": "011772a502d6",
"Domainname": "",
"User": "",
"AttachStdin": true,
"AttachStdout": true,
"AttachStderr": true,
"ExposedPorts": {
"8080/tcp": {}
},
"Tty": true,
"OpenStdin": true,
"StdinOnce": true,
"Env": [
"PATH=/usr/local/tomcat/bin:/usr/local/openjdk-8/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"LANG=C.UTF-8",
"JAVA_HOME=/usr/local/openjdk-8",
"JAVA_VERSION=8u252",
"JAVA_BASE_URL=https://github.com/AdoptOpenJDK/openjdk8-upstream-binaries/releases/download/jdk8u252-b09/OpenJDK8U-jdk_",
"JAVA_URL_VERSION=8u252b09",
"CATALINA_HOME=/usr/local/tomcat",
"TOMCAT_NATIVE_LIBDIR=/usr/local/tomcat/native-jni-lib",
"LD_LIBRARY_PATH=/usr/local/tomcat/native-jni-lib",
"GPG_KEYS=05AB33110949707C93A279E3D3EFE6B686867BA6 07E48665A34DCAFAE522E5E6266191C37C037D42 47309207D818FFD8DCD3F83F1931D684307A10A5 541FBE7D8F78B25E055DDEE13C370389288584E7 61B832AC2F1C5A90F0F9B00A1C506407564C17A3 713DA88BE50911535FE716F5208B0AB1D63011C7 79F7026C690BAA50B92CD8B66A3AD3F4F22C4FED 9BA44C2621385CB966EBA586F72C284D731FABEE A27677289986DB50844682F8ACB77FC2E86E29AC A9C5DF4D22E99998D9875A5110C01C5A2F6059E7 DCFD35E0BF8CA7344752DE8B6FB21E8933C60243 F3A04C595DB5B6A5F1ECA43E3B7BBB100D811BBE F7DA48BB64BCB84ECBA7EE6935CD23C10D498E23",
"TOMCAT_MAJOR=8",
"TOMCAT_VERSION=8.5.56",
"TOMCAT_SHA512=7a02a8e0b12eea2e0bf1175d754bd19dc445e7182c2db033ba6ca1330161cc74207c9b9b7f0fce510417ece28f26cc36816b34eb394b0d27350631e64204aed3"
],
"Cmd": [
"catalina.sh",
"run"
],
"Image": "tomcat:8.5.56-jdk8-openjdk",
"Volumes": null,
"WorkingDir": "/usr/local/tomcat",
"Entrypoint": null,
"OnBuild": null,
"Labels": {}
},
"NetworkSettings": {
"Bridge": "",
"SandboxID": "534beaf491055050df2accbb976225f847b8ede7e76ab05f568935098f0eb766",
"HairpinMode": false,
"LinkLocalIPv6Address": "",
"LinkLocalIPv6PrefixLen": 0,
"Ports": {
"8080/tcp": [
{
"HostIp": "0.0.0.0",
"HostPort": "8888"
}
]
},
"SandboxKey": "/var/run/docker/netns/534beaf49105",
"SecondaryIPAddresses": null,
"SecondaryIPv6Addresses": null,
"EndpointID": "31ef0dec106fbf40410402f8fc922d1e607c402e8f10585dc836167594b05550",
"Gateway": "172.17.0.1",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"IPAddress": "172.17.0.2",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"MacAddress": "02:42:ac:11:00:02",
"Networks": {
"bridge": {
"IPAMConfig": null,
"Links": null,
"Aliases": null,
"NetworkID": "50fdef33f7c97e63da6bad72f83b5b52e48358072348a3f3d9eec46240695d08",
"EndpointID": "31ef0dec106fbf40410402f8fc922d1e607c402e8f10585dc836167594b05550",
"Gateway": "172.17.0.1",
"IPAddress": "172.17.0.2",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:ac:11:00:02",
"DriverOpts": null
}
}
}
}
]
Como se puede ver tiene mucha información interesante sobre diferentes aspectos como estado, puertos, variables de entorno, IP asignada, red, etc.
Acceder a los logs del contenedor
docker logs 011772a502d6
Acceder al interior del contenedor
docker exec -it 011772a502d6 bash
Una vez dentro podemos realizar acciones de investigación como:
- "cat /etc/*release" : para determinar la versión e Linux utilizada
- "set" : para visualizar las variables de entorno declaradas
- ...
Ejemplo de ejecución de "cat /etc/*release"
Ejemplo de ejecución de "set"
Recordar que en la ejecución se vio el comando que lo lanzaba "catalina.sh run"
Pues podemos localizar dicho script y ver que hace:
Nota : si necesitamos ayuda podemos ejecutar una búsqueda con "find / -name "xxxx" "
Ejemplo de ejecución de "cat catalina.sh"
De esta forma podremos llegar a entender qué se ha ejecutado y si existe algún tipo de limitación.
También se pueden ejecutar comandos externos como EXEC directamente sin necesidad de tener que acceder desde el interior del contenedor
5. Conclusiones
Con la parte de "introducción" espero haberte ayudado a ver nuevas posibilidades del uso de contenedores. Así que te puedes hacer una idea de lo que puede venir en próximos artículos: montar base de datos para entornos de testing, montar aplicaciones concretas, montar infraestructura para el desarrollo a nivel de aplicaciones, montar entornos mockeados de Back, etc.
Con la parte de análisis lo que se ha buscado es dar una serie de herramientas o pautas básicas que puedan ayudar a entender mejor lo que le pasa a cada contenedor.
En los próximos artículos de 'Acelerando los desarrollos con contenedores' prometo seguir una máxima que es que : cada próximo artículo sólo tratará de un único tema ... jejeje
Si te ha gustado, ¡síguenos en Twitter para estar al día de nuevos posts!