Learning Cloud Foundry PHP staging and deployment
Background
VMWare Cloud Foudry is an open source Platform for PaaS (platform as a service). I see cloud foundry as a tool to simplify multiple application deployment in multiple servers. This post will describe things I found by reading source code of vcap (Vmware Cloud Application Platform) at github here and here.
My point of interest is php deployment capability that exists on vcap, which are contributed by phpfog developers.
My previous exploration of Cloud Foundry resulted in this picture below, which describe my current understanding of the Cloud Foundry platform.
Starting point
The starting point of php support is given an interesting commit I had seen before, where phpfog team implements php functionality. At first it took me about 10 minutes browsing the vcap network graph (https://github.com/cloudfoundry/vcap/network), then I just realized there is a distinct phpfog branch in the vcap git..
The interesting commit is titled 'Support for deploying PHP applications through a standard Apache configuration with built-in support for APC, memcache, mongo and redis', authored by 'cardmagic' about 2 years ago (see the commit in github here).
Staging
PHP applications are deployed using the vmc client. For now I just ignore the client part. The client communicates with the cloud controller, which in turn will command the DEA (Droplet Execution Agent) to deploy applications. DEA will execute the start_droplet function, which will invoke the correct staging plugin associated with the application's runtime.
[maybe I would research further on the relation between start_droplet and plugins]
The PHP plugin (ref) prepares the application by executing this ruby fragment :
def stage_applicationDir.chdir(destination_directory) docreate_app_directoriesApache.prepare(destination_directory)copy_source_filescreate_startup_scriptendend
I am no Ruby programmer, so I sure hope I read this correctly..
At first, the plugin changes the current directory into droplet instance directory. In there, it calls the method create_app_directories (which I guess would create some required directories in there). Then it calls prepare method of the Apache class. Reading apache.rb, we know what the Apache::prepare does is that it copies apache.zip from the plugin directory and extracts it into the droplet instance directory. The apache.zip consists of configuration directories of an apache httpd server, with some modification so it honors several environment variable that would be injected in apache/envvars below. Generate_apache_conf script is also being copied from the plugin resource directory.
I guess the copy_source_files method would copy the application source codes into the droplet instance directory.
After that, startup script will be created using startup_script method :
Which conveniently executes generate_apache_conf script, which in turn will create some apache configuration files and a shell script based on application parameters. The files are :def startup_scriptvars = environment_hashgenerate_startup_script(vars) do<<PHPEOFenv > env.logruby resources/generate_apache_conf $VCAP_APP_PORT $HOME $VCAP_SERVICESPHPEOFendend
- apache/sites-available/default, which defines DocumentRoot, ErrorLog file, log format, and VCAP_SERVICES environment variable
- apache/envvars, which defines apache user, group, pid file, base directory
- apache/ports.conf, which define the port where apache listens
- apache/start.sh, which is the script that would start the apache server in the droplet directory with the created configuration files
Running the App
Two methods in php plugin tells us how the platform starts the application :So it starts the application by running apache/start.sh that is created by the previous generate_apache_conf script.# The Apache start script runs from the root of the staged application.def change_directory_for_start"cd apache"enddef start_command"bash ./start.sh"end
The Most Current Version
I try to look for the latest lib/vcap/staging/plugin/php/plugin.rb file, at first I found none, because it is already migrated from vcap repository to vcap-staging repository. Refer here to the newer version.
I notice an improvement which would allow us to define application memory allocated to the PHP application and also a stop command which invoke kill -9 :
def stop_commandcmds = []cmds << "CHILDPIDS=$(pgrep -P ${1} -d ' ')"cmds << "kill -9 ${1}"cmds << "for CPID in ${CHILDPIDS};do"cmds << " kill -9 ${CPID}"cmds << "done"cmds.join("\n")endprivatedef startup_scriptgenerate_startup_script do<<- span="span">PHPEOF->env > env.logruby resources/generate_apache_conf $VCAP_APP_PORT $HOME $VCAP_SERVICES #{application_memory}mPHPEOFendend
The kill -9 thing really handy because in numerous ocassions I am forced to do such command manually to stop a stuck/hung php process. The generate_apache_conf script is enhanced to create an additional php configuration file (apache/php/memory.ini) which impose a memory limit:
output_path = 'apache/php/memory.ini'template = <<- span="span">ERB->memory_limit = <%= php_ram %>ERB
That tells us that memory limitiation is for a single apache/PHP process. Collective application memory usage can be determined by n * (php_ram + x) where n is the amount of apache process running and x is the memory used by apache on its own. That makes me wonder about max client in apache's configuration (in apache.zip), here is the latest version :
StartServers 5
MinSpareServers 5
MaxSpareServers 10
MaxClients 150
MaxRequestsPerChild 0
The configuration fragment above essentially says that running apache process could be between 5 to 150 child processes, and typically 10 during idle.
There is also an additional line in stage_application method to copy php configuration files too :
This environment variable export in generate_apache_conf script enables the apache/php directory to contain php configuration files :system "cp -a #{File.join(resource_dir, "conf.d", "*")} apache/php"
export PHP_INI_SCAN_DIR=$APACHE_BASEDIR/php
Finishing remarks
I hope by reading this will allow us to customize Cloud Foundry's PHP support as needed. I might need to add additional php extensions, that must be inserted into php's conf.d directory (shown above copied from the resource directory). And also it might be interesting to implement a method to change MaxClients from the cloud API
Comments