A while ago I had an issue where a LAMP application was being migrated to run on Azure App Service. One of the challenges that we had was that the disk IO performance was lower than the app was using previously before the migration to Azure resulting in very slow page loads. The app is pretty IO intensite and if I remember correctly the page loads were normally in the region of 1 sec but when we moved to app service they were taking 12-15 sec.
The problem here is that by default the deployment to App Service puts your code on a remote storage which is mounted on each node under the hood on the app service plan so any disk operations against the directory the code is in will be inherently slow as you have the network latency to the remote storage.
If your using a Windows based App Service Plan then you can configure a feature called local cache very easily with an app setting which will copy the code to the local disk on each node which significantly improves the disk IO. Unfortunately this feature isnt available on Linux based web apps.
You do have options thou and I wanted to share what we did. You really have 2 options:
- Option A = Create your own docker image
- Option B = Run a custom start up command
Option A is pretty straightforward if your already using docker regularly. App Service supports bringing your own image which you can have in a container registry and use for your Web App. However for this project the team werent that used to setting up docker images and we were reluctant to introduce another new technology on top of the migration from IaaS to PaaS and the migration from AWS to Azure. There was already a lot to learn and introducing more new tech would just add to the challenge.
The customizations we wanted to make to the Web App were not very complicated so we felt this could easily be done via a custom start up script. We added a script to the repo which we then configured in the Web App general settings like below.
In the command we add a bash command to run our start up script.
What happens now is that when App Service starts up a node it will trigger the start up script which will execute the actions we needed to optimize the node for the application. The things we wanted to do were as follows:
- Create a folder on the local disk
- Copy the code from the shared storage to the folder on the local disk
- Change the /var/www/html/wwwroot symlink to point to the local folder
- Copy across our own apache2.conf file to the apache folder on the node
Below is an example of the code you could use in the script.
#Make a directory on the local disk to run the code from mkdir -p /usr/local/cachedapp/ #Copy the contents from shared storage to a folder on local disk cp -R /home/site/wwwroot /usr/local/cachedapp/wwwroot #Change the symlink to point to the local folder unlink /var/www/html/wwwroot ln -s /usr/local/cachedapp/wwwroot /var/www/html/wwwroot #Copy the apache2.conf file from our code to the local disk cp /home/site/wwwroot/apache2.conf /etc/apache2/
Note: Depending on what your app does you might also need to apply a few folder/file permissions too in the start up script.
In our apache config we wanted to do a few optimizations like removing/adding some headers to harden the security and a few other optimizations.
When we deployed the code from our CI/CD pipeline we were using run from package so our app is mounted from the zip file to shared storage and then the app service will spin up a new node which will run our start up script as it spins up the node and apply the hardening and optimizations for performance.
At the end we had a pretty straightforward way to use the out of the box linux image on Azure App Service to run a LAMP app and just tweaked it with out script and it was running very nicely for us with minimal overhead. We also got out page load times to be back under 1 second.