วิธีตรวจสอบว่ากระบวนการกำลังทำงานอยู่ภายใน Docker container หรือไม่


89

[อัปเดต 1] ฉันมีเชลล์ซึ่งจะเปลี่ยนพารามิเตอร์เคอร์เนล TCP ในบางฟังก์ชัน แต่ตอนนี้ฉันจำเป็นต้องทำให้เชลล์นี้ทำงานในคอนเทนเนอร์ Docker นั่นหมายความว่าเชลล์จำเป็นต้องรู้ว่ามันกำลังทำงานอยู่ภายในคอนเทนเนอร์และหยุดการกำหนดค่าเคอร์เนล

ตอนนี้ฉันไม่แน่ใจว่าจะบรรลุได้อย่างไรนี่คือเนื้อหา/proc/self/cgroupภายในคอนเทนเนอร์:

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

แฟล็กใด ๆ ด้านบนที่ฉันสามารถใช้เพื่อดูว่ากระบวนการนี้กำลังทำงานอยู่ในคอนเทนเนอร์หรือไม่

[อัปเดต 2]: ฉันสังเกตเห็นด้วยว่าการพิจารณาว่ากระบวนการทำงานภายใน lxc / Dockerหรือไม่ แต่ดูเหมือนว่าจะไม่ทำงานในกรณีนี้เนื้อหาใน/proc/1/cgroupคอนเทนเนอร์ของฉันคือ

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

ไม่ใช่ / lxc / containerid


ไม่ใช่คำถามที่ชัดเจนมาก ทำไมคุณถึงต้องการสิ่งนี้?
Henk Langeveld


@fish no / lxc / <containerid> ในกรณีของฉันดูอัปเดต
harryz

1
พารามิเตอร์เคอร์เนล @HenkLangeveld เป็นแบบอ่านอย่างเดียวในคอนเทนเนอร์ Docker ดังนั้นฉันจำเป็นต้องทราบว่าเชลล์ของฉันทำงานภายในคอนเทนเนอร์หรือไม่และปิดใช้งานฟังก์ชันเคอร์เนลในเชลล์ของฉัน ดูการอัปเดต
harryz

บางขั้นตอนในสคริปต์พยายามแก้ไขพารามิเตอร์เคอร์เนลและจำเป็นต้องข้ามไปเมื่อรันใน Docker ชัดเจน.
Henk Langeveld

คำตอบ:


70

หากต้องการตรวจสอบภายในคอนเทนเนอร์ Docker ว่าคุณอยู่ในคอนเทนเนอร์ Docker หรือไม่สามารถทำได้ผ่านทาง/proc/1/cgroup. ในฐานะที่เป็นโพสต์นี้แนะนำคุณสามารถทำสิ่งต่อไปนี้:

ภายนอกคอนเทนเนอร์นักเทียบท่ารายการทั้งหมดใน/proc/1/cgroupท้ายที่สุด/ดังที่คุณเห็นที่นี่:

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

ภายในคอนเทนเนอร์ Docker กลุ่มควบคุมบางกลุ่มจะเป็นของ Docker (หรือ 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:/

คำตอบของผู้ก่อตั้งนั้นสะอาดกว่า
Scott Stensland

5
ไม่เป็นความจริงอย่างเคร่งครัดว่า "นอกคอนเทนเนอร์นักเทียบท่าทุกรายการใน / proc / 1 / cgroup end on /" ใน ubuntu 16.04 ตัวอย่างเช่นฉันมี: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

สิ่งนี้ใช้ได้กับ Linux เท่านั้นไม่ใช่ใน Darwin หรือ BSD อื่น ๆ ที่ไม่ได้ใช้ procfs
คริสเตียน

@Christian Docker / LXC เป็นลินุกซ์เท่านั้นไม่เป็นไรใช่ไหม :)?
โรเบิร์ตลาครัวซ์

@RobertLacroix ดังนั้นคุณจะบอกว่าถ้าคุณไม่พบ procfs คุณไม่ได้อยู่ใน Docker? ก็ยุติธรรมพอที่ฉันจะเดา ...
คริสเตียน

112

Docker สร้าง.dockerenvและ.dockerinit( ลบออกใน v1.11 ) ไฟล์ที่ด้านบนสุดของของคอนเทนเนอร์ดังนั้นคุณอาจต้องการตรวจสอบว่ามีอยู่หรือไม่

สิ่งนี้ควรใช้งานได้

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

1
เว้นแต่คุณหรือคนอื่นได้สร้างขึ้น/.dockerinitบนโฮสต์ของคุณ (อาจจะโดยบังเอิญ) ซึ่งในกรณีนี้จะผิดจากภายนอกคอนเทนเนอร์
sosiouxme

18
หากมีคนอื่นเข้ามา / แสดงว่าพวกเขารูทและคุณมีปัญหาที่เลวร้ายยิ่งกว่าการรู้ว่าคุณอยู่ในท่าเทียบเรือหรือไม่
davey

15
ระวังการพึ่งพา/.dockerenvในระยะยาว มันเป็นเรื่องที่ไม่ได้มีเจตนาที่จะใช้วิธีนี้
ReactiveRaven

FWIW, Podman /.dockerenvไม่สร้าง มันสร้าง/run/.containerenvแต่ด้วยตรรกะที่คล้ายกันดูเหมือนว่ารายละเอียดการใช้งานจะไม่ต้องพึ่งพา ดูgithub.com/containers/libpod/issues/3586สำหรับทางเลือกเฉพาะของ podman
Beni Cherniavsky-Paskin

24

เราใช้ proc's sched (/ proc / $ PID / sched) เพื่อแยก PID ของกระบวนการ PID ของกระบวนการภายในคอนเทนเนอร์จะแตกต่างกันจากนั้นเป็น PID บนโฮสต์ (ระบบที่ไม่ใช่คอนเทนเนอร์)

ตัวอย่างเช่นผลลัพธ์ของ / proc / 1 / sched บนคอนเทนเนอร์จะส่งคืน:

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

ขณะอยู่บนโฮสต์ที่ไม่ใช่คอนเทนเนอร์:

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

สิ่งนี้ช่วยแยกความแตกต่างว่าคุณอยู่ในตู้คอนเทนเนอร์หรือไม่ เช่นคุณสามารถทำได้:

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

นี่เป็นข้อมูลที่มีค่ามาก ขอบคุณ
Fabian Lange

4
ขึ้นอยู่กับระบบปฏิบัติการ "init" อาจต้องถูกแทนที่ด้วย "systemd" ข้อมูลเพิ่มเติมเกี่ยวกับ systemd ที่นี่
BrianV

2
ดังที่ @BrianV กล่าวไว้สิ่งนี้ก็ไม่ได้ผลสำหรับฉันเช่นกัน
Shubham Chaudhary

5
ในคอนเทนเนอร์ Docker ที่ทำงานบนคลัสเตอร์ k8s head -n1 /proc/1/schedจะส่งคืนdumb-init (1, #threads: 1)ดังนั้นการตรวจสอบที่แนะนำในคำตอบนี้จึงล้มเหลว (ตรงกันข้ามกับสิ่งที่คำตอบแนะนำ PID จะแสดงเป็น "1" ในบรรทัดนั้นแม้ว่าฉันจะทำสิ่งนี้ในคอนเทนเนอร์ก็ตาม)
Stefan Majewsky

นี่ไม่ใช่วิธีแก้ปัญหาสากลแน่นอน คุณสามารถ (ประเภท) การใช้สิ่งที่คุณต้องการสำหรับคอนเทนเนอร์ของ PID 1. เช่นถ้าคุณทำมันจะเป็นdocker run --init ... docker-initถ้าคุณทำเช่นมันจะเป็นdocker run ... head -n 1 /proc/1/sched head
jpkotta

21

โซลูชันของโทมัสเป็นรหัส:

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

บันทึก

readกับตัวแปรดัมมี่เป็นสำนวนที่ง่ายสำหรับการนี้ไม่ได้ผลิตออกใด ๆ . เป็นวิธีการที่กะทัดรัดสำหรับการเปลี่ยนรูปแบบที่เป็นไปได้grepหรือawkเป็นการทดสอบรูปแบบ

หมายเหตุเพิ่มเติมเกี่ยวกับการอ่าน


10
ยกเว้น ... สิ่งนี้จะล้มเหลวในสภาพแวดล้อมบางอย่างเพราะเช่น3:cpu,cpuacct:/system.slice/docker-1ce79a0dec4a2084d54acf187a1e177e0339dc90d0218b48b4456576ecaf291e.scopeจะไม่ตรงกัน ง่ายกว่าเพื่อgrep -q docker /proc/1/cgroup; โค้ดผลลัพธ์จากนั้นก็น่าจะเพียงพอเช่นกัน
larsks

2
readอาจใช้งานได้bashแต่ในdashเชลล์ที่ใช้บ่อยที่สุดคุณต้องใช้read dummy(หรือคล้ายกัน) หรือใช้โครงสร้างเช่น[ -n "$(command)" ]
Daniel Alder

@DanielAlder จับดีแดเนียล ฉันจะปรับปรุงข้อความ
Henk Langeveld

1
ก่อนหน้านี้อ้างว่าเชลล์ที่เข้ากันได้กับ Bourne ใด ๆ สนับสนุนแบบธรรมดาที่readไม่มีชื่อตัวแปร นี่เป็นจริงสำหรับ bash และ ksh93 เท่านั้น Opengroup ระบุread varและไม่กล่าวถึงreadพฤติกรรมโดยไม่มีตัวแปรอย่างน้อยหนึ่งตัว ในทุบตีและksh93หากไม่มีvarREPLYจะได้รับการอ่านการใช้ตัวแปรเปลือก
Henk Langeveld

1
ทำไมเราใช้awk -F: '$3 ~ /docker/' /proc/self/cgroup | readไม่ได้? เหมาะสำหรับฉัน
Shubham Chaudhary

7

สิ่งที่ใช้ได้ผลสำหรับฉันคือการตรวจสอบหมายเลขไอโหนดของ "/." ภายในนักเทียบท่ามีตัวเลขที่สูงมาก นอกนักเทียบท่าตัวเลขที่ต่ำมากเช่น '2' ฉันคิดว่าวิธีนี้จะขึ้นอยู่กับ FileSystem ที่ใช้

ตัวอย่าง

ภายในนักเทียบท่า:

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

นอกนักเทียบท่า

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

ในสคริปต์:

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

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

เหมือนกับ ls -di /? ดูเหมือนว่า inode num ไม่น่าเชื่อถือบนแพลตฟอร์มที่แตกต่างกัน
yurenchen

นี่เป็นสิ่งเดียวที่ใช้ได้ผลสำหรับฉันในการแยกความแตกต่างระหว่างโฮสต์ Xen domU และคอนเทนเนอร์นักเทียบท่า
Michael Altfield

1

เราจำเป็นต้องยกเว้นกระบวนการทำงานในภาชนะ แต่แทนที่จะตรวจสอบ cgroups นักเทียบท่าเพียงแค่เราตัดสินใจที่จะเปรียบเทียบ/proc/<pid>/ns/pidกับระบบ init /proc/1/ns/pidที่ ตัวอย่าง:

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

หรือในกรณีของเราเราต้องการซับเดียวที่สร้างข้อผิดพลาดหากกระบวนการไม่ได้อยู่ในคอนเทนเนอร์

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

ซึ่งเราสามารถดำเนินการจากกระบวนการอื่นและหากรหัสออกเป็นศูนย์แสดงว่า PID ที่ระบุจะทำงานในเนมสเปซอื่น


ไม่ได้ผลสำหรับฉัน จากภายในคอนเทนเนอร์ Docker ที่กำหนดตารางเวลา k8s readlink /proc/self/ns/pidและreadlink /proc/1/ns/pidสร้างเอาต์พุตเดียวกัน
Stefan Majewsky

1
@StefanMajewsky อาจต้องการลองใช้github.com/jessfraz/amicontainedเพื่อดูว่าคุณลักษณะใดบ้างที่เปิดใช้งานในรันไทม์ของคอนเทนเนอร์
Greg Bray

0

จากความคิดเห็นของ Dan Walsh เกี่ยวกับการใช้ SELinux ps -eZ | grep container_tแต่ไม่จำเป็นpsต้องติดตั้ง:

$ 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

นี่เป็นการบอกว่าคุณกำลังทำงานในไฟล์ภาชนะ แต่ไม่ซึ่งรันไทม์

ไม่ได้ตรวจสอบระยะเวลาการทำงานของคอนเทนเนอร์อื่น ๆ แต่https://opensource.com/article/18/2/understand-selinux-labels-container-runtimesให้ข้อมูลเพิ่มเติมและแนะนำว่าสิ่งนี้ใช้กันอย่างแพร่หลายอาจใช้ได้กับ rkt และ lxc หรือไม่


0

รหัส 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

ฉันได้สร้างสคริปต์ python ขนาดเล็ก หวังว่าจะมีคนพบว่ามีประโยชน์ :-)

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

มันเจ๋ง แต่จะช่วยระบุได้อย่างไรว่าคุณอยู่ในตู้คอนเทนเนอร์หรือไม่?
user528025

FileNotFoundError: [Errno 2] No such file or directory: '/sys/fs/cgroup/memory/docker/<docker_id>/memory.stat'
Scrooge McDuck
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.