如何从 Docker Linux 容器内部获取容器信息?

208

我希望我的 docker 容器 能够像通过元数据获取有关 EC2 实例的信息一样,意识到它们的配置。

我可以使用(前提是 docker 在端口 4243 上监听)。

curl http://172.17.42.1:4243/containers/$HOSTNAME/json

想获取容器的一些数据,但是希望知道是否有更好的方法至少可以获取容器的完整ID,因为HOSTNAME被缩短到12个字符,docker似乎对其执行了“最佳匹配”。

另外,如何获取docker主机的外部IP(除了访问EC2元数据之外,该元数据仅适用于AWS)?


4
注意:在尝试使用容器内的 /var/run/docker.sock 之前,您应该阅读此文章 https://www.lvh.io/posts/dont-expose-the-docker-socket-not-even-to-a-container.html 。在下面尝试使用此方法之前,请谨慎考虑。 - harschware
9
如果 @harschware 的链接失效了,我来做个总结:通过给容器访问 /var/run/docker.sock 权限,可以轻松地打破 Docker 提供的封闭环境,并获取主机的权限。显然,这可能是危险的。 - ProgrammingLlama
1
有人知道如何在使用了--hostname参数的run命令的Windows Docker容器中获取相同的信息,以便简单地运行“hostname”不再提供容器ID吗? - Timothy John Laird
将Docker守护程序API暴露给容器在安全方面是非常糟糕的方法。因为受损的容器可以访问Docker并执行任何想要的操作。应该有一些单独的API仅用于获取非私有信息。 - Marat Mkhitaryan
16个回答

123

除非被覆盖,否则在Docker 1.12中主机名似乎是短容器ID。

root@d2258e6dec11:/project# cat /etc/hostname
d2258e6dec11

外部地

$ docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED                 STATUS                      PORTS               NAMES
d2258e6dec11        300518d26271        "bash"              5 minutes ago       

$ docker -v
Docker version 1.12.0, build 8eab29e, experimental

8
这让我在Node.js中轻松获取信息。 const os = require('os'); console.log(os.hostname()); - pulkitsinghal
4
要在Java中获取与容器ID匹配的主机名,使用InetAddress.getLocalHost().getHostName() - Nathan
3
有时候在读取环境变量$HOSTNAME的值时更简单(例如在shell脚本中)。 - Faheel
1
在 Windows 容器中,相当于 COMPUTERNAME 环境变量。 - solstice333
如果在Windows上工作,请确保在传递到Docker Engine API之前将COMPUTERNAME值转换为小写。 - nickwesselman
通过以下 Python 代码可以实现:import socket; socket.gethostname() - Gulzar

79

我发现容器ID可以在/proc/self/cgroup中找到

所以你可以通过以下方式获取该ID:

cat /proc/self/cgroup | grep -o  -e "docker-.*.scope" | head -n 1 | sed "s/docker-\(.*\).scope/\\1/"

19
必须稍作调整,这在 Docker 1.4.1 中对我有效。cat /proc/self/cgroup | grep "docker" | sed s/\\//\\n/g | tail -1 - ICas
7
对于 Docker 1.6.2,我需要使用以下命令:cat /proc/self/cgroup | grep 'docker' | sed 's/^.*\///' | tail -n1 - Jay Taylor
16
Docker 1.12: cat /proc/1/cgroup | grep 'docker/' | tail -1 | sed 's/^.*\///' | cut -c 1-12 翻译为: Docker 1.12版本:cat /proc/1/cgroup | grep 'docker/' | tail -1 | sed 's/^.*\///' | cut -c 1-12 - smets.kevin
36
我有点喜欢 basename "$(cat /proc/1/cpuset)"basename "$(head /proc/1/cgroup)" - madeddie
7
将来如果在Docker中使用cgroup命名空间和cgroup v2,这种方法可能就不再适用了。 - Jing Qiu
显示剩余14条评论

50

对我来说,madeddie的评论最为优雅:

CID=$(basename $(cat /proc/1/cpuset))

5
能否详细说明这个实际上是做什么的?如果用户覆盖了容器的主机名,它是否仍然有效?它是否需要在容器外部访问文件系统? - simon
1
太好了!当 network_mode=host 时可以工作,使用 export SCID=${CID:0:12} 来获取短容器 ID - 如在 docker ps 中显示的那样。 - pangyuteng
在WSL和Arch中,cpuset/,因此为了使其在WSL和Docker中正常工作,我使用以下命令删除尾随斜杠: export _CID=${$(basename $(cat /proc/1/cpuset))//\/}export _SCID=${_CID:0:12} 这样我的shell命令就可以检查_CID变量是否设置。 - Loligans

37
您可以通过Docker Remote API使用Unix套接字从容器内部与Docker通信:

https://docs.docker.com/engine/reference/api/docker_remote_api/

在容器中,您可以通过检查$HOSTNAME环境变量来找出简写的Docker id。
根据文档,有发生冲突的小概率,但我认为对于数量较少的容器,您不必担心。我不知道如何直接获得完整的id。
您可以像banyan答案中概述的那样检查容器。
GET /containers/4abbef615af7/json HTTP/1.1

回应:

HTTP/1.1 200 OK
Content-Type: application/json

{
         "Id": "4abbef615af7......  ",
         "Created": "2013.....",
         ...
}

或者,您可以将Docker ID转移到文件中的容器中。该文件位于“挂载卷”上,因此会被传输到容器中:

docker run -t -i -cidfile /mydir/host1.txt -v /mydir:/mydir ubuntu /bin/bash

容器中的文件 /mydir/host1.txt 中将包含 Docker ID(缩短版)。

2
谢谢,但这是我正在使用的相同方法,如果您在运行docker时使用-h设置主机名,它将会中断。 - Alessandro
@Alessandro 我已经添加了关于-cidfile参数的信息到docker run中。这可能会帮助你将docker id传递给容器,而不是使用$HOSTNAME。 - Jiri
1
太好了!是的,这是我可以使用的东西!谢谢! - Alessandro
奇怪的是,在1.11.2中,env似乎没有列出HOSTNAME,但是echo $HOSTNAME可以工作。 - Jesse Glick
这根本不起作用,而且你的URL已经损坏并且现在重定向到错误的文档。requests.exceptions.MissingSchema: Invalid URL '/containers/1d26a841bf07/json': No schema supplied. Perhaps you meant http:///containers/1d26a841bf07/json? - Cerin

24

这将从容器内获取完整的容器ID:

cat /proc/self/cgroup | grep "cpu:/" | sed 's/\([0-9]\):cpu:\/docker\///g'

24

警告: 在您考虑使用此方法之前,您应该了解此方法的安全风险John对风险的总结:

通过将容器访问/var/run/docker.sock,轻松突破Docker提供的隔离,并访问主机。很明显,这是潜在的危险。


在容器内,dockerId是您的主机名。 因此,您可以:

  • 在容器中安装与主机版本相同的docker-io包
  • 使用--volume/var/run/docker.sock:/var/run/docker.sock --privileged启动它
  • 最后,在容器内运行:docker inspect $(hostname)

避免使用此方法。除非您了解风险并具备明确的风险缓解措施。


1
我怀疑如果使用了docker run的--hostname选项,这个方法可能行不通。 - hairyhenderson
如果设置了 --hostname,您可以使用此答案和 @Jay Taylor 在被接受的答案中的评论的组合:docker inspect $(cat /proc/self/cgroup | grep 'docker' | sed 's/^.*\///' | tail -n1) 来获取有关正在运行的容器的所有信息。 - Michael K.
你能否在其中加入对docker-io的引用? - Brad P.
我猜你指的是 https://www.npmjs.com/package/docker-io/,但这只是谷歌告诉我的,也许不是你想要的。 - Brad P.
那个缓解措施会是什么? - human

20

为了简单起见,

  1. 容器 ID 是您在 Docker 中的主机名
  2. 容器信息可以在 /proc/self/cgroup 中获得

要获取主机名,

hostname
或者
uname -n
或者
cat /etc/host

可以将输出重定向到任何文件,并从应用程序中读取回来 例如:# hostname > /usr/src//hostname.txt


14

我发现17.09版本中有一种最简单的方法可以在Docker容器内完成:

$ cat /proc/self/cgroup | head -n 1 | cut -d '/' -f3
4de1c09d3f1979147cd5672571b69abec03d606afcc7bdc54ddb2b69dec3861c

或者像已经说过的那样,用较短的版本

$ cat /etc/hostname
4de1c09d3f19

或者简单地说:

$ hostname
4de1c09d3f19

9
默认情况下,Docker会将容器ID设置为主机名,但用户可以使用--hostname来覆盖这个设置。相反,可以检查/proc
$ more /proc/self/cgroup
14:name=systemd:/docker/7be92808767a667f35c8505cbf40d14e931ef6db5b0210329cf193b15ba9d605
13:pids:/docker/7be92808767a667f35c8505cbf40d14e931ef6db5b0210329cf193b15ba9d605
12:hugetlb:/docker/7be92808767a667f35c8505cbf40d14e931ef6db5b0210329cf193b15ba9d605
11:net_prio:/docker/7be92808767a667f35c8505cbf40d14e931ef6db5b0210329cf193b15ba9d605
10:perf_event:/docker/7be92808767a667f35c8505cbf40d14e931ef6db5b0210329cf193b15ba9d605
9:net_cls:/docker/7be92808767a667f35c8505cbf40d14e931ef6db5b0210329cf193b15ba9d605
8:freezer:/docker/7be92808767a667f35c8505cbf40d14e931ef6db5b0210329cf193b15ba9d605
7:devices:/docker/7be92808767a667f35c8505cbf40d14e931ef6db5b0210329cf193b15ba9d605
6:memory:/docker/7be92808767a667f35c8505cbf40d14e931ef6db5b0210329cf193b15ba9d605
5:blkio:/docker/7be92808767a667f35c8505cbf40d14e931ef6db5b0210329cf193b15ba9d605
4:cpuacct:/docker/7be92808767a667f35c8505cbf40d14e931ef6db5b0210329cf193b15ba9d605
3:cpu:/docker/7be92808767a667f35c8505cbf40d14e931ef6db5b0210329cf193b15ba9d605
2:cpuset:/docker/7be92808767a667f35c8505cbf40d14e931ef6db5b0210329cf193b15ba9d605
1:name=openrc:/docker

这里有一个方便的一行代码可以提取容器ID:
$ grep "memory:/" < /proc/self/cgroup | sed 's|.*/||'
7be92808767a667f35c8505cbf40d14e931ef6db5b0210329cf193b15ba9d605

7

到目前为止,我看到有三个地方可能适用,每个地方都有优缺点:

  1. echo $HOSTNAMEhostname
  2. cat /proc/self/cgroup
  3. cat /proc/self/mountinfo

$HOSTNAME 很容易,但是它是部分的,并且也会被 K8s 覆盖为 pod 名称。

/proc/self/cgroup 在使用 cgroupV1 时似乎有效,但在 cgroupV2 中不可用。

/proc/self/mountinfo 对于 cgroupV2 仍将具有容器 ID,但是挂载点将根据不同的容器运行时具有不同的值。

  • 例如,在 Docker 引擎中,该值如下:
678 655 254:1 /docker/containers/7a0144cee1256c539fab790199527b7051aff1b603ebcf7ed3fd436440ef3b3a/resolv.conf /etc/resolv.conf rw,relatime - ext4 /dev/vda1 rw

ContainerD中(最近的K8s默认引擎),它看起来像这样:
1733 1729 0:35 /kubepods/besteffort/pod3272f253-be44-4a82-a541-9083e68cf99f/7a0144cee1256c539fab790199527b7051aff1b603ebcf7ed3fd436440ef3b3a /sys/fs/cgroup/blkio ro,nosuid,nodev,noexec,relatime master:17 - cgroup cgroup rw,blkio

此外,所有上述的最大问题是它们都是实现,没有抽象化,而且它们随时可能会改变。有一个正在努力将其标准化的项目,我认为值得关注:

https://github.com/opencontainers/runtime-spec/issues/1105


对于使用cgroupV2的主机上的较新版本Docker,这似乎是唯一可行的选项(mountinfo)。 - Jonas Andersson
谢谢!上面的大多数答案实际上更加脆弱,或者说没有意义,因为它们依赖于主机名是容器ID等。这是一个相当详细的问题解释以及一些解决方法(目前可行)。 - takecare

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接