本文共 24084 字,大约阅读时间需要 80 分钟。
本文将介绍在经典网络环境下,基于资源编排快速部署高可用Dubbox服务的过程。做这件事情的意义在于:提供给开发者一套高可用的Dubbox服务框架,节约开发人员部署Dubbox服务的时间,并降低了部署Dubbox过程中出错的风险。
ROS
阿里云资源编排(Resource Orchestration)是一种简单易用的云计算资源管理和自动化运维服务。用户通过模板描述多个云计算资源的依赖关系、配置等,并自动完成所有资源的创建和配置,以达到自动化部署、运维等目的。编排模板同时也是一种标准化的资源和应用交付方式,并且可以随时编辑修改,使基础设施即代码(Infrastructure as Code)成为可能。 AnsibleAnsible是一个简单的自动化IT工具。引用Ansible官网的介绍就是:“Deploy apps.Manage systems.Crush complexity.Ansible helps you build a strong foundation for DevOps.”。更多Ansible的相关知识可参考。Ansible的工作机制,可参考中“Ansible 及其运行机制”章节。 Dubbox Dubbox在Dubbo服务的基础上添加了一些新功能,如:REST风格的远程调用、支持基于Jackson的JSON序列化、支持基于Kryo和FST的Java高效序列化实现、支持完全基于Java代码的Dubbo配置、升级Spring、升级Zookeeper等。想了解更多关于Dubbox服务内容可参考
本文将从两个方面展开介绍:
首先申请一台ECS作为Ansible主机,并给这台机器绑定公网IP,方便访问外网。ECS系统版本信息如下:
CentOS Linux release 7.0.1406 (Core)
此版本的ECS已经安装了Python 2.7.5。
Dubbox服务的部署需要通过Ansible来完成,本文采用了yum来安装Ansible:
yum install ansible
Ansible默认安装目录为/etc/ansible
,更多Ansible安装方式可参考。
apt-get install ansible apt-get install sshpass
ROS提供了RESTful API和SDK,本文将介绍用Python的方式调用ROS SDK来创建资源。
如果Ansible主机未安装pip,可以使用以下命令先安装pip:curl "https://bootstrap.pypa.io/get-pip.py" -o "get-pip.py"python get-pip.py
使用pip安装aliyun-python-sdk-core:
pip install aliyun-python-sdk-core
使用pip安装ROS SDK:
pip install aliyun-python-sdk-ros
关于ROS SDK详细安装和使用过程可以参考。
根据ROS API的调用方式,在Python文件中定义ROS资源模板。模板包括以下资源类型:
创建一台SLB
HealthCheck
在经典网络环境下,ECS、KVStore、SLB均选择经典网络类型,资源栈的输出定义为:ECS的Public IP,KVStore的ConnectionDomain,SLB的公网IP以及KVStore Port。
定义ROS资源模板的Python文件generate_classic_ros_template.py
:
from string import Template# define ros templatecreate_resources_with_parameters = '''{ "ROSTemplateFormatVersion": "2015-09-01", "Resources": { "SecurityGroup": { "Properties": { "SecurityGroupIngress": [ { "IpProtocol": "tcp", "NicType": "internet", "PortRange": "8080/8080", "Priority": 1, "SourceCidrIp": "0.0.0.0/0" }, { "IpProtocol": "tcp", "NicType": "internet", "PortRange": "22/22", "Priority": 1, "SourceCidrIp": "0.0.0.0/0" } ], "SecurityGroupName": "securityGroup" }, "Type": "ALIYUN::ECS::SecurityGroup" }, "InstanceGroup": { "Type": "ALIYUN::ECS::InstanceGroup", "Properties": { "ImageId": "centos7u2_64_40G_cloudinit_20160520.raw", "SecurityGroupId": { "Ref": "SecurityGroup" }, "Password": "$ecs_password", "MinAmount": 2, "MaxAmount": 2, "InstanceType": "$instance_type", "ZoneId": "$zone_id", "InternetChargeType": "PayByTraffic", "NetworkType": "classic", "InstanceName": "ecs", "AllocatePublicIP": "true" } }, "KvInstance": { "Type": "ALIYUN::REDIS::Instance", "Properties": { "EvictionPolicy": "noeviction", "Password": "$kvstore_password", "ZoneId": "$zone_id", "Capacity": $kvstore_capacity_g, "InstanceName": "kvstore" } }, "LoadBalance": { "Properties": { "AddressType": "internet", "InternetChargeType": "paybytraffic", "LoadBalancerName": "balancer" }, "Type": "ALIYUN::SLB::LoadBalancer" }, "Attachment": { "Properties": { "BackendServers": [ { "ServerId": { "Fn::Select": ["0",{ "Fn::GetAtt": [ "InstanceGroup", "InstanceIds" ] }]}, "Weight": 100 }, { "ServerId": { "Fn::Select": ["1",{ "Fn::GetAtt": [ "InstanceGroup", "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,http_3xx,http_4xx,http_5xx", "URI": "$health_check_path" }, "Scheduler": "wrr" } } }, "Outputs": { "EcsPublicIps": { "Value": { "Fn::GetAtt": [ "InstanceGroup", "PublicIps"]}, "Description": "Public IP address list of created ecs instance." }, "LoadBalanceIp": { "Value": {"Fn::GetAtt": [ "LoadBalance", "IpAddress"]} }, "KvStoreHost": { "Value": { "Fn::GetAtt": ["KvInstance", "ConnectionDomain"] } }, "KvStorePort": { "Value": { "Fn::GetAtt": ["KvInstance", "Port"] } } }}'''# define func to generate ros templatedef generate_template(**kwargs): template = Template(create_resources_with_parameters) return template.substitute(kwargs)
初始化SDK客户端对象:
client = AcsClient(ak_id, ak_secret, region_id)
构造创建资源栈的请求:
req = CreateStacksRequest.CreateStacksRequest()
指定请求资源Region:
req.set_headers({'x-acs-region-id': region_id})
构造请求体的内容,包括:栈名、过期时间戳、ROS资源模板,请求体内容如下:
create_stack_body = ''' { "Name": "%s", "TimeoutMins": %d, "Template": %s } ''' % (stack_name, create_timeout, template)req.set_content(create_stack_body)
说明:其中template是通过调用generate_classic_ros_template.generate_template
方法生成的ROS资源模板对象,调用方式如下:
#create classic network resourcestemplate = generate_classic_ros_template.generate_template(ecs_password = ecs_password, instance_type = instance_type, zone_id = zone_id, kvstore_password = kvstore_password, kvstore_capacity_g = kvstore_capacity_g, listen_port = 8080, bachend_server_port =8080, health_check_path = '/dubbo-admin/favicon.ico')
发送请求,创建资源栈:
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
请求成功会返回资源栈的Id
和Name
信息,请求发出以后,可到查看资源栈详情。
资源栈创建好以后,我们再次调用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
调用时需要传入创建好的资源栈Id
和Name
信息,资源栈信息可从获取。由于创建资源栈所需时间未定,方法中定义了获取超时重试的机制。每次重试以后的等待时间要比上次等待时间少interval
秒,在attempt
次尝试后仍未获取到资源栈信息视为资源创建失败(一般不会出现这种情况)。
资源栈的输出格式如下:
[{u'OutputKey': u'KvStorePort', u'Description': u'No description given', u'OutputValue': 6379}, {u'OutputKey': u'EcsPublicIps', u'Description': u'No description given', u'OutputValue': [u'112.74.x.x', u'112.74.x.x']}, {u'OutputKey': u'KvStoreHost', u'Description': u'No description given', u'OutputValue': u'xxxx.x.xxxxx.kvstore.aliyuncs.com'}, {u'OutputKey': u'LoadBalanceIp', u'Description': u'No description given', u'OutputValue': u'112.74.x.x'}]
我们将以上输出定义为Outputs
。
根据Outputs
,获取ECS公网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
由于ECS登录密码在配置文件中已经给出,可以直接使用,用户默认为root用户,根据这些信息编辑/etc/ansible/hosts
文件,即通常我们所说的Ansible Inventory文件。方法如下:
def edit_hosts(remote_ips, remote_ports, remote_users, remote_pass): print 'Start edit hosts' if len(remote_ips) <= 0: print('Error! Remote ips is empty!') return if len(remote_ports) <= 0: print 'Error! Remote ports is empty!' return if len(remote_users) <= 0: print('Error! Remote users is empty!') return if len(remote_pass) <= 0: print('Error! Remote pass is empty!') host_str = ' ansible_ssh_port=%s ansible_ssh_user=%s ansible_ssh_pass=%s\n' with open(hosts_dir + '/' + hosts_file, 'wb') as file: file.write( '[%s]\n' % service_name ) for index in range(len(remote_ips)): file.write( ('%s'+host_str) % (remote_ips[index], remote_ports[index], remote_users[index], remote_pass[index]) ) print 'Edit hosts end'
生成Inventory文件,即/etc/Ansible/hosts
文件。文件内容如下:
[classic_dubbox]112.74.x.x ansible_ssh_port=22 ansible_ssh_user=xxxx ansible_ssh_pass=xxxx112.74.x.x ansible_ssh_port=22 ansible_ssh_user=xxxx ansible_ssh_pass=xxxx
Inventory文件中定义了远程主机组classic_dubbox
,并指明了该主机组下云主机的登录地址、登录协议(默认是 SSH )、登录端口、登录用户名和密码。
根据Outputs
,获取KVStore Host。方法如下:
def get_kvstore_host(outputs): for i in range(len(outputs)): if outputs[i]['OutputKey'] == kvstore_host_output_key: return outputs[i]['OutputValue'] return None
根据Outputs
,获取KVStore Port。方法如下:
def get_kvstore_port(outputs): for i in range(len(outputs)): if outputs[i]['OutputKey'] == kvstore_port_output_key: return outputs[i]['OutputValue'] return None
根据Outputs
,获取SLB 公网IP。方法如下:
def get_loadbalance_ip(outputs): for i in range(len(outputs)): if outputs[i]['OutputKey'] == loadbalance_ip_output_key: return outputs[i]['OutputValue'] return None
在/etc/ansible
目录下创建文件夹classic_dubbox
,并在此文件夹下生成PlayBook的执行文件classic_dubbox.yml
。生成classic_dubbox.yml
的方法如下:
def create_pb_init(kvstore_port, kvstore_host): print('Start to edit playbook init config...') #if service folder not exist, create it if not os.path.exists(pb_file_dir): os.makedirs(pb_file_dir) with open(pb_file_dir + '/' + pb_file_name, 'wb') as file_pb: playbook = generate_playbook_template.create_playbook(service_name=service_name, jetty_home=jetty_home, jetty_home_enforce=jetty_home_enforce, dubbo_root_password=dubbo_root_password, dubbo_guest_password=dubbo_guest_password, kvstore_host=kvstore_host, kvstore_port=kvstore_port, kvstore_password=kvstore_password, pb_name=pb_name) file_pb.write(playbook) print('Editting pb_init is finished.')
生成的classic_dubbox.yml
文件内容如下:
- name: deploy dubbox service hosts: classic_dubbox vars: - jetty_home: /opt/jetty - enforce: f - dubbo_root: root - dubbo_guest: guest - redis_host: c84efef77c4d49db.m.cnsza.kvstore.aliyuncs.com - redis_port: 6379 - redis_pwd: vmADMIN123 roles: - classic_dubbox_playbook
文件中的属性解释如下:
hosts
vars
install_Jdk.sh
install_Jetty.sh
deploy_Admin.sh
这三个脚本使用。roles
classic_dubbox_playbook
。classic_dubbox_playbook
定义好后会被传到阿里云oss,运行Python脚本会将classic_dubbox_playbook
下载至/etc/ansible/classic_dubbox
目录下。下载PlayBook到Ansible主机的方法如下:
# define func to download playbookdef download_playbook(): file_name = playbook_url.split('/')[-1] command_wget = 'wget -P ' + ansible_dir + service_name + ' ' + playbook_url print 'Execute linux command:' + command_wget subprocess.call(command_wget, shell = True) command_tar = 'tar zxf ' + ansible_dir + service_name + '/' + file_name + ' -C ' + ansible_dir + service_name print 'Execute linux command:' + command_tar subprocess.call(command_tar, shell = True) command_rm = 'rm -rf ' + ansible_dir + service_name + '/' + file_name print 'Execute linux command:' + command_rm subprocess.call(command_rm, shell = True)
classic_dubbox_playbook
目录结构如下:
[root@iZ94jwkjg0sZ classic_dubbox]# ls -l classic_dubbox_playbook/总用量 12drwxr-xr-x 2 501 games 4096 8月 8 11:27 filesdrwxr-xr-x 2 501 games 4096 8月 8 11:27 tasksdrwxr-xr-x 2 501 games 4096 8月 8 11:04 templates
关于classic_dubbox_playbook
结构的说明:
files
install_Jdk.sh
install_Jetty.sh
deploy_Admin.sh
三个文件,这三个文件分别用来安装JDK、Jetty以及部署Dubbox服务。tasks
存放要执行的yml文件 install.yml
,文件内容如下:
name: add config
template:dest: /root/configsrc: config.j2mode: 0600
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}} #KvStore域名,端口,密码设置,端口号可省略 REDIS_HOST { {redis_host}} REDIS_PORT { {redis_port}} REDIS_PASSWD { {redis_pwd}}
classic_dubbox.yml
文件中获取,然后在通过Ansible执行PlayBook的过程中在每一台远程主机/root
目录下生成config
文件,提供给install_Jdk.sh
install_Jetty.sh
deploy_Admin.sh
这三个脚本读取配置信息。Ansible是通过ssh命令连接远程主机,并执行PlayBook的。首次执行PlayBook前,由于当前Ansible主机并没有记录远端主机的RSA key,会弹出确认对话框,输入yes,回车:
OSX10111-0c4de9cb8aea:dubbox wujin.lhr$ ssh root@112.74.205.137The 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 ansibledef confirm_ssh_login(host_ips): print('Start to confirm ssh login to all nodes...') if len(host_ips) == 0: print('Host_ips is empty') return for ip in host_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
最后执行Ansible
subprocess.call('ansible-playbook ' + pb_file_dir + '/' + pb_file_name, shell=True)
运行Ansible以后会进行Dubbox服务的部署。
运行Ansible PlayBook以后,会进行Dubbox服务的部署,主要分为以下三个步骤:
安装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.logelse #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.rpmfi#检查java环境是否安装成功if which java 2>/dev/null; then echo $(eval $DATE) " install jdk8 success" >> ~/install_dubbox.logelse echo $(eval $DATE) " install jdk8 failed" >> ~/install_dubbox.log
安装JDK过程中,首先需要检查当前ECS是否存在java环境,存在就跳过安装过程,反之则安装JDK。
声明:JDK安装包请从Oracle官网下载,并下载本文指定的JDK版本。由于从本文链接中下载JDK带来的任何问题,和本文作者无关。安装Jetty的文件为install_Jetty.sh
,内容如下:
#!/bin/bash#jetty默认安装目录JETTY_HOME_DEFAULT=/opt/jetty#日志时间格式DATE="date +'%Y-%m-%d %H:%M:%S'"#安装jettywget http://dubbo.oss-cn-shenzhen.aliyuncs.com/jetty-distribution-8.1.19.v20160209.tar.gzecho $(eval $DATE) " wget jetty success" >> ~/install_dubbox.log#解压tar zxf jetty-distribution-8.1.19.v20160209.tar.gzecho $(eval $DATE) " tar zxf jetty success" >> ~/install_dubbox.log#删除压缩包rm -f jetty-distribution-8.1.19.v20160209.tar.gzecho $(eval $DATE) " rm jetty.tgz success" >> ~/install_dubbox.log#读取配置文件内容,给变量赋初值while read linedo #过滤掉注释行 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 fidone < ~/config#JETTY_HOME未配置,选择默认配置JETTY_HOME_DEFAULTif [ ! -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.logfi#移动jetty到JETTY_HOMEmv jetty-distribution-8.1.19.v20160209/* $JETTY_HOMErm -rf jetty-distribution-8.1.19.v20160209echo $(eval $DATE) " mv jetty success" >> ~/install_dubbox.log#将jetty配置文件/etc/webdefault.xml中的属性dirAllowed值设置为falsecd $JETTY_HOME/etc#确认行数NUMBER=`grep -n "dirAllowed " webdefault.xml | cut -d ":" -f 1`sed -i -e "$[++NUMBER]s/.*/false<\/param-value>/" webdefault.xmlecho $(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.logelse 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.logfi#查看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.logelse echo $(eval $DATE) " install jetty success" >> ~/install_dubbox.log kill -9 $JETTY_PROCESS_ID echo $(eval $DATE) " stop jetty" >> ~/install_dubbox.logfi
安装Jetty过程,主要包括:读取配置文件,设置Jetty安装目录,修改Jetty的配置文件。Jetty安装目录的选择包括以下几种情形:
指定了Jetty安装目录并且目录已经存在
部署Dubbox服务的文件为deploy_Admin.sh
,内容如下:
#!/bin/sh#redis默认端口号REDIS_PORT_DEFAULT=6379#默认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 linedo #过滤掉注释行 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 fidone < ~/config#JETTY_HOME未配置,选择默认配置JETTY_HOME_DEFAULTif [ ! -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 fifi#检测REDIS_HOST是否设置if [ ! $REDIS_HOST ]; then echo $(eval $DATE) " 未设置KvStore主机域名" >> ~/install_dubbox.log echo $(eval $DATE) " 程序退出" >> ~/install_dubbox.log #退出程序 exitfi#检测REDIS_PASSWD是否设置if [ ! $REDIS_PASSWD ]; then echo $(eval $DATE) " 未设置KvStore登录密码" >> ~/install_dubbox.log echo $(eval $DATE) " 程序退出" >> ~/install_dubbox.log exitfi#检测redis端口号是否设置,未设置的采用默认端口号if [ ! $REDIS_PORT ]; then echo $(eval $DATE) " 未设置KvStore端口号,选择默认端口号 $REDIS_PORT_DEFAULT" >> ~/install_dubbox.log REDIS_PORT=$REDIS_PORT_DEFAULTfi#检测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_DEFAULTfi#检测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_DEFAULTfi#从oss上下载dubbo-admin的war包wget http://dubbo.oss-cn-shenzhen.aliyuncs.com/dubbo-admin-2.8.4.warecho $(eval $DATE) " wget dubbo-admin success" >> ~/install_dubbox.log#将war包部署到jetty上mv dubbo-admin-2.8.4.war $JETTY_HOME/webapps/dubbo-admin.warecho $(eval $DATE) " mv dubbo-admin.war to webapps" >> ~/install_dubbox.log#修改配置文件mkdir $JETTY_HOME/webapps/dubbo-admincd $JETTY_HOME/webapps/dubbo-adminjar xf ../dubbo-admin.warcd $JETTY_HOME/webapps/dubbo-admin/WEB-INF#配置admin注册监听文件sed -i -e "s/^dubbo.registry.address.*/dubbo.registry.address=redis:\/\/a:$REDIS_PASSWD@$REDIS_HOST:$REDIS_PORT/" dubbo.propertiesecho $(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.propertiesecho $(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.propertiesecho $(eval $DATE) " set user guest passwd" >> ~/install_dubbox.logcd $JETTY_HOME/webapps/dubbo-adminjar cf dubbo-admin.war *mv dubbo-admin.war $JETTY_HOME/webapps/rm -rf $JETTY_HOME/webapps/dubbo-admin#启动jettynohup $JETTY_HOME/bin/jetty.sh start &echo $(eval $DATE) " start jetty" >> ~/install_dubbox.log#关闭centos7的防火墙systemctl stop firewalld.servicesleep 30CODE=`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.logif [ $CODE = 200 ]; then echo $(eval $DATE) " admin控制台启动成功" >> ~/install_dubbox.logelse echo $(eval $DATE) " admin控制台启动失败" >> ~/install_dubbox.error.logfirm -rf ~/config
部署Dubbox服务过程,主要包括:检查设置是否合理,将Dubbox服务部署到Jetty,设置Dubbox服务注册中心为redis,修改dubbo.properties配置文件,设置控制台的登录密码。
在部署Dubbox服务的过程中,有几个需要注意的问题:
当访问Dubbox服务时,需要访问服务器的8080端口,由于防火墙的原因,外部可能无法访问到服务器的Dubbox服务,因此需要修改防火墙的设置。我创建的ECS系统为Centos 7,为了简单起见,我直接关闭了系统的防火墙:
systemctl stop firewalld.service
nohup $JETTY_HOME/bin/jetty.sh start &
dubbo.registry.address=redis://user:password@ip:port
user
可随便填写,但是不能为空,password
为redis服务的登录密码,redis服务默认port
为6379 Dubbox服务部署好了以后,可通过以下地址访问Dubbox服务控制台:
http://ip:8080/dubbo-admin
注意:ip可以是SLB的公网IP,也可以是任意一台ECS的公网IP
输入用户名密码,点击登录:
Dubbox服务控制台如下:
现在,我们可以使用Dubbox服务了。
最终,基于资源编排快速部署出来的高可用Dubbox服务框架如下图所示:
Dubbox服务的高可用,主要体现在两个方面:
注册中心的高可用
Dubbox服务控制台的高可用
转载地址:http://jetla.baihongyu.com/