产品战略专家梁宁确认出席AICon北京站,分享AI时代下的商业逻辑与产品需求 了解详情
写点什么

5 种常见的 Docker Compose 错误

  • 2021-01-18
  • 本文字数:5058 字

    阅读完需:约 17 分钟

5种常见的Docker Compose错误

在构建一个容器化应用程序时,开发人员需要一种方法来引导他们正在使用的容器去测试其代码。虽然有几种方法可以做到这一点,但 Docker Compose 是最流行的选择之一。它让你可以轻松指定开发期间要引导的容器,其次建立一个快速的“编码-测试-调试”开发循环。


愿景是一个人编写一个docker-compose.yml文件,指定了开发中所需的一切,并将它提交到代码仓库。然后,每一个开发者只需运行docker-compose up,即可启动测试其代码需要的所有容器。


然而,要使docker-compose设置达到最高性能,需要大量工作。我们见过最好的团队在不到一分钟的时间内启动他们的开发环境,并在几秒中内测试每个更改。考虑到每个开发人员每天花在测试代码上的时间,小的改进会对开发人员的生产力产生巨大影响。


源自 XKCD

错误 1:频繁的容器重建


docker build需要很长时间。如果每次想要测试一个代码更改时都要重新构建你的容器,那么你就有很大潜力来加速你的开发循环。


处理非容器化应用程序的传统工作流如下:


  • 编码

  • 构建

  • 运行


这些年来,通过使用针对编译型语言的增量构建和热加载之类的技巧,这个过程得到高度优化。它变得非常快。


当人们第一次采用容器时,他们倾向于采用现有的工作流程,只添加一个docker build步骤。他们的工作流如下:


  • 编码

  • 构建

  • 容器构建

  • 运行


如果做的不好,docker build步骤会使所有优化都白费。另外,它还增加了一堆额外的耗时工作,例如使用 apt-get 重新安装依赖。所有这些加起来,会使得我们的测试过程比使用 Docker 之前要慢得多。

解决方案:在 Docker 外运行你的代码


一种方案是在 Docker Compose 中启动所有依赖项,但在本地运行你正在积极处理的代码。这模仿了开发非容器化应用程序的工作流。


只需要在localhost上暴露你的依赖,并将你正在使用的服务指向localhost:<port>地址。


然而,这并不总是可行的,尤其是如果你正在处理的代码依赖容器镜像内置的东西,而这些东西不容易从你的笔记本电脑访问。

解决方案:最大化缓存来优化 Dockerfile


如果必须构建 Docker 镜像,那么编写 Dockerfile 时,最大化缓存能将一个 10 分钟的 Docker 构建变为 1 分钟。


生产环境的 Dockerfile 文件的典型模式是通过将单个命令链接到一个RUN语句中来减少层数。然而,镜像大小在开发过程中并不重要。在开发过程中,你想要尽可能多的层数。


你的生产环境 Dockerfile 文件可能如下所示:


RUN \    go get -d -v \    && go install -v \    && go build
复制代码


这对开发来说很糟糕,因为每次重新运行该命令时,Docker 都会重新下载所有的依赖并重新安装它们。增量构建更加有效。


相反,你应该有一个专门用于开发环境的的 Dockerfile 文件。将每件事都分解成非常小的步骤,规划好你的 Dockerfile 文件,这样基于经常变化的代码的步骤最后执行。


最不频繁更改的内容,例如拉取依赖,应该放在第一位。这样,在重建 Dockerfile 时就不必构建整个项目。你只需要构建你刚刚修改的一小部分。


关于这个的例子,请看下面我们用于Blimp开发环境的 Dockerfile。它遵循上面所述的技术,将繁重的构建过程缩短到几秒钟。


FROM golang:1.13-alpine as builder
RUN apk add busybox-static
WORKDIR /go/src/github.com/kelda-inc/blimp
ADD ./go.mod ./go.modADD ./go.sum ./go.sumADD ./pkg ./pkg
ARG COMPILE_FLAGS
RUN CGO_ENABLED=0 go install -i -ldflags "${COMPILE_FLAGS}" ./pkg/...
ADD ./login-proxy ./login-proxyRUN CGO_ENABLED=0 go install -i -ldflags "${COMPILE_FLAGS}" ./login-proxy/...
ADD ./registry ./registryRUN CGO_ENABLED=0 go install -i -ldflags "${COMPILE_FLAGS}" ./registry/...
ADD ./sandbox ./sandboxRUN CGO_ENABLED=0 go install -i -ldflags "${COMPILE_FLAGS}" ./sandbox/...
ADD ./cluster-controller ./cluster-controllerRUN CGO_ENABLED=0 go install -i -ldflags "${COMPILE_FLAGS}" ./cluster-controller/...
RUN mkdir /gobinRUN cp /go/bin/cluster-controller /gobin/blimp-cluster-controllerRUN cp /go/bin/syncthing /gobin/blimp-syncthingRUN cp /go/bin/init /gobin/blimp-initRUN cp /go/bin/sbctl /gobin/blimp-sbctlRUN cp /go/bin/registry /gobin/blimp-authRUN cp /go/bin/vcp /gobin/blimp-vcpRUN cp /go/bin/login-proxy /gobin/login-proxy
FROM alpine
COPY --from=builder /bin/busybox.static /bin/busybox.staticCOPY --from=builder /gobin/* /bin/
复制代码


最后一点:通过最近引入的多阶段构建,现在可以创建具有良好分层且镜像很小的 Dockerfile。我们在本文中对这一点不会过多讨论,只能说上面显示的 Dockerfile 就是这样做的,因此它既用于Blimp开发环境也用于生产环境。

解决方案:使用主机卷


通常,最好的选择是使用一个主机卷来直接将你的代码加载到容器上。这使你能够以本机速度运行代码,同时仍然在包含运行时依赖项的 Docker 容器中运行。


主机卷将你笔记本电脑上的一个目录镜像到一个正在运行的容器中。当你在文本编辑器中编辑一个文件时,更改会自动同步到容器中,然后能立即在容器中执行。


大多数语言都有一种方法来监视你的代码,并在代码更改时自动重新运行。例如,nodemon是 Javascript 中的监视代码的方法。请查看这篇关于如何设置这一点的文章教程。


它最初需要一些工作,但结果是,你可以在 1-2 秒内看到你的代码更改的结果,而一次 Docker 构建可能需要几分钟。

错误 2:主机卷速度慢


如果使用了主机卷,你可能已经注意到,在 Windows 和 Mac 上读写文件的速度非常慢。对于读写大量文件的命令来说,这是一个已知的问题,例如具有复杂依赖的 Node.js 和 PHP 应用程序。


这是因为 Docker 是运行在 Windows 和 Mac 的一个虚拟机上。在进行主机卷加载时,必须经过大量的转换才能将笔记本电脑上的文件夹加载到容器中,这有点儿类似网络文件系统。这会增加大量负载,而在 Linux 本机上运行 Docker 时不会出现这些情况。

解决方案:放松强一致性


其中一个关键问题是,默认情况下,文件系统加载会保持强一致性。一致性是一个广泛的话题,可以浓墨重彩地大书特书,但是简而言之,它意味着所有特定文件的读取者和写入者都同意任何文件修改发生的顺序,从而(最终,某种程度上)同意该文件的内容。


问题是,强制实现强一致性是相当昂贵的,需要所有文件写入者确保他们不会不恰当地破坏彼此的更改。


虽然强一致性有时特别重要,例如,当在生产环境运行数据库时。好消息是,在开发环境,它不是必需的。你的代码文件只会有单个写入者(你自己),和单个信源(你的代码库)。因此,冲突并不像在生产中那么需要担心。


正是由于这个原因,Docker 实现了在加载卷时放松一致性保证的功能。在 Docker Compose 中,你只需将cached关键词添加到卷加载中即可获得显著的性能保证。(不要在生产环境这么做...)


volumes:    - "./app:/usr/src/app/app:cached"
复制代码

解决方案:代码同步


另一种方案是设置代码同步。你可以用一个工具来通知你的笔记本电脑和容器之间的更改,并复制文件来解决差异(类似于 rsync),而不是加载一个卷。


Docker 的下一个版本内置了Mutagen,作为卷的缓存模式的一种替代。如果你感兴趣,就等 Docker 发布下一个版本再试试,不过你也可以直接下载 Mutagen 项目,不用等就可以直接使用。

解决方案:不要加载包


对于 Node 这样的语言,大部分文件操作往往位于包目录(例如node_modules)。因此,从卷中排除这些目录会显著提高性能。


在下面的例子中,我们有一个卷将代码加载到一个容器中。然后用它自己干净的专用卷覆盖了node_modules目录。


volumes:  - ".:/usr/src/app"  - "/usr/src/app/node_modules"
复制代码


这个额外的卷加载告诉 Docker 为node_modules目录使用一个标准卷,这样当npm install运行时,它不会使用比较慢的主机加载。为了使之生效,当容器首次启动时,我们在entrypoint运行npm install来安装我们的依赖并填充node_modules目录。像这样:


entrypoint:  - "sh"  - "-c"  - "npm install && ./node_modules/.bin/nodemon server.js"
复制代码


克隆和下载上述示例代码的完整说明,请参考此处

错误 3:脆弱的配置


大多数 Docker Compose 文件都是有组织地演化的。我们通常会看到大量的复制粘贴代码,这使得代码修改非常困难。一个干净的 Docker Compose 文件可以更容易地在生产环境变化时进行定期更新。

解决方案:使用 env 文件


env 文件将环境变量从主 Docker Compose 配置中分离出来。这有助于:


  • 使密钥不会保存在 git 历史中

  • 使每个开发者拥有稍微不同的设置变得容易。例如,每个开发者可能有一个唯一的 access 密钥。将配置保存在一个.env文件中意味着他们不必修改提交的docker-compose.yml文件,并在这个文件更新时处理冲突。


要使用 env 文件,只需增加一个.env文件,或者使用env_file字段显式设置路径。

解决方案:使用 override 文件


Override文件让你有一个基本配置,然后在不同文件中指定修改。如果你使用 Docker Swarm,并且有一个生产环境的 YAML 文件,这将非常有用。你可以在docker-compose.yml中存储自己的生产环境配置,然后在一个 override 文件中指定开发环境所需的任何更改,例如使用主机卷。

解决方案:使用extends


如果你正在用 Docker Compose v2,你可以使用extends关键字在多个地方导入 YAML 片段。例如,你可能有一个定义,你公司的所有服务在开发环境的 Docker Compose 文件中都有这 5 个特定的配置项。你可以定义一次,然后使用extends关键词来将它放到任何需要的地方,这就提供了一些模块化特性。我们不得不在 YAML 中这样做是很痛苦的,但我们能够少写一个程序来生成它还是最好的。


Compose v3 移除了对extends关键词的支持。然而,你可以使用YAML anchors获得类似的结果。

解决方案:程序生成 Compose 文件


我们已经和一些使用Blimp的工程团队一起工作过,他们在开发环境的 Docker Compose 文件中有上百个容器。如果他们使用单个巨大的 Docker Compose 文件,就需要数千行无法维护的 YAML 代码。


随着扩展,可以编写一个脚本,来基于一些高级别的规范生成 Docker Compose 文件。这对于具有非常大的开发环境的工程团队来说是很常见的。

错误 4:脆弱的引导


docker-compose up是不是只有一半时间工作?你是不是不得不使用docker-compose restart来启动崩溃的服务?


大多数开发者都想要写代码,不想做 DevOps 工作。调试一个坏的开发环境是非常令人沮丧的。


docker-compose up应该每一次都好好工作。


这里的大多数问题都与服务启动顺序错误有关。例如,你的 Web 应用可能依赖一个数据库,如果 Web 应用启动时数据库还没有就绪,那么它就会崩溃。

解决方案:使用depends_on


depends_on使你能控制启动顺序。 默认地,depends_on会等待依赖被创建,而不等待处于“healthy”状态的依赖。然而,Docker Compose v2 支持将 depends_on 与健康状态检查结合起来。(不幸的是,这个功能在 Docker Compose v3 中被移除了。你可以使用一个类似wait-for-it.sh的脚本来手动实现类似功能)


Docker 文档建议不要使用类似depends_onwait-for-it.sh之类的方案。而且,我们同意,在生产环境,要求为容器指定特定的引导顺序是脆弱架构的一种标志。然而,作为一名试图完成工作的开发人员,修复整个工程组织中的每一个容器可能是不可行的。因此,对于开发环境,我们认为这是可以的。

错误 5:资源管理不善


要确保 Docker 拥有它流畅运行所需的资源,而不会完全超出你的笔记本电脑负担,可能是比较棘手的。如果你觉得自己的开发工作流由于 Docker 没有以峰值容量运行而变得迟缓,那么可以参考以下几点。

解决方案:修改 Docker Desktop 分配


Docker Desktop 需要大量 RAM 和 CPU,尤其是在 Mac 和 Windows 上它是一个虚拟机时。默认的 Docker Desktop 配置往往没有分配足够的 RAM 和 CPU,因此我们一般建议调整设置到过度分配。我倾向于在开发时给 Docker 分配大约 8GB RAM 和 4 CPU(在不使用 Docker Desktop 时,我会将它关掉,来使之可行)

解决方案:删除未使用的资源


人们在使用 Docker 时,经常会无意识地泄漏资源。人们拥有成百上千的卷、旧的容器镜像以及如果不小心有时还会运行的容器,这并不少见。这就是为什么我们推荐偶尔运行docker system prune,删除当前没有使用的所有卷、容器和网络。这会释放大量资源。

解决方案:在云上运行


最后,在某些情况下,即使有上述提示,也不可能在你的笔记本上运行所需的所有容器。如果是这样的话,可以看看Blimp,这是一种在云上运行 Docker Compose 文件的简单方法。

你应该做什么?


为了提升 Docker Compose 上的开发者体验,我鼓励你


  1. 最小化容器重新构建

  2. 使用主机卷

  3. 力求可维护的 compose 文件,就像代码一样。

  4. 使你的引导可靠

  5. 用心管理资源


原文链接:


https://blimpup.io/blog/common-docker-compose-mistakes/


2021-01-18 12:003379
用户头像

发布了 165 篇内容, 共 75.8 次阅读, 收获喜欢 343 次。

关注

评论

发布
暂无评论
发现更多内容

纯css实现:如何做个完美的平行四边形

南极一块修炼千年的大冰块

7月月更

如何学习好web前端开发技术知识

小谷哥

国产操作系统生态建设,小程序技术来帮忙

Speedoooo

小程序 国产操作系统 小程序容器 桌面应用

百度工程师眼中的云原生可观测性追踪技术

百度Geek说

Java 云原生

架构实战营模块七作业

Geek_Q

深圳web前端技术学习费用是多少?尚硅谷前端培训

小谷哥

大数据开发技术入门学习方法有哪些

小谷哥

7月《中国数据库行业分析报告》发布!居安思危,安全先行

墨天轮

数据库 腾讯云 阿里云 国产数据库 数据库安全

openGauss内核分析:查询重写

华为云开发者联盟

数据库 后端 查询 SQL语言 openGauss内核

单元测试,写起来到底有多痛?你会了吗

C++后台开发

网络编程 单元测试 后端开发 Linux服务器开发 C++开发

洞见科技纪凯:隐私计算助力「全链路」金融客户经营

洞见科技

隐私计算

零基础如何学习大数据开发知识

小谷哥

LP流动性质押挖矿系统开发详细程序

开发微hkkf5566

重磅预告!易观分析联合微软、中央财经大学,共话数字经济

易观分析

数据驱动

6W+字记录实验全过程 | 探索Alluxio经济化数据存储策略

Alluxio

数据湖 数据膨胀 降本增效 Alluxio 大数据 开源

NFT链游戏系统开发元宇宙GameFi搭建

薇電13242772558

dapp NFT 元宇宙

李宏毅《机器学习》丨4. Deep Learning(深度学习)

AXYZdong

机器学习 7月月更

TMECH发表优必选运控技术最新进展:实现人形机器人高鲁棒性行走

优必选科技

CSDN Meetup 回顾 丨从数据湖到指标中台,提升数据分析 ROI

Kyligence

数据分析 指标中台

业务出海,灵感乍现前要先「把手弄脏」

融云 RongCloud

瓜分30万奖金!DeepRec CTR模型性能优化天池挑战赛来啦

阿里云大数据AI技术

深度学习

ModuleNotFoundError_No_module_named通俗的解释和方法

和牛

测试

21条最佳实践,全面保障 GitHub 使用安全

SEAL安全

GitHub 安全

视频聊天源码——一对一直播系统源码

开源直播系统源码

软件开发 直播系统源码 开源源码

大咖说·图书分享 | HaaS 物联网设备云端一体开发框架

大咖说

阿里巴巴 物联网 大咖 HaaS

【字体反爬】猫X眼YingShi,我们又来欺负你了,用到了 OCR 识别技术

梦想橡皮擦

Python 爬虫 7月月更

不懂点儿统计学,《星球大战》白看了

图灵教育

统计学 贝叶斯定理

如何用度量数据驱动代码评审的改善

思码逸研发效能

数据分析 研发效能 科技 效能度量

华为云:一切皆服务,共建全场景智慧金融

Geek_2d6073

直播预告 | 7月22日《开源安全治理模型和工具》线上研讨会

安势信息

开源安全 SCA工具 开源软件供应链 SBOM SLSA

尚硅谷SSM新版视频教程发布

小谷哥

5种常见的Docker Compose错误_开源_Ethan Jackson_InfoQ精选文章