Getting Started on DigitalOcean with Pulumi

Posted on

Pulumi recently added support for managing DigitalOcean resources. This article will show you how to deploy some load balanced Droplets on DigitalOcean using Pulumi.

To get started, let’s create a new Pulumi program written in TypeScript:

 $ mkdir infra && cd infra
 $ pulumi new typescript

Now we should install the latest Pulumi DigitalOcean provider:

 $ npm install @pulumi/digitalocean

At the top of our application, we can add the resource imports:

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

We can now start to build the infrastructure. To get started, we will declare two constants:

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

Now, let’s create our web servers. Since our Pulumi application is written in TypeScript, we can use a loop, as follows:

const dropletTypeTag = new digitalocean.Tag("demo-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,
    }));
}

Let’s take a closer look at what we’ve programmed. First, we declared a dropletTypeTag; we’ll come back to what this tag signifies later. Next, we have a userData constant that we can pass to all the instances, so that we can see they are running a web server. Then, we are declaring a nameTag constant. This allows us to use the interpolation in the loop to ensure that each of our Droplets is tagged with the appropriate name. We can also see that the region constant is being used in the Droplet declaration and we are adding the meta-information back to the Droplets array for later use.

Our next piece of infrastructure is our load balancer. We are going to create a public load balancer that will accept connections on port 80 and forward those connections to the Droplets that are attached on port 80. The infrastructure block looks like this:

const lb = new digitalocean.LoadBalancer("public", {
    dropletTag: dropletTypeTag.name,
    forwardingRules: [{
        entryPort: 80,
        entryProtocol: digitalocean.Protocols.HTTP,
        targetPort: 80,
        targetProtocol: digitalocean.Protocols.HTTP,
    }],
    healthcheck: {
        port: 80,
        protocol: digitalocean.Protocols.TCP,
    },
    region: region,
});

export const endpoint = lb.ip;

The configuration is made up of an array of forwarding rules. Here, we forward traffic from the load balancer to the Droplets. Then we have a health check that makes sure that port 80 is actually available and we deploy the load balancer into the region that we declared as a constant earlier. The interesting thing about load balancers in DigitalOcean is that you can attach instances by Droplet tag without knowing the Droplet IDs. Here, we configure all Droplets that have the tag demo-app, in this region, to be attached to the load balancer.

Running this gives us:

Updating (dev):

     Type                                Name                     Status
 +   pulumi:pulumi:Stack                 do-ref-architecture-dev  created
 +   ├─ digitalocean:index:Tag           web-0                    created
 +   ├─ digitalocean:index:Tag           web-1                    created
 +   ├─ digitalocean:index:Tag           web-2                    created
 +   ├─ digitalocean:index:Tag           demo-app                 created
 +   ├─ digitalocean:index:Droplet       web-0                    created
 +   ├─ digitalocean:index:Droplet       web-1                    created
 +   ├─ digitalocean:index:LoadBalancer  public                   created
 +   └─ digitalocean:index:Droplet       web-2                    created

Outputs:
    endpoint: "45.55.120.64"

Resources:
    + 9 created

Duration: 3m5s

You should be able to take the endpoint address and put it into the browser or you should be able to curl it as follows:

curl "$(pulumi stack output endpoint)"

We are constantly working on ways to make Pulumi more productive to use with DigitalOcean. Pulumi is free and open source. You can get started with Pulumi today at https://www.pulumi.com.