In Part 1, we created a Docker image with MkDocs, and used the container to execute MkDocs. In this post, we will:
- Improve the Docker image with additional MkDocs features.
- Publish the image to Azure Container Registry.
- Use the container in Azure Pipelines.
- Use Azure Pipelines to publish the compiled documentation to Azure App Service.
Making Improvements
I'm not fond of MkDoc's default theme, and after trying out various themes, I've settled on Material for MkDocs. It seems to be the most feature-complete, with 3K GitHub stars and lots of extension support. (I also like Read-the-Docs theme, but it seems there are some bugs, such as code blocks rendering in a single line, and even though there's a pull-request for the fix, the developer has not merged it. That project has also been inactive for a couple of years.)
Let's change the Dockerfile to integrate the Material theme, and also to reduce some steps in preparation for Azure Pipelines. Refer to the sample GitHub repo for the project folder structure.
FROM python:3.8.1-alpine3.11 EXPOSE 8000 # Git is required for the git-revision-date plugin. RUN apk add git RUN pip install --no-cache-dir mkdocs && \ pip install --no-cache-dir mkdocs-material && \ pip install --no-cache-dir mkdocs-git-revision-date-localized-plugin && \ pip install --no-cache-dir pymdown-extensions # Note that /mnt/repo should be the git root folder, not the content folder, # otherwise the git-revision plugin will complain during build. CMD ["/bin/sh", "-c", "cd /mnt/repo/content && mkdocs build"]
I'm relatively new to Docker and python, so it's probably not the optimal Dockerfile, so will need to explore further, such as using multi-stage builds. Also should look into requirements.txt file for pip
.
Note the CMD
command in the Dockerfile – it assumes certain conventions for mounting the host directory path, so we'll need to follow it when we want to run the container in Azure Pipelines. If we don't want to follow the convention, it can be overwritten in docker run
command.
Just out of curiosity, what shell does Docker use when it's executing the RUN command? Looks like it's /bin/sh.
Publish to Azure Container Registry
We can publish the docker image to Docker Hub, but we need to keep things private, so for this demo, let's set up an Azure Container Registry to host the docker images. Follow the quick start guide to setup an Azure Container Registry to push the image we've built above.
Enable the admin user functionality, as we'll use that later in Azure Pipelines below. Note that enabling the admin user is not recommended, but good enough for this demo. Service principals is used in production.
Azure Pipelines
To setup the pipeline, first, add a secret variable to store the Azure Container Registry's admin user password, as ACR-SECRET
. Then add the following to azure-pipelines.yml
:
trigger: - master pool: vmImage: 'ubuntu-latest' steps: - script: docker login -u <acr-name> -p $(ACR-SECRET) <acr-name>.azurecr.io displayName: 'Login to Azure Container Registry' - script: docker pull <acr-name>.azurecr.io/tools/mkdocs:0.1 displayName: 'Pull MkDocs docker image' - script: docker run --rm --mount type=bind,source=$(pwd),target=/mnt/repo <acr-name>.azurecr.io/tools/mkdocs:0.1 displayName: Run MkDocs build through the docker container.
The above pipeline will login to our container registry, pull down the image, and spin up a container of that image to execute MkDocs to build the documentation. One thing that may need to be investigated is if there's a way to cache the docker image so we don't have to pull it down every time. We'll need to weigh which will be more cost effective, since pulling from the ACR may incur cost. Also note that the Azure Pipelines Build Agents already have common docker images preinstalled, so we should try to use those as base when creating the Dockerfile. We'll also need to weigh the speed of the build, since loading the cache in the pipeline can also take some time.
Now, of course, you don't have to use the docker container here. You can just install MkDocs and its plugins in the pipeline... However, since all of my team members will be using the same docker container to build and test their documentation, we can be sure that it will work the same way on this build. And yes, there are other ways of enforcing those standards even if we didn't use Docker, but let's leave it at that for now.
Publish to Azure App Service
So the pipeline above does the build but doesn't actually do anything with the output of the build. Let's publish the compiled documentation site to Azure App Service. Since the site is static, I can also do Azure Storage Static Hosting, but it seems it's always public access. With the App Service, I'll put it behind our Active Directory authentication.
Follow the documentation to create the site. The documentation doesn't go into details on how to setup a static HTML site using the Portal, so I ended up just creating a Windows .Net Framework site for this demo.
Add the following to the azure-pipelines.yml
from above:
- task: ArchiveFiles@2 inputs: rootFolderOrFile: 'content/site' includeRootFolder: false archiveType: 'zip' archiveFile: '$(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip' replaceExistingArchive: true - task: AzureRmWebAppDeployment@4 inputs: ConnectionType: 'AzureRM' azureSubscription: '<Your Subscription Here>' appType: 'webApp' WebAppName: '<app-site-name>' packageForLinux: '$(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip'
There are two additional tasks added – one to zip up the content of MkDocs build output, and the other to publish that zip file to Azure App Service.
We could also use Release Pipelines, but for this demo, it's enough to just let the build pipeline handle the deployment.
Now whenever the master branch is updated, the pipeline will do the build and publish the site.
- Link to the demo GitHub repo
Misc. Notes
The git-revision-date-localized
plugin requires git to be installed in the container, since it uses git when MkDocs builds the site. It takes the date of the commit and adds it to the compiled document, and is supported by the Material theme.
Note that there are two sections for configuring MkDocs – plugins and markdown extensions. The git-revision-date-localized
is configured under the plugin
section. Refer to the demo GitHub repo.
It's slightly concerning that MkDocs hasn't had a release since September 7th, 2018. There are some issues, such as favicon not working, where the fix has been merged but hasn't been released yet, though a workaround does exist — overwriting it after the site has been built, which I've implemented in the azure-pipelines.yml in the demo repo.
One of the big gripes when working with markdown is handling images — you can't simply paste in a screenshot, which we often need to do when creating technical documentation. Fortunately, Visual Studio Code has an extension, Markdown Paste, that seems to work well.