Here’s how you deploy your service to cloud run (and then have it use a Cloud Endpoint API Gateway).

Create a Basic Service

Let’s say you have a basic service with some endpoints.

from flask import Flask

app = Flask(__name__)

@app.route('/hello')
def hello():
    return 'Hello, World!'

@app.route('/goodbye')
def goodbye():
    return 'Goodbye, World!'

if __name__ == '__main__':
    app.run()

Basic Deploy

You can deploy your service to Cloud Run by running the following command:

gcloud run deploy --image gcr.io/PROJECT_ID/IMAGE_NAME --platform managed --concurrency 20 --allow-unauthenticated

Where:

  • --image specifies the docker image of your service.
  • --platform managed tells GCP to automatically scale the number of instances based on volume of requests.
  • --concurrency specifies the max number of concurrent requests each instance should handle. GCP will spin a new instance if the number of requests exceeds this limit. Defaults to 80.
  • --allow-unauthenticated allows unauthenticated access to your service.

You can optionally add --region REGION to deploy to a specific region.

This command returns a URL for your deployed service, which you can use to access it via HTTP.

Adding API Gateway (Cloud Endpoints)

Create an OpenAPI spec for Cloud Endpoints.

swagger: '2.0'
info:
  title: My Service
  description: My Service API
  version: 1.0.0
host: YOUR_API_GATEWAY_HOST
x-google-endpoints:
  - name: YOUR_API_GATEWAY_HOST
    allowCors: true
paths:
  /hello:
    get:
      summary: Hello World
      operationId: hello
      responses:
        '200':
          description: A successful response
          schema:
            type: string
  /goodbye:
    get:
      summary: Goodbye World
      operationId: goodbye
      responses:
        '200':
          description: A successful response
          schema:
            type: string

“Deploy” your API spec to cloud endpoints:

gcloud endpoints services deploy OPENAPI_SPEC.yaml

Deploy ESP (proxy HTTP server) to Cloud Run (in front of your service, which is also running in a Cloud Run instance):

gcloud run deploy espv2-gateway \
  --image=gcr.io/endpoints-release/endpoints-runtime:latest \
  --args="--config=/etc/endpoints/OPENAPI_SPEC.yaml,--backend=https://YOUR_CLOUD_RUN_URL" \ # <-- the URL of your service
  --platform managed \
  --allow-unauthenticated \
  --region REGION

This returns the URL of the ESP (proxy HTTP server), which is what you should ask clients to use when accessing your API.

Adding Authentication

In your OpenAPI spec, specify that authentication is required and the type(s) of authentication.

securityDefinitions:
    api_key:
        type: "apiKey"
        name: "key"
        in: "query"
    firebase_auth:
        type: "oauth2"
        flow: "implicit"
        authorizationUrl: "https://your-auth-server.com/auth" # documentation purpose only, clients must know this themselves
        x-google-issuer: "https://your-auth-server.com/"
        x-google-jwks_uri: "https://your-auth-server.com/.well-known/jwks.json"
        x-google-audiences: "https://your-auth-server.com/"
paths:
    /api/hello:
        get:
            security:
                - api_key: []
    /api/goodbye:
        get:
            security:
                - api_key: []
                - firebase_auth: []

Redeploy your service in cloud run but be sure to not specify --allow-unauthenticated.

Diagrams

Here are some diagrams that visually depict this “architecture”.

An API Gateway (ESP) sits between your clients and your service. Note that ESP is also running in cloud run:

flowchart LR
    Client[Client]
    ESP[ESP]
    Service[Service]

    Client --> ESP --> Service

The ESP just forwards requests from clients to the service:

sequenceDiagram
    participant Client
    participant ESP as ESP
    participant Service

    Client->>ESP: HTTP Request e.g., /hello
    ESP->>Service: Forwarded Request
    Service-->>ESP: Response
    ESP-->>Client: Response

If you have setup your ESP to authenticate (via the OpenAPI spec of your API), here is how authentication flow happens:

sequenceDiagram
    participant Client
    participant ESP as ESP
    participant Service

    Client->>ESP: HTTP Request e.g., /hello
    ESP->>ESP: Validate Token Using public Key
    alt Token Valid
        ESP->>Service: Forwarded Request
        Service-->>ESP: Response
        ESP-->>Client: Response
    else Token Invalid or Missing
        ESP-->>Client: 401 Unauthorized
    end

If token is missing/invalid, it is the job of the client to redirect to the identity provider, get a token, and try the HTTP request again:

sequenceDiagram
    participant Client
    participant Auth as Auth Server

    Client->>Auth: Redirect for Token
    Auth-->>Client: Token
    Client->>ESP: HTTP Request e.g., /hello

Summary

Basic (no authentication):

  1. Upload the docker image of your service to Google’s artifact registry.
  2. gcloud run deploy your service to deploy it in cloud run; you get back a URL to access it through.

With Authentication:

  1. Create an OpenAPI spec for your API, specifying the authentication methods you want to support (token-based, external identity provider, etc.)
  2. gcloud run deploy your service to deploy it in cloud run; you get back a URL to access it through, don’t provide this to clients.
  3. gcloud endpoints services deploy your OpenAPI spec to create/update the API config.
  4. gcloud run deploy your ESP (proxy) service to Cloud Run. You’ll need to specify the URL of your service as the target of the proxy.