在容器内访问Docker套接字

32

我正在尝试创建一个容器,可以通过docker套接字文件(主机- /var/run/docker.sock)访问主机docker远程API。

这里的答案建议将请求代理到套接字。我该如何做?

3个回答

39
如果想要在容器内部使用Docker,就必须清楚地了解安全方面的影响。
从容器内部访问Docker很简单:
1. 使用官方的docker镜像或在容器内安装Docker。或者您可以按照此处所述下载docker客户端二进制文件的归档文件。 2. 将Docker Unix套接字从主机暴露给容器。
这就是为什么。
docker run -v /var/run/docker.sock:/var/run/docker.sock \
       -ti docker

应该可以解决问题。

或者,您可以将其暴露到容器中并使用 Docker REST API

更新:此答案的早期版本(基于以前版本的 jpetazzo post)建议从主机向容器绑定挂载Docker二进制文件。这不再可靠,因为Docker引擎不再作为(几乎)静态库分发。

考虑事项:

  1. 所有主机容器都将对容器可访问,因此它可以停止它们、删除它们,在顶级Docker容器内以任何用户身份运行任何命令。
  2. 所有创建的容器都在顶层Docker中创建。
  3. 当然,您应该了解,如果容器可以访问主机的Docker守护程序,则具有对整个主机系统的特权访问。根据容器和系统(AppArmor)配置的不同,可能更或者更少危险。
  4. 其他警告请参见 不要公开Docker套接字

像将/var/lib/docker暴露给容器这样的其他方法很可能会导致数据损坏。有关详细信息,请参见不要在CI中使用Docker in Docker

适用于官方Jenkins CI容器的用户的注意事项

在此容器中(可能在许多其他容器中),Jenkins进程作为非root用户运行。这就是为什么它没有权限与docker套接字交互的原因。因此,快速而简单的解决方案是运行

docker exec -u root ${NAME} /bin/chmod -v a+s $(which docker)

启动容器后,所有用户都可以以root权限运行docker二进制文件。更好的方法是允许通过无需密码的sudo来运行docker二进制文件,但官方的Jenkins CI镜像似乎缺少sudo子系统。


2
此外,还有一篇非常高级的文章:http://jpetazzo.github.io/2016/04/03/one-container-to-rule-them-all/。 - Dmitriusan
安装Docker二进制文件是不鼓励的此前版本的文章建议将主机上的Docker二进制文件绑定到容器中。这已经不再可靠,因为Docker引擎不再作为(几乎)静态库分发。 - Murmel
谢谢你的 chmod,这确实有帮助! - johncorner06

32

3
你绝不能这样做,参见 https://www.lvh.io/posts/dont-expose-the-docker-socket-not-even-to-a-container.html - Zarathustra
1
@Zarathustra同意并感谢。然而,问题并不在于是否应该这样做。我会在这里更新答案并发出警告。 - Desmond Morris
3
这就是Portainer的实现方式。 - djangofan
2
那么,有什么替代方案吗? - Carlos Martinez
1
@DesmondMorris 看起来链接已经失效了。你有更新的版本吗? - Lucas
1
@Lucas https://www.lvh.io/posts/dont-expose-the-docker-socket-not-even-to-a-container/ - ᴠɪɴᴄᴇɴᴛ

10

尝试在作为nobody用户运行的容器中进行docker socket调用时,我偶然发现了这个页面。

我的情况是,当my-service尝试调用docker套接字以列出可用容器时,我遇到了访问被拒绝的错误。

最终,我使用docker-socket-proxy将docker socket代理到my-service。这是一种不同的在容器内访问docker套接字的方法,因此我想分享一下。

我使my-service能够通过DOCKER_HOST环境变量接收应该与之通信的docker主机(在本例中为docker-socker-proxy)。

请注意,docker-socket-proxy需要作为root用户运行,才能将docker套接字代理到my-service

示例docker-compose.yml

version: "3.1"

services:
  my-service:
    image: my-service
    environment:
      - DOCKER_HOST=tcp://docker-socket-proxy:2375
    networks:
      - my-service_my-network
  docker-socket-proxy:
    image: tecnativa/docker-socket-proxy
    environment:
      - SERVICES=1
      - TASKS=1
      - NETWORKS=1
      - NODES=1
    volumes:
     - /var/run/docker.sock:/var/run/docker.sock
    networks:
      - my-service_my-network
    deploy:
      placement:
        constraints: [node.role == manager]

networks:
  my-network:
    driver: overlay

请注意,上述的compose文件已经准备好用于Swarm(docker stack deploy my-service),但也可以在Compose模式下工作(docker-compose up -d)。这种方法的好处是my-service不再需要在Swarm manager上运行。


2
您介意解释一下为什么您的网络被称为 my-network,但您的服务将其称为 my-service_my-network 吗? - Geoff

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