In my recent webinar on Azure Container Apps, an attendee asked if gRPC services could be exposed. Although I was pretty sure this was possible, I asked the attendee for some patience because I wanted to try this independently. Long story short, it’s possible. This post demonstrates how to expose gRPC services with a simple .NET 6 gRPC API and a corresponding client. We’ll also talk about a small glitch that may prevent your gRPC services hosted in Azure Container Apps from being accessible.

Sample Code

Although the main parts of the sample are discussed in this article, I’ve published it in my Azure Container Apps sample repository on GitHub. Check the sub-folder 004-exposing-grpc.

Create a gRPC API with .NET 6

To create a gRPC API with .NET 6 we can use the handy grpc template, which is part of the dotnet CLI.

# move to your projects folder
cd projects

# create and move into a sample folder
mkdir grpc-sample
cd grpc-sample

# create the gRPC API project
dotnet new grpc -o service -n gRPC.Service

# open the project in VSCode
code ./service 

I use the Docker extension (published from Microsoft) to containerize the gRPC API quickly. From within VSCode, invoke the Docker: Add Docker Files to Workspace command and provide the following configuration when asked by VSCode:

  • Application Plattform: .NET: ASP.NET Core
  • Operating System: Linux
  • Port: 5000
  • Include additional Docker Compose files: No

With the Dockerfile in place, you can build a container image and push it to the registry of your choice (For demonstration purposes, I’ll push the image to public Docker Hub).

# verify that you're in the service folder
pwd
# /Users/thorsten/projects/grpc-sample/service

# use your dockerhub username
DH_USERNAME=thorstenhans

# build the container image
docker build . -t $DH_USERNAME/grpc-service:0.0.1

# push the container image to Docker Hub
docker push $DH_USERNAME/grpc-service:0.0.1

Create a gRPC client with .NET 6

With the gRPC API in place, we have to create a small client to call into the gRPC API. Again, we create a simple .NET 6 application to achieve this:

# verify that you're in the sample folder
pwd
# /Users/thorsten/projects/grpc-sample

# create the gRPC client project
dotnet new console -o client -n gRPC.Client

# move into the client directory
cd client

# create a Protos subfolder
mkdir Protos

# copy protos file from the gRPC service
cp ./service/Protos/greet.proto ./Protos

# install necessary NuGet packages
dotnet add package Google.Protobuf
dotnet add package Grpc.Net.Client
dotnet add package Grpc.Tools

# open client in VSCode
code .

Next, we’ve to update greet.proto and set the desired C# namespace from gRPC.Service to gRPC.Client (find the csharp_namespace option in line 3). Finally, we have to update the gRPC.Client.csproj file and instruct gRPC tooling to generate client side code during the build. To do so, add the following ItemGroup:

 <ItemGroup>
  <Protobuf Include="Protos\greet.proto" GrpcServices="Client" />
 </ItemGroup>

Finally, we can replace the code in Program.cs and call our gRPC API:

using Grpc.Net.Client;
using gRPC.Client;

Console.WriteLine("Please provide the gRPC url:");
var url = Console.ReadLine();

if (string.IsNullOrWhiteSpace(url))
{
  Console.WriteLine("Please provide a value.");
  Environment.Exit(-1);
}

if(!url.StartsWith("https://"))
{
    url = $"https://{url}";
}

using var channel = GrpcChannel.ForAddress(url);

var client = new Greeter.GreeterClient(channel);
var reply = await client.SayHelloAsync(
         new HelloRequest { Name = "Azure Container Apps Community" });

Console.WriteLine("Greeting: " + reply.Message);
Console.WriteLine("Press any key to exit...");
Console.ReadKey();

Deploy a gRPC API to Azure Container Apps

The best option to deploy workloads to Azure Container Apps is currently Project Bicep. If you have not yet used Project Bicep to deploy an Azure Container App, check out my detailed walkthrough article. We focus on one crucial aspect of the overall Bicep configuration for this article. To expose gRPC services in Azure Container Apps (remember it is using Project Envoy under the hoods), we must instruct the ingress controller to use http2 for transport. You can configure transport by setting the corresponding property on the Microsoft.Web/containerApps instance (see properties.configuration.ingress):

resource containerApp 'Microsoft.Web/containerApps@2021-03-01' = {
  name: name
  kind: 'containerapp'
  location: location
  properties: {
    kubeEnvironmentId: containerAppEnvironmentId
    configuration: {  
      ingress: {
        external: useExternalIngress
        targetPort: containerPort
        transport: 'http2'
      }
    }
    template: {
      containers: [
        {
          image: containerImage
          name: name
          env: envVars
        }
      ]
      scale: {
        minReplicas: 0
      }
    }
  }
}

If you’ve cloned the repository, you can quickly deploy the entire sample using the following Azure CLI commands:

# go to the sample directory
cd 004-exposing-grpc

# create a resource group
az group create -n rg-aca-grpc -l northeurope

# start the infrastructure deployment
az deployment group create -g rg-aca-grpc -f ./bicep/main.bicep

Within a couple of seconds or minutes, the deployment should finish. Once finished, you can ask for the full-qualified domain name (FQDN) using az containerapp show -n grpc-service -g rg-aca-grpc --query "configuration.ingress.fqdn" -o tsv.

Call a gRPC API hosted in Azure Container Apps

Having the FQDN of our gRPC service in place, we can finally send requests to the API. To do so, copy the FQDN and run the client application:

# go to client sample directory
cd 004-exposing-grpc/client

# start the client
dotnet run

# when asked, provide the Service FQDN
Please provide the gRPC url: <YOUR_FQDN_HERE>

Within a couple of milliseconds, the client should print the service response to the terminal as shown in the following figure:

Response from a gRPC service hosted in Azure Container Apps

Gotcha: Enabling HTTP2 on existing Azure Container Apps

Currently, there is one pitfall when it comes to exposing gRPC from Azure Container Apps. Suppose you’ve created your instance of Azure Container Apps without setting transport explicitly to http2. In that case, you have to deploy a new revision of your app, or you must apply a change to environment variables or secrets. Otherwise, Azure Container Apps will not respond successfully to gRPC requests.

Recap

Exposing gRPC services in Azure Container Apps is straightforward. Just ensure that you’ve configured your Ingress controller to use http2 for transport, and you’re good to go.The minor glitch with updating existing instances of Azure Container Apps is due to the service’s preview state, and I bet the team will address this issue sooner or later.

Consider watching the Azure Container Apps GitHub repository if you want to stay current about changes and feature improvements in Azure Container Apps Apps. The team tracks progress and shares the public roadmap.