En Mi Local Funciona

Technical thoughts, stories and ideas

Acelerando los desarrollos con contenedores: Introducción

Publicado por Víctor Madrid el

Arquitectura de SolucionesDockerContenedoresAceleradores de Desarrollo

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 el contenedor algunas veces sólo cubre aspectos básicos relacionados con el sistema operativo utilizado, en otras abarca alguna cosa más como un entorno (Java, .Net, etc.) que funciona sobre un sistema operativo, otras veces es un producto específico (base de datos, servidor de aplicaciones, microservicio, etc.) que funciona sobre los anteriores.

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

¿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!

Víctor Madrid
Autor

Víctor Madrid

Líder Técnico de la Comunidad de Arquitectura de Soluciones en atSistemas. Aprendiz de mucho y maestro de nada. Técnico, artista y polifacético a partes iguales ;-)