Cómo verificar si un proceso se está ejecutando dentro del contenedor Docker

85

[Actualizado1] Tengo un shell que cambiará los parámetros del kernel de TCP en algunas funciones, pero ahora necesito hacer que este shell se ejecute en el contenedor Docker, es decir, el shell necesita saber que se está ejecutando dentro de un contenedor y dejar de configurar el kernel.

Ahora no estoy seguro de cómo lograr eso, aquí está el contenido del /proc/self/cgroupinterior del contenedor:

9:hugetlb:/
8:perf_event:/
7:blkio:/
6:freezer:/
5:devices:/
4:memory:/
3:cpuacct:/
2:cpu:/docker/25ef774c390558ad8c4e9a8590b6a1956231aae404d6a7aba4dde320ff569b8b
1:cpuset:/

¿Puedo usar alguna de las banderas anteriores para averiguar si este proceso se está ejecutando dentro de un contenedor?

[Actualizado2]: También he notado Determinar si un proceso se ejecuta dentro de lxc / Docker , pero parece no funcionar en este caso, el contenido /proc/1/cgroupde mi contenedor es:

8:perf_event:/
7:blkio:/
6:freezer:/
5:devices:/
4:memory:/
3:cpuacct:/
2:cpu:/docker/25ef774c390558ad8c4e9a8590b6a1956231aae404d6a7aba4dde320ff569b8b
1:cpuset:/

No / lxc / containerid

Harryz
fuente
No es una pregunta muy clara. ¿Por qué necesitas esto?
Henk Langeveld
2
Duplicado de stackoverflow.com/questions/20010199/…
Johannes 'fish' Ziemke
@fish no / lxc / <containerid> en mi caso, ver actualización
harryz
1
Los parámetros del kernel de @HenkLangeveld son de solo lectura en el contenedor Docker, por lo que necesito saber si mi shell se está ejecutando dentro de los contenedores y deshabilitar las funciones del kernel en mi shell. ver actualización.
harryz
Algunos pasos en el script intentan modificar los parámetros del kernel y deben omitirse cuando se ejecutan en Docker. Claro.
Henk Langeveld

Respuestas:

68

Para verificar dentro de un contenedor Docker si está dentro de un contenedor Docker o no, se puede hacer a través de /proc/1/cgroup. Como sugiere esta publicación , puede hacer lo siguiente:

Fuera de un contenedor docker, todas las entradas /proc/1/cgroupterminan /como puede ver aquí:

vagrant@ubuntu-13:~$ cat /proc/1/cgroup
11:name=systemd:/
10:hugetlb:/
9:perf_event:/
8:blkio:/
7:freezer:/
6:devices:/
5:memory:/
4:cpuacct:/
3:cpu:/
2:cpuset:/

Dentro de un contenedor Docker, algunos de los grupos de control pertenecerán a Docker (o LXC):

vagrant@ubuntu-13:~$ docker run busybox cat /proc/1/cgroup
11:name=systemd:/
10:hugetlb:/
9:perf_event:/
8:blkio:/
7:freezer:/
6:devices:/docker/3601745b3bd54d9780436faa5f0e4f72bb46231663bb99a6bb892764917832c2
5:memory:/
4:cpuacct:/
3:cpu:/docker/3601745b3bd54d9780436faa5f0e4f72bb46231663bb99a6bb892764917832c2
2:cpuset:/
Thomas Uhrig
fuente
@ La respuesta del Fundador es más limpia
Scott Stensland
5
no es estrictamente cierto que "Fuera de un contenedor docker todas las entradas en / proc / 1 / cgroup terminan en /". En ubuntu 16.04, por ejemplo, tengo:12:perf_event:/ 11:blkio:/init.scope 10:cpuset:/ 9:devices:/init.scope 8:hugetlb:/ 7:cpu,cpuacct:/init.scope 6:net_cls,net_prio:/ 5:memory:/init.scope 4:pids:/init.scope 3:rdma:/ 2:freezer:/ 1:name=systemd:/init.scope
samfr
Esto prácticamente solo funciona en Linux, no en Darwin u otros BSD que ni siquiera usan un procfs.
Christian
@Christian Docker / LXC son solo cosas de Linux, así que está bien, ¿verdad :)?
Robert Lacroix
@RobertLacroix, ¿estás diciendo que si no encuentras un procfs, no estás en Docker? Bueno, eso es bastante justo, supongo ...
Christian
107

Docker crea .dockerenvy .dockerinit( eliminados en v1.11 ) archivos en la parte superior del árbol de directorios del contenedor, por lo que es posible que desee verificar si existen.

Algo como esto debería funcionar.

#!/bin/bash
if [ -f /.dockerenv ]; then
    echo "I'm inside matrix ;(";
else
    echo "I'm living in real world!";
fi
at0S
fuente
1
A menos, por supuesto, que usted u otra persona haya creado /.dockeriniten su host (quizás por accidente), en cuyo caso será incorrecto fuera de un contenedor.
sosiouxme
18
Si alguien más ingresó / entonces es root y tienes peores problemas que saber si estás en la ventana acoplable o no.
davey
15
Cuidado con confiar /.dockerenven el largo plazo. No está diseñado para usarse de esta manera .
ReactiveRaven
fwiw, Podman no crea /.dockerenv. Crea, /run/.containerenvpero con una lógica similar, suena como un detalle de implementación en el que no se debe confiar. Consulte github.com/containers/libpod/issues/3586 para conocer algunas alternativas específicas de podman.
Beni Cherniavsky-Paskin
21

La solución de Thomas como código:

running_in_docker() {
  (awk -F/ '$2 == "docker"' /proc/self/cgroup | read non_empty_input)
}

Nota

El readcon una variable ficticia es un modismo simple para ¿Produce esto alguna salida? . Es un método compacto para convertir un patrón posiblemente detallado grepo awken una prueba de un patrón.

Nota adicional sobre lectura

Henk Langeveld
fuente
10
Excepto ... esto fallará en algunos entornos, porque, por ejemplo, 3:cpu,cpuacct:/system.slice/docker-1ce79a0dec4a2084d54acf187a1e177e0339dc90d0218b48b4456576ecaf291e.scopeno coincidiría. Más simple de grep -q docker /proc/1/cgroup; el código de resultado de eso también debería ser suficiente.
larsks
2
readpodría funcionar bash, pero en el dashshell más utilizado debe usar read dummy(o similar) o usar una construcción como[ -n "$(command)" ]
Daniel Alder
@DanielAlder Buena atrapada, Daniel. Actualizaré el texto.
Henk Langeveld
1
Anteriormente, esto afirmaba que cualquier shell compatible con Bourne admite el formato simple readsin nombre de variable. Esto solo es cierto para bash y ksh93. El Opengroup solo especifica read vary no menciona el readcomportamiento sin al menos una variable. En bash y ksh93 , si no se proporciona var , read usa la variable de shell REPLY.
Henk Langeveld
1
¿Por qué no podemos simplemente usar awk -F: '$3 ~ /docker/' /proc/self/cgroup | read? Funciona para mi.
Shubham Chaudhary
21

Usamos el sched de proc (/ proc / $ PID / sched) para extraer el PID del proceso. El PID del proceso dentro del contenedor será diferente a su PID en el host (un sistema sin contenedor).

Por ejemplo, la salida de / proc / 1 / sched en un contenedor devolverá:

root@33044d65037c:~# cat /proc/1/sched | head -n 1
bash (5276, #threads: 1)

Mientras está en un host que no es un contenedor:

$ cat /proc/1/sched  | head -n 1
init (1, #threads: 1)

Esto ayuda a diferenciar si estás en un contenedor o no. por ejemplo, puedes hacer:

if [[ ! $(cat /proc/1/sched | head -n 1 | grep init) ]]; then {
    echo in docker
} else {
    echo not in docker
} fi
Fundador
fuente
esta es realmente una información muy valiosa. gracias
Fabian Lange
4
Dependiendo del sistema operativo, "init" podría necesitar ser reemplazado por "systemd". Más información sobre systemd aquí .
BrianV
2
Como lo mencionó @BrianV, esto tampoco funciona para mí.
Shubham Chaudhary
5
En un contenedor Docker que se ejecuta en un clúster k8s, head -n1 /proc/1/scheddevuelve dumb-init (1, #threads: 1), por lo que la verificación sugerida en esta respuesta falla. (Además, contrariamente a lo que sugiere la respuesta, el PID se muestra como "1" en esa línea, aunque estoy haciendo esto en un contenedor).
Stefan Majewsky
Definitivamente, esta no es una solución universal. Puede (más o menos) usar lo que quiera para el PID 1 de un contenedor. Por ejemplo, si lo hace, docker run --init ...lo será docker-init. Si lo hace, por ejemplo, docker run ... head -n 1 /proc/1/schedlo será head.
jpkotta
6

Lo que me funciona es comprobar el número de inodo del '/.' Dentro de la ventana acoplable, es un número muy alto. Fuera de la ventana acoplable, es un número muy bajo como '2'. Creo que este enfoque también dependería del FileSystem que se esté utilizando.

Ejemplo

Dentro de la ventana acoplable:

# ls -ali / | sed '2!d' |awk {'print $1'}
1565265

Fuera del acoplador

$ ls -ali / | sed '2!d' |awk {'print $1'}
2

En un guión:

#!/bin/bash
INODE_NUM=`ls -ali / | sed '2!d' |awk {'print $1'}`
if [ $INODE_NUM == '2' ];
then
        echo "Outside the docker"
else
        echo "Inside the docker"
fi
trohit
fuente
en MSYS2 ls -ali / | sed '2! d' | awk {'print $ 1'} 232779805740174872
bo0k
igual que ls -di /? Parece que el número de inodo no es confiable en una plataforma diferente
yurenchen
esto es lo único que funcionó para mí para diferenciar entre un host Xen domU y su contenedor docker
Michael Altfield
1

Necesitábamos excluir los procesos que se ejecutan en contenedores, pero en lugar de buscar solo docker cgroups, decidimos compararlos /proc/<pid>/ns/pidcon el sistema init en /proc/1/ns/pid. Ejemplo:

pid=$(ps ax | grep "[r]edis-server \*:6379" | awk '{print $1}')
if [ $(readlink "/proc/$pid/ns/pid") == $(readlink /proc/1/ns/pid) ]; then
   echo "pid $pid is the same namespace as init system"
else
   echo "pid $pid is in a different namespace as init system"
fi

O en nuestro caso queríamos un one liner que generara un error si el proceso NO está en un contenedor

bash -c "test -h /proc/4129/ns/pid && test $(readlink /proc/4129/ns/pid) != $(readlink /proc/1/ns/pid)"

que podemos ejecutar desde otro proceso y si el código de salida es cero, el PID especificado se está ejecutando en un espacio de nombres diferente.

Greg Bray
fuente
No me funciona. Desde dentro de un contenedor Docker programado por k8s readlink /proc/self/ns/pidy readlink /proc/1/ns/pidproducir el mismo resultado.
Stefan Majewsky
1
@StefanMajewsky Es posible que desee intentar usar github.com/jessfraz/amicontained para ver qué funciones están habilitadas en el tiempo de ejecución del contenedor.
Greg Bray
0

Basado en el comentario de Dan Walsh sobre el uso de SELinux ps -eZ | grep container_t, pero sin necesidad psde instalarlo:

$ podman run --rm fedora:31 cat /proc/1/attr/current
system_u:system_r:container_t:s0:c56,c299
$ podman run --rm alpine cat /proc/1/attr/current
system_u:system_r:container_t:s0:c558,c813
$ docker run --rm fedora:31 cat /proc/1/attr/current
system_u:system_r:container_t:s0:c8,c583
$ cat /proc/1/attr/current
system_u:system_r:init_t:s0

Esto solo le dice que está ejecutando en un contenedor, pero no en qué tiempo de ejecución.

No verifiqué otros tiempos de ejecución de contenedores, pero https://opensource.com/article/18/2/understanding-selinux-labels-container-runtimes proporciona más información y sugiere que esto se usa ampliamente, ¿ también podría funcionar para rkt y lxc?

Beni Cherniavsky-Paskin
fuente
-1

He creado una pequeña secuencia de comandos de Python. Espero que alguien lo encuentre útil. :-)

#!/usr/bin/env python3
#@author Jorge III Altamirano Astorga 2018
import re
import math

total = None
meminfo = open('/proc/meminfo', 'r')
for line in meminfo:
    line = line.strip()
    if "MemTotal:" in line:
        line = re.sub("[^0-9]*", "", line)
        total = int(line)
meminfo.close()
print("Total memory: %d kB"%total)

procinfo = open('/proc/self/cgroup', 'r')
for line in procinfo: 
    line = line.strip()
    if re.match('.{1,5}:name=systemd:', line):
        dockerd = "/sys/fs/cgroup/memory" + \
            re.sub("^.{1,5}:name=systemd:", "", line) + \
            "/memory.stat"
        #print(dockerd)
        memstat = open(dockerd, 'r')
        for memline in memstat:
            memline = memline.strip()
            if re.match("hierarchical_memory_limit", memline):
                memline = re.sub("[^0-9]*", \
                    "", memline)  
                total = math.floor(int(memline) / 2**10)
        memstat.close()
procinfo.close()
print("Total available memory to the container: %d kB"%total)
Jorge Altamirano
fuente
es genial, pero ¿cómo ayuda a determinar si estás dentro de un contenedor o no?
user528025
FileNotFoundError: [Errno 2] No such file or directory: '/sys/fs/cgroup/memory/docker/<docker_id>/memory.stat'
Scrooge McDuck