-
-
Notifications
You must be signed in to change notification settings - Fork 800
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Symfony Messenger #539
Comments
I use something similar in my projects. This definitely is worth a documentation section! |
Same here, I'm not sure if I create a second container or maybe frankenphp can handle the consumer in a second thread. |
noticed one thing with this approach, that all the images at startup tries to run database migrations |
I think that's totally fine. |
Yes, the first one to run wins :) but I am thinking for a flag that I'd set on the main container, and on messenger/scheduler containers not setting it then entrypoint could run based on that condition, is that possible? |
Yes, that is of course possible: php-worker:
build:
context: .
target: frankenphp_dev
image: ${IMAGES_PREFIX:-}app-php
restart: unless-stopped
command: php bin/console messenger:consume async -v
environment:
- RUN_MIGRATIONS=false And then react to it inside your entry point script:
But I personally would advise against it. Since migrations cannot be executed twice anyway there is no need for additional logic imho. |
Thanks :) |
Yes, but it's slows down a little bit container startup time :) |
Can I ask why the target is frankenphp_dev and not prod? |
For the prod I run the build in github actions and then fetch from ghcr registry, but if you want to build on your server then I think the answer is yes, it should be overriden in compose.prod.yaml |
Thank you! |
The configuration didn't work for me in the end and I had to do some tweaks. In compose.yml: services:
php:
image: ${IMAGES_PREFIX:-}app-php
restart: unless-stopped
environment:
...
RUN_MIGRATIONS: true
volumes:
- ...
- var_log:/app/var/log
php-worker:
build:
context: .
target: frankenphp_dev
image: ${IMAGES_PREFIX:-}app-php
restart: unless-stopped
command: php bin/console messenger:consume -vvv
environment:
DATABASE_URL: postgresql://${POSTGRES_USER:-app}:${POSTGRES_PASSWORD:-!ChangeMe!}@database:5432/${POSTGRES_DB:-app}?serverVersion=${POSTGRES_VERSION:-15}&charset=${POSTGRES_CHARSET:-utf8}
RUN_MIGRATIONS: false
volumes:
- var_log:/app/var/log
depends_on:
- php
- database in compose.override.yml: services:
php:
....
php-worker:
volumes:
- ./:/app and finally compose.prod.yml: services:
php:
build:
context: .
target: frankenphp_prod
environment:
APP_SECRET: ${APP_SECRET}
volumes:
- var_log:/app/var/log
php-worker-async:
build:
context: .
target: frankenphp_prod
environment:
APP_SECRET: ${APP_SECRET}
volumes:
- var_log:/app/var/log Most important is the volume in compose.override.yml. Without it, worker had no access to code and kept trying to install symfony from scratch. (Which wasn't problem when targeting frankenphp_prod as that contains all the linking in Dockerfile directly). I'm not that versed in docker, so this may not the be optimal solution but it works for me so far. Nonetheless, if there's something to improve, I'm all ears. |
Another improvement. I need to run several consumers in parallel. I guess that would be a good use case for the supervisor but I was unable to get that running, so I circled back to docker compose, despite some of its drawbacks. The config is mostly the same as in the above examples, I just specified transport in the command. Then I've added this to compose.yml: php-worker-slow:
extends:
service: php-worker
command: php bin/console messenger:consume scheduler_slow -vvv --time-limit=60 --limit=10 --memory-limit=128M
php-worker-fast:
extends:
service: php-worker
command: php bin/console messenger:consume scheduler_fast -vvv --time-limit=60 --limit=10 --memory-limit=128M and this to the other two: php-worker-slow:
extends:
service: php-worker
php-worker-fast:
extends:
service: php-worker Running more instances in parallel should be just a matter of duplicating the service under a new name. Sadly, you need to duplicate in all three files |
I took another way and used the
|
Ah, neat. I'll still need to keep it in separate consumers because I don't want long-running tasks blocking the short ones, but at least I won't need to duplicate workers with the same transport. |
Customize the frankenphp image and install supervisor to run Symfony messenger. |
@gremo, using supervisor inside of Docker is a bad practice (see https://stackoverflow.com/a/65570526/5533907, https://docs.docker.com/engine/containers/multi-service_container/). Best way to start Symfony Messenger consumer for this image is just change the |
@7-zete-7 In general, I agree, but here we are talking about a small system utility (like cron). Everything would still be logged by Symfony and, in addition, it's not necessary to "replicate" the environemnt in a new container just to run the command. |
@gremo, starting multiple processes inside of single container have many bad consequences. In case of Symfony Messenger you loose parallel executing (when you can start multiple instances of service). In case of Docker container you have issue of message handler interaption when php container be restarted. Incase of Symfony Scheduler you need to do more configuration to have stable working scheduler. |
@7-zete-7 thanks for your point of view! It would be very nice indeed to create a quick "how to" to make supervisor and/or cron work. |
I'm starting to get a little bit confused about this topic. I'm currently running the consume command in a separate container (php-worker), but would it be possible to run the consume command in the same default php container? Which option is the better choice? Another remark when using two containers: the dockerfile has the caddy health check baked in, so the php-worker container is being reported as unhealthy. |
The idea with docker is that each container has a single responsibility. So if that service needs more resources you just start more containers to scale up. Depending on your needs you could use The approach from @noximo works quite well. If you want to change the healthcheck for the workers you can install php-worker:
scale: 2
extends:
service: php
healthcheck:
test: ["CMD-SHELL", "/usr/bin/pgrep php || exit 1"]
interval: 3s
start_period: 10s
command: php bin/console messenger:consume async --time-limit=60 |
Well said, @n3o77. But seems like this healthcheck have no benefits. The It would be nice if Symfony had a built-in mechanism for checking the health of the consumer. In most cases, it makes sense to simply disable health checking for this service. The presence of the As an alternative to CRON within the application, Symfony has a (relatively new) Scheduler component that allows you to run scheduled and/or regular tasks within the Messenger component. The most important thing when working with this component is to remember to configure it for a production (see https://symfony.com/doc/current/scheduler.html#efficient-management-with-symfony-scheduler). An alternative to CRON within the infrastructure can be either something built-in (independent of applications). As an option for such a solution that does not require changing the application and/or its images (it was very helpful in such cases) — Ofelia - a job scheduler. |
Thank you @7-zete-7 for your usefull tips! I was able to run supervisord as you said, in a separate php worker container, with a few modifications: # compose.yaml
php-worker:
extends:
service: php
container_name: ${COMPOSE_PROJECT_NAME}-php-worker
command: ['bash', '-c', '/usr/bin/supervisord -c /etc/supervisor/supervisord.conf']
restart: unless-stopped
# compose.dev.yaml
php-worker:
extends:
service: php
volumes:
- ./config/docker/supervisor.conf:/etc/supervisor/conf.d/app.conf In the apt-get -y --no-install-recommends install supervisor; \
sed -i '/\[supervisord\]/a user=root' /etc/supervisor/supervisord.conf; \
sed -i '/\[supervisord\]/a nodaemon=true' /etc/supervisor/supervisord.conf; \ supervisorctl status
messenger-consume:messenger-consume_00 RUNNING pid 1126, uptime 0:00:00
messenger-consume:messenger-consume_01 RUNNING pid 1122, uptime 0:00:00 The only thing I'm trying to solve with this configuration is the fact that, even rebuilding the container, the worker seems to "remember" the old configuration. For cron, it's quite the same concept. I'm using Messenger component so I don't need cron, I'll go for the Scheduler component! |
Great example, @gremo! This is really useful information. However, all this can be achieved without using Supervisor. This will allow you to not complicate the configuration and not create a service that is not controlled by Docker. The same configuration can be written as follows: services:
...
consumer:
extends: php
restart: unless-stopped
deploy:
replicas: 2
healthcheck:
disable: true
#command: /usr/bin/true # if "disable: true" isn't works
# if you need to disable the entry point provided by this repository
#entrypoint: ""
# https://symfony.com/doc/current/messenger.html#deploying-to-production
command: bin/console messenger:consume --limit=... --time-limit=... async ... With this configuration, no additional packages need to be installed (except the An example of changing the number of running processes using this service configuration: # change replicas count to 6
docker compose up --detach --scale consumer=6 --no-deps consumer # restart all messenger:consume processes
docker compose restart consumer The Symfony documentation talks about using Process Manager, but it's talking about a non-Docker environment. |
Thanks for your input guys. Turns out I was doing everything the proper way already, just need to change the health check. Seems the best option for now is to just disable it for the consumer. |
@7-zete-7 Thank you again! I'm going to test Supervisor in production. I’m not sure, but putting a Symfony command directly in the Compose files feels a bit odd to me. I guess it's just some programmer paranoia. 😄 For the healthcheck: healthcheck:
test: ['CMD', 'supervisorctl', 'status']
interval: 30s
timeout: 5s
retries: 3 |
Used commands: make rebuild Worker container will be in restart loop until we add message consumer, configure everything and publish message in the next steps. References: - dunglas/symfony-docker#539 - https://stackoverflow.com/questions/39296472/how-to-check-if-an-environment-variable-exists-and-get-its-value# - https://stackoverflow.com/questions/46166304/docker-compose-volumes-without-colon - https://docs.docker.com/compose/how-tos/multiple-compose-files/extends/#extending-services-within-the-same-file-and-from-another-file - https://stackoverflow.com/questions/37254881/is-there-any-way-to-disable-a-service-in-docker-compose-yml - https://stackoverflow.com/questions/31746182/docker-compose-wait-for-container-x-before-starting-y
Feel free to watch this PR and give us your feedback: #681 |
How to run Symfony Messenger with this setup?
I thought about this in compose.yaml:
Does it make sense? What are the options? Would be good to add something about this to README :)
The text was updated successfully, but these errors were encountered: