Aujourd’hui, je me chauffe pour faire une architecture microservice. Mon idée est de combiner Cloudrun avec l’API gateway pour profiter de l’hyperscalabilité de Cloudrun et mettre en place du rate limiting sur mes services. J’utiliserai un identity provider pour gérer l’authentification et les permissions de mes utilisateurs.

donald trump , we have the best API gateway

Et bien sûr, je vais bricoler des services un peu éclatés pour le bien de mon POC hihihi.

Mon idée est de me dire que Cloudrun est bien car il s’autoscale automatiquement, mais lorsque la charge augmente considérablement, cela peut coûter cher. Il est donc essentiel de trouver des solutions pour atténuer les floods de requêtes et calmer les robots qui attaquent un peu trop vigoureusement, sans pénaliser excessivement les requêtes légitimes. On peut toujours bloquer les IPs un peu trop agressives avec Cloud Armor.

En revanche, je pense que le rate limiting est une protection passive plutôt simple à mettre en œuvre dès le début du projet, et c’est une bonne solution passive pour atténuer les attaques.

De plus, passer par l’API gateway permet de garder l’option de mélanger différentes technologies pour répondre au mieux aux différents besoins. Cela offre la possibilité d’utiliser des cloud functions, du Cloudrun, du Kubernetes, voire même du compute en fonction des besoins, de manière transparente pour l’utilisateur final.

Un point que j’évoque rarement, c’est l’idée de réversibilité. Si l’on change complètement d’avis et que, pour des raisons commerciales, nous décidons de quitter GCP, comment pouvons-nous réinternaliser des cloud functions, du Cloudrun, etc. ? Si nous avons fait une erreur en utilisant Kubernetes et que nous souhaitons revenir à un monolithe, comment puis-je faire cohabiter plusieurs technologies sur le même domaine ?

Plus d’info sur Identity provider

Un fournisseur d’identité (IdP) est un service qui stocke et vérifie l’identité des utilisateurs. Les IdP sont généralement des services hébergés dans le cloud qui travaillent souvent avec des fournisseurs d’authentification unique (SSO) pour authentifier les utilisateurs. source : https://www.cloudflare.com/fr-fr/learning/access-management/what-is-an-identity-provider/

En général on utilise keycloak en version opensource gratuite ou bien une solution payante en SAAS type okta

Plus d’info sur le rate limiting

Le rate limiting est utilisé pour contrôler le débit du trafic envoyé ou reçu sur une interface réseau.

Le trafic inférieur ou égal à la limite spécifiée est envoyé, tandis que le trafic excédant cette limite est rejeté ou retardé. Un appareil qui remplit cette fonction est un limiteur de débit. Le rate limiting est effectué par traffic policing (rejet des paquets en excès), queuing (retard des paquets en transit) ou par contrôle de congestion (manipulation des mécanismes de contrôle de congestion du protocole utilisé). Policing et queuing peuvent être appliqués à tous les réseaux informatiques. Le contrôle de congestion peut seulement être appliqué aux protocoles réseaux utilisant des mécanismes de contrôle de congestion, tels que TCP.

source : https://fr.wikipedia.org/wiki/Rate_limiting

On utilise le rate limiting pour empêcher les scripts un peu trop agressifs de surcharger de requêtes les serveurs et causer des indisponibilités de service.

Je vous vois consommer des services comme des festivaliers du hellfest à la tireuse des bars, bande de saoulars de la bande passante !

photo d’un des bars au hellfest

Mettre en place une API gateway

À partir de là, j’ai commencé à bricoler toutes sortes de trucs, et j’ai fait quelques découvertes choquantes.

J’ai comme une grande vibe à la IoT Core (qui a été déprécié au profit de Clearblade). Je pense que Google ne va pas faire plus d’efforts que ça pour mettre en avant ce produit et peut-être même le déprécier complètement au profit d’Apigee.

Caradoc, je trouve qu’on nous prend un peu trop souvent pour des cons en ce moment

Néanmoins, je ne vais pas jouer le jeu d’Apigee pour autant, puisque la stratégie ressemble énormément à celle de Microsoft : “Allez viens, on t’offre des jours gratuits et ensuite tu payes :)”

Comme je n’ai aucune idée de combien de temps encore je vais faire mon POC, je préfère le pricing de la gateway qui dit que tant que j’ai un trafic insignifiant je ne paie rien !

J’avais dans l’idée de mettre en place un FastAPI avec le CRUD Router, pour avoir des routes autogénérées et faire une petite API tranquille avec très peu de code.

J’aurais transporté ma spécification OpenAPI auto-générée de FastAPI dans ma conf API gateway, et j’aurais sûrement bricolé pour mettre en place l’authentification par clé d’API et du rate limiting.

En me renseignant un peu, je m’aperçois que l’API gateway GCP stagne techniquement avec un niveau très restreint de fonctionnalités et met un temps colossal pour mettre à jour la plateforme en version OpenAPI 3.

https://issuetracker.google.com/issues/204211041?pli=1

Ça veut dire que ce n’est même pas la peine d’essayer de transposer gracieusement mon Swagger FastAPI directement dans l’API gateway. Ça semble rien sur le papier, mais ça donne l’impression que ce service n’est pas du tout prioritaire dans la roadmap de GCP.

Hercule, Kevin Sorbo, disappointed meme

Donc ensuite je me suis dit : je vais faire le minimum et voir où ça nous mène, en restant dans les limitations techniques de l’outil.

On repart sur un scope plus simple

On va commencer par lire la doc et voir les promesses de l’outil.

Avec API Gateway, vous pouvez créer, sécuriser et surveiller les API pour les backends sans serveur de Google Cloud, y compris Cloud Functions, Cloud Run et App Engine. Conçu sur Envoy, API Gateway offre hautes performances et évolutivité, et vous permet de vous consacrer à la création d'applications de qualité. Ses niveaux de tarification basés sur la consommation vous aident à mieux gérer les coûts.

J’en comprends que le service rendu par API gateway est surtout prévu pour les services managés serverless. Du coup, on va partir dans l’idée de faire une cloud function servie par l’API gateway, à laquelle on ajoutera une authentification par API token et un rate limiting.

Si on crée une cloud function v2, en fait on s’aperçoit que c’est un Cloud Run qui tourne derrière. On peut tout à fait récupérer l’image et l’étudier. Personnellement, j’ai choisi d’en refaire un Cloud Run, car la ressource Terraform est infiniment plus simple à gérer que la cloud function.

Au passage, si on fouille l’image de la cloud function, on n’en apprend pas beaucoup plus que ce que la doc nous dit déjà. Un petit coup de “docker inspect” nous dit que l’entrypoint du conteneur est “/cnb/process/web”, donc on peut suspecter l’utilisation de cloud native buildpacks. Et notre fonction embarque comme dépendance le function framework de Google : https://cloud.google.com/functions/docs/functions-framework

All this fuss over fetching a tarball and executing the contents. (Kelsey Hightower)

blague de geek: à gauche un dinosaure, à droite un baril de pétrole ; légende: à gauche dinosaure, à droite dinosaure.zip

Du coup, au niveau Terraform, ça me permet de faire ce setup qui contiendra l’objet API gateway, la gateway de l’objet gateway, et la conf que j’ai templatisée.

Je fais un hash 256 de mon fichier de template, ce qui me permet d’avoir un nom aléatoire à chaque fois et de garder un état idempotent de mon déploiement sans changement. Je ne peux pas dater le nom de mon fichier de conf pour cette raison, et le service n’a pas prévu de faire tourner le même fichier de conf sous plusieurs versions. Donc conflit de nommage si on utilise un nom fixe.

J’utilise le data source de mon Cloud Run pour récupérer son URL et la templater dans ma conf.

Le reste de la conf est construit à la main et c’est pénible de rajouter et d’enlever de nouveaux URI sur la configuration. Disons que pour une dizaine de fonctions ça reste acceptable, mais au-delà, je pense que les maux de tête vont arriver assez vite.

Voici la conf minimale que j’ai mise en place.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
data "google_cloud_run_service" "hello_cloudrun" {
  location = var.region
  name     = "hello-cloudrun"
}

resource "google_api_gateway_api" "api_gateway" {
  project  = var.project
  provider = google-beta
  api_id   = "api-gateway"
}

locals {
  conf_template_file = "openapi.yaml.tpl"
}

resource "google_api_gateway_api_config" "api_gateway_configuration" {
  provider      = google-beta
  project       = var.project
  api           = google_api_gateway_api.api_gateway.api_id
  api_config_id = "api-conf-${substr(filesha256(local.conf_template_file), 1, 10)}"

  openapi_documents {
    document {
      path = "openapi.yaml"
      contents = base64encode(data.template_file.openapi_file_template.rendered)
    }
  }
  lifecycle {
    create_before_destroy = true
  }
}


data "template_file" "openapi_file_template" {
  template = "${file(local.conf_template_file)}"
  vars = {
    "cloudrun_url" = data.google_cloud_run_service.hello_cloudrun.status.0.url
  }
}

resource "google_api_gateway_gateway" "api_gateway_gateway" {
  region     = var.region
  provider   = google-beta
  project    = var.project
  api_config = google_api_gateway_api_config.api_gateway_configuration.id
  gateway_id = "api-gateway-gateway"
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
swagger: '2.0'
info:
  title: api-gateway
  description: demo generic API gateway
  version: 2.0.0
schemes:
  - https
produces:
  - application/json
x-google-management:
  metrics:
    - name: "hello-cloudrun-request"
      displayName: hello cloudrun quota"
      valueType: INT64
      metricKind: DELTA
  quota:
    limits:
      - name: "hello-cloudrun-limit"
        metric: "hello-cloudrun-request"
        unit: "1/min/{project}"
        values:
          STANDARD: 2
paths:
  /hello_cloudrun:
    post:
      summary: hello_cloudrun
      consumes:
      - application/json
      operationId: hello_cloudrun
      x-google-backend:
        address: ${cloudrun_url}/hello
      responses:
        '200':
          description: Successful response
          schema:
            type: string
      security:
      - api_key: []
      x-google-quota:
        metricCosts:
          "hello-cloudrun-request": 1

securityDefinitions:
  api_key:
    type: "apiKey"
    name: "key"
    in: "query"

Maintenant en ce qui concerne le rate limiting et l’authentification pas api key, ça rend un service acceptable.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
while true; do curl -m 70 -X POST https://api-gateway-gateway-294p3f5a.ew.gateway.dev/hello_cloudrun\?key\="AIzaSyAWAtNB7mLNPuU7RZ1846mlnnqv3T-IKEo" \                    
-H "Content-Type: application/json" \
-d '{
  "name": "Here is Johny"
}'; done
Hello Here is Johny!Hello Here is Johny!Hello Here is Johny!Hello Here is Johny!Hello Here is Johny!Hello Here is Johny!Hello Here is Johny!Hello Here is Johny!{"code":429,"message":"RESOURCE_EXHAUSTED:Quota exceeded for quota metric 'hello cloudrun quota\"' and limit 'hello cloudrun quota\" per minute' of service 'api-gateway-0u1xl5h9s52tt.apigateway.microservice-392612.cloud.goog' for consumer 'project_number:176603383918'."}
{"message":"RESOURCE_EXHAUSTED:Quota exceeded for quota metric 'hello cloudrun quota\"' and limit 'hello cloudrun quota\" per minute' of service 'api-gateway-0u1xl5h9s52tt.apigateway.microservice-392612.cloud.goog' for consumer 'project_number:176603383918'.","code":429}
Hello Here is Johny!{"message":"RESOURCE_EXHAUSTED:Quota exceeded for quota metric 'hello cloudrun quota\"' and limit 'hello cloudrun quota\" per minute' of service 'api-gateway-0u1xl5h9s52tt.apigateway.microservice-392612.cloud.goog' for consumer 'project_number:176603383918'.","code":429}
{"message":"RESOURCE_EXHAUSTED:Quota exceeded for quota metric 'hello cloudrun quota\"' and limit 'hello cloudrun quota\" per minute' of service 'api-gateway-0u1xl5h9s52tt.apigateway.microservice-392612.cloud.goog' for consumer 'project_number:176603383918'.","code":429}
{"message":"RESOURCE_EXHAUSTED:Quota exceeded for quota metric 'hello cloudrun quota\"' and limit 'hello cloudrun quota\" per minute' of service 'api-gateway-0u1xl5h9s52tt.apigateway.microservice-392612.cloud.goog' for consumer 'project_number:176603383918'.","code":429}
{"message":"RESOURCE_EXHAUSTED:Quota exceeded for quota metric 'hello cloudrun quota\"' and limit 'hello cloudrun quota\" per minute' of service 'api-gateway-0u1xl5h9s52tt.apigateway.microservice-392612.cloud.goog' for consumer 'project_number:176603383918'.","code":429}
{"message":"RESOURCE_EXHAUSTED:Quota exceeded for quota metric 'hello cloudrun quota\"' and limit 'hello cloudrun quota\" per minute' of service 'api-gateway-0u1xl5h9s52tt.apigateway.microservice-392612.cloud.goog' for consumer 'project_number:176603383918'.","code":429}
{"message":"RESOURCE_EXHAUSTED:Quota exceeded for quota metric 'hello cloudrun quota\"' and limit 'hello cloudrun quota\" per minute' of service 'api-gateway-0u1xl5h9s52tt.apigateway.microservice-392612.cloud.goog' for consumer 'project_number:176603383918'.","code":429}
{"message":"RESOURCE_EXHAUSTED:Quota exceeded for quota metric 'hello cloudrun quota\"' and limit 'hello cloudrun quota\" per minute' of service 'api-gateway-0u1xl5h9s52tt.apigateway.microservice-392612.cloud.goog' for consumer 'project_number:176603383918'.","code":429}
{"message":"RESOURCE_EXHAUSTED:Quota exceeded for quota metric 'hello cloudrun quota\"' and limit 'hello cloudrun quota\" per minute' of service 'api-gateway-0u1xl5h9s52tt.apigateway.microservice-392612.cloud.goog' for consumer 'project_number:176603383918'.","code":429}
{"message":"RESOURCE_EXHAUSTED:Quota exceeded for quota metric 'hello cloudrun quota\"' and limit 'hello cloudrun quota\" per minute' of service 'api-gateway-0u1xl5h9s52tt.apigateway.microservice-392612.cloud.goog' for consumer 'project_number:176603383918'.","code":429}
{"code":429,"message":"RESOURCE_EXHAUSTED:Quota exceeded for quota metric 'hello cloudrun quota\"' and limit 'hello cloudrun quota\" per minute' of service 'api-gateway-0u1xl5h9s52tt.apigateway.microservice-392612.cloud.goog' for consumer 'project_number:176603383918'."}
{"code":429,"message":"RESOURCE_EXHAUSTED:Quota exceeded for quota metric 'hello cloudrun quota\"' and limit 'hello cloudrun quota\" per minute' of service 'api-gateway-0u1xl5h9s52tt.apigateway.microservice-392612.cloud.goog' for consumer 'project_number:176603383918'."}

le message d’erreur est pas super mais au moins le code de retour est correct “429 Too Many Requests” et une fois la barrière du premier setup c’est assez simple de mettre de nouveaux entrypoints. du moins sur des fonctions ultra basiques.

On essaye des trucs plus complexes ?

Maintenant, en ce qui concerne le rate limiting et l’authentification par API key, ça rend un service acceptable.

meme, my life is patato

J’ai adapté l’exemple du FastAPI CRUD Router pour avoir un peu plus qu’un simple “hello world” et plusieurs routes à exposer dans le même conteneur.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
import os
import uuid

from pydantic import BaseModel
from fastapi import FastAPI, HTTPException, status, Depends
from google.cloud import bigquery

app = FastAPI(openapi_url="/potato/openapi.json", docs_url="/potato/docs")

client = bigquery.Client()

PROJECT = "microservice-392612"
BQ_DATASET = "potatoes"
BQ_TABLE = "potatoes"


class PotatoCreate(BaseModel):
    thickness: float
    mass: float
    color: str
    type: str


class Potato(BaseModel):
    uuid: str
    thickness: float
    mass: float
    color: str
    type: str

    class Config:
        orm_mode = True


def get_db():
    return client


@app.post("/potato/", response_model=Potato)
def create_potato(potato: PotatoCreate, db: bigquery.Client = Depends(get_db)):
    new_uuid = str(uuid.uuid4())

    new_potato_data = dict(potato, uuid=new_uuid)

    job_config = bigquery.LoadJobConfig(
        schema=[
            bigquery.SchemaField("uuid", "STRING"),
            bigquery.SchemaField("thickness", "FLOAT"),
            bigquery.SchemaField("mass", "FLOAT"),
            bigquery.SchemaField("color", "STRING"),
            bigquery.SchemaField("type", "STRING"),
        ],
        write_disposition="WRITE_APPEND",
    )
    table_ref = db.dataset("potatoes").table("potatoes")

    # Load the new potato data into the table
    job = db.load_table_from_json([new_potato_data], table_ref, job_config=job_config)
    job.result()

    return {**new_potato_data}


@app.get("/potato/{potato_id}", response_model=Potato)
def get_potato(potato_id: str, db: bigquery.Client = Depends(get_db)):
    query = """
        SELECT * FROM `microservice-392612.potatoes.potatoes`
        WHERE uuid = @potato_id
    """
    query_params = [bigquery.ScalarQueryParameter("potato_id", "STRING", potato_id)]
    job_config = bigquery.QueryJobConfig(query_parameters=query_params)

    query_job = db.query(query, job_config=job_config)
    results = list(query_job)

    if not results:
        raise HTTPException(status_code=404, detail="Potato not found")

    row = results[0]
    return Potato(**row)


@app.delete("/potato/{potato_id}/", response_model=dict)
def delete_potato(potato_id: str, db: bigquery.Client = Depends(get_db)):

    select_query = f"""
        SELECT 1 FROM `{PROJECT}.{BQ_DATASET}.{BQ_TABLE}`
        WHERE uuid = @potato_id
        LIMIT 1
    """
    query_params = [bigquery.ScalarQueryParameter("potato_id", "STRING", potato_id)]
    job_config = bigquery.QueryJobConfig(query_parameters=query_params)

    query_job = db.query(select_query, job_config=job_config)
    results = list(query_job)

    if not results:
        raise HTTPException(status_code=404, detail="Potato not found")

    delete_query = f"""
        DELETE FROM `{PROJECT}.{BQ_DATASET}.{BQ_TABLE}`
        WHERE uuid = @potato_id
    """
    delete_job = db.query(delete_query, job_config=job_config)
    delete_job.result()
    return {"message": "Potato deleted successfully"}


@app.get("/potatoes/", response_model=list[Potato])
def get_all_potatoes(db: bigquery.Client = Depends(get_db)):
    query = f"""
        SELECT * FROM `{PROJECT}.{BQ_DATASET}.{BQ_TABLE}`
    """
    query_job = db.query(query)
    results = list(query_job)

    return [Potato(**row) for row in results]

Je vous avoue que je n’ai pas fait d’effort particulier pour faire du beau code. Je voulais juste un service minimum qui fait lecture, écriture et suppression dans BigQuery. J’ai essayé d’utiliser le CRUD Router pour cela, mais clairement, ce n’est pas du tout fait pour ça !

J’ai pris un rabbit hole assez vite, même ChatGPT m’a dit de laisser tomber l’idée.

lapin blanc , Alice au pays des merveilles

Au final, on peut voir les routes de cette façon :

1
2
3
4
5
6
GET /potato/openapi.json
GET /potato/docs
POST /potato/
GET /potato/{potato_id}/
DELETE /potato/{potato_id}/
GET /potatoes/

Un CRUD de base, un GET all et la documentation OpenAPI. Juste ce qu’il faut pour un peu plus qu’un simple “hello world”.

Je pensais que je pourrais facilement adapter mon OpenAPI autogénéré, mais c’est même pas la peine d’y penser. Ça a été une grosse galère pour exposer les URLs sans erreur, et encore, le travail de mapping n’est même pas encore complet.

Je me trouve dans un état un peu pourri où je dois injecter mon token dans toutes mes URLs, et bien sûr, FastAPI devrait être adapté pour ça. Autant dire que la charge cognitive que ça représente pour le service rendu est juste parfaitement débile.

Je vous mets le fichier de template que j’ai bricolé, parce que franchement, ça serait dommage de jeter ça. Bonne indigestion :)

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
swagger: '2.0'
info:
  title: api-gateway
  description: demo generic API gateway
  version: 2.0.0
schemes:
  - https
produces:
  - application/json
x-google-management:
  metrics:
    - name: "cloudrun-request"
      displayName: cloudrun quota"
      valueType: INT64
      metricKind: DELTA
  quota:
    limits:
      - name: "cloudrun-limit"
        metric: "cloudrun-request"
        unit: "1/min/{project}"
        values:
          STANDARD: 2
paths:

  /potato/openapi.json:
    get:
      summary: get openapi.json file 
      consumes:
      - text/html
      operationId: potato_openapi
      x-google-backend:
        address: ${cloudrun_potato_url}/potato/openapi.json
      responses:
        '200':
          description: get openapi.json
          schema:
            type: string

  /potato/docs:
    get:
      summary: get documentation 
      consumes:
      - text/html
      operationId: potato_docs
      x-google-backend:
        address: ${cloudrun_potato_url}/potato/docs
      responses:
        '200':
          description: get documentation 
          schema:
            type: string
      security:
      - api_key: []
      x-google-quota:
        metricCosts:
          "cloudrun-request": 10

  /potato:
    post:
      summary: create potato
      consumes:
      - application/json
      operationId: potato
      x-google-backend:
        address: ${cloudrun_potato_url}/potato
      responses:
        '200':
          description: create potato
          schema:
            type: string
      security:
      - api_key: []
      x-google-quota:
        metricCosts:
          "cloudrun-request": 10

  /potatoes:
    get:
      summary: get all patatoes
      consumes:
      - application/json
      operationId: potatoes
      x-google-backend:
        address: ${cloudrun_potato_url}/potatoes
      responses:
        '200':
          description: get all potatoes
          schema:
            type: string
      security:
      - api_key: []
      security:
      - api_key: []
      x-google-quota:
        metricCosts:
          "cloudrun-request": 10

securityDefinitions:
  api_key:
    type: "apiKey"
    name: "key"
    in: "query"

On passe à l’addition (ou la soustraction de features)

Donc, dans la liste des trucs énervants :

  • Les doubles mappings de route (dans le Cloud Run et dans l’API) alors qu’on a déjà un Swagger propre et à jour généré automatiquement.
  • La non-compatibilité avec OpenAPI 3.
  • Je n’ai pas trouvé de webpoint qui pourrait exposer l’OpenAPI de l’API gateway, et franchement, ça m’a fatigué d’essayer de le faire, alors j’ai abandonné l’idée.
  • Le changement du fichier de la conf est looooooooooooong.
  • Les features sont basiques au possible.
  • Le rate limiting est super chiant à mettre en place.
  • Le token API est un token long qui ne change jamais et doit être passé en requêtes GET.
  • On peut faire une quantité très limitée d’API tokens (10 max), mais ce sont des tokens longs, ce n’est pas forcément une mauvaise chose.

Je n’ai pas vraiment compris comment fonctionne le calcul du rate limiting, mais je n’ai pas creusé plus loin, tellement ça m’a fatigué. Je n’ai pas tenté d’utiliser un identity provider tiers, étant donné le challenge débile que représentait cette configuration pour un “truc facile”. Je ne suis pas du tout convaincu par l’ergonomie et le service rendu sur ce service managé. Je reste persuadé qu’il va dégager au profit d’Apigee ou d’une autre solution plus mature et plus simple à utiliser.

Dessin animé, Zou

En ce qui me concerne, j’utiliserais plutôt un load balancer/Cloud Armor avec redirection vers Cloud Run pour bannir manuellement les méchants si j’étais dans une phase early de mon projet. Pourquoi pas tester Apigee au moment où un besoin de plus de sécurité se ferait sentir ?

Finalement, on a mieux à faire de gérer les routes vers des multi workloads directement depuis le load balancer, sans s’embêter à faire un mapping infernal et perdre un temps colossal pour un service peu rendu.

Autant gérer autrement l’authentification avec un JWT et mettre en place le rate limiting dans le code avec Redis, pourquoi pas.

Je dirais que par rapport à la qualité des services de base de Google Cloud, on est clairement sur un produit pas fini. Je n’ai aucune envie de m’embêter avec un truc pareil pour protéger mon backend tellement c’est casse-pieds !

Ou alors, c’est moi qui n’ai rien compris et je fais totalement de la merde, c’est aussi possible !

Déguisement de chien en forme d’usine avec le label “poop factory”.