12

I'm fairly new to Docker so excuse any obvious ignorance or misunderstandings. That said, I've been coding and configuring web applications for a long time now. I have recently been dabbling with some more sophisticated (Docker and "traditional") web/application server setups and experimenting with performance enhancements and simplifying deployments.

My personal favorite configuration to use for most projects thus far is nginx as a (mostly) "static file" web server &&|| caching mechanism &&|| Load Balancer in a reverse proxy config with Apache v2.4+ as the "backend" which runs PHP (also Perl &&|| Python) in a "FastCGI" (PHP-FPM) configuration (with mod_php disabled) and mpm_events (instead of mpm_prefork). We can always add in something else also such as REDIS or memcached, where applicable (but I rarely see a significant performance gain when using Cloudflare caching).

I've been dabbling with a few different ways to keep my "favorite web server composition" flexible and "ephemeral" enough to accommodate any and all possible options for further performance gain or resource load management. What I keep seeing is that there seems to have been a shift towards using PHP-FPM in its own separate "stand-alone" Docker container sometimes around late 2019.

Why?

While I can appreciate keeping resources isolated and separate for an easier to debug/config/secure configuration(s), I don't fully understand what the benefits are to having PHP-FPM in a separate Docker container that is implicitly REQUIRED by the application that the Docker containers are comprising.

If anything, having a separate PHP-FPM container seems like additional configuration, additional resource consumption and an even more complicated build/deploy consideration in a CI/CD pipeline.

I can even get onboard with "simple preference", such as instead of using Apache2 and nginx on the same Ubuntu/Debian or RHEL/CentOS container, break off nginx into its own container(s) and simply config your hostname(s) &&|| IPs and ports appropriately.

But what is a practical use case and advantages for having PHP-FPM separated from Apache or nginx containers for any additional benefit beyond perhaps using more Dockerfile &&|| docker-compose.yaml and less ba.sh scripting to get the same job done? If any part of the application fails or falters, then functionality will suffer. The additional network &&|| docker-network communications and hostname resolutions seems like it would just be a performance penalty over combining certain aspects of the docker-composer-ed application together, rather than splitting them up simply for the sake of splitting them up.

2
  • 1
    This is, by far, the best synopsis that I've found on this subject thus far, from 2017-2018: "So finally I'll quote the docker page with some emphasis: While “one process per container” is frequently a good rule of thumb, it is not a hard and fast rule. Use your best judgment to keep containers as clean and modular as possible. There's no "silver bullet rule" which apply to everything, it's always a balance between the complexity within the container and the complexity orchestrating the containers themselves." - devops.stackexchange.com/a/2090/9877
    – aK8sN0v1c3
    Commented Sep 7, 2020 at 15:31
  • Same OP as above comment has this to say, with an older link to an interesting article (a case FOR separation of a php-fpm container from the web server container) - devops.stackexchange.com/a/3080/9877
    – aK8sN0v1c3
    Commented Sep 7, 2020 at 18:28

2 Answers 2

11

Im heavily investigating this, and like many people, im discovering that in 2020 there doesn't seem to be so much logic in separating tightly coupled webserver + PHP + app process/code from each other:

Two main arguments for separation are scalability and separation of concerns (one process per container).

Logically the webserver should only act as webserver and distribute traffic to PHP nodes behind a service that uses a LB.

This way one container/service serves webserver purpose and serves static files, while proxying the php requests to the php pod which only concerns PHP.

And webserver and php pods can scale differently - ie you may need 50-100 PHP pods for a workload, but only 3-4 webserver nodes. So you save on resources. Great!

Except in respect to separation of concerns...

If the webserver is not going to serve requests to different apps, there is little logic for the separation of concerns to different containers - leave aside services. If the app is coupled with the webserver, you just end up spawning extra webserver pods, with accompanying services and load balancers to go with them.

On top of that you will have to distribute the app code to both containers or mount them from a NFS bind, or non NFS mount. You will have to deal with all the relevant access and permission stuff, if you put the app code in both pods you will have to deal with static + dynamic file separation, or even end up having to distribute both types of files to both pods. You could put two containers and make one of them mount directories on the other and that would work, but that actually puts two containers in the same logical host in regard to network, host etc so you are still combining them. This still provides some separation of concerns though. But the communication in between the two containers in the same pod is a concern - it has to either happen through intra-host network over loopback, which uses the network stack and is less performant. Maybe sockets could work.

Even worse if your app may require webserver being present in the same container - for example if you are doing ssl termination and have to carry over the visitor's actual ip to the php pod, you may end up having to jump through some hoops. While both webserver and php are available in the same container, this can be done through various means.

When everything is in one container, none of these is necessary. Everything is neatly packed in the same place, no need to track different deployments, services, app codes, or replication controllers.

This kind of singular container/deployment is very advantageous today especially if you are using ingress-nginx or nginx-ingress and hosting many different apps/sites in the same cluster. Each app/site is contained in its own container (imagine different WordPress sites), and each site is completely contained in its permissions, access, deployment, version, requirements etc.

So much that you could even allow apps/sites to customize their runtime environments by providing configuration files for webserver or php from NFS shares or other mounts - ie .htaccess, php.ini etc. Which is a must for hosting different apps since people tend to have different requirements.

In respect to the performance

Today NGINX or Apache require very few resources with event-based request handling. For example in my benchmarks, i saw Apache 2 with event mpm requiring only 3-4 MB memory, and a grand total of ~2-3% cpu (out of 1000m requested cpu, ie 1 vcpu) while handling 50-100 concurrent requests per second while serving WordPress.

The weights of webserver vs PHP pods would differ depending on the app or website of course, and there could definitely be apps that would require such a separation, but for most common web workloads this does not seem necessary.

So spawning a few Apache pods and then spawning hundred or more PHP pods does not seem to bring much gain. Aside from creating internal cluster network traffic as pods need to communicate with each other and it also creates more complications in the app configurations (deployments, services, load balancers etc).

Another disadvantage of separating containers is that you create two times the services per app. This will reflect on the limits of the cluster you create since services, pods require ip addresses.

When webserver + PHP +app are in the same container, the webserver can just communicate with php-fpm via file sockets. Which is faster, and creates less network overhead even on the loopback interface inside the container, leaving aside creating network traffic inside the cluster. Internal network and cpu load saved.

In conclusion...

For specific purposes, with one Apache 2 or NGINX server distributing requests to different apps on different PHP-FPM clusters, separating concerns would likely be a necessity.

But in the era of ingress-nginx, this really doesn't seem necessary.

Especially reduced complexity, increased portability, saved performance and internal cluster noise, better customization, and other benefits gained from packaging tightly coupled server + PHP + app deployments in one single container seem to be too good to pass.

Furthermore, this enables even shared-hosting-like formats in Kubernetes clusters, which seems to open many possibilities.

I would very much appreciate any input from anyone who had experience in dealing with this choice between selecting singular containers for a tightly coupled app vs multiple containers, or anyone who did benchmarks on these.

2
  • 2
    This is as good of a "working theory" answer that could be expected. Thank you for the detail in your hypothesis. I, too, would like to see benchmarks! Breaking each service out into it's own pod/container is adding more complexity than worth the potentially and occasional benefits to scalability, IMO, also. The expense of a smaller instance/pod/container is negligible in the face of benefits to elegance and consistent performance. I do agree edge-case workloads my benefit from service/server separation tho not the norm. Triggers for auto-scaling + warm-up times are key factors in a CBA.
    – aK8sN0v1c3
    Commented Feb 4, 2021 at 16:22
  • Great information here. One issue I didn't see addressed was logging output. Much of the docker/podman/k8s architecture assumes a single log output for that single process (in its single container). Though it's more of an inherited requirement of containerization and I've worked around the rule, it's always been a regret.
    – 8None1
    Commented Jan 30 at 16:58
3

Separation of concerns is quite a strong reason to keep web server and php-fpm on separate containers, especially in Kubernetes.

  1. Containers can be created and maintained by separated teams (PHP by Dev, Nginx by IT-Ops/DevOps), for example, different base images
  2. You can change configuration of Nginx without rebuilding the app
  3. Same as point 2 with upgrading Nginx server

If you have microservices in the Kubernetes cluster I would prefer to have two containers per pod. For example for 30 microservices similar PHP services you really don't want to rebuild all of them only to change some Nginx logs format. Of course, you can keep some Nginx configuration as a config map and share it in this way.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.