基於Ansible和Devops的一鍵測試環境部署實踐

​轉載本文需註明出處:微信公衆號EAWorld,違者必究。

隨着網絡架構的不斷升級和業務的複雜化,對產品多環境支持的要求越來越高。產品支持的數據庫、應用服務器、中間件、操作系統等的多樣化,使測試環境的組合越來越多,導致測試環境的部署難度不斷增加。

如何選擇一個合適的工具,實現多樣化環境部署的同時保證部署操作的易用性。下面分享一下我們基於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 -ilinux.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_tasks\include_role:

include_tasks\include_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"

工作量,增加腳本的複用性,我們將產品的部署過程分爲了以下幾個步驟:

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/

 

  - end -  

關於作者dozeno,高級測試開發工程師,主要參與EOS、ESB等產品的自動化測試和持續部署工作,熱衷於自動化測試、持續部署和Devops等相關技術。

 

 

 

關於EAWorld:微服務,DevOps,數據治理,移動架構原創技術分享。長按二維碼關注!

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章