基于资源编排在专有网络环境下快速部署高可用的Dubbox服务(ZooKeeper版)

本文将介绍在专有网络VPC(Virtual Private Cloud)下,基于资源编排服务,快速部署高可用的Dubbox服务的过程。Dubbox服务采用的注册中心是ZooKeeper集群。做这件事情的意义在于:节约部署Dubbox的时间,降低部署Dubbox过程中出错的风险。

ROS
阿里云资源编排(Resource Orchestration)是一种简单易用的云计算资源管理和自动化运维服务。用户通过模板描述多个云计算资源的依赖关系、配置等,并自动完成所有资源的创建和配置,以达到自动化部署、运维等目的。编排模板同时也是一种标准化的资源和应用交付方式,并且可以随时编辑修改,使基础设施即代码(Infrastructure as Code)成为可能。
Ansible
Ansible是一个简单的自动化IT工具。引用Ansible官网的介绍就是:“Deploy apps.Manage systems.Crush complexity.Ansible helps you build a strong foundation for DevOps.”。
更多Ansible的相关知识可参考Ansible中文权威指南
Ansible的工作机制,可参考基于资源编排和 Ansible 在 VPC 下快速交付应用中“Ansible 及其运行机制”章节。
Dubbox
Dubbox在Dubbo服务的基础上添加了一些新功能,如:REST风格的远程调用、支持基于Jackson的JSON序列化、支持基于Kryo和FST的Java高效序列化实现、支持完全基于Java代码的Dubbo配置、升级Spring、升级Zookeeper等。想了解更多关于Dubbox服务内容可参考Dubbox Github
ZooKeeper
Apache ZooKeeper是Apache软件基金会的一个软件项目,他为大型分布式计算提供开源的分布式配置服务、同步服务和命名注册。ZooKeeper的架构通过冗余服务实现高可用性。本文将介绍采用ZooKeeper集群作为Dubbox服务的注册中心,来保证Dubbox服务的高可用性。更多ZooKeeper相关的知识可参考ZooKeeper Wiki

本文将从以下三个方面展开介绍

  • 准备Ansible主机
  • VPC网络环境下快速部署高可用的Dubbox服务
  • 总结

准备Ansible主机

本章将从以下两个方面展开:

  • 安装Ansible
  • 安装ROS SDK

首先需要新建一个VPC,并在这个VPC下创建一个VSwitch,在这个VPC和VSwitch下申请一台ECS实例,并给这台机器绑定公网IP,方便访问外网。本文所采用的Ansible主机(ECS)系统版本信息如下:

CentOS Linux release 7.0.1406 (Core)  

这个版本的ECS已经安装了Python 2.7.5。
说明:专有网络VPC的创建链接。创建好VPC以后直接在该VPC下新建VSwitch即可。新建ECS的链接

创建好Ansible主机以后,我们需要给这台主机安装Ansible和ROS SDK。Ansible用来实现对远程主机的控制,ROS SDK用来实现对资源栈的操作。

安装Ansible

Dubbox服务的快速部署需要借助Ansible来完成,本文采用了yum来安装Ansible:

yum install ansible  

Ansible默认安装目录为/etc/ansible,安装好以后,/etc/ansible/目录结构如下:

[root@iZ94jwkjg0sZ ansible]# ls -l
总用量 24
-rw-r--r-- 1 root root 13819 5月  25 23:49 ansible.cfg
-rw-r--r-- 1 root root   441 8月  17 17:52 hosts
drwxr-xr-x 4 root root  4096 8月  17 17:52 roles

关于Ansible的安装方式可参考Ansible Installation
注意:如果采用的是Ubuntu系统,安装Ansible过程如下:

  apt-get install ansible
  apt-get install sshpass

安装ROS SDK

ROS提供了RESTful API和SDK,本文将介绍用Python的方式来调用ROS API创建资源。
在调用ROS API之前需要安装相关的依赖。

使用pip安装aliyun-python-sdk-core:

pip install aliyun-python-sdk-core

使用pip安装ROS SDK:

pip install aliyun-python-sdk-ros

如果Ansible主机未安装pip,可使用以下命令安装pip:

curl "https://bootstrap.pypa.io/get-pip.py" -o "get-pip.py"
python get-pip.py  

如果不了解pip,可参考*)。
关于ROS SDK详细安装和使用过程可以参考阿里云资源编排服务Python SDK使用入门

VPC网络环境下快速部署高可用的Dubbox服务

本章将详解讲解Dubbox服务的部署过程。您也可以跳过此章节,直接参考如何快速构建高可用Dubbox服务快速部署Dubbox服务。

本章将从以下五个方面展开:

  • 创建资源栈
  • 编辑Inventory文件
  • 构建PlayBook
  • 执行PlayBook
  • 部署Dubbox服务

创建资源栈

创建资源栈的过程主要分为以下三步:

  • 定义ROS资源模板
  • 调用ROS API,创建资源栈
  • 获取资源栈输出信息

定义ROS资源模板

本文通过调用ROS API的方式来创建资源栈。
ROS资源模板包括以下几种资源类型:

在VPC网络环境下,给InstanceGroup指定VPC和VSwtich,并保证和Anisble主机处在同一个VPC和VSwitch下,这样才能保证Ansible主机可通过ECS的私有IP来登录ECS并操控ECS实例。

定义ROS资源模板的Python文件为generate_vpc_ros_template.py,文件内容如下:

from string import Template
# define ros template
create_resources_with_parameters = '''
{
  "ROSTemplateFormatVersion": "2015-09-01",
  "Resources": {
    "InstanceGroupDubboxAdmin": {
      "Type": "ALIYUN::ECS::InstanceGroup",
      "Properties": {
        "ImageId": "centos7u2_64_40G_cloudinit_20160520.raw",
        "Password": "$ecs_password",
        "MinAmount": 2,
        "MaxAmount": 2,
        "InstanceType": "$instance_type",
        "ZoneId": "$zone_id",
        "InternetChargeType": "PayByTraffic",
        "NetworkType": "vpc",
        "InstanceName": "ecs",
        "VpcId": "$vpc_id",
        "VSwitchId": "$vswitch_id"
      }
    },
    "InstanceGroupZK": {
      "Type": "ALIYUN::ECS::InstanceGroup",
      "Properties": {
        "ImageId": "centos7u2_64_40G_cloudinit_20160520.raw",
        "Password": "$ecs_password",
        "MinAmount": 3,
        "MaxAmount": $instance_group_zk_size,
        "InstanceType": "$instance_type",
        "ZoneId": "$zone_id",
        "InternetChargeType": "PayByTraffic",
        "NetworkType": "vpc",
        "InstanceName": "zk",
        "VpcId": "$vpc_id",
        "VSwitchId": "$vswitch_id"
      }
    },
    "LoadBalance": {
      "Properties": {
        "AddressType": "internet",
        "InternetChargeType": "paybytraffic",
        "LoadBalancerName": "balancer"
      },
      "Type": "ALIYUN::SLB::LoadBalancer"
    },
    "Attachment": {
      "Properties": {
        "BackendServers": [
          {
            "ServerId": { "Fn::Select": ["0",{ "Fn::GetAtt": [ "InstanceGroupDubboxAdmin", "InstanceIds" ] }]},
            "Weight": 100
          },
           {
            "ServerId": { "Fn::Select": ["1",{ "Fn::GetAtt": [ "InstanceGroupDubboxAdmin", "InstanceIds" ] }]},
            "Weight": 100
          }
        ],
        "LoadBalancerId": {
          "Ref": "LoadBalance"
        }
      },
      "Type": "ALIYUN::SLB::BackendServerAttachment"
    },
    "Listener": {
      "Type": "ALIYUN::SLB::Listener",
      "Properties": {
          "LoadBalancerId": {
            "Ref": "LoadBalance"
          },
          "ListenerPort": $listen_port,
          "BackendServerPort": $bachend_server_port,
          "Bandwidth": -1,
          "Protocol": "http",
          "HealthCheck": {
              "HealthyThreshold": 3,
              "UnhealthyThreshold": 3,
              "Interval": 2,
              "Timeout": 5,
              "HttpCode": "http_2xx",
              "URI": "$health_check_path"
          },
          "Scheduler": "wrr"
      }
    }
  },
  "Outputs": {
    "EcsPrivateIps": {
      "Value": { "Fn::GetAtt": [ "InstanceGroupDubboxAdmin", "PrivateIps"]}
    },
    "ZKPrivateIps": {
      "Value": { "Fn::GetAtt": [ "InstanceGroupZK", "PrivateIps"]}
    },
    "LoadBalanceIp": {
      "Value": {"Fn::GetAtt": [ "LoadBalance", "IpAddress"]}
    }
   }
}
'''
# define func to generate ros template
def generate_template(**kwargs):
    template = Template(create_resources_with_parameters)
    return template.substitute(kwargs)

调用ROS API,创建资源栈

创建资源栈的Python文件为create_stack.py,文件内容如下:

from aliyunsdkcore.client import AcsClient
from aliyunsdkros.request.v20150901 import CreateStacksRequest
import generate_vpc_ros_template
import json
from config import *
# define func to create stack
def create_stack(stack_name, ak_id, ak_secret, region_id, zk_size):
    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})
    #create vpc network resources
    template = generate_vpc_ros_template.generate_template(vpc_id = vpc_id, ecs_password = ecs_password, 
        instance_type = instance_type, zone_id = zone_id, vswitch_id = vswitch_id, 
        instance_group_zk_size = zk_size, listen_port = 8080, 
        bachend_server_port =8080, health_check_path = '/dubbo-admin/favicon.ico') 
    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]:
        print('Create stack succeccfully!!!')
        return json.loads(response[-1])
    else:
        print('Unexpected errors: status=%d, error=%s' % (response[0], response[-1]))
        return None
if __name__ == '__main__':
    create_stack(stack_name, ak_id, ak_secret, region_id, zk_size)

下面详细解释该Python文件的执行过程:

  1. 初始化SDK客户端对象:

    client = AcsClient(ak_id, ak_secret, region_id)

  2. 初始化创建资源栈的请求:

    req = CreateStacksRequest.CreateStacksRequest()

  3. 指定请求资源Region:

    req.set_headers({'x-acs-region-id': region_id})

  4. 构造请求体,包括:栈名、过期时间戳、ROS资源模板

    create_stack_body = '''

    {
        "Name": "%s",
        "TimeoutMins": %d,
        "Template": %s
    }
    ''' % (stack_name, create_timeout, template)

    req.set_content(create_stack_body)

  5. 发送请求,创建资源栈:

    response = client.get_response(req)

6. 获取资源栈信息:

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

请求成功会返回资源栈的IdName信息,请求发出以后,可到ROS 控制台查看资源栈详情。

获取资源栈输出信息

资源栈创建好以后,我们再次调用ROS API获取资源栈的输出信息。方法如下:

def get_stack_outputs(stack, ak_id, ak_secret, region_id):
    print('Start to get stack output...')
    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
            outputs = resources['Outputs']
            print('Getting stack outputs finished. outputs: ', outputs)
            return outputs
        else:
            print('Unexpected errors: status=%d, error=%s' % (response[0], response[-1]))
            return None
    print('Getting stack outputs timeout.')
    return None

调用时需要传入创建好的资源栈IdName信息。由于创建资源栈需要的时间不确定,所以以上方法定义了超时重试的机制。每次重试以后的等待时间要比上次等待时间少interval秒,在尝试attempt次若仍未获取到资源栈信息视为资源创建失败(一般不会出现这种情况)。

资源栈的输出格式如下:

[{u'OutputKey': u'EcsPrivateIps', u'Description': u'No description given', u'OutputValue': [u'192.168.x.x', u'192.168.x.x']}, 
{u'OutputKey': u'ZKPrivateIps', u'Description': u'No description given', u'OutputValue': [u'192.168.x.x', u'192.168.x.x', u'192.168.x.x']}, 
{u'OutputKey': u'LoadBalanceIp', u'Description': u'No description given', u'OutputValue': u'112.74.x.x'}]

我们将以上输出定义为Outputs

编辑Inventory文件

根据上面获取的资源栈输出信息Outputs,获取Dubbox控制台服务器组的私有IP。方法如下:

def get_ecs_ips(outputs):
    for i in range(len(outputs)):
        if outputs[i]['OutputKey'] == ecs_ip_output_key:
            return outputs[i]['OutputValue']
    return None

根据上面获取的资源栈输出信息Outputs,获取ZooKeeper集群服务器组的私有IP。方法如下:

def get_zk_ips(outputs):
    for i in range(len(outputs)):
        if outputs[i]['OutputKey'] == zk_ip_output_key:
            return outputs[i]['OutputValue']
    return None

因为在创建资源栈的时候,已经在配置文件中配置好了ALIYUN::ECS::InstanceGroup的登录密码,所以可以直接使用配置文件中的密码信息,用户名默认为root。编辑/etc/ansible/hosts文件,即通常我们所说的Ansible Inventory文件。编辑该文件的方法如下:

# define func to create inventory
def edit_hosts(host_parameters, zk_parameters):
    print 'Start edit hosts'
    host_str = ' ansible_ssh_port=%s ansible_ssh_user=%s ansible_ssh_pass=%s\n'
    with open(hosts_file, 'wb') as file:
        file.write( '[%s]\n' % host_dubbo_admin )
        for index in range(len(host_parameters)):
            file.write( ('%s'+host_str) % (host_parameters[index][0], host_parameters[index][1], host_parameters[index][2], host_parameters[index][3]) )
        file.write( '[%s]\n' % host_zookeeper )
        for index in range(len(zk_parameters)):
            file.write( ('%s'+host_str) % (zk_parameters[index][0], zk_parameters[index][1], zk_parameters[index][2], zk_parameters[index][3]) )
    print 'Edit hosts end'

执行该方法以后,生成的Inventory文件,即/etc/Ansible/hosts文件,内容如下:

[dubbo_admin]
10.169.***.*** ansible_ssh_port=22 ansible_ssh_user=root ansible_ssh_pass=***
10.44.***.*** ansible_ssh_port=22 ansible_ssh_user=root ansible_ssh_pass=***
[zookeeper]
10.45.***.*** ansible_ssh_port=22 ansible_ssh_user=root ansible_ssh_pass=***
10.170.***.*** ansible_ssh_port=22 ansible_ssh_user=root ansible_ssh_pass=***
10.170.***.*** ansible_ssh_port=22 ansible_ssh_user=root ansible_ssh_pass=***

Inventory文件中定义了两个远程主机组,dubbo_adminzookeeper,并指定了不同主机的IP、登录协议(默认是 SSH )、登录端口、登录用户名和密码。Ansible在执行PlayBook的时候通过以上信息连接远程主机。

构建PlayBook

本文所描述的高可用Dubbox服务,注册中心采用的是ZooKeeper集群,因此需要构建两个PlayBook。一个用来部署Dubbox控制台集群,一个用来部署ZooKeeper集群。

部署Dubbox控制台集群的PlayBook为vpc_dubbox_admin_playbook,结构如下:

[root@iZ94jwkjg0sZ roles]# ls -l vpc_dubbox_admin_playbook
总用量 12
drwxr-xr-x 2 501 games 4096 8月  17 17:52 files
drwxr-xr-x 2 501 games 4096 8月  17 17:52 tasks
drwxr-xr-x 2 501 games 4096 8月  17 14:20 templates

关于vpc_dubbox_admin_playbook结构的说明如下:

  • files

    • 存放install_Jdk.shinstall_Jetty.shdeploy_Admin.sh三个文件,这三个文件分别用来安装JDK、Jetty以及部署Dubbox控制台服务。
  • tasks

    • 存放要执行的yml文件 install_dubbo_admin.yml,文件内容如下:


      • name: add config
        template:

        dest: /root/config
        src: config.j2
        mode: 0600
        
      • name: run install jdk
        script: install_Jdk.sh
      • name: run install jetty
        script: install_Jetty.sh
      • name: run deploy admin
        script: deploy_Admin.sh
  • templates

    • 存放配置文件模板config.j2,文件内容如下:

            #第一列 变量名 第二列 变量值 第三列 配置项 不同列之间用tab或者空格分开
            #jetty安装路径,当目录已经存在的时候加上f参数会强制覆盖,否则会退出安装
            JETTY_HOME {{jetty_home}} {{enforce}}
            #设置admin控制台root用户的密码
            DUBBO_ADMIN_ROOT_PASSWD {{dubbo_root}}
            #设置admin控制台guest用户的密码
            DUBBO_ADMIN_GUEST_PASSWD {{dubbo_guest}}
            #注册中心连接地址字符串
            REGISTRY_ADDRESS {{registry_address}}
            #ZooKeeper集群的IP地址
            ZK_IPS_STR {{zk_ips_str}}
            
    • 配置文件config.j2中的参数值从vpc_dubbox_zk.yml文件中获取,通过Ansible执行PlayBook的过程中会在每一台远程主机的/root目录下生成config文件,提供给install_Jdk.shinstall_Jetty.shdeploy_Admin.sh这三个脚本使用。关于vpc_dubbox_zk.yml文件,后面会提到。

部署ZooKeeper集群的PlayBook为vpc_dubbox_zookeeper_playbook,结构如下:

[root@iZ94jwkjg0sZ roles]# ls -l vpc_dubbox_admin_playbook
总用量 12
drwxr-xr-x 2 501 games 4096 8月  17 17:52 files
drwxr-xr-x 2 501 games 4096 8月  17 17:52 tasks
drwxr-xr-x 2 501 games 4096 8月  17 14:20 templates

关于vpc_dubbox_zookeeper_playbook结构的说明如下:

  • files

    • 存放install_Jdk.shinstall_ZK.sh两个文件,这两个文件分别用来安装JDK、部署ZooKeeper集群。
  • tasks

    • 存放要执行的yml文件 install_zk.yml,文件内容如下:


      • name: add config
        template:

        dest: /root/config
        src: config.j2
        mode: 0600
        
      • name: run install jdk
        script: install_Jdk.sh
      • name: run install zookeeper
        script: install_ZK.sh

  • templates

    • 存放配置文件模板config.j2,文件内容如下:

            #第一列 变量名 第二列 变量值 第三列 配置项 不同列之间用tab或者空格分开
            #jetty安装路径,当目录已经存在的时候加上f参数会强制覆盖,否则会退出安装
            JETTY_HOME {{jetty_home}} {{enforce}}
            #设置admin控制台root用户的密码
            DUBBO_ADMIN_ROOT_PASSWD {{dubbo_root}}
            #设置admin控制台guest用户的密码
            DUBBO_ADMIN_GUEST_PASSWD {{dubbo_guest}}
            #注册中心连接地址字符串
            REGISTRY_ADDRESS {{registry_address}}
            #ZooKeeper集群的IP地址
            ZK_IPS_STR {{zk_ips_str}}
            
    • 配置文件config.j2中的参数值从vpc_dubbox_zk.yml文件中获取,通过Ansible执行PlayBook的过程中会在每一台远程主机的/root目录下生成config文件,提供给install_Jdk.shinstall_ZK.sh使用。关于vpc_dubbox_zk.yml文件,后面会说明。

这两个PlayBook构建好了以后,可上传到阿里云OSS。在执行脚本的时候需要用到这两个PlayBook,可以通过wget命令从阿里云OSS上下载,然后直接使用。

执行PlayBook

执行PlayBook,需要以下三个步骤:

  • 生成Ansible可执行文件
  • 下载PlayBook
  • 执行PlayBook

生成Ansible可执行文件

我们需要通过Ansible执行vpc_dubbox_zk.yml文件来运行我们构建好的两个PlayBook。
vpc_dubbox_zk.yml文件的生成,需要分两步进行:

  • 定义文件模板
  • 生成文件

定义vpc_dubbox_zk.yml文件模板的Python文件为deploy_vpc_dubbox.py,文件内容如下:

from string import Template
create_hosts_with_parameters = '''
- name: deploy dubbox service
  hosts: $zookeeper
  vars:
    - jetty_home: $jetty_home
    - enforce: $jetty_home_enforce
    - dubbo_root: $dubbo_root_password
    - dubbo_guest: $dubbo_guest_password
    - registry_address: $registry_address 
    - zk_ips_str: $zk_ips_str
  roles:
    - $zookeeper_pb_name
    
- name: deploy dubbox service
  hosts: $dubbo_admin
  vars:
    - jetty_home: $jetty_home
    - enforce: $jetty_home_enforce
    - dubbo_root: $dubbo_root_password
    - dubbo_guest: $dubbo_guest_password
    - registry_address: $registry_address 
    - zk_ips_str: $zk_ips_str
  roles:
    - $dubbo_admin_pb_name
'''
#define func to define redis playbook template
def create_playbook(**kwargs):
    template = Template(create_hosts_with_parameters)
    return template.substitute(kwargs)

生成vpc_dubbox_zk.yml文件,方法如下:

# define func to create playbook init config
def create_pb_init(registry_address, zk_ips_str):
    print('Start to edit playbook init config...')
    with open(ansible_dir + pb_file_name, 'wb') as file_pb:
        playbook = generate_playbook_template.create_playbook(dubbo_admin=host_dubbo_admin, zookeeper=host_zookeeper, jetty_home=jetty_home, 
            jetty_home_enforce=jetty_home_enforce, dubbo_root_password=dubbo_root_password, dubbo_guest_password=dubbo_guest_password,
            registry_address=registry_address, zk_ips_str=zk_ips_str, dubbo_admin_pb_name=dubbo_admin_pb_name, zookeeper_pb_name=zookeeper_pb_name)
        file_pb.write(playbook)
    print('Editting pb_init is finished.')

生成的vpc_dubbox_zk.yml文件的内容如下:

- name: deploy dubbox service
  hosts: zookeeper
  vars:
    - jetty_home: /opt/jetty
    - enforce: f
    - dubbo_root: ***
    - dubbo_guest: ***
    - registry_address: 192.168.***.***:2181?backup=192.168.***.***:2181,192.168.***.***:2181
    - zk_ips_str: 192.168.***.***,192.168.***.***,192.168.***.***
  roles:
    - vpc_dubbox_zookeeper_playbook

- name: deploy dubbox service
  hosts: dubbo_admin
  vars:
    - jetty_home: /opt/jetty
    - enforce: f
    - dubbo_root: root
    - dubbo_guest: guest
    - registry_address: 192.168.***.***:2181?backup=192.168.***.***:2181,192.168.***.***:2181
    - zk_ips_str: 192.168.***.***,192.168.***.***,192.168.***.***
  roles:
    - vpc_dubbox_admin_playbook

vpc_dubbox_zk.yml文件中定义了一些参数,下面详细介绍文件中参数的作用:

  • hosts

    • 远程主机组名称,和Inventory文件中的远程主机组相对应。此文件说明要连接的主机组有两个。
  • vars

    • 配置文件config中的参数,提供给install_Jdk.shinstall_Jetty.shdeploy_Admin.shinstall_ZK.sh使用。
  • roles

    • 指定要执行的PlayBook名称。此文件说明要执行的PlayBook有两个。

最终生成的vpc_dubbox_zk.yml文件在目录/etc/ansible/下。

下载PlayBook

前面的章节构建PlayBook中提出,构建好PlayBook后会传到阿里云OSS上,然后通过wget命令下载到/etc/ansible/roles目录下。下载PlayBook的方法如下:

def download_playbook():
    for url in playbook_url:
        file_name = url.split('/')[-1]
        command_wget = 'wget -P ' + playbook_dir + ' ' + url
        subprocess.call(command_wget, shell = True)
        command_tar = 'tar zxf ' + playbook_dir + file_name + ' -C ' + playbook_dir
        subprocess.call(command_tar, shell = True)
        command_rm = 'rm -rf ' + playbook_dir + file_name
        subprocess.call(command_rm, shell = True)
        

执行playbook

Ansible是通过ssh命令连接远程主机,并执行playbook的。首次执行playbook前,由于当前Ansible主机并没有记录远端主机的RSA Key,会弹出RSA Key的确认对话框,对话框内容如下:

OSX10111-0c4de9cb8aea:dubbox wujin.lhr$ ssh root@112.74.205.137
    The authenticity of host '112.74.205.137 (112.74.205.137)' can't be established.
    ECDSA key fingerprint is SHA256:bbDuVh6dQYDQo/X+Qzh52VGAxBFpGSqVG0jVNCB/9cE.
    Are you sure you want to continue connecting (yes/no)?

因为整个过程不用人为的参与,所以可通过Python脚本自动实现上述确认的过程:

# define func to confirm ssh login before execute ansible
def confirm_ssh_login(all_ips):
    print('Start to confirm ssh login to all nodes...')
    if len(all_ips) == 0:
        print('Host_ips is empty')
        return
    for ip in all_ips:
        child = pexpect.spawn('ssh root@' + ip)
        ret_1 = child.expect(['Are you sure you want *', 'Password*', 'root@*', pexpect.EOF])
        if 0 == ret_1:
            child.sendline('yes')
            ret_2 = child.expect(['Password*', 'root@*', pexpect.EOF])
            if 0 == ret_2 or 1 == ret_2:
                print('Confirm ' + ip + ' ok!')
                child.sendintr()
                continue
            else:
                print('Confirm ' + ip + ' failed!')
        elif 1 == ret_1 or 2 == ret_1:
            print('Confirm ' + ip + ' ok!')
            child.sendintr()
        else:
            print('Confirm ' + ip + ' failed!')
    print('Confirm ssh login finished!')

由于用到了pexpect这个模块,在执行脚本前,需要使用pip安装pexpect模块:

pip install pexpect

VPC网络环境下未给ECS分配公网IP,导致每台ECS无法下载安装包,但是在安装JDK、Jetty以及部署Dubbox Admin和Zookeeper集群的时候需要这些安装包。因为Ansible主机配有公网IP,所以可以将安装包先下载到Ansible主机,又因为Ansible主机和每台ECS在同一个VSwitch下面,可以通过scp命令将安装包从Ansible主机传到每台ECS上。具体操作过程如下:

  1. 首先将JDK、Jetty、Dubbox Admin以及ZooKeeper安装包上传至阿里云OSS,获取安装包的下载地址。
  2. 在Python文件中填入安装包的下载地址,运行Python脚本,下载安装包至Ansible主机。方法如下:

    def wget_files():

    if not os.path.exists(wget_file_path):
        os.makedirs(wget_file_path)
    subprocess.call('wget -P ' + wget_file_path + ' ' + jdk_path, shell = True)
    subprocess.call('wget -P ' + wget_file_path + ' ' + jetty_path, shell = True)
    subprocess.call('wget -P ' + wget_file_path + ' ' + dubbo_admin_path, shell = True)
    subprocess.call('wget -P ' + wget_file_path + ' ' + zookeeper_path, shell = True)
            
  3. 通过scp命令将安装包拷贝到每台ECS。方法如下:

    def scp_files_from_ansible_host_to_ecs_zk(host_ips, zk_ips):

    scp_password_info = 'root@%s\'s password:' 
    scp_command_ecs = 'scp ' + wget_file_path + 'dubbo-admin-2.8.4.war ' + wget_file_path + 'jdk-8u101-linux-x64.rpm ' + wget_file_path + 'jetty-distribution-8.1.19.v20160209.tar.gz root@%s:/root'
    scp_command_zk = 'scp ' + wget_file_path + 'jdk-8u101-linux-x64.rpm ' + wget_file_path + 'zookeeper-3.4.6.tar.gz root@%s:/root'
    if host_ips is None or len(host_ips) == 0:
        print 'Host ips is None,exit!'
        return None
    if zk_ips is None or len(zk_ips) == 0:
        print 'ZK ips is None,exit!'
        return None
    for ip in host_ips:
        scp = pexpect.spawn(scp_command_ecs % ip)
        i = scp.expect([scp_password_info % ip, pexpect.EOF])
        if i == 0:
            scp.sendline(ecs_password)
            scp.expect(pexpect.EOF, timeout=None)
        else:
            print 'Scp files to' + ip + 'failed!'
    for ip in zk_ips:
        scp = pexpect.spawn(scp_command_zk % ip)
        i = scp.expect([scp_password_info % ip, pexpect.EOF])
        if i == 0:
            scp.sendline(ecs_password)
            scp.expect(pexpect.EOF, timeout=None)
        else:
            print 'Scp files to' + ip + 'failed!'
    
  4. 通过Ansible,执行PlayBook。方法如下:

    subprocess.call('ansible-playbook ' + pb_file_dir + '/' + pb_file_name, shell=True)

运行Ansible以后会进行Dubbox服务的部署过程。

部署Dubbox服务

Dubbox服务的部署,需要以下两个步骤:

  • 搭建ZooKeeper集群
  • 搭建Dubbox控制台集群

搭建ZooKeeper集群

ZooKeeper集群主要用来作为Dubbox服务的注册中心。

搭建ZooKeeper集群,需要以下两个步骤:

  • 安装JDK
  • 搭建ZooKeeper集群
安装JDK

因为ZooKeeper的运行需要Java环境,所以需要先安装JDK。安装JDK的脚本为install_Jdk.sh,文件内容如下:

#!/bin/bash
#日志时间格式
DATE="date +'%Y-%m-%d %H:%M:%S'"
#检查java环境是否存在
if which java 2>/dev/null; then
        echo $(eval $DATE) " java already exits" >> ~/install_dubbox.log
else
        #wget jdk安装包
        #wget http://dubbo.oss-cn-shenzhen.aliyuncs.com/jdk-8u101-linux-x64.rpm
        #echo $(eval $DATE) " wget jdk success" >> ~/install_dubbox.log
        rpm -ivh jdk-8u101-linux-x64.rpm
        rm -rf jdk-8u101-linux-x64.rpm
fi
#检查java环境是否安装成功
if which java 2>/dev/null; then
        echo $(eval $DATE) " install jdk8 success" >> ~/install_dubbox.log
else
        echo $(eval $DATE) " install jdk8 failed" >> ~/install_dubbox.log

安装JDK过程中,首先需要检查当前ECS是否存在Java环境,存在就跳过安装过程,反之则安装。
声明:JDK安装包请从Oracle官网下载,并下载本文指定的JDK版本(jdk-8u101-linux-x64.rpm)。从本文链接中下载JDK带来的任何法律问题,和本文作者无关。

搭建ZooKeeper集群

搭建ZooKeeper集群的脚本为install_ZK.sh,内容如下:

#!/bin/sh
#日志时间格式
DATE="date +'%Y-%m-%d %H:%M:%S'"
#download zk
#wget 'http://dubbo.oss-cn-shenzhen.aliyuncs.com/zookeeper-3.4.6.tar.gz'
#解压zk到/opt目录
tar -zxvf zookeeper-3.4.6.tar.gz -C /opt
#设置软链接
ln -s /opt/zookeeper-3.4.6 /opt/zookeeper
#复制zoo.cfg文件
cp /opt/zookeeper/conf/zoo_sample.cfg /opt/zookeeper/conf/zoo.cfg
#删除压缩包
rm -rf zookeeper-3.4.6.tar.gz
#读取配置文件内容,给变量赋初值
while read line
do
    #过滤掉注释行
    if [ ! "`echo $line|grep '#'`" ]; then
        varname=`echo $line|awk '{print $1}'`
        varvalue=`echo $line|awk '{print $2}'`
        varconfig=`echo $line|awk '{print $3}'`
        eval $varname=$varvalue
        eval $varname"_CONFIG"=$varconfig
    fi
done < ~/config
mkdir -p '/opt/zookeeper/data'
#修改配置文件
sed -i -e 's/^dataDir=.*/dataDir=\/opt\/zookeeper\/data/' /opt/zookeeper/conf/zoo.cfg
ZK_IPS_STR=${ZK_IPS_STR//,/ }
init_id=1
#获取本机ip
SELF_IP=`ifconfig -a|grep inet|grep -v 127.0.0.1|grep -v inet6|awk '{print $2}'|tr -d "addr:"`
for ip in $ZK_IPS_STR
do
    echo "server.$init_id=$ip:2888:3888" >> /opt/zookeeper/conf/zoo.cfg
    #创建myid文件,标识本机id号
    if [[ $SELF_IP == *$ip* ]]; then
        echo $init_id >  "/opt/zookeeper/data/myid"
    fi
    init_id=$((init_id+1))    
done
#启动zk
nohup /opt/zookeeper/bin/zkServer.sh start &
sleep 10
#centos 7 关闭防火墙
systemctl stop firewalld.service
#查看当前是否存在zookeeper进程
ZK_ID=`ps -ef |grep zookeeper |grep -v grep |awk '{print $2}'`
if [ ! "$ZK_ID" ]; then
    echo $(eval $DATE) " start zookeeper failed" >> ~/install_dubbox.error.log
else
    echo $(eval $DATE) " start zookeeper success" >> ~/install_dubbox.log
fi
#删除配置文件
rm -rf ~/config

这个脚本的主要功能是安装ZooKeeper,并通过修改ZooKeeper配置文件conf/zoo.cfg来配置ZooKeeper集群。修改后的配置文件内容如下:

tickTime=2000  
dataDir=/opt/zookeeper/data  
clientPort=2181  
initLimit=5  
syncLimit=2  
server.1=slave-01:2888:3888  
server.2=slave-02:2888:3888  
server.3=slave-03:2888:3888  

上述文件,配置了三台机器的ZooKeeper集群。在dataDir目录下,创建一个myid文件,里面内容为一个数字,用来标识当前主机号。
更多关于ZooKeeper集群配置的知识,可参考Running Replicated ZooKeeper

搭建Dubbox控制台集群

由于Dubbox控制台需要运行在Jetty容器上,Jetty容器的运行又需要有Java环境,因此部署Dubbox控制台之前,需要先安装JDK和Jetty。

搭建Dubbox控制台集群,需要以下三个步骤:

  • 安装JDK
  • 安装Jetty
  • 部署Dubbox控制台
安装JDK

安装JDK的文件为install_Jdk.sh,和上述过程安装JDK相同,此处不再重复。

安装Jetty

安装Jetty的文件为install_Jetty.sh,内容如下:

#!/bin/bash
#jetty默认安装目录
JETTY_HOME_DEFAULT="/opt/jetty"
#日志时间格式
DATE="date +'%Y-%m-%d %H:%M:%S'"
#安装jetty
#wget http://dubbo.oss-cn-shenzhen.aliyuncs.com/jetty-distribution-8.1.19.v20160209.tar.gz
#echo $(eval $DATE) " wget jetty success" >> ~/install_dubbox.log
#解压
tar zxf jetty-distribution-8.1.19.v20160209.tar.gz
echo $(eval $DATE) " tar zxf jetty success" >> ~/install_dubbox.log
#删除压缩包
rm -f jetty-distribution-8.1.19.v20160209.tar.gz
echo $(eval $DATE) " rm jetty.tgz success" >> ~/install_dubbox.log
#读取配置文件内容,给变量赋初值
while read line
do
        #过滤掉注释行
        if [ ! "`echo $line|grep '#'`" ]; then
                varname=`echo $line|awk '{print $1}'`
                varvalue=`echo $line|awk '{print $2}'`
                varconfig=`echo $line|awk '{print $3}'`
                eval $varname=$varvalue
                eval $varname"_CONFIG"=$varconfig
        fi
done < ~/config
#JETTY_HOME未配置,选择默认配置JETTY_HOME_DEFAULT
if [ ! -n "$JETTY_HOME" ]; then
        JETTY_HOME=$JETTY_HOME_DEFAULT
        rm -rf $JETTY_HOME
        mkdir -p $JETTY_HOME
        echo $(eval $DATE) " JETTY_HOME采用默认设置 $JETTY_HOME" >> ~/install_dubbox.log
#如果目录不存在,创建目录
elif [ ! -d "$JETTY_HOME" ]; then
        mkdir -p $JETTY_HOME
        echo $(eval $DATE) " 创建JETTY_HOME新目录 $JETTY_HOME" >> ~/install_dubbox.log       
#如果目录已经存在,并且配置了强制执行,则覆盖目录
elif [ "$JETTY_HOME_CONFIG" = "f" ]; then
        rm -rf $JETTY_HOME
        mkdir -p $JETTY_HOME
        echo $(eval $DATE) " 强制覆盖已存在的JETTY_HOME $JETTY_HOME" >> ~/install_dubbox.log
#如果目录已经存在,并且没有配置强制执行,退出程序
else
        echo $(eval $DATE) " $JETTY_HOME 已经存在,未选择强制覆盖,请重新设置JETTY_HOME或在配置文件中配
置强制执行选项:f" >> ~/install_dubbox.log
        #退出程序
        exit
        echo $(eval $DATE) " 程序退出" >> ~/install_dubbox.log
fi
#移动jetty到JETTY_HOME
mv jetty-distribution-8.1.19.v20160209/* $JETTY_HOME
rm -rf jetty-distribution-8.1.19.v20160209
echo $(eval $DATE) " mv jetty  success" >> ~/install_dubbox.log
#将jetty配置文件/etc/webdefault.xml中的属性dirAllowed值设置为false
cd $JETTY_HOME/etc
#确认行数
NUMBER=`grep -n "<param-name>dirAllowed</param-name>" webdefault.xml | cut  -d  ":"  -f  1`
sed -i -e "$[++NUMBER]s/.*/<param-value>false<\/param-value>/" webdefault.xml
echo $(eval $DATE) " set dirAllowed to false success" >> ~/install_dubbox.log
#查看jetty服务是否开启,即系统已经安装了jetty并已经开启
JETTY_PROCESS_ID=`ps -fe|grep jetty |grep -v grep |awk '{print $2}'`
#未开启jetty服务
if [ ! "$JETTY_PROCESS_ID" ]; then
        $JETTY_HOME/bin/jetty.sh start
        echo $(eval $DATE) " start jetty" >> ~/install_dubbox.log
else
        kill -9 $JETTY_PROCESS_ID
        echo $(eval $DATE) " stop jetty" >> ~/install_dubbox.log
        $JETTY_HOME/bin/jetty.sh start
        echo $(eval $DATE) " start jetty" >> ~/install_dubbox.log
fi
#查看jetty服务是否开启
JETTY_PROCESS_ID=`ps -fe|grep jetty |grep -v grep |awk '{print $2}'`
if [ ! "$JETTY_PROCESS_ID" ]; then
        echo $(eval $DATE) " install jetty failed" >> ~/install_dubbox.error.log
else
     echo $(eval $DATE) " install jetty success" >> ~/install_dubbox.log
     kill -9 $JETTY_PROCESS_ID
     echo $(eval $DATE) " stop jetty" >> ~/install_dubbox.log
fi

安装Jetty过程,主要包括:读取配置文件,设置Jetty安装目录,修改Jetty的配置文件etc/webdefault.xml。Jetty安装目录的选择包括以下三种情形:

  • 未指定Jetty安装目录,则选择默认目录进行安装
  • 指定了Jetty安装目录但是目录不存在,则创建目录并安装Jetty
  • 指定了Jetty安装目录并且目录已经存在

    • 如果配置了强制执行选项,则覆盖目录并安装Jetty
    • 如果没有配置强制执行选项,程序强制退出
部署Dubbox控制台

部署Dubbox服务的文件为deploy_Admin.sh,内容如下:

#!/bin/sh

#默认jetty安装目录
JETTY_HOME_DEFAULT="/opt/jetty"
#默认root用户密码
DUBBO_ADMIN_ROOT_PASSWD_DEFAULT=root
#默认guest用户密码
DUBBO_ADMIN_GUEST_PASSWD_DEFAULT=guest
#日志时间格式
DATE="date +'%m-%d-%Y %H:%M:%S'"

#读取配置文件内容,给变量赋初值
while read line
do
    #过滤掉注释行
    if [ ! "`echo $line|grep '#'`" ]; then
        varname=`echo $line|awk '{print $1}'`
        varvalue=`echo $line|awk '{print $2}'`
        varconfig=`echo $line|awk '{print $3}'`
        eval $varname=$varvalue
        eval $varname"_CONFIG"=$varconfig
    fi
done < ~/config
#JETTY_HOME未配置,选择默认配置JETTY_HOME_DEFAULT
if [ ! -n "$JETTY_HOME" ]; then
        JETTY_HOME=$JETTY_HOME_DEFAULT
#如果目录已经存在,并且没有要求强制覆盖
elif [ -d "$JETTY_HOME" ]; then
        if [ "$JETTY_HOME_CONFIG" != "f" ]; then
            echo $(eval $DATE) " $JETTY_HOME 已经存在,未选择强制覆盖,请重新设置JETTY_HOME或在配置文件中配置强制执行选项:f" >> ~/install_dubbox.log
            #退出程序
            echo $(eval $DATE) " 程序退出" >> ~/install_dubbox.log
            exit
    fi
fi

#检测admin root用户密码是否设置
if [ ! $DUBBO_ADMIN_ROOT_PASSWD ]; then
        echo $(eval $DATE) " 未设置admin root用户的密码,采用默认密码 $DUBBO_ADMIN_ROOT_PASSWD_DEFAULT" >> ~/install_dubbox.log
        DUBBO_ADMIN_ROOT_PASSWD=$DUBBO_ADMIN_ROOT_PASSWD_DEFAULT
fi

#检测admin guest用户密码是否设置
if [ ! $DUBBO_ADMIN_GUEST_PASSWD ]; then
        echo $(eval $DATE) " 未设置admin guest用户的密码,采用默认密码 $DUBBO_ADMIN_GUEST_PASSWD_DEFAULT" >> ~/install_dubbox.log
        DUBBO_ADMIN_GUEST_PASSWD=$DUBBO_ADMIN_GUEST_PASSWD_DEFAULT
fi

#从oss上下载dubbo-admin的war包
#wget http://dubbo.oss-cn-shenzhen.aliyuncs.com/dubbo-admin-2.8.4.war
#echo $(eval $DATE) " wget dubbo-admin success" >> ~/install_dubbox.log

#将war包部署到jetty上
mv dubbo-admin-2.8.4.war $JETTY_HOME/webapps/dubbo-admin.war
echo $(eval $DATE) " mv dubbo-admin.war to webapps" >> ~/install_dubbox.log

#修改配置文件
mkdir $JETTY_HOME/webapps/dubbo-admin
cd $JETTY_HOME/webapps/dubbo-admin
jar xf ../dubbo-admin.war
cd $JETTY_HOME/webapps/dubbo-admin/WEB-INF

#配置admin注册监听文件
sed -i -e "s/^dubbo.registry.address.*/dubbo.registry.address=zookeeper:\/\/$REGISTRY_ADDRESS/" dubbo.properties
echo $(eval $DATE) " set registry to redis" >> ~/install_dubbox.log

#设置root用户密码
sed -i -e "s/^dubbo.admin.root.password.*/dubbo.admin.root.password=$DUBBO_ADMIN_ROOT_PASSWD/" dubbo.properties
echo $(eval $DATE) " set user root passwd" >> ~/install_dubbox.log
#设置guest用户密码
sed -i -e "s/^dubbo.admin.guest.password.*/dubbo.admin.guest.password=$DUBBO_ADMIN_GUEST_PASSWD/" dubbo.properties
echo $(eval $DATE) " set user guest passwd" >> ~/install_dubbox.log

cd $JETTY_HOME/webapps/dubbo-admin
jar cf dubbo-admin.war *
mv dubbo-admin.war $JETTY_HOME/webapps/
rm -rf $JETTY_HOME/webapps/dubbo-admin

#启动jetty
nohup $JETTY_HOME/bin/jetty.sh start &
echo $(eval $DATE) " start jetty" >> ~/install_dubbox.log

#关闭centos7的防火墙
systemctl stop firewalld.service
sleep 30

CODE=`curl -I -m 10 -o /dev/null -s -w %{http_code}  -u root:$DUBBO_ADMIN_ROOT_PASSWD http://localhost:8080/dubbo-admin/`
echo $(eval $DATE) " return http status code: $CODE" >> ~/install_dubbox.log
if [ $CODE = 200 ]; then
    echo $(eval $DATE) " admin控制台启动成功" >> ~/install_dubbox.log
else
    echo $(eval $DATE) " admin控制台启动失败" >> ~/install_dubbox.error.log
fi
rm -rf ~/config

部署Dubbox服务控制台的过程,主要包括:先将Dubbox服务部署到Jetty上,然后修改dubbo.properties文件的方式来设置Dubbox服务注册中心为ZooKeeper集群的方式,并设置Dubbox服务控制台的登录密码。Dubbox服务注册中心的选择,可参考Dubbo用户指南

在部署Dubbox服务的过程中,有几个需要注意的问题:

  1. 当访问Dubbox服务时,需要访问服务器的8080端口,由于防火墙的原因,外部可能无法访问到服务器的Dubbox服务,因此需要修改防火墙的设置。本文所采用的ECS系统为Centos 7,简单起见,我直接关闭了系统的防火墙。关闭防火墙的方法如下:

    systemctl stop firewalld.service

    这里不建议采用直接关闭防火墙的方式。

  2. 通过Ansible控制远程服务器组启动Jetty服务时,Ansible命令执行结束以后,Jetty服务也自动退出,这是我们不想看到的结果。可通过nohup命令以守护进程的方式启动Jetty服务,可以解决Jetty服务自动退出的问题,启动Jetty命令如下:

    nohup $JETTY_HOME/bin/jetty.sh start &

Dubbox服务部署好了以后,可通过以下地址访问Dubbox服务控制台:

http://ip:8080/dubbo-admin

注意:在VPC网络下,ip指的是SLB的公网IP。

Dubbox服务部署好以后,可通过以下操作登录控制台:

输入用户名密码,点击登录:

基于资源编排在专有网络环境下快速部署高可用的Dubbox服务(ZooKeeper版)

登录进去以后的Dubbox控制台界面如下:

基于资源编排在专有网络环境下快速部署高可用的Dubbox服务(ZooKeeper版)

现在,我们可以使用Dubbox服务了。

总结

本章将从以下两个方面进行总结:

  • Dubbox服务系统结构图
  • 如何快速构建高可用Dubbox服务

Dubbox服务系统结构图

最终,采用ZooKeeper集群作为注册中心,基于资源编排快速部署出来的高可用Dubbox服务的系统结构图,如下图所示:

基于资源编排在专有网络环境下快速部署高可用的Dubbox服务(ZooKeeper版)

Dubbox服务的高可用,主要体现在两个方面:

  • 注册中心的高可用

    • 注册中心采用了ZooKeeper集群的方式,ZooKeeper集群中只要有超过半数的服务可用,Dubbox服务的注册中心就可以正常工作。
  • Dubbox服务控制台的高可用

    • 创建两台ECS实例并分别部署Dubbox控制台服务,这两台ECS挂载到一个SLB上,我们可通过SLB来访问Dubbox控制台服务。

注意:生产环境中应该将SLB放在VPC网络环境内,本文中为了测试方便,把SLB放在VPC网络环*。若需要修改SLB的网络环境,只需修改ROS资源模板

如何快速构建高可用Dubbox服务

前面章节描述的部署过程看起来可能比较繁琐,本文的核心是快速部署,因此你可以根据下面的指导,快速部署属于你的高可用Dubbox服务。四个步骤快速部署高可用Dubbox服务:

  • 准备Ansible主机
  • 下载源码
  • 修改配置文件
  • 运行main函数
准备Ansible主机

这个过程和前面章节的准备Ansible主机相同,这里不再重复。

下载源码

可从本文的附件中下载源码,然后将vpc_python文件拷贝到Ansible主机。

修改配置文件

修改vpc_python/config.py文件,文件内容如下:

#define stack name
stack_name = 'vpc_dubbox_zookeeper'
# define stack creation timeout(minutes)
create_timeout = 60
#vpc parameter
vpc_id = '******'
vswitch_id = '******'
#zookeeper cluster size
zk_size = 3
#ecsgroup parameters
ecs_password = '******'
instance_type =  'ecs.s2.large'
#ros ak id
ak_id = '******'
#ros ak secret
ak_secret = '******'
#ros region id
region_id = 'cn-shenzhen'
#zone id
zone_id = 'cn-shenzhen-a'
#set jetty path
jetty_home = '/opt/jetty'
#if jetty_home exists, choose f to overlap
jetty_home_enforce = 'f'
#dubbo admin root password
dubbo_root_password = '******'
#dubbo admin guest password
dubbo_guest_password = '******'

下面详细讲解配置文件中一些参数所代表的意义:

  • vpc_id

    • Ansible主机所在的VPC ID
  • vswitch_id

    • Ansible主机所在的VSwitch ID
  • zk_size

    • ZooKeeper集群的大小,ZooKeeper集群的大小必须为奇数,且必须大于1
  • ecs_password

    • 申请的ECS服务器的登录密码,用户名默认为root
  • ak_id

    • 用户的ak id
  • ak_secret

    • 用户的ak secret
  • region_id

    • 资源栈创建的区域
  • jetty_home

    • jetty默认安装目录
  • jetty_home_enforce

    • 是否强制安装jetty,f代表强制安装,其它代表非强制
  • dubbo_root_password

    • Dubbox控制台root用户的登录密码
  • dubbo_guest_password

    • Dubbox控制台guest用户的登录密码

    用户可根据自己的需求更改配置文件。

运行main函数

运行vpc_python/main.py。函数运行完以后,高可用Dubbox服务就部署好了。

上一篇:使用virtualenvwrapper模块管理python虚拟环境


下一篇:git和其他版本控制系统的区别