Deploy a Node application using Puppet Bolt

Application deployment is a multi-step process that can even include touching multiple machines or systems. In this blog post we’ll look at how we can use Puppet Bolt to deploy a node application. The application is a simple todo application created by scotch.io that is composed of a node application and a mongodb database. We’ll deploy the application and database on separate servers to show how this can be handled with Puppet Bolt.

We’ll create three (3) Bolt plans that will each be used for a different part of the automation. At a high level we want to install the mongodb database on our database virtual machine and then install our node application on the app virtual machine.

Plan Overview

Forge modules

The deployment plan uses the following Puppet Forge modules to deploy the application stack.

Initialize the Bolt project

Ensure that the latest version of Puppet Bolt is installed before getting started.

Puppet Bolt utilizes Project directories as launching points for running Bolt operations. Create a directory for our Puppet Bolt project name nodeproject.

mkdir nodeproject

Change the working directory to nodeproject directory

cd nodeproject

Now that we have a directory for hosting our Bolt project, we need to initialize the project.

bolt project init --modules puppet-mongodb,puppet-nodejs,camptocamp-systemd,puppetlabs-vcsrepo,puppet-firewalld

The command should generate output similar to that shown below if it ran successfully.

Installing project modules→ Resolving module dependencies, this may take a moment→ Writing Puppetfile at/system/path/nodeproject/Puppetfile→ Syncing modules from/system/path/nodeproject/Puppetfile to/system/path/nodeproject/modules→ Generating type referencesSuccessfully synced modules from /system/path/nodeproject/Puppetfile to /system/path/nodeproject/modulesSuccessfully created Bolt project at /system/path/nodeproject

Create the Bolt YAML plan

In order to utilize plans in Bolt, we need to create a directory named plans.

mkdir plans

Now that we have our plans directory created we’ll plan out what we want to accomplish as part of our plan.

The first thing we’ll do is create the todoapp plan that will call the app and db plans. The name of the sub or nested plans are app and db which are referenced in our plan by the name of the bolt project and the plan (bolt_project::bolt_plan). The plan accepts two parameters (app, db) that specify the IP address or FQDN of the virtual machines used for the application and database.

---
parameters:
app:
type: TargetSpec
db:
type: TargetSpec
steps:
- plan: nodeproject::db
description: "Deploy todo node application mongodb database"
parameters:
db_targets: $db
- plan: nodeproject::app
description: "Deploy todo node application"
parameters:
app_targets: $app
db_targets: $db

Now that we’ve got the todoapp plan in place we need to create the db plan to install the mongodb database. Create another plan in the plans directory named db.yaml.

Steps

---
parameters:
db_targets:
type: TargetSpec
steps:
- name: installmongo
targets: $db_targets
resources:
- class: mongodb::globals
parameters:
version: 4.2.0
manage_package_repo: true
server_package_name: mongodb-org-server
bind_ip: [0.0.0.0]
- class: mongodb::server
parameters:
port: 27017
- name: configurefirewall
targets: $db_targets
resources:
- class: firewalld
- firewalld_zone: 'public'
parameters:
ensure: present
purge_ports: true
- firewalld_port: 'node db'
parameters:
ensure: present
port: 27017
protocol: tcp

Finally, we’re ready to create the app plan to deploy the node application. Deploying the node application requires a number of steps to actually get the application running. The plan accepts the IP address or DNS name of the application virtual machine and the database virtual machine as parameters.

---
parameters:
app_targets:
type: TargetSpec
db_targets:
type: String
steps:
- name: installprerequisites
description: "install app prerequisite software"
targets: $app_targets
resources:
- package: git
parameters:
ensure: present
- name: configurefirewall
targets: $app_targets
resources:
- class: firewalld
- firewalld_zone: 'public'
parameters:
ensure: present
purge_ports: true
- firewalld_port: 'node app'
parameters:
ensure: present
port: 8080
protocol: 'tcp'
- name: installtodoapp
targets: $app_targets
resources:
- vcsrepo: '/opt/node-todo'
parameters:
ensure: present
provider: git
source: 'https://github.com/scotch-io/node-todo.git'
trust_server_cert: true
- class: nodejs
- nodejs::npm: 'app'
parameters:
ensure: present
target: /opt/node-todo
use_package_json: true
- file: '/opt/node-todo/config/database.js'
parameters:
ensure: present
content: >
module.exports = remoteUrl : "mongodb://$db_targets:27017/uwO3mypu",
localUrl: "mongodb://$db_targets:27017/meanstacktutorials"
>;
- file: '/etc/systemd/system/todo-app.service'
parameters:
ensure: present
content: >
[Unit]
Description=Todo node application Documentation=https://github.com/scotch-io/node-todo After=network.target [Service] Type=simple WorkingDirectory=/opt/node-todo ExecStart=/usr/bin/npm start Restart=on-failure [Install] WantedBy=multi-user.target
notify: Class[systemd::systemctl::daemon_reload]
- class: systemd::systemctl::daemon_reload
- service: 'todo-app'
parameters:
ensure: running
subscribe: File[/etc/systemd/system/todo-app.service]

Now that we’ve created our plans we can ensure that it’s recognized by Bolt by running the following command.

bolt plan show

If the plan registers properly the output should include the following entries.

aggregate::count
aggregate::nodes
aggregate::targets
canary
facts
facts::external
facts::info
nodeproject::app
nodeproject::db
nodeproject::todoapp

puppet_agent::run
puppetdb_fact
reboot
secure_env_vars
terraform::apply
terraform::destroy
secure_env_varsterraform::applyterraform::destroy

With the plans registered we are now ready to run the todoapp plan by running the bolt plan run nodeproject::todoapp command. The plan.

bolt plan run nodeproject::todoapp app=10.0.0.41 db=10.0.0.42

If the plan ran successfully it should have generated output similar to that displayed below.

Starting: plan nodeproject::todoapp
Starting: plan nodeproject::db
Starting: install puppet and gather facts on 10.0.0.42
Finished: install puppet and gather facts with 0 failures in 74.59 sec
Starting: apply catalog on 10.0.0.42
Finished: apply catalog with 0 failures in 35.91 sec
Starting: install puppet and gather facts on 10.0.0.42
Finished: install puppet and gather facts with 0 failures in 8.03 sec
Starting: apply catalog on 10.0.0.42
Finished: apply catalog with 0 failures in 19.44 sec
Finished: plan nodeproject::db in 2 min, 18 sec
Starting: plan nodeproject::app
Starting: install puppet and gather facts on 10.0.0.41
Finished: install puppet and gather facts with 0 failures in 74.78 sec
Starting: apply catalog on 10.0.0.41
Finished: apply catalog with 0 failures in 25.49 sec
Starting: install puppet and gather facts on 10.0.0.41
Finished: install puppet and gather facts with 0 failures in 7.59 sec
Starting: apply catalog on 10.0.0.41
Finished: apply catalog with 0 failures in 18.23 sec
Starting: install puppet and gather facts on 10.0.0.41
Finished: install puppet and gather facts with 0 failures in 8.9 sec
Starting: apply catalog on 10.0.0.41
Finished: apply catalog with 0 failures in 65.48 sec
Finished: plan nodeproject::app in 3 min, 21 sec
Finished: plan nodeproject::todoapp in 5 min, 40 sec
Plan completed successfully with no result

Once the command completes successfully we can check that everything worked by opening entering the IP address or FQDN of the Bolt target in a web browser. The site should show the following message.

We have now successfully deployed a node application using Puppet Bolt. The automation can be made more elaborate such as interacting with a load balancer, performing database prep operations or more.