Pragmatism in the real world

Deploying a PHP application to Cloud Foundry

I recently had a requirement to deploy a Slim application somewhere. As I already have a Bluemix account, it seemed sensible to deploy it to their Application Runtimes service which is an installation of the Open Source Cloud Foundry project.

This turned out to be quite easy, but there are a number of steps involved, so I’m documenting it here.

Setup the CLI tools

I’m a command line person, so did it all via the command line. There’s a Cloud Foundry CLI and also there’s the Bluemix CLI tool too.

If you have bx installed, then you can use bx cf to run Cloud Foundry commands. This appears to proxy to the cf client. Regardless, the same commands seem to work in both tools. As I tend to prefer the Open Source option when I can, I used the cf tool. If you’re using the Bluemix CLI, just prefix all my commands with bx and you should be fine.

On Mac, I used Homebrew to install cf:

$ brew install cloudfoundry/tap/cf-cli

Follow the relevant instructions for your operating system.

Prepare the PHP application

There are a number of things we need to do to set up our PHP application for deployment. In my case, I’m deploying a Slim application, but practically, these steps work for any PHP app.

1. Select PHP version

CF uses buildpacks which control the environment. In our case, we’ll use the PHP buildpack which comes with a variety of PHP versions. To select the one that we will use, we use the standard Composer require statement.

I want to use PHP 7.1, need to add "php" : "7.1.*" to the require section of composer.json like this:

composer.json:

    ...
    "require": {
        "php" : "7.1.*"
        "slim/slim": "^3.0",
    ...

2. Create the manifest file

Your Cloud Foundry environment is controlled by the manifest file, manifest.yml which must be in the root directory of your application.

manifest.yml:


---
applications:
    - name: slim-bookshelf
      buildpack: php_buildpack
      memory: 64M
      instances: 1
      host: slim-bookshelf

Every CF app needs a name, and this is minimum requirement for a valid manifest file. Note that the app name may be used on the command line, so it’s easier if it doesn’t have a space in it. Every other option is used to override the defaults and I find it useful to ensure that I know what will be configured.

I’ve set up:

  • buildpack: Which build pack to use. This can be a name from cf buildpacks or a GitHub URL. I’ve picked the PHP buildpack that’s supplied with Bluemix.
  • memory: Memory limit for the application. Memory is expensive in cloud apps, so keep this as low as you can.
  • instances: The number of instances to initially start.
  • host: The subdomain name for this application.

That’s all we need. However the full list of options is in the docs should you need them.

3. Set buildpack options

The PHP buildpack can be configured using the .bp-config/options.json file. Weirdly this is a JSON file rather than YAML, but whatever :)

In this file, we can set which directory to use as the public root of our project. Slim Bookshelf is a standard Slim application, so it’s public root directory is public, so we need to set this. We can also enable PHP extensions here.

.bp-config/options.json:

{
    "WEBDIR": "public",
    "PHP_EXTENSIONS": ["gd", "pdo", "pgsql", "pdo_pgsql"]
}

There are other options available; check the docs.

4. Setup Rewrite rules

The PHP buildpack runs Apache by default, so can you create a public/.htacces file to configure rewriting of URLs to index.php. For example:

public/.htaccess:

<IfModule mod_rewrite.c>
    RewriteEngine On

    RewriteCond %{REQUEST_FILENAME} -s [OR]
    RewriteCond %{REQUEST_FILENAME} -l [OR]
    RewriteCond %{REQUEST_FILENAME} -d
    RewriteRule ^.*$ - [NC,L]


    RewriteCond %{REQUEST_URI}::$1 ^(/.+)(.+)::\2$
    RewriteRule ^(.*) - [E=BASE:%1]
    RewriteRule ^(.*)$ %{ENV:BASE}index.php [NC,L]
</IfModule>

That’s it with configuring our application. The additional files we’ve created can be commited to git and we’re good to go.

Deploy our CF application

To deploy our Cloud Foundry application we need to log into Cloud Foundry and then push our application.

Log in to the CF cli

We log in using cf login -a {api url}, -o {organisation} -s {space} which will ask us for our username and password.

You can get the API endpoint you need from bx regions if you’re using Bluemix. For the UK, it’s https://api.eu-gb.bluemix.net, my organisation is 19FT and I’m using my demo space:

$ cf login -a  https://api.eu-gb.bluemix.net -o 19FT -s demo

It will ask you for your username and password and then print out some summary information.

Deploy our application

Deployment is trivially simple:

$ cf push

You’ll see it create the app and a route and then bind the route to the app. Then it uploads the our files and starts the app. This involves downloading a lot of components and then it runs composer for us. When it finishes you’ll get some status information like this:

0 of 1 instances running, 1 starting
1 of 1 instances running

App started


OK

App slim-bokshelf was started using this command `$HOME/.bp/bin/start`

Showing health and status for app slim-bookshelf in org 19FT / space demo as rob@19ft.com...
OK

requested state: started
instances: 1/1
usage: 64M x 1 instances
urls: slim-bookshelf.eu-gb.mybluemix.net
last uploaded: Tue Aug 29 16:06:21 UTC 2017
stack: cflinuxfs2
buildpack: php 4.3.27

     state     since                    cpu    memory     disk      details
#0   running   2017-08-29 05:07:45 PM   0.0%   0 of 64M   0 of 1G

At this point, your public/index.php file is being served on the URL it has created, http://slim-bookshelf.eu-gb.mybluemix.net in my case.

If you need to make any changes to your app, just cf push again.

Viewing logs

To view the logs, use cf logs {app name}. This will tail the logs, so run the command and then hit refresh to find out what went wrong

$ cf logs slim-bookshelf
Retrieving logs for app slim-bookshelf in org 19FT / space demo as rob@19ft.com...

   2017-08-30T21:56:02.47+0100 [RTR/3] OUT slim-bookshelf.eu-gb.mybluemix.net - [2017-08-30T20:56:02.466+0000] "GET / HTTP/1.1" 500 0 0 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36" "5.10.124.132:48703" "169.50.91.74:61062" x_forwarded_for:"80.100.100.100" x_forwarded_proto:"http" vcap_request_id:"15e43a48-f327-4ccb-5300-cade5bb3d5bd" response_time:0.008793145 app_id:"bbfdacfb-2a5f-4041-b426-01f6a8619011" app_index:"0" x_global_transaction_id:"2263314295" x_b3_traceid:"867d2c41fc8b278a" x_b3_spanid:"867d2c41fc8b278a" x_b3_parentspanid:"-"
   2017-08-30T21:56:02.47+0100 [RTR/3] OUT
   2017-08-30T21:56:02.51+0100 [APP/PROC/WEB/0] OUT 20:56:02 httpd   | 159.122.215.170 - - [30/Aug/2017:20:56:02 +0000] "GET / HTTP/1.1" 500 - vcap_request_id=15e43a48-f327-4ccb-5300-cade5bb3d5bd peer_addr=159.122.215.170
   2017-08-30T21:56:02.51+0100 [APP/PROC/WEB/0] OUT 20:56:02 httpd   | [Wed Aug 30 20:56:02.475171 2017] [proxy_fcgi:error] [pid 47:tid 140551289833216] [client 159.122.215.170:59576] AH01071: Got error 'PHP message: PHP Parse error:  syntax error, unexpected ''view'' (T_CONSTANT_ENCAPSED_STRING), expecting ']' in /home/vcap/app/app/settings.php on line 27\n'

Next steps

As you may have noticed, in our buildpack options file, we enabled the PostgreSQL PHP extension. Next up is to configure and use a PostgreSQL database with our app and also change that domain name to something more personal.

I’ll cover these points in separate articles.