在构建一个容器化应用程序时,开发人员需要一种方法来引导他们正在使用的容器去测试其代码。虽然有几种方法可以做到这一点,但 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语句中来减少层数。然而,镜像大小在开发过程中并不重要。在开发过程中,你想要尽可能多的层数。在公众号编程技术圈后台回复“Java”,获取Java面试题和答案惊喜礼包。

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

RUN \    go get -d -v \    && go install -v \    && go build

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

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

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

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

https://blimpup.io/

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 开发环境也用于生产环境。

在公众号编程技术圈后台回复“Java”,获取Java面试题和答案惊喜礼包。

解决方案:使用主机卷

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

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

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

https://www.npmjs.com/package/nodemon

https://blimpup.io/blog/docker-volumes-for-development/

它最初需要一些工作,但结果是,你可以在 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 项目,不用等就可以直接使用。

https://mutagen.io/

解决方案:不要加载包

对于 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"

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

https://blimpup.io/docs/#/getting-started

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 文件中指定开发环境所需的任何更改,例如使用主机卷。

https://docs.docker.com/compose/extends/

解决方案:使用extends

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

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

https://support.atlassian.com/bitbucket-cloud/docs/yaml-anchors/

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

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

https://blimpup.io/

随着扩展,可以编写一个脚本,来基于一些高级别的规范生成 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 的脚本来手动实现类似功能)

https://github.com/vishnubob/wait-for-it

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 文件的简单方法。

6你应该做什么?

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

  • 最小化容器重新构建

  • 使用主机卷

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

  • 使你的引导可靠

  • 用心管理资源

原文链接:https://blimpup.io/blog/common-docker-compose-mistakes/

5种常见的Docker Compose错误的更多相关文章

  1. 9种常见的Android开发错误及解决方案

    整理总结了9个Android开发中的错误以及解决方案,共同探讨进步! 1. 如果你的项目的R文件不见的话,可以试下改版本号在保存,R文件不见一般都是布局文本出错导致. 2. 布局文件不可以有大写字母 ...

  2. ADB几种常见的错误及解决方法

    下面列举出几种常见的错误及解决方法. Q1:无效的安装包,安装包已损坏[INSTALL_FAILED_INVALID_APK] A1:请检查安装包是否完整.如果是xpk包,可以通过 手动安装xpk来检 ...

  3. 几种常见的微服务架构方案——ZeroC IceGrid、Spring Cloud、基于消息队列、Docker Swarm

    微服务架构是当前很热门的一个概念,它不是凭空产生的,是技术发展的必然结果.虽然微服务架构没有公认的技术标准和规范草案,但业界已经有一些很有影响力的开源微服务架构平台,架构师可以根据公司的技术实力并结合 ...

  4. 记一次docker compose的低级错误

    记一次docker compose的低级错误 问题 ​ 今天在学习dockercompose的时候,启动docker compose up,结果却出现异常 Error response from da ...

  5. Docker 系列五(Docker Compose 项目).

    一.概念 Docker Compose 是官方编排项目之一,负责快速的部署分布式应用.它允许用户通过一个单独的 docker-compose.yml 模板文件(YAML格式)来定义一种相关联的应用容器 ...

  6. Docker入门-docker compose的使用

    Compose简介 Compose项目是Docker官方的开源项目,负责实现对Docker容器集群的快速编排.其代码目前在https://github.com/docker/compose 上开源. ...

  7. Sentry 监控 - 私有 Docker Compose 部署与故障排除详解

    内容整理自官方开发文档 系列 1 分钟快速使用 Docker 上手最新版 Sentry-CLI - 创建版本 快速使用 Docker 上手 Sentry-CLI - 30 秒上手 Source Map ...

  8. Docker Compose配置文件详解(V3)

    Docker Compose配置文件是Docker Compose的核心,用于定义服务.网络和数据卷.格式为YAML,默认路径为./docker-compose.yml,可以使用.yml或.yaml扩 ...

  9. .NET遇上Docker - 使用Docker Compose组织Ngnix和.NETCore运行

    本文工具准备: Docker for Windows Visual Studio 2015 与 Visual Studio Tools for Docker 或 Visual Studio 2017 ...

随机推荐

  1. VUE动态生成table表格(element-ui)(新增/删除)

    (直接复制即可测试) 结构(红色部分 data/prop/v-model 数据绑定): <template> <el-table size="small" :da ...

  2. Elasticsearch按请求体基本查询

    1 分页: localhost:9200/get-together/_search {"query": {"match_all": {}},"from ...

  3. 基于TMS320C6670的软件无线电核心板

    一.板卡概述 北京太速科技自主研发的TMS320C6670核心板,采用TI KeyStone系列的四核定点/浮点DSP TMS320C6670作主处理器.板卡引出处理器的全部信号引脚,便于客户二次开发 ...

  4. Vue 源码解读(1)—— 前言

    当学习成为了习惯,知识也就变成了常识. 感谢各位的 点赞.收藏和评论. 新视频和文章会第一时间在微信公众号发送,欢迎关注:李永宁lyn 文章已收录到 github 仓库 liyongning/blog ...

  5. Dubbo基础一之实战初体验

    本以为写这个小作文没什么难度的,可是好像并不是.前段时间重心放在驾考科目二,就想着小作文科二考过了再写也不是事,因为都实战过了.今天想着写却发现脑袋里啥都想不起来了,得翻项目和笔记回忆一下.所以还是那 ...

  6. IDEA tomcat启动报错----Artifact is being deployed, please wait...解决

    今天学习遇到了这个错误,记录下自己遇到的错误和解决方法! 这个报错的意思是: Artifact 正在部署中,请稍候- 实际上有可能就是jar包没有导进去.检查项目打包情况:file-->Proj ...

  7. elasticsearch算法之搜索模型(一)

    面对海量的信息,我们很容易被淹没在信息的海洋中:当我们需要查找某个信息的时候,我们就会输入能够体现我们意图的关键字,搜索引擎会通过解析我们的关键字从而构造相应的查询表示方法:然后搜索引擎通过构造的查询 ...

  8. Renix中如何实现流调速——网络测试仪实操

    在Renix操作中有时我们需要进行流调速,那么如何实现呢?接下来为您详细介绍. 第一步:预约测试资源 首先打开Renix软件,连接机箱, 预约端口 第二步:流调速 例如:端口下有3条流,分别设置为10 ...

  9. Google发布跨云Serverless管理平台Knative

    企业只要使用由Google与Pivotal.IBM.红帽和SAP等企业共同开发的跨云Serverless管理平台Knative,就能在支持Kubernetes的云平台上自由的迁移工作负载,无论是跨私有 ...

  10. Python "爬虫"出发前的装备之二数据先行( Requests 模块)

    1. 概念 爬虫不是动物,而是一种计算机程序. 这种程序有自己特定的功能,能按照使用者给定的一系列规则自行浏览万维网并获取需要的信息.此类程序被称为 网络爬虫(web crawler) 或 网络蜘蛛( ...