Docker + Laravel =❤

码头工人


在本文中,我将讨论我将Laravel应用程序“包装”在Docker容器中的经验,以便前端和后端开发人员可以在本地使用它,并在生产中启动它尽可能简单。 此外,CI将自动运行静态代码分析器, phpunit测试并生成映像。


“实际上,复杂性是什么?” -您可以说,您将部分正确。 事实是,俄语和英语社区中有很多讨论都专门针对此主题,我有条件地将几乎所有研究的话题分为以下几类:


  • “我正在使用docker进行本地开发。我放了laradock ,但我不知道有什么麻烦。” 很酷,但是自动化和生产启动又如何呢?
  • “我收集了一个基于fedora:latest容器(整体) fedora:latest容器约230 Mb),将所有服务(nginx,db,缓存等)放入其中,并在管理程序中运行所有内容。” 同样出色,易于上手,但是“一个容器-一个过程”的思想呢? 那么平衡和流程管理呢? 图片的大小是多少?
  • “这里有一些配置,用sh脚本摘录的季节,添加魔术env值,使用它。” 谢谢,但是至少我能分叉并充分发挥作用的一个活生生的例子呢?

您在下面阅读的所有内容都是主观体验,并不伪装成最终真理。 如果您有其他补充或不正确的迹象-欢迎发表评论。


对于不耐烦的人- 到存储库链接 ,您可以使用一个命令来克隆Laravel应用程序。 在同一个牧场主上运行它,正确地“链接”容器或使用docker-compose.yml杂货版本作为起点也并不困难。

理论部分


我们将在工作中使用哪些工具,我们将重点关注什么? 首先,我们需要在主机上安装:


  • 18.06.1-ce在撰写本文时,我使用的版本为18.06.1-ce
  • docker-compose compose-处理链接容器并存储必要的环境值; 版本1.22.0
  • make您可能会感到惊讶,但它完全适合与docker合作的环境

您可以使用命令curl -fsSL get.docker.com | sudo shcurl -fsSL get.docker.com | sudo sh debian的系统上curl -fsSL get.docker.com | sudo sh curl -fsSL get.docker.com | sudo sh ,但docker-compose最好使用pip进行安装,因为最新版本位于其存储库中( aptapt落后很多)。

这样就完成了依赖项列表。 您将使用什么来处理源代码phpstormnetbeans或已死的phpstorm由您决定。


接下来是关于(我不怕这个词)图像设计的即兴质量检查:


  • 问:基本图片-哪个更好选择?


  • 答:那是“稀薄的”,没有多余的装饰。 在alpine (〜5 Mb)的基础上您可以收集任何您想要的东西,但是最有可能的是您必须从源头开始组装服务。 作为替代方案, jessie-slim jessie-slim Mb) 。 或使用您的项目中最常用的一种。


  • 问:为什么图像重量很重要?


  • 答:流量减少,下载时出错的可能性降低(较少的数据-可能性较小),占用的位置减少。 “重力可靠”(©“ Snatch”)规则在这里不太适用。


  • 问:但是我的朋友%friend_name%说,具有所有依赖性的“整体”图像是最好的方法。


  • 答:让我们算一下。 该应用程序具有3个依赖项-PG,Redis和PHP。 您想测试在这些依赖的不同版本的捆绑包中它将如何表现。 PG-版本9.6和10,Redis-3.2和4.0,PHP-7.0和7.2。 如果每种成瘾都是一张单独的图片-您需要其中的6张,甚至不需要收集它们-一切就绪,并且都位于hub.docker.com 。 如果出于意识形态的原因,所有依赖项都“包装”在一个容器中,则您必须用钢笔将其重新组装……8次? 现在添加您仍要使用opcache 。 在分解的情况下,这仅仅是所使用图像标签的变化。 整体更易于运行和维护,但无路可走。


  • 问:为什么容器中的主管是邪恶的?


  • 答:因为PID 1 。 如果您不希望僵尸进程遇到很多问题,并且可以在需要时灵活地“增加容量”,请尝试在每个容器中运行一个进程。 nginx的工人和php-fpm是一个特有的例外,它们具有生成过程的能力,但是必须忍受这一点(而且,他们对SIGTERM反应还不错,可以很正确地“杀死”他们的工人)。 通过启动所有恶魔作为主管,您几乎肯定会注视着自己的问题。 尽管在某些情况下,没有它很难做到,但是这些已经是例外。



在确定了主要方法之后,让我们继续进行应用。 它必须能够:


  • web|api使用nginx赋予静态,并使用fpm生成动态内容
  • scheduler -运行本机任务计划程序
  • queue -从队列处理作业

一个基本集合,如有必要可以扩展。 现在让我们继续我们必须收集的图像,以便我们的应用程序“起飞”(它们的代码名称在方括号中给出):


  • PHP + PHP-FPMapp )-将在其中执行我们的代码的环境。 由于PHP和FPM的版本对我们来说是相同的-我们将它们收集在一个映像中。 因此,使用配置更容易管理,并且软件包的组成将完全相同。 当然-FPM和应用程序进程将在不同的容器中运行
  • nginxnginx )-不会为nginx提供配置和可选模块带来麻烦-我们将使用它收集一个单独的映像。 由于它是一个单独的服务,因此它具有自己的docker文件及其上下文
  • 应用程序的source )-源将使用单独的映像交付,将其安装在与应用程序一起放置的容器中。 基本映像是alpine ,内部只有具有已安装依赖项的源,并使用webpack资产(构建工件)进行收集

其他开发服务在容器中启动,将它们从hub.docker.com ; 另一方面,它们在生产环境中运行-在群集的单独服务器上运行。 对我们来说,剩下的就是(通过环境)告诉应用程序哪个地址/端口以及需要敲打哪些细节。 更加酷的是为此目的使用服务发现,但是这次不行。


在确定了理论部分之后,我建议继续进行下一部分。


实际部分


我建议如下组织存储库中的文件:


 . ├── docker #    -   │  ├── app │  │  ├── Dockerfile │  │  └── ... │  ├── nginx │  │  ├── Dockerfile │  │  └── ... │  └── sources │    ├── Dockerfile │    └── ... ├── src #   │ ├── app │ ├── bootstrap │ ├── config │ ├── artisan │ └── ... ├── docker-compose.yml # Compose-    ├── Makefile ├── CHANGELOG.md └── README.md 

您可以通过单击此链接来熟悉结构和文件。

要构建服务,可以使用以下命令:


 $ docker build \ --tag %local_image_name% \ -f ./docker/%service_directory%/Dockerfile ./docker/%service_directory% 

唯一的区别是图像与源的组装-为此, ./src将组装上下文(极端参数)设置为./src


在本地注册表中命名映像的规则建议使用docker-compose默认使用的规则,即: %root_directory_name%_%service_name% 。 如果项目目录名为my-awesome-project ,并且服务名为redis ,则映像名称(本地)最好分别选择my-awesome-project_redis


为了加快构建过程,您可以告诉docker使用先前组装的映像的缓存,为此,使用了启动--cache-from %full_registry_name% 。 因此,在启动Dockerfile中的特定指令之前,泊坞窗守护程序将查找-是否已更改? 如果没有(哈希收敛),它将使用图像中已经准备好的图层跳过该指令,您将告诉它用作缓存。 这个东西还不错,所以它将重建过程,尤其是在没有任何变化的情况下:)

注意用于启动应用程序容器的ENTRYPOINT脚本。

收集了用于启动应用程序(app)的环境的图像,考虑到它不仅可以在生产环境上运行,而且可以在本地运行,开发人员需要与之有效地交互。 安装和删除composer依赖项,运行unit测试, tail日志以及使用熟悉的别名( php /app/artisanartcomposerc )应该没有任何不适。 此外,它还将用于在CI上运行unit测试和静态代码分析器(在本例中为phpstan )。 例如,这就是为什么其Dockerfile包含xdebug安装xdebug ,但未启用模块本身(仅使用CI启用)的原因。


同样对于composer hirak/prestissimo软件包是hirak/prestissimo ,这大大提高了所有依赖项的安装。

在生产中,我们将映像中的/src目录的内容及其中的源(源)装入到/app目录中。 为了进行开发,我们使用应用程序源( -v "$(pwd)/src:/app:rw" )“滚动”本地目录。


这就是一种复杂性-这些是从容器创建的文件的访问权限 。 事实是,默认情况下,容器内运行的进程也从根目录( root:root ),这些进程创建的文件(缓存,日志,会话等)开始,因此,您在本地没有任何内容您可以在不执行sudo chown -R $(id -u):$(id -g) /path/to/sources


作为一种解决方案,请使用fixuid ,但是此解决方案很简单。 在我看来,最好的方法是在容器内USER_ID本地USER_ID及其GROUP_ID ,并使用这些值启动进程 。 默认情况下,将值1000:1000 (第一个本地用户的默认值)替换为调用$(id -u):$(id -g) ,如果需要,您可以始终覆盖它们( $ USER_ID=666 docker-compose up -d )或将.env -compose文件放入.env文件。


另外,在本地启动php-fpmphp-fpm不要忘记opcache禁用opcache否则会有很多“是的,该死的!” 将为您提供。


对于与redis和postgres的“直接”连接,我抛出了“ out”(分别为1543215432 )附加端口,因此原则上“连接并查看其内容和方式”没有问题。


为了方便访问应用程序,我将容器与代号为app运行( --command keep-alive.sh )。


以下是使用docker-compose解决日常问题的一些示例:


运作方式运行命令
安装composer软件包$ docker-compose exec app composer require package/name
运行phpunit$ docker-compose exec app php ./vendor/bin/phpunit --no-coverage
安装所有节点依赖项$ docker-compose run --rm node npm install
安装节点包$ docker-compose run --rm node npm i package_name
启动资产的实时重建$ docker-compose run --rm node npm run watch

您可以在docker-compose.yml文件中找到所有启动详细信息。


make活着!


第二次之后,每次键入相同的命令都会变得很无聊,并且由于程序员天生就是懒惰的生物,因此让我们进入其“自动化”状态。 保留一组sh脚本是一种选择,但不如单个Makefile那么吸引人,尤其是因为它在现代开发中的适用性被大大低估了。


您可以在此链接上找到完整的俄语手册。

让我们看一下make run在存储库根目录中的外观:


 [user@host ~/projects/app] $ make help Show this help app-pull Application - pull latest Docker image (from remote registry) app Application - build Docker image locally app-push Application - tag and push Docker image into remote registry sources-pull Sources - pull latest Docker image (from remote registry) sources Sources - build Docker image locally sources-push Sources - tag and push Docker image into remote registry nginx-pull Nginx - pull latest Docker image (from remote registry) nginx Nginx - build Docker image locally nginx-push Nginx - tag and push Docker image into remote registry pull Pull all Docker images (from remote registry) build Build all Docker images push Tag and push all Docker images into remote registry login Log in to a remote Docker registry clean Remove images from local registry --------------- --------------- up Start all containers (in background) for development down Stop all started for development containers restart Restart all started for development containers shell Start shell into application container install Install application dependencies into application container watch Start watching assets for changes (node) init Make full application initialization (install, seed, build assets) test Execute application tests Allowed for overriding next properties: PULL_TAG - Tag for pulling images before building own ('latest' by default) PUBLISH_TAGS - Tags list for building and pushing into remote registry (delimiter - single space, 'latest' by default) Usage example: make PULL_TAG='v1.2.3' PUBLISH_TAGS='latest v1.2.3 test-tag' app-push 

他非常擅长上瘾的目标。 例如,要运行watchdocker-compose run --rm node npm run watch ),您需要“提升”应用程序-您只需要将up目标指定为从属对象即可-不必担心在调用watch之前忘记这样做- make自己为您做所有的事情。 这同样适用于运行测试和静态分析器,例如,在提交更改之前-运行make test ,所有的魔力都会为您带来!


不用说,您不必担心要组装图像,下载图像,指定--cache-from以及几乎所有内容吗?


您可以在此链接中查看Makefile的内容。


汽车零件


让我们继续本文的最后一部分-这是Docker Registry中更新映像过程的自动化。 尽管在我的示例中使用了GitLab CI-将想法转移到另一个集成服务,但我认为这很有可能。


首先,我们将确定所使用图像标签的命名:


标签名称目的地
latestmaster分支收集的图像。
代码状态是最新的,但尚未准备好发布
some-branch-name在早午餐中以some-branch-name收集的图像。
因此,即使在将它们与master -light合并之前,我们也可以“推出”仅在特定早午餐框架内实现的任何环境中的更改,这足以使用此标签“拉伸”图像。
而且-是的,这些更改通常可能涉及所有服务的代码和映像!
vX.XX实际上,是应用程序的发布(用于部署特定版本)
stable别名,用于具有最新版本的标签(用于部署最新的稳定版本)

通过以vX.XX格式vX.XX发布标签来进行发布。


为了加快构建速度,使用了./src/vendor./src/node_modules + --cache-from目录为./src/vendor build进行缓存,该过程包括以下阶段:


阶段名称目的地
prepare准备阶段-组装所有服务图像(带有源图像除外)
test使用在准备阶段收集的图像测试应用程序(运行phpunit ,静态代码分析器)
build安装所有composer依赖项( --no-dev ), webpack组装assets ,并webpack 包含收到的工件vendor/*app.jsapp.css )的源代码webpack映像。

管道截图


master分支上的程序集产生带有latestmaster标签的push

平均而言,组装的所有阶段需要4分钟 ,这是一个很好的结果(并行执行任务就是我们的一切)。


您可以通过此链接熟悉收集器的配置内容( .gitlab-ci.yml )。


而不是结论


如您所见,使用Docker组织php应用程序(以Laravel为例)的工作并不困难。 作为测试,您可以派生存储库 ,并用自己的替换所有出现的tarampampam/laravel-in-docker自己尝试“实时”进行的所有操作。


对于本地启动-仅运行2个命令:


 $ git clone https://gitlab.com/tarampampam/laravel-in-docker.git ./laravel-in-docker && cd $_ $ make init 

然后在您喜欢的浏览器中打开http://127.0.0.1:9999


...抓住机会


目前,我正在从事TL项目“ autocode”的工作,我们正在寻找有才华的php开发人员和系统管理员(开发办公室位于叶卡捷琳堡)。 如果您认为自己是第一或第二位,请在电子邮件hr@avtocod.ru写上“我想成为开发团队,简历:%link_on_summary%”的人力资源信函,我们hr@avtocod.ru您提供重新安置的帮助。

Source: https://habr.com/ru/post/zh-CN425101/


All Articles