cover image
Web

Using Ansible to Install Node.js

In the first part of the DevOps series, I showed how to automatically create an AWS EC2 Spot instance with Terraform. You can read it again here: https://www.tderflinger.com/en/ec2-spot-with-terraform

DevOps skills are becoming more important for Node.js developers. With Ansible, you can configure your cloud servers automatically. Now, I explain to you how to automatically configure the created CentOS server with Node.js and Nginx. For that I use the open source tool Ansible. Ansible is a software configuration and deployment tool. It has been originally written by Michael DeHaan in 2012. The company behind Ansible was acquired in 2015 by Red Hat, now an IBM division.

Ansible allows you to automate the installation of software. Thus, it is an important piece in the Infrastructure as Code (IaC). With Ansible, you can ensure that the right packages are installed, the right configuration files in the correct place and the right services are running. You can also deploy your written software to the server with Ansible. Furthermore, Ansible allows for provisioning a new server, but I used Terraform for this task, as you can read in the previous article.

Ansible is a good tool for IaaS (Infrastructure as a Service) type of cloud computing environments. This includes AWS EC2 servers or Digital Ocean droplets. On this type of compute servers you need to configure the needed software yourself. This is in contrast to PaaS offerings, where everything is managed, you only upload the source code of your application. Heroku is an example of a PaaS offering.

When using Ansible, you will do most of your work in configuration files called playbooks. These are yml files that are DSL (Domain Specific Languages) and describe the functions to configure.

The great thing about Ansible is that you do not need to install anything on the target server. Everything works using port 22, via SSH. Only Python 2 or 3 needs to be available on the target machine. This distinguishes Ansible from tools like Chef or Puppet, where you need to install an agent.

Mock Server

For this example, I want to automatically install a Node.js mock server for testing purposes. For that, I use the NPM module json-server and add a bit of mockup code. Basically, the mock server is a REST server with mock data.

json-server-http.js

var fs = require('fs'),
  jsonServer = require('json-server'),
  server = jsonServer.create(),
  router = jsonServer.router('db.json'),
  middlewares = jsonServer.defaults()
const cors = require('cors')
const bodyParser = require('body-parser')

server.use(cors())
server.use(bodyParser.json())

server.use(middlewares)
server.use(router)

server.listen(3002, function() {
  console.log('json-server started on port ' + 3002)
})

It loads some data from a JSON file and returns the data via the REST endpoint.

One requirement for this mock server is that it is available via HTTPS. Thus, I use the Nginx server as a proxy with a TLS connection. The necessary certificate and key files can be generated offline with this Ansible Playbook.

Generate TLS Certificate

First, you need to install Ansible on your computer.

The following Ansible playbook generates on your local computer the self-signed certificate and private key files. These files are later uploaded to the Nginx directories.

generate_certificate.yml

--
- name: Generate Nginx TLS certificates and keys
  hosts: localhost

  tasks:
    - name: Generate OpenSSL private key
      openssl_privatekey: path=files/nginx.key

    - name: Generate OpenSSL Certificate Signing Request
      openssl_csr: path=files/csr.csr  privatekey_path=files/nginx.key common_name=www.someorg.org

    - name: Generate Self-signed certificate
      openssl_certificate: path=files/nginx.crt privatekey_path=files/nginx.key csr_path=files/csr.csr provider=selfsigned

You can run this playbook with the following command:

ansible-playbook generate_certificate.yml

Install Node.js and Nginx

The actual work of configuration happens with the installation of the required software. First, Node.js is needed in version 10.16. A good way to install Node.js on a Linux server is using the Node Version Manager (NVM). On GitHub, there is a ready-made Ansible playbook for installing NVM. We leverage that here and just invoke it in our script.

Then Nginx needs to be installed. The playbook looks something like this:

install_nodenginx.yml

---
- name: Install curl and wget
  gather_facts: 'no'
  hosts: mock
  become: yes
  remote_user: root

  tasks:
    - name: Install curl
      yum:
        name: curl
        state: present

    - name: Install wget
      yum:
        name: wget
        state: present

- name: Create nvm
  gather_facts: 'no'
  hosts: mock
  become: yes
  remote_user: root

  roles:
    - role: ansible-role-nvm
      nodejs_version: '10.16.0'

## Credits to John Lieske - https://www.ansible.com/blog/getting-started-writing-your-first-playbook
- name: Install Nginx
  hosts: mock
  become: yes
  remote_user: root

  tasks:
    - name: Add epel-release repo
      yum:
        name: epel-release
        state: present

    - name: Install Nginx
      yum:
        name: nginx
        state: present

    - name: Start NGiNX
      service:
        name: nginx
        state: started

Before running this playbook, you also need to specify your hosts file.

It can look like this, but you need to update the IP address with the server IP on which you like to install.

hosts

[default]
mock ansible_host=100.24.146.164

Then run the playbook script with:

ansible-playbook install_node_nginx.yml -i hosts

Ansible playbooks are idempotent. That means no matter how often you run, the result is always the same. You specify the outcome, like a file should be absent. Ansible then figures out how to achieve that.

Install the Mock Application

The next steps are copying the mock JavaScript files and the Nginx certification files over. For running the NodeJS mock application, we use the process manager PM2.

run_mock.yml

---
- name: Start mock and some other stuff
  hosts: mock
  become: true
  remote_user: root

  vars:
    key_file: /etc/nginx/ssl/nginx.key
    cert_file: /etc/nginx/ssl/nginx.crt
    conf_file: /etc/nginx/nginx.conf

  tasks:
    - name: Copy files
      copy: src=../../db.json dest=/home/centos

    - name: Copy package.json
      copy: src=../../package.json dest=/home/centos

    - name: Copy json-server-https
      copy: src=../../json-server-http.js dest=/home/centos

    - name: NPM installation
      shell: npm i
      args:
        chdir: /home/centos

    - name: Install PM2
      npm:
        name: pm2
        global: yes

    - name: Create directories for certificates
      file: path=/etc/nginx/ssl state=directory

    - name: Copy TLS key
      copy: src=files/nginx.key dest={{ key_file }} owner=root mode=0600

    - name: Copy TLS certificate
      copy: src=files/nginx.crt dest={{ cert_file }} owner=root mode=0600

    - name: Copy Nginx config file
      template: src=templates/nginx.conf.j2 dest={{ conf_file }}

    - name: Remove nginx.default
      file:
        path: /etc/nginx/nginx.conf.default
        state: absent

    - name: Restart Nginx
      service:
        name: nginx
        state: restarted

    - name: Start Mock
      shell: pm2 start json-server-http.js
      args:
        chdir: /home/centos

In this playbook, you can see the definition of variables. They are applied with the interpolation of two curly braces {{ var }}. Also, a template is used for the generation of the Nginx configuration file. Templates in Ansible are Jinja2 compatible, a Python templating engine.

When everything is started, and you can go in your browser to your server IP using https. The browser will display a dangerous looking warning because the certificate is self-signed. You need to accept the certificate, and then you will see the Mock application.

One thing to note is that these Ansible scripts only work on CentOS Linux machines. Ansible does not abstract between Linux distros. Thus, if you would like to configure and deploy on Ubuntu, you would need to adapt the scripts.

Conclusion

If you have some experience with Linux system administration, understanding an Ansible playbook should not be too difficult. With Infrastructure as Code, it has become mandatory to automate these system administration tasks. Also, when you need to configure many servers, tools like Ansible are an obvious necessity. Thus, Ansible skills are valuable for Node.js developers in the context of DevOps.

Sources and Further Reading

  • Infrastructure as Code: Managing Servers in the Cloud, 2016, Kief Morris, O'Reilly Media

  • Ansible: Up and Running, 2nd edition, 2017, Rene Moser and Lorin Hochstein, O'Reilly Media

  • Source code of this article: https://github.com/tderflinger/ansible-node-mock
Published 16 Jul 2019
Thomas Derflinger

Written by Thomas Derflinger

I am a visionary entrepreneur and software developer. In this blog I mainly write about web programming and related topics like IoT.