隨著網絡架構的不斷升級和業務的復雜化,對產品多環境支持的要求越來越高。產品支持的數據庫、應用服務器、中間件、操作系統等的多樣化,使測試環境的組合越來越多,導致測試環境的部署難度不斷增加。
如何選擇一個合適的工具,實現多樣化環境部署的同時保證部署操作的易用性。下面分享一下我們基于Ansible和Devops實現的一鍵式測試環境部署的過程。
Ansible是一款自動化運維工具,基于Python開發,集合了眾多運維工具(Saltstack、puppet、chef等)的優點,實現了批量系統配置、批量程序部署、批量運行命令等功能。Ansible是基于模塊工作,具有豐富的內置模塊,同時也支持自定義模塊開發[1]。以下是對Ansible和其他常見運維工具的對比[2]:

而ansible在自動化運維過程時具有如下優勢:
1.基于模塊運行,有豐富的內置模塊支持
2. 基于Python開發,方便二次開發
3. 基于SSH 交互,被管機器不要安裝 Agent
4. 無Server,在任何安裝ansible的機器上執行命令即可
5. 腳本用YAML編寫,易讀和易維護
正因為ansible操作簡單、易上手,功能豐富,已被很多公司納入使用。
Ansible主要有ad-hoc和playbook兩種執行方式,Ansible Ad-hoc是一次性命令,適合執行單個、簡單的任務,一次只調用一個模塊執行,如執行:
ansible -m yum -a “name=net-tools state=present“
即可完成通過yum方式在遠程機器上安裝net-tools;執行
ansible -m service -a “name=httpd state= started“
可以在遠程服務其上啟動httpd服務,若服務已啟動,在遠程機器上不會發生任何改變。
Ansible Playbook模式使用YAML格式定義操作,通過模塊編排完成復雜的操作,以角色(role)為執行單位,一個role包含多個文件目錄,不同目錄放置不同作用的文件,一個簡單的playbook腳本目錄結構如下所示:
.
├── group_vars
│ └── all.yml
├── install.yml
├── linux.inventory
├── roles
│ └── test
│ ├── defaults
│ │ └── main.yml
│ ├── files
│ │ └── update.sh
│ ├── handlers
│ │ └── main.yml
│ ├── tasks
│ │ └── main.yml
│ ├── templates
│ │ └── server.xml
│ └── vars
│ └── main.yml
└── windows.inventory
每個目錄下放置文件的具體作用為:
files:用來存放copy模塊或script模塊調用的文件
templates:用來存放jinja2模板
tasks:目錄包含一個main.yml文件,該角色執行入口
handlers: 角色中觸發條件時執行的動作
vars: 定義此角色用到的變量
defaults:為當前角色設定默認變量
Playbook模式在安裝有ansible 的機器上執行如下命令即可:
ansible-playbook -i linux.inventory install.yml --extra-vars “host=192.168.1.1”
-i: 用來指定具體的host inventory文件,默認使用/etc/ansible/hosts文件里面定義的主機或分組
--extra-vars: 通過命令行方式指定部署用到的參數,通過命令行指定的參數優先級高于腳本中定義的參數
下面介紹幾個ansible中常用的一些模塊。
1. set_fact
set_fact模塊主要用來在部署過程中修改和新增變量,設置的變量可以在后面的role中使用。如依賴MySQL數據庫時,可通過set_fact 設置db_driver_class、db_driver_jar、db_url等參數,避免在執行時傳入復雜的參數,減少執行時參數定義的復雜度,如下所示通過set_fact設置mysql數據庫的連接信息。
- name: set driver version
when: db_version|string == '5.7'
set_fact:
db_driver_name: mysql-connector-JAVA-5.1.32.jar
db_platform: "org.hibernate.dialect.MySQLDialect"
- name: Set ipv4 db_url and driver name
when: use_net4|bool
set_fact:
db_url: "jdbc:mysql://{{ db_ip }}:{{ db_port }}/{{ db_name }}"
db_driver: "com.mysql.jdbc.Driver"
2. with_items
with_items模塊用來執行循環,可與include_vars配合完成配置文件修改等操作。
- include_vars: "common_vars.yml"
- name: modify install.properties
lineinfile:
path: "{{ user_dir }}/config/install.properties"
regexp: "{{ re_item.original }}"
line: "{{ re_item.replace }}"
with_items: "{{ deploy_var }}"
loop_control:
loop_var: re_item
3. include_tasksinclude_role:
include_tasksinclude_role模塊主要用來引用其他task或role文件,實現功能復用和動態加載。在實際部署中可將不同類型的關聯操作定義在相同的task或role中,執行中根據參數動態加載,如windows和linux下模塊定義不一樣,將windows和linux下的操作定義在不同的task中,根據執行時傳入的os_type去執行不同的操作。
- include_tasks: "common/{{os_type}}/main.yml"
- include_tasks: "dbinfo/set-{{db_type|lower}}.yml"
- include_role: "name={{product_type}}"
4. template
template模塊主要將本地文件推送到遠端,并將文件中的變量定義替換為運行時變量值,實現可變的配置。在實際部署中可以通過template修改Tomcat的默認監聽端口
- name: create dir
file:
state: directory
dest: "{{ App_server_home }}/conf"
- name: change tomcat server port
template:
src: "tomcat/server-{{ app_server_version }}.xml"
dest: "{{ app_server_home }}/conf/server.xml"
5. wait_for
wait_for模塊主要用來判斷端口監聽、文件內容等條件是否滿足條件。在實際部署中可以通過端口去判斷服務是否啟動,或者通過文件中是否包含指定內容去判斷是否繼續下一步操作。
- name: wait server start
when: have_app_server_port
wait_for:
state: started
port: "{{app_server_port}}"
timeout: 60
- name: wait install success
wait_for:
path: "{{ user_dir }}/logs/install.log"
search_regex: "esb.* installed successfully"
有了ansible豐富的模塊支持,合理的對部署過程進行設計,就可以實現多種產品多組合環境的部署。為了盡可能的減少后期腳本維護、新產品支持帶來的工作量,增加腳本的復用性,我們將產品的部署過程分為了以下幾個步驟

1. 設置參數
為了保證整個部署腳本的擴展性和對不同產品、不同版本的支持,在部署過程中會有很多值需要參數化。部署過程中用到的很多參數,有些是不易理解和記憶的,如jdbc url、drive class等,每次執行腳本的時候需要再去查;還有一些參數對某個產品某個版本是固定的,可以根據一兩個值確定下來。設置參數這一步主要是為了解決這個問題,預定義好部署過程中的諸多參數,通過參數控制部署流程和操作。
- include_vars: "vars/{{os_type}}/main.yml"
- include_vars: "vars/{{ product_type }}/main.yml"
- when: install_var_file == ''
include_vars: "vars/{{ product_type }}/var-{{ product_version| string|lower }}.yml"
- when: install_var_file != ''
include_vars: "{{ install_var_file }}"
- include_tasks: ./common/silentinstall.yml
- include_tasks: "{{ product_type }}/setfactor.yml"
- include_tasks: "dbinfo/set-{{ db_type | lower }}.yml"
- include_tasks: "dbinfo/set-url.yml"
2. 虛擬機設置
在測試過程中,為了保證測試環境的有效性,每次部署的基礎依賴環境是要干凈的。但有些基礎環境的準備如有些應用服務器或中間件等的安裝是比較耗時的。為了保證干凈的基礎依賴環境并盡量簡化部署過程的前提下,我們利用了虛擬機的快照功能。對于一些復雜的依賴環境,提前安裝好并生成虛擬機快照,在部署過程中通過恢復快照的方式來簡化部署過程。
- include_vars: defaults/main.yml
- name: manage vm snapshot
vmware_guest_snapshot:
hostname: "{{ vsphere_hostname }}"
username: "{{ vsphere_username }}"
password: "{{ vsphere_password }}"
datacenter: "{{ vsphere_datacenter }}"
validate_certs: "{{ validate_certs }}"
name: "{{ vm_name }}"
folder: "{{ vm_folder }}"
state: "revert"
snapshot_name: "{{ vm_snapshot_name }}"
delegate_to: localhost
register: revertstate
3. 清理環境
為了保證產品安裝目錄未被占用,產品監聽的端口處于空閑狀態,需要對目錄和端口進行清理操作。在執行清理環境過程中,對與有停止、卸載腳本的產品,調用腳本進行清理;沒有停止、卸載服務的使用系統命令進行清理。對于不存在的目錄進行刪除操作時的錯誤忽略。
- name: copy killport file
when: have_port
template:
src: killport.sh
dest: "{{ user_dir }}//killport.sh"
mode: 0755
- name: close {{ deploy_type }} application
when: have_port
shell: bash killport.sh
args:
chdir: "{{ user_dir }}/"
ignore_errors: yes
- name: close {{ deploy_type }} application
when: not have_port
shell: bash {{ stopFile }}
args:
chdir: "{{ user_dir }}/"
ignore_errors: yes
- name: close {{ deploy_type }} application
when: not have_port
shell: ps -ef |grep "{{ user_dir }}/{{ deploy_type }}" |grep -v grep |awk '{print $2}' |xargs kill -9
args:
chdir: "{{ user_dir }}/"
ignore_errors: yes
4. 部署依賴
部署依賴主要進行產品部署前的準備工作,包括JDK的安裝、tomcat 端口配置等。通過參數定義,進行指定版本JDK,應用服務器等依賴的安裝,并可對不同產品進行自定義配置。對于JDK安裝、應用服務配置等操作都封裝為單獨的role以便復用。
- include_role: name=jdk
- when: need_app_server|bool
include_role: name=deployappserver
5. 部署
部署主要為執行產品部署操作,主要進行安裝包的獲取,配置文件的修改、部署等操作。在執行過程中根據product_type參數選擇對應的產品role,同一產品不同產品版本在同一role下定義不同的task執行不同的操作。
- include_role: name=setfactor
- when: revert_state|bool
include_role: name=revertsnapshot
- include_role: name=cleanenv
- include_role: name=getpackage
- include_role: name=jdk
- when: need_app_server|bool
include_role: name=deployappserver
- include_role: name={{ product_type }}
- when: start_server|bool
include_role: name=startserver
具體的部署過程根據product_type定義不同的操作,其中一個產品部署操作如下所示:
- include_vars: "common_vars.yml"
- include_vars: "{{product_module|lower}}.yml"
- name: modify install.properties
lineinfile:
path: "{{ user_dir }}/config/install.properties"
regexp: "{{ re_item.original }}"
line: "{{ re_item.replace }}"
with_items: "{{ deploy_var }}"
loop_control:
loop_var: re_item
- name: update "install.sh"
lineinfile:
dest: "{{ user_dir }}/install.sh"
regexp: "{{ item.line }}"
line: "{{ item.insertafter }}"
with_items:
- { line: "^export P_I_JAVA_HOME=", insertafter: "export P_I_JAVA_HOME={{ local_java_home }}" }
- name: install product
shell: ./install.sh
args:
chdir: "{{ user_dir }}/"
- name: wait install success
wait_for:
path: "{{ user_dir }}/logs/install.log"
search_regex: "esb.* installed successfully"
timeout: 60
6. 啟動
部署完成后修改啟動參數,并啟動服務,并檢查服務的啟動狀態。
- name: copy start.sh file
template:
src: start.sh
dest: "{{ install_dir }}/start.sh"
mode: 0755
- name: change vm options
when: app_server_name | lower == 'jboss'
lineinfile:
dest: "{{ install_dir }}/startServer.sh"
regexp: 'Display our environment'
line: 'JAVA_OPTS="$JAVA_OPTS -Xms2G -Xmx2G"'
- name: start Server
shell: bash start.sh
args:
chdir: "{{ install_dir }}"
- name: wait server start
when: have_app_server_port
wait_for: timeout=60 port="{{ app_server_port }}" state=started
以上六個部署過程實現了不同產品測試環境的快速部署。
部署腳本編寫完成了,該如何有效的去執行部署腳本。每個產品部署時的數據庫信息、應用服務器相關參數有十幾二十個,每次去查看腳本定義來確定這些參數對每個測試人員是不友好的。結合普元Devops產品的發布流水線功能,就可快速便捷的實現測試環境部署。
首先通過在DevOps中定義發布流水線,將產品部署流程分為代碼倉庫拉取腳本、部署產品和發送郵件三部分。

對于部署過程中的參數,通過發布流水線的參數化功能實現。將需要修改的參數定義為入參,這樣在執行發布的時候可根據實際需要修改參數值。

對于具有明確有限個值的參數,可定義為枚舉類型的參數,并可以映射為易讀易理解的名稱,devops中對枚舉類型的參數提供下拉選擇框,方便部署過程中進行參數修改??赏ㄟ^multiSelect屬性定義實現單選和多選。


所有參數化完成后,利用devops中shell腳本執行功能調用ansible-playbook命令并將定義的參數通過extra-vars選項傳遞給ansible完成測試環境的部署。

定義的發布流水線既可以通過定時構建觸發,定時構建觸發時使用參數定義的默認值;也可以手動發布,手動發布時可以動態修改部署參數。這樣就可以根據測試需求快速實現不同組合環境的部署。

對于不同的測試環境組合,也可以定義多個發布任務。根據實際的環境規劃,對不同的任務通過標簽進行分類管理,就可以快速定位部署任務,也可以有效實現環境部署任務的管理。

Ansible結合Devops,既實現了多產品多組合環境的快速部署,也完成了對環境部署任務的高效管理,為產品測試過程中環境提供保障。
[1] https://baike.baidu.com/item/ansible/20194655?fr=aladdin
[2] https://www.edureka.co/blog/chef-vs-puppet-vs-ansible-vs-saltstack/