Overview

Skill Level: Any Skill Level

Familiarity with Travis CI and OpenStack

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

Ingredients

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

In this tutorial we will use an Ansible Playbook executed from the Travis CI to create an Ubuntu compute instance in OpenStack. The playbook will then install Node.JS and run an application server on this compute instance. For this sample, we will create this instance in Bluemix Private Cloud (formerly Blue Box). 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:

    • 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 how to¬†create a compute instance on Bluemix Private Cloud and how to run a Node.JS server on it. 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"

     

    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. Fork the sample repo in GitHub

    1. Fork IBMCloudDevOps/travis-openstack-deploy. 
      Github Fork Repo
  7. Setup Travis CI to build your application repo

    1. Sign into Travis CI with your GitHub ID.
      Travis Sign In
    2. Click the + next to My Repositories to enable Travis CI to build the new repository.
      Travis Add Repo
    3. Flick the repository switch on for your repository. Sync account if the repository does not appear in your list.
    4. Click the gear icon beside your repository to ‚ÄúGo to Travis CI repository settings‚ÄĚ.
      Travis Enable Repo and Settings Cog
    5. Set the following Travis CI environment variables (ensure Display value in build log is off to protect your information):
      • 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.
        Travis Environmental Variables
  8. Run the deployment

    1. Create a new pull request from the branch deploy to the master branch of your base repository. Make sure you are not pulling against the upstream master. GitHub Create Application Pull Request
    2. Wait for the checks to pass and merge the pull request.
    3. Once merged, go to Travis CI and watch the the build for the master branch run. This will cause the OpenStack compute instance to be created if it does not exist.
      Travis Build Success
  9. 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>
  10. Closing thoughts

    Travis CI 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 Travis CI 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