5 个 Docker 致命陷阱 😱 - 致新用户
结束语
使用Docker 容器开发真是太棒了!uilicious的整个后端都在 Docker 上运行。
如果没有 docker,以我们目前的成本和规模推出 uilicious 是不可能的。
然而,与所有技术一样,Docker 也存在一些隐藏的、不太明显的陷阱,尤其是在跨多台主机大规模运行 Docker 时。对于那些从物理服务器或虚拟机工作负载迁移过来的人来说,尤其如此。
需要明确的是,本文并非讨论系统优化,而是讨论常见的错误配置,这些设置可能会给您和您的公司带来灾难(YMMV);您的生产系统可能会遭受长时间的宕机,甚至更糟的是,数据泄露给黑客。
本文还假设您对 localhost 有一点基本的 docker 知识。
因此,以下是我过去四年使用 Docker 时不幸遇到并幸存的 5 个致命陷阱,以及每个陷阱的简要总结。以及如何避免它们。
请注意,当我提到涉及多个主机的情况时,这仅适用于大于单个主机的 docker 设置,例如rancher 1.6(我们在内部使用)、Docker swarm或Kubernetes等。
1)Docker 与防火墙不兼容
Docker 主机端口绑定,绕过典型的 iptables INPUT 规则
遗憾的是,许多人一开始使用 Docker 时会本能地将容器端口绑定到主机,要么照搬他们在传统 Linux 中的常用设置(认为主机上常用的 iptables 防火墙规则无需验证即可正常工作)。要么遵循许多在线初学者指南,这些指南倾向于在本地机器上将端口绑定到“主机”,以便于开发(但没有任何关于此问题的建议)。
但是,Docker 端口绑定默认使用 iptables PREROUTING 级别。该级别优先于许多人常用来保护系统的大多数 iptables INPUT 防火墙规则。
最终结果是:特权端口(例如 MySql)被公开暴露在互联网上。更糟糕的是,NoSql 端口通常在未经身份验证的情况下部署,导致所有私人客户数据被暴露,成为黑客的“礼物”。
缓解指南
- 通过使用容器到容器的通信来缓解
- 使用基于网络的规则进行缓解
- AWS、Azure、DO 或 Google Cloud 的 VPC/防火墙规则
- 本地路由器规则
- 不适用于 Linode
- 最后的替代选择
- 通过身份验证检查、管理和保护所有主机绑定端口
- 使用自定义 docker iptables
幸运的是,大多数此类情况都可以通过 Google 或 AWS 等提供商的虚拟私有云 (VPC) 内应用基于网络的防火墙规则来轻松缓解。或者,也可以在其内部路由器上应用类似的规则,将服务器安全地公开。
然而对于一些云提供商来说,这种解决方案不是一个选择,最著名的是 Linode,其私有网络明确指的是在各自的本地数据中心内为所有客户提供公共访问权限。
在这种情况下,您可以选择另一种方法:仔细检查公开绑定的端口,并使用复杂的随机密码确保它们得到妥善保护,并确保所有非公开通信都发生在容器到容器网络之间,甚至实施双向 SSL 身份验证。或者,您可以学习设置 docker iptables 规则的复杂过程。
更多详细信息
2)不了解docker数据持久化
Docker 默认不会保留任何容器数据!
首先,这是一个极其复杂的话题,我只是想将其简化一下。
因此,这里简要介绍一下如何在容器中持久化处理数据。尤其是在容器重启(停止-启动)或重新部署到其他主机时。
- 不带卷的容器会临时在本地存储变更。如果容器重新部署,数据将被销毁。
- 默认卷仅在主机上持久,并且不会跨主机传输
- 当容器关闭或健康检查失败时,会发生以下任一情况。
- 重启,容器数据和卷保留
- 在同一主机上重新部署,卷仍然存在,容器数据被销毁
- 重新部署到不同的主机上,没有数据保留!
- 这可能以不明显的方式发生,例如主机暂时故障
- 基础设施工具只是决定在星期五这样做
- 孤立卷可能会被清理功能删除
- 主机存储可以直接作为卷安装,但是……
- 这将更难通过 docker 进行监控
- 仍然具有与主机卷相同的大部分缺点
- 至少数据不会消失在黑洞中,并且可以使用传统的 SSH 恢复
- 在某些关键情况下,这是首选(我们对 elasticsearch 使用此方法)
- 如果允许自动重建到其他主机,请非常仔细地规划。
那么这些意味着什么呢?以下是一些真实发生过的故障场景,并通过备份进行了挽救。
单个容器带卷,不受主机限制
您部署了一个带有卷的 MySql 容器,如果失败,它会默认重启。有一天,运行 mysql 的主机与 Docker 集群断开连接。它的集群管理器将容器重新部署到另一台主机上,并附加到一个新的卷。
更糟糕的是,还有一个用于删除悬空卷和容器的清理脚本。当你醒来时,数据将无法恢复。
主机受限容器,无卷挂载
您已指定一个 Jenkins 容器专门部署在一台主机上,并确保其故障条件为不执行任何操作。这将缓解“在其他主机上重建”的情况,太棒了!
然而,在一个阳光明媚的星期天,你注意到 Jenkins 需要进行安全更新。你决定点击升级按钮。突然,所有数据都消失了,因为你的容器正在重新部署。
分布式冗余系统,不受主机限制
您已在多台主机上部署了 ElasticSearch,并分别部署了各自的卷容器;您相信即使单个主机发生故障,ElasticSearch 内部的强大功能也能重新平衡并从其他副本中恢复数据。有一天,您决定进行升级,却没有意识到集群中运行的主机数量远远超过了所需的容器数量。
在升级过程中,所有新版本都被部署到之前未使用的主机上,旧容器被终止。由于容器的启动和停止操作是即时的,因此没有足够的副本,也没有时间传输数据。
所有数据都会丢失。如果您使用自动伸缩组,这种情况可能会悄无声息地发生。
缓解指南
为了缓解上述情况,应采取以下措施。
- 进行备份,并学习如何恢复它们
- 理想情况下,将持久数据放在 Docker 基础设施之外
- 使用云管理 SQL,或可用的各种云管理对象/No-SQL 服务
- 或者直接在您的 docker 容器中使用相当于 google persistent disk / aws EBS 的东西(如果支持的话)
- 容器应尽可能不保存需要持久化的数据
- 示例(零持久性):前端服务器、应用程序服务器
- 反例(需要持久性):SQL 服务器、文件服务器
- 对于保存持久数据的容器
- 注意并谨慎了解健康检查的配置及其失败行为
- 仔细规划升级,如有需要,一次升级一个容器
- 健康检查可以配置为“不执行任何操作”(需要时执行手动干预)
更多详细信息
- Docker 官方文档在这里,还有这里
- 管理docker的持久性
3)使用 :latest 标签来管理容器
不受管理的自动升级可能会破坏您的系统。
一种常见的模式是使用基础容器镜像,您可以将其合并为通常需要的容器模块,并从中扩展以获得更全面的容器镜像。
或者可以使用最新的官方容器镜像进行部署,例如Jenkins等。
但是,如果发生服务器故障,并且随后进行自动扩展替换,最新的升级依赖项可能与集群的其余部分不同步。
此外,有时最新版本会对你的系统进行向后破坏性的更改,从而导致严重的宕机。当某些系统只能单向升级,并且升级过程中会修改共享文件或资源(例如:Jenkins、GitLab)时,情况会变得更糟。
这种情况会使跟踪错误变得复杂。您可能只在一台机器上遇到此问题,而在另一台机器上不会遇到。除非做出更改(例如,在所使用的容器模板中指定要使用的固定版本而不是最新标签),否则无法挽救问题。
在生产环境中,你肯定会希望速度慢一些,并使用固定版本控制,或者至少使用主版本/稳定分支。然而,在测试环境中,:latest
如果你对速度有要求,可以自由地尝试最前沿的技术。
更多咆哮请见此处。
缓解指南
- 只是不要使用
:latest
,根据您的要求调整其余部分- 或者,只使用您自己的容器注册表,并手动选择部署在其上的所有图像 - 这是银行缓慢而痛苦的方式
4)容器没有持久IP地址
假设容器的 IP 地址持久。
假设 IP 地址持久化可能会造成一些棘手的问题,尤其是在 nginx 负载均衡器上。更糟糕的是,将 IP 地址硬编码到配置文件中。这可能会引发一些令人费解的错误,例如每个容器都“在线”且运行正常,但它们之间却无法通信。
例如,如果您使用 Nginx 作为多个容器的负载均衡器,则默认情况下,域名的 IP 地址会在启动时解析。这意味着,如果 Nginx 负载均衡器所连接的任何容器的 IP 地址被修改,负载均衡将不会更新并失败。(除非您使用的是 Nginx Pro)
缓解指南
- kubernetes:学习使用集群 IP,它可以改变这个问题。
- 配置你的堆栈以使用主机名而不是固定 IP 地址
- 假设应用程序支持主机名上的动态 IP
- 使用 HAProxy 或 Traefik 等 Nginx 替代品进行负载均衡,
- 如果不可能,请使用健康检查来自动重启容器以修复主机名路由
更多详细信息
5)规划和监控中缺少 CPU、RAM、磁盘
Docker 只是一个抽象层,基本原理没有改变
这尤其令人讨厌,因为在大多数云提供商的默认设置下,随机存取存储器(RAM)和磁盘存储使用情况不会显示在实例仪表板上。
如果规划不周,单个主机故障可能会引发“资源不足”错误的连锁反应,迫使集群将受影响的容器迁移到其他主机。这反过来又会引发更多“资源不足”错误。
这种情况的一个常见示例是在部署多个容器(例如 SQL、NoSql 或文件服务器)期间。默认情况下,随着使用量的增加,它们会尝试消耗尽可能多的内存来提高性能。
另一个经常被遗忘的事情:熵和对数旋转
如果不这样做,如果您正在使用身份验证系统或 SSL,熵会在某个时候减慢速度直至停止。或者,日志轮换是一个不错的选择,它可以确保您的服务器不会因为日志而耗尽文件空间。
最后,备份、备份、再备份。
缓解指南
(以防万一):从永久故障状态恢复集群
请注意,当发生此类级联“资源不足”错误时,最安全的解决方案是
- 停止/关闭整个集群
- 停止所有已调度的容器
- 反应式容器按组分组
通过这种方式,集群在任何给定时间点都不会被淹没。
还有一件事:监控不仅仅是关于http代码200
监控也适用于功能。毕竟,静态文件服务器可以成功启动并运行。而损坏的 JS 文件会导致所有浏览器功能悄无声息地中断。
您可以使用 API 内置的复杂健康检查命令或基于文本的静态网站健康检查来实现这一点。
您可以轻松使用 uilicious 进行工作监控。您可以快速构建简单的测试用例,对网站进行全面的端到端测试,并在后台安排测试运行。如果测试用例失败,我们会自动通知您。
// Test scripts can be as simple as the following
I.goTo('https://google.com')
I.fill("Search", "uilicious")
I.pressEnter()
I.click("Smart User Journey Testing")
I.amAt("https://uilicious.com")
结束语
希望以上内容能帮助你避免可能带来的痛苦。然而,正如所有快速发展的新技术一样,任何事都要谨慎对待。(注:此内容更新于2018年11月)
- Docker 场景和工具正在快速变化,请继续学习和阅读。
- 没有什么是魔法,一切都是工具,学习它。
- 不要盲目相信默认值和教程指南,了解它们的作用。
- 了解您的系统在各种可能的情况下会如何发生故障,并围绕这些情况制定计划。
最重要的是 - 拥有一个测试环境,并在那里进行实验,您可以在过程中安全地失败。