5种常见的Docker Compose错误

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在构建一个容器化应用程序时,开发人员需要一种方法来引导他们正在使用的容器去测试其代码。虽然有几种方法可以做到这一点,但Docker Compose是最流行的选择之一。它让你可以轻松指定开发期间要引导的容器,其次建立一个快速的“编码-测试-调试”开发循环。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"愿景是一个人编写一个"},{"type":"codeinline","content":[{"type":"text","text":"docker-compose.yml"}]},{"type":"text","text":"文件,指定了开发中所需的一切,并将它提交到代码仓库。然后,每一个开发者只需运行"},{"type":"codeinline","content":[{"type":"text","text":"docker-compose up"}]},{"type":"text","text":",即可启动测试其代码需要的所有容器。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然而,要使"},{"type":"codeinline","content":[{"type":"text","text":"docker-compose"}]},{"type":"text","text":"设置达到最高性能,需要大量工作。我们见过最好的团队"},{"type":"text","marks":[{"type":"strong"}],"text":"在不到一分钟的时间内"},{"type":"text","text":"启动他们的开发环境,并"},{"type":"text","marks":[{"type":"strong"}],"text":"在几秒中内"},{"type":"text","text":"测试每个更改。考虑到每个开发人员每天花在测试代码上的时间,小的改进会对开发人员的生产力产生巨大影响。"}]},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/21\/21a7ed4b2a503739443f21c2ef928e00.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","text":"源自XKCD"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"错误1:频繁的容器重建"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"docker build"}]},{"type":"text","text":"需要很长时间。如果每次想要测试一个代码更改时都要重新构建你的容器,那么你就有很大潜力来加速你的开发循环。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"处理非容器化应用程序的传统工作流如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"编码"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"构建"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"运行"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这些年来,通过使用针对编译型语言的增量构建和热加载之类的技巧,这个过程得到高度优化。它变得非常快。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"当人们第一次采用容器时,他们倾向于采用现有的工作流程,只添加一个"},{"type":"codeinline","content":[{"type":"text","text":"docker build"}]},{"type":"text","text":"步骤。他们的工作流如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"编码"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"构建"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"容器构建"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"运行"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果做的不好,"},{"type":"codeinline","content":[{"type":"text","text":"docker build"}]},{"type":"text","text":"步骤会使所有优化都白费。另外,它还增加了一堆额外的耗时工作,例如使用apt-get重新安装依赖。所有这些加起来,会使得我们的测试过程比使用Docker之前要慢得多。"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"解决方案:在Docker外运行你的代码"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一种方案是在Docker Compose中启动所有依赖项,但在本地运行你正在积极处理的代码。这模仿了开发非容器化应用程序的工作流。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"只需要在"},{"type":"codeinline","content":[{"type":"text","text":"localhost"}]},{"type":"text","text":"上暴露你的依赖,并将你正在使用的服务指向"},{"type":"codeinline","content":[{"type":"text","text":"localhost:"}]},{"type":"text","text":"地址。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然而,这并不总是可行的,尤其是如果你正在处理的代码依赖容器镜像内置的东西,而这些东西不容易从你的笔记本电脑访问。"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"解决方案:最大化缓存来优化Dockerfile"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果必须构建Docker镜像,那么编写Dockerfile时,最大化缓存能将一个10分钟的Docker构建变为1分钟。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"生产环境的Dockerfile文件的典型模式是通过将单个命令链接到一个"},{"type":"codeinline","content":[{"type":"text","text":"RUN"}]},{"type":"text","text":"语句中来减少层数。然而,镜像大小在开发过程中并不重要。在开发过程中,你想要尽可能多的层数。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你的生产环境Dockerfile文件可能如下所示:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"plain"},"content":[{"type":"text","text":"RUN \\\n go get -d -v \\\n && go install -v \\\n && go build"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这对开发来说很糟糕,因为每次重新运行该命令时,Docker都会重新下载所有的依赖并重新安装它们。增量构建更加有效。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"相反,你应该有一个专门用于开发环境的的Dockerfile文件。将每件事都分解成非常小的步骤,规划好你的Dockerfile文件,这样基于经常变化的代码的步骤最后执行。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最不频繁更改的内容,例如拉取依赖,应该放在第一位。这样,在重建Dockerfile时就不必构建整个项目。你只需要构建你刚刚修改的一小部分。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"关于这个的例子,请看下面我们用于"},{"type":"link","attrs":{"href":"https:\/\/blimpup.io\/","title":"","type":null},"content":[{"type":"text","text":"Blimp"}]},{"type":"text","text":"开发环境的Dockerfile。它遵循上面所述的技术,将繁重的构建过程缩短到几秒钟。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"plain"},"content":[{"type":"text","text":"FROM golang:1.13-alpine as builder\n\nRUN apk add busybox-static\n\nWORKDIR \/go\/src\/github.com\/kelda-inc\/blimp\n\nADD .\/go.mod .\/go.mod\nADD .\/go.sum .\/go.sum\nADD .\/pkg .\/pkg\n\nARG COMPILE_FLAGS\n\nRUN CGO_ENABLED=0 go install -i -ldflags \"${COMPILE_FLAGS}\" .\/pkg\/...\n\nADD .\/login-proxy .\/login-proxy\nRUN CGO_ENABLED=0 go install -i -ldflags \"${COMPILE_FLAGS}\" .\/login-proxy\/...\n\nADD .\/registry .\/registry\nRUN CGO_ENABLED=0 go install -i -ldflags \"${COMPILE_FLAGS}\" .\/registry\/...\n\nADD .\/sandbox .\/sandbox\nRUN CGO_ENABLED=0 go install -i -ldflags \"${COMPILE_FLAGS}\" .\/sandbox\/...\n\nADD .\/cluster-controller .\/cluster-controller\nRUN CGO_ENABLED=0 go install -i -ldflags \"${COMPILE_FLAGS}\" .\/cluster-controller\/...\n\nRUN mkdir \/gobin\nRUN cp \/go\/bin\/cluster-controller \/gobin\/blimp-cluster-controller\nRUN cp \/go\/bin\/syncthing \/gobin\/blimp-syncthing\nRUN cp \/go\/bin\/init \/gobin\/blimp-init\nRUN cp \/go\/bin\/sbctl \/gobin\/blimp-sbctl\nRUN cp \/go\/bin\/registry \/gobin\/blimp-auth\nRUN cp \/go\/bin\/vcp \/gobin\/blimp-vcp\nRUN cp \/go\/bin\/login-proxy \/gobin\/login-proxy\n\nFROM alpine\n\nCOPY --from=builder \/bin\/busybox.static \/bin\/busybox.static\nCOPY --from=builder \/gobin\/* \/bin\/"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最后一点:通过最近引入的"},{"type":"link","attrs":{"href":"https:\/\/docs.docker.com\/develop\/develop-images\/multistage-build\/","title":"","type":null},"content":[{"type":"text","text":"多阶段构建"}]},{"type":"text","text":",现在可以创建具有良好分层且镜像很小的Dockerfile。我们在本文中对这一点不会过多讨论,只能说上面显示的Dockerfile就是这样做的,因此它既用于"},{"type":"link","attrs":{"href":"https:\/\/blimpup.io\/","title":"","type":null},"content":[{"type":"text","text":"Blimp"}]},{"type":"text","text":"开发环境也用于生产环境。"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"解决方案:使用主机卷"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通常,最好的选择是使用一个主机卷来直接将你的代码加载到容器上。这使你能够以本机速度运行代码,同时仍然在包含运行时依赖项的Docker容器中运行。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"主机卷将你笔记本电脑上的一个目录镜像到一个正在运行的容器中。当你在文本编辑器中编辑一个文件时,更改会自动同步到容器中,然后能立即在容器中执行。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"大多数语言都有一种方法来监视你的代码,并在代码更改时自动重新运行。例如,"},{"type":"link","attrs":{"href":"https:\/\/www.npmjs.com\/package\/nodemon","title":"","type":null},"content":[{"type":"text","text":"nodemon"}]},{"type":"text","text":"是Javascript中的监视代码的方法。请查看这篇关于如何设置这一点的"},{"type":"link","attrs":{"href":"https:\/\/blimpup.io\/blog\/docker-volumes-for-development\/","title":"","type":null},"content":[{"type":"text","text":"文章"}]},{"type":"text","text":"教程。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"它最初需要一些工作,但结果是,你可以在1-2秒内看到你的代码更改的结果,而一次Docker构建可能需要几分钟。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"错误2:主机卷速度慢"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果使用了主机卷,你可能已经注意到,在Windows和Mac上读写文件的速度非常慢。对于读写大量文件的命令来说,这是一个已知的问题,例如具有复杂依赖的Node.js和PHP应用程序。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这是因为Docker是运行在Windows和Mac的一个虚拟机上。在进行主机卷加载时,必须经过大量的转换才能将笔记本电脑上的文件夹加载到容器中,这有点儿类似网络文件系统。这会增加大量负载,而在Linux本机上运行Docker时不会出现这些情况。"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"解决方案:放松强一致性"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其中一个关键问题是,默认情况下,文件系统加载会保持强一致性。一致性是一个广泛的话题,可以浓墨重彩地大书特书,但是简而言之,它意味着所有特定文件的读取者和写入者都同意任何文件修改发生的顺序,从而(最终,某种程度上)同意该文件的内容。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"问题是,强制实现强一致性是相当昂贵的,需要所有文件写入者确保他们不会不恰当地破坏彼此的更改。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"虽然强一致性有时特别重要,例如,当在生产环境运行数据库时。好消息是,在开发环境,它不是必需的。你的代码文件只会有单个写入者(你自己),和单个信源(你的代码库)。因此,冲突并不像在生产中那么需要担心。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"正是由于这个原因,Docker实现了在加载卷时放松一致性保证的功能。在Docker Compose中,你只需将"},{"type":"codeinline","content":[{"type":"text","text":"cached"}]},{"type":"text","text":"关键词添加到卷加载中即可获得显著的性能保证。(不要在生产环境这么做...)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"plain"},"content":[{"type":"text","text":"volumes:\n - \".\/app:\/usr\/src\/app\/app:cached\""}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"解决方案:代码同步"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"另一种方案是设置代码同步。你可以用一个工具来通知你的笔记本电脑和容器之间的更改,并复制文件来解决差异(类似于rsync),而不是加载一个卷。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Docker的下一个版本内置了"},{"type":"link","attrs":{"href":"https:\/\/mutagen.io\/","title":"","type":null},"content":[{"type":"text","text":"Mutagen"}]},{"type":"text","text":",作为卷的缓存模式的一种替代。如果你感兴趣,就等Docker发布下一个版本再试试,不过你也可以直接下载Mutagen项目,不用等就可以直接使用。"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"解决方案:不要加载包"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"对于Node这样的语言,大部分文件操作往往位于包目录(例如"},{"type":"codeinline","content":[{"type":"text","text":"node_modules"}]},{"type":"text","text":")。因此,从卷中排除这些目录会显著提高性能。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在下面的例子中,我们有一个卷将代码加载到一个容器中。然后用它自己干净的专用卷覆盖了"},{"type":"codeinline","content":[{"type":"text","text":"node_modules"}]},{"type":"text","text":"目录。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"plain"},"content":[{"type":"text","text":"volumes:\n - \".:\/usr\/src\/app\"\n - \"\/usr\/src\/app\/node_modules\""}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这个额外的卷加载告诉Docker为"},{"type":"codeinline","content":[{"type":"text","text":"node_modules"}]},{"type":"text","text":"目录使用一个标准卷,这样当"},{"type":"codeinline","content":[{"type":"text","text":"npm install"}]},{"type":"text","text":"运行时,它不会使用比较慢的主机加载。为了使之生效,当容器首次启动时,我们在"},{"type":"codeinline","content":[{"type":"text","text":"entrypoint"}]},{"type":"text","text":"运行"},{"type":"codeinline","content":[{"type":"text","text":"npm install"}]},{"type":"text","text":"来安装我们的依赖并填充"},{"type":"codeinline","content":[{"type":"text","text":"node_modules"}]},{"type":"text","text":"目录。像这样:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"plain"},"content":[{"type":"text","text":"entrypoint:\n - \"sh\"\n - \"-c\"\n - \"npm install && .\/node_modules\/.bin\/nodemon server.js\""}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"克隆和下载上述示例代码的完整说明,请参考"},{"type":"link","attrs":{"href":"https:\/\/blimpup.io\/docs\/#\/getting-started","title":"","type":null},"content":[{"type":"text","text":"此处"}]},{"type":"text","text":"。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"错误3:脆弱的配置"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"大多数Docker Compose文件都是有组织地演化的。我们通常会看到大量的复制粘贴代码,这使得代码修改非常困难。一个干净的Docker Compose文件可以更容易地在生产环境变化时进行定期更新。"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"解决方案:使用env文件"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"env文件将环境变量从主Docker Compose配置中分离出来。这有助于:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"使密钥不会保存在git历史中"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"使每个开发者拥有稍微不同的设置变得容易。例如,每个开发者可能有一个唯一的access密钥。将配置保存在一个"},{"type":"codeinline","content":[{"type":"text","text":".env"}]},{"type":"text","text":"文件中意味着他们不必修改提交的"},{"type":"codeinline","content":[{"type":"text","text":"docker-compose.yml"}]},{"type":"text","text":"文件,并在这个文件更新时处理冲突。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"要使用env文件,只需增加一个"},{"type":"codeinline","content":[{"type":"text","text":".env"}]},{"type":"text","text":"文件,或者使用"},{"type":"codeinline","content":[{"type":"text","text":"env_file"}]},{"type":"text","text":"字段显式设置路径。"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"解决方案:使用override文件"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https:\/\/docs.docker.com\/compose\/extends\/","title":"","type":null},"content":[{"type":"text","text":"Override文件"}]},{"type":"text","text":"让你有一个基本配置,然后在不同文件中指定修改。如果你使用Docker Swarm,并且有一个生产环境的YAML文件,这将非常有用。你可以在"},{"type":"codeinline","content":[{"type":"text","text":"docker-compose.yml"}]},{"type":"text","text":"中存储自己的生产环境配置,然后在一个override文件中指定开发环境所需的任何更改,例如使用主机卷。"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"解决方案:使用"},{"type":"codeinline","content":[{"type":"text","text":"extends"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果你正在用Docker Compose v2,你可以使用"},{"type":"codeinline","content":[{"type":"text","text":"extends"}]},{"type":"text","text":"关键字在多个地方导入YAML片段。例如,你可能有一个定义,你公司的所有服务在开发环境的Docker Compose文件中都有这5个特定的配置项。你可以定义一次,然后使用"},{"type":"codeinline","content":[{"type":"text","text":"extends"}]},{"type":"text","text":"关键词来将它放到任何需要的地方,这就提供了一些模块化特性。我们不得不在YAML中这样做是很痛苦的,但我们能够少写一个程序来生成它还是最好的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Compose v3移除了对"},{"type":"codeinline","content":[{"type":"text","text":"extends"}]},{"type":"text","text":"关键词的支持。然而,你可以使用"},{"type":"link","attrs":{"href":"https:\/\/support.atlassian.com\/bitbucket-cloud\/docs\/yaml-anchors\/","title":"","type":null},"content":[{"type":"text","text":"YAML anchors"}]},{"type":"text","text":"获得类似的结果。"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"解决方案:程序生成Compose文件"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们已经和一些使用"},{"type":"link","attrs":{"href":"https:\/\/blimpup.io\/","title":"","type":null},"content":[{"type":"text","text":"Blimp"}]},{"type":"text","text":"的工程团队一起工作过,他们在开发环境的Docker Compose文件中有上百个容器。如果他们使用单个巨大的Docker Compose文件,就需要数千行无法维护的YAML代码。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"随着扩展,可以编写一个脚本,来基于一些高级别的规范生成Docker Compose文件。这对于具有非常大的开发环境的工程团队来说是很常见的。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"错误4:脆弱的引导"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"docker-compose up"}]},{"type":"text","text":"是不是只有一半时间工作?你是不是不得不使用"},{"type":"codeinline","content":[{"type":"text","text":"docker-compose restart"}]},{"type":"text","text":"来启动崩溃的服务?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"大多数开发者都想要写代码,不想做DevOps工作。调试一个坏的开发环境是非常令人沮丧的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"docker-compose up"}]},{"type":"text","text":"应该每一次都好好工作。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这里的大多数问题都与服务启动顺序错误有关。例如,你的Web应用可能依赖一个数据库,如果Web应用启动时数据库还没有就绪,那么它就会崩溃。"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"解决方案:使用"},{"type":"codeinline","content":[{"type":"text","text":"depends_on"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"depends_on"}]},{"type":"text","text":"使你能控制启动顺序。 默认地,"},{"type":"codeinline","content":[{"type":"text","text":"depends_on"}]},{"type":"text","text":"会等待依赖被创建,而不等待处于“healthy”状态的依赖。然而,Docker Compose v2支持将depends_on与健康状态检查结合起来。(不幸的是,这个功能在Docker Compose v3中被移除了。你可以使用一个类似"},{"type":"link","attrs":{"href":"https:\/\/github.com\/vishnubob\/wait-for-it","title":"","type":null},"content":[{"type":"text","text":"wait-for-it.sh"}]},{"type":"text","text":"的脚本来手动实现类似功能)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Docker文档建议不要使用类似"},{"type":"codeinline","content":[{"type":"text","text":"depends_on"}]},{"type":"text","text":"和"},{"type":"codeinline","content":[{"type":"text","text":"wait-for-it.sh"}]},{"type":"text","text":"之类的方案。而且,我们同意,在生产环境,要求为容器指定特定的引导顺序是脆弱架构的一种标志。然而,作为一名试图完成工作的开发人员,修复整个工程组织中的每一个容器可能是不可行的。因此,对于开发环境,我们认为这是可以的。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"错误5:资源管理不善"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"要确保Docker拥有它流畅运行所需的资源,而不会完全超出你的笔记本电脑负担,可能是比较棘手的。如果你觉得自己的开发工作流由于Docker没有以峰值容量运行而变得迟缓,那么可以参考以下几点。"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"解决方案:修改Docker Desktop分配"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Docker Desktop需要大量RAM和CPU,尤其是在Mac和Windows上它是一个虚拟机时。默认的Docker Desktop配置往往没有分配足够的RAM和CPU,因此我们一般建议调整设置到过度分配。我倾向于在开发时给Docker分配大约8GB RAM和4 CPU(在不使用Docker Desktop时,我会将它关掉,来使之可行)"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"解决方案:删除未使用的资源"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"人们在使用Docker时,经常会无意识地泄漏资源。人们拥有成百上千的卷、旧的容器镜像以及如果不小心有时还会运行的容器,这并不少见。这就是为什么我们推荐偶尔运行"},{"type":"codeinline","content":[{"type":"text","text":"docker system prune"}]},{"type":"text","text":",删除当前没有使用的所有卷、容器和网络。这会释放大量资源。"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"解决方案:在云上运行"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最后,在某些情况下,即使有上述提示,也不可能在你的笔记本上运行所需的所有容器。如果是这样的话,可以看看"},{"type":"link","attrs":{"href":"https:\/\/blimpup.io\/","title":"","type":null},"content":[{"type":"text","text":"Blimp"}]},{"type":"text","text":",这是一种在云上运行Docker Compose文件的简单方法。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"你应该做什么?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"为了提升Docker Compose上的开发者体验,我鼓励你"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"最小化容器重新构建"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"使用主机卷"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"力求可维护的compose文件,就像代码一样。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","text":"使你的引导可靠"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":5,"align":null,"origin":null},"content":[{"type":"text","text":"用心管理资源"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"原文链接:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https:\/\/blimpup.io\/blog\/common-docker-compose-mistakes\/","title":null,"type":null},"content":[{"type":"text","text":"https:\/\/blimpup.io\/blog\/common-docker-compose-mistakes\/"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章