Kenan
Assistant Engineer
Assistant Engineer
  • UID621
  • Fans1
  • Follows0
  • Posts55
Reads:2605Replies:0

Fast Delivery of Applications under VPC Based on Resource Orchestration and Ansible

Created#
More Posted time:Sep 21, 2016 10:04 AM
Alibaba Cloud Resource Orchestration (ROS) provides a low-cost and standardized scheme for us to set up and integrate the cloud computing resource environment quickly. Based on the capabilities provided by ROS, all we need to do is to define the required resources in resource template form, and further implement fast production of the cloud resources defined. In addition, ROS also simplifies the application delivery and resource release processes.
The user has two ways to deliver the application based on ROS: one is to set up the resource environment, and then log in to the cloud host ECS for manual implementation of application deployment and maintenance; the other is to implement one-click delivery of the application through Cloud-Init. However, both means will encounter the same problem: the application deployment and maintenance costs will increase correspondingly as the number and complexity of ECS and application deployments increases. So how to implement fast delivery of the application while reducing the operation costs to set up the application? This requires automatic operation & maintenance tool Ansible.
So to speak, ROS helps us to set up the resource environment quickly, and Ansible helps us to implement fast delivery of the application in the ready resource environment.
This article introduces how to implement fast delivery of applications using ROS and Ansible. To better understand the whole implementation process of the application, this article first simply introduces its running mechanism, then addresses the process of fast delivery of the application based on ROS and Ansible, and finally presents the whole application delivery process using setting up a Redis cluster as an example. The general flow of this article is as follows:
• Ansible and its running mechanism
• Fast delivery of applications based on ROS and Ansible
• Fast setting up of Redis cluster based on ROS and Ansible
Ansible and its running mechanism
Ansible is a lightweight distributed automatic operation & management tool that is developed based on Python paramiko and to be installed with a client. It can implement such work as automatic application deployment, configuration, task execution and so on. In terms of operation & maintenance of multiple servers, it greatly simplifies the workload of operation & maintenance staff, and reduces deployment and delivery costs of the application.
Ansible connects the working host via SSH or ZeroMQ using Paramiko protocol library in YMAL syntax and Jinja2 template language, and further implements control and maintenance of the remote host. Generally one release version of Ansible will be issued every two months.
Ansible's running mechanism: Master management node pushes Ansible module to the managed node using the Inventory file that stores the node information via SSH protocol (or Kerberos and LDAP), and then executes Playbooks, which will be deleted automatically after execution, as shown in the figure below:


From the above figure, we can see that Ansible mainly consists of five components:
• Master: control node core of Ansible
• Inventory: to define list of Ansible management host
• Playbooks: to define Ansible task execution files and its configuration files
• Modules: including build-in core modules and custom modules of Ansible
• Plugins: to complete supplement of the module functions
Fast delivery of applications based on ROS and Ansible
After the running mechanism of Ansible, we will introduce how to create the application running environment quickly using ROS, and implement fast delivery of applications combining with Ansible, of which the general process is as follows:


• Install ROS SDK and Ansible
• Create resource stack, and set up the resource environment based on ROS
• Get the host information, create the Inventory file, and get Playbook
• Execute Playbook, complete fast delivery of the application, and deploy the application based on Ansible quickly
It is important to note that the fast delivery of applications mentioned in this article is implemented using python under VPC environment; therefore, before you start, you should have an ECS that can get access to the public network as the Master to implement all python procedures. Next we will introduce in detail the process of fast delivery of applications based on ROS and Ansible.
Install ROS SDK and Ansible
The creation of the resource stack mentioned in this article is implemented by invoking open API of ROS via python, so we should first install ROS SDK on Master.
The deployment of applications is completed via Ansible, so we should install Ansible on Master. To avoid unnecessary troubles, it is recommended to install the latest version of Ansible.
Create resource stack, and set up the resource environment based on ROS
After installation of the development environment, we will start to set up the resource environment based on ROS. First define the template of the resource to be created as needed, add the template to the python file, and then invoke ROS API with the template as the parameter to implement creation of the resource.
Create Inventory and get Playbook
According to the running mechanism of Ansible, after setting up the resource environment, we should first invoke ROS API again to obtain login information of the cloud host resource, such as host IP address and host login password, according to creation result of the resource stack. Then generate the Inventory file according to the host information as the list of Ansible management hosts. In the Inventory file, you can define the mode to log in to the cloud host. Below is a sample Inventory file:
[webservers]
192.168.xx.xx ansible_ssh_port=22 ansible_ssh_user=root ansible_ssh_pass=abc123
192.168.xx.xx ansible_ssh_port=22 ansible_ssh_user=root ansible_ssh_pass=abc123


In the file, it defines webservers of the remote host, and specifies the login address, login protocol (SSH by default), login port, and login user name and password of the two cloud hosts under the host group.
In the end, according to the applications to be deployed, we can query and retrieve the relevant playbook of the application in Ansible forums. Of course, in addition to this method, you can also write your own playbook in YMAL syntax and Jinja2 template language according to deployment of the application.
Execute Playbook, and deploy the application quickly
It is required to specify the execution file when running Playbook in Ansible. The execution file can be compiled according to the Inventory file and Playbook. In the execution file, it is required to specify the name of the Playbook to be run and the name of the remote host or a remote host group. Below is an execution file example.yml, which shows the execution of a Playbook named example for the remote group webservers:
---
- hosts: webservers
  roles:
    - Example


After the execution file is compiled successfully, we can run the following command to complete execution of Playbook.
ansible-playbook example.yml

Fast setting up of Redis cluster based on ROS and Ansible
To better explain fast delivery of applications based on ROS and Ansible, in this article, the setting up of Redis cluster will be taken as the demonstration example.
Now I have set up my VPC environment, and installed ROS SDK and Ansible on the master cluster. Next I will show the detailed process to set up one master and two slave Redis clusters based on ROS SDK and Ansible.
To improve the high availability of Redis clusters, I deployed high availability solution Sentinel when setting up the Redis cluster in order to help the Redis cluster implement automatic master/backup switching.
Set up the resource environment
First define the resource stack template. As I have set up my VPC environment control node Master, I only need to define and set up the three cloud host ECS resources, and correlate it to the existing VPC environment. For convenience, I define ECS resource using the Resources "Type": "ALIYUN::ECS::InstanceGroup". The template to define the resource stack is detailed in python file create_instancegroup_template.py in the annexes.
After the template is defined successfully, edit the python code, invoke ROS API with the template as the parameter to set up the resource environment. Below is the python file that invokes ROS API.
# invoke CreateStackRequest to create InstanceGroup stack

from aliyunsdkcore.client import AcsClient
from aliyunsdkros.request.v20150901 import CreateStacksRequest
import create_instancegroup_template
import json

# define stack creation timeout(minutes)
create_timeout = 60

# define func to create stack
def create_stack(stack_name, ak_id, ak_secret, region_id):
    print('invoke CreateStackRequest to create instances...')
    client = AcsClient(ak_id, ak_secret, region_id)
    req = CreateStacksRequest.CreateStacksRequest()
    req.set_headers({'x-acs-region-id': region_id})

    template = create_instancegroup_template.generate_template(io_optimized='optimized', network_type='vpc', image_id='centos6u5_64_40G_cloudinit_20160427.raw', security_group_id='sg-94qyqvpvj', instance_password='********', instance_min_amount=3, system_disk_category='cloud_ssd', instance_max_amount=3, vswitch_id='vsw-sm9i9vhin', vpc_id='vpc-a6al8acn4', instance_type='ecs.n1.small')

    create_stack_body = '''
    {
        "Name": "%s",
        "TimeoutMins": %d,
        "Template": %s
    }
    ''' % (stack_name, create_timeout, template)
    req.set_content(create_stack_body)
    # get response
    response = client.get_response(req)

    # deal response
    if 201 == response[0]:
        parameters = json.loads(response[-1])
        print('Create stack succeccfully!!!')
        return parameters
    else:
        print('Unexpected errors: status=%d, error=%s' % (response[0], response[-1]))

 After successful invocation, log in to ROS console to view the resource stack being created.


Create Inventory and get Playbook
The next step is to create the Inventory file. To eliminate the troublesome manual compilation of Inventory file, in this article, python programs will be used to generate it automatically. First, after the stack resource is created successfully, invoke ROS API to get login information of the resource ECS. As creation of the resource stack generally takes 2 to 5 minutes (according to the specific situation), waiting time should be set in the program as follows:
# define func to get host ip
def get_host_ips(stack, ak_id, ak_secret, region_id):
    # get host ip
    print('Start to get host ips...')

    if stack is None:
        return None
    req = DescribeStackDetailRequest.DescribeStackDetailRequest()
    req.set_headers({'x-acs-region-id': region_id})
    req.set_StackName(stack['Name'])
    req.set_StackId(stack['Id'])

    client = AcsClient(ak_id, ak_secret, region_id)
    attempt = attempt_times
    wait = wait_time
    while attempt >= 0 and wait >= 0:
        response = client.get_response(req)
        if 200 == response[0]:
            resources = json.loads(response[-1])
            if (resources is None) or (not resources.has_key('Outputs')):
                time.sleep(wait)
                attempt = attempt - 1
                wait = wait - interval
                continue
            private_ips = resources['Outputs'][0]['OutputValue']
            print('Getting host ips finished. ips: ', private_ips)
            return private_ips
        else:
            print('Unexpected errors: status=%d, error=%s' % (response[0], response[-1]))
            return None
    print('Getting host ips timeout.')
    return None


After the cloud host IP address is retrieved, we can begin to generate the Inventory file. As the login password of remote host is specified upon creation of the resource stack, it can be referenced directly here, as shown below:
# define func to create inventory
def edit_hosts(remote_ips, remote_ports, remote_users, remote_pass, is_sentinel):
if remote_ips is None or 0>=len(remote_ips):
    print('Error! Remote ips is empty!')
    return
else:
    if remote_ports is None or len(remote_ips)!=len(remote_ports):
        print('Error! Remote ports is empty!')

    if remote_users is None or len(remote_ips)!=len(remote_users):
        print('Error! Remote users is empty!')

    if remote_pass is None or len(remote_ips)!=len(remote_pass):
        print('Error! Remote pass is empty!')

    host_str = ' ansible_ssh_port=%s ansible_ssh_user=%s ansible_ssh_pass=%s\n'
    file = open(hosts_dir + '/' + hosts_file, 'wb')
    file.write( '[%s]\n' % host_master )
    file.write( ('%s'+host_str) % (remote_ips[0], remote_ports[0], remote_users[0], remote_pass[0]) )
    if len(remote_ips)>1:
        file.write( '\n[%s]\n' % host_slave )
        for index in range(1,len(remote_ips)):
            file.write( ('%s'+host_str) % (remote_ips[index], remote_ports[index], remote_users[index], remote_pass[index]) )
    if is_sentinel:
        file.write( '\n[%s]\n' % host_sentinel )
        for index2 in range(0,len(remote_ips)):
            file.write( ('%s redis_sentinel=True'+host_str) % (remote_ips[index2], remote_ports[index2], remote_users[index2], remote_pass[index2]) )
    file.close()
print 'End'


After the file is generated, we can get the Redis cluster playbook. In this article, the playbook compiled by David Wittman: DavidWittman.redis is used. We can invoke the command line tools of ansible ansible-galaxy to download the playbook directly to the Roles directory of Ansible (of course, we can also use other download methods, but it is still required to put it under the Roles directory after downloading). As the command ansible-galaxy applies HTTPS, we should confirm the current host has already got openssl installed before invoking the command, as shown below:
# Install openssl before execute ansible-galaxy
print('Install openssl ...')
subprocess.call('yum install openssl -y', shell=True)

# install playbook
print('Download playbook %s...' % pb_name)
subprocess.call('ansible-galaxy install ' + pb_name + ' -vvv', shell=True)


After the DavidWittman.redis playbook sets up the Redis cluster successfully, as only Sentinel is deployed when DavidWittman.redis is compiled and the master/slave configuration of the Redis cluster is not implemented, we should modify /tasks/main.yml in DavidWittman.redis before running in order to allow it to set up the distributed Redis cluster, as shown below:
# define func to mod playbook
def mod_playbook():
    file = open(ansible_dir + '/roles/' + pb_name + '/tasks/main.yml', 'rb+')
    lines=file.readlines()
    for row in range(0, len(lines)):
        if lines[row].find('when: not redis_sentinel')>=0:
           lines[row] = '#' + lines[row]
           break
    file = open(ansible_dir + '/roles/' + pb_name + '/tasks/main.yml', 'wb+')
    file.writelines(lines)


Execute Playbook, and deploy the application quickly
Before executing playbook, we should compile the execution file of playbook. Like the Inventory file, the execution file can be generated automatically using relevant programs according to the playbook downloaded, as shown below:
# define func to create playbook init config
def create_pb_init(host_ip, has_slaves, is_sentinel):
# config master
file_pb = open(pb_file_dir + '/' + pb_file_name, 'wb')
file_pb.write(redis_playbook_template.create_playbook(template='master', host_master_name=host_master, host_slave_name=host_slave, host_sentinel_name=host_sentinel, playbook_name=pb_name, master_ip=host_ip, master_port='6379', master_name='master01'))
file_pb.close()

# config slaves
if has_slaves:
    file_pb = open(pb_file_dir + '/' + pb_file_name, 'ab')
    file_pb.write(redis_playbook_template.create_playbook(template='slaves', host_master_name=host_master, host_slave_name=host_slave, host_sentinel_name=host_sentinel, playbook_name=pb_name, master_ip=host_ip, master_port='6379', master_name='master01'))
    file_pb.close()

# config sentinel
if is_sentinel:
    file_pb = open(pb_file_dir + '/' + pb_file_name, 'ab')
    file_pb.write(redis_playbook_template.create_playbook(template='sentinel', host_master_name=host_master, host_slave_name=host_slave, host_sentinel_name=host_sentinel, playbook_name=pb_name, master_ip=host_ip, master_port='6379', master_name='master01'))
    file_pb.close()


The template file used in the program to generate the execution file redis_playbook_template.py is detailed in the annexes.
Next we can execute the playbook by running the command ansible-playbook of Ansible, and thus set up the Redis cluster, as shown below:
# execute playbook
subprocess.call('ansible-playbook ' + pb_file_dir + '/' + pb_file_name, shell=True)


The process to create Inventory, get Playbook, generate the execution file and execute the Playbook is detailed in the execute_redis_playbook.py file in the annexes.
The main.py executed by the Python code is as shown below:
import execute_redis_playbook
import time
# invoke ros api params
ak_id=''
ak_secret=''
instance_password=''
region_id='cn-shenzhen'
is_sentinel = True

# define func main
def main():
    start_time = time.time()
    execute_redis_playbook.execute(ak_id, ak_secret, region_id, instance_password, is_sentinel)
    end_time = time.time()
    print end_time-start_time
if __name__=='__main__':
    main()


Before running main.py, we should specify AccessKeys information of the account, cloud host password and zone.
The above is the whole process to set up the Redis cluster. In the example, there are three nodes in the Redis cluster. Of course, we can also set up single-node or multi-node Redis clusters by modifying the number of cloud host ECS. In this example, the resource topology after the Redis cluster is set up successfully is shown in the figure below:
Guest