持續交付與滾動升級

介紹

持續交付是頻繁對軟件應用程序持續更新的概念.html

這個想法使在大量頻繁的更新面前, 你沒必要等待在一個指定的特殊時間點, 而且使你的組織在響應過程當中變得更好.前端

一些 Ansible 用戶每小時都在部署更新給他們的最終用戶甚至更加頻繁 – 每時每刻都有代碼修改的批准. 要實現這一點, 你須要工具能在零停機的時間內快速的應用這些更新.python

本文檔詳細介紹瞭如何現實這一目標, 使用 Ansible playbooks 做爲一個完整例子的模板: lamp_haproxy. 這個例子使用了大量的 Ansible 特性: roles, templates 和 group variables, 而且它還配備了一個業務流程的 playbook 能夠作到零停機滾動升級 web 應用程序棧.mysql

這個 playbooks 基於 CentOS 部署 Apache, PHP, MySQL, Nagios, 和 HAProxy 這些服務.github

在這裏咱們不去討論如何運行這些 playbooks. 閱讀包含在 github 項目中關於這個例子的 README 信息. 相反的, 咱們將進一步觀察這些 playbook 而且去解釋它們.web

部署網站

讓咱們首先使用 site.yml. 這是咱們網站部署的 playbook. 它被用來部署咱們最初的網站以及推送更新到全部的服務器:sql

---
# 這個 playbook 在這個網站上部署整個應用程序.

# 應用通用的配置到全部的主機上
- hosts: all

  roles:
  - common

# 配置和部署數據庫服務器.
- hosts: dbservers

  roles:
  - db

# 配置和部署 web 服務器. 注意這裏咱們包含了兩個 roles, 這個 'base-apache' role 用來簡單設置 Apache, 而 'web' 則包含了咱們的 web 應用程序.

- hosts: webservers

  roles:
  - base-apache
  - web

# 配置和部署 load balancer(s).
- hosts: lbservers

  roles:
  - haproxy

# 配置和部署 Nagios 監控節點(s).
- hosts: monitoring

  roles:
  - base-apache
  - nagios

Noteshell

若是你不熟悉 playbooks 和 plays, 你應該回顧 Playbooks.

在這個 palybook 咱們有 5 個 plays. 首先第一個目標 all (全部)主機和適用於全部主機的 commonrole. 這是整個網站要作的事像 yum 倉庫的配置, 防火牆的配置, 和其餘任何須要適用於全部服務器的配置.

接下來的這四個 plays 將運行於指定的主機組並特定的角色應用於這些服務器. 隨着對 Nagios monitoring, 數據庫角色, 和 web應用程序的應用, 咱們能夠經過 base-apache 角色安裝和配置一個基本的 Apache. 這是 Nagios 主機和 web 應用程序所須要的.

可重用的: Roles

關於 roles 你如今應該有一點了解以及它們是如何工做的. Roles 是組織: tasks, handlers, templates, 和 files, 到可重用的組件中的方法.

這個例子有 6 個 roles: commonbase-apachedbhaproxynagios, 和 web. 你如何組織你的 roles 是由你和你的應用程序所決定, 可是大部分網站都將適用一個或多個共同的 roles, 和一些列關於應用程序特定的 roles 來安裝和配置這個網站的特定部分.

Roles 能夠用變量和依賴關係, 你能夠經過參數來調整它們的行爲. 你能夠在 Playbook Roles and Include Statements 章節中閱讀更多關於 roles

配置: Group Variables

Group variables 是應用在服務器組上的. 經過設置和修改參數將他們應用在 templates 中來定義 playbooks 的行爲. 他們被存儲在和你的 inventory 相同目錄下名爲 group_vars 的目錄中. 下面是 lamp_haproxy 的 group_vars/all 文件內容. 正如你所指望的, 這些變量將會應用到 inventory 中的全部服務器上:

---
httpd_port: 80
ntpserver: 192.168.1.2

這是一個 YAML 文件, 而且你能夠建立列表和字典等更加複雜的變量結構. 在這種狀況下, 咱們只設置了兩個變量, 一個作爲 web server 的端口, 一個做爲咱們服務器所使用的時間同步 NTP 服務的地址.

這是另一個 group variables 文件. 這個 group_vars/dbservers 適用於在 dbservers 組中的主機:

---
mysqlservice: mysqld
mysql_port: 3306
dbuser: root
dbname: foodb
upassword: usersecret

若是你看了這個例子, 你會發現對於 webservers 組合 lbservers 組的 group variables 十分類似.

這些變量能夠用於任何地方. 你能夠在 playbooks 中使用它們, 像這樣, 在 roles/db/tasks/main.yml:

- name: Create Application Database
  mysql_db: name={{ dbname }} state=present

- name: Create Application DB User
  mysql_user: name={{ dbuser }} password={{ upassword }}
              priv=*.*:ALL host='%' state=present

你也能夠在 templates 中使用這些變量, 想這樣, 在 roles/common/templates/ntp.conf.j2:

driftfile /var/lib/ntp/drift

restrict 127.0.0.1
restrict -6 ::1

server {{ ntpserver }}

includefile /etc/ntp/crypto/pw

keys /etc/ntp/keys

你能夠看到這些變量替換的語法 {{ and }} 和 templates 中的變量是相同的. 這種花括號格式是採用的jinj2語法, 你在對於內部的數據作各類操做及應用不一樣的過濾器. 在 templates, 你也可使用循環和 if 語句來處理更加複雜的狀況, 想這樣, 在 roles/common/templates/iptables.j2:

{% if inventory_hostname in groups['dbservers'] %}
-A INPUT -p tcp  --dport 3306 -j  ACCEPT
{% endif %}

這是用來判斷, 名爲 (inventory_hostname) 的機器是否存在於組 dbservers. 若是這樣的話, 該機器將會添加一條 目標端口爲 3306 的 iptables 容許規則.

這有一些其餘的例子, 來自相同的模板:

{% for host in groups['monitoring'] %}
-A INPUT -p tcp -s {{ hostvars[host].ansible_default_ipv4.address }} --dport 5666 -j ACCEPT
{% endfor %}

這裏循環了一個組名爲 monitoring 中的全部主機, 而且配置了源地址爲全部監控主機的 IPV4 地址目標端口爲 5666 的 iptables 容許規則到當前主機上, 正由於如此 Nagios 才能夠監控這些主機.

你能夠學到更多關於 Jinja2 的功能 here, 而且你能夠讀到更多關於 Ansible 全部的變量在這個 Variables 章節

滾動升級

如今你有了一個全面的網站包含 web servers, 一個 load balancer, 和 monitoring. 如何更新它? 這就是 Ansible 的特殊功能發揮做用. 儘管一些應用程序使用’業務流程’來編排命令執行的邏輯, Ansible將指揮編排這些機器, 而且擁有一個至關複雜的引擎.

Ansible 有能力在一次操做中協調多種應用程序, 使在進行更新升級咱們的 web 應用程序時更加實現零停機時間. 這是一個單獨的 playbook, 叫作 roleing_upgrade.yml.

看這個 playbook, 你能夠看到它是由兩個 plays 組成. 首先第一個看起來十分簡單像這樣:

- hosts: monitoring
  tasks: []

這裏要作什麼, 爲何沒有 tasks? 你可能知道 Ansible 在運行以前會從服務上收集 「facts」. 這些 facts 是不少種有用的信息: 網絡信息, OS/發行版本, 配置. 在咱們的方案中, 在更新以前咱們須要瞭解關於全部監控服務器的環境信息, 所以這個簡單的 paly 將會在咱們的所監控的服務器上強制收集 fact 信息. 你有時會見到這種模式, 這是一個有用的技巧.

接下來的部分是更新 play. 第一部分看起來是這樣:

- hosts: webservers
  user: root
  serial: 1

咱們僅僅是像一般同樣在 webservers 組中定義了 play. 這個 serial 關鍵字告訴 Ansible 每次操做多少服務器. 若是它沒有被指定, Ansible 默認根據配置文件中 「forks」 限制指定的值進行併發操做. 可是對於零停機時間的更新, 你可能不但願一次操做多個主機. 若是你僅僅有少數的 web 服務器, 你可能但願設置 serial 爲 1, 在同一時間只執行一臺主機. 若是你有 100 臺, 你能夠設置 serial 爲 10, 同一時間執行 10 臺.

下面是更新 play 接下來的部分:

pre_tasks:
- name: disable nagios alerts for this host webserver service
  nagios: action=disable_alerts host={{ inventory_hostname }} services=webserver
  delegate_to: "{{ item }}"
  with_items: groups.monitoring

- name: disable the server in haproxy
  shell: echo "disable server myapplb/{{ inventory_hostname }}" | socat stdio /var/lib/haproxy/stats
  delegate_to: "{{ item }}"
  with_items: groups.lbservers

這個 pre_tasks 關鍵字僅僅是讓在 roles 調用前列出運行的 tasks. 這段時間將十分有用. 若是你看到這些 tasks 的名稱, 你會發現咱們禁用了 Nagios 的報警而且將當前更新的服務器從 HAProxy load balancing pool 中移除.

參數``delegate_to`` 和 with_items 一塊兒來使用, 由於 Ansible 循環每個 monitoring 服務器和 load balancer, 而且針對循環的值在 monitoring 或 load balancing 上操做(delegate 表明操做). 從編程方面來講, 外部的循環是 web 服務器列表, 內部的循環是 monitoring 服務器列表.

請注意 HAProxy 的步驟看起來有點複雜. 咱們使用它做爲例子是由於它是免費的, 但若是你有(例如)一個 F5 或 Netscaler 在你的基礎設施上(或者你有一個 AWS 彈性 IP 的設置?), 你可使用 Ansible 的模塊而不是直接和他們進行交互. 你也可能但願使用其餘的 monitoring 模塊來代替 nagios, 可是這僅僅是展現了在任務開始前的部分 – 把服務從監控中移除而且輪換它們.

下一步從新簡單的使正確的角色應用在 web 服務器上. 這將致使一些名爲 web 和 base-apache 的配置管理角色應用到 web 服務器上, 包含一個更新 web 應用程序自身代碼. 咱們不須要這樣作 – 咱們僅須要將其修改成純碎的更新 web 程序, 可是這是一個很好的例子關於如何經過 roles 來重用這些任務:

roles:
- common
- base-apache
- web

最後, 在 post_tasks 部分, 咱們反向的改變 Nagios 的配置而且將 web 服務器從新添加到 load balancing pool:

post_tasks:
- name: Enable the server in haproxy
  shell: echo "enable server myapplb/{{ inventory_hostname }}" | socat stdio /var/lib/haproxy/stats
  delegate_to: "{{ item }}"
  with_items: groups.lbservers

- name: re-enable nagios alerts
  nagios: action=enable_alerts host={{ inventory_hostname }} services=webserver
  delegate_to: "{{ item }}"
  with_items: groups.monitoring

再一次說明, 若是你在使用一個 Netscaler 或 F5 或 Elastic 的負載均衡器, 你僅僅須要替換爲適合的模塊對象.

管理其餘的負載均衡

在這個例子中, 咱們使用了簡單的 HAProxy 負載均衡到後端的 web 服務器. 它是很是容易配置和管理的. 正如咱們所提到的, Ansible 已經爲其餘的負載均衡器像 Citrix NetScaler, F5 BigIP, Amazon Elastic Load Balancers 等提供了內建的支持.閱讀更多信息 模塊相關

對於其餘的負載均衡器, 若是公開一個負載均衡時, 你可能須要向它們發送 shell 命令 (像上面咱們對 HAProxy 同樣), 或者調用一些 API. 你能夠越多更多關於 local actions 在這個 委託,滾動更新,本地動做章節中. 對於一些硬件的開發將更加有趣, 他們沒有一個核心模塊, 因此你可使用更好的模塊將他們封裝起來!

持續交付結束

如今你有一個自動化的方式來部署更新你的應用程序, 你將如何將他們綁定在一塊兒? 許多組織使用持續集成的工具像 Jenkins 或 Atlassian Bamboo 來完成開發, 測試, 發佈, 和部署這樣的流程步驟. 你也可使用這些工具像 Gerrit 來添加一個 code review 的步驟來審查提交的應用程序的自己或者 Ansible playbooks.

根據你的環境, 你可能會部署到一個測試環境, 在這個環境中運行一些集成測試, 而後自動部署到生產環境. 你能夠保持他們的簡單性僅按需來進行滾動升級到測試或者指定的生產環境中. 這些你都隨你決定.

與持續集成工具的結合, 你能夠經過 ansible-playbook 命令行工具很容易的觸發 playbook 的運行, 或者, 若是你使用 Ansible Towertower-cli 或者內置的 REST API. (這個 tower-cli 命令的 ‘joblaunch’ 將經過 REST API 遠程產生一個 job 這很是棒).

Ansible 對於如何組合多層應用程序在任務編排和持續交付給客戶的最終目標上給了你很好的主意. 你可使用滾動升級的思路來擴展一些應用程序之間的不一樣部分; 也許向前端 web 服務器添加後端應用服務, 例如, 使用 MongoDB 或 Riak 來替換 SQL 數據庫. Ansible 能夠給你在複雜的環境中輕鬆完成常見的自動化操做.

See also

lamp_haproxy example
The lamp_haproxy example discussed here.
Playbooks
An introduction to playbooks
Playbook Roles and Include Statements
An introduction to playbook roles
Variables
An introduction to Ansible variables
Ansible.com: Continuous Delivery
An introduction to Continuous Delivery with Ansible
相關文章
相關標籤/搜索