基於資源編排在經典網路環境下快速部署高可用Dubbox服務(Redis版)

來源:互聯網
上載者:User

摘要: 本文將介紹在經典網路環境下,基於資源編排快速部署高可用Dubbox服務的程序。做這件事情的意義在於:提供給開發人員一套高可用Dubbox服務架構,節約開發人員部署Dubbox服務的時間,並降低了部署Dubbox程序中出錯的風險。

本文將介紹在經典網路環境下,基於資源編排快速部署高可用Dubbox服務的程序。做這件事情的意義在於:提供給開發人員一套高可用Dubbox服務架構,節約開發人員部署Dubbox服務的時間,並降低了部署Dubbox程序中出錯的風險。

ROS
阿裡雲資源編排(ResourceOrchestration)是一種簡單易用的雲端運算資源管理和自動化運維服務。用戶通過範本標題多個雲端運算資源的相依性屬性、設定等,並自動完成所有資源的建立和設定,以達到自動化部署、運維等目的。編排範本同時也是一種標準化的資源和套用交付方式,並且可以隨時編輯修改,使基礎設施即代碼(Infrastructureas Code)成為可能。
Ansible
Ansible是一個簡單的自動化IT工具。參考Ansible官網的介紹就是:“Deployapps.Manage systems.Crush complexity.Ansible helps you build a strongfoundation for DevOps.”。
其他Ansible的相關知識可參考Ansible中文權威指南
Ansible的工作機制,可參考基於資源編排和 Ansible 在 VPC 下快速交付套用中“Ansible 及其執行機制”章節。
Dubbox
Dubbox在Dubbo服務的基礎上新增了一些新功能,如:REST風格的遠程叫用、支援基於Jackson的JSON序號、支援基於Kryo和FST的Java高效序號實現、支援完全基於Java代碼的Dubbo設定、升級Spring、升級Zookeeper等。想瞭解其他關於Dubbox服務內容可參考DubboxGithub

本文將從兩個方面展開介紹

  • 安裝Ansible和ROS SDK
  • 經典網路環境下快速部署Dubbox服務
  • 需要注意的問題

安裝Ansible和ROS SDK

首先申請一台ECS作為Ansible主機,並給這台機器繫結公網IP,方便存取外網。ECS系統組建資訊如下:

CentOS Linux release 7.0.1406 (Core)

此組建的ECS已經安裝了Python2.7.5。

安裝Ansible

Dubbox服務的部署需要通過Ansible來完成,本文採用了yum來安裝Ansible:

yum install ansible

Ansible預設安裝目錄為/etc/ansible,其他Ansible安裝方式可參考AnsibleInstallation
注意:如果採用的是Ubuntu系統,安裝Ansible程序如下:

apt-get install ansible
apt-get install sshpass

安裝ROSSDK

ROS提供了RESTfulAPI和SDK,本文將介紹用Python的方式叫用ROSSDK來建立資源。
如果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

關於ROSSDK詳細安裝和使用程序可以參考阿裡雲資源編排服務Python SDK使用入門

經典網路環境下快速部署Dubbox服務

定義ROS資源範本

根據ROSAPI的呼叫者式,在Python檔案中定義ROS資源範本。範本包括以下資源類型:

·"Type":"ALIYUN::ECS::InstanceGroup"

    • 定義了包含兩台ECS的InstanceGroup

·"Type":"ALIYUN::REDIS::Instance"

    • 定義一台KVStore,作為Dubbox服務的註冊中心

·"Type":"ALIYUN::SLB::LoadBalancer"

    • 建立一台SLB

·"Type":"ALIYUN::SLB::BackendServerAttachment"

    • 給SLB新增後端伺服器

·"Type":"ALIYUN::SLB::Listener"

    • SLB監聽兩台ECS的8080埠,並設定了HealthCheck

·"Type":"ALIYUN::ECS::SecurityGroup"

    • 定義安全性群組

在經典網路環境下,ECS、KVStore、SLB均選擇經典網路類型,資源棧的輸出定義為:ECS的Public IP,KVStore的ConnectionDomain,SLB的公網IP以及KVStorePort。

定義ROS資源範本的Python檔案generate_classic_ros_template.py:

from string import Template
# define ros template
create_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 template
def 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 resources
template = 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 主控台查看資源棧詳情。

建立Inventory,構建PlayBook

追蹤資源棧輸出資訊

資源棧建立好以後,我們再次叫用ROSAPI追蹤資源棧的輸出資訊。方法如下:

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。

編輯Inventory檔案

根據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檔案,即通常我們所說的AnsibleInventory檔案。方法如下:

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=xxxx
112.74.x.x ansible_ssh_port=22 ansible_ssh_user=xxxx ansible_ssh_pass=xxxx

Inventory檔案中定義了遠程主機群組classic_dubbox,並指明了該主機群組下雲主機的登入位址、登入合約(預設是SSH )、登入埠、登入使用者名稱和密碼。

生成和執行PlayBook

生成Ansible可執行檔

根據Outputs,追蹤KVStoreHost。方法如下:

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,追蹤KVStorePort。方法如下:

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

    • 遠端主機群組名稱,和Inventory檔案中的遠端主機群組相對應。

·vars

    • 設定參數,提供給install_Jdk.shinstall_Jetty.shdeploy_Admin.sh這三個腳本使用。

·roles

    • 指定要執行的PlayBook為classic_dubbox_playbook。
下載PlayBook檔案

classic_dubbox_playbook定義好後會被傳到阿裡雲oss,執行Python腳本會將classic_dubbox_playbook下載至/etc/ansible/classic_dubbox目錄下。下載PlayBook到Ansible主機的方法如下:

# define func to download playbook
def 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/
總用量 12
drwxr-xr-x 2 501 games 4096 8月8 11:27 files
drwxr-xr-x 2 501 games 4096 8月8 11:27 tasks
drwxr-xr-x 2 501 games 4096 8月8 11:04 templates

關於classic_dubbox_playbook結構的說明:

·files

    • 存放install_Jdk.shinstall_Jetty.shdeploy_Admin.sh三個檔案,這三個檔案分別用來安裝JDK、Jetty以及部署Dubbox服務。

·tasks

o存放要執行的yml檔案install.yml,檔案內容如下:

o---
o- name: add config
otemplate:
odest: /root/config
osrc: config.j2
omode: 0600
o
o- name: run install jdk
oscript: install_Jdk.sh
o
o- name: run install jetty
oscript: install_Jetty.sh
o
o- name: run deploy admin
oscript: deploy_Admin.sh

·templates

o存放設定檔範本config.j2,檔案內容如下:

o#第一列變數名第二列變數值第三列設定項不同列之間用tab或者空白字元分開
o#jetty安裝路徑,當目錄已經存在的時候加上f參數會強制覆寫,否則會結束安裝
oJETTY_HOME {{jetty_home}} {{enforce}}
o#設定admin主控台root用戶的密碼
oDUBBO_ADMIN_ROOT_PASSWD {{dubbo_root}}
o#設定admin主控台guest用戶的密碼
oDUBBO_ADMIN_GUEST_PASSWD {{dubbo_guest}}
o#KvStore網域名稱,埠,密碼設定,連接埠號碼可省略
oREDIS_HOST {{redis_host}}
oREDIS_PORT {{redis_port}}
oREDIS_PASSWD {{redis_pwd}}

o設定檔config.j2中的參數值從classic_dubbox.yml檔案中追蹤,然後在通過Ansible執行PlayBook的程序中在每一台遠端主機/root目錄下生成config檔案,提供給install_Jdk.shinstall_Jetty.shdeploy_Admin.sh這三個腳本讀取設定資訊。

執行PlayBook

Ansible是通過ssh指令串連遠端主機,並執行PlayBook的。首次執行PlayBook前,由於本期Ansible主機並沒有記錄遠端主機的RSAkey,會彈出確認對話方塊,輸入yes,回車:

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(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服務的部署。

快速部署Dubbox服務

執行AnsiblePlayBook以後,會進行Dubbox服務的部署,主要分為以下三個步驟:

安裝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。
宣告:JDK安裝包請從Oracle官網下載,並下載本文指定的JDK組建。由於從本文連結中下載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<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的設定檔。Jetty安裝目錄的選擇包括以下幾種情形:

  • 未指定Jetty安裝目錄,則選擇預設目錄進行安裝
  • 指定了Jetty安裝目錄並且目錄不存在,則建立目錄並安裝Jetty
  • 指定了Jetty安裝目錄並且目錄已經存在
    • 如果設定了強制執行選項,則覆寫目錄並安裝Jetty
    • 如果沒有設定強制執行選項,程式強制結束

部署Dubbox服務

部署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 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

#檢測REDIS_HOST是否設定
if [ ! $REDIS_HOST ]; then
echo $(eval $DATE) "未設定KvStore主機網域名稱" >> ~/install_dubbox.log
echo $(eval $DATE) "程式結束" >> ~/install_dubbox.log
#結束程式
exit
fi

#檢測REDIS_PASSWD是否設定
if [ ! $REDIS_PASSWD ]; then
echo $(eval $DATE) "未設定KvStore登入密碼" >> ~/install_dubbox.log
echo $(eval $DATE) "程式結束" >> ~/install_dubbox.log
exit
fi

#檢測redis連接埠號碼是否設定,未設定的採用預設連接埠號碼
if [ ! $REDIS_PORT ]; then
echo $(eval $DATE) "未設定KvStore連接埠號碼,選擇預設連接埠號碼 $REDIS_PORT_DEFAULT" >> ~/install_dubbox.log
REDIS_PORT=$REDIS_PORT_DEFAULT
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=redis:\/\/a:$REDIS_PASSWD@$REDIS_HOST:$REDIS_PORT/" 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,設定Dubbox服務註冊中心為redis,修改dubbo.properties設定檔,設定主控台的登入密碼。

在部署Dubbox服務的程序中,有幾個需要注意的問題:

1.當存取Dubbox服務時,需要存取伺服器的8080埠,由於防火牆的原因,外部可能無法存取到伺服器的Dubbox服務,因此需要修改防火牆的設定。我建立的ECS系統為Centos7,為了簡單起見,我直接關閉了系統的防火牆:

2.systemctl stop firewalld.service

3.通過Ansible控制遠端伺服器組啟動Jetty服務時,Ansible指令執行結束以後,Jetty服務也自動結束,這是我們不想看到的結果。可通過nohup指令以守護流程的方式啟動Jetty服務,可以解決Jetty服務自動結束的問題,啟動Jetty指令如下:

4.nohup $JETTY_HOME/bin/jetty.sh start &

5.Dubbox服務支援redis註冊中心加密的方式,設定檔dubbo.properties中的格式為:

6.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服務的高可用,主要體現在兩個方面:

·註冊中心的高可用

    • 註冊中心採用了KVStore,KVStore其實是一個主備雙機的高可用Redis伺服器。

·Dubbox服務主控台的高可用

    • 建立兩台ECS實例並分別部署Dubbox服務,然後建立一個SLB,將之前建立的兩台ECS實例作為SLB的後端伺服器,最後給這個SLB新增監聽,並設定健康檢查。外部通過SLB來存取Dubbox服務。

相關產品:

  1. 資源編排ROS
  2. 雲資料庫 Redis 版
  3. 專有網路VPC
  4. 雲端服務器ECS
相關文章

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.