Cómo desplegar NGINX en un Droplet de DigitalOcean usando Pulumi

Pulumi es una herramienta para la declaración de infraestructura como código pero con foco en el uso de lenguajes de programación reales. ¿Querés saber cómo funciona? Seguí leyendo...

La semana pasada mostraba cómo armar una ambiente totalmente funcional de Droplets, Configuración DNS, Implementación de Traefik con Docker usando Terraform. Hoy, te voy a mostrar como hacer algo parecido con Pulumi, una herramienta que buscar más o menos lo mismo que la mencionada anteriormente pero que hace foco en el hecho de que tanto desarrolladores como operadores (SysAdmin, DevOps Engineers, etc) puedan usarlo y entenderlo.

Además que tiene unas cosas interesantes como un backend en la nube que nos permite guardar nuestros "secretos" entre otras cosas que con más en detalle veremos en otro post.

Lo que hoy te quiero mostrar es cómo desplegar un Droplet (Maquina virtual) que corra NGINX en DigitalOcean.

Para arrancar, primero tenemos que elegir el lenguaje que vamos a usar para armar nuestros archivos. Pulumi soporta TypeScript, Python, JavaScript y versiones Preview de Go y C#.

Manos a la obra

Si no tenés instalado Pulumi, podés usar Brew para hacerlo, en mi caso, solo basto con ejecutar:

brew install pulumi

Una vez que está instalado, creemos una carpeta y entremos a ella, ahí es donde vamos a trabajar. La carpeta, para este ejemplo, se llamará "do".

mkdir do && cd do

Luego, vamos a iniciar un nuevo "trabajo" y vamos a especificar el lenguaje que vamos a usar. Para mi caso, elegí TypeScript.

pulumi new typescript

Esto va a lanzar una especie de asistente para crear nuestro stack.

This command will walk you through creating a new Pulumi project.

Enter a value or leave blank to accept the (default), and press <ENTER>.
Press ^C at any time to quit.

project name: <nombre-de-proyecto>
project description: (A minimal TypeScript Pulumi program) <este es un proyecto de prueba>
Created project '<nombre-de-proyecto'

Please enter your desired stack name.
To create a stack in an organization, use the format <org-name>/<stack-name> (e.g. `acmecorp/dev`).
stack name: (dev) dev
Created stack 'dev'

Luego de esto, comenzará descargar unos paquetes, se verá algo así:

Installing dependencies...


> deasync@0.1.19 install /Users/nacho/pulumi/do_2/node_modules/deasync
> node ./build.js

`darwin-x64-node-12` exists; testing
Binary is fine; exiting

> grpc@1.24.2 install /Users/nacho/pulumi/do_2/node_modules/grpc
> node-pre-gyp install --fallback-to-build --library=static_library

[grpc] Success: "/Users/nacho/pulumi/do_2/node_modules/grpc/src/node/extension_binary/node-v72-darwin-x64-unknown/grpc_node.node" is installed via remote

> protobufjs@6.8.8 postinstall /Users/nacho/pulumi/do_2/node_modules/protobufjs
> node scripts/postinstall

added 178 packages from 208 contributors and audited 282 packages in 13.459s
found 0 vulnerabilities

Finished installing dependencies

Your new project is ready to go! ✨

To perform an initial deployment, run 'pulumi up'

Luego, descargaremos el provider de DigitalOcean para Pulumi.

npm install @pulumi/digitalocean

Esto, nos devolverá algo como esto:

> @pulumi/digitalocean@1.4.0 install /Users/nacho/pulumi/do_2/node_modules/@pulumi/digitalocean
> node scripts/install-pulumi-plugin.js resource digitalocean v1.4.0

[resource plugin digitalocean-1.4.0] installing
npm WARN typescript@ No description
npm WARN typescript@ No repository field.
npm WARN typescript@ No license field.

+ @pulumi/digitalocean@1.4.0
added 2 packages from 1 contributor and audited 642 packages in 4.076s
found 0 vulnerabilities

   ╭────────────────────────────────────────────────────────────────╮
   │                                                                │
   │       New minor version of npm available! 6.9.0 → 6.13.6       │
   │   Changelog: https://github.com/npm/cli/releases/tag/v6.13.6   │
   │               Run npm install -g npm to update!                │
   │                                                                │
   ╰────────────────────────────────────────────────────────────────╯

Terminado esto, vamos a configurar nuestro token, para eso, si aún no tenés tu token de acceso a DigitalOcean, lo podés generar desde este apartado de tu panel de control. Una vez que lo tengamos, ejecutamos lo siguiente:

pulumi config set digitalocean:token <tu-token> --secret

Esto lo que va a hacer es guardar este "secreto" en el backend de Pulumi que está en la web y que te podés registrar de manera gratuita. (En otro artículo hablaré de profundidad de este backend)

Ya con esto configurado, vamos a ver que en la carpeta "do" tenemos varios archivos, el que nos interesa a nosotros para este tutorial es es: index.ts, que lo vamos a modificar para que quede de esta manera:

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

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

const dropletCount = 1;
const region = digitalocean.Regions.NYC3;

const dropletTypeTag = new digitalocean.Tag("mi-app");
const userData =
  `#!/bin/bash
  sudo apt-get update
  sudo apt-get install -y nginx`;

const droplets = [];
for (let i = 0; i < dropletCount; i++) {
    let nameTag = new digitalocean.Tag(`web-${i}`);

    droplets.push(new digitalocean.Droplet(`web-${i}`, {
        image: "ubuntu-18-04-x64",
        region: region,
        privateNetworking: true,
        size: digitalocean.DropletSlugs.Droplet512mb,
        tags: [nameTag.id, dropletTypeTag.id],
        userData: userData,
    }));
}

Vamos a desmenuzar un poco esto:

Lo primero que tenemos es la especificación del proveedor que usaremos y luego, la cantidad de Droplets y la región donde estará alojado. En este caso es 1 y NYC 3.

const dropletCount = 1;
const region = digitalocean.Regions.NYC3;

Lo siguiente que haremos es darle una etiqueta y ya pasarle comandos para cuando el Droplet esté listo. Para este caso se llamará "mi-app" y los comandos son la actualización de los repositorios y la instalación de NGINX.

const dropletTypeTag = new digitalocean.Tag("mi-app");
const userData =
  `#!/bin/bash
  sudo apt-get update
  sudo apt-get install -y nginx`;

Por último vemos configuración referida al nombre y lo más importante que es el tamaño del Droplet y qué imagen usará:

const droplets = [];
for (let i = 0; i < dropletCount; i++) {
    let nameTag = new digitalocean.Tag(`web-${i}`);

    droplets.push(new digitalocean.Droplet(`web-${i}`, {
        image: "ubuntu-18-04-x64",
        region: region,
        privateNetworking: true,
        size: digitalocean.DropletSlugs.Droplet512mb,
        tags: [nameTag.id, dropletTypeTag.id],
        userData: userData,
    }));
}

Si todo está listo y configuramos el index.ts como queremos. Guardamos, salimos y ejecutamos:

pulumi up

Nos devolverá algo como esto, que es básicamente el resumen de lo que va a hacer, los recursos que va a crear:

Previewing update (dev):

     Type                           Name      Plan
 +   pulumi:pulumi:Stack            do-dev    create
 +   ├─ digitalocean:index:Tag      mi-app    create
 +   ├─ digitalocean:index:Tag      web-0     create
 +   └─ digitalocean:index:Droplet  web-0     create

Resources:
    + 4 to create

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

Sí estamos de acuerdo, seleccionamos "yes" y lo dejamos que haga su magia.

Al finalizar, veremos algo como esto, en donde nos indica qué recursos se crearon, cuánto tiempo tomó y el enlace al backend donde Pulumi guardó todo.

Updating (dev):

     Type                           Name      Status
 +   pulumi:pulumi:Stack            do-dev    created
 +   ├─ digitalocean:index:Tag      web-0     created
 +   ├─ digitalocean:index:Tag      mi-app    created
 +   └─ digitalocean:index:Droplet  web-0     created

Resources:
    + 4 created

Duration: 38s

Permalink: https://app.pulumi.com/<tuusuario>/do/dev/updates/3

Para este escenario, no sucede como en Terraform que nos guarda un tfstate, dónde nos devuelve todo lo que hizo con el detalle del direccionamiento IP y demás, así que si o si debemos ir al backend de Pulumi y sacar los datos de ahí. En otro momento, les mostraré como hacerlo todo de manera local.

Por último, revisamos que ese NGINX este corriendo, así que basta con que vayamos a la IP que nos dió para este Droplet DigitalOcean para que nos aparezca la página de bienvenida del WebServer.

Así que listo, de esta manera, ejecutamos nuestra primera carga de trabajo usando Pulumi en DigitalOcean. Para destruirlo en el caso de que querramos, como también es mi caso, basta con ejecutar lo siguiente:

pulumi destroy

Nos devolverá algo como esto

Previewing destroy (dev):

     Type                           Name      Plan
 -   pulumi:pulumi:Stack            do-dev    delete
 -   ├─ digitalocean:index:Droplet  web-0     delete
 -   ├─ digitalocean:index:Tag      web-0     delete
 -   └─ digitalocean:index:Tag      mi-app    delete

Resources:
    - 4 to delete

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

Seleccionamos que "yes" y lo siguiente que veremos es esto:

Destroying (dev):

     Type                           Name      Status
 -   pulumi:pulumi:Stack            do-dev    deleted
 -   ├─ digitalocean:index:Droplet  mi-app    deleted
 -   ├─ digitalocean:index:Tag      demo-app  deleted
 -   └─ digitalocean:index:Tag      web-0     deleted

Resources:
    - 4 deleted

Duration: 29s

Permalink: https://app.pulumi.com/<tuusuario>/do/dev/updates/4
The resources in the stack have been deleted, but the history and configuration associated with the stack are still maintained.
If you want to remove the stack completely, run 'pulumi stack rm dev'.

Para cerrar

Para ir cerrando, me gustaría animarte a que lo pruebes. Pero no sin antes leer más sobre Pulumi desde esta dirección:

Pulumi - Modern Infrastructure as Code
Pulumi’s open source infrastructure as code SDK enables you to create, deploy, and manage infrastructure on any cloud, using your favorite languages.

También invitarte a que te registres en DigitalOcean, de esta manera, cuando uses el crédito de regalo, me dará crédito a mi que me viene bien para poder seguir demostrando cosas usando DO.

DigitalOcean – The developer cloud
Helping millions of developers easily build, test, manage, and scale applications of any size – faster than ever before.

Por último, te dejo el enlace al proyecto de Gitlab que te permitirá replicar este tutorial:

Ignacio Van Droogenbroeck / NGINX in DigitalOcean with Pulumi
GitLab.com

Si tenés dudas o consultas, no dudes en dejar un comentario acá abajo y lo vamos viendo.

Creditos imágen: Unplash