Blog Post

Apps on Azure Blog
6 MIN READ

Path and hostname-based routing in Azure Container Apps with NGINX

Cary_Chai's avatar
Cary_Chai
Icon for Microsoft rankMicrosoft
Mar 01, 2024

Azure Container Apps is a fully managed serverless container service that enables you to deploy and run containerized applications without having to manage the infrastructure.

 

By default, HTTP apps in Azure Container Apps are accessible via a public URL that is unique to the app. However, you can create a container app to use a reverse proxy like NGINX to control how traffic is routed to multiple apps based on the path or hostname.

 

In this tutorial, you'll learn how to use Azure Container Apps to configure path and hostname-based routing for a set of containerized applications using NGINX as a reverse proxy. You'll deploy 4 applications: 1 NGINX container which will be publicly exposed and 3 container apps which will only be accessible from within the environment and that traffic will be routed to from the NGINX container.

 

Architecture Diagram

 

Prerequisites

Deploy Azure Container Apps resources

  1. Configure environment variables for the various resources you'll deploy:

    RESOURCE_GROUP_NAME=path-based-routing-rg
    LOCATION=northeurope
    STORAGE_ACCOUNT_NAME=pathbasedrouting$RANDOM
    ENVIRONMENT_NAME=path-based-routing

    Note: $RANDOM is a bash variable that returns a random number and is used here to generate a storage account that is globally unique within Azure. If it's not available in your shell, use another unique value for the STORAGE_ACCOUNT_NAME variable.

  2. Create a resource group:

    az group create --name $RESOURCE_GROUP_NAME --location $LOCATION

     

  3. Create an Azure Container Apps environment:

    az containerapp env create --name $ENVIRONMENT_NAME \
        --resource-group $RESOURCE_GROUP_NAME --location $LOCATION

     

  4. Create two container apps in the environment:

    az containerapp create --name app1 --environment $ENVIRONMENT_NAME \
        --resource-group $RESOURCE_GROUP_NAME \
        --ingress internal --target-port 80
    
    az containerapp create --name app2 --environment $ENVIRONMENT_NAME \
        --resource-group $RESOURCE_GROUP_NAME \
        --ingress internal --target-port 80

    This will create two container apps, app1 and app2. Both apps are configured to not be publicly accessible and are only accessible within the environment. The only exposed public endpoint is from the NGINX app.

  5. Create a container app running NGINX:

    az containerapp create --name nginx --environment $ENVIRONMENT_NAME \
        --resource-group $RESOURCE_GROUP_NAME \
        --ingress external --target-port 80 --image nginx

    This will create a container app running NGINX. The app is publicly accessible and is accessible from the internet. The NGINX app will be used as a reverse proxy to route traffic to the other two apps.


    The command should print the public URL of the NGINX app. Navigate to it to verify that the app is running.

Now that the Container Apps resources are created, you can proceed to configure the path-based routing.

 

Configure path-based routing

To configure path-based routing, you'll create an NGINX configuration file that defines the routing rules and upload it to an Azure File Share. Then you'll mount the file share to the NGINX container app.

 

  1. Create a storage account to store the NGINX configuration file:

    az storage account create --name $STORAGE_ACCOUNT_NAME \
        --resource-group $RESOURCE_GROUP_NAME --location $LOCATION \
        --sku Standard_LRS

     

  2. Create a file share in the storage account:

    az storage share create --name nginx-config --account-name $STORAGE_ACCOUNT_NAME

     

  3. In the current directory, create a new file called nginx.conf with the following content:

    events {
    }
    
    http {
        server {
            listen 80;
            location /app1/ {
                proxy_http_version 1.1;
                proxy_pass http://app1/;
            }
            location /app2/ {
                proxy_http_version 1.1;
                proxy_pass http://app2/;
            }
        }
    }

    This NGINX configuration file defines two locations, /app1/ and /app2/, and routes traffic to the app1 and app2 container apps respectively using their internal URLs, http://app1/ and http://app2/.

  4. Upload the NGINX configuration file to the file share:

    az storage file upload --account-name $STORAGE_ACCOUNT_NAME --share-name nginx-config \
        --source nginx.conf --path nginx.conf

     

  5. Get the access key for the storage account:

    STORAGE_ACCOUNT_KEY=$(az storage account keys list --account-name $STORAGE_ACCOUNT_NAME \
        --resource-group $RESOURCE_GROUP_NAME --query "[0].value" --output tsv | tr -d '\r')
    
     
  6. Configure the file share in the Container Apps environment:

    az containerapp env storage set \
        --name $ENVIRONMENT_NAME --resource-group $RESOURCE_GROUP_NAME \
        --storage-name nginx-config \
        --account-name $STORAGE_ACCOUNT_NAME \
        --azure-file-account-key $STORAGE_ACCOUNT_KEY --azure-file-share-name nginx-config \
        --access-mode ReadOnly

     

  7. Export the YAML from the NGINX container app:

    az containerapp show --name nginx --resource-group $RESOURCE_GROUP_NAME \
        --output yaml > nginx.yaml

     

  8. Open nginx.yaml in a text editor. Add the volumes array to the template section to mount the Azure File Share to the NGINX container app. Then add the volumeMounts array to the containers array to mount the volume to the NGINX container. The modified YAML should look like this snippet:

    // ...
    properties:
      // ...
      template:
        containers:
        - image: nginx
          name: nginx
          resources:
            cpu: 0.5
            memory: 1Gi
          volumeMounts:
          - mountPath: /etc/nginx/nginx.conf
            subPath: nginx.conf
            volumeName: nginx-config
        scale:
          maxReplicas: 10
          minReplicas: 0
        serviceBinds: null
        terminationGracePeriodSeconds: null
        volumes:
        - name: nginx-config
          storageType: AzureFile
          storageName: nginx-config
    // ...

     

  9. Update the NGINX container app with the modified YAML:

    az containerapp update --name nginx --resource-group $RESOURCE_GROUP_NAME \
        --yaml nginx.yaml
    This will update the NGINX container app to use the NGINX configuration file from the Azure File Share.

  10. Verify that the path-based routing is working. Navigate to the public application URL of the NGINX app.
     
    If you need to get the NGINX container's URL, run the following command. Your NGINX app's URL is https://[your nginx's fqdn]/.
    az containerapp ingress show --name nginx --resource-group $RESOURCE_GROUP_NAME
     
    Append /app1/ and /app2/ to the URL to verify that the traffic is being routed to the app1 and app2 container apps respectively (Ex: [your NGINX Application URL]/app1/).
     
    Select Show more info about this app to see which container app is being used.

     

Now that you've deployed your NGINX container and are routing based on paths to your container apps, you'll learn how to update the routing configuration in your NGINX container.

 

Update the NGINX configuration

In order to change how the NGINX container handles routing, you'll need to follow steps 3-4 in Configure path-based routing to modify the nginx.conf and reupload it to the file share.

 

You'll need to restart the NGINX container app to apply the updated routing changes.

az containerapp revision restart --name nginx --resource-group $RESOURCE_GROUP_NAME \
--revision $(az containerapp revision list -n nginx -g $RESOURCE_GROUP_NAME --query '[0].name' -o tsv | tr -d '\r')
 
In the next section, you'll learn how to use the same NGINX container to configure hostname-based routing for your containers, and we'll walk you through an example of updating your nginx.conf.
 

Configure hostname-based routing

In addition to routing traffic based on paths, you can also configure NGINX to route traffic based on the hostname. To do this, use multiple server blocks in the NGINX configuration file, each with a different server_name directive. This example builds off the previous Configure path-based routing section.

 

  1. For the hostname-based routing, you'll create a third application which can be done using the following command:
    az containerapp create --name app3 --environment $ENVIRONMENT_NAME \
        --resource-group $RESOURCE_GROUP_NAME \
        --ingress internal --target-port 80​

     

    To configure your NGINX container for hostname-based routing, you'll need to update the nginx.conf and upload it to your file share like you did in steps 3-4 from the Configure path-based routing section by updating the nginx.conf and uploading it to your file share. The steps are shown below.

  2. Modify the nginx.conf to add additional domains to the nginx app. In the below example, we've added additional domains to the NGINX app which inform how traffic is routed to apps 1, 2, and 3. Traffic to nginx.proudgrass-abcdefgh.northeurope.azurecontainerapps.io is routed to app1 and app2, while traffic to path-based-routing.anthonychu.dev is routed to app3.

     

    Note the server_names_hash_bucket_size 128; directive. This is sometimes required when using a large number of server names, or in this case, when using a long domain name like the default one provided by Azure Container Apps.

    events {
    }
    
    http {
        server_names_hash_bucket_size 128;
    
        server {
            listen 80;
            server_name nginx.proudgrass-abcdefgh.northeurope.azurecontainerapps.io;
    
            location /app1/ {
                proxy_http_version 1.1;
                proxy_pass http://app1/;
            }
            location /app2/ {
                proxy_http_version 1.1;
                proxy_pass http://app2/;
            }
        }
        
        server {
            listen 80;
            server_name path-based-routing.anthonychu.dev;
            
            location /app3/ {
                proxy_http_version 1.1;
                proxy_pass http://app3/;
            }
        }
    }

     

  3.  Then, run the following command to upload your nginx.conf changes to the file share.
    az storage file upload --account-name $STORAGE_ACCOUNT_NAME --share-name nginx-config \
        --source nginx.conf --path nginx.conf​

     

  4. You'll need to restart the NGINX container app to apply the updated routing changes.
    az containerapp revision restart --name nginx --resource-group $RESOURCE_GROUP_NAME \
    --revision $(az containerapp revision list -n nginx -g $RESOURCE_GROUP_NAME --query '[0].name' -o tsv | tr -d '\r')​

     

  5. Verify that the path-based routing is working. Navigate to the URL configured with the server_name for app3 and provide the path /app3/.

    Select
     Show more info about this app to see which container app is being used with the hostname path-based-routing.anthonychu.dev instead of the default hostname for the NGINX app.

 

Congratulations!

You have now successfully setup both path and hostname-based routing with an NGINX container for your container apps! Please comment below to let us know what you think of the experience.

 

 

 

Updated Mar 01, 2024
Version 1.0
  • Sam Goldmann's avatar
    Sam Goldmann
    Copper Contributor

    This solution doesn't address scaling or resiliency which makes it nearly useless unfortunately.

  • Hi Cary_Chai

    We've requirement for header-based routing between aws and azure. Our application is deployed in azure container apps and also in aws. Based on request header value, the request should be redirect to either azure application gateway or to RDP gateway in aws. Then the gateway can forward the request to required backend pools.
    Could you please confirm if Nginx can help to satisfy this requirement?
    If yes, please let us know where the nginx can be installed (inside aca or azure vm).

    Regards,

    Subhiksha.