我第一次接触 Docker 可以追溯到 2015 年初。 Docker 被试验以找出它是否可以使我们受益。当时无法[在后台]运行容器,也没有任何命令可以查看容器中正在运行、调试或 ssh 的内容。实验很快,Docker 毫无用处,更接近于 alpha 原型而不是发布。快进到 2016 年。新工作、新公司和 Docker 炒作正在疯狂增长。这里的开发人员已将 docker 推入生产项目,我们坚持使用它。从好的方面来说,运行命令终于起作用了,我们可以启动、停止和查看容器。它是功能性的。在撰写本文时,我们有 12 个 dockerized 应用程序在生产中运行,分布在 AWS 上的 31 个主机上(每个主机 1 个 docker 应用程序 [注意:继续阅读以了解原因])。下面的文章讲述了我们与 Docker 的旅程,这是一次充满危险和意外转折的冒险。每个新版本都伴随着重大变化。今年年初,我们开始使用 docker 1.6 来运行单个应用程序。我们在 3 个月后更新,因为我们需要一个仅在更高版本中可用的修复程序。 1.6 分支已经被放弃了。 1.7 和 1.8 版本无法运行。我们转移到 1.9 只是为了在两周后发现一个严重的错误,所以我们升级(再次!)到 1.10。
Docker 版本之间存在各种微妙的回归。它不断以意想不到的方式打破不可预测的东西。我们必须调试的最棘手的回归与网络相关。 Docker 完全抽象了主机网络。这是一大堆端口重定向、DNS 技巧和虚拟网络。奖励:Docker 去年从官方 Debian 存储库中删除,然后该软件包从 docker.io 重命名为 docker-engine。此更改之前的文档和资源已过时。 Docker 中最需要和最缺乏的功能是清理旧图像(早于 X 天或 X 天未使用,无论如何)的命令。空间是一个关键问题,因为图像经常更新,每个图像可能需要超过 1GB。清理空间的唯一方法是运行这个 hack,最好每天在 cron 中运行:它枚举所有图像并删除它们。正在运行的容器当前正在使用的那些不能被删除(它给出了一个错误)。它很脏,但它完成了工作。 docker 之旅从一个清理脚本开始。这是每个组织都必须经历的入会仪式。
在互联网上可以找到许多尝试,但没有一种效果很好。没有列出带有日期的图像的 API,有时有,但它们在 6 个月内被弃用。一种常见的策略是从图像文件中读取日期属性并调用“docker rmi”,但在命名更改时失败。另一种策略是直接读取日期属性并删除文件,但如果做得不好会导致损坏,并且除了Docker本身之外无法完美完成。与内核、发行版、docker 和文件系统之间的交互相关的问题数不胜数,我们在生产中使用 Debian stable 和 backports。我们开始在 Debian Jessie 3.16.7-ckt20-1(2015 年 11 月发布)上运行。这个有一个主要的严重错误,它会不规则地使主机崩溃(平均每隔几个小时)。 AUFS 驱动程序不稳定。它受到引发内核恐慌和破坏数据的严重错误的影响。它在[至少]所有“linux-3.16.x”内核上都被破坏了。没有治愈方法。我们非常密切地关注 Debian 和内核更新。 Debian 在常规周期之外发布了特殊补丁。 2016 年 3 月左右,AUFS 有一个重大错误修复。我们认为这是真正的修复,但事实证明并非如此。之后内核恐慌发生的频率降低(每周,而不是每天),但它们仍然响亮且存在。今年夏天,一次重大更新出现了回归,这又带回了之前的一个关键问题。它开始一个一个地杀死 CI 服务器,两次谋杀之间平均间隔 2 小时。一个紧急补丁被迅速发布以修复回归。
2016 年发布了多个 AUFS 修复程序。修复了一些关键问题,但还有更多问题。 AUFS 在 [至少] 所有“linux-3.16.x”内核上都不稳定。 Debian 稳定版卡在内核 3.16 上。它不稳定。除了切换到 Debian 测试(可以使用内核 4)之外,没有什么可做的。 Ubuntu LTS 正在运行内核 3.19。无法保证此最新更新可以解决此问题。改变我们的主操作系统将是一个重大的破坏,但我们非常绝望,我们考虑了一段时间。 RHEL/CentOS-6 在内核 2.x 上,RHEL/CentoS-7 在内核 3.10 上(许多后来的向后移植由 RedHat 完成)。众所周知,AUFS 的问题层出不穷,被开发者视为自重。作为一个长期的目标,AUFS 文件系统终于在内核版本 4 中被删除了。没有非官方补丁支持它,没有可选模块,没有任何向后移植,什么都没有。 AUFS 完全消失了。 “ OverlayFS 是一个类似于 AUFS 的现代联合文件系统。与 AUFS 相比,OverlayFS 的设计更简单,自 3.18 版以来一直在主线 Linux 内核中,并且可能更快。” — Docker OverlayFS 驱动程序
请注意,它不会向后移植到现有发行版。 Docker 从不关心 [向后] 兼容性。评论后更新:Overlay 是支持它的内核模块(由 linux 维护者开发)和使用它的 docker 存储驱动程序(docker 的一部分,由 docker 开发)的名称。它们是两个不同的组成部分 [历史和开发人员可能有重叠]。这些问题似乎主要与 docker 存储驱动程序有关,而不是文件系统本身。文件系统驱动程序是一个复杂的软件,它需要非常高的可靠性。长时间的读者会记得 Linux 从 ext3 迁移到 ext4。编写需要时间,调试需要更多时间,并且在流行的发行版中作为默认文件系统提供了永恒。在 1 年内创建一个新的文件系统是一项不可能完成的任务。当考虑到任务分配给 Docker 时,这实际上是可笑的,他们有不稳定和灾难性破坏性更改的记录,这正是我们不想要的文件系统。长话短说。那并不顺利。您仍然可以使用 Google 查找恐怖故事。 “overlay2 驱动程序解决了覆盖限制,但仅与 Linux 内核 4.0 [或更高版本] 和 docker 1.12 兼容” — Overlay 与 Overlay2 存储驱动程序 在 1 年内制作新文件系统仍然是一项不可能完成的任务。 Docker 刚刚尝试并失败了。然而他们又在尝试!我们将在几年后看到结果如何。
目前,我们运行的任何系统都不支持它。我们不能使用它,我们甚至不能测试它。经验教训:如您所见,使用 Overlay 然后使用 Overlay2。没有反向移植。没有补丁。没有复古兼容性。 Docker 只会前进并破坏事物。如果你想采用 Docker,你还必须继续前进,遵循 Docker 的发布、内核、发行版、文件系统和一些依赖项。 2016 年 6 月 2 日,大约上午 9 点(伦敦时间)。新的存储库密钥被推送到 docker 公共存储库。直接后果是,在配置了损坏的 repo 的系统上运行任何“apt-get update”(或等效的)都会失败,并显示错误“Error https://apt.dockerproject.org/ Hash Sum mismatch”这个问题是全世界。它会影响地球上所有配置了 docker 存储库的系统。它在所有 Debian 和 ubuntu 版本上得到确认,独立于操作系统和 docker 版本。世界上所有依赖 docker 设置/更新或系统设置/更新的 CI 管道都已损坏。不可能在现有系统上运行系统更新或升级。创建一个新系统并在其上安装 docker 是不可能的。过了一会儿。我们从码头工人那里得到了一个更新:“提供更新;我在内部提出了这个问题,但需要解决这个问题的人在旧金山时区 [与伦敦的时差 8 小时],所以他们还没有出现。”
我个人在内部向我们的开发人员宣布。今天,没有 Docker CI,我们不能创建新系统,也不能更新依赖于 docker 的现有系统。我们所有的希望都寄托在旧金山的一个家伙身上,他目前正在睡觉。 [暂停等待修复,这时免费的食物和饮料就派上用场了] 下午 3 点左右(伦敦时间),佛罗里达州的 Docker 人员发布了更新。他醒了,他已经发现了问题,并且正在着手修复。由于 Docker,那是 7 小时的星际中断。停电后剩下的只是关于 GitHub 问题的几条消息。没有事后分析。尽管发生了灾难性的失败,但它几乎没有(没有?)科技新闻或新闻报道。有一个由 docker 运营的公共注册表。作为一个组织,我们还运行我们自己的内部 docker 注册表。这是一个在 docker 主机上的 docker 中运行的 docker 镜像(这很元)。 docker 注册表是最常用的 docker 镜像。 docker 注册表有 3 个版本。客户端可以无动于衷:可信注册中心,文档中随处提及的(付费?)服务,不确定它是什么,只需忽略它
docker registry v2 是完全重写的。注册表 v1 在 v2 发布后不久就停用了。我们不得不安装一个新东西(再次!)只是为了让 docker 工作。他们更改了配置、URL、路径和端点。向注册表 v2 的过渡并不是无缝的。我们必须修复我们的设置、构建和部署脚本。经验教训:不要相信任何 docker 工具或 API。它们不断地被遗弃和熄灭。注册表 v2 的目标之一是带来更好的 API。它记录在此处,我们不记得 9 个月前存在的文档。从 docker 注册表中删除图像是不可能的。也没有垃圾收集,文档提到了一个,但它不是真的。 (图像确实具有压缩和重复数据删除功能,但这是另一回事)。我们不能拥有无限量存储的服务器。我们的注册表几次用完空间,在我们的构建管道中释放了地狱,然后我们将图像存储移到了 S3。
我们总共进行了 3 次手动清理。在所有情况下,我们都必须停止注册表,清除所有存储并启动一个新的注册表容器。 (幸运的是,我们可以用我们的 CI 重新构建最新的 docker 镜像)。经验教训:从 docker 注册表存储中手动删除任何文件或文件夹都会损坏它。直到今天,还无法从 docker 注册表中删除图像。也没有 API。 (v2 的重点之一是拥有更好的 API。任务失败)。发布周期适用但不限于:docker 版本、特性、文件系统、docker 注册表、所有 API……从 Docker 过去的历史来看,我们可以近似地估计 Docker 制造的任何东西都有大约 1 年的半衰期,这意味着现在存在的一半将在 1 年内被放弃[和熄灭]。通常会有可用的替代品,它与它应该替代的东西不完全兼容,并且可能会或可能不会在同一个生态系统上运行(如果有的话)。 “我们制作软件不是为了让人们使用,而是因为我们喜欢制作新东西。” — 未来的 Docker 墓志铭 Docker 最初是通过 Web 应用程序出现的。当时,这是开发人员打包和部署它的一种简单方法。他们尝试并很快采用了它。然后它扩展到一些微服务,因为我们开始采用微服务架构。
Web 应用程序和微服务是相似的。它们是无状态的应用程序,它们可以不假思索地启动、停止、终止、重新启动。所有困难的事情都委托给外部系统(数据库和后端系统)。 Docker 的采用始于次要的新服务。起初,在开发、测试和生产中一切正常。随着越来越多的 Web 服务和 Web 应用程序被 Docker 化,内核恐慌慢慢开始发生。随着我们的成长,稳定性问题变得更加突出和影响。一年中发布了一些补丁和回归。一段时间以来,我们一直在使用 Docker 进行追赶和变通。这很痛苦,但似乎并没有阻止人们采用 Docker。组织内部的支持和需求仍在增长。注意:这些故障都没有影响到任何客户或资金。我们在包含 Docker 方面非常成功。我们有一些在 Erlang 中运行的关键应用程序,由“核心”团队中的几个人管理。他们试图在 Docker 中运行他们的一些应用程序。它没有用。出于某些原因,Erlang 应用程序和 docker 没有合作。这是很久以前完成的,我们不记得所有的细节。 Erlang 对系统/网络的行为方式以及预期的负载是每秒数千个请求有特别的想法。任何不稳定或不兼容都可能成为突出失败的理由。 (我们现在肯定知道试用期间使用的版本存在多个主要的不稳定问题)。
审判提出了一个危险信号。 Docker 还没有为任何关键的事情做好准备。这是正确的电话。后来的崩溃和问题设法证实了这一点。我们只将 Erlang 用于关键应用程序。例如,核心人员负责一个支付系统,该系统本月处理了 96,544,800 美元的交易。它包括几个应用程序和数据库,所有这些都在他们的职责范围内。 Docker 是一种危险的负债,可能会使数百万人处于危险之中。它被禁止在所有核心系统中使用。 Docker 是无状态的。容器没有永久的磁盘存储,无论发生什么都是短暂的,当容器停止时就会消失。容器不是用来存储数据的。实际上,它们的设计意图是不存储数据。任何违背这一理念的企图都注定要失败。而且。 Docker 通过其抽象来锁定进程和文件,它们无法访问,就好像它们不存在一样。如果出现问题,它可以防止进行任何类型的恢复。崩溃会破坏数据库并影响连接到它的所有系统。这是一个不稳定的错误,在密集使用下会更频繁地触发。数据库是最终的 IO 密集型负载,这是一个有保证的内核恐慌。另外,还有另一个错误可能会破坏 docker 挂载(破坏所有数据),也可能会破坏系统文件系统(如果它们在同一个磁盘上)。噩梦场景:主机崩溃,磁盘损坏,破坏主机系统和进程中的所有数据。
每隔一段时间,就会有人来问“我们为什么不把这些数据库放到 docker 中呢?”我们将讲述我们无数的战争故事中的一些,到目前为止,没有人问过两次。注意:我们开始回顾我们的 Docker 历史,将其作为我们入职流程的一个组成部分。这就是新的损害控制理念,在 docker 有机会成长并杀死我们之前扼杀它的想法。 Docker 正在获得动力,那里有一些疯狂的狂热支持。 docker 炒作不再只是一种技术负担,它也已经演变成一个社会学问题。目前边界是受控的,仅限于一些无状态的 Web 应用程序和微服务。这是不重要的东西,它们可以被docker化并每天崩溃一次,我不在乎。到目前为止,所有想使用 docker 来处理重要事情的人都在快速讨论后停止了。我最大的恐惧是有一天,一个 docker 狂热者不会听从理由并继续推动。我将被迫向他发起猛攻,这可能不好看。噩梦场景:未来的会计集群改造,目前持有 2300 万美元的客户资金(M 代表百万美元)。已经有一个人真诚地问架构师“你为什么不把这些数据库放到docker里?”,没有词可以形容架构师的脸。密切跟踪内核、操作系统、发行版、docker 以及介于两者之间的所有内容的版本和更改日志。寻找错误,希望补丁,仔细阅读所有内容。
偶尔,我们会查看哪些服务器已死机并强制重新启动它们。高可用性要求每个服务至少有 2 个实例,才能在一个实例失败后幸免于难。当将 docker 用于任何远程重要的事情时,我们应该有 3 个它的实例。 Docker 一直都死了,我们需要一个误差幅度来支持同一服务的原始数据中的 2 次崩溃。大多数情况下,崩溃的是 CI 或测试实例。 (他们进行了大量密集测试,问题特别突出)。我们有很多这样的。有时一个下午有 3 个它们连续崩溃。 Docker 旨在不存储数据。不要反对它,这是灾难的秘诀。最重要的是,当前存在杀死服务器并可能破坏数据的问题,因此这确实是一个很大的问题。它必须仅限于可以崩溃而不导致停机的应用程序。这意味着大多数无状态应用程序,可以在其他地方重新启动。
Docker 应用程序应该在自动缩放组中运行。 (注意:我们还没有完全到位)。每当实例崩溃时,它会在 5 分钟内自动更换。无需手动操作。自愈。 Docker 不可能的挑战是提供内核 + 发行版 + docker 版本 + 文件系统的工作组合。现在。我们不知道任何稳定的组合(也许没有?)。我们积极寻找一个,不断测试新系统和补丁。做一个好的稳定的软件需要5年时间,Docker v1.0才28个月,还没来得及成熟。硬件更新周期为3年,分发发布周期为18-36个月。 Docker 在上一个周期中并不存在,因此系统无法考虑与其兼容。更糟糕的是,它依赖于许多相对较新的高级系统内部结构,也没有时间成熟,也没有达到发行版。目标:等待事情变得更好。在此期间尽量不要破产。
Docker 仅限于无状态应用程序。如果应用程序可以打包为 Docker Image,则可以打包为 AMI。如果应用程序可以在 Docker 中运行,则它可以在 Auto Scaling 组中运行。大多数人都忽略了它,但 Docker 在 AWS 上毫无用处,它实际上是退步了。首先,容器的目的是通过在同一个[大]主机上运行多个容器来节省资源。 (让我们暂时忽略当前正在使主机 [及其上所有正在运行的容器] 崩溃的 docker 错误,这迫使我们在每个主机上仅运行 1 个容器以确保可靠性)。因此容器在云提供商上是无用的。总有一个大小合适的实例。只需为应用程序创建一个具有适当内存/CPU 的。 (AWS 上的最低要求是 t2.nano,对于 512MB 和 5% 的 CPU,每月 5 美元)。其次,容器最大的收获是当周围有一个完整的编排系统来自动管理创建/停止/启动/滚动更新/canary-release/blue-green-deployment。目前尚不存在实现该目标的编排系统。 (这就是 Nomad/Mesos/Kubernetes 最终会出现的地方,它们目前的状态还不够好)。 AWS 有 Auto Scaling 组来管理实例的编排和生命周期。这是一个与 Docker 生态系统完全无关的工具,但它可以实现更好的结果,没有任何缺点和麻烦。为每个服务创建一个自动伸缩组并为每个版本构建一个 AMI(提示:使用 Packer 构建 AMI)。如果操作是在 AWS 上进行的,人们已经熟悉管理 AMI 和实例,没有更多东西需要学习,也没有陷阱。由此产生的部署是黄金和
......