6 \ May 2019 \ http://www.phparch.com
Deploying ReactPHP Applications
Composer on the server since we’ve already done that on
CircleCI. As a result, we’re deploying a verified set of files to
the server. We’ll set up before-setup.yml with the following
contents:
---
- name: ANSISTRANO | Create Archive
delegate_to: localhost
archive:
path: "{{ playbook_dir }}/../"
dest: "{{ playbook_dir }}/metal.tgz"
We also want to clean it up after deploying, so we keep our
server clean and tidy. We set up after-cleanup.yml for that
with:
---
- name: ANSISTRANO | Remove Archive Locally
delegate_to: localhost
file:
path: "{{ playbook_dir }}/metal.tgz"
state: absent
Since our ReactPHP application is a long-running app, we
also restart Supervisord after symlinking everything into
position with after-symlink.yml(we’ll cover Supervisord in
the next section).
---
- name: ANSISTRANO | Restart supervisor
shell: php {{ ansistrano_release_path.stdout }}/ \
restart-supervisor.php
Now all you have to do to deploy is tag a release on GitHub.
CircleCI ultimately calls the following to deploy your appli-
cation for you.
ansible-playbook -i .circleci/hosts.ini .circleci/deploy.yml
Keeping It Running
Unlike your average PHP application which runs under
Apache or FPM, ReactPHP applications are often their own
daemon. We need to ensure they keep running when they go
down and get started at boot time. I’m using Supervisord^16
for that, but systemd is also a standard tool to achieve the
same. One of the reasons I chose Supervisord was that I came
from God^17 and was looking for something which worked on
current Ubuntu versions at the time. I ended up going for
Supervisord because it came as a system package.
Supervisord
One of the things I love about Supervisord is it has an
HTTP interface and API, so you can control everything from
a web browser. But my primary usage is restarting an appli-
cation on deployment, stopping the old running version, and
starting the new freshly deployed version.
16 Supervisord: http://supervisord.org
17 God: http://godrb.com
To enable supervisord’s HTTP server,, add the following to
your /etc/superisor/supervisord.conf:
[inet_http_server]
port = 0. 0 .0.0: 9005
Each application then has it’s own configuration in /etc/
superisor/conf.d/ using the template in Listing 3.
APP_NAME is the name you give the application inside Super-
visord; it’s used in the supervisorctl, API, and web UI to
identify the application. APP_COMMAND is the command you run
to start the application; this depends on how you build your
app, but in short, it’s like php app.php. PATH_TO_APP speaks
for itself—the path to where your application is located. (/
current/ is something Ansistrano related and symlinks to the
currently active release.) SYSTEM_USER is the user on the oper-
ating system under which the application runs.
There are some interesting options in that file; autostart
ensures the application starts the moment supervisord starts.
autorestart and startretries ensure on error your applica-
tion is restarted. It, however, stops trying after 1024 tries at
which point you should have been alerted that your appli-
cation is down and needs investigation. When you want two
instances running of the same app, you can, in combination
with so_reuseport on listening sockets, use numprocs to start
more than one instance, which brings their own set of compli-
cations with them and is a more advanced topic then we’ll
cover here. *logfile* are all logging related, useful for quickly
grepping through them if you don’t use something like Sentry,
Loki, or Loggly for (error) logging.
Monitoring
Getting your application deployed is one thing; keeping
it up and running is another. Your primary tool for this is
monitoring using tools like the ELK stack or my personal
favorite Grafana^18. Monitoring your application can be done
in a metric ton of different ways depending on what you need.
I always monitor memory usage, CPU usage, I/O throughput,
18 Grafana: https://grafana.com
Listing 3
- [program:APP_NAME]
- directory = PATH_TO_APP/current/
- command = APP_COMMAND
- process_name = APP_NAME
- numprocs = 1
- autostart = true
- autorestart = true
- startretries = 1024
- user = SYSTEM_USER
- stdout_logfile = /var/log/supervisor/APP_NAME.info.log
- stdout_logfile_maxbytes = 100MB
- stderr_logfile = /var/log/supervisor/APP_NAME.error.log
- stderr_logfile_maxbytes = 100MB
- logfile_backups = 2