Slide 1

Slide 1 text

Running Symfony in a Multi-Process Container 1

Slide 2

Slide 2 text

Anne-Julia Seitz • Software Developer • from Berlin • ~16 years of PHP • Symfony since v1.2 • Organizer of the Symfony User Group in Berlin • with QOSSMIC since 2021 • QOSSMIC is now OPEN Software Consulting 2

Slide 3

Slide 3 text

• Workshops & Trainings for Symfony & React • User Groups & Community Events • Supporting Teams in Symfony Projects 3

Slide 4

Slide 4 text

agenda 1. the challenge 2. s6-overlay 3. recipes for operating 4

Slide 5

Slide 5 text

context 5

Slide 6

Slide 6 text

dockerized dev setup # docker-compose.yaml services: app: image: php:8.4.1-fpm-alpine 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 6

Slide 7

Slide 7 text

1) PHP has no built-in production grade web server 7

Slide 8

Slide 8 text

Client NGINX PHP-FPM Client NGINX PHP-FPM 8

Slide 9

Slide 9 text

2) one process per container 9

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

11

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

command signal docker pause SIGSTOP docker stop SIGTERM, SIGKILL docker kill SIGKILL 13

Slide 14

Slide 14 text

special responsibilities of PID 1 14

Slide 15

Slide 15 text

15

Slide 16

Slide 16 text

challenge: deploy to the cloud 16

Slide 17

Slide 17 text

possible solutions • kubernetes • docker compose • FrankenPHP • apache with mod_php 17

Slide 18

Slide 18 text

all in one container all in one container all in one container all in one container all in one container 18

Slide 19

Slide 19 text

supervisord 19

Slide 20

Slide 20 text

s6-overlay? 20

Slide 21

Slide 21 text

s6-overlay S6-overlay is a container-focused process manager that offers end-to-end management of the container's lifecycle, from initialization to graceful shutdown. https://github.com/just-containers/s6-overlay 21

Slide 22

Slide 22 text

one thing in a container • Containers should do one thing • Containers should stop when that thing stops 22

Slide 23

Slide 23 text

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... 23

Slide 24

Slide 24 text

let me show you ... 24

Slide 25

Slide 25 text

install s6-overlay FROM busybox ARG RELEASE_PATH="https://github.com/just-containers/s6-overlay/releases/download/v3.2.0.2" 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"] 25

Slide 26

Slide 26 text

usage • ENTRYPOINT ["/init"] • CMD ["php", "your-script.php"] 26

Slide 27

Slide 27 text

3 service types • oneshot: run once and exit • longrun: supervised by s6 • bundle: groups of related services 27

Slide 28

Slide 28 text

configuration • directories • plain files 28

Slide 29

Slide 29 text

initial directory structure /etc/s6-overlay/s6-rc.d └── user └── contents.d 1 2 3 29

Slide 30

Slide 30 text

oneshot type programs that will run once and exit, initialization tasks service definition • type: plain text file with content "oneshot" • up: plain text file 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 30

Slide 31

Slide 31 text

longrun type daemon that will get supervised by s6 service definition • type: plain text file with content "longrun" • run: executable file • 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 31

Slide 32

Slide 32 text

bundle type organize and manage groups of related services service definition • type: plain text file 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 32

Slide 33

Slide 33 text

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 33

Slide 34

Slide 34 text

create service root directory mkdir -p /etc/s6-overlay/s6-rc.d 34

Slide 35

Slide 35 text

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/ 35

Slide 36

Slide 36 text

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 /usr/local/sbin/php-fpm --nodaemonize 36

Slide 37

Slide 37 text

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 37

Slide 38

Slide 38 text

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 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 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 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 5 29 root 0:00 s6-supervise s6rc-fdholder 6 7 8 32 root 0:00 s6-supervise s6rc-oneshot-runner 9 79 root 0:00 php-fpm: master process (/usr/local/etc/php-fpm.conf) 10 91 root 0:00 nginx: master process nginx 11 109 www-data 0:00 nginx: worker process 12 125 www-data 0:00 php-fpm: pool www 13 ... 14 /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 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 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 38

Slide 39

Slide 39 text

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 39

Slide 40

Slide 40 text

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 40

Slide 41

Slide 41 text

container environment #!/command/execlineb -P with-contenv /usr/local/sbin/php-fpm --nodaemonize 1 2 3 with-contenv #!/command/execlineb -P 1 2 /usr/local/sbin/php-fpm --nodaemonize 3 41

Slide 42

Slide 42 text

customizing s6-overlay behaviour • S6_BEHAVIOUR_IF_STAGE2_FAILS • S6_CMD_RECEIVE_SIGNALS • S6_CMD_WAIT_FOR_SERVICES_MAXTIME • S6_KEEP_ENV • S6_KILL_GRACETIME • S6_LOGGING • S6_STAGE2_HOOK • S6_VERBOSITY • ... 42

Slide 43

Slide 43 text

supervisord vs. s6-overlay 43

Slide 44

Slide 44 text

recap 44

Slide 45

Slide 45 text

recipes for operating 45

Slide 46

Slide 46 text

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 46

Slide 47

Slide 47 text

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 47

Slide 48

Slide 48 text

Feature flags with S6_STAGE2_HOOK # set path to feature-toggle script ENV S6_STAGE2_HOOK=/etc/s6-overlay/s6-hook/feature-toggle 1 2 #!/command/with-contenv sh INIT_MIGRATIONS="${FEATURE_INIT_MIGRATIONS:-false}" SVC_MESSENGER_SCHEDULER="${FEATURE_RUN_QUEUE_SCHEDULER:-false}" SVC_NGINX="${FEATURE_RUN_NGINX:-true}" for feature in "INIT_MIGRATIONS SVC_MESSENGER_SCHEDULER SVC_NGINX"; do is_enabled=$(eval echo \${$feature:-false}) feature_file=$(echo "$feature" | tr '[:upper:]' '[:lower:]' | tr '_' '-') if [ $is_enabled = false ]; then echo "feature-toggle: info: $feature is disabled. Deleting service: $feature_file" rm -f "/etc/s6-overlay/s6-rc.d/user/contents.d/$feature_file" fi done exit 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 48

Slide 49

Slide 49 text

bonus 49

Slide 50

Slide 50 text

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 50

Slide 51

Slide 51 text

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 51

Slide 52

Slide 52 text

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 52

Slide 53

Slide 53 text

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 53

Slide 54

Slide 54 text

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 54

Slide 55

Slide 55 text

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 55

Slide 56

Slide 56 text

Cookbook Repo https://github.com/dazz/s6-nginx-php-fpm 56

Slide 57

Slide 57 text

merci! 57

Slide 58

Slide 58 text

questions? 58

Slide 59

Slide 59 text

fin. 59

Slide 60

Slide 60 text

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 60