2022 年从零开始的 Docker 上的 PHP

在我关于在 Docker 上开发 PHP 的教程系列的这一部分中,我们将重温之前的教程,并在 2022 年更新一些内容。

https://youtube.com/watch?v=NuSWKx9FSso

所有代码示例都在我 在 Github 上的 Docker PHP 教程存储库中公开提供。您可以在part-4-1-docker-from-scratch-for-php-applications-in-2022
找到本教程的分支 

Docker PHP 教程的所有已发布部分都收集在Docker PHP 教程的专用页面下 。上一部分是 为 PHP 项目构建 Docker 设置, 下 一部分是2022 年 PHP 8.1 上的 PhpStorm、Docker 和 Xdebug 3。

如果您想继续关注,请订阅RSS 提要 或通过电子邮件在下一部分发布时 获得自动通知:)

目录

  • 介绍
  • 本地Docker设置
  • Docker
    • Docker撰写
      • .docker/.env文件和所需的 ENV 变量
    • 图片
      • PHP 图像
        • ENV 与 ARG
      • 图像命名约定
      • 环境和构建目标
  • 生成文件
    • .make/*.mk包括
    • 共享变量:.make/.env
      • 手动修改
    • 强制执行所需参数
  • Make + Docker = <3
    • 确保构建顺序
    • 在 docker 容器中运行命令
      • 解决权限问题
  • PHP POC
  • 包起来

介绍

如果您已阅读之前的教程Structuring the Docker setup for PHP Projects ,您可能会遇到一些重大变化。该教程发布于 2 年多前,Docker 已经发展,我对它有了更多的了解。另外,我通过以前的设置收集了实践经验(好的和坏的)。我现在将 基础知识下关于构建容器的大部分要点 视为“不需要”或只是“过度设计/过于复杂”。具体来说:

  • 设置时区
    • 如果默认值已经是 UTC,则不需要(几乎总是如此)
  • 同步共享卷上的文件和文件夹所有权
    • 如果文件需要由容器和主机系统修改,这只是一个问题- 这仅与 PHP 容器真正相关
    • 此外,我建议添加一个全新的用户(例如application)而不是重新使用现有的用户 – 这大大简化www-data整个用户设置
    • 从现在开始,我们将使用application用户名 ( APP_USER_NAME) 和10000用户 ID ( APP_USER_ID;遵循最佳实践, 不使用低于 10,000 的 UID )
  • 修改配置文件
    • 只需使用sed– 无需专用脚本
  • 安装php扩展
  • 安装常用软件
    • 查看PHP 图像– 因为只有一个基础图像,所以不需要专门的脚本
  • 打扫干净
    • 没有真正的意义,因为“清理文件”已经是前一层的一部分
    • 当我们优化图像大小以加快将图像推入/拉出注册表时,我们可能会“把它带回来”
  • 为 linux 主机系统提供 host.docker.internal
    • 现在可以通过host-gateway魔术参考来完成 yaml services: myservice: extra_hosts: - host.docker.internal:host-gateway
    • 因此,不再需要自定义入口点

本地Docker设置

这部分的目标是介绍一个无需开发工具的本地工作设置。换句话说:我们希望最低限度的东西在本地运行。

主要成分是:

  • 和目录中的make设置Makefile.make/
  • .docker/目录中的 docker 设置
  • 一些 PHP 文件充当 docker 设置的端到端功能的 POC

通过查看代码

git checkout part_4_section_1_docker_from_scratch_for_php_applications_in_2022_code_structure

通过初始化它

make make-init
make docker-build

并通过运行它

make docker-up

现在您可以通过http://127.0.0.1访问 Web 界面。下图显示了容器是如何连接的

有关设置的完整测试,另请参阅PHP POC

Docker

docker设置包括

  • 一个 nginx 容器作为网络服务器
  • MySQL 数据库容器
  • 充当队列的 Redis 容器
  • 一个 php 基础镜像,由
    • 一个 php 工作容器,通过它产生多个 PHP 工作进程supervisor
    • 一个 php-fpm 容器作为 nginx 容器的后端
    • 我们用来运行命令的应用程序容器
Docker 镜像

我们保留.docker/上一个教程中的目录docker-compose/, 尽管它会被拆分成images/如下所示:

.
└── .docker/
    ├── docker-compose/
    |   ├── docker-compose.yml
    |   └── <other docker-compose files>
    ├── images/
    |   ├── nginx/
    |   |   ├── Dockerfile
    |   |   └── <other files for the nginx image>
    |   └── <other folders for docker images>
    ├── .env
    └── .env.example

Docker撰写

所有图像都是通过构建docker-compose的,因为docker-compose.yml文件为构建配置提供了一个很好的抽象层。此外,我们还可以使用它来编排容器,即控制卷、端口映射、网络等 – 以及通过docker-compose up和启动和停止它们docker-compose down

仅供参考:尽管这两种方法使用起来都很方便,但我发现它也使设置变得比在生产后期运行时(当我们不再使用时)docker-compose所需的更复杂 。我认为这里的问题是,一些修改只需要构建,而其他修改只需要运行 – 并且将两者结合在同一个文件中会产生一定量的噪音。但是它就是这样啊。docker-compose

我们使用三个单独的docker-compose.yml文件:

  • Docker-compose.yml
    • 包含适用于所有环境的所有信息
  • docker-compose.local.yml
  • docker-compose-php-base.yml
    • 包含构建 php 基础映像的信息,请参阅PHP 映像

.docker/.env文件和所需的 ENV 变量

在我们的 docker 设置中,我们基本上有 3 种不同类型的变量:

  1. 取决于单个开发人员的本地设置的 变量,例如NGINX_HOST_HTTP_PORT主机上的变量(因为默认的可能已经在使用)
  2. 在多个图像中使用的变量,例如容器文件系统中代码库的位置
  3. 包含“可能改变”的信息的变量,例如基础图像的确切版本

因为 – 再次 – 我们努力保留单一的事实来源,我们将信息提取为变量并将它们放入.docker/.env文件中。在一个完美的世界里,我想将这些不同的类型分开在不同的文件中——但docker-compose只允许一个.env文件,参见例如这个评论。如果文件不存在,则从.docker/.env.example.

然后在docker-compose.yml文件中使用变量。?我发现总是在变量上使用修饰符是“最不痛苦的”, 这样docker-compose 如果变量丢失就会立即失败

注意:一些变量在调用时需要通过环境变量传递docker-compose (即它们是必需的,但未在.env文件中定义;另请参阅 共享变量:.make/.env

图片

对于MySQLredis,我们不使用自定义构建的镜像,而是直接使用官方 镜像,并在启动容器时通过环境变量进行配置。在生产中,我们不会对这些服务使用 docker,而是依赖于托管版本,例如

其余容器在目录中各自的子目录中定义.docker/images/ ,例如nginx容器的映像是通过Dockerfile位于 .docker/images/nginx/Dockerfile.

PHP 图像

我们需要 3 个不同的 PHP 映像(fpm、workers、应用程序)并使用与Structuring the Docker setup for PHP Projects中略有不同的方法:

我们不使用官方的 PHP 基础镜像(即 cli 或 fpm),而是使用“普通”的 alpine 基础镜像并在其中手动安装 PHP 和所需的扩展。这允许我们为所有 PHP 镜像构建一个通用的基础镜像。好处:

  • 共享工具和配置的中心位置(不再需要.shared/目录)
  • 推送单个图像时减小图像大小(基础图像被识别为一个层,因此“已经存在”)
  • 安装扩展 viaapk add比via 快很多docker-php-ext-install

这种新方法有两个主要缺点:

  • 我们依赖于 PHP(和 PHP 扩展)的高山发布周期
  • 镜像构建过程比较复杂,因为我们必须先构建基础镜像,然后才能构建最终镜像

幸运的是,这两个问题都可以很容易地解决:

  • codecasts/php-alpine维护一个apk存储库,其中包含 alpine 的最新 PHP 版本
  • 我们使用专用make目标来构建图像而不是docker-compose 直接调用 – 这使我们能够定义“构建顺序”(首先是基础,然后是休息),同时仍然必须作为开发人员只运行一个命令(请参阅确保构建顺序
ENV 与 ARG

我注意到在多个 PHP 容器中需要一些构建参数,例如在APP_USER_NAMEENV 变量中定义的应用程序用户的名称。用户名是必需的

  • 在基础镜像中创建用户
  • 在 fpm 映像中定义运行 fpm 进程的用户(请参阅 参考资料php-fpm.d/www.conf
  • 在工作映像中定义运行工作进程的用户(请参阅supervisor/supervisord.conf

而不是通过构建参数将名称传递给所有图像,即

  • services.*.build.argsdocker-compose.yml文件中明确定义它
  • 通过在 Dockerfile 中“检索”它ARG APP_USER_NAME

我选择通过以下方式将用户名作为ENV基础映像中的变量提供

ARG APP_USER_NAME
ENV APP_USER_NAME=${APP_USER_NAME}

因此能够直接在子图像中访问它,我现在可以写

RUN echo ${APP_USER_NAME}

代替

ARG APP_USER_NAME
RUN echo ${APP_USER_NAME}

我不能 100% 确定我喜欢这种方法,因为我或多或少地以可能不是有意的方式“滥用”ENV 变量(“为什么需要将用户名存储为 ENV 变量?”) -但我也没有看到任何其他实际的缺点。

图像命名约定

为图像定义一个完全限定的名称将使以后引用图像变得更加容易,例如在将它们推送到注册表时。

图像的命名约定是 $(DOCKER_REGISTRY)/$(DOCKER_NAMESPACE)/$(DOCKER_SERVICE_NAME)-$(ENV),例如

                   docker.io/dofroscra/nginx-local
$(DOCKER_REGISTRY)---^          ^        ^     ^        docker.io
$(DOCKER_NAMESPACE)-------------^        ^     ^        dofroscra
$(DOCKER_SERVICE_NAME)-------------------^     ^        nginx
$(ENV)-----------------------------------------^        local

它被用作 的值services.*.image,例如nginx

services:
  nginx:
    image: ${DOCKER_REGISTRY?}/${DOCKER_NAMESPACE?}/nginx-${ENV?}:${TAG?}

如果您想知道:dofroscra源于Do cker Fro m Scratch

环境和构建目标

我们的最终目标是我们可以使用的设置

  • 地方发展
  • 在 CI/CD 管道中
  • 在生产中

即使我们努力在这些不同的环境之间实现平等,也会因为根本不同的要求而存在差异。例如

  • 生产中,我想要一个包含源代码的容器,没有任何测试依赖项
  • CI上,我想要一个包含带有测试依赖项的源代码的容器
  • 本地我想要一个从我的主机安装源代码的容器(包括依赖项)

这通过ENV环境变量反映出来。我们在两个地方使用它:

  1. 作为图像名称的一部分作为服务名称的后缀(请参阅图像命名约定
  2. 指定目标构建阶段

参见docker-compose-php-base.yml文件,例如:

services:
  php-base:
    image: ${DOCKER_REGISTRY?}/${DOCKER_NAMESPACE?}/php-base-${ENV?}:${TAG?}
    build:
      dockerfile: images/php/base/Dockerfile
      target: ${ENV?}

在同一个 Dockerfile 中使用多个目标使我们能够保持一个共同的基础,但也包括特定于环境的指令php-base例如查看镜像的 Dockerfile

ARG ALPINE_VERSION
FROM composer:${COMPOSER_VERSION} as composer
FROM alpine:${ALPINE_VERSION} as base

RUN apk add --update --no-cache \
        bash

WORKDIR $APP_CODE_PATH

FROM base as local

RUN apk add --no-cache --update \
        mysql-client \
  • 它首先定义了一个base包含所有环境所需软件的阶段
  • 然后定义一个local阶段,额外添加一个mysql-client帮助我们调试连接问题的阶段

构建local完成后,我们最终得到一个名为的映像,该映像php-base-local使用local构建阶段作为目标构建阶段。

生成文件

在下一节中,我将介绍一些命令,例如用于构建和运行容器。老实说,我发现在不必查找确切的选项和论点的情况下牢记它们有点挑战性。在这种情况下,我通常会在我的本地文件中创建一个辅助函数或别名.bashrc– 但那时团队的其他成员将无法使用它,而且它对于这个项目来说非常具体。

相反,我们将使用一个自记录的 Makefile作为应用程序的中心入口点 。由于 Makefile 往往会随着时间的推移而增长,因此我采用了一些策略来通过包含、共享变量和更好的错误处理来保持它们“健全”。

.make/*.mk包括

随着时间的推移,make设置将大幅增长,因此我们将其拆分为目录.mk中的多个文件.make/。当我们将它们包含在主路径中时,各个文件都带有一个数字前缀,以确保它们的Makefile顺序

include .make/*.mk
.
└── .make/
    ├── 01-00-application-setup.mk
    ├── 01-01-application-commands.mk
    └── 02-00-docker.mk

共享变量:.make/.env

我们尝试在这里提供共享变量,因为我们可以将它们作为前缀传递给单个命令,例如

.PHONY: some-target
some-target: ## Run some target
    ENV_FOO=BAR some_command --baz

这将使ENV_FOO作为环境变量可用于some_command.

共享变量被不同的组件使用,我们总是试图只维护一个单一的事实来源。一个例子是DOCKER_REGISTRY我们需要 在文件中定义docker 镜像的镜像名称docker-compose.yml的变量,以及稍后通过 make 目标推送/拉取/部署镜像时的变量。在这种情况下,变量是make和 所必需的docker-compose

为了清楚地区分变量和“代码”,我们使用.env位于. make/.env. 它可以通过初始化

make make-init

通过复制.make/.env.example.make/.env.

.
└── .make/
    ├── .make/.env.example
    └── .make/.env

该文件包含在主Makefile通过

-include .make/.env

-前缀确保如果文件不存在(尚),make 不会失败,请参阅 GNU make:包括其他 Makefiles

手动修改

如果需要,您可以随时手动修改.make/.env文件。当您docker在 Linux 上运行并且需要将user id主机系统的 与 user iddocker 容器的匹配时,可能会出现这种情况。您的本地用户和组通常具有 id 1000。在这种情况下,您将手动将条目添加到.make/.env文件中。

APP_USER_ID=1000
APP_GROUP_ID=1000

另请参阅解决权限问题部分。

强制执行所需参数

我们有点“滥用”执行任意命令(而不是构建工件),其中一些命令需要可以 作为命令参数以形式传递的参数

make some-target FOO=bar

没有办法像在方法签名中那样“定义”这些参数 – 但我们仍然可以确保在缺少参数时尽早失败

@$(if $(FOO),,$(error FOO is empty or undefined))

另请参阅SO:如果未设置变量,如何中止 makefile?

例如,我们使用这种技术来确保在通过validate-docker-variables前置条件目标执行 docker 目标时定义所有必需的变量:

.PHONY: validate-docker-variables
validate-docker-variables: 
    @$(if $(TAG),,$(error TAG is undefined))
    @$(if $(ENV),,$(error ENV is undefined))
    @$(if $(DOCKER_REGISTRY),,$(error DOCKER_REGISTRY is undefined - Did you run make-init?))
    @$(if $(DOCKER_NAMESPACE),,$(error DOCKER_NAMESPACE is undefined - Did you run make-init?))
    @$(if $(APP_USER_NAME),,$(error APP_USER_NAME is undefined - Did you run make-init?))
    @$(if $(APP_GROUP_NAME),,$(error APP_GROUP_NAME is undefined - Did you run make-init?))

.PHONY:docker-build-image
docker-build-image: validate-docker-variables
    $(DOCKER_COMPOSE) build $(DOCKER_SERVICE_NAME)

Make + Docker = <3

我们已经在我们的设置中引入了相当多的复杂性:

  • “全局”变量(在make和之间共享docker
  • 多个docker-compose.yml文件
  • 构建依赖项

“手动”将它们组合在一起是一项相当大的努力,而且容易出错。但是我们可以.make/02-00-docker.mk通过定义两个变量 DOCKER_COMPOSEDOCKER_COMPOSE_PHP_BASE

DOCKER_DIR:=./.docker
DOCKER_ENV_FILE:=$(DOCKER_DIR)/.env
DOCKER_COMPOSE_DIR:=$(DOCKER_DIR)/docker-compose
DOCKER_COMPOSE_FILE:=$(DOCKER_COMPOSE_DIR)/docker-compose.yml
DOCKER_COMPOSE_FILE_LOCAL:=$(DOCKER_COMPOSE_DIR)/docker-compose.local.yml
DOCKER_COMPOSE_FILE_PHP_BASE:=$(DOCKER_COMPOSE_DIR)/docker-compose-php-base.yml
DOCKER_COMPOSE_PROJECT_NAME:=dofroscra_$(ENV)

DOCKER_COMPOSE_COMMAND:=ENV=$(ENV) \
 TAG=$(TAG) \
 DOCKER_REGISTRY=$(DOCKER_REGISTRY) \
 DOCKER_NAMESPACE=$(DOCKER_NAMESPACE) \
 APP_USER_NAME=$(APP_USER_NAME) \
 APP_GROUP_NAME=$(APP_GROUP_NAME) \
 docker-compose -p $(DOCKER_COMPOSE_PROJECT_NAME) --env-file $(DOCKER_ENV_FILE)

DOCKER_COMPOSE:=$(DOCKER_COMPOSE_COMMAND) -f $(DOCKER_COMPOSE_FILE) -f $(DOCKER_COMPOSE_FILE_LOCAL)
DOCKER_COMPOSE_PHP_BASE:=$(DOCKER_COMPOSE_COMMAND) -f $(DOCKER_COMPOSE_FILE_PHP_BASE)
  • DOCKER_COMPOSE使用docker-compose.yml并扩展它docker-compose.local.yml
  • DOCKER_COMPOSE_PHP_BASE仅使用docker-compose-php-base.yml

这些变量可以稍后在制作配方中使用。

确保构建顺序

正如在PHP 图像中提到的,我们需要按照一定的顺序构建图像并使用以下 make 目标:

.PHONY: docker-build-image
docker-build-image: ## Build all docker images OR a specific image by providing the service name via: make docker-build DOCKER_SERVICE_NAME=<service>
    $(DOCKER_COMPOSE) build $(DOCKER_SERVICE_NAME)

.PHONY: docker-build-php
docker-build-php: ## Build the php base image
    $(DOCKER_COMPOSE_PHP_BASE) build $(DOCKER_SERVICE_NAME_PHP_BASE)

.PHONY: docker-build
docker-build: docker-build-php docker-build-image ## Build the php image and then all other docker images

作为开发人员,我现在可以简单地运行make docker-build——它将首先通过构建php-base 图像docker-build-php,然后通过构建所有剩余的图像docker-build-image (通过不指定DOCKER_SERVICE_NAME变量,docker-compose将构建文件中列出的所有docker-compose.yml服务)。

我认为make 配方本身是非常可读且易于理解的,但是当我们使用 仅“打印将执行的配方,但不执行它”-n选项运行它们时,我们会感觉到复杂性:

$ make docker-build -n
ENV=local TAG=latest DOCKER_REGISTRY=docker.io DOCKER_NAMESPACE=dofroscra APP_USER_NAME=application APP_GROUP_NAME=application docker-compose -p dofroscra_local --env-file ./.docker/.env -f ./.docker/docker-compose/docker-compose-php-base.yml build php-base
ENV=local TAG=latest DOCKER_REGISTRY=docker.io DOCKER_NAMESPACE=dofroscra APP_USER_NAME=application APP_GROUP_NAME=application docker-compose -p dofroscra_local --env-file ./.docker/.env -f ./.docker/docker-compose/docker-compose.yml -f ./.docker/docker-compose/docker-compose.local.yml build

在 docker 容器中运行命令

工具是开发工作流程中的重要组成部分。这包括诸如 linter、静态分析器和测试工具之类的东西,还包括针对您特定工作流程的“自定义”工具。这些工具通常需要 PHP 运行时。目前,我们在文件中只定义了一个“工具” setup.php。它确保jobs创建了一个名为的表。

要运行这个工具,我们必须首先启动 docker setup via make docker-up,然后在application容器中执行脚本。相应的目标定义在 .make/01-00-application-setup.mk

.PHONY: setup-db
setup-db: ## Setup the DB tables
    $(EXECUTE_IN_APPLICATION_CONTAINER) php setup.php $(ARGS);

这基本上转化为

docker-compose exec -T --user application application php setup.php

如果我们在一个容器之外并且

php setup.php

如果我们在一个容器内。这非常方便,因为我们可以直接从主机系统运行工具,而无需登录容器。

“魔术”发生在定义为的EXECUTE_IN_APPLICATION_CONTAINER变量中 .make/02-00-docker.mk

EXECUTE_IN_WORKER_CONTAINER?=
EXECUTE_IN_APPLICATION_CONTAINER?=

EXECUTE_IN_CONTAINER?=
ifndef EXECUTE_IN_CONTAINER
    # check if 'make' is executed in a docker container, 
    # see https://stackoverflow.com/a/25518538/413531
    # `wildcard $file` checks if $file exists, 
    # see https://www.gnu.org/software/make/manual/html_node/Wildcard-Function.html
    # i.e. if the result is "empty" then $file does NOT exist 
    # => we are NOT in a container
    ifeq ("$(wildcard /.dockerenv)","")
        EXECUTE_IN_CONTAINER=true
    endif
endif
ifeq ($(EXECUTE_IN_CONTAINER),true)
    EXECUTE_IN_APPLICATION_CONTAINER:=$(DOCKER_COMPOSE) exec -T --user $(APP_USER_NAME) $(DOCKER_SERVICE_NAME_APPLICATION)
    EXECUTE_IN_WORKER_CONTAINER:=$(DOCKER_COMPOSE) exec -T --user $(APP_USER_NAME) $(DOCKER_SERVICE_NAME_PHP_WORKER)
endif

我们可以通过-n再次查看主机系统上已解决的配方

pascal.landau:/c/_codebase/dofroscra# make setup-db ARGS=--drop -n
ENV=local TAG=latest DOCKER_REGISTRY=docker.io DOCKER_NAMESPACE=dofroscra APP_USER_NAME=application APP_GROUP_NAME=application docker-compose -p dofroscra_local --env-file ./.docker/.env -f ./.docker/docker-compose/docker-compose.yml -f ./.docker/docker-compose/docker-compose.local.yml exec -T --user application application php setup.php --drop

在一个容器中,它看起来像这样:

root:/var/www/app# make setup-db ARGS=--drop -n
php setup.php --drop;

解决权限问题

如果您使用的是 Linux,当用户 id 与 上一教程的在共享卷上同步文件和文件夹所有权部分中的说明 不同时,您可能会在修改主机系统和 docker 容器之间共享的文件时遇到权限问题.

在这种情况下,您需要根据本地设置手动修改.make/.env并添加 APP_USER_ID和变量。APP_GROUP_ID必须 在构建映像之前完成,以确保在映像user id中使用正确的。

在极少数情况下,它可能会导致问题,因为您的本地 id已经存在于 docker 容器中我个人从来没有遇到过这个问题,但你可以在Docker 和主机文件系统所有者匹配问题中更详细地了解它 。作者甚至 通过 Github 项目“FooBarWidget/matchhostfsowner”提出了一个通用的解决方案。

PHP POC

为确保一切按预期工作,存储库包含一个最小的 PHP 概念证明。默认情况下,主机的 80 端口被转发到nginx容器的 80 端口。

仅供参考:我还建议 在主机上的 hosts 文件中添加以下条目

127.0.0.1 app.local

这样我们就可以通过http://app.local而不是http://127.0.0.1访问应用程序。

POC 的文件基本上确保 本地 docker 设置中概述的容器连接按预期工作:

  • dependencies.php
    • 返回配置RedisPDO对象以与队列和数据库对话
  • setup.php
    • =>确保application可以交谈mysql
  • public/index.php
  • worker.php
    • php-worker容器中作为守护进程启动
    • 每秒检查一次 redis 数据库0中的密钥"queue"
    • 如果找到一个值,则将其存储在jobs数据库的表中
      • =>确保php-worker可以redismysql

完整的测试场景定义test.sh如下:

$ bash test.sh


  Building the docker setup


//...


  Starting the docker setup


//...


  Clearing DB


ENV=local TAG=latest DOCKER_REGISTRY=docker.io DOCKER_NAMESPACE=dofroscra APP_USER_NAME=application APP_GROUP_NAME=application docker-compose -p dofroscra_local --env-file ./.docker/.env -f ./.docker/docker-compose/docker-compose.yml -f ./.docker/docker-compose/docker-compose.local.yml exec -T --user application application php setup.php --drop;
Dropping table 'jobs'
Done
Creating table 'jobs'
Done


  Stopping workers


ENV=local TAG=latest DOCKER_REGISTRY=docker.io DOCKER_NAMESPACE=dofroscra APP_USER_NAME=application APP_GROUP_NAME=application docker-compose -p dofroscra_local --env-file ./.docker/.env -f ./.docker/docker-compose/docker-compose.yml -f ./.docker/docker-compose/docker-compose.local.yml exec -T --user application php-worker supervisorctl stop worker:*;
worker:worker_00: stopped
worker:worker_01: stopped
worker:worker_02: stopped
worker:worker_03: stopped


  Ensuring that queue and db are empty


Items in queue
array(0) {
}
Items in db
array(0) {
}


  Dispatching a job 'foo'


Adding item 'foo' to queue


  Asserting the job 'foo' is on the queue


Items in queue
array(1) {
  [0]=>
  string(3) "foo"
}


  Starting the workers


ENV=local TAG=latest DOCKER_REGISTRY=docker.io DOCKER_NAMESPACE=dofroscra APP_USER_NAME=application APP_GROUP_NAME=application docker-compose -p dofroscra_local --env-file ./.docker/.env -f ./.docker/docker-compose/docker-compose.yml -f ./.docker/docker-compose/docker-compose.local.yml exec -T --user application php-worker supervisorctl start worker:*;
worker:worker_00: started
worker:worker_01: started
worker:worker_02: started
worker:worker_03: started


  Asserting the queue is now empty


Items in queue
array(0) {
}


  Asserting the db now contains the job 'foo'


Items in db
array(1) {
  [0]=>
  string(3) "foo"
}

包起来

恭喜,你成功了!如果现在有些事情还不完全清楚,请不要犹豫发表评论。除此之外,您现在应该有一个正在运行的 docker 设置以及通过make.

php-on-docker-from-scratch-in-2022