Slide 1

Slide 1 text

Running PHP-Applications in Multi-Process Containers 1

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

preface 5

Slide 6

Slide 6 text

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

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

challenge: deploy with one container 15

Slide 16

Slide 16 text

deploy single containers • Azure AppService • AWS ECS (Elastic Container Service) • Google App Engine • DigitalOcean droplet • ... 16

Slide 17

Slide 17 text

possible solutions 17

Slide 18

Slide 18 text

apache with mod_php 18

Slide 19

Slide 19 text

FrankenPHP 19

Slide 20

Slide 20 text

all in one container all in one container all in one container all in one container all in one container 20

Slide 21

Slide 21 text

supervisord 21

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

s6-overlay? 23

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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

Slide 26

Slide 26 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... 26

Slide 27

Slide 27 text

let me show you 27

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

configuration • directories • plain �les 30

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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

Slide 35

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

Slide 36

Slide 36 text

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

Slide 37

Slide 37 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/ 37

Slide 38

Slide 38 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 with-contenv /usr/local/sbin/php-fpm --nodaemonize 38

Slide 39

Slide 39 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 39

Slide 40

Slide 40 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 -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

Slide 41

Slide 41 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 41

Slide 42

Slide 42 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 42

Slide 43

Slide 43 text

container environment #!/command/with-contenv sh 1 env 2 43

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

supervisord vs. s6-overlay 45

Slide 46

Slide 46 text

No content

Slide 47

Slide 47 text

recap 46

Slide 48

Slide 48 text

recipes for operating 47

Slide 49

Slide 49 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 48

Slide 50

Slide 50 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 49

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

bonus 51

Slide 53

Slide 53 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 52

Slide 54

Slide 54 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 53

Slide 55

Slide 55 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 54

Slide 56

Slide 56 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 55

Slide 57

Slide 57 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 56

Slide 58

Slide 58 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 57

Slide 59

Slide 59 text

merci! 58

Slide 60

Slide 60 text

questions? 59

Slide 61

Slide 61 text

fin. 60

Slide 62

Slide 62 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 61