Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Running PHP-Applications in Multi-Process Conta...

Running PHP-Applications in Multi-Process Containers

The deployment scenario for PHP looks a little different than for other languages like Go or Ruby that have embedded webservers. The classic stack of Nginx + php-fpm doesn’t perfectly fit in the container world. Some solution providers, like DigitalOcean or certain Azure products, only allow for single containers to be deployed. In this talk, we’ll explore ways other people have solved this, like FrankenPHP, supervisord and wrapping all in bash scripts. I’ll talk about the benefits and downsides of these solutions, compare them with the choice we made for our last project, s6-overlay, show off the s6-cli project I developed for this purpose and talk about recipes for running database migrations, cronjobs and a few more.

Anne-Julia Seitz

September 23, 2024
Tweet

More Decks by Anne-Julia Seitz

Other Decks in Technology

Transcript

  1. Anne-Julia Seitz • Software Developer • ~16 years of PHP

    • Symfony since v1.2 • Organizer of the Symfony User Group in Berlin • with QOSSMIC since 2021 2
  2. • Workshops & Training for Symfony & React • User

    Groups & Community Events • Supporting Teams in Symfony Projects 3
  3. dockerized dev setup version: '3.8' services: app: image: php:8.4-rc-fpm volumes:

    - ./app:/var/www/html nginx: image: nginx:latest ports: - "80:80" volumes: - ./app/public:/var/www/html/public depends_on: - app 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 6
  4. container: isolated process tree [root] └── dockerd (docker daemon) ├──

    containerd (container daemon) ├── docker-containerd (container manager) ├── containerd-shim (per container process) │ └── nginx (main process) │ ├── nginx (worker process) │ ├── nginx (worker process) │ └── nginx (worker process) └── containerd-shim (per container process) └── php-fpm (main process) ├── php-fpm (pool www) ├── php-fpm (pool www) └── php-fpm (pool www) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 https://docs.docker.com/engine/containers/run/ 10
  5. 11

  6. signals signal name number default action description SIGSTOP Stop 19

    Stop Stop the process; cannot be caught or ignored. SIGTERM Terminate 15 Terminate Termination signal; can be caught or ignored. SIGKILL Kill 9 Terminate (forceful) Forcefully kill the process; cannot be caught or ignored. 12
  7. deploy single containers • Azure AppService • AWS ECS (Elastic

    Container Service) • Google App Engine • DigitalOcean droplet • ... 16
  8. all in one container all in one container all in

    one container all in one container all in one container 20
  9. what is an init system? 1. System Boot and Initialization

    2. Service and Process Management 3. Runlevel Management (or Target Management) 4. Handling System Shutdown and Reboot 5. Con�guration and Logging 6. Dependency Resolution 7. Event Handling 22
  10. s6-overlay S6-overlay is a container-focused process manager that o�ers end-to-end

    management of the container's lifecycle, from initialization to graceful shutdown. https://github.com/just-containers/s6-overlay 24
  11. one thing in a container • Containers should do one

    thing • Containers should stop when that thing stops 25
  12. key features of s6-overlay • Easy Integration • Proper PID

    1 functionality • Versatile Process Management • Dependency Control • Sequence Management • Environment Variable Templating • Log Management • Graceful Shutdown • Multi-Arch Support: Ubuntu, CentOS, Fedora, Alpine, Busybox... 26
  13. install FROM busybox ARG RELEASE_PATH="https://github.com/just-containers/s6-overlay/releases/download/v3.2.0.0" ADD $RELEASE_PATH/s6-overlay-noarch.tar.xz /tmp RUN tar

    -C / -Jxpf /tmp/s6-overlay-noarch.tar.xz ADD $RELEASE_PATH/s6-overlay-x86_64.tar.xz /tmp RUN tar -C / -Jxpf /tmp/s6-overlay-x86_64.tar.xz ENTRYPOINT ["/init"] 28
  14. 3 service types • oneshot: run once and exit •

    longrun: supervised by s6 • bundle: groups of related services 31
  15. oneshot type programs that will run once and exit, initialization

    tasks service definition • type: plain text �le with content "oneshot" • up: plain text �le containing the path to the script • dependencies.d: directory containing dependencies of a service /etc/s6-overlay/s6-rc.d ├── init-nginx │ ├── dependencies.d │ ├── type # contains "oneshot" │ └── up # contains "/etc/s6-overlay/scripts/init-nginx" └── scripts └── init-nginx 1 2 3 4 5 6 7 /etc/s6-overlay/s6-rc.d 1 ├── init-nginx 2 │ ├── dependencies.d 3 │ ├── type # contains "oneshot" 4 │ └── up # contains "/etc/s6-overlay/scripts/init-nginx" 5 └── scripts 6 └── init-nginx 7 /etc/s6-overlay/s6-rc.d ├── init-nginx 1 2 │ ├── dependencies.d 3 │ ├── type # contains "oneshot" 4 │ └── up # contains "/etc/s6-overlay/scripts/init-nginx" 5 └── scripts 6 └── init-nginx 7 │ └── up # contains "/etc/s6-overlay/scripts/init-nginx" /etc/s6-overlay/s6-rc.d 1 ├── init-nginx 2 │ ├── dependencies.d 3 │ ├── type # contains "oneshot" 4 5 └── scripts 6 └── init-nginx 7 └── scripts /etc/s6-overlay/s6-rc.d 1 ├── init-nginx 2 │ ├── dependencies.d 3 │ ├── type # contains "oneshot" 4 │ └── up # contains "/etc/s6-overlay/scripts/init-nginx" 5 6 └── init-nginx 7 └── init-nginx /etc/s6-overlay/s6-rc.d 1 ├── init-nginx 2 │ ├── dependencies.d 3 │ ├── type # contains "oneshot" 4 │ └── up # contains "/etc/s6-overlay/scripts/init-nginx" 5 └── scripts 6 7 /etc/s6-overlay/s6-rc.d ├── init-nginx │ ├── dependencies.d │ ├── type # contains "oneshot" │ └── up # contains "/etc/s6-overlay/scripts/init-nginx" └── scripts └── init-nginx 1 2 3 4 5 6 7 32
  16. longrun type daemon that will get supervised by s6 service

    definition • type: plain text �le with content "longrun" • run: executable �le • dependencies.d: directory containing dependencies of a service /etc/s6-overlay/s6-rc.d └── svc-nginx ├── dependencies.d ├── run └── type # contains "longrun" 1 2 3 4 5 └── svc-nginx /etc/s6-overlay/s6-rc.d 1 2 ├── dependencies.d 3 ├── run 4 └── type # contains "longrun" 5 /etc/s6-overlay/s6-rc.d └── svc-nginx ├── dependencies.d ├── run └── type # contains "longrun" 1 2 3 4 5 └── type # contains "longrun" /etc/s6-overlay/s6-rc.d 1 └── svc-nginx 2 ├── dependencies.d 3 ├── run 4 5 /etc/s6-overlay/s6-rc.d └── svc-nginx ├── dependencies.d ├── run └── type # contains "longrun" 1 2 3 4 5 33
  17. bundle type organize and manage groups of related services service

    definition • type: plain text �le with content "bundle" • contents.d: services that are part of the bundle /etc/s6-overlay/s6-rc.d └── my-app ├── contents.d │ ├── svc-nginx │ └── svc-other-service └── type # contains "bundle" 1 2 3 4 5 6 34
  18. Step by step nginx & php-fpm service structure 1. service

    root directory 2. initial structure 3. php-fpm service 4. nginx service 5. run it 35
  19. add user bundle /etc/s6-overlay/s6-rc.d └── user └── contents.d └── type

    # contains "bundle" 1 2 3 4 https://skarnet.org/software/s6-rc/ 37
  20. create php-fpm service /etc/s6-overlay/s6-rc.d/svc-php-fpm/run ├── svc-php-fpm │ ├── run │

    └── type │ └── svc-php-fpm /etc/s6-overlay/s6-rc.d 1 2 3 4 └── user 5 ├── contents.d 6 7 └── type 8 #!/command/execlineb -P with-contenv /usr/local/sbin/php-fpm --nodaemonize 38
  21. create nginx service /etc/s6-overlay/s6-rc.d/svc-nginx/run: ├── svc-nginx │ ├── dependencies.d │

    │ └── svc-php-fpm │ ├── run │ └── type └── svc-nginx /etc/s6-overlay/s6-rc.d 1 2 3 4 5 6 ├── svc-php-fpm 7 │ ├── run 8 │ └── type 9 └── user 10 └── contents.d 11 12 /etc/s6-overlay/s6-rc.d ├── svc-nginx │ ├── dependencies.d │ │ └── svc-php-fpm │ ├── run │ └── type ├── svc-php-fpm │ ├── run │ └── type └── user └── contents.d └── svc-nginx 1 2 3 4 5 6 7 8 9 10 11 12 #!/command/execlineb -P nginx -g "daemon off;" 1 2 39
  22. run it! docker run --name s6-demo -d -p 80:80 s6-demo

    /var/www/html # ps PID USER TIME COMMAND 1 root 0:00 /package/admin/s6/command/s6-svscan -d4 -- /run/service 18 root 0:00 s6-supervise s6-linux-init-shutdownd 20 root 0:00 /package/admin/s6-linux-init/command/s6-linux-init-shutdownd -d3 -c /run/s6/bas 29 root 0:00 s6-supervise s6rc-fdholder 30 root 0:00 s6-supervise svc-php-fpm 31 root 0:00 s6-supervise svc-nginx 32 root 0:00 s6-supervise s6rc-oneshot-runner 38 root 0:00 /package/admin/s6/command/s6-ipcserverd -1 -- /package/admin/s6/command/s6-ipcs 79 root 0:00 php-fpm: master process (/usr/local/etc/php-fpm.conf) 91 root 0:00 nginx: master process nginx 109 www-data 0:00 nginx: worker process 125 www-data 0:00 php-fpm: pool www ... 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 1 root 0:00 /package/admin/s6/command/s6-svscan -d4 -- /run/service 30 root 0:00 s6-supervise svc-php-fpm 31 root 0:00 s6-supervise svc-nginx /var/www/html # ps 1 PID USER TIME COMMAND 2 3 18 root 0:00 s6-supervise s6-linux-init-shutdownd 4 20 root 0:00 /package/admin/s6-linux-init/command/s6-linux-init-shutdownd -d3 -c /run/s6/bas 5 29 root 0:00 s6-supervise s6rc-fdholder 6 7 8 32 root 0:00 s6-supervise s6rc-oneshot-runner 9 38 root 0:00 /package/admin/s6/command/s6-ipcserverd -1 -- /package/admin/s6/command/s6-ipcs 10 79 root 0:00 php-fpm: master process (/usr/local/etc/php-fpm.conf) 11 91 root 0:00 nginx: master process nginx 12 109 www-data 0:00 nginx: worker process 13 125 www-data 0:00 php-fpm: pool www 14 ... 15 79 root 0:00 php-fpm: master process (/usr/local/etc/php-fpm.conf) 91 root 0:00 nginx: master process nginx 109 www-data 0:00 nginx: worker process 125 www-data 0:00 php-fpm: pool www /var/www/html # ps 1 PID USER TIME COMMAND 2 1 root 0:00 /package/admin/s6/command/s6-svscan -d4 -- /run/service 3 18 root 0:00 s6-supervise s6-linux-init-shutdownd 4 20 root 0:00 /package/admin/s6-linux-init/command/s6-linux-init-shutdownd -d3 -c /run/s6/bas 5 29 root 0:00 s6-supervise s6rc-fdholder 6 30 root 0:00 s6-supervise svc-php-fpm 7 31 root 0:00 s6-supervise svc-nginx 8 32 root 0:00 s6-supervise s6rc-oneshot-runner 9 38 root 0:00 /package/admin/s6/command/s6-ipcserverd -1 -- /package/admin/s6/command/s6-ipcs 10 11 12 13 14 ... 15 /var/www/html # ps PID USER TIME COMMAND 1 root 0:00 /package/admin/s6/command/s6-svscan -d4 -- /run/service 18 root 0:00 s6-supervise s6-linux-init-shutdownd 20 root 0:00 /package/admin/s6-linux-init/command/s6-linux-init-shutdownd -d3 -c /run/s6/bas 29 root 0:00 s6-supervise s6rc-fdholder 30 root 0:00 s6-supervise svc-php-fpm 31 root 0:00 s6-supervise svc-nginx 32 root 0:00 s6-supervise s6rc-oneshot-runner 38 root 0:00 /package/admin/s6/command/s6-ipcserverd -1 -- /package/admin/s6/command/s6-ipcs 79 root 0:00 php-fpm: master process (/usr/local/etc/php-fpm.conf) 91 root 0:00 nginx: master process nginx 109 www-data 0:00 nginx: worker process 125 www-data 0:00 php-fpm: pool www ... 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 40
  23. graceful shutdown docker stop ├── finish /etc/s6-overlay/s6-rc.d 1 └── svc-nginx

    2 3 ├── run 4 └── type 5 #!/bin/sh # do important thing before termination echo "0" > /run/s6-linux-init-container-results/exitcode 1 2 3 4 5 41
  24. dropping privileges in execline: In sh: #!/command/execlineb -P s6-setuidgid www-data

    myservice 1 2 3 #!/bin/sh exec s6-setuidgid www-data myservice 1 2 42
  25. customizing s6-overlay behaviour • S6_KEEP_ENV • S6_LOGGING • S6_BEHAVIOUR_IF_STAGE2_FAILS •

    S6_KILL_GRACETIME • S6_CMD_WAIT_FOR_SERVICES_MAXTIME • S6_STAGE2_HOOK • S6_VERBOSITY • S6_CMD_RECEIVE_SIGNALS • ... 44
  26. migrations /etc/s6-overlay/s6-rc.d ├── init-migrations │ ├── dependencies.d │ │ └──

    svc-php-fpm │ ├── type │ └── up └── scripts └── init-migrations 1 2 3 4 5 6 7 8 #!/command/with-contenv sh s6-setuidgid www-data php /var/www/html/bin/console doctrine:migrations:migrate --no-interaction php /var/www/html/bin/console doctrine:migrations:status 1 2 3 4 5 48
  27. cronjobs with scheduler /etc/s6-overlay/s6-rc.d/svc-scheduler/run /etc/s6-overlay/s6-rc.d ├── svc-scheduler │ ├── dependencies.d

    │ │ └── svc-php-fpm │ ├── type │ └── up └── scripts └── svc-scheduler 1 2 3 4 5 6 7 8 #!/command/with-contenv sh s6-setuidgid www-data php /var/www/html/bin/console messenger:consume scheduler_default \ --time-limit=300 --limit=10 --env=`printcontenv APP_ENV` --quiet 1 2 3 4 5 49
  28. check requirements in dev /etc/s6-overlay/s6-rc.d ├── init-dependencies │ ├── dependencies.d

    │ │ ├── base │ │ └── svc-php-fpm │ ├── type │ └── up └── scripts └── init-dependencies 1 2 3 4 5 6 7 8 9 #!/command/with-contenv sh if command -v composer >/dev/null 2>&1; then composer -d /var/www/html check-platform-reqs fi exit 0 1 2 3 4 5 50
  29. s6-overlay base image FROM alpine3 COPY --from=hakindazz/s6-overlay-base /s6/root / #

    install your app here ENTRYPOINT ["/init"] 1 2 3 4 5 6 https://github.com/dazz/s6-overlay-base 52
  30. s6-cli docker run -it --rm hakindazz/s6-cli help COMMANDS: create, c

    create a service remove, rm remove a service lint, l lint directories and files mermaid, m document s6 service dependencies in mermaid syntax help, h Shows a list of commands or help for one command https://github.com/dazz/s6-cli 53
  31. create a service with s6-cli docker run -it --rm -v

    ./:/etc/s6-overlay hakindazz/s6-cli create oneshot init-dependencies /etc/s6-overlay/s6-rc.d ├── init-dependencies │ ├── dependencies.d │ │ ├── base │ │ └── svc-php-fpm │ ├── type │ └── up └── scripts └── init-dependencies 1 2 3 4 5 6 7 8 9 54
  32. use s6-cli in your CI docker run -it --rm -v

    .:/etc/s6-overlay hakindazz/s6-cli lint s6-cli: lint found no issues s6-cli: lint found issues with services in /etc/s6-overlay/s6-rc.d - svc-lint-me - type file for "svc-lint-me" does not end with a newline - invalid type in svc-lint-me/type file specified 55
  33. document your setup with mermaid docker run -it --rm -v

    .:/etc/s6-overlay hakindazz/s6-cli mermaid ```mermaid graph TD; user --> init-dependencies user --> init-migrations user --> svc-nginx init-migrations --> svc-php-fpm svc-php-fpm --> init-directories svc-nginx --> init-nginx svc-nginx --> svc-php-fpm 56
  34. document your setup with mermaid docker run -it --rm -v

    .:/etc/s6-overlay hakindazz/s6-cli mermaid user init-dependencies init-migrations svc-nginx svc-php-fpm init-directories init-nginx 57
  35. References • • • • • • https://github.com/just-containers/s6-overlay https://skarnet.org/software/s6/overview.html https://serversideup.net/open-source/docker-php/docs/guide/using-s6-overlay

    https://www.tonysm.com/multiprocess-containers-with-s6-overlay/ https://github.com/dazz/s6-overlay-base https://github.com/dazz/s6-nginx-php-fpm 61