Deploy a Web Server to Google Compute Engine
15 minIn this tutorial, you'll use JavaScript to deploy a simple web server virtual machine instance to Google Compute Engine. You'll define a network, a firewall, and an instance, deploy them with pulumi up, and then update the program to expose port 80 and serve a page from a startup script. Along the way you'll see how Pulumi decides which changes it can make in place and which require replacing a resource.
- How to define a GCE network, firewall, and instance in a Pulumi program
- How to preview and deploy your infrastructure with pulumi up
- How to export and read stack outputs like an instance's IP address
- How Pulumi computes in-place updates versus resource replacements
- The Pulumi CLI
- A Pulumi Cloud account and an access token
- A Google Cloud account with a project you can create virtual machines in
- The Google Cloud CLI (
gcloud), configured with credentials - Node.js
In this tutorial, we’ll use JavaScript to deploy a simple web server virtual machine instance to Google Compute Engine. The code for this tutorial is available on GitHub.
Create a virtual machine with SSH access#
In a new folder
webserver, create an empty project withpulumi new. Choose a Google Cloudprojectyou have access to create virtual machines within.Terminal window mkdir webserver && cd webserverpulumi new gcp-javascript...gcp:project: The Google Cloud project to deploy into: <your project>Open
index.jsand replace the contents with the following:const gcp = require("@pulumi/gcp");// Create a networkconst network = new gcp.compute.Network("network");const computeFirewall = new gcp.compute.Firewall("firewall", {network: network.id,allows: [{protocol: "tcp",ports: [ "22" ],}],});// Create a Virtual Machine Instanceconst computeInstance = new gcp.compute.Instance("instance", {machineType: "f1-micro",zone: "us-central1-a",bootDisk: { initializeParams: { image: "debian-cloud/debian-9" } },networkInterfaces: [{network: network.id,// accessConfigs must include a single empty config to request an ephemeral IPaccessConfigs: [{}],}],});// Export the name and IP address of the Instanceexports.instanceName = computeInstance.name;exports.instanceIP = computeInstance.networkInterfaces.apply(ni => ni[0].accessConfigs[0].natIp);This example uses the @pulumi/gcp package to create and manage three Google Cloud resources: a gcp.compute.Network in which the virtual machine will run, a gcp.compute.Firewall which allows access for incoming SSH access, and a gcp.compute.Instance which is created inside the network from the Debian 9 base image.
To preview and deploy changes, run
pulumi up. The command shows a preview of the resources that will be created and prompts on whether to proceed with the deployment. Note that the stack itself is counted as a resource, though it does not correspond to a physical cloud resource.$ pulumi up Previewing update (webservergcp-dev):
Type Name Plan pulumi:pulumi:Stack webservergcp-webservergcp-dev + ├─ gcp:compute:Network network create + ├─ gcp:compute:Firewall firewall create + └─ gcp:compute:Instance instance create
Resources: + 3 to create 1 unchanged
Now, proceed with the deployment, which will take around a minute to complete.
Do you want to perform this update? yes Updating (webservergcp-dev):
Type Name Status pulumi:pulumi:Stack webservergcp-webservergcp-dev + ├─ gcp:compute:Network network created + ├─ gcp:compute:Instance instance created + └─ gcp:compute:Firewall firewall created
Outputs: + instanceIP : “173.255.117.131” + instanceName: “instance-bf0ab1f”
Resources: + 3 created 1 unchanged
Duration: 1m20s
Permalink: https://app.pulumi.com/lukehoban/webservergcp-dev/updates/1
To see the full details of the deployment and the resources that are now part of the stack, open the update permalink in a browser.
To view the provisioned resources on the command line, run
pulumi stack. You’ll also see two stack outputs corresponding to the IP and full-qualified host name of the virtual machine instance we’ve created.$ pulumi stack...Current stack resources (5):TYPE NAMEpulumi:pulumi:Stack webservergcp-webservergcp-devpulumi:providers:gcp defaultgcp:compute/network:Network networkgcp:compute/firewall:Firewall firewallgcp:compute/instance:Instance instanceCurrent stack outputs (2):OUTPUT VALUEinstanceIP 173.255.117.131instanceName instance-bf0ab1f
Updating the Pulumi program#
Now that we have an instance of our Pulumi program deployed, we may want to make changes. We do this by updating our Pulumi program to define the new state we want our infrastructure to be in, then and running pulumi up to commit the changes.
Replace the creation of the two firewall and instance with the following. This exposes an additional port and adds a startup script to run a simple HTTP server at startup.
...const computeFirewall = new gcp.compute.Firewall("firewall", {network: computeNetwork.id,allows: [{protocol: "tcp",ports: [ "22", "80" ], // <-- ADD "80" HERE}],});// v-- ADD THIS DEFINITIONconst startupScript = `#!/bin/bashecho "Hello, World!" > index.htmlnohup python -m SimpleHTTPServer 80 &`;const computeInstance = new gcp.compute.Instance("instance", {machineType: "f1-micro",zone: "us-central1-a",metadataStartupScript: startupScript, // <-- ADD THIS LINEbootDisk: { initializeParams: { image: "debian-cloud/debian-9" } },networkInterfaces: [{network: network.id,// accessConfigus must include a single empty config to request an ephemeral IPaccessConfigs: [{}],}],});...Run
pulumi upto preview and deploy the changes. You’ll see two changes: theallowsproperty of theFirewallwill be updated in-place. Second, theInstancewill be replaced with a new virtual machine Instance which will run the new script on startup. Pulumi understands which changes to a given cloud resource can be made in-place, and which require replacement, and computes the minimally disruptive change to achieve the desired state.Terminal window pulumi upPreviewing update (webservergcp-dev):...Updating (webservergcp-dev):Type Name Status Infopulumi:pulumi:Stack webservergcp-webservergcp-dev~ ├─ gcp:compute:Firewall firewall updated [diff: ~allows]+- └─ gcp:compute:Instance instance replaced [diff: +metadataStartupScript~name]Outputs:~ instanceIP : "173.255.117.131" => "35.202.30.129"~ instanceName: "instance-bf0ab1f" => "instance-31bbd12"Resources:~ 1 updated+-1 replaced2 changes. 2 unchangedDuration: 2m41sPermalink: https://app.pulumi.com/lukehoban/webservergcp-dev/updates/2We can use
pulumi stack outputto get the value of stack outputs from the CLI. So we cancurlthe virtual machine instance to see the HTTP server running there. Stack outputs can also be viewed on the Pulumi Cloud.Terminal window curl $(pulumi stack output instanceIP)Hello, World!
Clean up#
Before moving on, let’s tear down the resources that are part of our stack.
Run
pulumi destroyto tear down all resources. You’ll be prompted to make sure you really want to delete these resources. This takes about 60 seconds; Pulumi waits for the virtual machine instance to shutdown and for the compute network to be removed before it considers the destroy operation to be complete.To delete the stack itself, run
pulumi stack rm. Note that this command deletes all deployment history from the Pulumi Cloud.
Summary#
In this tutorial, we saw how to use Pulumi programs to create and manage cloud resources in Google Cloud, using regular JavaScript and NPM packages. To preview and update infrastructure, use pulumi up. To clean up resources, run pulumi destroy.
For a similar example in other languages and clouds, see the Web Server examples collection.