Gérer une infrastructure avec Terraform, CDKTF et NixOS
Vincent Bernat
Il y a quelques années, j’ai réduit mon infrastructure personnelle au strict minimum. Jusqu’en 2018, elle représentait une douzaine de conteneurs tournant sur un seul serveur Hetzner1. J’ai migré mon courriel vers Fastmail et mes zones DNS vers Gandi. Il ne me restait plus que mon blog à héberger. À ce jour, ma petite infrastructure est composée de 4 machines virtuelles exécutant NixOS sur Hetzner Cloud et Vultr, d’une poignée de zones DNS sur Gandi et Route 53, et de quelques distributions Cloudfront. Elle est gérée par CDK pour Terraform (CDKTF), tandis que les déploiements de NixOS sont gérés par NixOps.
Dans cet article, je présente brièvement Terraform, CDKTF et l’écosystème Nix. J’explique également comment utiliser Nix pour accéder à ces outils dans votre shell afin de les utiliser rapidement.
Mise à jour (11.2023)
Récemment, HashiCorp a opté pour la Business Source License pour tous ces logiciels. C’est une déception, notamment pour Terraform qui est un composant clé pour automatiser les infrastructures et qui a bénéficié d’une large communauté. Il existe désormais un fork communautaire, OpenTofu, mais celui-ci ne couvre pas CDKTF.
CDKTF : infrastructure en tant que code#
Terraform est un outil d’« infrastructure en tant que code ». Vous pouvez définir votre infrastructure en déclarant des ressources avec le langage HCL. Ce dernier possède quelques fonctionnalités supplémentaires, comme des boucles permettant de déclarer plusieurs ressources à partir d’une liste, des fonctions intégrées que vous pouvez appeler dans les expressions et l’expansion de variables dans les chaînes de caractères. Terraform s’appuie sur un large ensemble de fournisseurs pour gérer les ressources.
Gérer des serveurs#
Voici un court exemple utilisant le fournisseur pour Hetzner Cloud pour créer une machine virtuelle :
variable "hcloud_token" { sensitive = true } provider "hcloud" { token = var.hcloud_token } resource "hcloud_server" "web03" { name = "web03" server_type = "cpx11" image = "debian-11" datacenter = "nbg1-dc3" } resource "hcloud_rdns" "rdns4-web03" { server_id = hcloud_server.web03.id ip_address = hcloud_server.web03.ipv4_address dns_ptr = "web03.luffy.cx" } resource "hcloud_rdns" "rdns6-web03" { server_id = hcloud_server.web03.id ip_address = hcloud_server.web03.ipv6_address dns_ptr = "web03.luffy.cx" }
L’expressivité de HCL est assez limitée et je trouve qu’un langage généraliste est plus pratique pour décrire les ressources. C’est là qu’intervient CDK pour Terraform : vous pouvez gérer votre infrastructure à l’aide de votre langage de programmation préféré, notamment TypeScript, Go et Python. Voici l’exemple précédent utilisant CDKTF et TypeScript :
import { App, TerraformStack, Fn } from "cdktf"; import { HcloudProvider } from "./.gen/providers/hcloud/provider"; import * as hcloud from "./.gen/providers/hcloud"; class MyStack extends TerraformStack { constructor(scope: Construct, name: string) { super(scope, name); const hcloudToken = new TerraformVariable(this, "hcloudToken", { type: "string", sensitive: true, }); const hcloudProvider = new HcloudProvider(this, "hcloud", { token: hcloudToken.value, }); const web03 = new hcloud.server.Server(this, "web03", { name: "web03", serverType: "cpx11", image: "debian-11", datacenter: "nbg1-dc3", provider: hcloudProvider, }); new hcloud.rdns.Rdns(this, "rdns4-web03", { serverId: Fn.tonumber(web03.id), ipAddress: web03.ipv4Address, dnsPtr: "web03.luffy.cx", provider: hcloudProvider, }); new hcloud.rdns.Rdns(this, "rdns6-web03", { serverId: Fn.tonumber(web03.id), ipAddress: web03.ipv6Address, dnsPtr: "web03.luffy.cx", provider: hcloudProvider, }); } } const app = new App(); new MyStack(app, "cdktf-take1"); app.synth();
La commande de cdktf synth
génère un fichier de configuration pour
Terraform, terraform plan
prévisualise les changements et terraform apply
les applique. Maintenant que vous disposez d’un langage généraliste, vous pouvez
utiliser des fonctions.
Gérer des enregistrements DNS#
Si l’utilisation de CDKTF pour 4 serveurs web peut sembler un peu exagérée, il
en va tout autrement lorsqu’il s’agit de gérer quelques zones DNS. Avec
DNSControl, qui utilise JavaScript comme langage, j’ai pu définir la zone
bernat.ch
avec ce bout de code :
D("bernat.ch", REG_NONE, DnsProvider(DNS_BIND, 0), DnsProvider(DNS_GANDI), DefaultTTL('2h'), FastMailMX('bernat.ch', {subdomains: ['vincent']}), WebServers('@'), WebServers('vincent');
Cela produit 38 enregistrements. Avec CDKTF, j’écris :
new Route53Zone(this, "bernat.ch", providers.aws) .sign(dnsCMK) .registrar(providers.gandiVB) .www("@", servers) .www("vincent", servers) .www("media", servers) .fastmailMX(["vincent"]);
Toute la magie est située dans les fonctions appelées. Vous pouvez regarder le fichier dns.ts dans le dépôt cdktf-take1 pour comprendre comment cela fonctionne. Rapidement :
Route53Zone()
crée une zone sur Route 53,sign()
signe la zone avec une clé maître,registrar()
inscrit la zone auprès du registre de domaines et configure DNSSEC,www()
crée les enregistrementsA
etAAAA
pour les serveurs web,fastmailMX()
crée les enregistrementsMX
et d’autres enregistrements liés pour configurer Fastmail en tant que fournisseur de courriel.
Voici le contenu de la fonction fastmailMX()
. Elle génère quelques
enregistrements et retourne la zone en cours pour faciliter le chaînage :
fastmailMX(subdomains?: string[]) { (subdomains ?? []) .concat(["@", "*"]) .forEach((subdomain) => this.MX(subdomain, [ "10 in1-smtp.messagingengine.com.", "20 in2-smtp.messagingengine.com.", ]) ); this.TXT("@", "v=spf1 include:spf.messagingengine.com ~all"); ["mesmtp", "fm1", "fm2", "fm3"].forEach((dk) => this.CNAME(`${dk}._domainkey`, `${dk}.${this.name}.dkim.fmhosted.com.`) ); this.TXT("_dmarc", "v=DMARC1; p=none; sp=none"); return this; }
Je vous encourage à parcourir le dépôt pour plus de détails !
À propos de Pulumi#
Ma première tentative autour de Terraform a été d’utiliser Pulumi. Vous pouvez trouver cette tentative sur GitHub. C’est assez similaire à ce que je fais actuellement avec CDKTF. La principale différence est que j’utilise Python au lieu de TypeScript2 car ce dernier ne m’était pas familier à l’époque.
Pulumi est antérieur à CDKTF et il utilise une approche légèrement différente. CDKTF génère une configuration pour Terraform (au format JSON au lieu de HCL), laissant la planification, la gestion des états et le déploiement à ce dernier. Il est donc lié aux limites de ce qui peut être exprimé par Terraform, notamment lorsque vous devez transformer des données obtenues d’une ressource à une autre3. Pulumi a besoin de fournisseurs spécifiques pour chaque ressource. De nombreux fournisseurs encapsulent des fournisseurs Terraform.
Bien que Pulumi offre une bonne expérience utilisateur, je suis passé à CDKTF car écrire des fournisseurs pour Pulumi est une corvée. CDKTF ne nécessite pas de passer par cette étape. En dehors des grands acteurs (AWS, Azure et Google Cloud), l’existence, la qualité et la fraîcheur des fournisseurs Pulumi sont inégales. La plupart des fournisseurs s’appuient sur un fournisseur Terraform et il se peut qu’ils soient en retard de quelques versions, qu’il leur manque quelques ressources ou qu’ils présentent des bogues qui leur sont propres.
Lorsqu’un fournisseur n’existe pas, vous pouvez en écrire un à l’aide de la
bibliothèque pulumi-terraform-bridge. Le projet Pulumi fournit un
modèle à cet effet. J’ai eu une mauvaise expérience avec celui-ci
lors de l’écriture de fournisseurs pour Gandi et
Vultr : le Makefile
installe automatiquement
Pulumi en utilisant curl | sh
et ne fonctionne pas avec
/bin/sh
. Il y a un manque d’intérêt pour les contributions
communautaires4 ou même pour les fournisseurs pour les acteurs
tiers.
NixOS & NixOps#
Nix est un langage de programmation purement fonctionnel. Nix est aussi le nom du gestionnaire de paquets qui est construit au-dessus du langage Nix. Il permet aux utilisateurs d’installer des paquets de manière déclarative. nixpkgs est un dépôt de paquets. Vous pouvez installer Nix au-dessus d’une distribution Linux ordinaire. Si vous voulez plus de détails, une bonne ressource est le site officiel, notamment la section « Apprendre ». La courbe d’apprentissage est rude, mais la récompense est grande.
NixOS : distribution Linux déclarative#
NixOS est une distribution Linux construite au-dessus du gestionnaire de paquets Nix. Voici un bout de configuration pour ajouter quelques paquets :
environment.systemPackages = with pkgs; [ bat htop liboping mg mtr ncdu tmux ];
Il est possible de modifier une dérivation5 existante pour utiliser une version
différente, activer une fonctionnalité spécifique ou appliquer un correctif.
Voici comment j’active et configure Nginx pour désactiver le module stream
,
ajouter le module de compression Brotli et ajouter
le module d’anonymisation des adresses IP. De
plus, au lieu d’utiliser OpenSSL 3, je continue à utiliser OpenSSL 1.16.
services.nginx = { enable = true; package = (pkgs.nginxStable.override { withStream = false; modules = with pkgs.nginxModules; [ brotli ipscrub ]; openssl = pkgs.openssl_1_1; });
Si vous avez besoin d’ajouter certaines modifications, c’est également possible. À titre d’exemple, voici comment j’ai corrigé en avance les failles de sécurité découvertes en 2019 dans Nginx en attendant que cela soit corrigé dans NixOS7 :
services.nginx.package = pkgs.nginxStable.overrideAttrs (old: { patches = old.patches ++ [ # HTTP/2: reject zero length headers with PROTOCOL_ERROR. (pkgs.fetchpatch { url = https://github.com/nginx/nginx/commit/dbdd[…].patch; sha256 = "a48190[…]"; }) # HTTP/2: limited number of DATA frames. (pkgs.fetchpatch { url = https://github.com/nginx/nginx/commit/94c5[…].patch; sha256 = "af591a[…]"; }) # HTTP/2: limited number of PRIORITY frames. (pkgs.fetchpatch { url = https://github.com/nginx/nginx/commit/39bb[…].patch; sha256 = "1ad8fe[…]"; }) ]; });
Si cela vous intéresse, jetez un coup d’œil à ma configuration relativement
réduite : common.nix
contient la configuration à appliquer sur
tous les serveurs (SSH, utilisateurs, paquets communs), web.nix
contient la configuration pour les serveurs web et isso.nix
exécute Isso dans un conteneur systemd.
NixOps : outil de déploiement pour NixOS#
Sur un seul nœud, la configuration de NixOS se trouve dans le fichier
/etc/nixos/configuration.nix
. Après l’avoir modifiée, vous devez exécuter la
commande nixos-rebuild switch
. Nix va chercher toutes les dépendances
possibles dans le cache binaire et construit le reste. Il crée une nouvelle
entrée dans le menu du chargeur de démarrage et active la nouvelle
configuration.
Pour gérer plusieurs nœuds, il existe plusieurs options, dont NixOps,
deploy-rs, Colmena et morph. Je ne les connais pas toutes, mais de
mon point de vue, les différences ne sont pas si importantes. Il est également
possible de construire un tel outil soi-même car Nix fournit les blocs de
construction les plus importants : nix build
et nix copy
. NixOps est l’un
des premiers outils publiés mais je vous encourage à explorer les alternatives.
La configuration de NixOps est écrite avec la langage Nix. Voici une
configuration simplifiée pour déployer znc01.luffy.cx
, web01.luffy.cx
et
web02.luffy.cx
, à l’aide des fonctions server
et web
:
let server = hardware: name: imports: { deployment.targetHost = "${name}.luffy.cx"; networking.hostName = name; networking.domain = "luffy.cx"; imports = [ (./hardware/. + "/${hardware}.nix") ] ++ imports; }; web = hardware: idx: imports: server hardware "web${lib.fixedWidthNumber 2 idx}" ([ ./web.nix ] ++ imports); in { network.description = "Luffy infrastructure"; network.enableRollback = true; defaults = import ./common.nix; znc01 = server "exoscale" [ ./znc.nix ]; web01 = web "hetzner" 1 [ ./isso.nix ]; web02 = web "hetzner" 2 []; }
Connecter le tout avec Nix#
L’écosystème Nix est une solution unifiée aux différents problèmes liés à la gestion des logiciels et des configurations. Les environnements de développement déclaratifs et reproductibles en constituent une caractéristique très intéressante. Cela ressemble aux environnements virtuels de Python, mais ils ne sont pas spécifiques à un langage.
Courte introduction aux « flakes » Nix#
J’utilise les « flakes », une nouvelle fonctionnalité de Nix qui améliore
la reproductibilité en fixant toutes les dépendances et en isolant le processus
de construction. Bien que cette fonctionnalité soit marquée comme
expérimentale8, elle est de plus en plus populaire et vous pouvez
trouver flake.nix
et flake.lock
à la racine de certains dépôts.
A titre d’exemple, voici le contenu du flake.nix
livré avec Snimpy, un
outil SNMP interactif pour Python reposant sur libsmi, une bibliothèque C :
{ inputs = { nixpkgs.url = "nixpkgs"; flake-utils.url = "github:numtide/flake-utils"; }; outputs = { self, ... }@inputs: inputs.flake-utils.lib.eachDefaultSystem (system: let pkgs = inputs.nixpkgs.legacyPackages."${system}"; in { # nix build packages.default = pkgs.python3Packages.buildPythonPackage { name = "snimpy"; src = self; preConfigure = ''echo "1.0.0-0-000000000000" > version.txt''; checkPhase = "pytest"; checkInputs = with pkgs.python3Packages; [ pytest mock coverage ]; propagatedBuildInputs = with pkgs.python3Packages; [ cffi pysnmp ipython ]; buildInputs = [ pkgs.libsmi ]; }; # nix run + nix shell apps.default = { type = "app"; program = "${self.packages."${system}".default}/bin/snimpy"; }; # nix develop devShells.default = pkgs.mkShell { name = "snimpy-dev"; buildInputs = [ self.packages."${system}".default.inputDerivation pkgs.python3Packages.ipython ]; }; }); }
Si Nix est installé sur votre système :
nix run github:vincentbernat/snimpy
exécute Snimpy,nix shell github:vincentbernat/snimpy
fournit un shell avec Snimpy prêt à être utilisé,nix build github:vincentbernat/snimpy
construit le paquet Python,nix develop .
fournit un shell pour développer autour de Snimpy depuis un clône du dépôt9.
Pour plus d’informations sur les flakes, regardez le tutoriel de Tweag.
Nix et CDKTF#
A la racine du dépôt que j’utilise pour CDKTF, il y a un fichier
flake.nix
pour configurer un shell avec Terraform
et CDKTF installés et avec les variables d’environnement nécessaires pour
automatiser mon infrastructure.
Terraform est déjà présent dans nixpkgs, mais je dois appliquer une rustine sur le fournisseur Gandi. Ce n’est pas un problème avec Nix !
terraform = pkgs.terraform.withPlugins (p: [ p.aws p.hcloud p.vultr (p.gandi.overrideAttrs (old: { src = pkgs.fetchFromGitHub { owner = "vincentbernat"; repo = "terraform-provider-gandi"; rev = "feature/livedns-key"; hash = "sha256-V16BIjo5/rloQ1xTQrdd0snoq1OPuDh3fQNW7kiv/kQ="; }; })) ]);
CDKTF est écrit en TypeScript. J’ai un fichier
package.json
avec toutes les dépendances
nécessaires, y compris celles pour utiliser TypeScript comme langage cible :
{ "name": "cdktf-take1", "version": "1.0.0", "main": "main.js", "types": "main.ts", "private": true, "dependencies": { "@types/node": "^14.18.30", "cdktf": "^0.13.3", "cdktf-cli": "^0.13.3", "constructs": "^10.1.151", "eslint": "^8.27.0", "prettier": "^2.7.1", "ts-node": "^10.9.1", "typescript": "^3.9.10", "typescript-language-server": "^2.1.0" } }
J’utilise Yarn pour obtenir un fichier yarn.lock
qui peut ensuite être utilisé directement pour construire la dérivation
contenant toutes les dépendances :
nodeEnv = pkgs.mkYarnModules { pname = "cdktf-take1-js-modules"; version = "1.0.0"; packageJSON = ./package.json; yarnLock = ./yarn.lock; };
L’étape suivant est de générer les fournisseurs CDKTF à partir des fournisseurs Terraform et de les inclure dans une dérivation :
cdktfProviders = pkgs.stdenvNoCC.mkDerivation { name = "cdktf-providers"; nativeBuildInputs = [ pkgs.nodejs terraform ]; src = nix-filter { root = ./.; include = [ ./cdktf.json ./tsconfig.json ]; }; buildPhase = '' export HOME=$(mktemp -d) export CHECKPOINT_DISABLE=1 export DISABLE_VERSION_CHECK=1 export PATH=${nodeEnv}/node_modules/.bin:$PATH ln -nsf ${nodeEnv}/node_modules node_modules # Build all providers we have in terraform for provider in $(cd ${terraform}/libexec/terraform-providers; echo */*/*/*); do version=''${provider##*/} provider=''${provider%/*} echo "Build $provider@$version" cdktf provider add --force-local $provider@$version | cat done echo "Compile TS → JS" tsc ''; installPhase = '' mv .gen $out ln -nsf ${nodeEnv}/node_modules $out/node_modules ''; };
Enfin, nous définissons l’environnement de développement :
devShells.default = pkgs.mkShell { name = "cdktf-take1"; buildInputs = [ pkgs.nodejs pkgs.yarn terraform ]; shellHook = '' # No telemetry export CHECKPOINT_DISABLE=1 # No autoinstall of plugins export CDKTF_DISABLE_PLUGIN_CACHE_ENV=1 # Do not check version export DISABLE_VERSION_CHECK=1 # Access to node modules export PATH=$PWD/node_modules/.bin:$PATH ln -nsf ${nodeEnv}/node_modules node_modules ln -nsf ${cdktfProviders} .gen # Credentials for p in \ njf.nznmba.pbz/Nqzvavfgengbe \ urgmare.pbz/ivaprag@oreang.pu \ ihyge.pbz/ihyge@ivaprag.oreang.pu; do eval $(pass show $(echo $p | tr 'A-Za-z' 'N-ZA-Mn-za-m') | grep '^export') done eval $(pass show personal/cdktf/secrets | grep '^export') export TF_VAR_hcloudToken="$HCLOUD_TOKEN" export TF_VAR_vultrApiKey="$VULTR_API_KEY" unset VULTR_API_KEY HCLOUD_TOKEN ''; };
Les dérivations listées dans buildInputs
sont disponibles dans le shell
fourni. Le contenu de shellHook
est exécuté lors du démarrage du shell. Il
établit des liens symboliques pour rendre disponible l’environnement JavaScript
construit à une étape précédente, ainsi que les fournisseurs CDKTF générés. Il
exporte également toutes les informations d’identification10.
J’utilise également direnv avec un fichier .envrc
pour passer automatiquement dans l’environnement de développement. Cela permet
également à ce dernier d’être disponible depuis Emacs, notamment lors de
l’utilisation de lsp-mode pour obtenir les complétions. nix develop .
permet aussi d’activer manuellement l’environnement.
J’utilise les commandes suivantes pour déployer11 :
$ cdktf synth $ cd cdktf.out/stacks/cdktf-take1 $ terraform plan --out plan $ terraform apply plan $ terraform output -json > ~-automation/nixops-take1/cdktf.json
La dernière commande produit un fichier JSON contenant les données nécessaires pour finir le déploiement avec NixOps.
NixOps#
Le fichier JSON exporté par Terraform contient la liste des serveurs avec quelques attributs :
{ "hardware": "hetzner", "ipv4Address": "5.161.44.145", "ipv6Address": "2a01:4ff:f0:b91::1", "name": "web05.luffy.cx", "tags": [ "web", "continent:NA", "continent:SA" ] }
Dans le fichier network.nix
, cette liste est
importée et transformée en un ensemble d’attributs décrivant les serveurs. Une
version simplifiée ressemble à cela :
let lib = inputs.nixpkgs.lib; shortName = name: builtins.elemAt (lib.splitString "." name) 0; domainName = name: lib.concatStringsSep "." (builtins.tail (lib.splitString "." name)); server = hardware: name: imports: { networking = { hostName = shortName name; domain = domainName name; }; deployment.targetHost = name; imports = [ (./hardware/. + "/${hardware}.nix") ] ++ imports; }; cdktf-servers-json = (lib.importJSON ./cdktf.json).servers.value; cdktf-servers = map (s: let tags-maybe-import = map (t: ./. + "/${t}.nix") s.tags; tags-import = builtins.filter (t: builtins.pathExists t) tags-maybe-import; in { name = shortName s.name; value = server s.hardware s.name tags-import; }) cdktf-servers-json; in { // […] } // builtins.listToAttrs cdktf-servers
Pour web05
, on obtient ceci :
web05 = { networking = { hostName = "web05"; domainName = "luffy.cx"; }; deployment.targetHost = "web05.luffy.cx"; imports = [ ./hardware/hetzner.nix ./web.nix ]; };
Comme pour CDKTF, à la racine du dépôt que j’utilise pour
NixOps, il y a un fichier flake.nix
pour fournir un shell avec NixOps configuré. Comme NixOps ne supporte pas
les déploiements progessifs, j’utilise généralement ces commandes pour déployer
sur un unique serveur12 :
$ nix flake update $ nixops deploy --include=web04 $ ./tests web04.luffy.cx
Si les tests se déroulent sans soucis, je déploie les autres nœuds un par un avec la commande suivante :
$ (set -e; for h in web{03..06}; do nixops deploy --include=$h; done)
La commande nixops deploy
déploie tous les serveurs en parallèle et peut donc
provoquer une panne si tous les serveurs Nginx sont indisponibles au même
moment.
Cet article est en chantier depuis trois ans. Le contenu a été mis à jour et affiné au fur et à mesure de mes expérimentations. Il y a encore beaucoup à explorer13, mais j’estime que le contenu est désormais suffisant pour être publié ! 🎄
-
C’était un AMD Athlon 64 X2 5600+ avec 2 Go de RAM et 2 disques de 400 Go en RAID logiciel. Je payais quelque chose autour de 59 € par mois pour cela. Si c’était une bonne affaire en 2008, en 2018, ce n’était plus rentable. Il fonctionnait sous Debian Wheezy avec Linux-VServer pour l’isolation, tous deux dépassés en 2018. ↩︎
-
Je n’ai pas non plus utilisé Python car le support de Poetry dans Nix était cassé quand j’ai commencé à essayer d’utiliser CDKTF. ↩︎
-
Pulumi peut appliquer des fonctions arbitraires à l’aide de
apply()
. Cela permet de transformer les données qui ne sont pas connues lors de l’étape de planification. Terraform a des fonctions pour un usage similaire mais elles sont plus limitées. ↩︎ -
Les deux modifications mentionnées ne sont pas encore fusionnées. La seconde est remplacée par la PR #61, soumise deux mois plus tard, qui impose l’utilisation de
/bin/bash
. J’ai également soumis la PR #56, qui a été fusionnée 4 mois plus tard et rapidement annulée sans explication. ↩︎ -
Grosso modo, une dérivation est un synonyme pour paquet dans l’écosystème Nix. ↩︎
-
OpenSSL 3 a de nombreux problèmes de performance. ↩︎
-
NixOS peut être un peu lent à intégrer les correctifs car il est nécessaire de reconstruire une partie du cache binaire. Dans ce cas précis, cela a été rapide : la vulnérabilité et les correctifs ont été publiés le 13 août 2019 et disponibles dans NixOS le 15 août. À titre de comparaison, Debian n’a publié la version corrigée que le 22 août, ce qui est inhabituellement tardif. ↩︎
-
Comme les flakes sont expérimentaux, de nombreuses documentations ne les utilisent pas et c’est un aspect supplémentaire à apprendre. ↩︎
-
Comme dans les autres exemples, il est possible de remplacer
.
pargithub:vincentbernat/snimpy
. Cependant, obtenir les dépendances de Snimpy sans son code source a peu d’intérêt. ↩︎ -
J’utilise le gestionnaire de mots de passe pass. Le nom des mots de passe sont brouillés uniquement pour éviter les courriers indésirables. ↩︎
-
La commande
cdktf
sait appeler les commandesterraform
, mais je préfère les utiliser directement car elles sont plus flexibles. ↩︎ -
Si le changement est risqué, je désactive le serveur avec CDKTF. Cela le retire des enregistrements DNS. ↩︎
-
Je voudrais remplacer NixOps avec une alternative gérant les déploiements progressifs et les tests. Je voudrais aussi passer à Nomad ou Kubernetes pour déployer les applications. ↩︎