Comment gérer les infrastructures DigitalOcean et Kubernetes avec Pulumi

L'auteur a sélectionné lesDiversity in Tech Fund pour recevoir un don dans le cadre du programmeWrite for DOnations.

introduction

Pulumi est un outil pour créer, déployer et gérer une infrastructure à l'aide de code écrit dans des langages de programmation à usage général. Il prend en charge l’automatisation de tous les services gérés de DigitalOcean, tels que les droplets, les bases de données gérées, les enregistrements DNS et les clusters Kubernetes, en plus de la configuration des applications. Les déploiements sont effectués à partir d'une interface de ligne de commande conviviale, qui s'intègre également à une grande variété de systèmes CI / CD populaires.

Pulumi prend en charge plusieurs langues, mais dans ce didacticiel, vous utiliserezTypeScript, une version à typage statique deJavaScript qui utilise le runtime deNode.js. Cela signifie que vous bénéficierez d'un support IDE et d'une vérification lors de la compilation qui vous aideront à vous assurer que vous avez configuré les bonnes ressources, utilisé les bons slugs, etc., tout en étant toujours en mesure d'accéder aux modulesNPM pour les tâches utilitaires.

Dans ce didacticiel, vous allez provisionner un cluster DigitalOceanKubernetes, une application Kubernetes à charge équilibrée et un domaine DNS DigitalOcean qui rend votre application disponible sous un nom de domaine stable de votre choix. Tout cela peut être provisionné en 60 lignes d'infrastructure en tant que code et en un seul geste de ligne de commandepulumi up. Après ce tutoriel, vous serez prêt à construire de manière productive de puissantes architectures cloud en utilisant l’infrastructure Pulumi en tant que code qui exploite toute la surface de DigitalOcean et de Kubernetes.

Conditions préalables

Pour suivre ce tutoriel, vous aurez besoin de:

  • Un compte DigitalOcean sur lequel déployer des ressources. Si vous n'en avez pas déjà un,register here.

  • Un jeton d'API DigitalOcean pour effectuer des déploiements automatisés. Generate a personal access token here et gardez-le à portée de main car vous l'utiliserez à l'étape 2.

  • Étant donné que vous allez créer et utiliser un cluster Kubernetes, vous devrezinstall kubectl. Ne vous inquiétez pas de le configurer davantage - vous le ferez plus tard.

  • Vous écrirez votre code d'infrastructure sous TypeScript, vous aurez donc besoin de Node.js 8 ou version ultérieure. Download it here ou installez-leusing your system’s package manager.

  • Vous utiliserez Pulumi pour déployer l'infrastructure, vous aurez donc besoin deinstall the open source Pulumi SDK.

  • Pour exécuter l'étape 5 facultative, vous aurez besoin d'un nom de domaine configuré pour utiliser les serveurs de noms DigitalOcean. This guide explique comment procéder pour le bureau d'enregistrement de votre choix.

[[step-1 -—- scaffolding-a-new-project]] == Étape 1 - Scaffolding d'un nouveau projet

La première étape consiste à créer un répertoire qui stockera votre projet Pulumi. Ce répertoire contiendra le code source de vos définitions d'infrastructure, en plus des fichiers de métadonnées décrivant le projet et ses dépendances NPM.

Tout d'abord, créez le répertoire:

mkdir do-k8s

Ensuite, déplacez-vous dans le répertoire nouvellement créé:

cd do-k8s

À partir de maintenant, exécutez les commandes à partir de votre répertoiredo-k8s nouvellement créé.

Ensuite, créez un nouveau projet Pulumi. Il existe différentes manières d'accomplir cela, mais la manière la plus simple est d'utiliser la commandepulumi new avec le modèle de projettypescript. Cette commande vous demandera d’abord de vous connecter à Pulumi pour que votre projet et l’état de déploiement soient enregistrés, puis créera un projet TypeScript simple dans le répertoire en cours:

pulumi new typescript -y

Ici, vous avez passé l'option-y à la commandenew qui lui dit d'accepter les options de projet par défaut. Par exemple, le nom du projet est tiré du nom du répertoire actuel, de même quedo-k8s. Si vous souhaitez utiliser différentes options pour le nom de votre projet, élidez simplement les-y.

Après avoir exécuté la commande, répertoriez le contenu du répertoire avecls:

ls

Les fichiers suivants seront maintenant présents:

OutputPulumi.yaml       index.ts          node_modules
package-lock.json package.json      tsconfig.json

Le fichier principal que vous allez modifier estindex.ts. Bien que ce tutoriel utilise uniquement ce fichier, vous pouvez organiser votre projet comme bon vous semble en utilisant les modules Node.js. Ce tutoriel décrit également une étape à la fois, en exploitant le fait que Pulumi peut détecter et déployer de manière incrémentielle uniquement ce qui a changé. Si vous préférez, vous pouvez simplement remplir le programme entier et tout déployer en une seule fois en utilisantpulumi up.

Maintenant que vous avez échafaudé votre nouveau projet, vous êtes prêt à ajouter les dépendances nécessaires pour suivre le didacticiel.

[[step-2 -—- add-dependencies]] == Étape 2 - Ajout de dépendances

L'étape suivante consiste à installer et à ajouter des dépendances sur les packages DigitalOcean et Kubernetes. Tout d'abord, installez-les à l'aide de NPM:

npm install @pulumi/digitalocean @pulumi/kubernetes

Cela téléchargera les packages NPM, les plugins Pulumi, et les enregistrera en tant que dépendances.

Ensuite, ouvrez le fichierindex.ts avec votre éditeur préféré. Ce tutoriel utilisera nano:

nano index.ts

Remplacez le contenu de vosindex.ts par ce qui suit:

index.ts

import * as digitalocean from "@pulumi/digitalocean";
import * as kubernetes from "@pulumi/kubernetes";

Cela rend le contenu complet de ces packages disponible pour votre programme. Si vous tapez"digitalocean." à l'aide d'un IDE qui comprend TypeScript et Node.js, vous devriez voir une liste des ressources DigitalOcean prises en charge par ce package, par exemple.

Enregistrez et fermez le fichier après avoir ajouté le contenu.

[.note] #Note: Nous utiliserons un sous-ensemble de ce qui est disponible dans ces packages. Pour une documentation complète des ressources, des propriétés et des API associées, veuillez vous référer à la documentation API appropriée pour les packages@pulumi/digitalocean et@pulumi/kubernetes.
#

Vous allez ensuite configurer votre jeton DigitalOcean afin que Pulumi puisse provisionner les ressources de votre compte:

pulumi config set digitalocean:token YOUR_TOKEN_HERE --secret

Notez l'indicateur--secret, qui utilise le service de chiffrement de Pulumi pour chiffrer votre jeton, en vous assurant qu'il est stocké dans cyphertext. Si vous préférez, vous pouvez utiliser la variable d'environnementDIGITALOCEAN_TOKEN à la place, mais vous devrez vous rappeler de la définir chaque fois que vous mettez à jour votre programme, alors que l'utilisation de la configuration la stocke et l'utilise automatiquement pour votre projet.

Dans cette étape, vous avez ajouté les dépendances nécessaires et configuré votre jeton d'API avec Pulumi afin que vous puissiez provisionner votre cluster Kubernetes.

[[step-3 -—- provisioning-a-kubernetes-cluster]] == Étape 3 - Provisionnement d'un cluster Kubernetes

Vous êtes maintenant prêt à créer un cluster DigitalOcean Kubernetes. Commencez par rouvrir le fichierindex.ts:

nano index.ts

Ajoutez ces lignes à la fin de votre fichierindex.ts:

index.ts

...
const cluster = new digitalocean.KubernetesCluster("do-cluster", {
    region: digitalocean.Regions.SFO2,
    version: "latest",
    nodePool: {
        name: "default",
        size: digitalocean.DropletSlugs.DropletS2VPCU2GB,
        nodeCount: 3,
    },
});

export const kubeconfig = cluster.kubeConfigs[0].rawConfig;

Ce nouveau code alloue une instance dedigitalocean.KubernetesCluster et définit un certain nombre de propriétés dessus. Cela inclut l'utilisation dessfo2region slug, la version prise en charge de Kuberneteslatest, less-2vcpu-2gbDroplet size slug et indique le nombre souhaité de trois instances de Droplet. N'hésitez pas à en changer, mais sachez que DigitalOcean Kubernetes n'est disponible que dans certaines régions au moment de la rédaction de cet article. Vous pouvez vous référer auxproduct documentation pour des informations mises à jour sur la disponibilité de la région.

Pour une liste complète des propriétés que vous pouvez configurer sur votre cluster, reportez-vous auxKubernetesCluster API documentation.

La dernière ligne de cet extrait de code exporte leskubeconfig file du cluster Kubernetes résultant afin qu'il soit facile à utiliser. Les variables exportées sont imprimées sur la console et accessibles aux outils. Vous l'utiliserez momentanément pour accéder à notre cluster à partir d'outils standards commekubectl.

Vous êtes maintenant prêt à déployer votre cluster. Pour ce faire, exécutezpulumi up:

pulumi up

Cette commande prend le programme, génère un plan pour créer l'infrastructure décrite et effectue une série d'étapes pour déployer ces modifications. Cela fonctionne pour la création initiale d'infrastructure en plus de pouvoir différer et mettre à jour votre infrastructure lorsque des mises à jour ultérieures sont effectuées. Dans ce cas, la sortie ressemblera à ceci:

OutputPreviewing update (dev):

     Type                                     Name        Plan
 +   pulumi:pulumi:Stack                      do-k8s-dev  create
 +   └─ digitalocean:index:KubernetesCluster  do-cluster  create

Resources:
    + 2 to create

Do you want to perform this update?
  yes
> no
  details

Cela indique que procéder à la mise à jour créera un seul cluster Kubernetes nommédo-cluster. L'inviteyes/no/details nous permet de confirmer qu'il s'agit du résultat souhaité avant que des modifications ne soient réellement apportées. Si vous sélectionnezdetails, une liste complète des ressources et de leurs propriétés s'affiche. Choisissezyes pour commencer le déploiement:

OutputUpdating (dev):

     Type                                     Name        Status
 +   pulumi:pulumi:Stack                      do-k8s-dev  created
 +   └─ digitalocean:index:KubernetesCluster  do-cluster  created

Outputs:
    kubeconfig: "..."

Resources:
    + 2 created

Duration: 6m5s

Permalink: https://app.pulumi.com/.../do-k8s/dev/updates/1

La création du cluster prend quelques minutes, mais il sera alors opérationnel et leskubeconfig complets seront imprimés sur la console. Enregistrez leskubeconfig dans un fichier:

pulumi stack output kubeconfig > kubeconfig.yml

Et puis utilisez-le aveckubectl pour exécuter n'importe quelle commande Kubernetes:

KUBECONFIG=./kubeconfig.yml kubectl get nodes

Vous recevrez une sortie similaire à celle-ci:

OutputNAME           STATUS    ROLES     AGE       VERSION
default-o4sj   Ready         4m5s      v1.14.2
default-o4so   Ready         4m3s      v1.14.2
default-o4sx   Ready         3m37s     v1.14.2

À ce stade, vous avez configuré l’infrastructure sous forme de code et disposez d’une méthode reproductible pour créer et configurer de nouveaux clusters DigitalOcean Kubernetes. Dans la prochaine étape, vous construirez par dessus pour définir l'infrastructure de Kubernetes dans le code et apprendre à les déployer et à les gérer de la même manière.

[[step-4 -—- deploying-an-application-to-your-cluster]] == Étape 4 - Déploiement d'une application sur votre cluster

Ensuite, vous allez décrire la configuration d’une application Kubernetes en utilisant l’infrastructure en tant que code. Ce sera composé de trois parties:

  1. Un objetProvider, qui indique à Pulumi de déployer les ressources Kubernetes sur le cluster DigitalOcean, plutôt que la valeur par défaut de toutkubectl configuré pour l'utiliser.

  2. AKubernetes Deployment, qui est la manière standard de Kubernetes de déployer une image de conteneur Docker qui est répliquée sur n'importe quel nombre de pods.

  3. AKubernetes Service, qui est le moyen standard de dire à Kubernetes d'équilibrer la charge de l'accès sur un ensemble cible de pods (dans ce cas, le déploiement ci-dessus).

C'est unreference architectureassez standard pour être opérationnel avec un service à charge équilibrée dans Kubernetes.

Pour déployer ces trois éléments, ouvrez à nouveau votre fichierindex.ts:

nano index.ts

Une fois le fichier ouvert, ajoutez ce code à la fin du fichier:

index.ts

...
const provider = new kubernetes.Provider("do-k8s", { kubeconfig })

const appLabels = { "app": "app-nginx" };
const app = new kubernetes.apps.v1.Deployment("do-app-dep", {
    spec: {
        selector: { matchLabels: appLabels },
        replicas: 5,
        template: {
            metadata: { labels: appLabels },
            spec: {
                containers: [{
                    name: "nginx",
                    image: "nginx",
                }],
            },
        },
    },
}, { provider });
const appService = new kubernetes.core.v1.Service("do-app-svc", {
    spec: {
        type: "LoadBalancer",
        selector: app.spec.template.metadata.labels,
        ports: [{ port: 80 }],
    },
}, { provider });

export const ingressIp = appService.status.loadBalancer.ingress[0].ip;

Ce code est similaire à la configuration standard de Kubernetes et le comportement des objets et de leurs propriétés est équivalent, à la différence qu’il est écrit en TypeScript avec vos autres déclarations d’infrastructure.

Enregistrez et fermez le fichier après avoir apporté les modifications.

Tout comme avant, exécutezpulumi up pour prévisualiser puis déployer les modifications:

pulumi up

Après avoir sélectionnéyes pour continuer, la CLI imprimera des mises à jour détaillées de l'état, y compris des diagnostics concernant la disponibilité des pods, l'allocation d'adresses IP, etc. Cela vous aidera à comprendre pourquoi votre déploiement prend du temps à se terminer ou reste bloqué.

La sortie complète ressemblera à ceci:

OutputUpdating (dev):

     Type                            Name        Status
     pulumi:pulumi:Stack             do-k8s-dev
 +   ├─ pulumi:providers:kubernetes  do-k8s      created
 +   ├─ kubernetes:apps:Deployment   do-app-dep  created
 +   └─ kubernetes:core:Service      do-app-svc  created

Outputs:
  + ingressIp : "157.230.199.202"

Resources:
    + 3 created
    2 unchanged

Duration: 2m52s

Permalink: https://app.pulumi.com/.../do-k8s/dev/updates/2

Une fois cette opération terminée, notez que le nombre souhaité de pods est en cours d'exécution:

KUBECONFIG=./kubeconfig.yml kubectl get pods
OutputNAME                                   READY     STATUS    RESTARTS   AGE
do-app-dep-vyf8k78z-758486ff68-5z8hk   1/1       Running   0          1m
do-app-dep-vyf8k78z-758486ff68-8982s   1/1       Running   0          1m
do-app-dep-vyf8k78z-758486ff68-94k7b   1/1       Running   0          1m
do-app-dep-vyf8k78z-758486ff68-cqm4c   1/1       Running   0          1m
do-app-dep-vyf8k78z-758486ff68-lx2d7   1/1       Running   0          1m

De la même manière que le programme exporte le fichierkubeconfig du cluster, ce programme exporte également l'adresse IP de l'équilibreur de charge résultant du service Kubernetes. Utilisez ceci pourcurl le point de terminaison et voir qu'il est opérationnel:

curl $(pulumi stack output ingressIp)
Output


Welcome to nginx!



Welcome to nginx!

If you see this page, the nginx web server is successfully installed and working. Further configuration is required.

For online documentation and support please refer to nginx.org.
Commercial support is available at nginx.com.

Thank you for using nginx.

À partir de là, vous pouvez facilement modifier et redéployer votre infrastructure d’applications. Par exemple, essayez de changer la lignereplicas: 5 pour direreplicas: 7, puis réexécutezpulumi up:

pulumi up

Notez que cela montre simplement ce qui a changé et que la sélection des détails affiche le diff précis:

OutputPreviewing update (dev):

     Type                           Name        Plan       Info
     pulumi:pulumi:Stack            do-k8s-dev
 ~   └─ kubernetes:apps:Deployment  do-app-dep  update     [diff: ~spec]

Resources:
    ~ 1 to update
    4 unchanged

Do you want to perform this update? details
  pulumi:pulumi:Stack: (same)
    [urn=urn:pulumi:dev::do-k8s::pulumi:pulumi:Stack::do-k8s-dev]
    ~ kubernetes:apps/v1:Deployment: (update)
        [id=default/do-app-dep-vyf8k78z]
        [urn=urn:pulumi:dev::do-k8s::kubernetes:apps/v1:Deployment::do-app-dep]
        [provider=urn:pulumi:dev::do-k8s::pulumi:providers:kubernetes::do-k8s::80f36105-337f-451f-a191-5835823df9be]
      ~ spec: {
          ~ replicas: 5 => 7
        }

Vous avez maintenant à la fois un cluster Kubernetes entièrement fonctionnel et une application opérationnelle. Lorsque votre application est opérationnelle, vous pouvez configurer un domaine personnalisé à utiliser avec votre application. La prochaine étape vous guidera dans la configuration du DNS avec Pulumi.

[[step-5 -—- creating-a-dns-domain-optional]] == Étape 5 - Création d'un domaine DNS (facultatif)

Bien que le cluster et l’application Kubernetes soient opérationnels, son adresse dépend des caprices de l’attribution automatique d’adresses IP par votre cluster. Lorsque vous ajustez et redéployez des éléments, cette adresse peut changer. Au cours de cette étape, vous verrez comment attribuer un nom DNS personnalisé à l’adresse IP de l’équilibreur de charge afin qu’elle soit stable, même si vous modifiez ultérieurement votre infrastructure.

[.note] #Note: Pour terminer cette étape, assurez-vous que vous disposez d'un domaine utilisant les serveurs de noms DNS de DigitalOcean,ns1.digitalocean.com,ns2.digitalocean.com etns3.digitalocean.com. Les instructions pour configurer ceci sont disponibles dans la section Prérequis.
#

Pour configurer DNS, ouvrez le fichierindex.ts et ajoutez le code suivant à la fin du fichier:

index.ts

...
const domain = new digitalocean.Domain("do-domain", {
    name: "your_domain",
    ipAddress: ingressIp,
});

Ce code crée une nouvelle entrée DNS avec un enregistrement A qui fait référence à l’adresse IP de votre service Kubernetes. Remplacezyour_domain dans cet extrait de code par le nom de domaine choisi.

Il est courant de vouloir que des sous-domaines supplémentaires, tels quewww, pointent vers l'application Web. Ceci est facile à réaliser en utilisant un enregistrement DNS DigitalOcean. Pour rendre cet exemple plus intéressant, ajoutez également un enregistrementCNAME qui pointewww.your_domain.com versyour_domain.com:

index.ts

...
const cnameRecord = new digitalocean.DnsRecord("do-domain-cname", {
    domain: domain.name,
    type: "CNAME",
    name: "www",
    value: "@",
});

Enregistrez et fermez le fichier après avoir apporté ces modifications.

Enfin, exécutezpulumi up pour déployer les modifications DNS afin qu'elles pointent vers votre application et votre cluster existants:

OutputUpdating (dev):

     Type                             Name             Status
     pulumi:pulumi:Stack              do-k8s-dev
 +   ├─ digitalocean:index:Domain     do-domain        created
 +   └─ digitalocean:index:DnsRecord  do-domain-cname  created

Resources:
    + 2 created
    5 unchanged

Duration: 6s

Permalink: https://app.pulumi.com/.../do-k8s/dev/updates/3

Une fois les modifications DNS propagées, vous pourrez accéder à votre contenu sur votre domaine personnalisé:

curl www.your_domain.com

Vous recevrez une sortie similaire à celle-ci:

Output


Welcome to nginx!



Welcome to nginx!

If you see this page, the nginx web server is successfully installed and working. Further configuration is required.

For online documentation and support please refer to nginx.org.
Commercial support is available at nginx.com.

Thank you for using nginx.

Avec cela, vous avez réussi à configurer un nouveau cluster DigitalOcean Kubernetes, à y déployer une application Kubernetes à charge équilibrée et à donner à l'équilibreur de charge de cette application un nom de domaine stable utilisant DigitalOcean DNS, le tout dans 60 lignes de code et unpulumi up commander.

La prochaine étape vous guidera tout au long de la suppression des ressources si vous n'en avez plus besoin.

[[step-6 -—- remove-the-resources-optional]] == Étape 6 - Suppression des ressources (facultatif)

Avant de terminer le didacticiel, vous souhaiterez peut-être détruire toutes les ressources créées ci-dessus. Cela garantira que vous ne serez pas facturé pour des ressources qui ne sont pas utilisées. Si vous préférez que votre application reste opérationnelle, n'hésitez pas à ignorer cette étape.

Exécutez la commande suivante pour détruire les ressources. Soyez prudent en utilisant ceci, car cela ne peut pas être annulé!

pulumi destroy

Tout comme avec la commandeup,destroy affiche un aperçu et une invite avant d'agir:

OutputPreviewing destroy (dev):

     Type                                     Name             Plan
 -   pulumi:pulumi:Stack                      do-k8s-dev       delete
 -   ├─ digitalocean:index:DnsRecord          do-domain-cname  delete
 -   ├─ digitalocean:index:Domain             do-domain        delete
 -   ├─ kubernetes:core:Service               do-app-svc       delete
 -   ├─ kubernetes:apps:Deployment            do-app-dep       delete
 -   ├─ pulumi:providers:kubernetes           do-k8s           delete
 -   └─ digitalocean:index:KubernetesCluster  do-cluster       delete

Resources:
    - 7 to delete

Do you want to perform this destroy?
  yes
> no
  details

En supposant que c'est ce que vous voulez, sélectionnezyes et regardez les suppressions se produire:

OutputDestroying (dev):

     Type                                     Name             Status
 -   pulumi:pulumi:Stack                      do-k8s-dev       deleted
 -   ├─ digitalocean:index:DnsRecord          do-domain-cname  deleted
 -   ├─ digitalocean:index:Domain             do-domain        deleted
 -   ├─ kubernetes:core:Service               do-app-svc       deleted
 -   ├─ kubernetes:apps:Deployment            do-app-dep       deleted
 -   ├─ pulumi:providers:kubernetes           do-k8s           deleted
 -   └─ digitalocean:index:KubernetesCluster  do-cluster       deleted

Resources:
    - 7 deleted

Duration: 7s

Permalink: https://app.pulumi.com/.../do-k8s/dev/updates/4

À ce stade, rien ne reste: les entrées DNS ont disparu et le cluster Kubernetes, ainsi que l'application qui s'exécute à l'intérieur, ont disparu. Le lien permanent est toujours disponible, vous pouvez donc toujours revenir en arrière et voir l'historique complet des mises à jour pour cette pile. Cela pourrait vous aider à récupérer si la destruction était une erreur, car le service conserve l'historique complet de l'état de toutes les ressources.

Si vous souhaitez détruire votre projet dans son intégralité, supprimez la pile:

pulumi stack rm

Vous recevrez une sortie vous demandant de confirmer la suppression en tapant le nom de la pile:

OutputThis will permanently remove the 'dev' stack!
Please confirm that this is what you'd like to do by typing ("dev"):

Contrairement à la commande destroy, qui supprime les ressources d’infrastructure cloud, la suppression d’une pile efface complètement l’historique complet de votre pile de la compétence de Pulumi.

Conclusion

Dans ce didacticiel, vous avez déployé des ressources d'infrastructure DigitalOcean - un cluster Kubernetes et un domaine DNS avec des enregistrements A et CNAME - en plus de la configuration d'application Kubernetes qui utilise ce cluster. Vous avez utilisé l'infrastructure-as-code écrite dans un langage de programmation familier, TypeScript, qui fonctionne avec les éditeurs, les outils et les bibliothèques existants et exploite les communautés et les packages existants. Vous avez tout fait en utilisant un seul flux de travail en ligne de commande pour effectuer des déploiements couvrant votre application et votre infrastructure.

A partir de là, vous pouvez prendre un certain nombre de prochaines étapes:

L'échantillon complet de ce didacticiel estavailable on GitHub. Pour plus de détails sur l'utilisation de l'infrastructure en tant que code Pulumi dans vos propres projets aujourd'hui, consultez les guidesPulumi Documentation,Tutorials ouGetting Started. Pulumi est open source et libre d'utilisation.

Related