Hosted Targets vs. Google Cloud Run

Hosted Targets is a long time feature of Apigee Edge that provides the option to deploy Node.js applications in a managed and easy to use runtime. The intent of this runtime is to provide customers with the ability to securely run applications in a fully managed environment and expose them through Apigee API proxies. This pattern is used in production architectures to quickly deploy Node.js-based microservices or to implement orchestration logic that goes beyond the capabilities of simple JavaScript or Java callout policies. Additionally, Hosted Targets are also commonly used for backend mocks in lower environments of the SDLC.

Whilst Hosted Targets are fairly easy to implement and provision, they come with some limitations:

  • Hosted Targets are only available in Edge Public Cloud organizations.
  • They are available in the Enterprise and Enterprise Plus pricing plan only.
  • Some deployment aspects (e.g. bundle size) are subject to Apigee limits as documented here.
  • Node.js is the only supported runtime and the allocated compute resources can not be configured.
  • Limited debugging and logging capabilities.

 

Alternatives to Hosted Targets

As Hosted Targets are built on top of Google App Engine (GAE), one obvious candidate to address the limitations above would be to host a target service directly in GAE. This would allow for more flexibility as it supports other languages such as Java, Python or Go in addition to Node.js. Developing and deploying applications for GAE is simple as the format is idiomatic for each runtime environment.

For even simpler use cases that can be expressed as a simple function Cloud Functions might even be a good fit. Cloud Functions abstract the entirety of the underlying runtime away from the developers and let them focus on delivering the required application functionality with minimal operational overhead.

The third option for hosting serverless workloads on GCP is Cloud Run. This article focuses on Cloud Run as an alternative to Hosted Targets as it is the most flexible out of the three offerings and can therefore cover most use cases.

A good starting point to explore all three serverless offerings as well as a feature comparison matrix can be found here.

 

Running Apigee Target Services on Cloud Run

Cloud Run is Google Cloud’s fully managed compute platform that allows users to deploy and scale their containerized workloads. Workloads can be implemented as arbitrary containers which opens up the space for any programming language, framework or even custom binaries. Obviously, this also includes Node.js runtimes and we will see how hosted targets can easily be transformed into Cloud Run compatible containers. Cloud Run can also run on existing Kubernetes clusters or on Anthos which makes it a great fit for Apigee hybrid as well.

Here are some of the key differences when comparing Cloud Run with Apigee’s native Hosted Targets:

  • Cloud Run is available to any Apigee deployment option; including hybrid.
  • It is separately billed and therefore independent of the Apigee pricing plan.
  • Supports any containerized workload.
  • Provides finer grained control over scaling behavior and available resources.
  • Easier to develop, monitor and operate critical workloads by using Cloud Code, Cloud Build, Cloud Monitoring, and Cloud Logging.

It’s important to note here that Cloud Run is not directly tied to Apigee and acts as a microservice that serves HTTPS requests. As such it can be used as both a target for service callouts as well as a target server. Because the services are loosely coupled some of the Apigee specifics are not available to it:

  • Multi-region support. Apigee hosted targets are deployed in the same region as the Apigee runtime and replicated when Apigee is deployed in a multi-region setup. Cloud Run has (at the time of writing) BETA support for multi-region deployments.
  • Authentication for Apigee Hosted Targets is transparent for the user and the runtime is only available from the Apigee proxy. With Cloud Run the authentication has to be explicitly configured. There is an option for allowing unauthenticated invocations but the clear recommendation is to require authentication on Cloud Run and issue service account keys to Apigee.
  •  

Getting started with Apigee Target Services on Cloud Run

After explaining the difference between Apigee Hosted Targets and Cloud Run we would like to close out with some practical advice for how to integrate Apigee with Cloud Run along the following steps:

  1. Deploy applications to Cloud Run
  2. Authenticate Apigee to access Cloud Run
  3. Operational Considerations
  4.  

Deploy Applications to Cloud Run

Please refer to the official Cloud Run documentation for deploying greenfield workloads and make sure you answer ‘No’ when being asked if you want to allow unauthenticated access to your services.

If you have an existing Hosted Target implementation that you would want to deploy to Cloud Run you need to perform the following steps:

Add a Dockerfile to the root of your Hosted Targets bundle:

FROM node:12-alpine3.11
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install --only=production
COPY . ./
CMD [ "node", "app.js" ]

and also a .dockerignore for better performance:

Dockerfile
.dockerignore
node_modules
npm-debug.log

Ensure that your code reads the serving port from the PORT environment variable:

process.env.PORT || 9000

Now you can either create a build config specification or directly submit your build via the gcloud cli:

gcloud builds submit --tag gcr.io/$(gcloud config get-value project)/apigee-target

Once the build is completed the image is available in the Google Container Registry (gcr.io) and ready to be deployed to Cloud Run:

gcloud run deploy apigee-target-demo \
	 --image=gcr.io/$(gcloud config get-value project)/apigee-target \
	 --platform=managed \
	 --region=europe-west1 \
	 --no-allow-unauthenticated

The parameters used here:

  • image: the image you built just before
  • platform: fully managed cloud run
  • zone: ideally the same zone(s) where you run Apigee
  • no-allow-unauthenticated: requires authentication for invocations of the service

This a successful deployment yields a service URI which will be used in later steps:

Service URL: https://apigee-target-demo-xxxxxxxx.a.run.app

If you try to access your service via the URI above you should see a 403 - Forbidden error. We will create authentication through an Apigee proxy in our next session.

 

Authorizing an Apigee ServiceAccount to access Cloud Run

In the previous step we created a Cloud Run service that only allows authenticated invocations. We therefore have to first create a service account `apigee-test-cloudrun` and authorize it to invoke the service.

gcloud iam service-accounts create apigee-test-cloudrun \
	 --description="Apigee Test Env. Cloud Run"
gcloud run services add-iam-policy-binding apigee-target-demo \
	 --member="serviceAccount:apigee-test-cloudrun@$(gcloud config get-value project).iam.gserviceaccount.com" \
	 --role='roles/run.invoker' \
	 --region=europe-west1 \
	 --platform=managed

 

Option 1: Authenticating Apigee via  Google Authentication

Apigee provides a Google Authentication feature that can be used e.g. to authenticate Apigee Callouts and TargetEndpoints to access Google Services via either an Identity or Access Token.

To configure an Apigee authentication for the Cloud Run endpoint you will have to add an Authentication Tag to your TargetEndpoint configuration as shown here:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<TargetEndpoint name="default">
    <HTTPTargetConnection>
        <Authentication>
            <GoogleIDToken>
                <Audience>https://apigee-target-demo-xxxxxxxx.a.run.app</Audience>
            </GoogleIDToken>
        </Authentication>
        <URL>https://apigee-target-demo-xxxxxxxx.a.run.app</URL>
    </HTTPTargetConnection>
</TargetEndpoint>

make sure you replace the https://apigee-target-demo-xxxxxxxx.a.run.app placeholder with your own endpoint.

When deploying your proxy you just need to supply the service account that you have created before to let Apigee know that this service account should be used to authenticate the call to Cloud Run.

 

Option 2: Authenticating Apigee via  SA Keys

Next up we have to generate and download the service account JSON key and store it’s content in an Apigee encrypted KVM value.

gcloud iam service-accounts keys create ./sa-key.json \
	 --iam-account apigee-test-cloudrun@$(gcloud config get-value project).iam.gserviceaccount.com

Once the service account key is stored in the Apigee KVM we need to create a standard passthrough proxy in Apigee with the Cloud Run endpoint as a target:

And create the following four policies in the RequestFlow of the ProxyEndpoint:

1. An AssignMessage policy to set the TargetAudience for the AccessToken request and store it in gcp.target_audience.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<AssignMessage name="AM.SetTargetAudience">
   <AssignVariable>
       <Name>gcp.target_audience</Name>
       <Value>https://apigee-target-demo-xxxxxxxx.a.run.app</Value>
   </AssignVariable>
   <IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
</AssignMessage>

2. A KVM lookup policy to fetch the previously stored service account key and store it in private.gcp.service_account.key.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<KeyValueMapOperations name="KV.LookupSAKey" mapIdentifier="gcp-sa">
   <ExclusiveCache>false</ExclusiveCache>
   <ExpiryTimeInSecs>300</ExpiryTimeInSecs>
   <Get assignTo="private.gcp.service_account.key">
       <Key>
           <Parameter>apigee-test-cloudrun@PROJECT_HERE.iam.gserviceaccount.com</Parameter>
       </Key>
   </Get>
   <Scope>environment</Scope>
</KeyValueMapOperations>

3. A callout to the GCP Access Token shared flow from Apigee DevRel (https://github.com/apigee/devrel/tree/main/references/gcp-sa-auth-shared-flow)

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<FlowCallout name="SC.GcpSAToken">
   <SharedFlowBundle>gcp-sa-auth-v1</SharedFlowBundle>
</FlowCallout>

4. An AssignMessage policy to set the access token in private.gcp.access_token as the bearer token in the authorization header of the request.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<AssignMessage name="AM.SetAuthHeader">
   <Set>
       <Headers>
           <Header name="Authorization">Bearer {private.gcp.access_token}</Header>
       </Headers>
   </Set>
   <AssignTo createNew="false" transport="http" type="request"/>
</AssignMessage>

Tracing the request we can see that the access token is correctly generated and passed to the target service.

Operational Considerations

For using Cloud Run with Apigee in a production scenario it is important to understand some basic characteristics of how the products play together.

From a performance point of view, you should ensure that the Cloud Run service is running in the same GCP region as your Apigee runtime. If you are running a multi-region Apigee deployment then it might make sense to look at Cloud Run with a global load balancer to automatically select the Cloud Run service with the lowest latency. It is also worth noting that Cloud Run is able to cache your responses to further speed things up. If you are using the shared flow implementation from the Apigee DevRel Github repository, service account access tokens are automatically cached for you so you do not have to worry about that.

Cloud Run also automatically sends metrics and logs to Cloud Monitoring and Cloud Logging for you. This way you can investigate the behavior of your services and proactively monitor them for potential issues. Similarly the container vulnerability scanning feature of the Google Container Registry can be used to monitor the images used in Cloud Run and ensure the images are free of known vulnerabilities.

Comments
dchiesa1
Staff

Daniel, outstanding! Nice stuff!

rezarahmati
New Member

Thank you for great article,
For storing service account as KVM , I don't see any option in apigee UI to add values, it just ask for name and that is it




Even I tried api to create KVM its same, it doesn't me to add values/entries and I get this error
"Invalid JSON payload received. Unknown name \"entry\" at 'key_value_map': Cannot find field."

strebel
Staff

Hi Reza, You're absolutely right. The KVM entries in Apigee X and hybrid are administered through the runtime plane and therefore the UI to edit entries is not available.

Instead you'd use a proxy within your runtime to edit the values through the KVM policy. If you're interested in a drop-in proxy, check out our DevRel asset: https://github.com/apigee/devrel/tree/main/references/kvm-admin-api

rezarahmati
New Member

Thank you, actually I added another proxy with a KVM Operation policy and tries to store values in there

rezarahmati
New Member

@Daniel Strebel Another question, in the step you metioned to store service account file to KVM, do we need to store the whole file? do we need to escape chars? or can we add multiple keys for each property in the service account file?

rezarahmati
New Member

I think in this step https://github.com/apigee/devrel/tree/main/references/gcp-sa-auth-shared-flow#prerequisites content needed to be switched (based on env names seems edge is for x and x is for edge)

strebel
Staff

The current implementation assumes that you're sending the whole file. This requires the key that is in json format to be string encoded. There is an example of how to do this using jq in the GCP shared flow in github https://github.com/apigee/devrel/tree/main/references/gcp-sa-auth-shared-flow. If you want to edit the javascript that parses the key, you can definitely also use separate entries for each of the fields in the GCP SA key.

rezarahmati
New Member

Sorry to ask many question, I am new in apigee. I followed the steps in https://github.com/apigee/devrel/tree/main/references/gcp-sa-auth-shared-flow to create the shared flow, after running command it creates a proxy `kvm-admin-v1` in Api Proxies, and adds a KVM called `gcp-sa-devrel`, but it doesn't add any shared flows to be used in policies. What I am doing wrong?

optimism
Silver 2
Silver 2

Thanks, @strebel for sharing this article, especially "Option 2: Authenticating Apigee via SA Keys". And thanks to @dknezic for pointing to this article.

Version history
Last update:
‎11-03-2021 08:28 AM
Updated by: