Serving High Performance the Symfony App with Swoole/Docker

Emre Çalışkan
Beyn Technology
Published in
7 min readSep 21, 2022
Photo by Kolleen Gladden on Unsplash

In this article, we will creating a container image for your Symfony application using the high-performance Swoole Runtime and automatically deploying this image to Docker Hub.

Why Container Image ?

Containers are especially popular because of they offer many advantages than the virtual machines (VMs). VMs basically contain complete copies of a powerful operating system, the application itself, all the necessary binaries and libraries. But the Container image only hosts and runs the binaries and libraries you need. In this way, you spend only the minimum required resources while scaling your application.

https://www.docker.com/resources/what-container/

On the other hand, virtual machine installation, management and scaling is a costly operation in terms of time and money. Containers are easier to install, better manage and scale than the virtual machines. Tools such as Kubernetes and Docker Swarm are available for scaling.

Another advantage of container image emerges in development and testing processes. PHP gets many of its features from its extensions. Configuring and installing these extensions greatly increases the process of preparing the development environment to work. Even if we think that you have solved such cases, even operating system differences can affect output. In your development and CI environments (Gitlab Runner, Github Actions). A container image configured with the same properties as the production environment allows you to solve most problems before they occur.

Why Alpine ?

Alpine describes itself as:

Small. Simple. Secure. Alpine Linux is a security-oriented, lightweight Linux distribution based on musl libc and busybox.

Docker describes Alpine as:

We recommend the Alpine image as it is tightly controlled and small in size (currently under 6 MB), while still being a full Linux distribution.

Container images using Ubuntu produce lower performance due to having too many components in them. If you are developing in a more professional and security-focused project, you can take a look at the paid Redhat Universal Base Image. We will continue using Alpine in this article.

Why Swoole ?

PHP-FPM, which has been used for PHP for years, can no longer satisfy today’s demands such as performance and scaling. On the other hand Swoole, is among the best runtimes for PHP applications with innovations such as high performance, easy scaling and re-running only the necessary codes in the processing of the request. To learn more about Swoole, you can read the article following.

Installation

As I mentioned at the beginning of the article, we will create the container image automatically with the CI tools. However, you need Container Runtime (Docker, Podman etc.) to build and deploy the image you developed on your own computer for testing purposes. You can review the following article to install Docker according to your operating system.

Container Images

We will create 2 Container Images;

  • PHP Base Container Image
  • App Container Image

PHP Base Container Image

We will use this image to create a base image for multiple projects. We will create this image because it will waste time to build the extensions required for each project again and again. In this way, you can easily change the PHP version in your application. In this article, we will continue with PHP 8.1.

Dockerfile

To create a container image, you must have one Dockerfile. Each instruction creates one layer. If it does not change, it will not be compiled again.

Each instruction creates one layer:

  • FROM creates a layer from the php:8.1-alpine Docker image.
  • RUN instruction will execute any commands in a new layer on top of the current image and commit the results.
  • COPY instruction copies new files or directories from <src> and adds them to the filesystem of the container at the path <dest> .
  • ENV instruction sets the environment variable <key> to the value <value> .
  • WORKDIR instruction sets the working directory for any RUN, CMD, ENTRYPOINT, COPY and ADD instructions that follow it in the Dockerfile .
  • EXPOSE instruction informs Docker that the container listens on the specified network ports at runtime.
  • ENTRYPOINT specifies what command to run within the container.

You can customize the Dockerfile file according to your own wishes. In this example we will use Supervisor to handle Swoole. Also, if your project has cron, we will use Supervisor to handle cron. This is the reason why we use Supervisor is that Supervisor will automatically restart your application when there is a problem and your application goes into a panic state.

Entrypoint file is as follows.

start-container
supervisor.d/app-swoole.ini
supervisor.d/app-cron.ini

Build

To create the container image, it will be enough to run the following command the terminal in the directory where the project located.

docker build -t thecaliskan/symfony-swoole-docker:latest .
  • Docker Hub Username: thecaliskan
  • Container Name: symfony-swoole-docker
  • Tag Name (like branch): latest

When you run the docker image ls command to check your created image, if you see an output like the one above, it has successfully created. We need a container registry service to access this container image from anywhere. In this article, we will use Docker Hub. We will use Github Actions for auto build and publish. You can host your private and public container images on Docker Hub.

Auto Build and Publish

.github/workflows/ci.yml

You can get tokens after the creating a new user on Docker Hub. You need to add this token as an environment variable in the secrets section of your github project’s settings page. Check out the document below for more detailed information.

App Container Image

Now everything is ready to create the container image of project. While we create this container image, we will create 3 layer Dockerfile.

Build Instructions

  • First step, creates a layer from the symfony-swoole-docker Docker image.
  • Second step, we will copy the project files into the image.
  • Third step, we will install composer packages.
Dockerfile

Swoole Runtime

We will be using the official symfony swoole runtime (runtime/swoole) to add the Swoole Runtime to the project.

composer require runtime/swoole

After the installation is completed, the last step to activate the swoole is to add the following in the /public/index.php file. Since the environment variable SWOOLE_RUNTIME is defined inside the symfony-swoole-dockercontainer image, the Swoole Runtime will not run when you run the project outside the container image. Check out the link for more detailed information about configuration settings.

public/index.php

Cron

If you are using cron in the project, you can add it to the crontab file as follows.

crontab

Auto Build and Publish

.github/workflows/ci.yml

We will use the .github/workflows/ci.yml file used in the PHP Base Container Image. We will replace the container image name at the bottom with the name of the project. After that, container image will be ready to use.

Run Container Image

You can run the container image your created with the following code. To run the container image with a single node, you can apply the following. In next article, I will explain how to run multi-node and publish with proxy server.

Docker run references
docker run -itd --rm --name symfony-example -p 9000:80 --env-file .env thecaliskan/symfony-swoole-docker-example
Docker Container Run
Server Test

You can stop the container with the following code.

docker stop symfony-example

Benchmark Container ImageContainer Image

The resource usage when the container image running with 100 workers is first opened is as follows.

Container statistics before test

An endpoint has been set to return a simple JSON response. We will reviewing its performance and resource consumption by sending 100 simultaneous requests, a total of 10K requests.

src/Controller/HomeController.php
ab -n 10000 -c 100 http://localhost:9000/
Benchmark results
Container statistics after test

Conclusion

In this article, we learned how to make your Symfony application a portable container image with its Swoole runtime. We improved the performance of Symfony application with Swoole Runtime. We were able to process 10K requests in 1.182 seconds with zero errors. This means almost a static file response time. In next article, we will learn how to serve this container image with multiple nodes with services such as proxy server, postgresql and redis.

Thank you for reading!

Example Projects

--

--