什么是持续交付
持续交付(CD)指频繁向软件应用交付更新。更频繁地更新,意味着无需等待固定周期,也能让组织更熟练地响应变化。
一些 Ansible 用户每小时甚至更频繁地发布更新,有时每次代码修改获批就部署。为此,需要能够快速、零停机地应用更新的工具。
本文以完整示例 playbook lamp_haproxy 为模板,说明如何实现。示例使用角色、模板、组变量,并包含可以对 Web 应用栈执行零停机滚动升级的编排 playbook。
这些 playbook 在一组 CentOS 服务器上部署 Apache、PHP、MySQL、Nagios 与 HAProxy。
本文不介绍如何运行示例,相关操作见 GitHub 项目附带 README;这里逐部分说明其作用。
站点部署
site.yml 是站点级部署 playbook,既可用于首次部署,也可向所有服务器推送更新:
---
# This playbook deploys the whole application stack in this site.
# Apply common configuration to all hosts
- hosts: all
roles:
- common
# Configure and deploy database servers.
- hosts: dbservers
roles:
- db
# Configure and deploy the web servers. Note that we include two roles
# here, the 'base-apache' role which simply sets up Apache, and 'web'
# which includes our example web application.
- hosts: webservers
roles:
- base-apache
- web
# Configure and deploy the load balancer(s).
- hosts: lbservers
roles:
- haproxy
# Configure and deploy the Nagios monitoring node(s).
- hosts: monitoring
roles:
- base-apache
- nagios
注意:不熟悉 playbook、play 等术语时,先阅读 使用 playbook。
这里有五个 play。第一个对全部主机应用 common,处理 yum 仓库、防火墙等所有服务器共用配置。
后四个针对特定主机组应用角色。除了监控、数据库与 Web 应用,base-apache 安装配置基础 Apache,供 Web 应用与 Nagios 主机共同使用。
可复用内容:角色
角色把任务、处理器、模板和文件组织成可复用组件。
示例有 common、base-apache、db、haproxy、nagios、web 六个角色。如何组织取决于应用,通常会有一个或多个应用于所有系统的公共角色,再有负责具体应用部分的角色。
角色可以有变量、依赖,也可以通过参数改变行为,详情见 角色。
配置:组变量
组变量应用于一组服务器,可在模板和 playbook 中定制行为,提供容易修改的参数。它们保存在与 inventory 同级的 group_vars 目录。
group_vars/all 中的变量适用于 inventory 的全部机器:
---
httpd_port: 80
ntpserver: 192.0.2.23
这是 YAML 文件,也可使用列表、字典构造复杂结构。本例只有 Web 服务器端口和时间同步所用 NTP 服务器两个变量。
group_vars/dbservers 只应用于数据库主机组:
---
mysqlservice: mysqld
mysql_port: 3306
dbuser: root
dbname: foodb
upassword: usersecret
示例同样为 webservers 与 lbservers 提供组变量。
变量可在不同位置使用,例如 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
也可用于模板,例如 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
两处都使用 {{ }} 替换变量,花括号内采用 Jinja2,可以执行操作和过滤器。模板还支持 for 与 if,例如 iptables 模板:
{% if inventory_hostname in groups['dbservers'] %}
-A INPUT -p tcp --dport 3306 -j ACCEPT
{% endif %}
这里判断当前主机 inventory_hostname 是否属于 dbservers。如果属于,就添加允许 TCP 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 地址,向当前机器添加允许规则,让 Nagios 能监控这些主机。
更多能力见 Jinja2 文档;Ansible 变量说明见 使用变量。
滚动升级
站点已有 Web 服务器、负载均衡和监控后,如何更新?这就需要 Ansible 编排。某些工具把基本执行顺序或批量发命令称为编排,而 Ansible 强调协调各机器,如同指挥乐队,并有相应执行引擎。
它可协调多层应用,实现复杂的零停机滚动升级。示例通过单独的 rolling_update.yml 完成。
该 playbook 有两个 play。第一个很简单:
- hosts: monitoring
tasks: []
为什么没有任务?Ansible 操作服务器前会收集 facts,例如网络信息和系统版本。升级前需要知道所有监控服务器的信息,因此这个空 play 强制执行事实收集。这是一个实用技巧。
第二个是更新 play,开头如下:
- hosts: webservers
user: root
serial: 1
它操作 webservers。serial 指定一次处理多少台服务器。省略时,会按配置中的默认 forks 上限并行执行。零停机升级通常不希望同时操作太多主机。只有几台时可设为 1,100 台时也许可设为 10。
接下来的部分:
pre_tasks:
- name: disable nagios alerts for this host webserver service
nagios:
action: disable_alerts
host: "{{ inventory_hostname }}"
services: webserver
delegate_to: "{{ item }}"
loop: "{{ groups.monitoring }}"
- name: disable the server in haproxy
shell: echo "disable server myapplb/{{ inventory_hostname }}" | socat stdio /var/lib/haproxy/stats
delegate_to: "{{ item }}"
loop: "{{ groups.lbservers }}"
注意:serial 让 play 分批执行,每批被视为只含部分主机的完整 play。这会影响失败行为:若一批全部主机失败,play 失败,整个运行也失败。搭配 max_fail_percentage 时要考虑这一点。
pre_tasks 在角色之前执行。这里先禁用 Nagios 对当前 Web 服务器的告警,再将它移出 HAProxy 负载均衡池。
delegate_to 与 loop 配合,遍历各监控服务器和负载均衡器,代表当前 Web 服务器在这些远程机器上执行操作。用编程术语说,外层循环是 Web 服务器,内层是监控服务器。
HAProxy 步骤看起来较复杂。示例选它是因为免费可用;已有 F5、NetScaler 或 AWS Elastic IP 等基础设施时,可改用相应 Ansible 模块。监控也可以换别的模块。前置任务的核心目的始终是暂时移出监控和流量轮转。
随后重新应用服务器角色,包括配置更新和 Web 应用代码更新:
roles:
- common
- base-apache
- web
并非必须如此,也可以只更新应用;这里展示的是角色如何复用任务。
最后,post_tasks 撤销临时监控变更,并将服务器重新加入负载均衡池:
post_tasks:
- name: Enable the server in haproxy
shell: echo "enable server myapplb/{{ inventory_hostname }}" | socat stdio /var/lib/haproxy/stats
delegate_to: "{{ item }}"
loop: "{{ groups.lbservers }}"
- name: re-enable nagios alerts
nagios:
action: enable_alerts
host: "{{ inventory_hostname }}"
services: webserver
delegate_to: "{{ item }}"
loop: "{{ groups.monitoring }}"
使用 NetScaler、F5 或 Elastic Load Balancer 时,替换为相应模块即可。
管理其他负载均衡器
示例的 HAProxy 易于配置和管理。Ansible 也支持 Citrix NetScaler、F5 BigIP、Amazon Elastic Load Balancer 等。
其他设备可能需要发送 shell 命令,或者调用它们提供的 API。使用 API 的 Ansible 模块可考虑通过 local_action 运行,详见 控制任务执行位置:委派与本地操作。若为尚无模块的硬件实现了有用功能,也可以贡献给社区。
端到端持续交付
自动部署已具备后,可以用 Jenkins 或 Atlassian Bamboo 等持续集成工具连接开发、测试、发布与部署。也可以用 Gerrit,为应用代码、Ansible playbook 或两者加入代码审查。
可以持续部署到测试环境,运行集成测试后自动发布生产;也可以简单地按需滚动部署到测试或生产,取决于环境与需求。
集成 CI 时,可通过 ansible-playbook 命令触发。使用 AWX 时,也可使用 tower-cli 或内置 REST API;原文指出 tower-cli 的 joblaunch 会通过 REST API 创建远程作业。
这展示了如何组织多层应用并编排操作,最终持续向用户交付。滚动升级也能扩展到其他层,例如同时处理前端和应用服务器,或把 SQL 换成 NoSQL。Ansible 提供管理复杂环境、自动执行常见操作的能力。
另请参阅
原文:Playbook Example: Continuous Delivery and Rolling Upgrades。作者/维护者:Ansible 文档贡献者。本文为原文的中文译文;代码保留原文内容。











暂无评论内容