Local Development with Azure API Management Gateway
This was rather tricky to figure out so I thought I would share my experience with setting up Visual Studio with a local development subscription of Azure API Management (APIM) Gateway. It turns out that running a local copy of the Azure API Management Gateway is possible through either Docker or Kubernetes. Since this is for local development I will demonstrate this using Docker. A self-hosted gateway can be used for local development purposes or an on-prem solution. Some key understandings about Azure API Management (APIM) are that it will poll Azure every 10 seconds on port 443 to look for changes to the Gateway and registered APIs every 10 seconds.
Note: I’m going to demonstrate two ways of doing this; running the API application with Kestrel on the host machine and running the API application within a Docker container.
Sample Source Code
Why Do Any of This?
There are many reasons to run a Self-Hosted APIM for Development purposes. Primarily, developing Microservices can be rather complicated and if an APIM is used to route traffic this would alleviate the need for writing code to bypass that missing functionality. Also, if an APIM is used in the upper environments (PROD) it would be best to incorporate this functionality within local development since it’s always best practice to design software that interacts in a way that is as close to the production environment as possible. This will help weed out bugs, architectural challenges from the beginning.
Which Azure API Management Subscription?
In order to create a self-hosted gateway, you must use either Developer ($50/month) or Premium ($2,800/month)… I’d recommend the Developer tier. After all, this is just for local development. A Premium Subscription with a self-hosted gateway would typically be used internally at an enterprise environment where an APIM solution could help manage internal traffic. If you are developing for an external application it would be ideal to use both a Developer Subscription and either a Basic or Standard Subscription for the production environment.
Setting up an Azure API Management Gateway
Azure’s API Management Gateway is really incredible and offers a lot of features. One of my favorites is the ability to import an OpenAPI Specification. I typically do this by viewing the swagger.json file and saving that to disk and then importing that into Azure.
Importing the OpenAPI Document into Azure APIM
To start with I have Swashbuckle installed and will export the /swagger/v1/swagger.json file by right-clicking it and selecting “Save link as…”.
Under APIs in the Azure API Management there is an option labeled “OpenAPI”.
There is an option to select a file. Click that and select the swagger.json file that was recently saved. Be sure to set the “API URL suffix”. This will append a suffix to the Base URL.
That’s it! The Azure API is set up. However it will need some tweaking.
Creating the Azure APIM Gateway
In order for this to be run locally as a self-hosted gateway, you must set up a gateway by clicking “Add” and specifying a name and selecting the APIs.
To run this locally you will need to get the configuration. This is located under the newly created Gateway.
Configuring the Azure API
In order for this to work locally, you will need to set the Backend API URL through a policy. Again, this depends on if you are going to run the application in Kestrel or Docker.
API Settings
Some settings will need to be adjusted for this to work smoothly. I typically enable “Both” for local development and I also disable the “Subscription required” field so I don’t have to pass an “Ocp-Apim-Subscription-Key”.
Kestrel
For a Kestrel webserver use localhostdev for the hostname.
Docker
To run the application in Docker use the Gateway IP address of 172.20.0.1.
Binding Visual Studio to Listen on All IPs
There are two ways of doing this. If you are going to be running the Kestrel web server then it is important to bind to all ports since this will be running and listening on the host machine. The reason we have to do this is that the APIM will run in Docker and will need to connect back to the host’s machine, so listening on the localhost binding won’t work. If the APIM tries to connect to the localhost it will try and connect to its self. Binding can be done with a wildcard like “*” or with “0.0.0.0” which is very common for all IPs.
I would also run Visual Studio in Administrative mode.
I also recommend reading Rick Strahl’s blog post on External Network Access.
Binding All IPs with .NET Core
In the Program.cs file under CreateHostBuilder method I put code similar to this.
1 2 3 4 5 |
public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseUrls("http://0.0.0.0:5000", "https://0.0.0.0:5001"); |
This snippet of code will bind HTTP(S) to all IP addresses on port 5000 (HTTP) and port 5001 (HTTPs).
Binding All IPs with Legacy ASP.NET
Edit the applicationhost.config
file and modify the binding protocol.
<binding protocol="http" bindingInformation="*:5000:localhost" /> <binding protocol="http" bindingInformation="*:5001:192.168.2.102" />
Running the Self-Hosted APIM Locally
Running the self-hosted gateway locally is rather easy with the provided Docker command, however, to get it to work locally, some tweaks will be involved. This will depend on several situations: are you running your app in a Kestrel server bound on the host? …or is this app running in Docker?
Downloading Docker Environment Configuration
You’ll need to download the env.conf file or create one with the values provided under the Gateway.
Running the Self-Hosted Gateway Container
Then running the Docker image is really as simple as this command.
docker run -d -p 80:8080 -p 443:8081 --name local-apim-demo --env-file env.conf mcr.microsoft.com/azure-api-management/gateway:latest
What If I’m Running the Project in Docker?
If you’re running the image in Docker then binding to all IP addresses isn’t necessary since the Docker network can orchestrate this. I like to take the approach where I know how to do things either way so I’ll demonstrate this both ways.
Edit .csproj File to Use Bridged Docker Network
Adding this to the <PropertyGroup> in the .csproj file will modify the Docker run command. I also want to consistently map certain ports to the container so that if I am running multiple microservices I can map those backends in Azure APIM.
<DockerfileRunArguments>--network apim-demo -p 5000:80 -p 5001:443</DockerfileRunArguments>
Disabling HTTPs Redirection and Dodging Certificate Validation
For local development, I would certainly disable HTTPs redirection. Otherwise, the APIM will make requests that redirect, and that will bubble up. The reason I would use HTTP in local development is to get around having to deal with certificates. The APIM will attempt to validate the SSL certificate on the backend and if you don’t have one it’s best to just use HTTP.
//app.UseHttpsRedirection();
Setting up a Bridged Network
A bridge network connects all of the containers together through a common Gateway.
docker inspect network apim-demo
Testing the Bridge Network
Testing is rather easy, get an interactive terminal in the Docker APIM and then curl the gateway on port 5001.
docker exec -it local-apim-demo /bin/bash
curl -k https://172.20.0.1:5001/api/test
Configuring Docker to Use the Host’s Network (Kestrel)
All of the configuration for the Azure APIM can be found under the Gateway configuration tab. However, you will also need to pass parameters that specify the network so that the host machine’s network can be shared within the APIM Docker container.
Docker: Add Host Option
In order for the container be able to hit the host machines’ services you will have to set a host entry by using to make this work. The Add Host Option will create an entry in the /etc/hosts file so that you could curl localhostdev on port 5001 and hit the API. The reason we want to use the Add Host Option is that other developers may be using this. We have to update the Dev APIM to point to the backend and use the localhostdev hostname.
I use a PowerShell script to run the container.
1 2 3 4 5 6 7 8 |
# remove existing docker image docker rm -f local-apim-demo # get current ip address that has internet $ip = (Test-Connection -ComputerName $env:computername -count 1).IPV4Address.IPAddressToString # key changes here are --add-host which will add a host entry into the docker container's /etc/host file docker run -d -p 80:8080 -p 443:8081 --name local-apim-demo --add-host=localhostdev:${ip} --env-file env.conf mcr.microsoft.com/azure-api-management/gateway:latest |
https://docs.docker.com/network/network-tutorial-host/
https://docs.docker.com/engine/reference/commandline/run/#add-entries-to-container-hosts-file—add-host
Debugging Azure API Management Self-Hosted Gateway
There are several strategies to debugging a self-hosted gateway.
Interactive Terminal (-it)
Accessing the interactive terminal of the container would allow us to view the /etc/hosts file and or run a curl command to manually verify that the container can reach the host machine or other containers.
docker exec -it local-apim-demo /bin/bash
View Hosts File
cat /etc/hosts
Test a Connection using Curl
curl -k https://localhostdev:5001/api/test
Viewing Docker Logs
The easiest way to see what’s happening in the APIM is to follow the Docker logs of the container.
docker -f local-apim-demo
Mock Responses
This is likely one of the easiest ways to see if a request is hitting the APIM. Setting a Mock Response will allow the APIM to return a certain response and status. To create a mock response click “Add Policy” on the “Inbound processing” pane.
Final Request
