简体   繁体   English

如何检查 docker 容器内是否正在运行进程?

[英]How to check if a process is running inside docker container?

[Updated1] I have a shell which will change TCP kernel parameters in some functions, but now I need to make this shell run in Docker container, that means, the shell need to know it is running inside a container and stop configuring the kernel. [Updated1] I have a shell which will change TCP kernel parameters in some functions, but now I need to make this shell run in Docker container, that means, the shell need to know it is running inside a container and stop configuring the kernel.

Now I'm not sure how to achieve that, here is the contents of /proc/self/cgroup inside the container:现在我不确定如何实现,这是容器内/proc/self/cgroup的内容:

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

Any flags above can I use to figure out if this process is running inside a container?我可以使用上面的任何标志来确定此进程是否在容器内运行?

[Updated2]: I have also noticed Determining if a process runs inside lxc/Docker , but it seems not working in this case, the content in /proc/1/cgroup of my container is: [Updated2]:我还注意到Determining if a process running inside lxc/Docker ,但在这种情况下似乎不起作用,我容器的/proc/1/cgroup中的内容是:

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

No /lxc/containerid没有 /lxc/containerid

Docker creates .dockerenv and .dockerinit ( removed in v1.11 ) files at the top of the container's directory tree so you might want to check if those exist. Docker 在容器目录树的顶部创建.dockerenv .dockerinit 在 v1.11 中删除)文件,因此您可能需要检查它们是否存在。

Something like this should work.像这样的东西应该工作。

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

To check inside a Docker container if you are inside a Docker container or not can be done via /proc/1/cgroup .要检查 Docker 容器内部是否在 Docker 容器中,可以通过/proc/1/cgroup完成。 As this post suggests you can to the following:正如这篇文章所建议的那样,您可以执行以下操作:

Outside a docker container all entries in /proc/1/cgroup end on / as you can see here:在 docker 容器之外, /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:/

Inside a Docker container some of the control groups will belong to Docker (or LXC):在 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:/

We use the proc's sched (/proc/$PID/sched) to extract the PID of the process.我们使用 proc 的 sched (/proc/$PID/sched) 来提取进程的 PID。 The process's PID inside the container will differ then it's PID on the host (a non-container system).容器内进程的 PID 与主机(非容器系统)上的 PID 不同。

For example, the output of /proc/1/sched on a container will return:例如,容器上 /proc/1/sched 的输出将返回:

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

While on a non-container host:在非容器主机上:

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

This helps to differentiate if you are in a container or not.这有助于区分您是否在容器中。 eg you can do:例如你可以这样做:

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

Thomas' solution as code:托马斯的解决方案作为代码:

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

Note笔记

The read with a dummy variable is a simple idiom for Does this produce any output?使用虚拟变量read是一个简单的习惯用法,这会产生任何输出吗? . . It's a compact method for turning a possibly verbose grep or awk into a test of a pattern.这是一种将可能冗长的grepawk转换为模式测试的紧凑方法。

Additional note on read 阅读时的附加说明

Using Environment Variables使用环境变量

For my money, I prefer to set an environment variable inside the docker image that can then be detected by the application.为了我的钱,我更喜欢在 docker 映像中设置一个环境变量,然后应用程序可以检测到该环境变量。

For example, this is the start of a demo Dockerfile config:例如,这是演示Dockerfile配置的开始:

FROM node:12.20.1 as base
ENV DOCKER_RUNNING=true
RUN yarn install --production
RUN yarn build

The second line sets an envar called DOCKER_RUNNING that is then easy to detect.第二行设置一个名为DOCKER_RUNNING的 envar,然后很容易检测到它。 The issue with this is that in a multi-stage build, you will have to repeat the ENV line every time you FROM off of an external image.这样做的问题是,在多阶段构建中,每次您从外部映像FROM时都必须重复ENV行。 For example, you can see that I FROM off of node:12.20.1 , which includes a lot of extra stuff (git, for example).例如,您可以看到 I FROM off of node:12.20.1 ,其中包含很多额外的东西(例如 git)。 Later on in my Dockerfile I then COPY things over to a new image based on node:12.20.1-slim , which is much smaller:稍后在我的Dockerfile中,我将内容COPY到基于node:12.20.1-slim的新图像中,该图像要小得多:

FROM node:12.20.1-slim as server
ENV DOCKER_RUNNING=true
EXPOSE 3000
COPY --from=base /build /build
CMD ["node", "server.js"]

Even though this image target server is in the same Dockerfile , it requires the ENV var to be defined again because it has a different base image.即使此镜像目标server位于同一个Dockerfile中,它也需要再次定义 ENV var,因为它具有不同的基础镜像。

If you make use of Docker-Compose, you could instead easily define an envar there.如果您使用 Docker-Compose,您可以轻松地在那里定义一个 envar。 For example, your docker-compose.yml file could look like this:例如,您docker-compose.yml文件可能如下所示:

version: "3.8"
services:
  nodeserver:
    image: michaeloryl/stackdemo
    environment:
      - NODE_ENV=production
      - DOCKER_RUNNING=true

What works for me is to check for the inode number of the '/.'对我有用的是检查“/”的 inode 号。 Inside the docker, its a very high number.在码头内部,这是一个非常高的数字。 Outside the docker, its a very low number like '2'.在码头外,它是一个非常低的数字,例如“2”。 I reckon this approach would also depend on the FileSystem being used.我认为这种方法还取决于所使用的文件系统。

Example例子

Inside the docker:码头工人内部:

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

Outside the docker码头外

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

In a 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

golang code, via the /proc/%s/cgroup to check a process in a docker,include the k8s cluster golang 代码,通过 /proc/%s/cgroup 来检查 docker 中的进程,包括 k8s 集群

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
}

We needed to exclude processes running in containers, but instead of checking for just docker cgroups we decided to compare /proc/<pid>/ns/pid to the init system at /proc/1/ns/pid .我们需要排除在容器中运行的进程,但我们决定将/proc/<pid>/ns/pid/proc/1/ns/pid的 init 系统进行比较,而不是只检查 docker cgroups。 Example:例子:

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

Or in our case we wanted a one liner that generates an error if the process is NOT in a container或者在我们的例子中,如果进程不在容器中,我们想要一个生成错误的衬垫

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

which we can execute from another process and if the exit code is zero then the specified PID is running in a different namespace.我们可以从另一个进程执行,如果退出代码为零,则指定的 PID 正在不同的命名空间中运行。

Based on Dan Walsh's comment about using SELinux ps -eZ | grep container_t基于 Dan Walsh 关于使用 SELinux ps -eZ | grep container_t评论| ps -eZ | grep container_t , but without requiring ps to be installed: 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

This just tells you you're running in a container, but not which runtime.这只是告诉您您容器中运行,而不是在哪个运行时。

Didn't check other container runtimes but https://opensource.com/article/18/2/understanding-selinux-labels-container-runtimes provides more info and suggests this is widely used, might also work for rkt and lxc?没有检查其他容器运行时,但https://opensource.com/article/18/2/understanding-selinux-labels-container-runtimes提供了更多信息并表明这被广泛使用,可能也适用于 rkt 和 lxc?

What works for me, as long as I know the system programs/scrips will be running on, is confirming if what's running with PID 1 is systemd (or equivalent).只要我知道系统程序/脚本将在其上运行,对我有用的是确认使用PID 1运行的是否是systemd (或等效)。 If not, that's a container.如果不是,那是一个容器。 And this should be true for any linux container, not only docker.这应该适用于任何 linux 容器,而不仅仅是 docker。

Had the need for this capability in 2022 on macOS and only the answer by @at0S still works from all the other options. 2022 年在 macOS 上需要此功能,只有@at0S 的答案仍然适用于所有其他选项。

  • /proc/1/cgroup only has the root directory in a container unless configured otherwise /proc/1/cgroup除非另有配置,否则只有容器中的根目录
  • /proc/1/sched showed the same in-container process number. /proc/1/sched显示了相同的容器内进程号。 The name was different ( bash ) but that's not very portable.名称不同( bash ),但这不是很便携。
  • Environment variables work if you configure your container yourself, but none of the default environment variables helped如果您自己配置容器,环境变量会起作用,但默认环境变量都没有帮助

I did find an option not listed in the other answers: /proc/1/mounts included an overlay filesystem with "docker" in its path.我确实找到了其他答案中未列出的选项: /proc/1/mounts在其路径中包含了一个带有“docker”的overlay文件系统。

[Updated1] I have a shell which will change TCP kernel parameters in some functions, but now I need to make this shell run in Docker container, that means, the shell need to know it is running inside a container and stop configuring the kernel. [Updated1]我有一个外壳,它将在某些功能中更改TCP内核参数,但是现在我需要使该外壳在Docker容器中运行,这意味着,该外壳需要知道它在容器内运行并停止配置内核。

Now I'm not sure how to achieve that, here is the contents of /proc/self/cgroup inside the container:现在我不确定如何实现,这是容器内/proc/self/cgroup的内容:

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

Any flags above can I use to figure out if this process is running inside a container?我可以使用上面的任何标志来确定此过程是否在容器内运行吗?

[Updated2]: I have also noticed Determining if a process runs inside lxc/Docker , but it seems not working in this case, the content in /proc/1/cgroup of my container is: [Updated2]:我还注意到确定进程是否在lxc / Docker中运行,但在这种情况下似乎不起作用,我容器的/proc/1/cgroup的内容是:

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

No /lxc/containerid否/ lxc / containerid

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM