So überprüfen Sie, ob ein Prozess im Docker-Container ausgeführt wird

87

[Updated1] Ich habe eine Shell, die in einigen Funktionen die TCP-Kernelparameter ändert, aber jetzt muss diese Shell im Docker-Container ausgeführt werden. Das bedeutet, dass die Shell wissen muss, dass sie in einem Container ausgeführt wird, und die Konfiguration des Kernels beenden muss.

Jetzt bin ich mir nicht sicher, wie ich das erreichen soll. Hier ist der Inhalt des /proc/self/cgroupBehälters:

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

Kann ich mit den oben genannten Flags herausfinden, ob dieser Prozess in einem Container ausgeführt wird?

[Updated2]: Ich habe auch festgestellt, dass festgestellt wurde, ob ein Prozess in lxc / Docker ausgeführt wird , aber in diesem Fall scheint er nicht zu funktionieren. Der Inhalt in /proc/1/cgroupmeinem Container lautet:

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

Nein / lxc / containerid

harryz
quelle
Keine sehr klare Frage. Warum brauchst du das?
Henk Langeveld
2
Duplikat von stackoverflow.com/questions/20010199/…
Johannes 'Fisch' Ziemke
@fish no / lxc / <containerid> in meinem Fall, siehe Update
Harryz
1
Die Kernel-Parameter von @HenkLangeveld sind im Docker-Container schreibgeschützt. Daher muss ich wissen, ob meine Shell in Containern ausgeführt wird, und die Kernelfunktionen in meiner Shell deaktivieren. siehe Update.
Harryz
Einige Schritte im Skript versuchen, die Kernelparameter zu ändern, und müssen beim Ausführen in Docker übersprungen werden. Klar.
Henk Langeveld

Antworten:

69

Das Überprüfen in einem Docker-Container, ob Sie sich in einem Docker-Container befinden oder nicht, kann über erfolgen /proc/1/cgroup. Wie dieser Beitrag vorschlägt, können Sie Folgendes tun:

Außerhalb eines Docker-Containers /proc/1/cgroupenden alle Einträge am Ende, /wie Sie hier sehen können:

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

In einem Docker-Container gehören einige der Kontrollgruppen zu Docker (oder 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
quelle
@ Gründer Antwort ist sauberer
Scott Stensland
5
Es ist nicht unbedingt richtig, dass "Außerhalb eines Docker-Containers alle Einträge in / proc / 1 / cgroup auf / enden". Am Ubuntu 16.04 zum Beispiel habe ich: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
Dies funktioniert so ziemlich nur unter Linux, nicht unter Darwin oder anderen BSDs, die nicht einmal ein procfs verwenden.
Christian
@Christian Docker / LXC sind nur Linux-Dinge, also ist das in Ordnung, oder :)?
Robert Lacroix
@RobertLacroix Sie sagen also, wenn Sie keinen Prozess finden, sind Sie nicht in Docker? Nun, das ist fair genug, denke ich ...
Christian
110

Docker erstellt .dockerenvund .dockerinit( in Version 1.11 entfernt ) Dateien oben im Verzeichnisbaum des Containers, sodass Sie möglicherweise überprüfen möchten, ob diese vorhanden sind.

So etwas sollte funktionieren.

#!/bin/bash
if [ -f /.dockerenv ]; then
    echo "I'm inside matrix ;(";
else
    echo "I'm living in real world!";
fi
at0S
quelle
1
Es sei denn natürlich, Sie oder eine andere Person haben /.dockerinitauf Ihrem Host erstellt (möglicherweise aus Versehen). In diesem Fall ist dies außerhalb eines Containers falsch.
Sosiouxme
18
Wenn jemand anderes es in / geschafft hat, sind sie root und Sie haben schlimmere Probleme als zu wissen, ob Sie im Docker sind oder nicht.
Dave
15
Verlassen Sie sich /.dockerenvlangfristig darauf. Es ist nicht für diese Verwendung vorgesehen .
ReactiveRaven
fwiw, Podman erstellt nicht /.dockerenv. Es erzeugt zwar, /run/.containerenvaber nach ähnlicher Logik klingt es nach Implementierungsdetails, auf die man sich nicht verlassen kann. Unter github.com/containers/libpod/issues/3586 finden Sie einige podman-spezifische Alternativen.
Beni Cherniavsky-Paskin
23

Wir verwenden den Zeitplan des Prozesses (/ proc / $ PID / sched), um die PID des Prozesses zu extrahieren. Die PID des Prozesses im Container unterscheidet sich von der PID auf dem Host (einem Nicht-Container-System).

Beispielsweise gibt die Ausgabe von / proc / 1 / sched für einen Container Folgendes zurück:

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

Auf einem Nicht-Container-Host:

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

Dies hilft zu unterscheiden, ob Sie sich in einem Container befinden oder nicht. zB können Sie tun:

if [[ ! $(cat /proc/1/sched | head -n 1 | grep init) ]]; then {
    echo in docker
} else {
    echo not in docker
} fi
Gründer
quelle
Dies ist eigentlich eine ziemlich wertvolle Information. danke
Fabian Lange
4
Je nach Betriebssystem muss "init" möglicherweise durch "systemd" ersetzt werden. Weitere Informationen zu systemd finden Sie hier .
BrianV
2
Wie von @BrianV erwähnt, funktioniert dies auch bei mir nicht.
Shubham Chaudhary
5
Gibt in einem Docker-Container, der auf einem k8s-Cluster ausgeführt wird, head -n1 /proc/1/schedzurück dumb-init (1, #threads: 1), sodass die in dieser Antwort vorgeschlagene Prüfung fehlschlägt. (Entgegen der Antwort wird die PID in dieser Zeile als "1" angezeigt, obwohl ich dies in einem Container mache.)
Stefan Majewsky
Dies ist definitiv keine universelle Lösung. Sie können (irgendwie) alles verwenden, was Sie für die PID 1 eines Containers wollen. Wenn Sie dies tun docker run --init ..., wird dies der Fall sein docker-init. Wenn Sie dies tun docker run ... head -n 1 /proc/1/sched, wird es z head.
jpkotta
21

Thomas 'Lösung als Code:

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

Hinweis

Die readVariable mit einem Dummy ist eine einfache Redewendung für Erzeugt dies eine Ausgabe? . Es ist eine kompakte Methode eines möglicherweise ausführlichen zum Drehen grepoder awkin einen Test eines Musters.

Zusätzlicher Hinweis zum Lesen

Henk Langeveld
quelle
10
Außer ... dies wird in einigen Umgebungen fehlschlagen, weil z. B. 3:cpu,cpuacct:/system.slice/docker-1ce79a0dec4a2084d54acf187a1e177e0339dc90d0218b48b4456576ecaf291e.scopenicht übereinstimmen würde. Einfacher zu grep -q docker /proc/1/cgroup; Der Ergebniscode daraus sollte ebenfalls ausreichen.
Larsks
2
readkönnte funktionieren für bash, aber in der am häufigsten verwendeten dashShell müssen Sie entweder read dummy(oder ähnlich) oder ein Konstrukt wie[ -n "$(command)" ]
Daniel Alder
@ DanielAlder Guter Fang, Daniel. Ich werde den Text aktualisieren.
Henk Langeveld
1
Zuvor wurde behauptet, dass jede Bourne-kompatible Shell die Ebene readohne Variablennamen unterstützt. Dies gilt nur für bash und ksh93. Die Opengroup gibt nur Verhalten ohne mindestens eine Variable an read varund erwähnt es nicht read. Wenn in bash und ksh93 kein var angegeben ist, verwendet read die Shell-Variable REPLY.
Henk Langeveld
1
Warum können wir nicht einfach verwenden awk -F: '$3 ~ /docker/' /proc/self/cgroup | read? Funktioniert bei mir.
Shubham Chaudhary
7

Was für mich funktioniert, ist die Überprüfung der Inode-Nummer des '/.' Im Docker ist es eine sehr hohe Zahl. Außerhalb des Dockers ist es eine sehr niedrige Zahl wie '2'. Ich gehe davon aus, dass dieser Ansatz auch vom verwendeten Dateisystem abhängt.

Beispiel

Im Hafen:

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

Vor dem Hafenarbeiter

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

In einem Skript:

#!/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
quelle
in MSYS2 ls -ali / | sed '2! d' | awk {'print $ 1'} 232779805740174872
bo0k
das gleiche wie ls -di /? scheint inode num auf verschiedenen plattformen nicht zuverlässig zu sein
yurenchen
Dies ist das einzige, was für mich funktioniert hat, um zwischen einem Xen domU-Host und seinem Docker-Container zu unterscheiden
Michael Altfield,
1

Wir mussten Prozesse ausschließen, die in Containern ausgeführt wurden, aber anstatt nur nach Docker-Gruppen zu suchen, haben wir uns entschlossen, /proc/<pid>/ns/pidmit dem Init-System unter zu vergleichen /proc/1/ns/pid. Beispiel:

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

Oder in unserem Fall wollten wir einen Einzeiler, der einen Fehler generiert, wenn sich der Prozess NICHT in einem Container befindet

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

Dies können wir von einem anderen Prozess aus ausführen. Wenn der Exit-Code Null ist, wird die angegebene PID in einem anderen Namespace ausgeführt.

Greg Bray
quelle
Funktioniert bei mir nicht Aus einem K8S Planmäßige Docker Behälter, readlink /proc/self/ns/pidund readlink /proc/1/ns/piderzeugt die gleiche Leistung.
Stefan Majewsky
1
@StefanMajewsky Vielleicht möchten Sie versuchen, mit github.com/jessfraz/amicontained zu sehen, welche Funktionen in der Container-Laufzeit aktiviert sind.
Greg Bray
0

Basierend auf Dan Walshs Kommentar zur Verwendung von SELinux ps -eZ | grep container_t, ohne dass pseine Installation erforderlich ist:

$ 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

Dies sagt Ihnen nur, dass Sie in einem Container laufen , aber nicht welche Laufzeit.

Andere Container-Laufzeiten wurden nicht überprüft, aber https://opensource.com/article/18/2/understanding-selinux-labels-container-runtimes bietet weitere Informationen und schlägt vor, dass dies weit verbreitet ist und möglicherweise auch für rkt und lxc funktioniert.

Beni Cherniavsky-Paskin
quelle
0

Golang-Code

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
}
wcc526
quelle
-1

Ich habe ein kleines Python-Skript erstellt. Hoffe, jemand findet es nützlich. :-)

#!/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
quelle
Es ist cool, aber wie hilft es festzustellen, ob Sie sich in einem Container befinden oder nicht?
user528025
FileNotFoundError: [Errno 2] No such file or directory: '/sys/fs/cgroup/memory/docker/<docker_id>/memory.stat'
Scrooge McDuck