Come verificare se un processo è in esecuzione all'interno del contenitore Docker


89

[Aggiornato1] Ho una shell che cambierà i parametri del kernel TCP in alcune funzioni, ma ora ho bisogno di far funzionare questa shell nel contenitore Docker, ciò significa che la shell deve sapere che è in esecuzione all'interno di un contenitore e smettere di configurare il kernel.

Ora non sono sicuro di come ottenerlo, ecco il contenuto dell'interno /proc/self/cgroupdel contenitore:

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

Eventuali flag sopra posso utilizzare per capire se questo processo è in esecuzione all'interno di un contenitore?

[Aggiornato2]: ho anche notato determinare se un processo viene eseguito all'interno di lxc / Docker , ma in questo caso sembra non funzionare, il contenuto /proc/1/cgroupdel mio contenitore è:

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

No / lxc / containerid


Domanda non molto chiara. Perchè ti serve?
Henk Langeveld


@fish no / lxc / <containerid> nel mio caso, vedi aggiornamento
harryz

1
I parametri del kernel @HenkLangeveld sono di sola lettura nel container Docker, quindi ho bisogno di sapere se la mia shell è in esecuzione all'interno dei container e disabilita le funzioni del kernel nella mia shell. vedi aggiornamento.
harryz

Alcuni passaggi nello script tentano di modificare i parametri del kernel e devono essere ignorati durante l'esecuzione in Docker. Chiaro.
Henk Langeveld

Risposte:


70

Per controllare all'interno di un container Docker se ci si trova all'interno di un container Docker o meno può essere eseguito tramite /proc/1/cgroup. Come suggerisce questo post , puoi fare quanto segue:

Fuori da un container docker, tutte le voci alla /proc/1/cgroupfine sono attive, /come puoi vedere qui:

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:/

All'interno di un container Docker alcuni dei gruppi di controllo apparterranno 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:/

@ La risposta del fondatore è più pulita
Scott Stensland

5
non è del tutto vero che "Fuori da un contenitore docker tutte le voci in / proc / 1 / cgroup terminano in /". Su ubuntu 16.04, ad esempio, ho: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

Questo funziona praticamente solo su Linux, non su Darwin o altri BSD che non usano nemmeno un procfs.
Christian

@Christian Docker / LXC sono solo cose Linux, quindi va bene, giusto :)?
Robert Lacroix

@RobertLacroix quindi stai dicendo che se non trovi un procfs, non sei in Docker? Beh, è ​​abbastanza giusto, immagino ...
Christian

112

Docker crea .dockerenve .dockerinit( rimossi nella v1.11 ) file nella parte superiore dell'albero di directory del contenitore, quindi potresti voler controllare se esistono.

Qualcosa di simile dovrebbe funzionare.

#!/bin/bash
if [ -f /.dockerenv ]; then
    echo "I'm inside matrix ;(";
else
    echo "I'm living in real world!";
fi

1
A meno che, ovviamente, tu o qualcun altro non abbia creato /.dockerinitsul tuo host (forse per caso), nel qual caso sarà sbagliato fuori da un contenitore.
sosiouxme

18
Se qualcun altro è entrato in / allora è root e hai problemi peggiori che sapere se sei nella finestra mobile o no.
davey

15
Attenzione a fare affidamento /.dockerenva lungo termine. Non è concepito per essere utilizzato in questo modo .
ReactiveRaven

fwiw, Podman non crea /.dockerenv. Crea /run/.containerenvma con una logica simile, suona come dettagli di implementazione su cui non fare affidamento. Vedi github.com/containers/libpod/issues/3586 per alcune alternative specifiche per podman.
Beni Cherniavsky-Paskin

24

Usiamo lo sched del proc (/ proc / $ PID / sched) per estrarre il PID del processo. Il PID del processo all'interno del contenitore sarà diverso dal PID sull'host (un sistema non contenitore).

Ad esempio, l'output di / proc / 1 / sched su un contenitore restituirà:

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

Mentre su un host non contenitore:

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

Questo aiuta a differenziare se sei in un contenitore o meno. ad esempio puoi fare:

if [[ ! $(cat /proc/1/sched | head -n 1 | grep init) ]]; then {
    echo in docker
} else {
    echo not in docker
} fi

questa è in realtà un'informazione piuttosto preziosa. grazie
Fabian Lange

4
A seconda del sistema operativo, "init" potrebbe dover essere sostituito con "systemd". Maggiori informazioni su systemd qui .
BrianV

2
Come accennato da @BrianV, anche questo non funziona per me.
Shubham Chaudhary

5
In un contenitore Docker in esecuzione su un cluster k8s, head -n1 /proc/1/schedrestituisce dumb-init (1, #threads: 1), quindi il controllo suggerito in questa risposta fallisce. (Inoltre, contrariamente a quanto suggerisce la risposta, il PID viene mostrato come "1" in quella riga anche se lo sto facendo in un contenitore.)
Stefan Majewsky

Questa non è sicuramente una soluzione universale. Puoi (più o meno) usare quello che vuoi per il PID 1. Ad esempio, se lo fai docker run --init ...lo sarà docker-init. Se lo fai, ad esempio, docker run ... head -n 1 /proc/1/schedlo sarà head.
jpkotta

21

La soluzione di Thomas come codice:

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

Nota

L' readcon una variabile fittizia è un semplice linguaggio per Questo produce alcun output? . È un metodo compatto per trasformare un possibile verboso grepo awkun test di un pattern.

Nota aggiuntiva sulla lettura


10
Tranne che ... questo fallirà in alcuni ambienti, perché, ad esempio, 3:cpu,cpuacct:/system.slice/docker-1ce79a0dec4a2084d54acf187a1e177e0339dc90d0218b48b4456576ecaf291e.scopenon corrisponderebbe. Più semplice da grep -q docker /proc/1/cgroup; anche il codice del risultato dovrebbe essere sufficiente.
Larsks

2
readpotrebbe funzionare bash, ma nella dashshell più utilizzata devi usare read dummy(o simile) o usare un costrutto come[ -n "$(command)" ]
Daniel Alder

@DanielAlder Buona cattura, Daniel. Aggiornerò il testo.
Henk Langeveld

1
In precedenza questo affermava che qualsiasi shell compatibile con Bourne supporta il plain readsenza nome di variabile. Questo è vero solo per bash e ksh93. L'Opengroup specifica read vare non menziona solo il readcomportamento senza almeno una variabile. In bash e ksh93 , se non viene fornita alcuna var , read usa la variabile di shell REPLY.
Henk Langeveld

1
Perché non possiamo semplicemente usare awk -F: '$3 ~ /docker/' /proc/self/cgroup | read? Per me va bene.
Shubham Chaudhary

7

Quello che funziona per me è controllare il numero di inode di "/." All'interno della finestra mobile, è un numero molto alto. Fuori dalla finestra mobile, è un numero molto basso come "2". Penso che questo approccio dipenda anche dal FileSystem utilizzato.

Esempio

All'interno della finestra mobile:

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

Fuori dal docker

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

In uno script:

#!/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

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

uguale a ls -di /? sembra inode num non affidabile su piattaforme diverse
yurenchen

questa è l'unica cosa che ha funzionato per me per distinguere tra un host domU Xen e il suo container
docker

1

Avevamo bisogno di escludere i processi in esecuzione nei contenitori, ma invece di controllare solo i cgroup docker abbiamo deciso di confrontarli /proc/<pid>/ns/pidcon il sistema init in /proc/1/ns/pid. Esempio:

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 nel nostro caso volevamo un one liner che genera un errore se il processo NON è in un contenitore

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

che possiamo eseguire da un altro processo e se il codice di uscita è zero, il PID specificato è in esecuzione in uno spazio dei nomi diverso.


Non funziona per me. Dall'interno di un contenitore Docker pianificato da k8s readlink /proc/self/ns/pide readlink /proc/1/ns/pidprodurre lo stesso output.
Stefan Majewsky

1
@StefanMajewsky Potrebbe voler provare a utilizzare github.com/jessfraz/amicontained per vedere quali funzionalità sono abilitate nel runtime del contenitore.
Greg Bray

0

Basato sul commento di Dan Walsh sull'utilizzo di SELinux ps -eZ | grep container_t, ma senza che pssia necessario installarlo:

$ 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

Questo ti dice solo che stai eseguendo in un contenitore, ma non in quale runtime.

Non ho controllato altri runtime del container ma https://opensource.com/article/18/2/understanding-selinux-labels-container-runtimes fornisce maggiori informazioni e suggerisce che questo è ampiamente utilizzato, potrebbe funzionare anche per rkt e lxc?


0

codice golang

func GetContainerID(pid int32) string {
    cgroupPath := fmt.Sprintf("/proc/%s/cgroup", strconv.Itoa(int(pid)))
    return getContainerID(cgroupPath)
}

func GetImage(containerId string) string {
    if containerId == "" {
        return ""
    }
    image, ok := containerImage[containerId]
    if ok {
        return image
    } else {
        return ""
    }
}
func getContainerID(cgroupPath string) string {
    containerID := ""
    content, err := ioutil.ReadFile(cgroupPath)
    if err != nil {
        return containerID
    }
    lines := strings.Split(string(content), "\n")
    for _, line := range lines {
        field := strings.Split(line, ":")
        if len(field) < 3 {
            continue
        }
        cgroup_path := field[2]
        if len(cgroup_path) < 64 {
            continue
        }
        // Non-systemd Docker
        //5:net_prio,net_cls:/docker/de630f22746b9c06c412858f26ca286c6cdfed086d3b302998aa403d9dcedc42
        //3:net_cls:/kubepods/burstable/pod5f399c1a-f9fc-11e8-bf65-246e9659ebfc/9170559b8aadd07d99978d9460cf8d1c71552f3c64fefc7e9906ab3fb7e18f69
        pos := strings.LastIndex(cgroup_path, "/")
        if pos > 0 {
            id_len := len(cgroup_path) - pos - 1
            if id_len == 64 {
                //p.InDocker = true
                // docker id
                containerID = cgroup_path[pos+1 : pos+1+64]
                // logs.Debug("pid:%v in docker id:%v", pid, id)
                return containerID
            }
        }
        // systemd Docker
        //5:net_cls:/system.slice/docker-afd862d2ed48ef5dc0ce8f1863e4475894e331098c9a512789233ca9ca06fc62.scope
        docker_str := "docker-"
        pos = strings.Index(cgroup_path, docker_str)
        if pos > 0 {
            pos_scope := strings.Index(cgroup_path, ".scope")
            id_len := pos_scope - pos - len(docker_str)
            if pos_scope > 0 && id_len == 64 {
                containerID = cgroup_path[pos+len(docker_str) : pos+len(docker_str)+64]
                return containerID
            }
        }
    }
    return containerID
}

-1

Ho creato un piccolo script Python. Spero che qualcuno lo trovi utile. :-)

#!/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)

è bello, ma come aiuta a determinare se sei all'interno di un contenitore o no?
user528025

FileNotFoundError: [Errno 2] No such file or directory: '/sys/fs/cgroup/memory/docker/<docker_id>/memory.stat'
Scrooge McDuck
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.