一、Playbook简介

1.Playbook是一种简单的配置管理系统与多台机器部署系统的基础,且非常适合
于复杂应用的部署
2.playbook中可以编排有序的执行过程,甚至可以做到在多组机器之间,来回有序的执行特别指定的步骤,并且可以同步或异步的发起任务
3.可以重用代码,可以移植到不同的机器上
4.通过YAML格式来描述定义

二、playbook编写

1.语法

  • 文件的第一行应该以“- - -”三个连字符开始,表明YAML文件的开始
  • 在同一行中,#之后的内容表示注释,类似于shell,python和ruby
  • YAML中的列表元素以“-”开头然后紧跟着一个空格,同一个列表中的元素应该保持相同的缩进(通常是两个空格)
    ---
    #一个美味的水果列表
    - apple
    - orange
    - mango
  • 一个字典是由一个简单的键: 值的形式组成(这个冒号后面必须是一个空格)
    ---
    #一位职工的记录
    name: Example
    job: Developer
    skill: Elite
  • 字典也可以使用缩进形式来表示:
    ---
    #一位职工的记录
    {name: Example,job: Develop,skill: Elite}

2.Tasks列表

  • play的主体部分是task列表,task列表中的各任务按次序逐个在hosts主机执行,即所有主机完成第一个任务后再开始第二个任务。如果一个hosts执行task失败,整个task都会回滚。
  • 每一个task必须有一个名称name,这样在运行playbook的时候,从其输出的任务执行信息中可以很好的辨别出属于哪一个task的。

小知识1:针对不同文件设置Tab值不同
vim .vimrc #编写文件,ts空格,sw ,et将tab变成空格
autocmd FileType yaml setlo=cal ai ts=2 sw=2 et #设置为tab两个空格

3.第一个playbook文件

---
- hosts: prod
  tasks:
   - name: install apache
     yum:
      name: httpd
      state: present
   - name: start apache
     service:
      name: httpd
      state: started
   - name: create index.html
     copy:
      content: "www.westos.org"
      dest: /var/www/html/index.html

ansible-playbook apache.yml --syntax-check

检测是否有语法错误

ansible-playbook apache.yml --list-hosts

列出主机

ansible-playbook apache.yml --list-tasks

列出任务

ansible-playbook apache.yml

执行任务

ansibleplaybook 替换文件中指定行 ansible playbook编写_apache


ansibleplaybook 替换文件中指定行 ansible playbook编写_vim_02


ansibleplaybook 替换文件中指定行 ansible playbook编写_html_03

4 对第一个文件添加触发器,服务配置文件修改时触发

---
- hosts: prod
  tasks:
    - name: install apache
      yum:
        name: httpd
        state: present
    - name: configure
      copy:
        src: httpd.conf
        dest: /etc/httpd/conf/httpd.conf
        mode: 644
      notify: restart apache
    - name: start apache
      service:
        name: httpd
        state: started
    - name: create index.html
      copy:
        content: "www.westos.org"
        dest: /var/www/html/index.html
  handlers:
    - name: restart apache
      service:
        name: httpd
        state: restarted
[ansible@node1 ~]$ ansible-playbook apache.yml

ansibleplaybook 替换文件中指定行 ansible playbook编写_html_04


ansibleplaybook 替换文件中指定行 ansible playbook编写_vim_05

5 添加防火墙

---
- hosts: prod
  tasks:
    - name: install apache
      yum:
        name: httpd
        state: present
    - name: configure
      copy:
        src: httpd.conf
        dest: /etc/httpd/conf/httpd.conf
        mode: 644
      notify: restart apache
    - name: start apache
      service:
        name: httpd
        state: started
    - name: create index.html
      copy:
        content: "www.westos.org"
        dest: /var/www/html/index.html
    - name: start firewalld  #开启防火墙
      service:
        name: firewalld
        state: started
        enabled: yes
    - name: custom firewalld  #配置防火墙
      firewalld:
        service: http
        permanent: yes
        immediate: yes
       state: enabled

  handlers:
    - name: restart apache
      service:
         name: httpd
         state: restarted

ansibleplaybook 替换文件中指定行 ansible playbook编写_html_06


node3上查看策略:

[root@node3 html]# netstat -antlupe

ansibleplaybook 替换文件中指定行 ansible playbook编写_vim_07


node1上测试:成功访问

ansibleplaybook 替换文件中指定行 ansible playbook编写_apache_08

6 添加标签,按标签执行任务

在5的代码基础上添加
 handlers:
    - name: restart apache
      service:
        name: httpd
        state: restarte
- hosts: localhost       #新添加的按标签执行任务
  become: no             #不变成超户
  tasks:
    - name: test apache
      uri:
        url: http://node3/index.html
        return_content: yes  
      tags: test  # 给test apache添加标签
  tags: teeest   #给整个任务添加标签

ansibleplaybook 替换文件中指定行 ansible playbook编写_html_09


ansibleplaybook 替换文件中指定行 ansible playbook编写_vim_10

三、Playbook中的变量

  1. 变量的定义及引用
[ansible@node1 ~]$ cat hosts 
[test]
node2
[prod]
node3 web=httpd   #定义web变量对应的值是httpd
[ansible@node1 ~]$ vim apache.yml
---
 - hosts: prod
  tasks:
    - name: install {{ web }}  #名字,括号可以不加''
      yum:
        name: '{{ web }}'   #此处安装模块必须加,不然找不到对应web=httpd
        state: present
    - name: configure
      copy:
        src: httpd.conf
        dest: /etc/httpd/conf/httpd.conf
        mode: 644
      notify: restart apache
    - name: start apache
      service:
        name: httpd
        state: started
    - name: create index.html
      copy:
        content: "www.westos.org"
        dest: /var/www/html/index.html

ingore_error: yes用于在某个任务执行出错时自动忽略,继续执行接下来的操作(一般用于此个任务正确与否不影响接下来的任务)

在hosts文件中故意把变量定义错误: web=http,若没忽略参数,后续task会中段不执行,加上,则忽略此错误,继续执行。

ansibleplaybook 替换文件中指定行 ansible playbook编写_vim_11


ansibleplaybook 替换文件中指定行 ansible playbook编写_apache_12


变量也可以在apache.yml文件中定义,优先级高于hosts中定义的

ansibleplaybook 替换文件中指定行 ansible playbook编写_apache_13


ansibleplaybook 替换文件中指定行 ansible playbook编写_apache_14


2.预留变量facts查看

[ansible@node1 ~]$ ansible node2 -m setup

ansibleplaybook 替换文件中指定行 ansible playbook编写_apache_15


当playbook中不引入gather_fact的时候,可以把gather_facts禁掉,不会影响后续执行。

ansibleplaybook 替换文件中指定行 ansible playbook编写_apache_16


ansibleplaybook 替换文件中指定行 ansible playbook编写_vim_17


当引用fact变量的时候,facts必须打开,将apache.yml中gather_facts: no删除掉。否则无法识别

ansibleplaybook 替换文件中指定行 ansible playbook编写_apache_18


ansibleplaybook 替换文件中指定行 ansible playbook编写_vim_19


ansibleplaybook 替换文件中指定行 ansible playbook编写_vim_20


3.facts变量官方推荐写法

ansibleplaybook 替换文件中指定行 ansible playbook编写_apache_21

  • {{ ansible_facts[“eth0”][“ipv4”][“address”] }}
  • {{ ansible_facts.eth0.ipv4.address }}
  • {{ ansible_facts[“hostname”] }}
  • {{ ansible_facts[[‘dns’][‘nameservers’]] }}
  • 禁用facts:
  • -hosts: prod
  • gather_facts: no
[ansible@node1 ~]$ vim apache.yml  #最后添加一个测试
- hosts: all
  tasks:
    - name: create hostinfo
      copy:
        content: "{{ ansible_facts['eth0']['ipv4']['address'] }}"
        dest: /tmp/hostinfo
  tags: fact_info
[ansible@node1 ~]$ ansible-playbook apache.yml  -t fact_info
[root@node2 ~]# cat /tmp/hostinfo   #node2上查看
172.25.7.132[root@node2 ~]#

4.编写j2文件

[ansible@node1 ~]$ vim hostinfo.j2
主机ip: {{  ansible_facts['eth0']['ipv4']['address'] }}
主机名: {{ ansible_facts['hostname'] }}
dns: {{ ansible_facts['dns']['nameservers'] }}
内存: {{ ansible_facts['memfree_mb'] }}
[ansible@node1 ~]$ vim apache.yml
- hosts: all
  tasks:
    - name: create hostinfo
      template:
        src: hostinfo.j2
        dest: /tmp/hostinfo
  tags: fact_info
[ansible@node1 ~]$ ansible-playbook apache.yml -t fact_info #从标签fact_info开始执行
 [root@node2 ~]# cat /tmp/hostinfo    #node2上查看
主机ip: 172.25.7.132
主机名: node2
dns: [u'114.114.114.114']
内存: 677
[root@node3 ~]# cat /tmp/hostinfo   #nod3上查看
主机ip: 172.25.7.133
主机名: node3
dns: [u'114.114.114.114']
内存: 716

5.编写j2文件。针对主机的不同分别改配置文件,以apache为例

[ansible@node1 ~]$ vim httpd.conf.j2

ansibleplaybook 替换文件中指定行 ansible playbook编写_vim_22

[ansible@node1 ~]$ vim apache.yml

ansibleplaybook 替换文件中指定行 ansible playbook编写_vim_23


ansibleplaybook 替换文件中指定行 ansible playbook编写_html_24

[ansible@node1 ~]$ ansible-playbook apache.yml -t appache
[ansible@node1 ~]$ curl node2   #测试一下
node2
[ansible@node1 ~]$ curl node3
node3
[ansible@node1 ~]$ cat hosts  #组信息
[test]
node2

[prod]
node3 web=http
 
[webserver:children]
test
prod

[webserver:vars]
http_port=80

在node2和node3上看配置文件,查看已更改。

ansibleplaybook 替换文件中指定行 ansible playbook编写_apache_25


ansibleplaybook 替换文件中指定行 ansible playbook编写_html_26


6.registry注册变量,debug调试输出

- hosts: localhost
  become: no
  tasks:
    - name: test apache
      uri:
        url: http://node3/index.html
        return_content: yes
      register: result    #注册变量
    - debug:
        var: result   
        #var: result.content  #去掉注释,只显示
        #msg: this a message  #印制信息
        #msg: '{{ result.content }}'
  tags: teeest

ansibleplaybook 替换文件中指定行 ansible playbook编写_apache_27


ansibleplaybook 替换文件中指定行 ansible playbook编写_apache_28


ansibleplaybook 替换文件中指定行 ansible playbook编写_vim_29


ansibleplaybook 替换文件中指定行 ansible playbook编写_apache_30


7.使用loop创建多个用户

[ansible@node1 ~]$ vim apache.yml 
- hosts: test
  #vars:
   # u: user1
    #pd: westos
  tasks:
    - name: create user
      user:
        name: '{{ item.user }}'
        password: '{{ item.pd|password_hash("sha512") }}'
      loop:
        - { user: user01,pd: westos }
        - { user: user02,pd: redhat }
  tags: usercreate
  [ansible@node1 ~]$ ansible-playbook apache.yml -t usercreate  #执行

 node2上检验有无:
 [root@node2 ~]# cat /etc/passwd

ansibleplaybook 替换文件中指定行 ansible playbook编写_apache_31


用户可以创建成功,但是此法将密码裸露在文件中,不安全。

为了安全起见,将要创建的用户写入文件中

[ansible@node1 ~]$ cat vars/userlist.yml 
---
userlist:
  - user: yy1
    pd: westos
  - user: yy2
    pd: redhat
  - user: yy3
    pd: redhat
[ansible@node1 ~]$ vim apache.yml 
- hosts: test
  vars_files:
    - vars/userlist.yml
  tasks:
    - name: create user
      user:
        name: '{{ item.user }}'
        password: '{{ item.pd|password_hash("sha512") }}'
      loop: "{{ userlist }}"
  tags: usercreate
[ansible@node1 ~]$ ansible-playbook apache.yml -t usercreate

node2上查看:

[root@node2 ~]# cat /etc/passwd

ansibleplaybook 替换文件中指定行 ansible playbook编写_html_32


8. 魔术变量

[ansible@node1 ~]$ cat test.j2 
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6

{% for host in groups['webserver'] %}
{{ hostvars[host]['ansible_facts']['eth0']['ipv4']['address'] }} {{ hostvars[host]['ansible_facts']['hostname'] }}
{% endfor %}

[ansible@node1 ~]$ vim apache.yml 
- hosts: all
  tasks:
     - name: create hosts
       template:
         src: test.j2
         dest: /tmp/hosts
  tags: hosts
[ansible@node1 ~]$ ansible-playbook apache.yml -t hosts

ansibleplaybook 替换文件中指定行 ansible playbook编写_vim_33


创建成功,node2上查看

ansibleplaybook 替换文件中指定行 ansible playbook编写_vim_34


9.yml文件部署haproxy

[ansible@node1 ~]$ cat hosts   #主机的分组
[balance]
node1
[test]
node2
[prod]
node3 web=http
 
[webserver:children]
test
prod
[webserver:vars]
http_port=80

[ansible@node1 ~]$ vim haproxy.cfg.j2   #用魔术变量编辑配置文件
frontend  main *:80
    default_backend             app
backend app
    balance     roundrobin
{% for host in groups['webserver'] %}
server {{ hostvars[host]['ansible_facts']['hostname'] }} {{ hostvars[host]['ansible_facts']['eth0']['ipv4']['address'] }}:80
{% endfor %}

[ansible@node1 ~]$ vim apache.yml
- hosts: all
  tasks:
    - name: install haproxy
      yum:
        name: haproxy
        state: present
      when: ansible_hostname == 'node1'
    - name: config haproxy
      template:
        src: haproxy.cfg.j2
        dest: /etc/haproxy/haproxy.cfg
      notify: restart haproxy
      when: ansible_hostname == 'node1'
    - name: start haproxy
      service:
        name: haproxy
        state: started
      when: ansible_hostname == 'node1'
  handlers:
    - name: restart haproxy
      service:
        name: haproxy
        state: restarted
  tags: haproxy

执行
[ansible@node1 ~]$ ansible-playbook apache.yml -t haproxy

[ansible@node1 ~]$ vim haproxy.cfg.j2

ansibleplaybook 替换文件中指定行 ansible playbook编写_apache_35


测试:

[ansible@node1 ~]$ curl node1

node2

[ansible@node1 ~]$ curl node1

node3

[ansible@node1 ~]$ curl node1

node2

10. block模块

- hosts: all
  tasks:
    - name: deployment haproxy   #下面三个任务合为
      block:
        - name: install haproxy
          yum:
            name: haproxy
            state: present
        - name: config haproxy
          template:
            src: haproxy.cfg.j2
            dest: /etc/haproxy/haproxy.cfg
          notify: restart haproxy
        - name: start haproxy
          service:
            name: haproxy
            state: started
            enabled: yes
      when: ansible_hostname == 'node1'  #
  handlers:
    - name: restart haproxy
      service:
        name: haproxy
        state: restarted
  tags: haproxy
  [ansible@node1 ~]$ ansible-playbook apache.yml -t haproxy

ansibleplaybook 替换文件中指定行 ansible playbook编写_vim_36


11. rescue ,当上述抛出异常,启用rescue任务

---
- hosts: localhost
  become: no
  tasks:
    - name: localhost date
      command: date
      register: result
      changed_when: false
    - name: print name
      debug:
        var: result.stdout
- hosts: node2
  tasks:
    - name: deployment apache
      block:
        - name: install webserver
          yum:
            name: httpd
            state: present
          failed_when: yes    #抑制更改,更改任务运行后的报告状态,但不会更改任务本身的行为

ansibleplaybook 替换文件中指定行 ansible playbook编写_apache_37

---
- hosts: localhost
  become: no
  tasks:
    - name: localhost date
      command: date
      register: result
      changed_when: false
    - name: print name
      debug:
        var: result.stdout
- hosts: node2
  tasks:
    - name: deployment apache
      block:
        - name: install webserver
          yum:
            name: httpd
            state: present
          failed_when: yes
      rescue:                 #当上述任务执行的时候有失败,运行拯救任务
        - debug:
            msg: rescue is running    #拯救措施,此处随便填一条打印信息

ansibleplaybook 替换文件中指定行 ansible playbook编写_apache_38


12 .ansible-vault 加密

ansible-vault encrypt userlist.yml

加密

ansible-vault view userlist.yml

查看,需要输入密码

ansible-vault decrypt userlist.yml

解密

[ansible@node1 ~]$ ansible-playbook apache.yml -t usercreate --ask-vault-pass  #运行包含加密文件的yml的文件时,需要加--ask-vault-pass

ansibleplaybook 替换文件中指定行 ansible playbook编写_vim_39


ansibleplaybook 替换文件中指定行 ansible playbook编写_html_40


13.变量文件推荐的管理方式

  • host_vars:存放主机变量
  • group_vars:存放主机组变量
  • vars: 存放纯文本信息
  • vault:存放加密信息
- hosts: test
 # vars_files:
  #  - vars/userlist.yml
  tasks:
    - name: create user
      user:
        name: '{{ item.user }}'
        password: '{{ item.pd|password_hash("sha512") }}'
      loop: "{{ userlist }}"
  tags: usercreate

ansibleplaybook 替换文件中指定行 ansible playbook编写_vim_41


ansibleplaybook 替换文件中指定行 ansible playbook编写_vim_42


14. 导入与包含任务

(1)导入playbook

[ansible@node1 ~]$ cat play1.yml 
---
- hosts: localhost
  become: no
  tasks:
    - name: localhost date
      command: date
      register: result
      changed_when: false
    - name: print name
      debug:
        var: result.stdout
- name: play2.yml
  import_playbook: play2.yml
[ansible@node1 ~]$ cat play2.yml 
---
- hosts: node3
  tasks:
    - name: deployment apache
      block:
        - name: install webserver
          yum:
            name: httpd
            state: present
          failed_when: yes
      rescue:
        - debug:
            msg: rescue is running

ansibleplaybook 替换文件中指定行 ansible playbook编写_html_43


(2)导入task

静态加载(import_tasks):所有导入进来的变量都会生效

动态加载(include_tasks):按顺序执行,执行到的时候才会生效

[ansible@node1 ~]$ cat play3.yml 
---
- hosts: node1
  tasks:
    - include_tasks: task2.yml     #动态加载
      when: ansible_os_family == 'RedHat'
[ansible@node1 ~]$ cat task2.yml 
---
- set_fact: ansible_os_family='Centos'
- debug: 
    var: ansible_os_family


[ansible@node1 ~]$ cat play3.yml 
---
- hosts: node1
  tasks:
    - import_tasks: task2.yml            #静态加载,加载所有任务变量,所以不会执行task2
    - when: ansible_os_family == 'RedHat'
[ansible@node1 ~]$ cat task2.yml 
---
- set_fact: ansible_os_family='Centos'
- debug: 
    var: ansible_os_family

ansibleplaybook 替换文件中指定行 ansible playbook编写_html_44


ansibleplaybook 替换文件中指定行 ansible playbook编写_vim_45