Overview

Skill Level: Any Skill Level

Familiarity with Bluemix and OpenStack

Ansible is a simple, powerful, agentless technology for automating application deployment and configuration management. Learn how to use Ansible from your Bluemix DevOps pipelines to deploy a Node.JS server to Bluemix Private Cloud (OpenStack).

Ingredients

This is the third recipe in a series on using Ansible from Bluemix Delivery Pipelines. For the first segment, check out how to Run Ansible from your IBM Bluemix DevOps Pipelines. For the second segment, see Deploying to AWS from Bluemix?!

To learn how to deploy to to the Bluemix Private Cloud from Travis CI, check out the recipe: Deploying to Bluemix Private Cloud (OpenStack) from Travis CI.

In this tutorial we will use an Ansible Playbook executed from the Bluemix Continuous Delivery service to create an Ubuntu compute instance in OpenStack. This process will automatically create a Delivery Pipeline from the Toolchain. Then we will use Ansible to create this instance in Bluemix Private Cloud (formerly Blue Box). Ansible will then install Node.JS and run an application server on this compute instance. Ansible Playbooks can be used to deliver simple applications, as well as complex application infrastructure.

 

Step-by-step

  1. Explore the sample application

     Review the sample source repository. These are the key elements of the repository:

    • setup_tools/install_dependencies.sh – Installs the needed packages in the pipeline stage.
    • setup_tools/install_python.sh – Installs Python 2.7.9 for the pipeline user.
    • setup_tools/make_venv.sh –¬†Creates a Python virtual-environment that uses the pipeline user’s version of Python and installs¬†Ansible and¬†shade through pip.
    • vars/main.yml – This YAML defines variables that we will use in our playbook
      ---
      os_auth_url: "{{ lookup('env', 'OS_AUTH_URL') }}"
      os_username: "{{ lookup('env', 'OS_USERNAME') }}"
      os_password: "{{ lookup('env', 'OS_PASSWORD') }}"
      os_proj_name: "{{ lookup('env', 'OS_PROJ_NAME') }}"
      id_rsa_base64: "{{ lookup('env', 'ID_RSA_BASE64') }}"
      os_vm_name: "{{ lookup('env', 'OS_VM_NAME') | default('openstack-ansible-test', true) }}"
      os_vm_ram: "{{ lookup('env', 'OS_VM_RAM') | default(1024, true) }}"
      os_vm_image: "{{ lookup('env', 'OS_VM_IMAGE') | default('Ubuntu 14.04', true) }}"
      user_home: "{{ lookup('env', 'PWD') }}"
      rsa_filename: "ansible_rsa"
      os_key_name: "ansible-key"
      ssh_port: "22"
      host_group: "new_hosts"
    • .playbook.yml ‚Äď This playbook defines 2 plays, one to¬†a compute instance on Bluemix Private Cloud and another to install and¬†run a Node.JS server¬†on the compute instance. Note: The last debug task prints the host’s IP address. If you do not wish to display this information, remove the Print app URL task from the playbook.
      # Create a compute instance on OpenStack using Ansible
      ---
      - name: Launch an OpenStack compute instance
      hosts: localhost
      vars_files: - vars/main.yml
      tasks:
      # Create key file with 0600 permissions
      # http://docs.ansible.com/ansible/copy_module.html
      - name: Add private key
      copy:
      content: "{{ id_rsa_base64| b64decode }}"
      dest: "{{ user_home }}/{{ rsa_filename }}"
      mode: 0600
      # Generate a public key from the private key
      # http://docs.ansible.com/ansible/shell_module.html
      - name: Generate public key
      shell: "ssh-keygen -P '' -y -f {{ user_home }}/{{ rsa_filename }} > {{ user_home }}/{{ rsa_filename }}.pub"

      # Add the public key to OpenStack
      # http://docs.ansible.com/ansible/os_keypair_module.html
      - name: Add keypair
      os_keypair:
      state: present
      name: "{{ os_key_name }}"
      public_key_file: "{{ user_home }}/{{ rsa_filename }}.pub"
      auth:
      auth_url: "{{ os_auth_url }}"
      username: "{{ os_username }}"
      password: "{{ os_password }}"
      project_name: "{{ os_proj_name }}"
      register: "create_key"

      # Launch an OpenStack compute instance
      # http://docs.ansible.com/ansible/os_server_module.html
      - name: Launch the compute instance
      os_server:
      state: present
      auth:
      auth_url: "{{ os_auth_url }}"
      username: "{{ os_username }}"
      password: "{{ os_password }}"
      project_name: "{{ os_proj_name }}"
      name: "{{ os_vm_name }}"
      flavor_ram: "{{ os_vm_ram }}"
      image: "{{ os_vm_image }}"
      key_name: "{{ os_key_name }}"
      register: "create_server"

      # Add system to host_group
      # http://docs.ansible.com/ansible/add_host_module.html
      - name: Add system as host
      add_host:
      name: "{{ create_server.openstack.public_v4 }}:{{ ssh_port }}"
      groups: "{{ host_group }}"
      ansible_ssh_private_key_file: "{{ user_home }}/{{ rsa_filename }}"

      # Wait for port to be opened
      # http://docs.ansible.com/ansible/wait_for_module.html
      - name: Wait for SSH to be active
      local_action: wait_for port="{{ ssh_port }}" host="{{ create_server.openstack.public_v4 }}" search_regex=OpenSSH

      # --------------------------------------------------------------------------------
      # Install Node.JS on the created instance
      - name: Configure instance to run a Node.JS app
      hosts: new_hosts #must match groupname in "add_host" above
      user: ubuntu
      vars:
      app_folder: "bluemix-node-sample"
      tasks:
      # Update apt list
      # http://docs.ansible.com/ansible/apt_module.html
      - name: Update apt list
      apt:
      update_cache: yes
      become: true

      # Install APT packages
      - name: Install packages
      apt:
      name: "{{ item }}"
      become: true
      with_items:
      - git
      - build-essential
      - python-dev
      - python-pip

      # Install PIP packages
      # http://docs.ansible.com/ansible/pip_module.html
      - name: pip install
      pip:
      name: "{{ item }}"
      become: true
      with_items:
      - urllib3
      - pyopenssl
      - ndg-httpsclient
      - pyasn1

      # Add key for Node.JS
      # http://docs.ansible.com/ansible/apt_key_module.html
      - name: Add apt key for nodesource
      apt_key: url=https://deb.nodesource.com/gpgkey/nodesource.gpg.key
      become: true

      # Add the Node.JS repo
      # http://docs.ansible.com/ansible/apt_repository_module.html
      - name: add repo for nodesource
      apt_repository:
      repo: 'deb https://deb.nodesource.com/node_6.x {{ ansible_distribution_release }} main'
      update_cache: true
      become: true

      # Instal Node.JS
      - name: Install packages
      apt:
      name: nodejs
      become: true

      # Clone the Node.JS project
      # http://docs.ansible.com/ansible/git_module.html
      - name: clone repo
      git:
      repo: https://github.com/IBMCloudDevOps/bluemix-node-sample
      accept_hostkey: yes
      force: yes
      dest: "{{ ansible_env.HOME }}/{{ app_folder }}"

      # Install npm packages
      # http://docs.ansible.com/ansible/command_module.html
      - name: npm install
      command: "npm install"
      args:
      chdir: "{{ ansible_env.HOME }}/{{ app_folder }}"

      # Run node server as cronjob
      # http://docs.ansible.com/ansible/cron_module.html
      - name: Start server on boot
      cron:
      name: "Run node app"
      special_time: reboot
      job: "/usr/bin/nodejs {{ ansible_env.HOME }}/{{ app_folder }}/app.js"

      # Reboot the server
      - name: Restart server
      command: /sbin/shutdown -r now
      async: 0
      poll: 0
      ignore_errors: true
      become: true

      # Print App URL
      - name: Print app URL
      debug: msg="Node app available at http://{{ inventory_hostname }}:3000"
    • .bluemix/ – the configuration files needed to create the toolchain and pipeline.

     

    The compute instance in Bluemix Private Cloud will look like this:
    The Running Compute Instance in OpenStack
    The running application will look like this:
    The Running Node.JS Application

  2. Set the OpenStack firewall rules

    1. Log in to your instance of Bluemix Private Cloud. The authentication URL will look like: https://<account-subdomain>.openstack.blueboxgrid.com.
      The Bluemix Private Cloud Log In
    2. Navigate to Project > Compute > Access & Security through the menu on the lefthand side.
    3. From the Access & Security page click the Security Groups tab.
    4. Click the Manage Rules button on the default security group.
      Manage rules
    5. Click the Add Rule button
    6. In the popup dialog, keep the defaults and set the Port field to 22. This will add a new Custom TCP Rule,  The direction should be Ingress, and the Port to open is 22 (SSH)
      Add SSH Rule
    7. Repeat the previous step, but this time open up Port 3000. This will allow us to view our web application.
    8. Your default rules like should now look like this:
      Default rules set
  3. Copy the OpenStack identity API endpoint

    1. If needed, log back in to the OpenStack dashboard
    2. Navigate to Project > Compute > Access & Security through the menu on the lefthand side.
    3. From the Access & Security page click the API Access tab.
    4. Copy the URL of the Identity service. This will look like: https://<account-subdomain>.openstack.blueboxgrid.com:5000/v2.0. We will use this as an environment variable in step 7. The V3 Identity service is not supported by the Ansible module.
      The OpenStack Identity URL
  4. View the available OpenStack images

    1. If needed, log back in to the OpenStack dashboard
    2. Navigate to Project > Compute > Images through the menu on the lefthand side.
    3. We are using an image of Ubuntu 14.04. If you do not have this image in OpenStack, you can use the Create Image to add it. If your image is note named Ubuntu 14.04 you will need to set OS_VM_IMAGE environment variable in step 7

    OpenStack Images

  5. Generate a SSH key

    On your local machine, open the terminal and generate an SSH key using the following steps:

    mkdir $HOME/.ssh/ansible
    ssh-keygen -t rsa -N ''  -f $HOME/.ssh/ansible/bluebox_rsa
    cat $HOME/.ssh/ansible/bluebox_rsa | base64

    Copy the output of the last command, we will use this as an environment variable in step 7.

    We are generating this key so we can SSH into our compute instance and install Node.JS. Since the SSH key is baked in to the compute instance on creation, we would be unable to access the instance if we generated a new key on each run. 

  6. Create the toolchain

    1. Authenticate to GitHub and Bluemix.
    2. Click the Create toolchain button in the repository readme to fork the repo into your GitHub account.
      Repo Read Me
    3. Once the repository is forked, you will be taken to the Bluemix Continuous Delivery toolchain setup. This toolchain has been defined by the template in the sample repository.
      Toolchain Template Page
      • If you have not authenticated to GitHub you will see an Authorize button.
        NotAuthorized
      • You can select the Bluemix organization and space by clicking the Delivery Pipelines button. This is useful if you also plan to deploy an CloudFoundry application as part of this deployment.
        Toolchain Pipeline Config
    4. Click the Create button. This will generate a toolchain that looks like the following:
      The Generated Toolchain
  7. Configure the stage properties

    1. Select the Delivery Pipeline tile from the toolchain view to open the pipeline stages view.
      Toolchain Pipeline button
    2. The pipeline executes immediately after being created. The OpenStack Deploy stage will fail on the first run. The stages view will look like this once the build completes.
      Deployment Failed
    3. Click on the gear at the top right corner of the OpenStack Deploy stage to select Configure Stage.
    4. Set the following properties:
      • ID_RSA_BASE64 – The RSA private key that we generated in step 4.
      • OS_AUTH_URL – The auth URL for OpenStack we copied in step 2.
      • OS_USERNAME – The username to authenticate to OpenStack.
      • OS_PASSWORD – The password to authenticate to OpenStack.
      • OS_PROJ_NAME – The OpenStack project where the compute instance is to be created.
      • OS_VM_NAME – (optional) The name of the OpenStack compute instance.
      • OS_VM_RAM – (optional) The amount of RAM to provision the OpenStack compute instance with.
      • OS_VM_IMAGE – (optional) The image in OpenStack to provision the compute instance from.
        Configure Properties
    5. Run the OpenStack Deploy stage using the Run Stage button at the top righthand side of the stage’s card. This time the OpenStack Deploy will succeed.
      Deployment Passed
  8. View the results

    1. Log in to your Bluemix Private Cloud dashboard.
    2. Navigate to Project > Compute > Access & Security. Click on the Key Pairs tab. You should see a new key called ansible-key.
    3. Navigate to Project > Compute > Instances. You should see your running compute instance like below. This screen will contain the machine’s floating IP sddress which we will use in the next step to access the web app.
      The Running Compute Instance
    4. In your browser navigate to: http://<instance_floating_ip>:3000. You should see the following application running:
      The Running Node.JS Application
    5. To SSH into the machine using the keyfile generated in step 5, run the follwoing from the terminal:
      ssh -i ~/.ssh/ansible/bluebox_rsa ubuntu@<instance_floating_ip>
  9. Closing thoughts

    The Bluemix Continuous Delivery service provides a flexible continuous delivery pipeline that can employ a variety of technologies to effect deployments. Ansible provides an open, all purpose deployment and configuration management framework. Together they give you effective continuous delivery using an open platform for deployment. Managing OpenStack compute instances from your pipeline allows deployment of cloud-native apps, and infrastructure they may depend on at the same time.

     

    Authors

    Jesse Antoszyk is a Software Engineer with IBM Cloud.

    Adam King is a Senior Software Engineer with IBM Cloud.

Join The Discussion