Send request to app in specific location

Hi,

First time here, read through lots of info in the docs and some threads here.
Anycast is great for routing users to the ‘closest/best’ datacenter, but my need is to be able to send requests (HTTPS) to our Node app in a specific location.

Say, I have selected the regions ams, syd and lax.
I’d like Fly to fire up an instance in ams, as per the requests coming in to https://ams.myservice.com.
Can’t launch instance in ams? Do it in a nearby backup location (that’s fine).

Fly volumes may play a role here, but I’ve not been able to find anything that informs me how to send a request to a specific location (which may not be possible since you’re running an Anycasted network).

Right now, the only way to do what I think you want is to run three apps.

Autoscaling and load balancing are two different processes. We monitor metrics to scale instances when the soft limit thresholds are met. New instances get spread evenly across your regions. Requests are routed to the nearest instance that’s under its limits.

What kind of requests are you trying to route to specific regions? Are you trying to keep people “pinned” to one VM, or doing something like game matchmaking?

There are some headers you can use to send specific HTTP request to specific VMs. These might be useful if you’re making requests from JavaScript, but won’t help for links people click.

Three apps, that’s what I figured.
I guess the downside of this is that I need to set things up 3 times. Not a biggie, but setting up and managing one app would be a lot nicer.

At launch, the app will act as a microservice and only has the role of a helper to another service.
The requests to the app on Fly are not from browsers/JS, all from cURL (testing) or Fetch.
So, using request headers can work.

send specific HTTP request to specific VMs

Plz tell me more!

If you get a list of instance IDs with flyctl status, you can send a fly-force-instance-id: <id> header in your request. The caveat is that if the instance id doesn’t exist, or it’s maxed out, the request will fail.

Since this sounds like it’s for internal use, you can also just hit private IP addresses directly if you setup a wireguard peer. Something like:

curl http://ams.<app-name>.internal:8080

Will send a request to port 8080 on an AMS based instance (if you have one running).

Interesting.
I should have made clear earlier that the core service, to which the app on Fly is a helper, lives in Cloudflare.
Obviously, Cloudflare can’t send a request to the .internal.
And Cloudflare can’t use flyctl.
I’m assuming the answer is No, but asking anyway: can the app in each location have a static public IP?

An ugly solution (because, high latency) would be to have Cloudflare send the request to Fly and this is routed to the app in whatever Fly location and if that is not the target location, this location then acts as a proxy and sends a request to the correct target location using the private IP address.
High latency is is currently not an issue with the service, but Cloudflare US <=> Fly US <=> Fly AU is definitely not pretty and not future-proof.

Oh, I just realized we have a better answer for this as of a few weeks ago. Try setting fly-prefer-region: ams with a request, that should do the right thing.

We’re working on regional IPs but it’ll be a few months!

2 Likes

You mean that sending that header in a request to Fly will force Fly to route the request to the preferred region?
Sounds like what I’m looking for :boom:

1 Like

Yep that’s exactly it.

1 Like

Probably the last question on this topic: is there a way to know from the response from which location the response was served?
My Node app will send out a response with some specific headers (e.g. cache-control) but I’d love for Fly to add a header to the response, e.g. x-fly-region: ams

There is a Fly-Region response header on all our responses that shows the edge region where the request was received.

If you want the region for your instance (might be different from the edge region), then you’ll want to add the header yourself from the environment variable FLY_REGION from inside your VM :slight_smile:

1 Like

My bad, the Fly-Region header is on requests we forward to your app. Feel free to use that in your app and forward it to your users if you’d like!

App is deployed in Chicago and running.

In my code, I’m grabbing the runtime env var FLY_REGION per the
The Fly Runtime Environment · Fly
and then putting that as the value in the x-cdnp-fly-region-node response header our app sends out.

curl -sv0 /dev/null 'https://nameless-sun-2664.fly.dev/'
The 404 is expected.
Note the x-cdnp-fly-region-nodeis empty, which means the Fly-Region was empty or triggered an error.
Also, the request header Fly-Region seems not present on requests coming into the app.

Full code in Node:

let flyRegionNode = '',
      flyRegionEdge = ''

  try {
    flyRegionNode = FLY_REGION
    flyRegionEdge = req.headers['Fly-Region']
  } catch (e) {}

  res.setHeader('x-cdnp-fly-region-node', flyRegionNode)
  res.setHeader('x-cdnp-fly-region-edge', flyRegionEdge)

Help is appreciated greatly.

Is that block returning an error? Neither of those lines should ever throw an exception.

You can verify the environment variable with SSH. Run this:

➜  fly ssh console
Connecting to <app>.internal... complete
# echo $FLY_REGION
ord

If this is node, you might have better luck with req.getHeader("fly-region").

$fly ssh console
Error create ssh certificate: issue_certificate failed: no root key established

Yes, this is Node and using .getHeader on the incoming request results in TypeError: req.getHeader is not a function (to my surprise, can’t explain that) but on local the req.headers works fine, so this is not the issue.

I’ve re-deployed and now the catch returns a 400 response with in the body the error message.
curl -sv 'https://nameless-sun-2664.fly.dev/' gives FLY_REGION is not defined

Oh that SSH error is related to a timely bug: "create ssh certificate: issue_certificate failed: no root key established" with flyctl ssh - didn't tell me what to do next · Issue #390 · superfly/flyctl · GitHub

Try changing that line that’s erroring to this. You can run flyctl ssh establish once to fix it for your organization:

flyRegionNode = process.env.FLY_REGION

It’s throwing an error because FLY_REGION isn’t a constant, it’s just a key on the environment hash.

That did the trick (RTFM Aaron, come on!) and echo $FLY_REGION gives me ord :smiley:

And, process.env.FLY_REGION did the trick too. My brain was in Cloudflare Workers land.

And, after fixing a case sensitivity issue related to headers, it’s all good now:

curl -sv 'https://nameless-sun-2664.fly.dev/' gives the two response headers just how I expect them :partying_face:

1 Like