Running PHP applications on Azure App Engine
Azure App Service is a way to host your web application in a container without having to think about the server. It’s the same PaaS concept as AWS Elastic Beanstalk and supports all the main web programming languages. It also supports Windows and Linux OS containers.
I have a client that is moving an on-premises PHP application to App Service and so have been looking at what I needed to do to deploy it there.
To get started, Azure have a couple of great tutorials for Linux and Windows with which I got a basic app running in no time, so I could turn my attention to the specifics I cared about.
Database drivers
My app uses SQL Server, so I was pleased to see that both the Windows and Linux PHP containers have the sqlsrv extension installed. However, there’s a bug today where the Linux container is missing the ODBC driver, so SQL Server support doesn’t actually work on the Linux container. This should be fixed in July apparently.
Both containers also support PostgreSQL and MySQL, so the databases I care for are covered.
Rewriting all URLs to index.php in the public directory
In common with many PHP applications, I have a public directory where the files to be served by the web server are kept and my PHP and other source files are elsewhere. I also use rewriting to map all URLs to my public/index.php file.
The way we handle this depends on the container.
Windows container
On the Windows container, we need to change the path mapping so that the public directory is mapped to the `/`. This can be done in the web portal in Configuration -> Path mappings where by default the / virtual path is mapped to site\wwwroot physical path. Edit this so that it points to site\wwwroot\public. I’m not a fan of web-based tooling for things like this as it’s error-prone. Azure provides a command line tool, so we can make the same path mapping change with:
$ az resource update --name web --resource-group {Resource Group} \
--namespace Microsoft.Web --resource-type config --parent sites/{App Service} \
--api-version 2015-06-01 \
--set properties.virtualApplications[0].physicalPath="site\wwwroot\public"
(Change {Resource Group} and {App Service} to the correct names for your installation.)
Now that we’re serving from `/public`, we can now add the rewrite rule. With IIS, we use a web.config file:
/public/web.config:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="Pretty URLs to index.php" stopProcessing="true">
<match url="^(.*)
quot; />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
</conditions>
<action type="Rewrite" url="index.php" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
Linux container
On Linux, the container runs Apache, so we can rewrite the URL to public/index.php with a single .htaccess file in our root directory:
/.htaccess:
RewriteEngine On
RewriteBase /
# Rewrite static files that live in public/
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)\.(woff|ttf|svg|js|ico|gif|jpg|png|css|htc|xml|txt|pdf)$ /public/$1.$2 [L,NC]
# Redirect all other URLs to public/index.php
RewriteRule ^((?!public/).*)$ public/index.php/$1 [L,QSA]
Two rewrite rules are required: firstly we rewrite static files so that the continue to be served by Apache and then we rewrite everything else to index.php.
Environment variables
Environment variables are a common way to set per-installation configuration and this is done vie the portal or the command line:
$ az webapp config appsettings set --name {App Service} \
--resource-group {Resource Group} \
--settings \
ENV_VAR_1="some value" \
ENV_VAR_2="some other value"
Summary
Running a PHP application on Azure App Engine with either the Windows or Linux container is much the same as on any other hosting and seems to work well.
I've tested extensively the two approaches and it doesn't work well. The main issue is the very low I/O speed you get in those solutions. If you need to do any kind of file operation, even just storing them and then upload to a CDN it won't work properly. Most SD cards today can outperform (by far) the Azure App Service.
There's even a MS/Azure page somewhere talking about this limits.
What about a automated deployment? If you try to deploy via devops all files will get copied to site\wwwroot\public instead of site\wwwroot\public.
Quite a problem if you have other folders you need to update on the same level as 'public' like 'config'.
The only solution I came across is to run 'az resource update' twice – once before and once after the deployment…
Thanks' your solution is a great help for me.
Azure App Engine for Linux/PHP is poorly designed because you can't set the www root path. You have to use the slow windows persisted mounted 'SMB' drive for the wwwroot. Why there isn't an option for the wwwroot to be attached to the quick epmhereal drive is just beyond me. So your web apps are always slow as it relives on SMB! You can try symlinking to ephemreal but then your app will stop working on any form of update. You could build your own Docker image with the wwwroot mounted somewhere sane and try and config in a non SMB drive. But the better option is to just use AWS.