If you have been watching Connect(); or if you read the Azure blog, you should be aware by now, that Azure has recently introduced a pretty cool service called App Service on Linux. It is probably also not going to be a surprise to you if I tell you that it is being powered by Docker. And that is not the only great thing about it – you can also bring your own Docker image into App Service on Linux!
I might use ASL shortcut sometimes which refers to App Service on Linux and ASW which refers to App Service on Windows.
This article also assumes you have some elementary knowledge of Containers and Docker. If you need some quickstart, I suggest you head over to this course at Microsoft Virtual Academy.
Why is this so super cool?
One of the questions you may have is “Why do you say it is important when there is Azure Container Service?” The answer to that is very simple and very straightforward: Take the power of App Service on Windows with all of its services (scalability, custom domains, continuous integration, …) and leverage its power with your custom environment thanks to Docker.
This also allows you to rapidly deploy and scale custom containers and solutions built-in Docker.
Building your own
As such as it might seem, you can make use of the default containers (at the time of writing we have PHP, Node and .NET Core, all in multiple versions). However, you can also make use of your own in order to use a specific version, module or even custom runtime. In this article, we are going to take Facebook’s HHVM and run it on App Service on Linux.
Reusing existing containers
You may ask – why do we need to build a custom container when there is already a HHVM image on Docker hub, can’t we just use it? The answer is no. The Docker image is just basically the runtime, nothing else.
Theoretically, you could take an existing container image and use it directly on App Service on Linux, however, you may not be able to have logging configured correctly or you may not have access to the filesystem where your application is.
If you already have experience with Docker, you know you can either package your application directly into the image or you can mount it in (so Docker basically serves as environment container).
On ASL you can make use of both types. You can for example take the Adminer image and deploy it and everything will work out of box. But then, you won’t be able to access the files in the container, not even using FTP.
Storage in containers
So like I mentioned above, you can either pack your application directly into the container, which results in sort of App Service Local Cache equivalent when using ASW. In this case, you will be probably using some sort of continuous integration system like VSTS to automatically build your containers (remember that without a storage connection, any filesystem write will not be persistent).
However, in this case, we would like to leverage the regular App Service Filesystem, so we can interact with the application using FTP. When a container is deployed, ASL mounts the equivalent of D:\home path on ASW to /home (using volume mount in Docker). Now when that happens, it is up to your container to map the corresponding paths into the application. In order to understand how this works more closely, take a look at the official Dockerfile used in PHP7 container on ASL.
The important part there is following:
&& ln -s /home/site/wwwroot /var/www/html \
&& ln -s /home/LogFiles /var/log/apache2
What these two lines actually do is that they create a symbolic link between the /home/site/wwwroot and the default Apache’s /var/www/html folder. You could also take a different approach and for example specify these paths in the apache2.conf for the virtual host.
Making your own Dockerfile
On a side note, Proxygen will never be exposed directly to Internet, there is of course a loadbalancer in front of it just like with ASW, which for example handles SSL termination.
Like mentioned above in the storage part, all we have to do to make this work is to map the file system paths correctly, but unlike in the official images which using symbolic links, we are going to point directly to the mapped volume. Which means that we are not going to be changing the Dockerfile itself, but we will be changing server.ini file which serves as the configuration. After you add the correct paths to the configuration and also specify the log file, the server.ini should look like so:
; php options
pid = /var/run/hhvm/pid
; hhvm specific
hhvm.server.port = 80
hhvm.server.type = proxygen
hhvm.server.default_document = index.php
hhvm.server.error_document404 = index.php
hhvm.repo.central.path = /var/run/hhvm/hhvm.hhbc
hhvm.server.source_root = /home/site/wwwroot
; default log location is stdout/err, which is good for docker
hhvm.log.use_log_file = true
hhvm.log.file = /home/LogFiles/hhvm.log
You can also find full INI reference here in case you need to make more changes.
Building and publishing the container
Now all you need is to build the container and push it to to your repository, we will be using Docker hub for this. This is also very straightforward, so all you have to do is to first login into Docker hub:
And then, when in the folder with your Dockerfile and server.ini you just need to execute Docker’s build command:
docker build -t "hajekj/hhvm" .
Which is going to download the dependencies and build the container image for you and then all you have to do is to publish it into the Docker hub:
docker push hajekj/hhvm
After that, you should be able to see it online on your Docker hub’s profile.
In order to be able to do this, you need to have Docker installed, in order to install it you can get started here. I have been using Docker for Windows for this process. Also if you need more information about these commands, some basics can be found in the official documentation.
Using custom image on App Service on Linux
So now with the image published we can now create a website and use our custom image in it!
The process is very simple – in Azure Portal you create a new resource – Web App on Linux and specify the image name you published to Docker Hub like so:
After waiting a minute or so for the resource deployment, you can go ahead and create an index.php in the site’s FTP directory (or publish to it from Git) and with just a simple use of <?php phpinfo(); ?> you will be able to see that we have the HHVM up and running on App Service on Linux!
Regarding container lifecycle, especially updates – whenever you need to update the image (assuming you are using the latest version), you are going to have to restart the site for it to fetch the new version of the image. In case of you needing to change the image completely or changing the version, you would change it first in the Docker tab and then restart the site as well.
As for the logs, all the Docker logs get stored into regular log path which can be accessed from FTP or Kudu (/home/LogFiles/docker/). From there you can find out if there were any issues starting the container or during the runtime which is very useful when debugging custom image.
Additionally, it is always good to force the Docker image which you will be using to at least store logs into the filesystem so you can access the application logs (may it be Apache or your custom application as well) and diagnose any issues which might occur.
Personally, I believe that App Service on Linux offers a great flexibility in being able to bring your own container as well as platform-oriented features like scaling and so on and is very useful when bringing your existing workloads to Azure. Last, but not least, it also solves the issue where Node.js native modules (written only for Linux for example) didn’t work or compile on App Service on Windows.