Hosting Jekyll on Azure App Service on Linux
Tuesday, February 28, 2017
Microsoft announced Azure App Service on Linux back in September. On the surface it looks just like the App Service we know and love, except now running on Linux. But under the covers it's quite different and uses Docker extensively.
Thanks to its support of Docker, we can use almost any Linux-based Docker image in a Linux Web App. Today we'll look at how to set up App Service on Linux to build and serve a Jekyll site that is deployed continuously from GitHub.
App Service on Linux architecture
Not a ton has been shared about the architecture of App Service on Linux, so here is a simplified diagram based on how I think it works (almost certain to contain some inaccuracies):
As far as I know, the load balancer, use of IIS ARR (yep, running on Windows), and the underlying file server are almost exactly the same as App Service on Windows.
What is different is the box I labelled "App Service Plan". It's a Linux VM (or VMs) that run the Docker engine. For each Web App, there is a container that runs the main app. There's also a container for Kudu; it looks like it's using a port of Kudu that's running on Mono (?).
App Service for Linux comes with a bunch of preconfigured containers such as Node and .NET Core.
As the architecture diagram above shows, each container has its /home
directory mounted from the file share. This means while the container has everything we need to run the application, the actual application itself is located outside of the container. This allows the Kudu container to also see the application folder. It also allows continuous deployment to deploy to the shared folder and the application is automatically updated without restarting the container.
To get more insight into how these built-in containers work, their dockerfiles are all available at https://github.com/Azure-App-Service.
Custom images
Because App Service for Linux can run pretty much any Docker image on Docker Hub (public or private) and on a private Docker registry, we are able run customized versions of the built-in images or completely new images altogether.
We'll leverage this capability to host Jekyll.
Jekyll custom image
Jekyll is a static CMS. I'm currently using it for this blog. For more information on how I use Jekyll, check out my previous articles:
- Free Jekyll Blog Hosting with Amazon S3 and CloudFlare
- Add Search to a Jekyll Blog for Free with Azure Search
- Add Related Posts to a Jekyll Blog Using Azure Search
To get started, we'll create a custom image for running Jekyll. Thankfully, there's already an official image for Jekyll on Docker Hub that we can use.
The dockerfile that we'll be using is really simple:
FROM jekyll/jekyll
CMD ["jekyll", "serve", "--force_polling", "-s", "/home/site/wwwroot"]
All we have to do is add a startup command to run jekyll serve
. We're setting the source to /home/site/wwwroot
which is automatically mounted by App Service.
When it builds the site, it'll output its content to /srv/jekyll
by default and serve it from there. We could have outputted the compiled site into /home/site/wwwroot
as well, but if we have more than one instance of the container running, all of them writing to the same share might cause bad things to happen.
Now all we have to do is build the image from our dockerfile and push it to Docker Hub.
Setting up App Service on Linux
We'll start by creating a new Web App on Linux in the Azure Portal. In the "Configure container" blade, specify our custom image from Docker Hub:
Note that we could have just used the jekyll/jekyll
image directly and set the Startup Command to run what we have in our dockerfile. But I wasn't able to get it to work.
After the Web App is created, we can try browsing to it. But it won't work! This is because our container exposes port 4000, but by default App Service uses port 80. Luckily this setting is configurable.
Go to Application Settings and add a setting named PORT
and set it to 4000
. We also see that the custom image we specified is just another application setting value.
Now we check out site again and we should see a directory listing of our wwwroot
folder (it's actually copied its contents and serving it from /src/jekyll
). We can tell it's our container doing the work because it says the web server is WEBrick and it's running Ruby.
If we peek at the developer tools, we can see that it indeed is being served through IIS and we have a cookie for sticky sessions if ARR affinity is turned on in our settings.
We're now done setting up Azure App Service to serve a Jekyll site. We just have to find a site to deploy.
Continous deployment from GitHub
We'll deploy a barebones, sample Jekyll site from GitHub. To do that we need to set up Azure App Service continuous deployment. The steps to do this are exactly the same as for Windows, so there are no surprises here.
Once deployment is complete, reload our site! Jekyll should have noticed the files have changed and recompiled the site.
If we need to make and changes to our site, make the change locally and push it to GitHub. It'll automatically deploy to our Web App!
Possible improvements
I'm not sure how production-ready the WEBrick web server is. One thing we can do to improve this is use NGINX to serve the files and change the jekyll serve
to jekyll build --watch
.
Microsoft announced Azure App Service on Linux back in September. On the surface it looks just like the App Service we know and love, except now running on Linux. But under the covers it's quite different and uses Docker extensively.
Thanks to its support of Docker, we can use almost any Linux-based Docker image in a Linux Web App. Today we'll look at how to set up App Service on Linux to build and serve a Jekyll site that is deployed continuously from GitHub.
App Service on Linux architecture
Not a ton has been shared about the architecture of App Service on Linux, so here is a simplified diagram based on how I think it works (almost certain to contain some inaccuracies):
As far as I know, the load balancer, use of IIS ARR (yep, running on Windows), and the underlying file server are almost exactly the same as App Service on Windows.
What is different is the box I labelled "App Service Plan". It's a Linux VM (or VMs) that run the Docker engine. For each Web App, there is a container that runs the main app. There's also a container for Kudu; it looks like it's using a port of Kudu that's running on Mono (?).
App Service for Linux comes with a bunch of preconfigured containers such as Node and .NET Core.
As the architecture diagram above shows, each container has its /home
directory mounted from the file share. This means while the container has everything we need to run the application, the actual application itself is located outside of the container. This allows the Kudu container to also see the application folder. It also allows continuous deployment to deploy to the shared folder and the application is automatically updated without restarting the container.
To get more insight into how these built-in containers work, their dockerfiles are all available at https://github.com/Azure-App-Service.
Custom images
Because App Service for Linux can run pretty much any Docker image on Docker Hub (public or private) and on a private Docker registry, we are able run customized versions of the built-in images or completely new images altogether.
We'll leverage this capability to host Jekyll.
Jekyll custom image
Jekyll is a static CMS. I'm currently using it for this blog. For more information on how I use Jekyll, check out my previous articles:
- Free Jekyll Blog Hosting with Amazon S3 and CloudFlare
- Add Search to a Jekyll Blog for Free with Azure Search
- Add Related Posts to a Jekyll Blog Using Azure Search
To get started, we'll create a custom image for running Jekyll. Thankfully, there's already an official image for Jekyll on Docker Hub that we can use.
The dockerfile that we'll be using is really simple:
FROM jekyll/jekyll
CMD ["jekyll", "serve", "--force_polling", "-s", "/home/site/wwwroot"]
All we have to do is add a startup command to run jekyll serve
. We're setting the source to /home/site/wwwroot
which is automatically mounted by App Service.
When it builds the site, it'll output its content to /srv/jekyll
by default and serve it from there. We could have outputted the compiled site into /home/site/wwwroot
as well, but if we have more than one instance of the container running, all of them writing to the same share might cause bad things to happen.
Now all we have to do is build the image from our dockerfile and push it to Docker Hub.
Setting up App Service on Linux
We'll start by creating a new Web App on Linux in the Azure Portal. In the "Configure container" blade, specify our custom image from Docker Hub:
Note that we could have just used the
jekyll/jekyll
image directly and set the Startup Command to run what we have in our dockerfile. But I wasn't able to get it to work.
After the Web App is created, we can try browsing to it. But it won't work! This is because our container exposes port 4000, but by default App Service uses port 80. Luckily this setting is configurable.
Go to Application Settings and add a setting named PORT
and set it to 4000
. We also see that the custom image we specified is just another application setting value.
Now we check out site again and we should see a directory listing of our wwwroot
folder (it's actually copied its contents and serving it from /src/jekyll
). We can tell it's our container doing the work because it says the web server is WEBrick and it's running Ruby.
If we peek at the developer tools, we can see that it indeed is being served through IIS and we have a cookie for sticky sessions if ARR affinity is turned on in our settings.
We're now done setting up Azure App Service to serve a Jekyll site. We just have to find a site to deploy.
Continous deployment from GitHub
We'll deploy a barebones, sample Jekyll site from GitHub. To do that we need to set up Azure App Service continuous deployment. The steps to do this are exactly the same as for Windows, so there are no surprises here.
Once deployment is complete, reload our site! Jekyll should have noticed the files have changed and recompiled the site.
If we need to make and changes to our site, make the change locally and push it to GitHub. It'll automatically deploy to our Web App!
Possible improvements
I'm not sure how production-ready the WEBrick web server is. One thing we can do to improve this is use NGINX to serve the files and change the jekyll serve
to jekyll build --watch
.