Docker in Docker(其实是 Docker outside Docker): /var/run/docker.sock

在 Docker 容器里面使用 docker run/docker build

Docker 容器技术目前是微服务/持续集成/持续交付领域的第一选择。而在 DevOps 中,咱们须要将各类后端/前端的测试/构建环境打包成 Docker 镜像,而后在须要的时候,Jenkins 会使用这些镜像启动容器以执行 Jenkins 任务。html

为了方便维护,咱们的 CI 系统如 Jenkins,也会使用 Docker 方式部署。
Jenkins 任务中有些任务须要将微服务构建成 Docker 镜像,而后推送到 Harbor 私有仓库中。
或者咱们全部的 Jenkins Master 镜像和 Jenkins Slave 镜像自己都不包含任何额外的构建环境,执行任务时都须要启动包含对应环境的镜像来执行任务。前端

咱们的 Jenkins Master、Jenkins Slaves 都是跑在容器里面的,该如何在它们里面调用 docker run 命令启动包含 CI 环境的镜像呢?
在这些 CI 镜像里面,咱们从源码编译完成后,又如何经过 docker build 将编译结果打包成 Docker 镜像,而后推送到内网仓库呢?python

答案下面揭晓。docker

/var/run/docker.sock

Docker 采起的是 Client/Server 架构,咱们经常使用的 docker xxx 命令工具,只是 docker 的 client,咱们经过该命令行执行命令时,其实是在经过 client 与 docker engine 通讯。后端

咱们经过 apt/yum 安装 docker-ce 时,会自动生成一个 systemd 的 service,因此安装完成后,须要经过 sudo systemctl enable docker.service 来启用该服务。
这个 Docker 服务启动的,就是 docker engine,查看 /usr/lib/systemd/system/docker.service,能看到有这样一条语句:网络

ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock

默认状况下,Docker守护进程会生成一个 socket(/var/run/docker.sock)文件来进行本地进程通讯,所以只能在本地使用 docker 客户端或者使用 Docker API 进行操做。
sock 文件是 UNIX 域套接字,它能够经过文件系统(而非网络地址)进行寻址和访问。架构

若是咱们在容器内部安装 docker client,而且经过添加参数 -v /var/run/docker.sock:/var/run/docker.sock 将宿主机的 /var/run/docker.sock 文件以 volume 方式挂载到容器内部,这样就能实现 "Docker in Docker",在容器内使用 docker 命令了。socket

要记住的是,真正执行咱们的 docker 命令的是 docker engine,而这个 engine 跑在宿主机上。因此这并非真正的 "Docker in Docker".微服务

实践

version: '3.3'
services:
  jenkins-master:
    image: jenkinsci/blueocean:latest
    container_name: jenkins-master
    environment:
      - TZ=Asia/Shanghai
    ports:
      - "8080:8080"
      - "50000:50000"
    volumes:
      - ./jenkins_home:/var/jenkins_home
      - /usr/bin/docker:/usr/bin/docker  # 为容器内部提供 docker 命令行工具(这个随意)
      - /var/run/docker.sock:/var/run/docker.sock  # 容器内部经过 unix socket 使用宿主机 docker engine
    user: root
    restart: always

或者若是你想使用 python 客户端链接,在镜像构建时,用 pip install docker 将客户端放到镜像里就行了。工具

须要的时候,也能够将 /etc/docker 映射到容器内,这样容器内的 docker 命令行工具也会使用与宿主机一样的配置。

Docker 中的 uid 与 gid

经过上面的操做,咱们在容器内执行 docker ps 时,仍是极可能会遇到一个问题:权限问题

若是你容器的默认用户是 root,那么你不会遇到这个问题,由于 /var/run/docker.sock 的 onwer 就是 root.

可是通常来讲,为了限制用户的权限,容器的默认用户通常都是 uid 和 gid 都是 1000 的普通用户。这样咱们就没有权限访问 /var/run/docker.sock 了。

解决办法:

方法一(不必定有效):在构建镜像时,最后一层添加以下内容:

# docker 用户组的 id,一般都是 999
RUN groupadd -g 999 docker \
    && usermod -aG docker <your_user_name>

这样咱们的默认用户,就能使用 docker 命令了。

P.S. 999 不必定是 docker 用户组,因此上述方法某些状况下可能失效。这时仍是老老实实经过 docker run -u root 启动容器吧。(或者在 docker-compose.yml 中添加 user: root 属性)

参考

相关文章
相关标签/搜索