本文最初发布于 Earthly 博客,经原作者授权由 InfoQ 中文站翻译并分享。
告诉我,这是不是听起来很熟悉?有人要把 docker-compose 介绍给你,你可能是自愿的,也可能是被迫的。你用了一段时间,但发现它很不灵活。我要告诉你,你可能用错了。
这可能有点夸张。我不认为存在 100%正确或错误的使用方法:自主构建和开发设置往往有各种奇怪的要求,所以标准可能不符合需求。如果你的情况并不完全相符,请带着适当的怀疑态度阅读本文。
本文介绍了我自己使用 docker-compose 犯的一些错误。
我将重点关注与集成测试相关的用例,以及使用 docker-compose 作为开发环境。对于生产使用,我认为 docker-compose 通常并不适合。
新用户遇到的第一个麻烦是 Docker 网络的使用。在你了解了 docker build 和 docker run 的基础知识后,这是你需要学习的另一层知识……坦白说,为什么你还需要了解 Docker 网络呢?通过主机网络,一切工作正常,这对吗?错了!
使用主机网络意味着你必须为使用的各种微服务保留特定端口。如果你碰巧打开两个有端口冲突的栈,那你就倒霉了。如果你想打开同一个栈的两个版本,那也会倒霉。当某个服务有多个副本时,你想测试它的行为吗?这会非常艰难……
在默认情况下,docker-compose 在一个名为<project-name>_default 的独立网络上启动其容器(其中<project-name>为默认的目录名)。所以,你无需做任何特殊的事情就可以利用 Docker 网络。
这个网络立马能给你带来很多好处:
这种做法随处可见,你肯定也见过很多,每个人都见过很多:将端口绑定为 8080:8080。乍一看,这似乎没什么问题。但魔鬼在细节中。这种极其常见的端口绑定不仅仅是将一个容器端口转发到本地主机——它还将其转发到系统上的每个网络接口上,包括用于连接互联网的任何接口。
换言之,你的开发容器很可能一直在监听你的无线局域网——当你在家、在办公室或在麦当劳时。它总是可以访问。这可能很危险。不要这样做。
“但是 Vlad,我用了 ufw,我的端口默认是不能访问的”。
这也许没错——但如果你在团队中使用这样的 docker-compose 设置,你的队友可能没有在他们的笔记本电脑上安装防火墙。
修复方法非常简单:只需在前面添加 127.0.0.1:,例如 127.0.0.1:8080:8080。这是告诉 docker 只向回环网络接口公开端口,不包括其他网络接口。
我要坦白一件事。关于这一点,我是百分百有错的。
这个问题之所以如此复杂,主要原因是 Docker 或 Docker Compose 没有提供支持解决这个问题。Docker-compose 文件格式的 2.1 版本中有一个名为 condition 的 depends_on 选项,可以设置为 service_healthy。而且,每个服务都可以有一个 healthcheck 命令,可以告诉 docker-compose“健康”是什么意思。这在3.0版本中不再可用,也没有提供替换项。
Docker 文档的基本建议是,服务要在其他服务暂时离线的情况下具有弹性,因为这在生产环境中可能会发生,例如出现短暂的网络不稳定,或者一个服务重新启动。这是无可争辩的。
当你运行一个集成测试,而用于初始化测试环境的例程(例如预先用一些测试数据填充数据库)在其他服务准备就绪之前无法恢复启动,就会变得有点麻烦。因此,关于“至少它在生产环境中会有弹性”的论点在这里并不适用,因为用测试数据填充数据库的代码从未在生产环境中使用。
对于这种情况,你需要等待服务就绪。Docker 建议使用wait-for-it、Dockerize或wait-for。但是,请注意,端口就绪并不总是表示服务已经准备好可供使用。例如,在使用具有特定模式的特定 SQL DB 的集成测试中,当数据库初始化时,端口变为可用,但是,测试可能只有在特定模式迁移完成之后才能开始。你可能需要在前面进行特定于应用程序的检查。
有这样一种情况:你想运行一些单元测试,但这些测试依赖于一些外部服务。可能是数据库,可能是 Redis,也可能是另外一个 API。简单:让我们把这些依赖放在 docker-compose 中,并让单元测试连接到它们。
这很好——但请注意,你的测试不再仅仅是单元测试,它们现在是集成测试了。除术语之外,现在还需要考虑一个重要的区别:你需要考虑测试环境的设置和清理。通常,最好在测试代码之外执行设置/清理操作——主要原因是可能有多个不同的包依赖于这些外部服务。但你的情况可能不太一样。
如果你最终将测试设置和清理分开,那么你需要多做一些工作,将集成测试容器化。
容器化测试意味着:
使用容器化集成测试的一个技巧是,为它们使用一个独立的 docker-compose 定义。例如,如果你的大部分服务都存在于 docker-compose.yml 中,那么可以添加一个包含集成测试定义的 docker- composition .test.yml 文件。这意味着 docker-compose up 会提供你常用的服务,而 docker-compose -f docker-compose.yml -f docker-compose.test.yml up 会启动集成测试。要了解完整的实现示例,请参阅 Ardan Labs 提供的这个优秀的docker-compose集成测试库。
说这是错误有点不公平。在许多情况下,不容器化是更可取的。举个简单的例子,许多语言都与 IDE 深度集成,这使得在语言和 IDE 之间插入容器几乎不可能。有很多正当的理由不这样做。
对本地开发来说,Docker Compose 是一个非常棒的工具。尽管它有一些缺陷,但通常,它会给许多工程团队带来很多生产力方面的好处,尤其是在与集成测试一起使用时。
原文链接:https://blog.earthly.dev/youre-using-docker-compose-wrong/
领取专属 10元无门槛券
私享最新 技术干货