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


Não é uma pergunta muito clara. Por que você precisa disso?
Henk Langeveld


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

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

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

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


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

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.


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?


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
}

-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)

é 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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.