Skip to content

Container based solution to do performance based proxy requests to APIM backends.

License

Notifications You must be signed in to change notification settings

microsoft/SimpleL7Proxy

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

SimpleL7Proxy

The SimpleL7Proxy is a lightweight, performance based proxy designed to route network traffic between clients and servers. It operates at the application layer (Layer 7) of the OSI model, enabling it to route HTTP(s) requests. It can be used as a regional internal proxy to manage traffic across specific regions. The server calculates the latency for each backend on a regular interval so that when a request comes in, it will connect to the lowest latency host first. Optionally, higher priority transactions can preempt lower priority requests.

In the diagram below, a client connected to the proxy which has 3 backend hosts. The proxy identified the host with the lowest latency (Host 2) to make the request to.

image

Features

  • HTTP/HTTPS traffic proxy
  • Failover
  • Load balance based on latency
  • SSL termination
  • Priority based processing
  • Cross-platform compatibility (Windows, Linux, macOS)
  • Logging to Application Insights
  • Logging to EventHub

Usage

SimpleL7Proxy can be run standalone on the commandline or it can be deployed as a container. All configuration is passed to it via environment variables. When running, the server will periodically display the current back-end latencies.

Environment Variables:

Variable Description Default
APPENDHOSTSFILE When running as a container and DNS does not resolve you can have the service append to the hosts file. You will need to specify Host1, IP1 as the host and ip combination. When the container starts up, this will add an entry to the hosts file for each combination specified.
APPINSIGHTS_CONNECTIONSTRING This variable is used to specify the connection string for Azure Application Insights. If it's set, the application will send logs to the application insights instance. None
DnsRefreshTimeout The number of ms to force a dns refresh. Useful to have a small value when testing failover. 120000
EVENTHUB_CONNECTIONSTRING The connection for the eventhub to log into. Both the connection string and namespace are needed for logging to work. None
EVENTHUB_NAME The eventhub namesapce. Both the connection string and namespace are needed for logging to work. None
Host1, Host2, ... The hostnames of the backend servers. Up to 9 backend hosts can be specified. If a hostname is provided, the application creates a new BackendHost instance and adds it to the hosts list. The hostname should be in the form http(s)://fqdnhostname and DNS should resolve these to an IP address. None
IgnoreSSLCert Toggles if the server should validate certificates. If your hosts are using self-signed certs, set this value to true. false
IP1, IP2, ... Used to specify the IP address of hosts if DNS is unavailable. Must define Host, IP and APPENDHOSTSFILE and run as container for this to work.
OAuthAudience The audience to fetch the Oauth token for. Used in combination with UseOauth.
PollInterval This variable is used to specify the interval (in milliseconds) at which the application will poll the backend servers. 15000
Port Specifies the port number that the server will listen on. 443
PriorityKeys If the incoming request has the header 'S7PPriorityKey' set to one of these values, use the value of PriorityValues as the priority. The contents should be a comma delimited list of keys.
PriorityValues This is the list of priority values to use. If the incoming request has the header 'S7PPriorityKey' matches PriorityKeys, the corresponding value from this list will be used as the message priority. In the case this list is not specified or parsable, the default priority is used. 5
Probe_path1, Probe_path2, ... Specifies the probe paths for the corresponding backend hosts. If a Host variable is set, the application will attempt to read the corresponding Probe_path variable when creating the BackendHost instance. Depending on the tier for your APIM, you can also try: status-0123456789abcdef or internal-status-0123456789abcdef echo/resource?param1=sample
RequestIDPrefix the set of characters to prefix to the unique request ID
Success-rate The percentage success rate required to be used for proxying. Any host whose success rate is lower will not be in rotation. 80
Timeout The connection timeout for each backend. If the proxy times out, it will try the next host. 3000
UseOauth Enable the Oauth token fetch. Should be used in combination with OAuthAudience. false
Workers The number of proxy worker threads. 10

Proxy response codes:

Code Description
200 Success
400 Bad Request, there was an issue with the http request format
408 Request Timed Out, the request to the specific backend host timed out.
410 The time specified in the S7PTTL header is older than the time when the request was processed
500 Internal Server Error, Check application insights for details
502 Bad Gateway, The request could not be completed. Are the backend hosts overloaded?

Headers Used :

Header Description
S7PDEBUG Set to true to enable tracing at the request level.
S7PPriorityKey If the incoming request has this header and it matches one of the values in the 'PriorityKeys' environment variable, than the request will use the associated priority.
S7PREQUEUE If the remote host returns a 429 and sets this header with a value of true, the request will be requeued using the original enqueue time.
S7PTTL The time after which the message will be considered to be expired. In this case, the proxy will respond with a 410 status code.
x-Request-Queue-Duration The amount of time the message was enqueue'd.
x-Request-Process-Duration The amount of time it took to process it.
x-Request-Worker The worker ID

Example:

Port=8000
Host1=https://localhost:3000
Host2=http://localhost:5000
PollInterval=1500
Timeout=3000

This will create a listener on port 8000 and will check the health of the two hosts (https://localhost:3000 and http://localhost:5000) every 1.5 seconds. Any incoming requests will be proxied to the server with the lowest latency ( as measured every PollInterval). The first request will timeout after 3 seconds. If the second request also timesout, the service will return error code 503.

Running it on the command line

Pre-requisutes:

  • Cloned repo
  • Dotnet SDK 8.0

Run it

export Port=8000
export Host1=https://localhost:3000
export Host2=http://localhost:5000
export Timeout=2000

dotnet run

Running it as a container

Pre-requisites:

  • Cloned repo
  • Docker

Run it

docker build -t proxy -f Dockerfile .
docker run -p 8000:443 -e "Host1=https://localhost:3000" -e "Host2=http://localhost:5000" -e "Timeout=2000" proxy

Deploy the container to Azure Container Apps

Pre-requisites:

  • Cloned repo
  • Docker
  • az cli ( logged into azure account )

Deploy it

# FILL IN THE NAME OF YOUR ACR HERE ( without the azurecr.io part )
export ACR=<ACR>
export GROUP=simplel7proxyg
export ACENV=simplel7proxyenv
export ACANAME=simplel7proxy
az acr login --name $ACR.azurecr.io
docker build -t $ACR.azurecr.io/myproxy:v1 -f Dockerfile .
docker push $ACR.azurecr.io/myproxy:v1

ACR_CREDENTIALS=$(az acr credential show --name $ACR)
export ACR_USERNAME=$(echo $ACR_CREDENTIALS | jq -r '.username')
export ACR_PASSWORD=$(echo $ACR_CREDENTIALS | jq -r '.passwords[0].value')

az group create --name $GROUP --location eastus
az containerapp env create --name $ACENV --resource-group $GROUP --location eastus
az containerapp create --name $ACANAME --resource-group $GROUP --environment $ACENV --image $ACR.azurecr.io/myproxy:v1 --target-port 443 --ingress external --registry-server $ACR.azurecr.io --query properties.configuration.ingress.fqdn --registry-username $ACR_USERNAME --registry-password $ACR_PASSWORD --env-vars Host1=https://localhost:3000 Host2=http://localhost:5000

Deploy to Container Apps via a Github Action

You can create a github workflow to deploy this code to an Azure container app. You can follow the step by step instruction from a similar project in the following video:

Video Title