Zaher Ghaibeh
PHP Backend developer
I've experience in a few PHP Frameworks, such as Laravel, Lumen and Slim (The last two are used for building Microservices/API services).
Laravel, Swoole and Docker
Published at Saturday, November 24, 2018 , Categorized under: PHP, Docker, Laravel, Swoole

What is Swoole

Before I can explain how you can install Swoole and use it with Laravel and Docker, I've to explain what is Swoole, and for that am going to quote the official explanation:

Production-Grade Async programming Framework for PHP, which enable PHP developers to write high-performance, scalable, concurrent TCP, UDP, Unix socket, HTTP, Websocket services in PHP programming language without too much knowledge about non-blocking I/O programming and low-level Linux kernel.

And they are doing a good job here by the way, now you can always check the official documentation for more about how it actually works, right now am going to show you how you can install it and use it with laravel, then I'll explain how you can dockerize your application in one image.

Install Swoole on linux:

There are two ways to install Swoole on your system, either using PECL or via source code:

Using PECL

It's simple and easy just run the following command:

#!/bin/bash
pecl install swoole

Build from sources:

It is recommended that you download the latest stable version from Github or from PECL, then execute the following commands:

PS: I'll be downloading the code from Github, and by the time of writing version 4.2.8 is the latest stable one.

$ wget -c https://github.com/swoole/swoole-src/archive/v4.2.8.tar.gz -O swoole.tar.gz
$ tar -xvzf swoole.tar.gz
$ cd swoole-src-4.2.8
$ phpize             #  prepare the build environment for a PHP extension
$ ./configure        #  add configuration paramaters as needed
$ make               #  a successful result of make is swoole/module/swoole.so
$ sudo make install  #  install the swoole into the PHP extensions directory

If you are planning to build it from source, I would recommend that you check the configuration options.

Enable swoole extension:

Now that we have installed swoole, it's time for us to enable the extension, and this can be done by simple loading the extension via php.ini like the following:

php -i | grep php.ini                      # check the php.ini file location
sudo echo "extension=swoole.so" > php.ini  # add the extension=swoole.so to the end of php.ini
php -m | grep swoole                       # check if the swoole extension has been enabled

Another option would be to create a new ini file under the conf.d directory for the installed php on the system.

Installing Laravel and Laravel-Swoole package

Basically you can just run composer create-project command to create a new project using laravel like:

$ composer create-project laravel/laravel swoole

After composer finish, we just need to install laravel-swoole package by running the following command:

$ composer require swooletw/laravel-swoole

Configure Lravel-Swoole package

If you are using anything before laravel 5.6 you will need to load the service provide manually:

[
    'providers' => [
        SwooleTW\Http\LaravelServiceProvider::class,
    ],
]

Otherwise, Laravel will autoload it for you.

If you want to change the default configurations, you can run the publish command which will generate two configuration file that you can modify

$ php artisan vendor:publish --tag=laravel-swoole

You can read more about the configuration part on the package wiki page.

Run laravel-Swoole

Now that everything is setup, you can run Swoole server by executing the command:

$ php artisan swoole:http start

Which will load the configuration from the config files and start your server, by default it will run on http://127.0.0.1:1215.

There are few more commands that you can use with swoole:http, feel free to check them:

$ php artisan swoole:http {start|stop|restart|reload|infos}
Command Description
start Start Laravel Swoole, list the processes by ps aux|grep swoole
stop Stop Laravel Swoole
restart Restart Laravel Swoole
reload Reload all worker process(Contain your business & Laravel/Lumen codes), excluding master/manger process
infos Show PHP and Swoole basic miscs infos(including PHP version, Swoole version, Laravel version, server status and PID)

Just remember that The swoole_http_server can only run in cli environment, so the artisan command helps you to manage it.

The support of swoole_http_server for Http is not complete. So, you should configure the domains via nginx proxy in your production environment. You can check the wiki for more information about it.

Dockerizing your application:

There are a lot of tutorials out there talking about how to dockerize your application, so am not going to go into deep details about the process.

In general we should have a Dockerfile which have all the steps that we need, so basically your Dockerfile should have:

  1. PHP 7.x.
  2. Swoole.
  3. Nginx (optional, just keep reading).

The main advice which everyone miss to say, is that you should build a base image that you can always use so you dont repeat the build and wait to have everything in your image every time you run build it.

Based on this, I already have built a docker image which has Swoole installed, enabled and ready to use with PHP 7.2 and Composer, you can find it here.

Remember to add any environment variables you need to your .env.example file so every person in your team will be able to use them and be notified when changed, as an example I have added the two environment variables to the file in my code

SWOOLE_HTTP_PORT=80
SWOOLE_HTTP_HOST=0.0.0.0

And you will see next how I use them in my Docker image.

Dockerfile content

So first lets have our Dockerfile at the root of our Laravel application, and it will contain the following:

FROM zaherg/php-swoole:7.2

LABEL Maintainer="Zaher Ghaibeh <z@zah.me>"

ENV APP_NAME ${APP_NAME:-laravel}
ENV APP_ENV ${APP_ENV:-production}
ENV APP_KEY ${APP_KEY}
ENV APP_DEBUG ${APP_DEBUG:-false}
ENV APP_URL ${APP_URL:-"http://localhost"}
ENV LOG_CHANNEL ${LOG_CHANNEL:-stack}
ENV APP_TIMEZONE ${APP_TIMEZONE:-UTC}
ENV DB_CONNECTION ${DB_CONNECTION:-mysql}
ENV DB_HOST ${DB_HOST:-database}
ENV DB_PORT ${DB_PORT:-3306}
ENV DB_DATABASE ${DB_DATABASE:-docker}
ENV DB_USERNAME ${DB_USERNAME:-docker}
ENV DB_PASSWORD ${DB_PASSWORD:-secret}
ENV DB_COLLATION ${DB_COLLATION:-utf8mb4_unicode_ci}
ENV DB_CHARSET ${DB_CHARSET:-utf8mb4}
ENV BROADCAST_DRIVER ${BROADCAST_DRIVER:-log}
ENV CACHE_DRIVER ${CACHE_DRIVER:-file}
ENV QUEUE_CONNECTION ${QUEUE_CONNECTION:-sync}
ENV SESSION_DRIVER ${SESSION_DRIVER:-file}
ENV SESSION_LIFETIME ${SESSION_LIFETIME:-120}
ENV REDIS_HOST ${REDIS_HOST:-"127.0.0.1"}
ENV REDIS_PORT ${REDIS_PORT:-6379}
ENV REDIS_PASSWORD ${REDIS_PASSWORD:-null}
ENV MAIL_DRIVER ${MAIL_DRIVER:-smtp}
ENV MAIL_HOST ${MAIL_DRIVER:-smtp.mailtrap.io}
ENV MAIL_PORT ${MAIL_PORT:-2525}
ENV MAIL_USERNAME ${MAIL_USERNAME:-null}
ENV MAIL_PASSWORD ${MAIL_PASSWORD:-null}
ENV MAIL_ENCRYPTION ${MAIL_ENCRYPTION:-null}
ENV PUSHER_APP_ID ${PUSHER_APP_ID}
ENV PUSHER_APP_KEY ${PUSHER_APP_KEY}
ENV PUSHER_APP_SECRET ${PUSHER_APP_SECRET}
ENV PUSHER_APP_CLUSTER ${PUSHER_APP_CLUSTER}
ENV MIX_PUSHER_APP_KEY ${PUSHER_APP_KEY:-""}
ENV MIX_PUSHER_APP_CLUSTER ${PUSHER_APP_CLUSTER:-""}
ENV SWOOLE_HTTP_PORT ${SWOOLE_HTTP_PORT:-80}
ENV SWOOLE_HTTP_HOST ${SWOOLE_HTTP_HOST:-"0.0.0.0"}

USER root

ADD ./ /var/www

RUN composer global require hirak/prestissimo && \
    composer install --no-progress --no-suggest --prefer-dist --optimize-autoloader

CMD ["php", "artisan","swoole:http","start"]

In the first line, we specify our base image, which as I said has Swoole and PHP 7.2:

FROM zaherg/php-swoole:7.2

From line 5 till line 42 we define the default our environment variables, which as you can notice all from the .env.example file in laravel, except the last two one which is related to Swoole.

Then we copy our code over to the Docker image, and run composer install.

The last line is the most important one, as we use it to start Swoole once the image is ready

CMD ["php", "artisan","swoole:http","start"]

To clean up the build process for Docker, we will create .dockerignore file which will contain the following:

/.git
/bin
/vendor

Building the image:

As any docker image, to build it we should run the following command:

docker build -t laravel-swoole:latest .

Running your image

Running the image is simple, all you have to do is to execute the following command:

docker run -p 80:80 laravel-swoole:latest -d

And open your browser type http://localhost and you should be greeting with the default laravel homepage.

Now that you have your image, you can deploy it and put it behind Nginx or any Proxy server and it will work.

Utilizing Traefik

Now that we have everything we need, lets raise the bar a bit and use Traefik as our proxy in-front of our Laravel application, am not going to go into details, but I'll share my docker-compose.yml file:

version: "3.6"

services:

  app:
    image: laravel-swoole
    build:
      context: ./
      dockerfile: Dockerfile
    networks:
      - backend
    volumes:
      - ./:/var/www
    labels:
      - "traefik.backend=web"
      - "traefik.frontend.rule=Host:web.docker.local"
      - "traefik.enable=true"
      - "traefik.port=80"
      - "traefik.backend.loadbalancer.method=drr"
    healthcheck:
      disable: true
    env_file:
      - .env
    restart: unless-stopped

  database:
    image: mysql:5.7
    hostname: database
    ports:
      - "3306:3306"
    environment:
      - "MYSQL_RANDOM_ROOT_PASSWORD=yes"
      - "MYSQL_DATABASE=docker"
      - "MYSQL_USER=docker"
      - "MYSQL_PASSWORD=secret"
    networks:
      - backend
    volumes:
      - mysql:/var/lib/mysql
    labels:
      - "traefik.backend=database"
      - "traefik.enable=false"
    restart: unless-stopped

  proxy:
    image: traefik:latest
    command:
      - --web
      - --web.statistics
      - --docker
      - "--docker.domain=docker.local"
      - "--docker.swarmMode=false"
      - "--logLevel=DEBUG"
      - --defaultentrypoints=gamester
      - "--entryPoints=Name:gamester Address::80 Compress:true"
    networks:
      - backend
    ports:
      - "80:80"
      - "8080:8080"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - /dev/null:/traefik.toml
    labels:
      - "traefik.backend=proxy"
      - "traefik.frontend.rule=Host:proxy.docker.local"
      - "traefik.enable=true"
      - "traefik.port=8080"
    restart: unless-stopped

networks:
  backend:

volumes:
  mysql:

Final words

As you can see it is so simple to have your application dockerized and run by the help of few tools, and using Swoole within your code will give you a big advantages in speed.

Lastly, to make things simpler, I've created two starter projects that you can use as a template for your when building laravel or lumen application and you want to try swoole, you can get them from packagist :

  1. Laravel Starter.
  2. Lumen Starter.