Uno de los comandos esenciales de Docker es la inspección de docker. Te permite extraer información sobre varios objetos docker, saber usarlo es algo TODOS debe conocer.
En caso de que se lo pregunte, los objetos o recursos de Docker son simplemente cosas como contenedores, volúmenes, redes, etc.
La principal fortaleza de inspect
proviene de sus capacidades de formateo.
Por ejemplo, puede extraer la dirección IP de un contenedor en ejecución inspeccionándolo y formateándolo de una manera específica.
➟ docker container inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' nginx
172.17.0.2
Docker usa go-templates para formatear su salida.
En este artículo, primero repasaré los conceptos básicos del comando de inspección de Docker y luego me centraré en cómo formatear la salida según sus necesidades específicas.
¿Qué hace Docker inspeccionar?
Inspect le proporciona una gran cantidad de metadatos sobre todos los diferentes objetos administrados por docker. El tipo de información varía de un objeto a otro.
Por ejemplo, si inspecciona un volumen, obtendrá información relacionada con cuándo se creó, el controlador de volumen en uso, la ubicación en el sistema de archivos del host, las etiquetas, etc.
Si lo que está inspeccionando es una red, obtendrá información como su subred, puerta de enlace, contenedores conectados y sus direcciones IP, etiquetas y otra información.
Para comprender mejor lo que proporciona la inspección para un objeto determinado, le recomiendo que ejecute los comandos y lo compruebe por sí mismo.
¿Cuáles son los objetos que pueden ser inspeccionados?
En Docker, un objeto o tipo de objeto son todas las construcciones controladas por Docker. Esto incluye lo siguiente:-
- Contenedores.
- Imágenes.
- Redes.
- Volúmenes.
- Contextos.
- Complementos.
- Nodos (objeto de enjambre).
- Servicios (objeto de enjambre).
- Secretos (objeto de enjambre).
- Configuraciones (objeto de enjambre).
Uso del comando de inspección de Docker
Hay dos formas de usar el inspect
subcomando.
docker inspect [object] [options]
docker [object_type] inspect [object] [options]
El segundo método es el que debería usar siempre . El inspect
el subcomando proporciona una salida JSON, lo abordaré en un momento.
Cree un volumen llamado unique
.
docker volume create unique
Ahora crea una red con el mismo nombre, unique
.
docker network create unique
Ahora intentemos inspeccionar el objeto llamado unique
usando la primera sintaxis.
docker inspect unique
Mi salida:-
➟ docker inspect unique
[
{
"Name": "unique",
"Id": "09a7e2163ee058b1057d95599f764d571ec6a42a5792803dc125e706caa525b0",
"Created": "2021-05-07T15:47:20.341493099+05:30",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.21.0.0/16",
"Gateway": "172.21.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {},
"Options": {},
"Labels": {}
}
]
En mi sistema, como puede ver, inspect
inspeccioné la red, pero ¿y si pretendía inspeccionar el volumen?
Este es el problema con docker inspect
, cuando tiene dos objetos diferentes con el mismo nombre, no puede simplemente usar docker inspect [object_name]
. Para inspeccionar exactamente lo que desea, deberá usar el ID del objeto o usar el --type=[object_type]
opción. Puede escribir el comando anterior con --type
opción así:-
docker inspect --type=volume unique
Aunque esto funciona, creo que es innecesario ya que ya tenemos la otra sintaxis. Puede usar el subcomando específico del objeto como lo estoy haciendo aquí:-
docker volume inspect unique
Es menos para escribir y mucho más fácil de leer.
Algunos ejemplos útiles de comandos de inspección de Docker
En esta sección, registraré una lista de consultas comunes y cómo se vería el comando de inspección relevante para obtener esa información.
Consultas de contenedores
Para los ejemplos, tendré un contenedor nginx de muestra en ejecución, y todos los comandos se ejecutarán en este contenedor en ejecución. El comando que usé para ejecutar este contenedor:-
docker container run \
--rm --name nginx \
-p target=80,published=127.0.0.1:8081,protocol=tcp \
-p target=80,published=[::1]:8081,protocol=tcp \
-e ENV_VAR=somevalue \
-e ENV_VAR2=linux \
-v $PWD:/mnt:ro \
-v /tmp:/tmp:ro \
-d nginx
Puede obtener la ID del contenedor usando el siguiente comando:-
docker container inspect -f '{{.Id}}' [container_name]
Ejemplo:-
➟ docker container inspect -f '{{.Id}}' nginx
0409779fc2d976387170d664a6aed5ee80a460f8a8dd02c44a02af97df0bb956
El proceso contenedor principal es básicamente ENTRYPOINT
+ CMD
.
docker container inspect -f '{{printf "%s " .Path}}{{range .Args}}{{printf "%s " .}}{{end}}' [container_name|id]
Ejemplo:-
➟ docker container inspect -f '{{printf "%s " .Path}}{{range .Args}}{{printf "%s " .}}{{end}}' nginx
/docker-entrypoint.sh nginx -g daemon off;
El siguiente comando enumera todos los enlaces de puerto de contenedor a host.
docker container inspect -f '{{range $target, $published := .NetworkSettings.Ports}}{{range $published}}{{printf "%s -> %s:%s\n" $target .HostIp .HostPort}}{{end}}{{end}}' [container_name|id]
Ejemplo:-
➟ docker container inspect -f '{{range $target, $published := .NetworkSettings.Ports}}{{range $published}}{{printf "%s -> %s:%s\n" $target .HostIp .HostPort}}{{end}}{{end}}' nginx
80/tcp -> ::1:8081
80/tcp -> 127.0.0.1:8081
Puede lograr lo mismo con docker container port
comando.
Un contenedor se puede conectar a múltiples redes, en lugar de imprimir una de esas muchas direcciones IP, puede imprimir todas esas direcciones IP con este comando.
docker container inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' [container_name|id]
Ejemplo:-
➟ docker container inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' nginx
172.17.0.2
También puede enumerar la variable de entorno de un contenedor.
docker container inspect -f '{{range .Config.Env}}{{printf "%s\n" .}}{{end}}' [container_name|id]
Ejemplo:-
➟ docker container inspect -f '{{range .Config.Env}}{{printf "%s\n" .}}{{end}}' nginx
ENV_VAR=somevalue
ENV_VAR2=linux
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
NGINX_VERSION=1.19.10
NJS_VERSION=0.5.3
PKG_RELEASE=1~buster
El siguiente comando imprime los montajes de enlace en este formato, "[fuente] -> [destino], modo:[modo]".
docker container inspect -f '{{range .Mounts}}{{printf "%s -> %s, mode: %s\n" .Source .Destination .Mode}}{{end}}' [container_name|id]
Ejemplo:-
➟ docker container inspect -f '{{range .Mounts}}{{printf "%s -> %s, mode: %s\n" .Source .Destination .Mode}}{{end}}' nginx
/home/debdut -> /mnt, mode: ro
/tmp -> /tmp, mode: ro
Consultas de volumen
No hay mucho para inspeccionar un volumen, excepto conocer la ubicación del host, que se encuentra en data-dir/volumes
. Puede obtener esa información con el siguiente comando:-
docker volume inspect -f '{{.Mountpoint}}' [volume_name|id]
Ejemplo:-
➟ docker volume create unique
unique
~
➟ docker volume inspect -f '{{.Mountpoint}}' unique
/var/lib/docker/volumes/unique/_data
Consultas de red
Hay dos consultas que personalmente me encuentro haciendo con frecuencia, una es conocer una subred de redes y todos los contenedores que están conectados a esa red y las IP asociadas con ellos.
Para esto creé una red simple con docker network create unique
comando.
Para obtener la subred, use el siguiente comando:-
docker network inspect -f '{{range .IPAM.Config}}{{.Subnet}}{{end}}' [network_name|id]
Ejemplo:-
➟ docker network inspect -f '{{range .IPAM.Config}}{{.Subnet}}{{end}}' unique
172.21.0.0/16
El comando se ve así,
docker network inspect -f '{{range .Containers}}{{printf "%s -> %s\n" .Name .IPv4Address}}{{end}}' [network_name|id]
Ejemplo:-
➟ docker network inspect -f '{{range .Containers}}{{printf "%s -> %s\n" .Name .IPv4Address}}{{end}}' unique
cranky_wescoff -> 172.21.0.5/16
nginx -> 172.21.0.2/16
upbeat_carson -> 172.21.0.3/16
objective_jones -> 172.21.0.4/16
Formateo de la salida del comando de inspección de Docker
inspect
nos proporciona una matriz JSON para la salida, que puede filtrar usando algo como jq
. Entonces, si tienes experiencia en jq
, es posible que desee utilizarlo. El problema con jq
es que no viene preinstalado en la mayoría de las distribuciones de Linux, mientras que el mecanismo de formateo predeterminado de docker .. inspect
ya está allí, y es muy poderoso.
Docker usa go-templates para formatear su salida. Este artículo no tratará sobre go-templates, pero si desea obtener más información, puede leerlo aquí.
Internamente, los JSON se representan mediante varias estructuras de datos de Go. Es por eso que las plantillas go realmente funcionan con los tipos de datos go. Como no quiero explicar qué son estas estructuras de datos, en lugar de usar esos términos, usaré términos JSON para que sea más comprensible.
Extracción de campos simples
Considere un objeto JSON como el siguiente:-
{
"mary": 43,
"john": 44
}
Digamos que desea extraer la información asociada con la clave mary
. Para hacerlo, lo que usa es la notación de punto [.], donde antepone la clave con un punto y agrega claves recursivamente para cualquier clave anidada. Esto es lo mismo que jq
. Así que .mary
aquí serían 43.
Considere el siguiente JSON ahora.
{
"mary": {
"jane": 43,
"soyas": 56
},
"john": 65
}
En este caso .mary.jane
sería 43, y de manera similar .mary.soyas
serían 56.
Se puede usar una sintaxis similar con go-templates. Para formatear la salida, debe pasar la plantilla a -f
o --format
opción de inspect
subcomando. Revisemos el resultado de la inspección de volumen.
[
{
"CreatedAt": "2021-05-07T15:53:10+05:30",
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/unique/_data",
"Name": "unique",
"Options": {},
"Scope": "local"
}
]
Si quieres conocer el Mountpoint
, usaría el siguiente comando:-
docker volume inspect -f '{{.Mountpoint}}' unique
➟ docker volume inspect -f '{{.Mountpoint}}' unique
/var/lib/docker/volumes/unique/_data
Probablemente estés notando las llaves allí mismo, son como bloques, las expresiones están encapsuladas dentro de estos bloques.
Probemos algo anidado ahora. Inspeccione la red y busque el IPAM
sección.
➟ docker network inspect unique
<snipped>
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
<snipped>
Mirando esto, puede averiguar fácilmente cuál es el controlador de esta red. Pero en lugar de buscarlo así, puede formatearlo a partir de la salida JSON completa.
Observe el Driver
la clave está anidada dentro de IPAM
. Entonces, la expresión de punto para extraer el controlador sería .IPAM.Driver
. Véalo en acción:-
➟ docker network inspect -f '{{.IPAM.Driver}}' unique
default
Recorriendo objetos o listas (rango)
Los objetos JSON son como matrices asociativas en Bash o hashes, donde las claves son cadenas y los valores pueden ser de cualquier tipo de datos.
Para que entiendas esto un poco más fácil, comenzaré con un ejemplo práctico. Considere .NetworkSettings.Networks
sección en un resultado de inspección de un contenedor. Enumera las redes a las que está conectado el contenedor y, para cada red, algunos detalles asociados más, como la dirección IP.
Piensa si alguien te pide que le digas la dirección IP de un contenedor. Elegir solo una red y la IP asociada no tiene mucho sentido, lo mejor sería listar todas las direcciones IP que están asociadas a todas las redes.
Puede lograr esto con un simple bash for loop if ya conoces los nombres de las redes. Como en mi caso, puedo hacer algo como lo siguiente:-
for network in bridge unique; do
docker container inspect -f \
"{{.NetworkSettings.Networks.$network.IPAddress}}" nginx
done
Pero, obviamente, esto es una limitación a gran escala, ya que no siempre podemos recordar todos los nombres de las redes.
Puede mitigar este problema utilizando la acción de plantilla range
. range
pasa por un mapa (una matriz asociativa o un objeto JSON) y no nos proporciona la clave, sino los valores para cada iteración (este comportamiento es modificable).
Entonces, en este caso, puede escribir un bloque como {{range .NetworkSettings.Networks}}
para recorrer los valores de cada red o los datos asociados con cada red, y de eso puede extraer la dirección IP como lo haría desde una estructura similar a JSON normal, es decir, {{.IPAddress}}}
. Una cosa para recordar es terminar siempre la plantilla completa que comienza con range
, con {{end}}
.
Poniendo todo eso junto, puedes reescribir el bucle for anterior de esta manera:-
docker container inspect -f \
'{{range .NetworkSettings.Networks}}
{{.IPAddress}}{{end}}' nginx
Salida de muestra:-
➟ docker container inspect -f \
> '{{range .NetworkSettings.Networks}}
> {{.IPAddress}}{{end}}' nginx
172.17.0.2
172.21.0.2
Usando el index
función en arreglos y objetos
Puedes usar el index
función para extraer partes de su objeto o matriz JSON. Si la estructura es un objeto JSON, usaría {{index .Field "key"}}
, si la estructura es una matriz JSON, usaría {{index .Field index}}
.
En el ejemplo anterior, imprimió todas las direcciones IP de un contenedor. Supongamos que conoce una de las redes a las que está conectado (puente) y desea imprimir la dirección IP asociada con esa red. Puedes hacerlo con index
funciona así:-
docker container inspect -f '{{(index .NetworkSettings.Networks "bridge").IPAddress}}' nginx
Salida:-
➟ docker container inspect -f '{{(index .NetworkSettings.Networks "bridge").IPAddress}}' nginx
172.17.0.2
Usando json
función
Los datos exportados después del formateo no en JSON, está en alguna estructura de datos go. Pero puede convertir eso a JSON usando json
función.
El nivel superior del objeto JSON es .
. Entonces, para imprimir eso, harías algo como esto:-
docker network inspect -f '{{.}}' unique
➟ docker network inspect -f '{{.}}' unique
{unique 09a7e2163ee058b1057d95599f764d571ec6a42a5792803dc125e706caa525b0 2021-05-07 15:47:20.341493099 +0530 IST local bridge false {default map[] [{172.21.0.0/16 172.21.0.1 map[]}]} false false false {} false map[2646cbbde5efc218bb6f3a5c882f8eb9e3e4331d090ad46ccc0a2eec9c2eea1b:{nginx c0291394a48f7e8e8aa98fd31631eb00e68daacbee9cf24bac530f16359d051d 02:42:ac:15:00:02 172.21.0.2/16 }] map[] map[] [] map[]}
Lo que está viendo aquí es una estructura grande que consiste en mapas y tipos de datos básicos de otras estructuras. Estos no son muy legibles, ni utilizables fuera de go. Pero puede convertirlos a JSON usando json
función. Simplemente anteponga un campo con json
como lo estoy haciendo aquí:-
➟ docker network inspect -f '{{json .}}' unique
➟ docker network inspect -f '{{json .}}' unique
{"Name":"unique","Id":"09a7e2163ee058b1057d95599f764d571ec6a42a5792803dc125e706caa525b0","Created":"2021-05-07T15:47:20.341493099+05:30","Scope":"local","Driver":"bridge","EnableIPv6":false,"IPAM":{"Driver":"default","Options":{},"Config":[{"Subnet":"172.21.0.0/16","Gateway":"172.21.0.1"}]},"Internal":false,"Attachable":false,"Ingress":false,"ConfigFrom":{"Network":""},"ConfigOnly":false,"Containers":{"2646cbbde5efc218bb6f3a5c882f8eb9e3e4331d090ad46ccc0a2eec9c2eea1b":{"Name":"nginx","EndpointID":"c0291394a48f7e8e8aa98fd31631eb00e68daacbee9cf24bac530f16359d051d","MacAddress":"02:42:ac:15:00:02","IPv4Address":"172.21.0.2/16","IPv6Address":""}},"Options":{},"Labels":{}}
Para que se vea un poco mejor canalícelo a jq
.
➟ docker network inspect -f '{{json .}}' unique | jq
{
"Name": "unique",
"Id": "09a7e2163ee058b1057d95599f764d571ec6a42a5792803dc125e706caa525b0",
"Created": "2021-05-07T15:47:20.341493099+05:30",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.21.0.0/16",
"Gateway": "172.21.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"2646cbbde5efc218bb6f3a5c882f8eb9e3e4331d090ad46ccc0a2eec9c2eea1b": {
"Name": "nginx",
"EndpointID": "c0291394a48f7e8e8aa98fd31631eb00e68daacbee9cf24bac530f16359d051d",
"MacAddress": "02:42:ac:15:00:02",
"IPv4Address": "172.21.0.2/16",
"IPv6Address": ""
}
},
"Options": {},
"Labels": {}
}
¿Es asi? Absolutamente no. Le recomiendo que lea acerca de cómo usar las plantillas go.
Aquí he intentado comenzar a usarlo sin tener que saber mucho sobre las plantillas de go. Espero haber tenido éxito.
Para cualquier aclaración, no dude en utilizar la sección de comentarios.