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:
- Deploy applications to Cloud Run
- Authenticate Apigee to access Cloud Run
- Operational Considerations
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](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:
- 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>
- 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>
- 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>
- 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.



