Creating a HipHop Virtual Machine (HHVM) Cartridge for Stratos 2.0
- Lakmal Warusawithana
- Senior Director - Cloud Architecture - WSO2
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.