Creating a HipHop Virtual Machine (HHVM) Cartridge for Stratos 2.0
- Lakmal Warusawithana
- Vice President & Distinguished Engineer - WSO2 LLC
Table of contents
- Introduction
- What is a cartridge?
- Selecting the IaaS
- Setting up HHVM
- Setting up Stratos Cartridge Components
- Creating AMI for HHVM Cartridge
- Register with Cloud Controller
- Prepare Wordpress code for run in HHVM
- Deploying wordpress over HHVM Cartridge
- References
Introduction
HipHop Virtual Machine (HHVM) is a new open source virtual machine designed for executing programs written in PHP. HHVM’s JIT compiler allows it to execute PHP faster than Zend PHP, in most cases, unmodified. Facebook now uses HHVM as a faster replacement for hphpi, with plans to eventually use HHVM for all PHP execution. [1]
With the Stratos 2.0 release we have shipped PHP, MySQL, TOMCAT, and CARBON cartridges. Here, we explain how to create a HHVM cartridge, registering with Cloud Controller, and we finally show how to deploy wordpress into a HHVM cartridge.
What is a cartridge?
A Cartridge is the core functional component of Stratos 2.0, which is pluggable. A Cartridge component can make use of core services provided by WSO2 Stratos 2.0 (e.g. auto-scaling, load-balancing, health monitoring, metering, billing, tenant provisioning, code deployment, identity management, and entitlement) to build a platform in which tenants can deploy their applications. In summary, the Stratos 2.0 Cartridge is a Cloud-aware platform environment, which extends legacy technologies into the Cloud and deliver Cloud benefits.
Selecting the IaaS
Stratos runs on top of an IaaS. With the Stratos 2.0 release, we have pre-configured Stratos AMIs that can simply run on top of AWS EC2. Hence, we explain this cartridge creation using EC2.
To create a cartridge, we need to have a Cloud-based images with respect to IaaS; for EC2 it's AMI. Here you can use the ami-23d9a94a Ubuntu 12.04 Cloud image, which is available in the US EAST (North Virginia) EC2 zone. You have to create at least an M1 Small instance because HHVM cannot run inside the T1 Micro Instance with 613MB RAM [2]

Setting up HHVM
First SSH to the created Instance.
ssh -i Downloads/lakmal-s2.pem [email protected]
Let's use pre-built packages for setting up HHVM. [3] Add the hiphop repository to your /etc/apt/sources.list
deb https://dl.hiphop-php.com/ubuntu precise main
Also enable the universe component
deb https://archive.ubuntu.com/ubuntu precise main universe
Then update your package index
$ sudo apt-get update
Finally, you can install (or update) your package with the normal apt-get install process
$ sudo apt-get install hiphop-php
Then create a config file /etc/hhvm.hdf
Server {
Port = 80
SourceRoot = /var/www/
}
Eval {
Jit = true
}
Log {
Level = Error
UseLogFile = true
File = /var/log/hhvm/error.log
Access {
* {
File = /var/log/hhvm/access.log
Format = %h %l %u %t \"%r\" %>s %b
}
}
}
VirtualHost {
* {
Pattern = .*
RewriteRules {
dirindex {
pattern = ^/(.*)/$
to = $1/index.php
qsa = true
}
}
}
}
StaticFile {
FilesMatch {
* {
pattern = .*\.(dll|exe)
headers {
* = Content-Disposition: attachment
}
}
}
Extensions {
css = text/css
gif = image/gif
html = text/html
jpe = image/jpeg
jpeg = image/jpeg
jpg = image/jpeg
png = image/png
tif = image/tiff
tiff = image/tiff
txt = text/plain
}
}
Make sure you have created /var/www and /var/log/hhvm directories. Then, create a system user to run hiphop PHP server. For example, we can run the server as a web user (you do not need to provide a password because we are not going to login as this user).
adduser web
Thereafter, all installed software need to run HHVM. Now, we will continue with the Stratos configuration and convert this to a cartridge.
Setting up Stratos Cartridge Components
For the Stratos component, we need to install some system packages. git need to artifacts synchronization, libboost-all-dev need for compile thrift, which need to cartridge logging agent. xml-twig-tools need to extract git credentials from the response xml. We will explain these in detail in the relevant section.
sudo apt-get install git libboost-all-dev ruby xml-twig-tools make unzip
Here, we will create a bash script (wso2-cartridge-init.sh) with Stratos Configuration. The sub section below explain content of the wso2-cartridge-init.sh script.
Initialize variables
First, we initialize some variables, which we need in the later part of the script.
#!/bin/bash # ---------------------------------------------------------------------------- # Copyright 2005-2013 WSO2, Inc. https://www.wso2.org # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ---------------------------------------------------------------------------- export LOG=/var/log/wso2-cartridge.log instance_path=/opt PUBLIC_IP="" SLEEP_DURATION=3
Extracting payload
In the Stratos, we would be passing some values through the payload. Therefore, we need to have some mechanism to get these payload values to our script. Note that you can add some comments, and these will be useful in the case of debug.
Here, we can get payload by using EC2 meta data services (https://169.254.169.254/latest/user-data. ) In Stratos, payload is passed as a zip file. In the following script, extract all payload variables and export as the environment variables.
if [ -d ${instance_path}/payload ]; then
rm -fr ${instance_path}/payload
fi
echo "creating payload dir ... " >> $LOG
mkdir ${instance_path}/payload
echo "creating payload dir ... " >> $LOG
echo "payload dir created ... " >> $LOG
wget https://169.254.169.254/latest/user-data -O ${instance_path}/payload/payload.zip
echo "payload copied ... " >> $LOG
unzip -d ${instance_path}/payload ${instance_path}/payload/payload.zip
echo "unzipped..." >> $LOG
for i in `/usr/bin/ruby /opt/get-launch-params.rb`
do
echo "exporting to bashrc $i ... " >> $LOG
echo "export" ${i} >> /home/ubuntu/.bashrc
done
source /home/ubuntu/.bashrc
You can create /opt/get-launch-params.rb with following content. And make sure it's executable.
#! /usr/bin/ruby
### get-launch-params.rb
# The following script obtains the launch parameters from
# the file /tmp/payload/launch-params, then parses out the
# parameters for this instance by using the launch index
# of this particular EC2 instance.
#
# Pass the command the -e flag to output the instance
# parameters as exports of shell variables. Any other
# arguments are ignored.
def get_launch_params(launch_params_file)
IO.readlines launch_params_file
end
export_stmt = ""
launch_params = get_launch_params(
"/opt/payload/launch-params")
if launch_params.length > 0
instance_params_str = launch_params[0]
instance_params = instance_params_str.split(',')
export_stmt = "export " if ARGV.length > 0 && ARGV.include?("-e")
instance_params.each { |param|
puts export_stmt + param
}
end
Getting public IP
The following code will be assigned the public IP of the instance and exported as the environment variable.
echo "getting public ip from metadata service" >> $LOG
wget https://169.254.169.254/latest/meta-data/public-ipv4
files="`cat public-ipv4`"
if [[ -z ${files} ]]; then
echo "getting public ip" >> $LOG
for i in {1..30}
do
rm -f ./public-ipv4
wget https://169.254.169.254/latest/meta-data/public-ipv4
files="`cat public-ipv4`"
if [ -z $files ]; then
echo "Public ip is not yet assigned. Wait and continue for $i the time ..." >> $LOG
sleep $SLEEP_DURATION
else
echo "Public ip assigned" >> $LOG
break
fi
done
if [ -z $files ]; then
echo "Public ip is not yet assigned. So exit" >> $LOG
exit 0
fi
for x in $files
do
PUBLIC_IP="$x"
done
else
PUBLIC_IP="$files"
fi
for i in `/usr/bin/ruby /opt/get-launch-params.rb`
do
export ${i}
done
rm -f ./public-ipv4
Starting up HipHop server
To start up HipHop server add following to script
echo "Starting HipHop Server" >> $LOG /usr/bin/hhvm --mode daemon --user web --config /etc/hhvm.hdf
Clustering with ELB through the agent
Now, we add a piece of coding to handle clustering with the ELB. To keep the cartridge light weight, we have the Stratos Agent running outside. We can join the ELB through the Stratos Agent. First, create request.xml. Note that many ${} variables comes from the environment variable, which we export from the payload.
mkdir -p /etc/agent/conf
echo "<soapenv:Envelope xmlns:soapenv=\"https://schemas.xmlsoap.org/soap/envelope/\" xmlns:agen=\"https://service.agent.cartridge.carbon.wso2.org\">
<soapenv:Header/>
<soapenv:Body>
<agen:register>
<registrant>
<alarmingLowerRate>${ALARMING_LOWER_RATE}</alarmingLowerRate>
<alarmingUpperRate>${ALARMING_UPPER_RATE}</alarmingUpperRate>
<hostName>${HOST_NAME}</hostName>
<key>${KEY}</key>
<maxInstanceCount>${MAX}</maxInstanceCount>
<maxRequestsPerSecond>${MAX_REQUESTS_PER_SEC}</maxRequestsPerSecond>
<minInstanceCount>${MIN}</minInstanceCount> " > /etc/agent/conf/request.xml
IFS='|' read -ra PT <<< "${PORTS}"
for i in "${PT[@]}"; do
IFS=':' read -ra PP <<< "$i"
echo " <portMappings>
<primaryPort>${PP[1]}</primaryPort>
<proxyPort>${PP[2]}</proxyPort>
<type>${PP[0]}</type>
</portMappings>">> /etc/agent/conf/request.xml
done
echo " <remoteHost>${PUBLIC_IP}</remoteHost>
<service>${SERVICE}</service>
<remoteHost>${PUBLIC_IP}</remoteHost>
<roundsToAverage>${ROUNDS_TO_AVERAGE}</roundsToAverage>
<scaleDownFactor>${SCALE_DOWN_FACTOR}</scaleDownFactor>
<tenantRange>${TENANT_RANGE}</tenantRange>
</registrant>
</agen:register>
</soapenv:Body>
</soapenv:Envelope>
" >> /etc/agent/conf/request.xml
We can simply call CURL request to Stratos Agent to connect to ELB.
echo "Sending register request to Cartridge agent service" >> $LOG
curl -X POST -H "Content-Type: text/xml" -H "SOAPAction: urn:register" -d @/etc/agent/conf/request.xml -k --silent --output /dev/null "${CARTRIDGE_AGENT_EPR}"
Handle artifacts synchronization
Let's create repoinforequest.xml, which we will use to obtain credentials for git cloning and pulling.
echo "Creating repoinfo request " >> $LOG
echo "TENANT_ID and SERVICE ${TENANT_ID} and ${SERVICE} " >> $LOG
set -- "${HOST_NAME}"
IFS="."; declare -a Array=($*)
ALIAS="${Array[0]}"
echo "<soapenv:Envelope xmlns:soapenv=\"https://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsd=\"https://org.apache.axis2/xsd\">
<soapenv:Header/>
<soapenv:Body>
<xsd:getRepositoryCredentials>
<xsd:tenantId>${TENANT_ID}</xsd:tenantId>
<xsd:cartridgeType>${SERVICE}</xsd:cartridgeType>
<xsd:alias>${ALIAS}</xsd:alias>
</xsd:getRepositoryCredentials>
</soapenv:Body>
</soapenv:Envelope>" > /opt/repoinforequest.xml
Now, we add the code to handle git clone/pull.
echo "Git repo sync" >> $LOG
# If repo is available do a git pull, else clone
echo "#!/bin/bash
if [ -d \"${APP_PATH}/.git\" ]; then
cd ${APP_PATH}
curl -X POST -H \"Content-Type: text/xml\" -H \"SOAPAction: urn:getRepositoryCredentials\" -d @/opt/repoinforequest.xml --silent \"${REPO_INFO_EPR}\" --insecure > /tmp/git.xml
sed '1,5d' /tmp/git.xml > /tmp/git1.xml
sed '2d' /tmp/git1.xml > /tmp/git.xml
username=\`xml_grep 'ax29:userName' /tmp/git.xml --text_only\`
password=\`xml_grep 'ax29:password' /tmp/git.xml --text_only\`
repo=\`xml_grep 'ax29:url' /tmp/git.xml --text_only\`
rm /tmp/git1.xml
rm /tmp/git.xml
url=\`echo \$repo |sed 's/http.*\/\///g' |sed 's/\:.*//g' |sed 's/\/.*//g'\`
echo \"machine \${url} login \${username} password \${password}\" > ~/.netrc
sudo echo \"machine \${url} login \${username} password \${password}\" > /root/.netrc
chmod 600 ~/.netrc
sudo chmod 600 /root/.netrc
git config --global --bool --add http.sslVerify false
sudo git pull
rm ~/.netrc
sudo rm /root/.netrc
sudo chown -R web:web ${APP_PATH}
else
sudo rm -f ${APP_PATH}/index.html
curl -X POST -H \"Content-Type: text/xml\" -H \"SOAPAction: urn:getRepositoryCredentials\" -d @/opt/repoinforequest.xml --silent \"${REPO_INFO_EPR}\" --insecure > /tmp/git.xml
sed '1,5d' /tmp/git.xml > /tmp/git1.xml
sed '2d' /tmp/git1.xml > /tmp/git.xml
username=\`xml_grep 'ax29:userName' /tmp/git.xml --text_only\`
password=\`xml_grep 'ax29:password' /tmp/git.xml --text_only\`
repo=\`xml_grep 'ax29:url' /tmp/git.xml --text_only\`
rm /tmp/git1.xml
rm /tmp/git.xml
url=\`echo \$repo |sed 's/http.*\/\///g' |sed 's/\:.*//g' |sed 's/\/.*//g'\`
echo \"machine \${url} login \${username} password \${password}\" > ~/.netrc
sudo echo \"machine \${url} login \${username} password \${password}\" > /root/.netrc
chmod 600 ~/.netrc
sudo chmod 600 /root/.netrc
git config --global --bool --add http.sslVerify false
sudo git clone \${repo} ${APP_PATH}
rm ~/.netrc
sudo rm /root/.netrc
sudo chown -R web:web ${APP_PATH}
fi" > /opt/git.sh
echo "File created.." >> $LOG
chmod 755 /opt/git.sh
echo "git cloning.." >> $LOG
/opt/git.sh
Configure log publisher
Before we configure the log publisher, we have to prepare our cartridge instance to work thrift. You can download the tested version from [4].
tar zxvf thrift-0.8.0.tar.gz cd thrift-0.8.0 ./configure make make install
Let's setup logging agent. It can be downloaded from [5]
cd /opt/ wget https://svn.wso2.org/repos/wso2/carbon/platform/branches/4.1.0/build/stratos2/setup/tools/cartridge_create/init_scripts\ /php/cartridge_data_publisher_1.0.2.zip unzip cartridge_data_publisher_1.0.2.zip cd cartridge_data_publisher_1.0.2 make
We have setup all the necessary software for log publisher to BAM. To add to startup script, note that ${} variables come from payload. Moreover, make sure that you have included the correct access and error log location.
echo "setting up logging conf" >> $LOG
echo "host: ${BAM_IP}
thriftPort: ${BAM_PORT}
#cartridge configs
cartridgeAlias: ${CARTRIDGE_ALIAS}
tenantName: ${HOST_NAME}
tenantId: ${TENANT_ID}
localIP: ${PUBLIC_IP}" > /opt/cartridge_data_publisher_1.0.2/conf/data_publisher.conf
echo "started loggin ........."
cd /opt/cartridge_data_publisher_1.0.2/dist/Debug/GNU-Linux-x86/
nohup ./cartridge_data_publisher_1.0.2 /var/log/hhvm/access.log /var/log/hhvm/error.log >> /var/log/wso2-data-publisher.log &
Complete Startup Script
We have completed our startup script. If we put everything together, it will look like as shown below.
#!/bin/bash
# ----------------------------------------------------------------------------
# Copyright 2005-2013 WSO2, Inc. https://www.wso2.org
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ----------------------------------------------------------------------------
export LOG=/var/log/wso2-cartridge.log
instance_path=/opt
PUBLIC_IP=""
SLEEP_DURATION=3
if [ -d ${instance_path}/payload ]; then
rm -fr ${instance_path}/payload
fi
echo "creating payload dir ... " >> $LOG
mkdir ${instance_path}/payload
echo "creating payload dir ... " >> $LOG
echo "payload dir created ... " >> $LOG
wget https://169.254.169.254/latest/user-data -O ${instance_path}/payload/payload.zip
echo "payload copied ... " >> $LOG
unzip -d ${instance_path}/payload ${instance_path}/payload/payload.zip
echo "unzipped..." >> $LOG
for i in `/usr/bin/ruby /opt/get-launch-params.rb`
do
echo "exporting to bashrc $i ... " >> $LOG
echo "export" ${i} >> /home/ubuntu/.bashrc
done
source /home/ubuntu/.bashrc
echo "getting public ip from metadata service" >> $LOG
wget https://169.254.169.254/latest/meta-data/public-ipv4
files="`cat public-ipv4`"
if [[ -z ${files} ]]; then
echo "getting public ip" >> $LOG
for i in {1..30}
do
rm -f ./public-ipv4
wget https://169.254.169.254/latest/meta-data/public-ipv4
files="`cat public-ipv4`"
if [ -z $files ]; then
echo "Public ip is not yet assigned. Wait and continue for $i the time ..." >> $LOG
sleep $SLEEP_DURATION
else
echo "Public ip assigned" >> $LOG
break
fi
done
if [ -z $files ]; then
echo "Public ip is not yet assigned. So exit" >> $LOG
exit 0
fi
for x in $files
do
PUBLIC_IP="$x"
done
else
PUBLIC_IP="$files"
fi
for i in `/usr/bin/ruby /opt/get-launch-params.rb`
do
export ${i}
done
rm -f ./public-ipv4
echo "Starting HipHop Server" >> $LOG
/usr/bin/hhvm --mode daemon --user web --config /etc/hhvm.hdf
mkdir -p /etc/agent/conf
echo "<soapenv:Envelope xmlns:soapenv=\"https://schemas.xmlsoap.org/soap/envelope/\" xmlns:agen=\"https://service.agent.cartridge.carbon.wso2.org\">
<soapenv:Header/>
<soapenv:Body>
<agen:register>
<registrant>
<alarmingLowerRate>${ALARMING_LOWER_RATE}</alarmingLowerRate>
<alarmingUpperRate>${ALARMING_UPPER_RATE}</alarmingUpperRate>
<hostName>${HOST_NAME}</hostName>
<key>${KEY}</key>
<maxInstanceCount>${MAX}</maxInstanceCount>
<maxRequestsPerSecond>${MAX_REQUESTS_PER_SEC}</maxRequestsPerSecond>
<minInstanceCount>${MIN}</minInstanceCount> " > /etc/agent/conf/request.xml
IFS='|' read -ra PT <<< "${PORTS}"
for i in "${PT[@]}"; do
IFS=':' read -ra PP <<< "$i"
echo " <portMappings>
<primaryPort>${PP[1]}</primaryPort>
<proxyPort>${PP[2]}</proxyPort>
<type>${PP[0]}</type>
</portMappings>">> /etc/agent/conf/request.xml
done
echo " <remoteHost>${PUBLIC_IP}</remoteHost>
<service>${SERVICE}</service>
<remoteHost>${PUBLIC_IP}</remoteHost>
<roundsToAverage>${ROUNDS_TO_AVERAGE}</roundsToAverage>
<scaleDownFactor>${SCALE_DOWN_FACTOR}</scaleDownFactor>
<tenantRange>${TENANT_RANGE}</tenantRange>
</registrant>
</agen:register>
</soapenv:Body>
</soapenv:Envelope>
" >> /etc/agent/conf/request.xml
echo "Sending register request to Cartridge agent service" >> $LOG
curl -X POST -H "Content-Type: text/xml" -H "SOAPAction: urn:register" -d @/etc/agent/conf/request.xml -k --silent --output /dev/null
"${CARTRIDGE_AGENT_EPR}"
echo "Creating repoinfo request " >> $LOG
echo "TENANT_ID and SERVICE ${TENANT_ID} and ${SERVICE} " >> $LOG
set -- "${HOST_NAME}"
IFS="."; declare -a Array=($*)
ALIAS="${Array[0]}"
echo "<soapenv:Envelope xmlns:soapenv=\"https://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsd=\"https://org.apache.axis2/xsd\">
<soapenv:Header/>
<soapenv:Body>
<xsd:getRepositoryCredentials>
<xsd:tenantId>${TENANT_ID}</xsd:tenantId>
<xsd:cartridgeType>${SERVICE}</xsd:cartridgeType>
<xsd:alias>${ALIAS}</xsd:alias>
</xsd:getRepositoryCredentials>
</soapenv:Body>
</soapenv:Envelope>" > /opt/repoinforequest.xml
echo "Git repo sync" >> $LOG
# If repo is available do a git pull, else clone
echo "#!/bin/bash
if [ -d \"${APP_PATH}/.git\" ]; then
cd ${APP_PATH}
curl -X POST -H \"Content-Type: text/xml\" -H \"SOAPAction: urn:getRepositoryCredentials\" -d @/opt/repoinforequest.xml --silent
\"${REPO_INFO_EPR}\" --insecure > /tmp/git.xml
sed '1,5d' /tmp/git.xml > /tmp/git1.xml
sed '2d' /tmp/git1.xml > /tmp/git.xml
username=\`xml_grep 'ax29:userName' /tmp/git.xml --text_only\`
password=\`xml_grep 'ax29:password' /tmp/git.xml --text_only\`
repo=\`xml_grep 'ax29:url' /tmp/git.xml --text_only\`
rm /tmp/git1.xml
rm /tmp/git.xml
url=\`echo \$repo |sed 's/http.*\/\///g' |sed 's/\:.*//g' |sed 's/\/.*//g'\`
echo \"machine \${url} login \${username} password \${password}\" > ~/.netrc
sudo echo \"machine \${url} login \${username} password \${password}\" > /root/.netrc
chmod 600 ~/.netrc
sudo chmod 600 /root/.netrc
git config --global --bool --add http.sslVerify false
sudo git pull
rm ~/.netrc
sudo rm /root/.netrc
sudo chown -R web:web ${APP_PATH}
else
sudo rm -f ${APP_PATH}/index.html
curl -X POST -H \"Content-Type: text/xml\" -H \"SOAPAction: urn:getRepositoryCredentials\" -d @/opt/repoinforequest.xml --silent
\"${REPO_INFO_EPR}\" --insecure > /tmp/git.xml
sed '1,5d' /tmp/git.xml > /tmp/git1.xml
sed '2d' /tmp/git1.xml > /tmp/git.xml
username=\`xml_grep 'ax29:userName' /tmp/git.xml --text_only\`
password=\`xml_grep 'ax29:password' /tmp/git.xml --text_only\`
repo=\`xml_grep 'ax29:url' /tmp/git.xml --text_only\`
rm /tmp/git1.xml
rm /tmp/git.xml
url=\`echo \$repo |sed 's/http.*\/\///g' |sed 's/\:.*//g' |sed 's/\/.*//g'\`
echo \"machine \${url} login \${username} password \${password}\" > ~/.netrc
sudo echo \"machine \${url} login \${username} password \${password}\" > /root/.netrc
chmod 600 ~/.netrc
sudo chmod 600 /root/.netrc
git config --global --bool --add http.sslVerify false
sudo git clone \${repo} ${APP_PATH}
rm ~/.netrc
sudo rm /root/.netrc
sudo chown -R web:web ${APP_PATH}
fi" > /opt/git.sh
echo "File created.." >> $LOG
chmod 755 /opt/git.sh
echo "git cloning.." >> $LOG
/opt/git.sh
echo "setting up logging conf" >> $LOG
echo "host: ${BAM_IP}
thriftPort: ${BAM_PORT}
#cartridge configs
cartridgeAlias: ${CARTRIDGE_ALIAS}
tenantName: ${HOST_NAME}
tenantId: ${TENANT_ID}
localIP: ${PUBLIC_IP}" > /opt/cartridge_data_publisher_1.0.2/conf/data_publisher.conf
echo "started loggin ........."
cd /opt/cartridge_data_publisher_1.0.2/dist/Debug/GNU-Linux-x86/
nohup ./cartridge_data_publisher_1.0.2 /var/log/hhvm/access.log /var/log/hhvm/error.log >> /var/log/wso2-data-publisher.log &
Make the script (wso2-cartridge-init.sh) executable Add entry to /etc/rc.local to run it in the startup. Then it should look like below
#!/bin/sh -e # # rc.local # # This script is executed at the end of each multiuser runlevel. # Make sure that the script will "exit 0" on success or any other # value on error. # # In order to enable or disable this script just change the execution # bits. # # By default this script does nothing. chmod 755 /opt/wso2-cartridge-init.sh /opt/wso2-cartridge-init.sh > /var/log/wso2-openstack-init.log exit 0
We have now completed the cartridge setup. Now, make an AMI out of this instance.
Creating AMI for HHVM cartridge
Go to your AWS EC2 management console and select the created instance, click on action, and then select Create AMI.

If you wish, you can make it a public image, so others can use it as well. Here, my HHVM cartridge ami id is ami-a4c2b0cd
Register with Cloud Controller
First, you need to follow [6] and setup Stratos on EC2. Now, we can add our new HHVM cartridge. We have to create cartridge xml. (please note that we have configured our Stratos domain as stratos2-demo.com). Make sure you have changed the highlighted values according to your environments. AMI id is the HHVM cartridge ID that you have created above.
<!-- Use below section to specify properties that are needed in order to start Cartridges. --> <cartridges> <!-- You can have 1..n cartridge elements. --> <cartridge type="hhvm" host="hhvm.stratos2-demo.com" provider="hhvm" version="5.5" multiTenant="false"> <!-- cartridge element can have 0..n properties, and they'll be overwritten by the properties specified under iaasProvider child elements of cartridge element. --> <displayName>HHVM</displayName> <description>HHVM Cartridge</description> <!-- A cartridge element should add a reference to an existing IaaS provider (specified in the above <iaasProviders> section) or it can create a completely new IaaS Provider (which should have a unique "type" attribute. --> <iaasProvider type="ec2" > <imageId>us-east-1/ami-a4c2b0cd</imageId> <maxInstanceLimit>250</maxInstanceLimit> <property name="instanceType" value="m1.small"/> </iaasProvider> <deployment baseDir="/var/www"> <dir>www=copy#app#files#here</dir> </deployment> <portMapping> <http port="80" proxyPort="8280"/> </portMapping> </cartridge> </cartridges>
Create hhvm.xml with above content and copy to the following folder
/opt/wso2cc-1.0.1/repository/deployment/server/cartridges/
Now HHVM cartridge are ready to used.
Prepare Wordpress code for run in HHVM
There are some small code changes that have to be made to wordpress to run on HHVM. You can find more details on [7]. Here, we have a git repository [8], which has applied the above changes and you can directly used this.
Deploying wordpress over HHVM Cartridge
First, you have to create a tenant on Stratos. Please follow [9] to created a tenant. Then log-in using created tenant and go to Main->Available Cartridge->Single-Tenant Cartridge. Now, you can find the newly registered HHVM cartridge.
Let's subscribe to the HHVM Cartridge.

Set up your /etc/hosts file to access the subscribed cartridge. Our sample looks like as shown below.
54.227.40.254 website.hhvm.stratos2-demo.com
After subscription, the status become ACTIVE - try accessing the url (make sure to append index.php)
https://website.hhvm.stratos2-demo.com:8280/index.php
It should load the wordpress installation page. For wordpress, you need to configure MySQL database. You can subscribe to the MySQL cartridge to setup the database.
Add relevant host entries. In my sample, it look like below 54.227.40.254 webdb.mysql.stratos2-demo.com You can get MySQL credentials by clicking on the subscribe MySQL alias

Log in to Access URL (in my sample https://webdb.mysql.stratos2-demo.com:8280 ) and create a database for wordpress.
Then, continue with the wordpress installation.

Finally, you will have wordpress running inside the HHVM cartridge. Likewise, you can deploy any HipHop compatible PHP application on top of this HHVM cartridge.

References
- https://www.facebook.com/note.php?note_id=10150415177928920
- https://www.swageroo.com/wordpress/hiphop-vm-on-ec2-could-not-allocate-1210089471-bytes-for-translation-cache/
- https://github.com/facebook/hiphop-php/wiki
- https://svn.wso2.org/repos/wso2/carbon/platform/branches/4.1.0/build/stratos2/setup/tools/cartridge_create/init_scripts/php/thrift-0.8.0.tar.gz
- https://svn.wso2.org/repos/wso2/carbon/platform/branches/4.1.0/build/stratos2/setup/tools/cartridge_create/init_scripts/php/cartridge_data_publisher_1.0.2.zip
- https://docs.wso2.org/wiki/display/Stratos200/Quick+Start+Guide#QuickStartGuide-Stratos2setupwithEC2astheIaaS
- https://github.com/lakwarus/wordpresshhvm
- https://docs.wso2.org/wiki/display/Stratos200/GUI+User+Guide
Author
Lakmal Warusawithana, Software Architect, WSO2 Inc.
About Author
- Lakmal Warusawithana
- Vice President & Distinguished Engineer
- WSO2 LLC