Source code of my website
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

🚧 : add docs draft

+794
content/posts/drafts/2026-04-10-docs-sur-scaleway/deploy-architecture.png

This is a binary file and will not be displayed.

content/posts/drafts/2026-04-10-docs-sur-scaleway/github-docs.png

This is a binary file and will not be displayed.

+794
content/posts/drafts/2026-04-10-docs-sur-scaleway/index.md
··· 1 + --- 2 + date: 2026-04-10 3 + title: "Installer docs sur Scaleway" 4 + slug: docs-scaleway 5 + tags: 6 + - kubernetes 7 + - scaleway 8 + - souveraineté 9 + --- 10 + 11 + Vous avez probablement vu passer, comme moi, les annonces concernant "La Suite numérique". 12 + La Suite est un ensemble d'outils développés par la DINUM, pour équiper les agents du service public d'un outillage moderne et intégré, qui vise à remplacer les outils des GAFAM (Google Workspace et Office 365 dans le viseur). 13 + 14 + La suite se veut aussi être Open Source. Le code est disponible sur [GitHub](https://github.com/suitenumerique). 15 + 16 + Et il s'avère qu'on peut facilement héberger sa propre instance. 17 + 18 + Aujourd'hui, j'ai donc installé la suite sur mon compte Scaleway, pour jouer un peu et pouvoir avoir à terme un remplaçant à Notion. 19 + 20 + <!--more--> 21 + 22 + ## Le repo github 23 + 24 + Le repo GitHub de docs est complet : README, CHANGELOG, CONTRIBUTING. C'est propre et clair. 25 + En bonus, un fichier UPGRADE.md est aussi présent pour aider à la mise à jour sur les différentes versions. 26 + 27 + ![Le GitHub de Docs](github-docs.png) 28 + 29 + > Amusant, les messages de commit sont rédigés avec [GitMoji](https://gitmoji.dev/) 🤩 30 + 31 + La documentation technique est assez bien fournie également. Elle est située dans le dossier `docs/` du repo (docs-ception 😅). 32 + 33 + On y retrouve des ADRs (un seul pour l'instant), quelques schémas d'architecture, des procédures d'installation et de configuration avec les variables d'environnement. 34 + 35 + ## L'architecture cible 36 + 37 + En quelques clics, je tombe sur la [procédure d'installation de docs sur Kubernetes](https://github.com/suitenumerique/docs/blob/main/docs/installation/kubernetes.md). 38 + 39 + Quelques prérequis sont listés : 40 + 41 + * Un cluster Kubernetes (jusque là, pas du surprise) 42 + * Un provider OIDC pour authentifier les utilisateurs 43 + * Un serveur PostgreSQL 44 + * Un serveur Redis 45 + * Un bucket S3 pour stocker les fichiers 46 + 47 + La doc propose des exemples de solution complète avec Keycloak pour l'authentification, et Minio pour la partie S3. 48 + 49 + Pour mon installation sur Scaleway, je vais partir sur une solution managée. 50 + 51 + * Un cluster Kapsule 52 + * Un Keycloak installé dans le cluster 53 + * Deux instances serverless de PostgreSQL (une pour Keycloak et une pour docs) 54 + * Une instance de Redis 55 + * Un bucket object storage 56 + 57 + Les différentes ressources seront provisionnées avec OpenTofu. 58 + 59 + Le déploiement et la configuration de Keycloak et de Docs se feront avec Helm. 60 + 61 + ![Architecture du déploiement](deploy-architecture.png) 62 + 63 + ## Configuration de OpenTofu 64 + 65 + ### Création de l'application et de l'API Key pour OpenTofu 66 + 67 + J'installe la dernière version du CLI Scaleway et d'OpenTofu avec `mise` : 68 + 69 + ```shell 70 + ❯ mise use scaleway 71 + scaleway@2.54.0 72 + 73 + ❯ scw version 74 + Version 2.54.0 75 + BuildDate 2026-04-02T07:01:46Z 76 + GoVersion go1.26.1 77 + GitBranch HEAD 78 + GitCommit da45726d 79 + GoArch amd64 80 + GoOS linux 81 + UserAgentPrefix scaleway-cli 82 + 83 + ❯ mise use opentofu 84 + opentofu@1.11.6 85 + 86 + ❯ tofu version 87 + OpenTofu v1.11.6 88 + on linux_amd64 89 + ``` 90 + 91 + Pour commencer, je configure OpenTofu avec mon compte Scaleway. 92 + 93 + Je crée un projet "Docs" avec le CLI : 94 + 95 + ```shell 96 + ❯ scw account project create name=Docs 97 + ID 7f715427-5a38-49ee-a2f5-910ec786506c 98 + Name Docs 99 + OrganizationID d2f60dce-f716-4e45-96cb-3837fe56f0d9 100 + CreatedAt now 101 + UpdatedAt now 102 + Description - 103 + ``` 104 + 105 + Ensuite, je crée une application pour mon OpenTofu : 106 + 107 + ```shell 108 + ❯ scw iam application create name=opentofu-docs 109 + ID 092e0bdd-72b2-491e-947e-bcc1c9ce4e5c 110 + Name opentofu-docs 111 + Description - 112 + CreatedAt now 113 + UpdatedAt now 114 + OrganizationID d2f60dce-f716-4e45-96cb-3837fe56f0d9 115 + Editable true 116 + Deletable true 117 + Managed false 118 + NbAPIKeys 0 119 + ``` 120 + 121 + Et j'attache une policy à mon application avec des droits élevés sur le projet : 122 + 123 + ```shell 124 + ❯ scw iam policy create name=opentofu-docs application-id=092e0bdd-72b2-491e-947e-bcc1c9ce4e5c rules.0.project-ids.0=7f715427-5a38-49ee-a2f5-910ec786506c rules.0.permission-set-names.0=AllProductsFullAccess 125 + ID c55a11d4-9b80-4aa8-a508-82de286aef84 126 + Name opentofu-docs 127 + Description - 128 + OrganizationID d2f60dce-f716-4e45-96cb-3837fe56f0d9 129 + CreatedAt now 130 + UpdatedAt now 131 + Editable true 132 + Deletable true 133 + Managed false 134 + NbRules 0 135 + NbScopes 0 136 + NbPermissionSets 0 137 + ApplicationID 092e0bdd-72b2-491e-947e-bcc1c9ce4e5c 138 + ``` 139 + 140 + Enfin, j'extrait une clé d'API pour mon application : 141 + 142 + ```shell 143 + ❯ scw iam api-key create application-id=092e0bdd-72b2-491e-947e-bcc1c9ce4e5c default-project-id=7f715427-5a38-49ee-a2f5-910ec786506c 144 + AccessKey SCW6DXYWXQE1EF0F7QV0 145 + SecretKey 11a461bb-74fc-409b-b1ff-19fffc270ea4 146 + ApplicationID 092e0bdd-72b2-491e-947e-bcc1c9ce4e5c 147 + Description - 148 + CreatedAt now 149 + UpdatedAt now 150 + DefaultProjectID 7f715427-5a38-49ee-a2f5-910ec786506c 151 + Editable true 152 + Deletable true 153 + Managed false 154 + CreationIP 81.254.129.78 155 + ``` 156 + 157 + Je sauvegarde l'id du projet, les Access Key et Secret Key dans des variables d'environnement pour que OpenTofu puisse les utiliser : 158 + 159 + ```shell 160 + ❯ fnox set SCW_DEFAULT_PROJECT_ID 161 + Enter secret value ************ 162 + ✓ Set secret SCW_DEFAULT_PROJECT_ID 163 + 164 + ❯ fnox set SCW_ACCESS_KEY 165 + Enter secret value ************ 166 + ✓ Set secret SCW_ACCESS_KEY 167 + 168 + ❯ fnox set SCW_SECRET_KEY 169 + Enter secret value ************ 170 + ✓ Set secret SCW_SECRET_KEY 171 + ``` 172 + 173 + ### Configuration du provider Scaleway 174 + 175 + Je commence par créer un Bucket pour venir y stocker le state de mon projet : 176 + 177 + ```shell 178 + ❯ scw object bucket create name=docs-opentofu-state 179 + ✅ Success. 180 + ID docs-opentofu-state 181 + Region fr-par 182 + APIEndpoint https://s3.fr-par.scw.cloud 183 + BucketEndpoint https://docs-opentofu-state.s3.fr-par.scw.cloud 184 + EnableVersioning false 185 + Owner 7f715427-5a38-49ee-a2f5-910ec786506c 186 + ``` 187 + 188 + Une fois le bucket créé, je peux configurer le provider Scaleway et un backend S3 pour l'utiliser pour son state : 189 + 190 + ```hcl 191 + provider "scaleway" {} 192 + 193 + terraform { 194 + backend "s3" { 195 + bucket = "docs-opentofu-state" 196 + key = "docs.tfstate" 197 + region = "fr-par" 198 + endpoint = "https://s3.fr-par.scw.cloud" 199 + skip_credentials_validation = true 200 + use_path_style = true 201 + skip_region_validation = true 202 + skip_requesting_account_id = true 203 + } 204 + } 205 + ``` 206 + 207 + Le backend S3 a besoin des access key et secret key au format AWS, donc je duplique mes clés : 208 + 209 + ```shell 210 + ❯ fnox set AWS_ACCESS_KEY_ID $(fnox get SCW_ACCESS_KEY) 211 + ✓ Set secret AWS_ACCESS_KEY_ID 212 + 213 + ❯ fnox set AWS_SECRET_ACCESS_KEY $(fnox get SCW_SECRET_KEY) 214 + ✓ Set secret AWS_SECRET_ACCESS_KEY 215 + ``` 216 + 217 + L'exécution de `tofu init` finalise mon setup, je peux commencer à configurer mon infrastructure : 218 + 219 + ```shell 220 + ❯ tofu init 221 + 222 + Initializing the backend... 223 + 224 + Initializing provider plugins... 225 + - Reusing previous version of hashicorp/scaleway from the dependency lock file 226 + - Using previously-installed hashicorp/scaleway v2.71.0 227 + 228 + OpenTofu has been successfully initialized! 229 + ``` 230 + 231 + ## Création de l'infrastructure pour Keycloak 232 + 233 + ### Les réseaux privés 234 + 235 + Pour commencer, je crée l'infrastructure réseau : 236 + 237 + * un VPC 238 + * un subnet qui sera utilisé par le cluster Kapsule 239 + * un subnet qui sera utilisé par les bases de données 240 + 241 + ```hcl 242 + resource "scaleway_vpc" "this" { 243 + name = "docs-vpc" 244 + enable_routing = true 245 + } 246 + 247 + resource "scaleway_vpc_private_network" "pn_kapsule" { 248 + name = "kapsule-subnet" 249 + 250 + vpc_id = scaleway_vpc.this.id 251 + } 252 + 253 + resource "scaleway_vpc_private_network" "pn_db" { 254 + name = "database-subnet" 255 + 256 + vpc_id = scaleway_vpc.this.id 257 + } 258 + ``` 259 + 260 + Un coup de `tofu apply` et l'infratructure réseau est prête : 261 + 262 + ```shell 263 + ❯ tofu apply 264 + 265 + OpenTofu used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: 266 + + create 267 + 268 + OpenTofu will perform the following actions: 269 + 270 + # scaleway_vpc.this will be created 271 + + resource "scaleway_vpc" "this" 272 + 273 + # scaleway_vpc_private_network.pn_db will be created 274 + + resource "scaleway_vpc_private_network" "pn_db" 275 + 276 + # scaleway_vpc_private_network.pn_kapsule will be created 277 + + resource "scaleway_vpc_private_network" "pn_kapsule" 278 + 279 + Plan: 3 to add, 0 to change, 0 to destroy. 280 + 281 + Do you want to perform these actions? 282 + OpenTofu will perform the actions described above. 283 + Only 'yes' will be accepted to approve. 284 + 285 + Enter a value: yes 286 + 287 + scaleway_vpc.this: Creating... 288 + scaleway_vpc.this: Creation complete after 1s [id=fr-par/c616f8b0-69b2-487f-a9ba-4bbe5b2a12c0] 289 + scaleway_vpc_private_network.pn_kapsule: Creating... 290 + scaleway_vpc_private_network.pn_db: Creating... 291 + scaleway_vpc_private_network.pn_db: Creation complete after 1s [id=fr-par/bdd5962a-e390-4d9d-a6d3-19b616597cc8] 292 + scaleway_vpc_private_network.pn_kapsule: Creation complete after 1s [id=fr-par/254c2900-0022-4ebf-a7da-2e564acace10] 293 + ``` 294 + 295 + ![img.png](scaleway-vpc-subnets.png) 296 + 297 + ### Création du cluster Kapsule 298 + 299 + Je crée ensuite le cluster Kapsule et un node pool minimaliste : 300 + 301 + ```hcl 302 + # Use the latest version 303 + data "scaleway_k8s_version" "latest" { 304 + name = "latest" 305 + } 306 + 307 + resource "scaleway_k8s_cluster" "this" { 308 + name = "docs-cluster" 309 + version = data.scaleway_k8s_version.latest.name 310 + 311 + type = "kapsule" 312 + cni = "cilium" 313 + private_network_id = scaleway_vpc_private_network.pn_kapsule.id 314 + 315 + delete_additional_resources = false 316 + } 317 + 318 + resource "scaleway_k8s_pool" "pool" { 319 + cluster_id = scaleway_k8s_cluster.this.id 320 + name = "docs-cluster-pool" 321 + node_type = "DEV1-M" 322 + size = 1 323 + } 324 + 325 + output "cluster_id" { 326 + value = scaleway_k8s_cluster.this.id 327 + } 328 + ``` 329 + 330 + L'exécution de `tofu apply` donne la sortie suivante : 331 + 332 + ```shell 333 + ❯ tofu apply 334 + data.scaleway_k8s_version.latest: Reading... 335 + scaleway_vpc.this: Refreshing state... [id=fr-par/c616f8b0-69b2-487f-a9ba-4bbe5b2a12c0] 336 + data.scaleway_k8s_version.latest: Read complete after 0s [id=fr-par/1.35.3] 337 + scaleway_vpc_private_network.pn_db: Refreshing state... [id=fr-par/bdd5962a-e390-4d9d-a6d3-19b616597cc8] 338 + scaleway_vpc_private_network.pn_kapsule: Refreshing state... [id=fr-par/254c2900-0022-4ebf-a7da-2e564acace10] 339 + 340 + OpenTofu used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: 341 + + create 342 + 343 + OpenTofu will perform the following actions: 344 + 345 + # scaleway_k8s_cluster.this will be created 346 + + resource "scaleway_k8s_cluster" "this" 347 + 348 + # scaleway_k8s_pool.pool will be created 349 + + resource "scaleway_k8s_pool" "pool" 350 + 351 + Plan: 2 to add, 0 to change, 0 to destroy. 352 + 353 + Do you want to perform these actions? 354 + OpenTofu will perform the actions described above. 355 + Only 'yes' will be accepted to approve. 356 + 357 + Enter a value: yes 358 + 359 + scaleway_k8s_cluster.this: Creating... 360 + scaleway_k8s_cluster.this: Creation complete after 5s [id=fr-par/c8983aa1-0290-42eb-bcb0-e20240146992] 361 + scaleway_k8s_pool.pool: Creating... 362 + scaleway_k8s_pool.pool: Creation complete after 3m12s [id=fr-par/537be92b-65c9-4a9a-a0e9-cd43109beaa5] 363 + 364 + Outputs: 365 + 366 + cluster_id = "fr-par/c8983aa1-0290-42eb-bcb0-e20240146992" 367 + ``` 368 + 369 + Après quelques minutes, le cluster est prêt : 370 + 371 + ![img.png](scaleway-cluster.png) 372 + 373 + Je peux extraire un fichier `kubeconfig` avec la commande suivante : 374 + 375 + ```shell 376 + ❯ scw k8s kubeconfig get c8983aa1-0290-42eb-bcb0-e20240146992 > kubeconfig.yaml 377 + 378 + ❯ mise set KUBECONFIG=kubeconfig.yaml 379 + ``` 380 + 381 + et essayer de m'y connecter : 382 + 383 + ```shell 384 + ❯ k get nodes 385 + NAME STATUS ROLES AGE VERSION 386 + scw-docs-cluster-docs-cluster-pool-d0f0306b16a Ready <none> 9m43s v1.35.3 387 + ``` 388 + 389 + ### Création de la base de données de Keycloak 390 + 391 + Je crée ensuite les ressources nécessaires à Keycloak, sa base de données Serverless SQL, une application et une API Key pour l'authentification, et je stocke les informations dans un secret de Secret Manager : 392 + 393 + ```hcl 394 + resource scaleway_iam_application "keycloak" { 395 + name = "keycloak" 396 + } 397 + 398 + resource scaleway_iam_policy "keycloak" { 399 + name = "Keycloak Database Access" 400 + application_id = scaleway_iam_application.keycloak.id 401 + rule { 402 + project_ids = [data.scaleway_account_project.this.id] 403 + permission_set_names = ["ServerlessSQLDatabaseReadWrite"] 404 + } 405 + } 406 + 407 + resource scaleway_iam_api_key "keycloak" { 408 + application_id = scaleway_iam_application.keycloak.id 409 + } 410 + 411 + resource scaleway_sdb_sql_database "keycloak" { 412 + name = "keycloak-database" 413 + min_cpu = 0 414 + max_cpu = 1 415 + } 416 + 417 + resource "scaleway_secret" "keycloak" { 418 + name = "keycloak_database" 419 + type = "database_credentials" 420 + } 421 + 422 + resource "scaleway_secret_version" "keycloak" { 423 + secret_id = scaleway_secret.keycloak.id 424 + data_wo = jsonencode({ 425 + engine = "postgresql" 426 + username = scaleway_iam_application.keycloak.id 427 + password = scaleway_iam_api_key.keycloak.secret_key 428 + host = scaleway_iam_api_key.keycloak.host 429 + port = "5432" 430 + dbname = scaleway_sdb_sql_database.keycloak.name 431 + }) 432 + } 433 + ``` 434 + 435 + Une fois `tofu` exécuté (je vous passe les logs), les ressources sont prêtes : 436 + 437 + ![La serverless database](scaleway-serverless-database.png) 438 + 439 + ![Le secret](scaleway-secret.png) 440 + 441 + ## Configuration de External Secrets 442 + 443 + Pour pouvoir récupérer les secrets de Secret Manager dans le cluster, Scaleway propose l'utilisation de [External Secrets](https://www.scaleway.com/en/docs/secret-manager/api-cli/external-secrets/#external-secrets---overview). 444 + 445 + L'installation se fait assez simplement : 446 + 447 + ```shell 448 + helm repo add external-secrets https://charts.external-secrets.io 449 + helm repo update 450 + 451 + helm upgrade --install external-secrets external-secrets/external-secrets \ 452 + -n external-secrets \ 453 + --create-namespace \ 454 + --set installCRDs=true 455 + ``` 456 + 457 + Une fois External Secrets installé, il faut lui donner des accès à l'API Scaleway pour qu'il puisse récupérer les secrets et les charger dans le cluster. 458 + 459 + Je crée donc une application, avec une policy read-only, et une API Key pour pour External Secrets, je fais ça avec Terraform cette fois-ci : 460 + 461 + ```hcl 462 + resource scaleway_iam_application "external_secrets" { 463 + name = "external_secrets" 464 + } 465 + 466 + resource scaleway_iam_policy "external_secrets" { 467 + name = "External Secrets Access" 468 + application_id = scaleway_iam_application.external_secrets.id 469 + rule { 470 + project_ids = [data.scaleway_account_project.this.id] 471 + permission_set_names = ["SecretManagerReadOnly", "SecretManagerSecretAccess"] 472 + } 473 + } 474 + 475 + resource scaleway_iam_api_key "external_secrets" { 476 + application_id = scaleway_iam_application.external_secrets.id 477 + } 478 + 479 + output "external_secrets_access_key" { 480 + value = scaleway_iam_api_key.external_secrets.access_key 481 + sensitive = true 482 + } 483 + 484 + output "external_secrets_secret_key" { 485 + value = scaleway_iam_api_key.external_secrets.secret_key 486 + sensitive = true 487 + } 488 + ``` 489 + 490 + Puis je crée ensuite le fichier de configuration de External Secrets avec les informations de l'API Key : 491 + 492 + ```shell 493 + ❯ kubectl create secret generic scwsm-secret --namespace external-secrets \ 494 + --from-literal=access_key=$(tofu output --raw external_secrets_access_key) \ 495 + --from-literal=secret_key=$(tofu output --raw external_secrets_secret_key) 496 + secret/scwsm-secret created 497 + ``` 498 + 499 + Le ClusterSecretStore va permettre de pouvoir récupérer les différents secrets depuis chaque namespace. 500 + 501 + ```yaml 502 + apiVersion: external-secrets.io/v1 503 + kind: ClusterSecretStore 504 + metadata: 505 + name: secret-store 506 + spec: 507 + provider: 508 + scaleway: 509 + region: fr-par 510 + projectId: 7f715427-5a38-49ee-a2f5-910ec786506c 511 + accessKey: 512 + secretRef: 513 + namespace: external-secrets 514 + name: scwsm-secret 515 + key: access_key 516 + secretKey: 517 + secretRef: 518 + namespace: external-secrets 519 + name: scwsm-secret 520 + key: secret_key 521 + ``` 522 + 523 + ```shell 524 + ❯ k get clustersecretstore 525 + NAME AGE STATUS CAPABILITIES READY 526 + secret-store 55s Valid ReadWrite True 527 + ``` 528 + 529 + ## Installation et configuration de Keycloak 530 + 531 + Maintenant que tout est prêt, je passe à l'installation de Keycloak sur le cluster. 532 + Je commence par créer un namespace pour Keycloak : 533 + 534 + ```shell 535 + ❯ kubectl create ns keycloak 536 + namespace/keycloak created 537 + ``` 538 + 539 + Puis je charge les secrets pour Keycloak en utilisant External Secrets : 540 + 541 + ```yaml 542 + apiVersion: external-secrets.io/v1 543 + kind: ExternalSecret 544 + metadata: 545 + name: keycloak-database-secret 546 + namespace: keycloak 547 + spec: 548 + refreshInterval: 1h 549 + secretStoreRef: 550 + kind: ClusterSecretStore 551 + name: secret-store 552 + target: 553 + name: keycloak-database-secret 554 + creationPolicy: Owner 555 + data: 556 + - secretKey: host 557 + remoteRef: 558 + key: name:keycloak_database 559 + property: host 560 + - secretKey: port 561 + remoteRef: 562 + key: name:keycloak_database 563 + property: port 564 + - secretKey: username 565 + remoteRef: 566 + key: name:keycloak_database 567 + property: username 568 + - secretKey: password 569 + remoteRef: 570 + key: name:keycloak_database 571 + property: password 572 + - secretKey: dbname 573 + remoteRef: 574 + key: name:keycloak_database 575 + property: dbname 576 + ``` 577 + 578 + Une fois l'External Secret chargé, les secrets de la database sont disponibles dans le cluster : 579 + 580 + ```shell 581 + ❯ k describe secret keycloak-database-secret 582 + Name: keycloak-database-secret 583 + Namespace: keycloak 584 + Labels: reconcile.external-secrets.io/created-by=f674ac18cab6d6ee187f41973b27253a186407dda3d4cc7b8a192cfd 585 + reconcile.external-secrets.io/managed=true 586 + Annotations: reconcile.external-secrets.io/data-hash: b5e84d691dc28d966906283f6aaf385c5cd406e82ba7ce7326e30299 587 + 588 + Type: Opaque 589 + 590 + Data 591 + ==== 592 + dbname: 17 bytes 593 + host: 43 bytes 594 + password: 36 bytes 595 + port: 4 bytes 596 + username: 36 bytes 597 + ``` 598 + 599 + Je démarre ensuite mon instance de Keycloak : 600 + 601 + ```shell 602 + ❯ k apply -f keycloak/statefulset.yaml 603 + statefulset.apps/keycloak configured 604 + ``` 605 + 606 + Après un peu d'attente, le serveur Keycloak est démarré : 607 + 608 + ```text 609 + 2026-04-10 15:10:04,527 INFO [org.keycloak.services.resources.KeycloakApplication] (pool-6-thread-1) Bootstrap completed in 14.594000 seconds 610 + ``` 611 + 612 + ### Exposer le Keycloak sur Internet 613 + 614 + Je vais utiliser un sous-domaine `auth.codeka.io` pour y exposer mon Keycloak et `docs.codeka.io` pour y exposer mon instance de Docs. 615 + 616 + Pour exposer mes instance sur Internet, il me faut utiliser un Ingress sur mon cluster Kapsule. 617 + 618 + J'installe donc Traefik (je passe sur la configuration du TLS) : 619 + 620 + ```shell 621 + helm repo add traefik https://traefik.github.io/charts 622 + helm repo update 623 + 624 + kubectl create namespace traefik 625 + helm install --namespace traefik traefik traefik/traefik 626 + ``` 627 + 628 + Une fois traefik démarré, un Load Balancer est crée sur mon cluster : 629 + 630 + ```shell 631 + ❯ k get svc 632 + NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE 633 + traefik LoadBalancer 10.32.7.27 163.172.173.104 80:31873/TCP,443:31696/TCP 2m44s 634 + ``` 635 + 636 + J'utilise l'IP externe de mon load balancer pour créer un enregistrement DNS sur mon domaine : 637 + 638 + ```shell 639 + ❯ dig +short auth.codeka.io 640 + 163.172.173.104 641 + ``` 642 + 643 + Pour expose mon Keycloak, je déclare une IngressRoute qui permet d'exposer mon instance en HTTPS : 644 + 645 + ```yaml 646 + apiVersion: traefik.io/v1alpha1 647 + kind: IngressRoute 648 + metadata: 649 + name: keycloak-ingress-route 650 + namespace: keycloak 651 + spec: 652 + entryPoints: 653 + - websecure 654 + routes: 655 + - match: Host(`auth.codeka.io`) 656 + kind: Rule 657 + services: 658 + - name: keycloak 659 + port: 8080 660 + tls: 661 + certResolver: le 662 + ``` 663 + 664 + Une fois tout ceci fait, mon instance de Keycloak est accessible : 665 + 666 + ![img.png](keycloak.png) 667 + 668 + La première moitié du travail est faite. 669 + 670 + Je peux maintenant attaquer à l'infrastructure pour Docs. 671 + 672 + ## Création de l'infrastructure pour Docs 673 + 674 + Docs a besoin d'une base de données PostgreSQL. 675 + Je reprends le même processus pour créer cette base de données que ce que j'ai fait pour Keycloak, pas de surprise sur cette partie. 676 + 677 + Cependant, Docs utilise des tables Temporaires, donc l'utilisation de Serverless SQL n'est pas possible. 678 + Il faut donc utiliser une Managed SQL Database. 679 + 680 + En complément, Docs a aussi besoin d'une instance de Redis et d'un bucket, je les crée avec OpenTofu : 681 + 682 + ```hcl 683 + # Redis Cluster 684 + resource "random_pet" "redis_user" { 685 + } 686 + 687 + resource "random_password" "redis_password" { 688 + length = 16 689 + min_upper = 1 690 + min_lower = 1 691 + min_numeric = 1 692 + min_special = 1 693 + override_special = "!@#$%^&*()_+-=[]{}|;:,.<>?" 694 + } 695 + 696 + resource "scaleway_redis_cluster" "docs" { 697 + name = "docs-redis-cluster" 698 + version = "8.4.0" 699 + node_type = "RED1-MICRO" 700 + user_name = random_pet.redis_user.id 701 + password = random_password.redis_password.result 702 + cluster_size = 1 703 + tls_enabled = "true" 704 + 705 + private_network { 706 + id = scaleway_vpc_private_network.pn_db.id 707 + } 708 + } 709 + 710 + resource "scaleway_secret" "docs_redis" { 711 + name = "docs_redis" 712 + type = "key_value" 713 + } 714 + 715 + resource "scaleway_secret_version" "docs_redis" { 716 + secret_id = scaleway_secret.docs_redis.id 717 + data_wo = jsonencode({ 718 + engine = "redis" 719 + host = scaleway_redis_cluster.docs.private_ips[0].address 720 + username = scaleway_redis_cluster.docs.user_name 721 + password = random_password.redis_password.result 722 + }) 723 + } 724 + ``` 725 + 726 + Docs a aussi besoin d'un bucket pour y stocker des fichiers. 727 + 728 + Après avoir exécuté le code OpenTofu, le cluster Redis est disponible ainsi que le bucket et les secrets : 729 + 730 + ![Le cluster Redis créé](scaleway-redis-cluster.png) 731 + 732 + ![Les secrets](scaleway-secrets.png) 733 + 734 + ## L'installation de Docs 735 + 736 + Tout est maintenant prêt pour l'installation de Docs ! 737 + 738 + Je commence par importer leur Chart : 739 + 740 + ```shell 741 + ❯ helm repo add impress https://suitenumerique.github.io/docs/ 742 + 743 + ❯ helm repo update 744 + ``` 745 + 746 + Puis je génère un fichier de values : 747 + 748 + ```shell 749 + ❯ helm show values impress/docs > values.yaml 750 + ``` 751 + 752 + Je crée le namespace pour Docs : 753 + 754 + ```shell 755 + ❯ kubectl create namespace docs 756 + namespace/docs created 757 + ``` 758 + 759 + J'applique ensuite le Chart : 760 + 761 + ```shell 762 + ❯ helm install docs impress/docs -f values.yaml --namespace docs 763 + 764 + ``` 765 + 766 + ## Liens et références 767 + 768 + * Le site de [présentation de Docs](https://lasuite.numerique.gouv.fr/produits/docs) au sein de La Suite 769 + * Le site de [Docs](https://docs.la-suite.eu/home/) 770 + * Le [GitHub de la Suite numérique](https://github.com/suitenumerique) 771 + * Le [GitHub de Docs](https://github.com/suitenumerique/docs) 772 + 773 + * [Installation de docs sur Kubernetes](https://github.com/suitenumerique/docs/blob/main/docs/installation/kubernetes.md) 774 + 775 + Scaleway et OpenTofu : 776 + * [Authentification du provider](https://registry.terraform.io/providers/scaleway/scaleway/latest/docs#authentication) 777 + * [Configuration du Backend S3](https://registry.terraform.io/providers/scaleway/scaleway/latest/docs/guides/backend_guide#alternative-store-terraform-state-in-scaleway-object-storage-without-locking) 778 + 779 + 780 + * [Deploying External Secrets on Kubernetes Kapsule](https://www.scaleway.com/en/docs/secret-manager/api-cli/external-secrets/#external-secrets---overview) 781 + 782 + * Les [permission sets](https://www.scaleway.com/en/docs/iam/reference-content/permission-sets/) de Scaleway 783 + 784 + Ressources Terraform : 785 + * Kubernetes : 786 + * [scaleway_k8s_version](https://registry.terraform.io/providers/scaleway/scaleway/latest/docs/data-sources/k8s_version) 787 + * [scaleway_k8s_cluster](https://registry.terraform.io/providers/scaleway/scaleway/latest/docs/resources/k8s_cluster) 788 + * [scaleway_k8s_pool](https://registry.terraform.io/providers/scaleway/scaleway/latest/docs/resources/k8s_pool) 789 + * Databases : 790 + * [scaleway_sdb_sql_database](https://registry.terraform.io/providers/scaleway/scaleway/latest/docs/resources/sdb_sql_database) 791 + * [scaleway_redis_cluster](https://registry.terraform.io/providers/scaleway/scaleway/latest/docs/resources/redis_cluster) 792 + * Secrets Manager : 793 + * [scaleway_secret](https://registry.terraform.io/providers/scaleway/scaleway/latest/docs/resources/secret) 794 + * [scaleway_secret_version](https://registry.terraform.io/providers/scaleway/scaleway/latest/docs/resources/secret_version)
content/posts/drafts/2026-04-10-docs-sur-scaleway/keycloak.png

This is a binary file and will not be displayed.

content/posts/drafts/2026-04-10-docs-sur-scaleway/scaleway-cluster.png

This is a binary file and will not be displayed.

content/posts/drafts/2026-04-10-docs-sur-scaleway/scaleway-redis-cluster.png

This is a binary file and will not be displayed.

content/posts/drafts/2026-04-10-docs-sur-scaleway/scaleway-secret.png

This is a binary file and will not be displayed.

content/posts/drafts/2026-04-10-docs-sur-scaleway/scaleway-secrets.png

This is a binary file and will not be displayed.

content/posts/drafts/2026-04-10-docs-sur-scaleway/scaleway-serverless-database.png

This is a binary file and will not be displayed.

content/posts/drafts/2026-04-10-docs-sur-scaleway/scaleway-vpc-subnets.png

This is a binary file and will not be displayed.