Como verificar se um processo está sendo executado dentro do contêiner do docker

89

[Atualizado1] Eu tenho um shell que mudará os parâmetros do kernel TCP em algumas funções, mas agora preciso fazer esse shell rodar no contêiner Docker, ou seja, o shell precisa saber que está rodando dentro de um contêiner e parar de configurar o kernel.

Agora não tenho certeza de como fazer isso, aqui está o conteúdo de /proc/self/cgroupdentro do contêiner:

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

Posso usar algum sinalizador acima para descobrir se esse processo está sendo executado dentro de um contêiner?

[Atualizado2]: também observei como determinar se um processo é executado dentro do lxc / Docker , mas parece não funcionar neste caso, o conteúdo /proc/1/cgroupdo meu contêiner é:

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

Não / lxc / containerid

harryz
fonte
Não é uma pergunta muito clara. Por que você precisa disso?
Henk Langeveld
2
Duplicado de stackoverflow.com/questions/20010199/…
Johannes 'fish' Ziemke
@fish no / lxc / <containerid> no meu caso, consulte a atualização
harryz de
1
Os parâmetros do kernel @HenkLangeveld são somente leitura no contêiner do Docker, então preciso saber se meu shell está sendo executado dentro de contêineres e desabilitar as funções do kernel em meu shell. veja atualização.
harryz de
Algumas etapas do script tentam modificar os parâmetros do kernel e precisam ser ignoradas durante a execução no Docker. Claro.
Henk Langeveld

Respostas:

70

Para verificar dentro de um contêiner Docker se você está dentro de um contêiner Docker ou não, pode ser feito através de /proc/1/cgroup. Como esta postagem sugere, você pode fazer o seguinte:

Fora de um contêiner docker, todas as entradas /proc/1/cgroupterminam, /como você pode ver aqui:

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 um contêiner do Docker, alguns dos grupos de controle pertencerão ao Docker (ou 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
fonte
A resposta do fundador é mais limpa
Scott Stensland
5
não é estritamente verdade que "Fora de um contêiner docker, todas as entradas em / proc / 1 / cgroup terminam em /". No ubuntu 16.04, por exemplo, eu tenho: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
Praticamente só funciona no Linux, não no Darwin ou em outros BSDs que nem mesmo usam procfs.
Christian
@Christian Docker / LXC são coisas do Linux, então tudo bem, certo :)?
Robert Lacroix
@RobertLacroix então você está dizendo que se não encontrar um procfs, você não está no Docker? Bem, isso é justo, eu acho ...
Christian
112

O Docker cria .dockerenve .dockerinit( remove na v1.11 ) arquivos no topo da árvore de diretórios do contêiner, então você pode querer verificar se eles existem.

Algo assim deve funcionar.

#!/bin/bash
if [ -f /.dockerenv ]; then
    echo "I'm inside matrix ;(";
else
    echo "I'm living in real world!";
fi
at0S
fonte
1
A menos, é claro, que você ou outra pessoa tenha criado /.dockerinitem seu host (talvez por acidente), caso em que estará errado fora de um contêiner.
sosiouxme
18
Se outra pessoa fez isso no /, então eles são root e você terá problemas piores do que saber se está no docker ou não.
davey
15
Cuidado com a confiança no /.dockerenvlongo prazo. Não se destina a ser usado desta forma .
ReactiveRaven
fwiw, Podman não cria /.dockerenv. Ele cria, /run/.containerenvmas por uma lógica semelhante, soa como detalhes de implementação nos quais não se pode confiar. Consulte github.com/containers/libpod/issues/3586 para algumas alternativas específicas do podman.
Beni Cherniavsky-Paskin
24

Usamos o sched do proc (/ proc / $ PID / sched) para extrair o PID do processo. O PID do processo dentro do contêiner será diferente do PID no host (um sistema não contêiner).

Por exemplo, a saída de / proc / 1 / sched em um contêiner retornará:

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

Enquanto em um host não contêiner:

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

Isso ajuda a diferenciar se você está em um contêiner ou não. por exemplo, você pode fazer:

if [[ ! $(cat /proc/1/sched | head -n 1 | grep init) ]]; then {
    echo in docker
} else {
    echo not in docker
} fi
Fundador
fonte
esta é realmente uma informação bastante valiosa. obrigado
Fabian Lange
4
Dependendo do sistema operacional, "init" pode precisar ser substituído por "systemd". Mais informações sobre o systemd aqui .
BrianV
2
Como mencionado por @BrianV, isso também não funciona para mim.
Shubham Chaudhary
5
Em um contêiner Docker em execução em um cluster k8s, head -n1 /proc/1/schedretorna dumb-init (1, #threads: 1), portanto, a verificação sugerida nesta resposta falha. (Além disso, ao contrário do que a resposta sugere, o PID é mostrado como "1" nessa linha, embora eu esteja fazendo isso em um contêiner.)
Stefan Majewsky
Definitivamente, essa não é uma solução universal. Você pode (meio que) usar o que quiser para o PID 1. de um contêiner. Por exemplo, se você fizer docker run --init ...isso, será docker-init. Se você fizer docker run ... head -n 1 /proc/1/schedisso, por exemplo , será head.
jpkotta
21

A solução de Thomas como código:

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

Nota

O readcom uma variável fictícia é um idioma simples para Isso produz alguma saída? . É um método compacto para transformar um possivelmente prolixo grepou awkum teste de um padrão.

Nota adicional na leitura

Henk Langeveld
fonte
10
Exceto ... isso irá falhar em alguns ambientes, porque, por exemplo, 3:cpu,cpuacct:/system.slice/docker-1ce79a0dec4a2084d54acf187a1e177e0339dc90d0218b48b4456576ecaf291e.scopenão corresponderia. Mais simples para grep -q docker /proc/1/cgroup; o código de resultado disso também deve ser suficiente.
larsks
2
readpode funcionar para bash, mas no dashshell mais usado você tem que usar read dummy(ou semelhante) ou usar uma construção como[ -n "$(command)" ]
Daniel Alder
@DanielAlder Boa captura, Daniel. Vou atualizar o texto.
Henk Langeveld
1
Anteriormente, isso afirmava que qualquer shell compatível com Bourne suporta o readnome simples sem variável. Isso só é verdade para bash e ksh93. O Opengroup apenas especifica read vare não menciona o readcomportamento sem pelo menos uma variável. Em bash e ksh93 , se nenhuma var for fornecida, read usa a variável shell REPLY.
Henk Langeveld
1
Por que não podemos simplesmente usar awk -F: '$3 ~ /docker/' /proc/self/cgroup | read? Funciona para mim.
Shubham Chaudhary
7

O que funciona para mim é verificar o número do inode do '/.' Dentro da janela de encaixe, é um número muito alto. Fora do docker, é um número muito baixo, como '2'. Acho que essa abordagem também dependeria do FileSystem sendo usado.

Exemplo

Dentro da janela de encaixe:

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

Fora do docker

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

Em um 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
trohit
fonte
em MSYS2 ls -ali / | sed '2! d' | awk {'print $ 1'} 232779805740174872
bo0k
mesmo que ls -di /? parece inode num não confiável em plataforma diferente
yurenchen
esta é a única coisa que funcionou para eu diferenciar entre um host Xen domU e seu contêiner docker
Michael Altfield
1

Precisamos excluir processos em execução em contêineres, mas em vez de verificar apenas os cgroups do docker, decidimos comparar /proc/<pid>/ns/pidcom o sistema init em /proc/1/ns/pid. Exemplo:

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

Ou, em nosso caso, queríamos um liner que gerasse um erro se o processo NÃO estivesse em um contêiner

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

que podemos executar a partir de outro processo e se o código de saída for zero, o PID especificado está sendo executado em um namespace diferente.

Greg Bray
fonte
Nao funciona para mim. De dentro de um contêiner Docker agendado para k8s readlink /proc/self/ns/pide readlink /proc/1/ns/pidproduza a mesma saída.
Stefan Majewsky
1
@StefanMajewsky Pode querer tentar usar github.com/jessfraz/amicontained para ver quais recursos estão habilitados no tempo de execução do contêiner.
Greg Bray
0

Com base no comentário de Dan Walsh sobre o uso do SELinux ps -eZ | grep container_t, mas sem a necessidade psde instalação:

$ 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

Isso apenas informa que você está executando em um contêiner, mas não em qual tempo de execução.

Não verificou outros tempos de execução do contêiner, mas https://opensource.com/article/18/2/understanding-selinux-labels-container-runtimes fornece mais informações e sugere que isso é amplamente usado, também pode funcionar para rkt e lxc?

Beni Cherniavsky-Paskin
fonte
0

código 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
}
wcc526
fonte
-1

Eu criei um pequeno script python. Espero que alguém ache ú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
fonte
é legal, mas como ajuda determinar se você está dentro de um container ou não?
user528025 01 de
FileNotFoundError: [Errno 2] No such file or directory: '/sys/fs/cgroup/memory/docker/<docker_id>/memory.stat'
Scrooge McDuck