自动化运维之ansible续

ansible的七个命令

ansible命令 用于临时性工作
ansible-doc ansible模块的文档说明,针对每个模块都有详细的说明及应用案例介绍。这个很重要,类似于linux的man
ansible-console 为用户提供的交互式工具,用户可以在这个命令虚拟出来的终端上像shell一样使用ansible内置的各种命令
ansible-galaxy 从github上下载管理Roles的一款工具,与python的pip类似
ansible-playbook 应用频率最高的命令。工作机制:通过读取写好的playbook文件实现批量管理,也就是一定条件组成的ansible任务集
ansible-vault 配置文件加密,如编写playbook文件中包含敏感信息,不想其他人随意查看,可用它加密/解密
ansible-pull 两种工作模式pull/push,默认是push(与pull模式相反),在有大批量机器的场景下使用

JSON JSON是JavaScript对象表示法,基于文本独立语言的轻量级数据交换格式

分隔符:限于' 单引号 ',( 小括号 ),[ 中括号 ],{ 大括号 },  :冒号  ,  ,逗号

特性: 纯文本,具有自我描述性(人类可读),具有层级结构(值中存在值),可通过JavaScript解析

语法: 数据存在名称/值对中,数据由逗号分隔,大括号保存对象,中括号保存数组

格式: 名称/值对包括字段名称(在双引号中),后面写一个冒号,然后是值。数组{"key": ["value","value","value"] }

              例如:{ "诗人":    

                                 [{"李白":  "诗仙",   " 年代 ":  "唐" ,

                                    "杜甫":  "诗圣",   " 年代":   "唐"}

                                  ]

                                {

YAML ---YAML Ain't  Markup Language 表达数据序列的格式

语法:结构通常通过空格来表示,数组使用“- ”  ,键值对使用“: ”来表示(注意:-和:后面有一个空格)

            使用一个固定的缩进风格表示数据层级结构关系,一般每个缩进级别由两个以上空格组成   # 表示注释

注意: 不要使用tab键,同一层级必须对齐

表示:  "name": "jack"   or         "name":

                                                          "jack"

              哈希数组复合表达式          "name":                       "name":

                                                               - "jack":                       - "jack": ''male"

                                                               - "rose"                         "hobby": "play"

                                                                                                    - "rose": "female"

                                                                                                      "hobby": "book"    

Jinja2 基于Python的模板引擎,包含变量和表达式,两者在模板求值时会被替换为值,模板中还有标签,控制模板的逻辑

            playbook的模板就是使用python的jinja2模块来处理

语法 : 模板的表达式都是包含在分隔符{{  }}内的,控制语句都是包含在分隔符{%   %}内的

               模板支持注释,都是包含在分隔符{#    #}内的,支持块注释

模板:   {%  if name  ==  ... ... %}

                  ....

                 {%   elif   name  == '李白'%}

                       {%  for  method in [写诗,饮酒,舞剑] %}

                               {{ do  method }}

                        {%  enfor %}

                         ...  ...

                   {%  endif  %}

Jinja2过滤器

变量可以通过过滤器修改。过滤器与变量用管道符号“|”分割,也可以用圆括号传递可选参数,多个过滤器可以链式调用,前一个过滤器的输出会被作为后一个过滤器的输入

在线文档   http://docs.jinkan.org/docs/jinja2/templates.html#builtin-filters

playbook

playbook是ansible用于配置,部署和管理托管主机剧本,通过playbook的详细描述,执行其中的一系列tasks,可以让远端主机达到预期状态

用途:执行一些简单的任务,使用ad-hoc命令可以方便的解决问题,但有时一个设施过于复杂时,执行ad-hoc命令是不合适的

            playbook可以反复使用编写的代码,可以放到不同的机器上面,像函数一样,最大化的利用代码,在使用ansible的过程中,处理的大部分操作都是在编写playbook

语法:由YAML语言编写,遵循YAML标准,在同一行中,#之后内容表示注释

             同一个列表中的元素应该保持相同的缩进,playbook由一个或多个play组成

              play中hosts,variables,roles,tasks等对象的表示方法都是键值中间以“: ”分隔表示

              YAML还有一个小的怪痞,它的文件开始行都应该是 --- ,这是YAML格式的一部分,表明一个文件的开始

构成:Target:定义将要执行playbook的远程主机组

             Variable:定义playbook运行时需要使用的变量

              Tasks:定义将要在远程主机上执行的任务列表

              Handler:定义task执行完成以后需要调用的任务

执行结果:使用ansible-playbook运行playbook文件,输出内容为JSON格式,由不同颜色组成便于区别

                     绿色代表执行成功,***代表系统状态发生改变,红色代表执行失败

第一个playbook

[root@ansible ~]# vim ping.yml
---                                                   #第一行,表示开始
- hosts: all                                          #hosts行,内容是一个(多个)组或主机的patterns,以逗号为分隔符
  remote_user: root                                   #remote_user 账户名
  tasks:                                              
# 每一个play包含了一个task列表(任务列表,一个task对应所有主机(通过host pattern匹配所有主机)执行完毕后,下一个task才会执行
在一个play中,所有的hosts会获取相同的指令,这是一个play的目的所在,即将一组选出的hosts映射到task,执行相同的操作
    - ping:
[root@ansible ~]# ansible-playbook ping.yml -f 5       #并发进程数量,默认是5,可不写

PLAY [all] *********************************************************************

TASK [Gathering Facts] *********************************************************
ok: [cache]
...
ok: [web2]

TASK [ping] ********************************************************************
ok: [db1]
...
ok: [web2]

PLAY RECAP *********************************************************************
cache                      : ok=2    changed=0    unreachable=0    failed=0   
...
web2                       : ok=2    changed=0    unreachable=0    failed=0   

案例1:添加用户,创建密码,设置密码第一次登录需修改

[root@ansible ~]# ansible-doc user                     //需要那个模块,可以查看相应帮助
...
EXAMPLES:
# Add the user 'johnd' with a specific uid and a primary group of 'admin'
- user:
    name: johnd
    comment: "John Doe"
    uid: 1040
    group: admin
...
[root@ansible ~]# vim user.yml 
---
- hosts: all
  remote_user: root
  tasks:
    - name: create a user                      //这个用来描述相应,可以省略,属于tasks下的
      user:
        name: Jack                             //创建一个用户,属于user下的
    - name: create a password for the user
      shell: echo "123456" | passwd --stdin Jack
    - shell: chage -d 0 Jack
[root@ansible ~]# ansible-playbook user.yml 
...
PLAY RECAP *********************************************************************
cache                      : ok=4    changed=3    unreachable=0    failed=0   
...
[root@ansible ~]# ansible all -m shell -a 'id Jack'
...
cache | SUCCESS | rc=0 >>
uid=1000(Jack) gid=1000(Jack) 组=1000(Jack)

案例2:安装apache并修改监听端口为8080,修改ServerName,执行apachectl -t不报错 设置主页为hello world 启动服务并设置开机自启

[root@ansible ~]# vim httpd.yml
---
  - hosts: databases
    remote_user: root
    tasks:
       - yum:
           name: httpd
           state: latest
       - lineinfile:
           path: '/etc/httpd/conf/httpd.conf'
           regexp: '^Listen '
           line: 'Listen 8080'
       - lineinfile:
           path: '/etc/httpd/conf/httpd.conf'
           regexp: '^#ServerName www.example.com:80 '
           line: 'ServerName localhost'
       - copy:
           src: /etc/ansible/index.html
           dest: /var/www/html/index.html
       - service:
           name: httpd
           enabled: yes
           state: started   
[root@ansible ~]# ansible-playbook  httpd.yml
[root@ansible ~]# curl db1:8080
hello world
[root@ansible ~]# curl db2:8080
hello world
[root@ansible ~]# ansible databases -m shell -a 'ss -pntul | grep 8080'
db1 | SUCCESS | rc=0 >>
tcp    LISTEN     0      128      :::8080                 :::*                   users:(("httpd",pid=1812,fd=4),("httpd",pid=1811,fd=4),("httpd",pid=1810,fd=4),("httpd",pid=1809,fd=4),("httpd",pid=1808,fd=4),("httpd",pid=1807,fd=4))

db2 | SUCCESS | rc=0 >>
tcp    LISTEN     0      128      :::8080                 :::*                   users:(("httpd",pid=1810,fd=4),("httpd",pid=1809,fd=4),("httpd",pid=1808,fd=4),("httpd",pid=1807,fd=4),("httpd",pid=1806,fd=4),("httpd",pid=1805,fd=4))
[root@ansible ~]# ansible databases -m shell -a 'apachectl -t '
db1 | SUCCESS | rc=0 >>
Syntax OK

db2 | SUCCESS | rc=0 >>
Syntax OK

playbook拓展

变量:

添加用户(沿用案例1):用key:value 表示变量

[root@ansible ~]# vim user.yml 
---
- hosts: all
  remote_user: root
  vars:                                         //定义变量
    username: wangwu
  tasks:
    - name: create a user
      user:
        name: "{{username}}"
    - name: create a password for the user
      shell: echo "123456" | passwd --stdin "{{username}}"
    - shell: chage -d 0 "{{username}}"
[root@ansible ~]# ansible-playbook  user.yml 
[root@ansible ~]# ansible-playbook  user.yml 

PLAY [all] *********************************************************************

TASK [Gathering Facts] *********************************************************
ok: [web1]
ok: [cache]
ok: [db1]
ok: [db2]
ok: [web2]
...
[root@ansible ~]# ansible all -m shell -a 'id wangwu'
db2 | SUCCESS | rc=0 >>
uid=1002(wangwu) gid=1002(wangwu) 组=1002(wangwu)
...

虽然我们上面定义了变量,但是我们可以通过外部给变量赋值来添加相应的用户,也就是外部赋值的优先级要高于内部的变量赋值

[root@ansible ~]# ansible-playbook  user.yml   -e '{"username": "Rose"}'   //可以通过-e给变量赋值
[root@ansible ~]# ansible all -m shell -a 'id Rose'
db2 | SUCCESS | rc=0 >>
uid=1003(Rose) gid=1003(Rose) 组=1003(Rose)
...

user模块下面也有一个password,但是不能直接设置密码,因为linux的shadow密码是经过加密的,所以我们可以通过过滤器password—_hash来执行

[root@ansible ~]# vim user.yml 
---
...
  tasks:
      user:
        name: "{{username}}"
        password: "{{'123456' | password_hash('sha512')}}"
...

error

ansible-playbook对错误的处理,默认情况$?,如果不为0就停止执行,某些情况需要忽略错误继续执行

第一种:用true(返回值为0) false(返回值为1)

[root@localhost ~]# useradd wangwu 
useradd:用户“wangwu”已存在
[root@localhost ~]# echo $?
9
[root@localhost ~]# useradd wangwu || true
useradd:用户“wangwu”已存在
[root@localhost ~]# echo $?
0

第二种:ignore_errors:true

[root@ansible ~]# vim user.yml 
---
- hosts: all
  remote_user: root
  vars:
    username: wangwu
  tasks:
    - name: create a user
      user:
        name: "{{username}}"
      ignore_errors: true

 handlers 当关注的资源发生变化时采取的操作

notify这个action可用于在每个play的最后被触发,这样可以避免有多次改变发生时每次都执行指定的操作,现在通过这个尽在所有变化发生完成后一次性的执行指定操作

在notify中列出的操作称为handler,即notify调用handler中定义的操作

借用前面的案例2来演示

[root@ansible ~]# vim httpd.yml 
---
  - hosts: databases
    remote_user: root
    tasks:
       - yum:
           name: httpd
           state: latest
       - copy:
           src: /root/httpd.conf                   //把http的端口改成了80,然后触发操作重启
           dest: /etc/httpd/conf/httpd.conf
         notify:
           - reload httpd
    handlers:
      - name: reload httpd
        service:
          name: httpd
          state: restarted
          enabled: yes
[root@ansible ~]# ansible-playbook httpd.yml  
...
PLAY RECAP *********************************************************************
db1                        : ok=4    changed=2    unreachable=0    failed=0   
db2                        : ok=4    changed=2    unreachable=0    failed=0   
...
[root@ansible ~]# ansible databases -m shell -a 'ss -pntul | grep 80'
db1 | SUCCESS | rc=0 >>
tcp    LISTEN     0      128      :::80                   :::*       
...
db2 | SUCCESS | rc=0 >>
tcp    LISTEN     0      128      :::80                   :::*    

注意事项:notify调用的handler段的name定义的串,必须一致,否则达不到触发的效果

                    多个task触发同一个notify的时候,同一个服务只会触发一次

                    notify可以触发多个条件,在生产环境中往往涉及到某一个配置文件的改变要重启若干服务的场景,handler适合

                    结合vars可以写出非常合适的服务管理脚本

when 条件判断

需要在满足特定的条件后再触发某一项操作,或在特定的条件下终止某个行为,需要用到when,也是最佳的选择。远程中的系统变量facts作为when的条件,可以通过setup模块查看

register 适用复杂的例子

如判断前一个命令的执行结果去处理后面的操作,这时候就需要register模块来保存前一个命令的返回状态,在后面进行调用

[root@ansible ~]# vim load.yml
---
- hosts: databases
  remote_user: root
  tasks:
    - shell: uptime | awk '{printf("%.2f",$(NF-2))}'
      register: result
    - service:
        name: httpd
        state: stopped
      when: result.stdout | float > 0.7
[root@localhost ~]# uptime                          //db2主机目前负载已经超过0.7
 16:58:23 up  6:20,  1 user,  load average: 1.49, 0.93, 0.41
[root@ansible ~]# ansible-playbook load.yml 
...
TASK [service] *****************************************************************
skipping: [db1]
changed: [db2]
...
[root@localhost ~]# netstat -pntul | grep 80          //检测,http服务已经关闭

with_items 标准循环

 可以用于迭代一个列表或字典,通过{{ item }}获取每次迭代的值

tags 给指定的任务定义一个调用标识

格式:name:NAME

            module:arguments

             tags:TAG_ID

 调用方式:-t  TAGS,-- tags=TAGS

                     --skip-tags=SKIP_TAGS

                     --start-at-task=START_AT

调试 debug

检测语法:ansible-playbook   --syntax-check      playbook.yaml

测试运行:ansible-playbook   -C  playbook.yaml

显示受到影响的主机   --list-hosts

显示工作的task   --list-tasks

显示将要运行的tag  --list-tags

猜你喜欢

转载自blog.csdn.net/weixin_43800781/article/details/86568746