Ansible Playbook 示例:持续交付与滚动升级

什么是持续交付

持续交付(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 文档贡献者。本文为原文的中文译文;代码保留原文内容。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容