示例讲解

官网提供的 Haproxy+LAMP+Nagios 经典示例,也是目前国内最常用的技术架构,此案例访问地址为: https://github.com/ansible/ansible-examples/tree/master/lamp_haproxy 。下面将对该示例进行详细说明,内容覆盖前面涉及的几乎所有知识点,起到温故的作用,同时作为对 Ansible 的总结内容。

下面介绍 playbook 的基本信息。

  1. 目录结构

    示例 playbook 目录结构见图9-9。

    image 2023 12 08 21 42 18 868
    Figure 1. 图9-9 示例目录结构
  2. 设备环境说明

    两台 Web 主机、1台数据库主机、1台负载均衡器主机、1台监控主机,hosts 配置如下:

    hosts
    [webservers]
    web1
    web2
    [dbservers]
    db1
    [lbservers]
    lb1
    [monitoring]
    nagios
  3. palybook 入口文件 site.yml

    需要注意的是 base-apache 角色,由于 webservers 及 monitoring 都需要部署 Apache 环境,为提高复用性,将部署 Apache 独立成 base-apache 角色。

    Site.yml
    ---
    - hosts: all
      roles:
      - common
    - hosts: dbservers
      user: root
      roles:
      - db
    - hosts: webservers
      user: root
      roles:
      - base-apache
      - web
    - hosts: lbservers
      user: root
      roles:
      - haproxy
    - hosts: monitoring
      user: root
      roles:
      - base-apache
      - nagios
  4. 定义组变量

    下面定义 playbook 全局变量,变量作用域为所有主机。

    group_vars/all
    ---
    # Variables here are applicable to all host groups
    httpd_port: 80
    ntpserver: 192.168.1.2

    all 文件定义了匹配所有主机作用域的变量,一般为系统公共类基础配置,如 ntpserver 地址、sysctl 变量、iptables 配置等。

    下面为定义 webservers 组的变量,变量作用域为 webservers 组主机。

    group_vars/webservers
    ---
    # Variables for the web server configuration
    # Ethernet interface on which the web server should listen.
    # Defaults to the first interface. Change this to:
    #
    #  iface: eth1
    #
    # ...to override.
    #
    iface: '{{ ansible_default_ipv4.interface }}'
    # this is the repository that holds our sample webapp
    repository: https://github.com/bennojoy/mywebapp.git
    # this is the sha1sum of V5 of the test webapp.
    webapp_version: 351e47276cc66b018f4890a04709d4cc3d3edb0d

    webservers 文件定义了 webservers 组作用域的变量。本示例涉及 Apache 相关配置,其中 “iface: '{{ansible_default_ipv4.interface }}'” 引用了 Facts 获取的本地网卡接口名信息,另外定义了一个 GitHub 的 repository,方便下载 Web 测试文件,如内部搭建 git 版本控制环境,此处也可以修改成本地的服务地址。

    下面为定义 dbservers 组的变量,变量作用域为 dbservers 组主机。

    group_vars/dbservers
    ---
    # The variables file used by the playbooks in the dbservers group.
    #  These  don't  have  to  be  explicitly  imported  by  vars_files:  they  are
    autopopulated.
    mysqlservice: mysqld
    mysql_port: 3306
    dbuser: root
    dbname: foodb
    upassword: abc

    dbservers 文件定义了 dbservers 组作用域变量,本示例涉及 MySQL 数据库的基本应用信息。

    下面为定义 lbservers 组作用域变量文件,本示例主要涉及 haproxy 环境涉及的配置参数值。

    group_vars/lbservers
    ---
    # Variables for the HAproxy configuration
    # HAProxy supports "http" and "tcp". For SSL, SMTP, etc, use "tcp".
    mode: http
    # Port on which HAProxy should listen
    listenport: 8888
    # A name for the proxy daemon, this wil be the suffix in the logs.
    daemonname: myapplb
    # Balancing Algorithm. Available options:
    # roundrobin, source, leastconn, source, uri
    # (if persistance is required use, "source")
    balance: roundrobin
    # Ethernet interface on which the load balancer should listen
    # Defaults to the first interface. Change this to:
    #
    #  iface: eth1
    #
    # ...to override.
    #
    iface: '{{ ansible_default_ipv4.interface }}'
  5. playbook角色详解

    本示例划分了 6 个角色,包括 base-apache、common、db、haproxy、nagios、web,分别对应 6 个功能环境部署,根据不同业务场景的需求,可以随意加、减角色,如将 base-apache 更换成 nginx,然后在 site.yml 中引用。

    • common角色

      common 的主要功能是部署、配置系统基础服务,包括 yum源、安装 nagios插件、NTP服务、iptables、SELinux 等,任务(tasks)的定义如下:

      common/tasks/main.yml
      ---
      # This role contains common plays that will run on all nodes.
      - name: Create the repository for EPEL
            copy: src=epel.repo dest=/etc/yum.repos.d/epel.repo
      - name: Create the GPG key for EPEL
            copy: src=RPM-GPG-KEY-EPEL-6 dest=/etc/pki/rpm-gpg
      - name: install some useful nagios plugins
            yum: name={{ item }} state=present
            with_items:
          - nagios-nrpe
          - nagios-plugins-swap
          - nagios-plugins-users
          - nagios-plugins-procs
          - nagios-plugins-load
          - nagios-plugins-disk
      - name: Install ntp
            yum: name=ntp state=present
            tags: ntp
      - name: Configure ntp file
            template: src=ntp.conf.j2 dest=/etc/ntp.conf
            tags: ntp
            notify: restart ntp
      - name: Start the ntp service
            service: name=ntpd state=started enabled=true
            tags: ntp
      - name: insert iptables template
            template: src=iptables.j2 dest=/etc/sysconfig/iptables
            notify: restart iptables
      - name: test to see if selinux is running
            command: getenforce
            register: sestatus
            changed_when: false

      上述代码定义了两个远程文件复制 copy,其中 src(源文件)的默认位置在 roles/common/files,使用 with_item 标签实现循环安装 nagios 插件,同时安装 ntp 服务,引用模块文件 roles/common/templatesntp.conf.j2,且同步到目标主机 /etc/ntp.conf 位置。配置系统 iptables,引用 roles/common/templates/iptables.j2 模板,“notify:restart iptables”,状态或模板发生变化时将通知处理程序(handlers)来处理。“command: getenforce” 运行 getenforce 来检测 selinux 是否在运行状态,“changed_when:false” 作用为不记录命令运行结果的 changed 状态,即 changed 为 False。

      下面定义 common 角色的处理程序。

      common/handlers/main.yml
      ---
      # Handlers for common notifications
      - name: restart ntp
        service: name=ntpd state=restarted
      - name: restart iptables
        service: name=iptables state=restarted

      上述代码定义了两个处理程序,功能分别为重启 ntp、iptables 服务,其中 “name: restart ntp” 与任务(tasks)定义中的 “notify: restart ntp” 是一一对应的,“name: restart iptables” 同理。

      下面定义了 common 角色 iptables 的配置模板:

      common/templates/iptables.j2
      {% if (inventory_hostname in groups['webservers']) or (inventory_hostname in
      groups['monitoring']) %}
      -A INPUT -p tcp  --dport 80-j ACCEPT
      {% endif %}
      … …
      {% for host in groups['monitoring'] %}
      -A INPUT -p tcp -s {{ hostvars[host].ansible_default_ipv4.address }} --dport
      5666-j ACCEPT
      {% endfor %}

      "inventory_hostname" 作为存放在 Ansible 的 inventory 文件中的主机名或 IP,好处是可以不依靠 Facts 的主机名参数 ansible_hostname 或其他原因,一般情况下 inventory_hostname 等于 ansible_hostname,但有时候我们习惯在 Ansible 的 inventory 中使用 IP 地址,而 ansible_hostname 则返回主机名。模板使用了 jinja2 的语法,本例 if…​endif 语句判断当前的 inventory_hostname 是否在 webservers 及 monitoring 组中(定义具体在 hosts 文件中),条件成立则添加 80 端口访问权限(-A INPUT -p tcp --dport 80-j ACCEPT)。For…​endfor 语句实现了循环开通允许 monitoring 组主机访问 5666 端口,使用 hostvars[host] 得到主机对象,可以获得主机的 Facts 信息,如 hostvars[host].ansible_default_ipv4.address 获取主机 IP。

    • haproxy角色

      haproxy 角色主要实现了 haproxy 平台的部署、配置功能,任务(tasks)的定义:

      haproxy/tasks
      ---
      # This role installs HAProxy and configures it.
      - name: Download and install haproxy and socat
            yum: name={{ item }} state=present
            with_items:
            - haproxy
            - socat
      - name: Configure the haproxy cnf file with hosts
            template: src=haproxy.cfg.j2 dest=/etc/haproxy/haproxy.cfg
            notify: restart haproxy

      任务(tasks)定义了两个功能,一为安装,二为同步配置文件,安装使用了 yum 模块,循环安装 haproxy、socat 两个工具,同时根据配置参数渲染 roles/haproxy/templates/haproxy.cfg.j2 模板文件,完成后同步到目标主机 /etc/haproxy/haproxy.cfg 位置,状态发生变化时重启 haproxy 服务,使之生效。

      下面定义了 haproxy 角色 haproxy.cfg 的配置模板:

      haproxy/templates/haproxy.cfg.j2
      … …
      backend app
              {% for host in groups['lbservers'] %}
              listen {{ daemonname }} {{ hostvars[host]['ansible_' + iface].ipv4.
      address }}:{{ listenport }}
              {% endfor %}
              balance     {{ balance }}
              {% for host in groups['webservers'] %}
              server {{ hostvars[host].ansible_hostname }} {{ hostvars[host]
      ['ansible_' + iface].ipv4.address }}:{{ httpd_port }}
              {% endfor %}

      {{ hostvars[host]['ansible_' + iface].ipv4.address }} 实现了获取网卡名变量 iface(group_vars/lbservers 中定义)的 IPv4 IP 地址。

    • web角色

      web 角色主要实现了 php、php-mysql、git 平台部署及 SELinux 的配置功能,任务(tasks)的定义如下:

      web/tasks/main.yml
      ---
      # httpd is handled by the base-apache role upstream
      - name: Install php and git
        yum: name={{ item }} state=present
        with_items:
        - php
        - php-mysql
        - git
      - name: Configure SELinux to allow httpd to connect to remote database
        seboolean: name=httpd_can_network_connect_db state=true persistent=yes
        when: sestatus.rc != 0
      - name: Copy the code from repository
        git: repo={{ repository }} version={{ webapp_version }} dest=/var/www/html/

      判断 sestatus 变量( roles/common/tasks/main.yml 中定义)返回的 rc(运行代码)不等于 0(失败)则配置 selinux httpd 访问远程数据库的权限,使用的是 Ansible 的 seboolean 模块,该条语句等价于命令行 “setsebool httpd_can_network_connect_db 1”,其中 “persistent=yes” 表示开机自启动。

    • nagios角色

      nagios 角色主要实现了 nagios 监控平台的部署,重点介绍任务(tasks)的定义:

      nagios/tasks/main.yml
      … …
      - name: create the nagios object files
        template: src={{ item + ".j2" }}
                  dest=/etc/nagios/ansible-managed/{{ item }}
        with_items:
          - webservers.cfg
          - dbservers.cfg
          - lbservers.cfg
        notify: restart nagios

      template 分发多个模板文件时可以使用 with_items 来循环同步,变量与字符使用 “+” 号连接(具体见 jinja2 语法)。理解以上 4 个角色的定义后,再理解 ansible-examples 其他 playbook 的内容已经没有太大的困难,本书将不一一说明。

参考提示