azureappservice lets you manage Azure App Service (AAS), a Platform-as-a-Service (PaaS) and Container-as-a-Service (CaaS) from Microsoft.
You can use it to deploy a containerised shiny app to the cloud.
Cloud services such as AAS can rack up unexpected bills, if you don’t manage costs carefully. If you are not familiar with cloud services, or would like a solution with a simpler way to control costs, consider Posit’s shinyapps.io.
Prerequisites
- A dockerised shiny app. You must ship your shiny app inside a docker container, where the app has all the dependencies it needs to run. You can use muggle (recommended), rocker/shiny or roll your own.
- An azure account with an active subscription.
Usage
You can deploy a shiny app to AAS from CI (GitHub Actions), or using your local computer (Shell).
Deploying from CI is recommended to let you reap the benefits of fully automated continuous integration and continuous delivery (CI/CD). If you deploy from CI, every git push
to the appropriate branch will be deployed to production.
You can use your local shell as backstop or for a quicker turnaround during debugging.
Log In to Azure
You first need to authenticate into Azure to be able to make changes to Azure resources.
Sign in interactively with Azure CLI.
Use the Azure Login GitHub Action. For maximum security and convenience, use OpenID Connect (OIDC) based Federated Identity Credentials (or Workflow Identify Federation, WIF).
To authenticate via WIF, you need to complete two steps on the Azure side:
- Create an app registration for GitHub. You only need one app registration for all your deployments from GitHub Actions to Azure. (Though there might be a limit of 20 federated credentials for each app registration).
-
For each combination of GitHub organisation/repository (
octouser/octoproject
) and environment (say,production
) create one federated credential.The values for organization/repository and environment must match those in the GitHub repo from which you want to deploy. You can add several of these federated credentials; without storing additional secrets on GitHub. All federated credentials on the same app registration use the same secrets as created in the above.
At this point in the setup, the app you registered in the above has no assigned roles and cannot do anything on Azure. As a result, you may get this error message if you run the Azure Login GitHub Action:
This will be fixed below, where you’ll give the app registration the necessary privileges.
If you want to run the Azure Login GitHub Action as is, pass the allow-no-subscriptions: true
argument.
Create an Azure Container Registry (Once)
The easiest and safest way for AAS to retrieve container images is from Azure’s own container registry (ACR). If you don’t already have an instance, set up a container registry.
There is nothing specifically shiny-related about the required ACR instance, and any configuration will do. Consequently, no ARM template is included here. If your organisation already has an ACR instance, please use that or defer to your organisations needs and policies in configuring a new one.
Login to ACR
Log in to ACR, leveraging the above login to Azure. This step is necessary to allow docker
to speak to your ACR instance.
No extra step necessary.
If you’re using OIDC (recommended) as per the above, give your app AcrPush
privileges on your registry using Azure’s access control.
Push runner
Image to ACR (Every Commit)
Push the image which contains the shiny app (runner
) to ACR.
Create an Azure App Service Plan (Once)
The Azure App Service plan (ASP) is the compute resource running the container with your shiny app. Loosely, it is the host virtual machine (VM) on which docker run
is executed.
Create an ASP with Linux as the host operating system.
Like the ACR instance above, Shiny places no special demands on the ASP. The best SKU and other settings depend on your organisations policies and individual use case.
Contra other Container-as-a-Service (CaaS) offerings, Azure allows you to run entirely separate (dockerised) web apps inside one ASP, sharing compute resources. For example, you could run several unrelated shiny apps in the same ASP.
Hosting several shiny apps on the same ASP can be a cost-effective way to host shiny, especially when you expect uncorrelated and limited traffic on each of the apps.
If you deploy several shiny apps to the same ASP, the apps are isolated in their own docker containers. For example, if the R session of one shiny app is busy, the other apps on the same ASP should still be responsive.
However, these apps and their containers still share the same physical resources, and high resource use of one app may spill over into another. Memory usage in particular can be a bottleneck on the lower-powered ASP SKUs; if one shiny app exhausts the ASP’s memory, other apps may become unavailable.
Create a Web App (Once per App)
You’re now ready to create the (dockerised) web app which will drive your shiny app.
The easiest way to make sure all the settings are correct is to use the Azure Resource Manager (ARM) bicep template included with this package (inst/arm
).
- Copy
template.bicep
to the repository with your shiny app. To avoidR CMD check
warning messages, place it insideinst/arm
. - Copy
template.parameters.json
and replace the values.
az deployment group create \
--resource-group marketing \
--template-file inst/arm/template.bicep \
--parameters inst/arm/template.parameters.json
For extra security, you will have to enter your Azure subscription at the prompt.
Use the arm-deploy GitHub Action to deploy. See .github/workflows/cicd.yaml
for an example.
There’s no need to recreate the (identical) app in CI on every commit, but it’s also harmless to do so. Recreating the app on every commit ensures that the settings
Update the Web App (Every Commit)
To put the latest version of your dockerised shiny app in production, simply restart the web app; it will then pull the current image under the appropriate tag.
For example,
Give the above created app registration for GitHub Actions write privileges on the web app using the web apps access control (IAM) settings. Choose Contributor
as a role.
You can then run the same command as in your shell; Azure CLI is already installed on most GitHub runners.
For example:
Installation
Install the development version from GitHub with:
remotes::install_github("dataheld/azureappservice")
You need not take on azureappservice as a runtime dependency (in your DESCRIPTION
s Imports
field), because the package is typically only needed during deployment. Consider adding it as an optional Suggests
dependency, or add it separately to the compute environment from which you deploy.