Ansible第二篇:ansible-playbook

一、Playbook语法

Ansible-playbook采用YAML语法编写。

示例:
[root@LOCALHOST ~]# cat yaml/httpd.yaml

---
- hosts: control-node    # 将要执行任务的主机,已经在hosts文件中定义好了,可是单个主机或主机组
  remote_user: root      # 在目标主机上执行任务时的用户身份
  vars:
    - pkg: httpd
  tasks:
    - name: "install httpd package."
      yum: name={{ pkg }}  state=installed
    - name: "copy httpd configure file to remote host."
      copy: src=/root/conf/httpd.conf dest=/etc/httpd/conf/httpd.conf
      notify: restart httpd     # 当这个任务执行状态发生改变时,触发handlers执行.
    - name: "boot httpd service."
      service: name=httpd state=started
  handlers:      # handlers与tasks是同一级别
    - name: restart httpd
      service: name=httpd state=restarted

连续的项目(即列表)用 -减号来表示,key/value(字典)用冒号:分隔。

playbook语法有如下特性:

  1. --- (三个减号)开始,必须顶行写;
  2. 次行开始写Playbook的内容,但是一般要求写明该playbook的功能;
  3. 严格缩进,并且不能用Tab键缩进;
  4. 缩进级别必须是一致的,同样的缩进代表同样的级别,程序判别配置的级别是通过缩进结合换行来实现的;
  5. K/V的值可同行写,也可换行写。同行使用 :分隔,换行写需要以 - 分隔;

执行playbook:

[root@LOCALHOST ~]# ansible-playbook yaml/httpd.yaml 

PLAY [control-node] ********************************************************************************************************

TASK [Gathering Facts] *****************************************************************************************************
ok: [openstack-control2]
ok: [openstack-control1]

TASK [install httpd package.] **********************************************************************************************
changed: [openstack-control2]
changed: [openstack-control1]

TASK [copy httpd configure file to remote host.] ***************************************************************************
changed: [openstack-control1]
changed: [openstack-control2]

TASK [boot httpd service.] *************************************************************************************************
changed: [openstack-control2]
changed: [openstack-control1]

PLAY RECAP *****************************************************************************************************************
openstack-control1         : ok=4    changed=3    unreachable=0    failed=0   
openstack-control2         : ok=4    changed=3    unreachable=0    failed=0 

二、Playbook中的变量

2.1 yaml文件中定义变量

[root@LOCALHOST ~]# cat yaml/vars.yaml 
---
- hosts: compute-node
  remote_user: root
  vars:
    pkg: httpd        # 定义变量
  tasks:
    - name: install httpd service
      yum: name={{ pkg }} state=installed     # 引用变量

playbook中用vars关键字声明变量,变量定义 变量名: 变量值
变量引用 :{{ 变量名 }}

2.2 引用外部变量

在命令行使用--extra-vars参数赋值变量;

[root@LOCALHOST ~]# cat yaml/extra_vars.yaml 
---
- hosts: control-node
  remote_user: root
  tasks:
    - name: create a file
      file:
        path: /tmp/{{ filename }}    # 引用外部变量
        mode: 0644
        state: touch

命令行使用--extra_vars传入变量:

[root@LOCALHOST ~]# ansible-playbook yaml/extra_vars.yaml --extra-vars "filename=temp.txt"

--extra_vars "变量名=变量值"

2.3 资产清单(inventory)中定义的变量

也就是在/etc/ansible/hosts文件中定义的变量;

[root@LOCALHOST ~]# cat /etc/ansible/hosts
[load-node]
openstack-load1 
openstack-load2

[compute-node]
openstack-compute1 ansible_ssh_host=10.0.1.10 ansible_ssh_port=2002 ansible_ssh_user=stanley ansible_ssh_pass=etyfhzmweadf
openstack-compute2

[control-node]
openstack-control1 filename=control1.txt    # 主机变量
openstack-control2 filename=control2.txt

[openstack:children]
load-node
compute-node
control-node

[openstack:vars]
issue="Hello, World"    # 组变量

注意:组变量定义时,不要落下关键字vars[组名:vars]
在playbook中引用{{ 变量名 }}即可。

ansible内置了一些固定的主机变量名,在inventory中定义其值,如下:

ansible_ssh_host
      将要连接的远程主机名.与你想要设定的主机的别名不同的话,可通过此变量设置.

ansible_ssh_port
      ssh端口号.如果不是默认的端口号,通过此变量设置.

ansible_ssh_user
      默认的 ssh 用户名

ansible_ssh_pass
      ssh 密码(这种方式并不安全,我们强烈建议使用 --ask-pass 或 SSH 密钥)

ansible_sudo_pass
      sudo 密码(这种方式并不安全,我们强烈建议使用 --ask-sudo-pass)

ansible_sudo_exe (new in version 1.8)
      sudo 命令路径(适用于1.8及以上版本)

ansible_connection
      与主机的连接类型.比如:local, ssh 或者 paramiko. Ansible 1.2 以前默认使用 paramiko.1.2 以后默认使用 'smart','smart' 方式会根据是否支持 ControlPersist, 来判断'ssh' 方式是否可行.

ansible_ssh_private_key_file
      ssh 使用的私钥文件.适用于有多个密钥,而你不想使用 SSH 代理的情况.

ansible_shell_type
      目标系统的shell类型.默认情况下,命令的执行使用 'sh' 语法,可设置为 'csh' 或 'fish'.

ansible_python_interpreter
      目标主机的 python 路径.适用于的情况: 系统中有多个 Python, 或者命令路径不是"/usr/bin/python",比如  \*BSD, 或者 /usr/bin/python
      不是 2.X 版本的 Python.我们不使用 "/usr/bin/env" 机制,因为这要求远程用户的路径设置正确,且要求 "python" 可执行程序名不可为 python以外的名字(实际有可能名为python26).

      与 ansible_python_interpreter 的工作方式相同,可设定如 ruby 或 perl 的路径....

参考Ansible中文权威指南

2.4 直接使用facts变量

使用命令ansible all -m setup获取到的主机信息,其中的KEY都可以被当作变量引用:

[root@LOCALHOST ~]# cat yaml/facts_vars.yaml 
---
- hosts: compute-node
  remote_user: root
  tasks:
    - name: write fqdn into fqdn.log
      shell: "echo {{ ansible_fqdn }} {{ ansible_eth1.ipv4.address }} > /tmp/fqdn.log"

上述代码中{{ ansible_fqdn }}就是直接引用的facts变量;
{{ ansible_eth1.ipv4.address }} 引用的值就相当于下面这样。

{ 
    "ansible_eth1": {
        "ipv4": {
            "address":10.0.1.10,
        },
    },
}

最终{{ ansible_eth1.ipv4.address }}=10.0.1.10

2.5 注册变量

在playbook中用register关键字定义一个变量,这个变量的值就是当前任务执行的输出结果;

[root@LOCALHOST ~]# cat yaml/reg_vars.yaml 
---
- hosts: load-node
  remote_user: root
  tasks:
    - name: show date
      shell: "/bin/date"
      register: date        # 注册一个变量
    - name: Record time log
      shell: "echo {{ date.stdout }} > /tmp/date.log"

引用注册变量要用 {{ date.stdout }}表示标准输出。

[root@openstack-load1 ~]# cat /tmp/date.log 
2018年 03月 29日 星期四 15:52:01 CST

如果直接{{ date }}这样引用,则文件中写入的是如下内容:

{stderr_lines: [], uchanged: True, uend: u2018-03-29 15:49:52.609894, failed: False, ustdout: u2018\u5e74 03\u6708 29\u65e5 \u661f\u671f\u56db 15:49:52 CST, ucmd: u/bin/date, urc: 0, ustart: u2018-03-29 15:49:52.602918, ustderr: u, udelta: u0:00:00.006976, stdout_lines: [u2018\u5e74 03\u6708 29\u65e5 \u661f\u671f\u56db 15:49:52 CST]}

2.6 变量优先级

上述这些变量定义的方法,它们的优先级如下:

  1. 在命令行中定义的变量(即用-e--extra-vars定义的变量);
  2. 在Inventory中定义的连接变量(比如:ansible_ssh_user);
  3. 大多数的其他变量(命令行转换、play中的变量、included的变量、role中的变量等);
  4. 在Inventory中定义的其他变量;
  5. Facts变量;
  6. “Role”默认变量,这个是默认的值,很容易丧失优先权。

三、Playbook中的方法

3.1 handlers

在需要被监控的任务(tasks)中定义一个notify,只有当这个任务被执行时,才会触发notify对应的handlers去执行相应操作。

[root@LOCALHOST ~]# cat yaml/httpd.yaml 
---
- hosts: control-node
  remote_user: root
  vars:
    - pkg: httpd
  tasks:
    - name: "install httpd package."
      yum: name={{ pkg }}  state=installed
    - name: "copy httpd configure file to remote host."
      copy: src=/root/conf/httpd.conf dest=/etc/httpd/conf/httpd.conf
      notify: restart httpd
    - name: "boot httpd service."
      service: name=httpd state=started
  handlers:
    - name: restart httpd
      service: name=httpd state=restarted

在使用handlers的过程中,有以下几点需要注意:

  1. handlers只有在其所在的任务被执行时,都会被运行;
  2. handlers只会在Play的末尾运行一次;如果想在一个Playbook的中间运行handlers,则需要使用meta模块来实现,例如:- meta: flush_handlers
  3. 如果一个Play在运行到调用handlers的语句之前失败了,那么这个handlers将不会被执行。我们可以使用mega模块的--force-handlers选项来强制执行handlers,即使在handlers所在Play中途运行失败也能执行。

3.2 tags

tags用于让用户选择运行playbook中的部分代码。ansible具有幂等性,因此会自动跳过没有变化的部分,即便如此,有些代码为测试其确实没有发生变化的时间依然会非常地长。此时,如果确信其没有变化,就可以通过tags跳过此些代码片断。

ansible的标签(Tags)功能可以给角色(Roles)、文件、单独的任务,甚至整个Playbook打上标签,然后利用这些标签来指定要运行Playbook中的个别任务,或不执行指定的任务。

[root@LOCALHOST ~]# cat yaml/httpd.yaml 
---
- hosts: control-node
  remote_user: root
  vars:
    - pkg: httpd
  tasks:
    - name: "install httpd package."
      yum: name={{ pkg }}  state=installed
    - name: "copy httpd configure file to remote host."
      copy: src=/root/conf/httpd.conf dest=/etc/httpd/conf/httpd.conf
      notify: restart httpd
    - name: "start httpd service."
      tags:
        - start_httpd     # 给“start httpd service”这个任务打个标签
      service: name=httpd state=started
  handlers:
    - name: restart httpd
      service: name=httpd state=restarted

在命令行中调用:

[root@LOCALHOST ~]# ansible-playbook yaml/httpd.yaml --tags start_httpd

PLAY [control-node] ********************************************************************************************************

TASK [Gathering Facts] *****************************************************************************************************
ok: [openstack-control1]
ok: [openstack-control2]

TASK [start httpd service.] ************************************************************************************************
ok: [openstack-control1]
ok: [openstack-control2]

PLAY RECAP *****************************************************************************************************************
openstack-control1         : ok=2    changed=0    unreachable=0    failed=0   
openstack-control2         : ok=2    changed=0    unreachable=0    failed=0 

四、Playbook的流程控制

4.1 when 条件判断

当关键字when后面的条件满足时(也就是通过运算得到的结果为true时),才会执行当前任务。

示例如下(yaml/when.yaml):

---
- hosts: load-node,img
  remote_user: root
  tasks:
  - name: "touch flag file"
    command: "touch /tmp/this_is_{{ ansible_distribution }}_system" 
    when: (ansible_distribution == "CentOS" and ansible_distribution_major_version == '6') or
          (ansible_distribution == "Debian" and ansible_distribution_major_version == '7')

上述代码中,ansible_distributionansible_distribution_major_version都是是Facts变量,分别表示Linux发行版和版本号

执行结果如下图:

image.png

4.2 循环语句

Playbook中的循环类型,如下表:

循环类型 关键字
标准循环 with_items
遍历字典 with_dict
遍历目录文件 with_fileglob
遍历文件列表的内容 with_file
嵌套循环 with_nested
并行遍历列表 with_together
遍历列表和索引 with_indexed_items
重试循环 until
查找第一个匹配文件 with_first_found
随机选择 with_random_choice
在序列中循环 with_sequence

注意:列表中前三个为常用的循环语句。

4.2.1 标准循环 with_items

示例1(with_items_1.yaml):

---
- name: when and with_items
  hosts: load-node
  remote_user: root
  gather_facts: false            
  tasks:
  - name: Create thress groups
    group: name=testgroup6 state=present
    ignore_errors: yes
    register: excu_result
  - name: Append excu_result to tmp.txt
    shell: "echo {{ excu_result }} > /tmp/tmp.txt"
  - name: Create some users
    user: name={{ item }} group=testgroup6 state=present
    when: excu_result|success
    with_items:
      - testuser1
      - testuser2
      - testuser3

释义:

  • gather_facts: false表示运行此playbook时不收集目标主机的系统信息。因为默认此项是开启的,每次运行playbook都会收集主机facts,这会影响playbook的运行速度。将gather_facts设为false即可关闭。

  • when:excu_result|success的意思为当变量excu_result执行结果为成功状态,则执行当前的任务。其中success为Ansible内部过滤器方法,返回True代表命令运行成功。还有excu_result|failed表示excu_result执行结果为失败状态;excu_result|skipped表示excu_result执行被跳过。

  • with_items的值还可以写成[testuser1, testuser2, testuser3]

示例2:
添加多个用户,并将用户加入不同的组内:

- name: add several users
  user: name={{ item.name }} state=present groups={{ item.groups }}
  with_items:
    - { name: 'testuser1', groups: 'wheel' }
    - { name: 'testuser2', groups: 'root' }

4.2.2 遍历字典 with_dict

输出用户的姓名和电话:

---
- hosts: load-node
  remote_user: root
  tasks:
  - name: print phone records
    debug: msg="User {{ item.key }} is {{ item.value.name }} ({{ item.value.phone }})"
    with_dict: {'alice':{'name':'Alice Appleworth', 'phone':'13243252136'}, 'bob':{'name':'Bob Banarama', 'phone': '18766761211'}}

注意:Playbook中对字典的循环,与python中对字典循环类似,取值方法也一样。

4.2.3 遍历目录文件 with_fileglob

遍历/root/sh/目录下的所有文件,并将其拷贝至目标主机。

[root@LOCALHOST ~]# cat yaml/with_fileglob.yaml 
---
- hosts: DH-TEST
  remote_user: root
  gather_facts: false
  tasks:
  - name: create a directory
    file: path=/root/script state=directory
    ignore_errors: yes
    register: result
  - name: copy some scripts
    copy: src={{ item }} dest=/root/script owner=root mode=600
    when: result|success
    with_fileglob:
      - /root/sh/*

注意:with_fileglob匹配单个目录中的所有文件,非递归匹配模式。
作者: Jeson老师
链接:http://www.imooc.com/article/22753

执行结果如下图:

image.png

4.2.4 其它循环

其它循环请参考 Jeson老师的手记

五、Playbook任务计时插件

Github地址: https://github.com/jlafon/ansible-profile

安装这个插件后会显示 ansible-playbook 执行每一个任务所花费的时间。

这个插件安装很简单,只需要简单的三个命令即可完成安装。在你的 playbook 文件的目录下创建一个目录,目录名 callback_plugins 然后将下载的 profile_tasks.py 文件放到该目录下即可。

# mkdir callback_plugins
# cd callback_plugins
# wget https://raw.githubusercontent.com/jlafon/ansible-profile/master/callback_plugins/profile_tasks.py
image.png

六、Playbook异常处理

6.1 ignore_errors

在有些情况下,一些必须运行的命令或脚本会报一些错误,而这些错误并不一定真的说明有问题,但是经常会给接下来要运行的任务造成困扰,甚至直接导致playbook运行中断。

这时候,我们可以在相关任务中添加ignore_errors: true来屏蔽当前任务的报错信息。ansible也将视该任务运行成功,不再报错,这样就不会对接下来要运行的任务造成额外困扰。但是要注意的是,我们不应过度依赖ignore_errors,因为它会隐藏所有的报错信息,而应该把精力集中在寻找报错的原因上面,这样才能从根本上解决问题。

例如:

[root@LOCALHOST ~]# cat yaml/httpd.yaml 
---
- hosts: load-node
  remote_user: root
  vars:
    - pkg: httpd
  tasks:
    - name: "install httpd package."
      yum: name={{ pkg }}  state=installed
    - name: "copy httpd configure file to remote host."
      copy: src=/root/config/httpd.conf dest=/etc/httpd/conf/httpd.conf
      notify: restart httpd
      ignore_errors: true         # 忽略错误
    - name: "start httpd service."
      tags:
        - start_httpd
      service: name=httpd state=started
  handlers:
    - name: restart httpd
      service: name=httpd state=restarted

上述代码中,任务"copy httpd configure file to remote host."中的src是一个不存在的路径,所以此任务运行一定会出错。这个playbook运行结果如下图:

image.png

从运行结果图中可以看到有两个...ignoring,表示忽略了这些错误,并继续执行下面的任务。

6.2 failed_when

当满足一定的条件时,主动抛出错误。

[root@LOCALHOST ~]# cat yaml/failed_when.yaml 
---
- hosts: DH-TEST
  remote_user: root
  gather_facts: false
  tasks:
  - name: get process
    shell: ps aux | wc -l 
    register: process_count
    failed_when: process_count > 3
  - name: touch a file
    file: path=/tmp/test3.txt state=touch owner=root mode=0700

failed_when: process_count > 3当进程数大于3时主动抛出错误,后续任务就不会执行了。如果不满足条件,则不会抛出错误。

6.3 changed_when

[root@LOCALHOST ~]# cat yaml/changed_when.yaml 
---
- hosts: DH-TEST
  remote_user: root
  gather_facts: false
  tasks:
  - name: touch a file
    file: path=/tmp/changed_test state=touch

上述Playbook执行结果如下图:

image.png

如果想要关闭状态改变的提示,则可以添加changed_when: false,如下:

[root@LOCALHOST ~]# cat yaml/changed_when.yaml 
---
- hosts: DH-TEST
  remote_user: root
  gather_facts: false
  tasks:
  - name: touch a file
    file: path=/tmp/changed_testi2 state=touch
    changed_when: false       # 关闭状态改变提示

执行结果如下图:

image.png

七、Roles

Roles是一种利用在大型Playbook中的剧本配置模式,它有着自己特定的结构。用于层次性、结构化地组织playbook。roles能够根据层次型结构自动装载变量文件、tasks以及handlers等。要使用roles只需要在playbook中使用include指令即可。简单来讲,roles就是通过分别将变量、文件、任务、模板及处理器放置于单独的目录中,并可以便捷地include它们的一种机制。角色一般用于基于主机构建服务的场景中,但也可以是用于构建守护进程等场景中。

剧本roles设计思路:将公共任务、资源、变量等对象尽可能独立。

一个roles的案例如下所示:

site.yml            # 主入口文件
webservers.yml      # webserver类型服务所用的剧本
dbservers.yml       # 数据库类型的服务所用的剧本
files/              # 存放通用的将要被上传的文件
templates/          # 存放通用的模板文件
roles/              # roles目录名称是固定的
   common/          # 此目录下的各个组件是所有角色共用的
     tasks/         # 存放通用的任务文件
     handlers/      # 存放通用的处理器文件
     vars/          # 存放通用的变量文件 
     meta/          # 存放通用的角色依赖文件
   webservers/      # 存放webserver类型的服务的各个组件  
     files/         # 存放webserver角色需要的上传文件
     templates/     # 存放webserver角色需要的模板文件
     tasks/
     handlers/
     vars/
     meta/

而在playbook中,可以这样使用roles:

---
- hosts: webservers
  roles:
     - common
     - webservers

也可以向roles传递参数,例如:

---
- hosts: webservers
  roles:
    - common
    - { role: foo_app_instance, dir: '/opt/a',  port: 5000 }
    - { role: foo_app_instance, dir: '/opt/b',  port: 5001 }

甚至也可以条件式地使用roles,例如:

---

- hosts: webservers
  roles:
    - { role: some_role, when: "ansible_os_family == 'RedHat'"

7.1 创建role的步骤

(1) 创建以roles命名的目录;

(2) 在roles目录中分别创建以各角色名称命名的目录,如webservers等;

(3) 在每个角色命名的目录中分别创建files、handlers、meta、tasks、templates和vars目录;用不到的目录可以创建为空目录,也可以不创建;

(4) 在roles目录的同级目录下创建一个yaml文件,如:site.yml ,在此文件中调用各角色;

7.2 role内各目录中可用的文件

tasks目录:至少应该包含一个名为main.yml的文件,其定义了此角色的任务列表;此文件可以使用include包含其它的位于此目录中的task文件;

files目录:存放由copy或script等模块调用的文件;

templates目录:template模块会自动在此目录中寻找Jinja2模板文件;

handlers目录:此目录中应当包含一个main.yml文件,用于定义此角色用到的各handler;在handler中使用include包含的其它的handler文件也应该位于此目录中;

vars目录:应当包含一个main.yml文件,用于定义此角色用到的变量;

meta目录:应当包含一个main.yml文件,用于定义此角色的特殊设定及其依赖关系;ansible 1.3及其以后的版本才支持;

default目录:为当前角色设定默认变量时使用此目录;应当包含一个main.yml文件;

7.3 简单部署LAMP的Roles案例

先看一下这个roles的目录结构:

[root@LOCALHOST ansible-examples-master]# tree [root@LOCALHOST ansible-examples-master]# tree lamp_simple
lamp_simple
├── group_vars
│   ├── all
│   └── dbservers
├── hosts
├── LICENSE.md
├── README.md
├── roles
│   ├── common
│   │   ├── handlers
│   │   │   └── main.yml
│   │   ├── tasks
│   │   │   └── main.yml
│   │   └── templates
│   │       └── ntp.conf.j2
│   ├── db
│   │   ├── handlers
│   │   │   └── main.yml
│   │   ├── tasks
│   │   │   └── main.yml
│   │   └── templates
│   │       └── my.cnf.j2
│   └── web
│       ├── handlers
│       │   └── main.yml
│       ├── tasks
│       │   ├── copy_code.yml
│       │   ├── install_httpd.yml
│       │   └── main.yml
│       └── templates
│           └── index.php.j2
└── site.yml

14 directories, 17 files

查看各个playbook的内容:

  • 查看主机清单文件
    # cat lamp_simple/hosts
[webservers]
web3

[dbservers]
web2
  • 查看主入口文件
    # cat lamp_simeple/site.yml
---
# This playbook deploys the whole application stack in this site.

- name: apply common configuration to all nodes
  hosts: all
  remote_user: root

  roles:
    - common

- name: configure and deploy the webservers and application code
  hosts: webservers
  remote_user: root

  roles:
    - web

- name: deploy MySQL and configure the databases
  hosts: dbservers
  remote_user: root

  roles:
    - db
  • 查看变量文件:

# cat lamp_simple/group_vars/all

---
# Variables listed here are applicable to all host groups

httpd_port: 80
ntpserver: 192.168.1.2
repository: https://github.com/bennojoy/mywebapp.git

# cat lamp_simple/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: foouser
dbname: foodb
upassword: abc
  • 查看通用hanlder文件:

# cat lamp_simple/roles/common/handlers/main.yml

---
# Handler to handle common notifications. Handlers are called by other plays.
# See http://docs.ansible.com/playbooks_intro.html for more information about handlers.

- name: restart ntp
  service: name=ntpd state=restarted

- name: restart iptables
  service: name=iptables state=restarted
  • 查看通用tasks文件:

# cat lamp_simple/roles/common/tasks/main.yml

---
# This playbook contains common plays that will be run on all nodes.

- 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=yes
  tags: ntp

- name: test to see if selinux is running
  command: getenforce
  register: sestatus
  changed_when: false
  • 查看通用模板文件:

# cat lamp_simple/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
  • 查看db角色的handlers文件:

# cat lamp_simple/roles/db/handlers/main.yml

---
# Handler to handle DB tier notifications

- name: restart mysql
  service: name=mysqld state=restarted

- name: restart iptables
  service: name=iptables state=restarted[root@LOCALHOST ansible-examples-master]# cat lamp_simple/roles/db/handlers/main.yml 
---
# Handler to handle DB tier notifications

- name: restart mysql
  service: name=mysqld state=restarted

- name: restart iptables
  service: name=iptables state=restarted
  • 查看db角色的tasks文件:

# cat lamp_simple/roles/db/tasks/main.yml

---
# This playbook will install mysql and create db user and give permissions.

- name: Install Mysql package
  yum: name={{ item }} state=installed
  with_items:
   - mysql-server
   - MySQL-python
   - libselinux-python
   - libsemanage-python

- name: Configure SELinux to start mysql on any port
  seboolean: name=mysql_connect_any state=true persistent=yes
  when: sestatus.rc != 0

- name: Create Mysql configuration file
  template: src=my.cnf.j2 dest=/etc/my.cnf
  notify:
  - restart mysql

- name: Start Mysql Service
  service: name=mysqld state=started enabled=yes

- name: insert iptables rule
  lineinfile: dest=/etc/sysconfig/iptables state=present regexp="{{ mysql_port }}"
              insertafter="^:OUTPUT " line="-A INPUT -p tcp  --dport {{ mysql_port }} -j  ACCEPT"
  notify: restart iptables

- 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
  • 查看db角色的模板文件:

# cat lamp_simple/roles/db/templates/my.cnf.j2

[mysqld]
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
user=mysql
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0
port={{ mysql_port }}

[mysqld_safe]
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid
  • 查看web角色的handlers文件:

# cat lamp_simple/roles/web/handlers/main.yml

---
# Handler for the webtier: handlers are called by other plays.
# See http://docs.ansible.com/playbooks_intro.html for more information about handlers.

- name: restart iptables
  service: name=iptables state=restarted
  • 查看web角色的tasks文件:

# cat lamp_simple/roles/web/tasks/main.yml

---
- include: install_httpd.yml
- include: copy_code.yml

# cat lamp_simple/roles/web/tasks/install_httpd.yml

---
# These tasks install http and the php modules.

- name: Install http and php etc
  yum: name={{ item }} state=present
  with_items:
   - httpd
   - php
   - php-mysql
   - git
   - libsemanage-python
   - libselinux-python

- name: insert iptables rule for httpd
  lineinfile: dest=/etc/sysconfig/iptables create=yes state=present regexp="{{ httpd_port }}" insertafter="^:OUTPUT "
              line="-A INPUT -p tcp  --dport {{ httpd_port }} -j  ACCEPT"
  notify: restart iptables

- name: http service state
  service: name=httpd state=started enabled=yes

- 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`

# cat lamp_simple/roles/web/tasks/copy_code.yml

---
# These tasks are responsible for copying the latest dev/production code from
# the version control system.

- name: Copy the code from repository
  git: repo={{ repository }} dest=/var/www/html/

- name: Creates the index.php file
  template: src=index.php.j2 dest=/var/www/html/index.php

查看web角色的模板文件:

# cat lamp_simple/roles/web/templates/index.php.j2

<html>
 <head>
  <title>Ansible Application</title>
 </head>
 <body>
 </br>
  <a href=http://{{ ansible_default_ipv4.address }}/index.html>Homepage</a>
 </br>
<?php 
 Print "Hello, World! I am a web server configured using Ansible and I am : ";
 echo exec('hostname');
 Print  "</BR>";
echo  "List of Databases: </BR>";
        {% for host in groups['dbservers'] %}
                $link = mysqli_connect('{{ hostvars[host].ansible_default_ipv4.address }}', '{{ hostvars[host].dbuser }}', '{{ hostvars[host].upassword }}') or die(mysqli_connect_error($link));
        {% endfor %}
        $res = mysqli_query($link, "SHOW DATABASES;");
        while ($row = mysqli_fetch_assoc($res)) {
                echo $row['Database'] . "\n";
        }
?>
</body>
</html>

执行这个roles

# ansile-playbook -i lamp_simple/hosts lamp_simple/site.yml

八、Ansible Galaxy

Ansible Galaxy是Ansible官方Roles分享平台(galaxy.ansible.com),在Galaxy平台上所有人可以免费上传或下载Roles,在这里好的技巧、思想、架构得以积累和传播。

8.1 ansible-galaxy命令的用法:

[root@LOCALHOST tasks]# ansible-galaxy --help
Usage: ansible-galaxy [delete|import|info|init|install|list|login|remove|search|setup] [--help] [options] ...

Options:
  -h, --help            show this help message and exit
  -c, --ignore-certs    Ignore SSL certificate validation errors.
  -s API_SERVER, --server=API_SERVER
                        The API server destination
  -v, --verbose         verbose mode (-vvv for more, -vvvv to enable
                        connection debugging)
  --version             show program's version number and exit

 See 'ansible-galaxy <command> --help' for more information on a specific command.

搜索lamp相关的roles:

命令:ansible-galaxy search lamp
结果如下图:

image.png

来安装一个搜索到的角色:
命令:ansible-galaxy install vivaserver.lamp

[root@LOCALHOST ~]# ansible-galaxy install vivaserver.lamp
- downloading role 'lamp', owned by vivaserver
- downloading role from https://github.com/vivaserver/ansible-lamp/archive/master.tar.gz
- extracting vivaserver.lamp to /root/.ansible/roles/vivaserver.lamp
- vivaserver.lamp (master) was installed successfully

根据输出提示,安装的role放在/root/.ansible/roles/vivaserver.lamp

查看这个role目录结构:

[root@LOCALHOST vivaserver.lamp]# tree /root/.ansible/roles/vivaserver.lamp/
/root/.ansible/roles/vivaserver.lamp/
├── files
│   ├── favicon.ico
│   └── www.html
├── LICENSE
├── meta
│   └── main.yml
├── README.md
├── screenshot.png
└── tasks
    └── main.yml

3 directories, 7 files

按作者搜索roles:

[root@LOCALHOST ~]# ansible-galaxy search zabbix --author dj-wasabi

Found 5 roles matching your search:

 Name                         Description
 ----                         -----------
 dj-wasabi.zabbix-javagateway Installing and maintaining zabbix-javagateway for RedHat/Debian/Ubuntu.
 dj-wasabi.zabbix-server      Installing and maintaining zabbix-server for RedHat/Debian/Ubuntu.
 dj-wasabi.zabbix-web         Installing and maintaining zabbix-web for RedHat/Debian/Ubuntu.
 dj-wasabi.zabbix-proxy       Installing and maintaining zabbix-proxy for RedHat/Debian/Ubuntu.
 dj-wasabi.zabbix-agent       Installing and maintaining zabbix-agent for RedHat/Debian/Ubuntu.

上述命令中 dj-wasabi是作者的用户名

8.2 批量安装多个roles

# cat roles.txt
user1.role1,v1.0.0
user2.role2,v0.5
user2.role3 

安装:

# ansible-galaxy install -r roles.txt
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容