🌚

Hey Folks!

How to Deploy Serverless Nodejs/Express app to Azure Container Apps from GitHub

In 2019, I dmed Azure Support to ask about how we can run serverless containerized workloads on Azure.

A screenshot from my Twitter chat with Azure Support

After two years, Microsoft Azure has finally released Azure Container apps. Azure Container apps allow you to deploy containers the serverless way at scale. Now, you don’t have to worry about the underlying infrastructure and you don’t even need to know Kubernetes. It supports any programming language and framework, autoscales on demand, and even more powerful features.

If you can write a Dockerfile, then you can use the Azure container apps. In this article, we are going to deploy a Nodejs app to Azure Container apps from GitHub. So let’s get right into it!

Prerequisites

  • A Microsoft Azure Account
  • Microsoft Azure CLI
  • NPM and Nodejs
  • A Github Account

Step 1

Install and run express application generator following the steps below:

  • Run the command below to install it

    mkdir container-svc && cd container-svc && npx express-generator
  • Install the npm dependencies

  • npm install
  • Start the application

  • npm start

You should see an output that looks similar to this:

A terminal showing a result

  • Now, open your browser and type in localhost:3000. You should see the output look like this:

A chrome browser showing some webpage

Well done. You are awesome! Let’s keep going!

Step 2

Create a Public repository to push your Node.js code to GitHub following the steps below:

  • Copy and paste this command below:

    echo "# Nodejs-azure-container-app" >> README.md
    git init
    echo "node_modules" > .gitignore
    git add .
    git commit -m "first commit"
    git branch -M main
    git remote add origin https://github.com/nerdeveloper/nodejs-azure-container-app.git # add your repo URL here
    git push -u origin main
  • The image below shows the structure git push Nodejs app on GitHub.

    The Github image showing Nodejs Structure

Step 3

Let’s create a Dockerfile for our Node.js app and Push it to GitHub following the steps below:

  • Create a Dockerfile in the root of the folder.

    touch Dockerfile
  • Copy and paste the following commands to the Dockerfile.

    FROM node:16-alpine
    WORKDIR /usr/src/app
    COPY package*.json ./
    RUN npm install
    COPY . .
    EXPOSE 3000
    CMD ["npm", "start"]
  • Run this command to exclude node_modules in the .dockerignore when building the docker image.

    echo "node_modules" > .dockerignore
  • Push the updated code back to GitHub.

    git add .
    git commit -am "add docker components."
    git push origin main

Step 4

Build the image and Push it to a Docker Registry following the steps below:

  • Log into your docker Registry.

    docker login

    This command shows a prompt for a username and password. If you don’t have a docker account, kindly create one here

  • Let’s build the image

    docker build -t nerdeveloper/nodejs-containerapp . # Change nerdeveloper to your docker username 
  • Finally, we will push the image to our docker registry

    docker push nerdeveloper/nodejs-containerapp # change nerdeveloper to your docker username

    If you are not familiar with docker or creating Dockerfile(s). Please check out this article.

Step 5

Create a Resource Group on Azure by running the command below. The command will create a group in North Europe.

az group create -l northeurope -n nodejs-container-app 

You will see the output below

{
  "id": "/subscriptions/786180ec-7ebe-4ada-8cd4-4201fd5a426c/resourceGroups/nodejs-container-app",
  "location": "northeurope",
  "managedBy": null,
  "name": "nodejs-container-app",
  "properties": {
    "provisioningState": "Succeeded"
  },
  "tags": null,
  "type": "Microsoft.Resources/resourceGroups"
}

Step 6

Create an Azure container registry. It is the service that will store your docker images. Run the following command below:

az acr create --resource-group nodejs-container-app \
 --name NodejsExpressAcr --sku Basic

You will see the output below:

{
  "adminUserEnabled": false,
  "anonymousPullEnabled": false,
  "creationDate": "2021-11-04T01:20:04.834773+00:00",
  "dataEndpointEnabled": false,
  "dataEndpointHostNames": [],
  "encryption": {
    "keyVaultProperties": null,
    "status": "disabled"
  },
  "id": "/subscriptions/786180ec-7ebe-4ada-8cd4-4201fd5a426c/resourceGroups/nodejs-container-app/providers/Microsoft.ContainerRegistry/registries/NodejsExpressAcr",
  "identity": null,
  "location": "northeurope",
  "loginServer": "nodejsexpressacr.azurecr.io",
  "name": "NodejsExpressAcr",
  "networkRuleBypassOptions": "AzureServices",
  "networkRuleSet": null,
  "policies": {
    "exportPolicy": {
      "status": "enabled"
    },
    "quarantinePolicy": {
      "status": "disabled"
    },
    "retentionPolicy": {
      "days": 7,
      "lastUpdatedTime": "2021-11-04T01:20:08.687607+00:00",
      "status": "disabled"
    },
    "trustPolicy": {
      "status": "disabled",
      "type": "Notary"
    }
  },
  "privateEndpointConnections": [],
  "provisioningState": "Succeeded",
  "publicNetworkAccess": "Enabled",
  "resourceGroup": "nodejs-container-app",
  "sku": {
    "name": "Basic",
    "tier": "Basic"
  },
  "status": null,
  "systemData": {
    "createdAt": "2021-11-04T01:20:04.834773+00:00",
    "createdBy": "odirionye@gmail.com",
    "createdByType": "User",
    "lastModifiedAt": "2021-11-04T01:20:04.834773+00:00",
    "lastModifiedBy": "odirionye@gmail.com",
    "lastModifiedByType": "User"
  },
  "tags": {},
  "type": "Microsoft.ContainerRegistry/registries",
  "zoneRedundancy": "Disabled"
}

Step 7

Setup the Azure container app and deploy the Nodejs app following the steps below:

  • Install the Azure Container apps extension.

    az extension add --source https://workerappscliextension.blob.core.windows.net/azure-cli-extension/containerapp-0.2.0-py2.py3-none-any.whl
  • Register the Microsoft.Web namespace.

    az provider register --namespace Microsoft.Web
  • Export the following enviroment variables.

    export RESOURCE_GROUP=nodejs-container-app
    export LOCATION=northeurope
    export LOG_ANALYTICS_WORKSPACE=nodejs-container-app-logs
    export CONTAINERAPPS_ENVIRONMENT=container-app-env

Step 8

Create an app environment for the Nodejs app following the steps below:

  • Create a new Log Analytics workspace.

    az monitor log-analytics workspace create --resource-group $RESOURCE_GROUP --workspace-name $LOG_ANALYTICS_WORKSPACE
  • Get the Log Analytics Client ID and client secret.

    • Run this command first

      LOG_ANALYTICS_WORKSPACE_CLIENT_ID=`az monitor log-analytics workspace show --query customerId -g $RESOURCE_GROUP -n $LOG_ANALYTICS_WORKSPACE --out tsv`
    • Run this command next

      LOG_ANALYTICS_WORKSPACE_CLIENT_SECRET=`az monitor log-analytics workspace get-shared-keys --query primarySharedKey -g $RESOURCE_GROUP -n $LOG_ANALYTICS_WORKSPACE --out tsv`
  • Create the environment.

      az containerapp env create \
    --name $CONTAINERAPPS_ENVIRONMENT \
    --resource-group $RESOURCE_GROUP \
    --logs-workspace-id $LOG_ANALYTICS_WORKSPACE_CLIENT_ID \
    --logs-workspace-key $LOG_ANALYTICS_WORKSPACE_CLIENT_SECRET \
    --location "$LOCATION"

    You should see an output like this:

    Command group 'containerapp env' is in preview and under development. Reference and support levels: https://aka.ms/CLI_refstatus
    {
      "aksResourceId": null,
      "appLogsConfiguration": {
        "destination": "log-analytics",
        "logAnalyticsConfiguration": {
          "customerId": "1b409e45-6bdb-496a-9f6c-78446cdf07b3",
          "sharedKey": null
        }
      },
      "arcConfiguration": null,
      "containerAppsConfiguration": {
        "aciSubnetResourceName": null,
        "appSubnetResourceId": null,
        "controlPlaneSubnetResourceId": null,
        "daprAIInstrumentationKey": null,
        "subnetResourceId": null
      },
      "defaultDomain": "politesky-63867f57.northeurope.azurecontainerapps.io",
      "deploymentErrors": null,
      "extendedLocation": null,
      "id": "/subscriptions/786180ec-7ebe-4ada-8cd4-4201fd5a426c/resourceGroups/nodejs-container-app/providers/Microsoft.Web/kubeEnvironments/nodejs-container-app-env",
      "internalLoadBalancerEnabled": null,
      "kind": "containerenvironment",
      "kubeEnvironmentType": "managed",
      "location": "northeurope",
      "name": "nodejs-container-app-env",
      "provisioningState": "Succeeded",
      "resourceGroup": "nodejs-container-app",
      "staticIp": "13.74.44.20",
      "tags": null,
      "type": "Microsoft.Web/kubeenvironments"
    }

Step 9

Now! we can fire up the container app following the steps below:

  • Run the following command.

      az containerapp create \
    --name container-app \
    --resource-group $RESOURCE_GROUP \
    --environment $CONTAINERAPPS_ENVIRONMENT \
    --image docker.io/nerdeveloper/nodejs-containerapp \ # Replace with your image name 
    --target-port 3000 \ # Replace with your custom port
    --ingress 'external' \
    --query configuration.ingress.fqdn

    This command should return a URL similar to this:

    "container-app.victoriousocean-4a8f30a3.northeurope.azurecontainerapps.io"
  • Copy the URL into your browser and you should now see an express homepage

Screenshot of an express app

  • Destroy all the services when you are done.

    az group delete --name $RESOURCE_GROUP

Conclusion

You should note that Azure Container Apps is not production-ready, but you can still play around with it. Yes! We were able to deploy a simple Nodejs/express app to Azure Container apps and I am as excited you are about this serverless service. You should check out the Docs to learn more. I’d love to hear from you about your experience. Cheerio!

— Nov 12, 2021