
这篇文章是应工人的要求而写的,他们定期询问“如何在docker中运行Illuminate / Symfony / MyOwn Psr7应用程序”。 我不想链接到以前写的文章 ,因为我对如何解决问题的看法已经发生了很大变化。
下面将要写的所有内容都是主观的体验, (一如既往)并没有声称它是唯一正确的决定,但是某些方法和解决方案对您来说似乎很有趣且有用。
作为一个应用程序,我还将使用Laravel,因为它对我来说是最熟悉且分布广泛的。 可以适应其他基于PSR-7的框架/组件,但是这个故事并非如此。
错误处理
我想从上一篇文章的背景来看是“不是最佳实践”:
- 需要更改存储库中文件的结构
- 使用FPM。 如果我们希望从应用程序中获得性能,那么即使在技术选择阶段,最好的解决方案之一也可能是放弃它,取而代之的是更快并且“适应”内存可能泄漏的事实。 lachezis的 RoadRunner从未出现在这里
- 带有来源和资产的单独图像。 尽管使用这种方法,我们仍可以重用同一张图像来构建传入请求的更复杂的路由(nginx位于前端,用于静态输出;动态请求由另一个容器提供,该容器将具有相同源的卷扔入其中-对于更好的缩放比例)-该方案已证明在产品操作中相当复杂。 而且,RR本身可以完美地呈现静态信息,如果静态信息很多(或者资源可以加载并显示用户生成的内容) ,我们会将其带到CDN(S3 + CloudFront + CloudFlare捆绑包可以正常工作),并且原则上忽略此问题
- 复杂CI。 当在组装和自动测试阶段开始活跃的“建筑肉”时期时,这成为一个真正的问题。 一个以前不支持此CI的家伙,很难更改它而又不怕破坏任何东西。
现在,在知道需要解决哪些问题并了解如何解决此问题后,我建议着手解决这些问题。 “开发人员工具”的集合没有改变-它是相同的docker-ce
, docker-compose
和功能强大的Makefile
。
结果,我们得到:
- 具有应用程序的独立容器,无需安装其他卷
- 使用git-hooks的示例-我们将在
git pull
自动之后放置必要的依赖项,并在测试失败的情况下禁止推送代码(当然,钩子将存储在git下) - RoadRunner将处理HTTP请求
- 开发人员仍然可以执行
dd(..)
和dump(..)
进行调试,而浏览器中不会崩溃 - 测试可以直接从PHPStorm IDE运行,而测试将与应用程序一起在容器中运行
- CI将在发布新的应用程序版本标记时为我们收集图像
- 让我们采取严格的规则来维护文件
CHANGELOG.md
和ENVIRONMENT.md
直观介绍新方法
为了进行良好的演示,我将整个过程分为几个阶段,其中的更改将作为单独的MR进行(合并之后,所有早午餐都将保留;在“步骤”的标题中链接至MR)。 起点是使用composer create-project laravel/laravel
的应用程序的Laravel框架:
$ docker run \ --rm -i \ -v "$(pwd):/src" \ -u "$(id -u):$(id -g)" \ composer composer create-project --prefer-dist laravel/laravel \ /src/laravel-in-docker-with-rr "5.8.*"
第一步是教应用程序在容器中运行。 为此,我们需要一个Dockerfile
, Dockerfile
docker-compose.yml
来描述“如何提升和链接容器”,以及一个Makefile
来将已经简化的过程简化为一个或两个命令。
Docker文件
我使用php:XXX-alpine
的基本映像php:XXX-alpine
最简单,其中包含您需要运行的映像。 此外,对解释器的所有后续更新都减少为仅更改此行中的值(更新PHP比以往更加容易了)。
使用multistage和COPY --from=...
将Composer和RoadRunner二进制文件传递到容器中,这非常方便,与版本相关的所有值都不会“分散”,而是位于文件的开头。 这可以快速运行,并且不依赖curl
/ git clone
/ make build
。 如果需要,我支持512k / roadrunner图像 -您可以自己汇编二进制文件。
一个有趣的故事发生在环境变量PS1
(负责shell中的提示)上-事实证明您可以在其中使用表情符号,并且一切都在本地运行,但是如果您尝试使用包含表情符号的变量(例如,牧场主)来启动图像,则它将崩溃(将所有内容淹没)可以正常工作)。
在Dockerfile
我开始生成自签名SSL证书,以便将其用于传入的HTTPS请求。 自然地,没有什么可以阻止使用“正常”证书。
我也想谈一谈:
COPY ./composer.* /app/ RUN set -xe \ && composer install --no-interaction --no-ansi --no-suggest --prefer-dist \ --no-autoloader --no-scripts \ && composer install --no-dev --no-interaction --no-ansi --no-suggest \ --prefer-dist --no-autoloader --no-scripts
此处的含义如下-将composer.lock
和composer.json
文件放在映像的单独层中,然后安装其中描述的所有依赖项。 这样做是为了在以后使用--cache-from
构建映像期间,如果未更改已安装依赖项的组成和版本,则不会执行composer install
,从而从缓存中获取该层,从而节省了构建时间和流量(感谢您的想法) jetexe )。
composer install
执行两次(第二次使用--no-dev
)以“预热”开发依赖项的缓存,因此,当我们将所有依赖项放在CI上以运行测试时,它们将从已在映像中的composer缓存中放入,而不是从遥远的星系延伸。
使用最后一条RUN
指令,我们将在构建日志中显示历史记录,并显示“已至少存在并且以某种方式启动”,显示已安装软件的版本和PHP模块的组成。
我还使用我的Entrypoint,因为在集群中某个地方启动应用程序之前,我真的想检查依赖服务的可用性-DB,Redis,Rabbit等。
行军
为了将RoadRunner与Laravel应用程序集成,编写了一个程序包 ,该程序包将所有集成减少到Shell中的几个命令(通过运行docker-compose run app sh
):
$ composer require avto-dev/roadrunner-laravel "^2.0" $ ./artisan vendor:publish --provider='AvtoDev\RoadRunnerLaravel\ServiceProvider' --tag=rr-config
将APP_FORCE_HTTPS=true
添加到./docker/docker-compose.env
文件,并在.rr*.yaml
的容器中指定SSL证书的路径。
为了能够使用dump(..)
和dd(..)
并且一切正常,有另一个软件包avto-dev/stacked-dumper-laravel
。 所需要做的就是向这些辅助程序添加一个pefix,分别是\dev\dd(..)
和\dev\dump(..)
。 否则,您将看到以下形式的错误:
worker error: invalid data found in the buffer (possible echo)
完成所有操作后,执行docker-compose up -d
和voila:

PostgeSQL数据库,redis和RoadRunner工作人员已成功在容器中运行。
如我先前所写,Makefile是一个被低估的项目。 相互依赖的目标,自己的语法能力,99%的可能性使他已经站在Linux / mac开发机器上,“自动完成”(即装即用)-一小部分优点。
将其添加到我们的项目中并make
不带参数的make
,我们可以观察到:

要运行单元测试,我们可以做一个make test
,或者通过应用程序( make shell
)在容器中放入一个shell,运行composer phpunit
。 要获取覆盖率报告,只需进行make test-cover
,然后在运行测试之前,将xdebug及其依赖项传递到容器并启动测试(因为此过程通常不执行,并且不是由CI执行-此解决方案比将所有内容都保留一个单独的映像更好开发)。
吊钩
在我们的案例中,钩子将扮演两个重要角色-不允许将代码推送到测试不成功的源中; 并自动将所有必要的依赖项放到您的机器上,如果发现更改composer.lock
了。 在Makefile
,有一个单独的目标:
cwd = $(shell pwd) git-hooks: ## Install (reinstall) git hooks (required after repository cloning) -rm -f "$(cwd)/.git/hooks/pre-push" "$(cwd)/.git/hooks/pre-commit" "$(cwd)/.git/hooks/post-merge" ln -s "$(cwd)/.gitlab/git-hooks/pre-push.sh" "$(cwd)/.git/hooks/pre-push" ln -s "$(cwd)/.gitlab/git-hooks/pre-commit.sh" "$(cwd)/.git/hooks/pre-commit" ln -s "$(cwd)/.gitlab/git-hooks/post-merge.sh" "$(cwd)/.git/hooks/post-merge"
make git-hooks
只是简单地.gitlab/git-hooks
了现有的钩子,并将它们放在.gitlab/git-hooks
目录中。 可以在此链接上查看其来源。
从PhpStorm运行测试
尽管它非常简单和方便-我使用了很长时间./vendor/bin/phpunit --group=foo
而不是在编写与之相关的测试或代码时直接按热键。
单击File > Settings > Languages & Frameworks > PHP > CLI interpreter > [...] > [+] > From Docker, Vargant, VM, Remote
。 选择Docker compose和应用程序服务名称。

第二步是告诉phpunit使用容器中的解释器: File > Settings > Test frameworks > [+] > PHPUnit by remote interpreter
然后选择先前创建的远程解释器。 在Path to script
的Path to script
/app/vendor/autoload.php
,指定/app/vendor/autoload.php
,在Path mappings
指定在/app
安装的项目的根目录。

现在,我们可以使用应用程序在映像内使用解释器直接从IDE运行测试,按(默认情况下为Linux)Ctrl + Shift + F10。
我们剩下要做的就是自动化运行测试和组装映像的过程。 为此,请在应用程序的根目录中创建.gitlab-ci.yml
在其中填充以下内容 。 此配置的主要思想是尽可能简单,但不要同时失去功能。
图像在每个早午餐,每次提交时进行组装。 使用--cache-from
重新提交时组装图像非常快。 需要重建的原因是,在每个早午餐上我们都有一张图像,其中包含作为早午餐的一部分而进行的更改,因此,没有什么可以阻止我们将其推广到群体/ k8s /等以确保“实时”确保一切正常,甚至在与master
灯合并之前都可以正常工作。
组装之后,我们运行单元测试并检查容器中应用程序的启动,将第curl请求发送给运行状况检查端点(此操作是可选的,但几次此测试对我有很大帮助)。
对于“发行版”-只需发布格式为vX.XX
的标签 (如果您仍然坚持语义版本控制-这将非常酷)-CI将收集映像,运行测试并执行在deploy to somewhere
指定的操作。
不要忘记在项目设置中(如果可能的话)将发布标签的功能限制为仅允许“发布发布”的人员发布。
CHANGELOG.md
和ENVIRONMENT.md
在接受一个或另一个MR之前,检查人员必须毫无CHANGELOG.md
检查CHANGELOG.md
和ENVIRONMENT.md
文件CHANGELOG.md
ENVIRONMENT.md
。 如果第一个越来越清晰 ,那么相对的第二个我将给出解释。 该文件用于描述应用程序容器响应的所有环境变量。 即 如果开发人员添加或删除了对一个或另一个环境变量的支持,则必须在此文件中反映出来。 当出现“我们迫切需要重新定义该问题”的问题时-没有人疯狂地开始研究文档或源代码-而是查看一个文件。 很舒服
结论
在本文中,我们研究了将应用程序开发和启动转移到Docker环境,集成的RoadRunner以及使用简单的CI脚本自动完成我们的应用程序的图像组装和测试这一相当轻松的过程。
克隆存储库后,开发人员必须make git-hooks && make install && make up
并开始编写有用的代码。 * ops-am同志-拍摄带有所需标签的图像并将其滚动到他们的集群上。
当然,该方案也得到了简化,在“战斗”项目中,我要花很多时间,但是如果本文中描述的方法对某人有所帮助,我就会浪费时间。