[翻译] 在 CI 或测试环境中使用 Docker-in-Docker,三思而后行

发布日期:2024-04-08 18:01:01

原文地址:Using Docker-in-Docker for your CI or testing environment? Think twice.

Docker-in-Docker 的主要目的是帮助 Docker 本身的开发。许多人使用它来运行 CI(例如使用 Jenkins),起初这似乎很好,但他们遇到了许多“有趣”的问题,这些问题可以通过将 Docker 套接字(socket)“绑定安装”到 Jenkins 容器中来避免。

我们看看这意味着什么。如果您想要不包含详细信息的快速解决方案,只需滚动到本文底部即可。☺

更新(2020 年 7 月):当我在 2015 年写这篇博客文章时,运行 Docker-in-Docker 的唯一方法是在 Docker 上使用 -privileged 标签。今天,情况大不相同。容器安全性和沙盒技术(sandboxing)有了显著的进步,例如可以通过 rootless 容器和 sysbox 等工具实现。后者允许运行 Docker-in-Docker 时不需要 -privileged 标签,甚至还为一些特定场景提供了优化,比如将 Kubernetes 集群的多个节点作为普通容器运行。这篇文章已经针对这一点进行了更新!

Docker-in-Docker:好的方面

两年多前,我在 Docker 中贡献了 -privileged 标签,并编写了 dind 的第一个版本。目标是帮助核心团队更快地进行 Docker 开发。在 Docker-in-Docker 出现之前,典型的开发周期是:

  • 方案尝试(hackity hack)
  • 构建
  • 停止当前运行的 Docker 的守护进程
  • 运行新的 Docker 的守护进程
  • 测试
  • 重复

如果你想要一个漂亮的、可复制的构建(即在容器中),它就有点复杂了:

  • 方案尝试(hackity hack)
  • 确保 Docker 的可用版本正在运行
  • 使用旧 Docker 构建新 Docker
  • 停止 Docker 守护程序
  • 运行新的 Docker 守护进程
  • 测试
  • 停止新的 Docker 守护进程
  • 重复

随着 Docker-in-Docker 的出现,这被简化为:

  • 方案尝试(hackity hack)
  • 同时做构建 + 运行
  • 重复

好多了,对吧?

Docker-in-Docker:坏的方面

然而,与主流的观点相对的是,Docker-in-Docker 并不是 100% 由火花、小马和独角兽组成的(这里表示并不完美)。我在这里的意思是,有几个问题需要注意。

一个是关于像 AppArmor 和 SELinux 这样的 LSM(Linux 安全模块):当启动一个容器时,“内部 Docker”可能会在尝试应用安全配置文件时与“外部 Docker”产生冲突或混淆。合并 -privileged 标签的原始实现是当时最难解决的问题。我的更改在 Debian 机器和 Ubuntu 测试虚拟机上有效(所有测试都会通过),但它会在 Michael Crosby 的机器上崩溃(如果我记得很清楚的话,那就是 Fedora)。我记不清问题的确切原因了,但可能是因为 Mike 是一个聪明的人,他使用 SELINUX=enforce 运行(我当时使用的是 AppArmor),我的更改并没有考虑 SELINUX 的配置文件。

Docker-in-Docker:丑陋的方面

第二个问题与存储驱动器有关。当你在 Docker 中运行 Docker 时,外部 Docker 运行在普通的文件系统(EXT4、Btrfs 等)之上,但内部 Docker 运行在写时复制(copy-on-write)系统(AUFS、Btrfs、Device Mapper 等,具体取决于外部 Docker 设置使用什么)之上。这会有许多组合不起作用。例如,你不能在 AUFS 之上运行 AUFS。如果你在 Btrfs 之上运行 Btrfs,它应该首先工作,但一旦你有嵌套的子卷,删除父卷就会失败。Device Mapper 不支持命名空间,所以如果多个 Docker 实例在同一台机器上使用它,它们都可以看到(并影响)彼此的镜像和容器支持设备。没有好办法。

对于许多这些问题,都有妥协方案;例如,如果你想在内部的 Docker 中使用 AUFS,只需将 /var/lib/docker 提升为卷,一切就都正常了。Docker 为 Device Mapper 目标名称添加了一些基本的命名空间,这样如果同一台机器上运行了多个 Docker 调用,它们就不会互相影响了。

然而,这些设置并不简单直接,正如您在 GitHub 上的 dind 存储库上看到的那些问题一样。

Docker-in-Docker:情况越来越糟

那么构建缓存呢?这可能也会变得相当棘手。人们经常问我,“我正在运行 Docker-in-Docker;我如何使用位于我的主机上的镜像,而不是在我内部的 Docker 中再次拉取所需镜像?”

一些喜欢冒险的人试图将 /var/lib/docker 从主机绑定到 Docker-in-Docker 容器中。有时他们与多个容器共享 /var/lib/docker

pFO18Gq.jpg

Docker 守护进程被明确设计为具有对 /var/lib/docker 的独占访问权限。其他任何东西都不应该触及、戳动或触动隐藏在那里的任何 Docker 文件。

这是为什么呢?这是从 dotCloud 时代学到的最难的一课。dotCloud 容器引擎通过让多个进程同时访问 /var/lib/dotcloud 来工作。像原子文件替换(而不是就地编辑)、在代码中添加自愿和强制锁定(peppering the code with advisory and mandatory locking)等聪明的技巧,以及像 SQLite 和 BDB 这样的安全系统的其他实验,只让我们走到现在;当我们重构我们的容器引擎(最终成为 Docker)时,一个重大的设计决定是将所有容器操作集中到一个守护进程中,并完成所有的无意义的并发访问。

(不要误解我的意思:完全有可能做一些漂亮、可靠、快速的事情,来支持多个进程和最先进的并发管理;但我们认为,使用 Docker 的单角色模型更简单,也更易于编写和维护。)

这意味着,如果你在多个 Docker 实例之间共享 /var/lib/docker 目录,你会遇到麻烦。当然,它可能会起作用,特别是在早期测试期间。“看,妈妈,我可以 docker run ubuntu!”但是试着做一些更复杂的事情(从两个不同的实例中提取相同的镜像……),然后看着世界燃烧。

这意味着,如果你的 CI 系统进行构建和重新构建,每次重启 Docker-in-Docker 容器时,你可能会清除其缓存。这真的不好。

Docker-in-Docker:然后它会变得更好

你肯定听过马克·吐温那句名言的变体:“他们不知道这是不可能的,所以他们做了。”

许多人试图在安全地运行 Docker-in-Docker。几年前,我在用户名称空间和一些非常讨厌的黑客攻击方面取得了一定的成功(包括在 tmpfs 挂载上伪造的 cgroups pseudo-fs 结构,以便容器运行时就不会各种报警或报错;真是有趣),但看起来一个干净的解决方案将是一项重大努力。

这种干净的解决方案现在已经存在:它被称为 sysbox。Sysbox 是一个 OCI 运行时,可以代替 runc 使用,也可以在 runc 之外使用。它使通常需要拥有特权标志才能运行的“系统容器”,在不需要特权标志时也可以运行;并且在这些容器之间以及在这些容器与其宿主之间提供足够的隔离。

Sysbox 还提供了在容器中运行容器(containers-in-containers)的优化。具体来说,当并行运行 Docker 的多个实例时,可以用一组共享的镜像“播种”它们。这节省了大量的磁盘空间和时间,我认为这在容器中运行例如 Kubernetes 节点时会产生巨大的不同。

(当您希望部署 Kubernetes 暂存应用程序(staging app)或在其自己的集群中运行测试时,在容器中运行 Kubernete 节点对 CI/CD 特别有用,节省了在专用机器上部署完整集群基础设施的成本和时间开销。)

长话短说:如果你的用例真的绝对要求 Docker-in-Docker 时,看看 sysbox,它可能就是你所需要的。

基于套接字(socket)的解决方案

让我们后退一步。你真的需要 Docker-in-Docker 吗?或者,当 CI 系统本身在一个容器中时,你只是想从你的 CI 系统中运行 Docker(特别是:构建、运行,有时推送容器和映像)吗?

我敢打赌,大多数人都想要后者。您所需要的只是一个解决方案,以便像 Jenkins 这样的 CI 系统可以启动容器。

最简单的方法是将 Docker 套接字暴露给 CI 容器,方法是使用 -v 标志绑定安装它。

简单地说,当你启动你的 CI 容器( Jenkins 或其他)时,不要在 Docker-in-Docker 使用一些 hacking 的手段,而是通过:

docker run -v /var/run/docker.sock:/var/run/docker.sock ...

现在,这个容器将可以访问 Docker 套接字(socket),因此拥有了启动容器的能力。不同的是,它将启动“兄弟”容器,而不是启动“子”容器。

使用 docker 官方图像(其中包含 docker 二进制文件)进行尝试:

docker run -v /var/run/docker.sock:/var/run/docker.sock \-ti docker

⚠️ Former versions of this post advised to bind-mount the docker binary from the host to the container. This is not reliable anymore, because the Docker Engine is no longer distributed as (almost) static libraries.

If you want to use e.g. Docker from your Jenkins CI system, you have multiple options:

installing the Docker CLI using your base image’s packaging system (i.e. if your image is based on Debian, use .deb packages),
using the Docker API.

这看起来像 Docker-in-Docker,感觉就像 Docker-in-Docker,但它不是 Docker-in-Docker:当这个容器将创建更多的容器时,这些容器将在顶级 Docker 中创建。您将不会遇到嵌套的副作用,并且构建缓存将在多个调用之间共享。

⚠️ 这篇文章的前几个版本建议将 docker 二进制文件从主机绑定到容器。这不再可靠,因为 Docker 引擎不再作为(几乎)静态库分发。

如果您想在 Jenkins CI 系统中使用 Docker,您有多种选择:

  • 使用基础映像的打包系统安装 Docker CLI(即,如果镜像基于 Debian,请使用 .deb 包),
  • 使用 Docker API。

参考文档:

[1] Using Docker-in-Docker for your CI or testing environment? Think twice. https://jpetazzo.github.io/2015/09/03/do-not-use-docker-in-docker-for-ci/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/803327.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[NKCTF2024]-PWN:leak解析(中国剩余定理泄露libc地址,汇编覆盖返回地址)

查看保护 查看ida 先放exp 完整exp: from pwn import* from sympy.ntheory.modular import crt context(log_leveldebug,archamd64)while True:pprocess(./leak)ps[101,103,107,109,113,127]p.sendafter(bsecret\n,bytes(ps))cs[0]*6for i in range(6):cs[i]u32(p…

Java 基于微信小程序的校园请教小程序的研究与实现,附源码

博主介绍:✌程序员徐师兄、10年大厂程序员经历。全网粉丝12W、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末获取源码联系🍅 👇🏻 精彩专栏推荐订阅&#x1f447…

SpringBoot整合Spring Data JPA

✅作者简介:大家好,我是Leo,热爱Java后端开发者,一个想要与大家共同进步的男人😉😉🍎个人主页:Leo的博客💞当前专栏: 循序渐进学SpringBoot ✨特色专栏: MySQL学习 🥭本文内容: SpringBoot整合Spring Data JPA 📚个人知识库: Leo知识库,欢迎大家访问 1.…

淘宝销量API商品详情页原数据APP接口测试㊣

淘宝/天猫获得淘宝app商品详情原数据 API 返回值说明 item_get_app-获得淘宝app商品详情原数据 公共参数 名称类型必须描述keyString是调用key(必须以GET方式拼接在URL中)secretString是调用密钥api_nameString是API接口名称(包括在请求地…

Java-StringBuilder容器

一、基础用法 1.创建对象 StringBuilder sbnew StringBuilder(); 2.添加元素 可以添加整型、浮点型、字符串等。 sb.append(1); sb.append(2.3); sb.append(true); 3.反转 sb.reverse(); 4.获取长度 int len sb.length(); 5.转变成字符串 tring strsb.toString(); …

Python学习笔记11 - 列表

1. 列表的创建与删除 2. 列表的查询操作 3. 列表的增、删、改操作 4. 列表元素的排序 5. 列表生成式

利用IP地址判断羊毛用户:IP数据云提供IP风险画像

在当今数字化社会,互联网已经成为人们日常生活和商业活动中不可或缺的一部分。然而,随着网络的普及,网络欺诈行为也日益猖獗,其中包括了羊毛党这一群体。羊毛党指的是利用各种手段获取利益、奖励或者优惠而频繁刷取优惠券、注册账…

png转换成jpg格式?这几种方法很简单

在发送电子邮件时,附件的大小是一个重要的考虑因素。将PNG图像转换为jpg格式可以减小文件大小,减少附件的传输时间和存储空间占用。这对于商务邮件、个人邮件或邮件营销活动中的图片附件都非常有用,下面就介绍几个可以快速完成图片转格式的方…

Docker容器与虚拟化技术:OpenEuler 部署 Prometheus 与 Grafana

目录 一、实验 1.环境 2.OpenEuler 部署 Prometheus 3.OpenEuler 部署 Grafana 4.使用cpolar内网穿透 二、问题 1.拉取镜像失败 2.如何导入Grafana监控模板(ES) 一、实验 1.环境 (1)主机 表1 主机 系统架构版本IP备注…

Scrapy框架spider类异常处理

说明:仅供学习使用,请勿用于非法用途,若有侵权,请联系博主删除 作者:zhu6201976 一、捕获Request所有网络相关异常 在spider类中,我们构造Request对象或FormRequest对象时,可传递参数errback回调…

BugKu:Simple SSTI

1.进入此题 2.查看源代码 可以知道要传入一个名为flag的参数,又说我们经常设置一个secret_key 3.flask模版注入 /?flag{{config.SECRET_KEY}} 4.学有所思 4.1 什么是flask? flask是用python编写的一个轻量web开发框架 4.2 SSTI成因(SST…

【数据结构与算法】:堆排序和选择排序

1. 堆排序 堆排序是一种比较复杂的排序算法,因为它的流程比较多,理解起来不会像冒泡排序和选择排序那样直观。 1.1 堆的结构 要理解堆排序,首先要理解堆。堆的逻辑结构是一棵完全二叉树,物理结构是一个数组。 (如果不知道什么是…

链表的中间结点——每日一题

题目链接: OJ链接 题目: 给你单链表的头结点 head ,请你找出并返回链表的中间结点。 如果有两个中间结点,则返回第二个中间结点。 示例 1: 输入:head [1,2,3,4,5] 输出:[3,4,5] 解释&…

【架构师】-- 成长路线图

成长为软件架构师不是一件容易的事,这篇文章列举了架构师需要学习的技术储备,给出了成为软件架构师的路线图,帮助有志于在架构领域成长的同学可以明确学习的方向。原文:Master Plan for becoming a Software Architect[1] 软件架…

【优选算法专栏】专题十八:BFS解决拓扑排序(一)

本专栏内容为:算法学习专栏,分为优选算法专栏,贪心算法专栏,动态规划专栏以及递归,搜索与回溯算法专栏四部分。 通过本专栏的深入学习,你可以了解并掌握算法。 💓博主csdn个人主页:小…

shamrockcms代码审计-啥也没有

shamrockcms 环境搭建 使用阿里源,创建数据库,运行shamrockcms.sql文件,将configure.properties中的jdbc修改为自己本地或者其他ip数据库连接,并且将ueditor.config.json中的master修改为localhost或者其他自己设置的ip 危险组件…

基于知识图谱的推理:智能决策与自动发现

基于知识图谱的推理:智能决策与自动发现 一、引言 在今天这个数据驱动的时代,我们经常会听到人们提及“知识图谱”这个词。知识图谱,作为一种结构化知识的表达方式,已经成为智能系统不可或缺的一部分,它通过连接大量的…

numpy,matplotilib学习(菜鸟教程)

所有内容均来自于: NumPy 教程 | 菜鸟教程 Matplotlib 教程 | 菜鸟教程 numpy模块 numpy.nditer NumPy 迭代器对象 numpy.nditer 提供了一种灵活访问一个或者多个数组元素的方式。 for x in np.nditer(a, orderF):Fortran order,即是列序优先&#x…

三小时使用鸿蒙OS模仿羊了个羊,附源码

学习鸿蒙arkTS语言,决定直接通过实践的方式上手,而不是一点点进行观看视频再来实现。 结合羊了个羊的开发思路,准备好相应的卡片素材后进行开发。遇到了需要arkTS进行解决的问题,再去查看相应的文档。 首先需要准备卡片对应的图片…

【STL】顺序容器与容器适配器

文章目录 1顺序容器概述1.1array1.2forward_list1.3deque 2.如何确定使用哪种顺序容器呢?3.容器适配器的概念4.如何定义适配器呢? 1顺序容器概述 给出以下顺序容器表: 顺序容器类型作用vector可变大小的数组,支持快速访问&#…