1. Ansible自动化运维工具

“无主从无架构,开箱即用,用完即走”

  • 简介

    • ansible是新出现的自动化运维工具,基于Python开发,集合了众多运维工具(puppet、cfengine、chef、func、 fabric)的优点,实现了批量系统配置、批量程序部署、批量运行命令等功能。ansible是基于模块工作的,本身没有批量部署的能力。真正具有批量部署的是ansible所运行的模块,ansible只是提供一种框架。
    • 基于SSH协议实现远程管理,不需要代理
  • 运维自动化应用场景

    • 操作系统预备自动化
    • 配置自动化
    • 监控自动化

      • 系统与应用监控
      • 日志监控
    • 代码持续集成与持续发布自动化
  • 核心组件
  1. Ansible:核心组件
  2. Modules:包括Ansible自带的核心模块及自定义模块
  3. Plugins:完成模块功能的补充,包括连接插件、邮箱插件
  4. Playbooks:剧本;定义Ansible多任务配置文件,完成对主机批量部署操作
  5. Inventory:定义Ansible管理主机的清单 /etc/ansibe/hosts
  6. Connection Plugins:负责和被监控端实现通信
  • 主机清单:

    • 仅对主机清单内的主机列表进行操作
    • 实现主机分组
  • 特点
  1. 1. 不需要在被管控主机上安装任何客户端
  2. 2. 无服务器端,使用时直接运行命令即可
  3. 3. 基于模块工作,可使用任意语言开发模块
  4. 4. 使用yaml语言定制剧本playbook,支持playbook编排任务
  5. 5. 安全,基于SSH工作
  6. 6. 可实现多级指挥
  7. 7. 幂等性:一种操作重复多次结果相同
  8. 8.模块化:调用特定模块,完成特定任务

安装和配置

  1. #需先安装epel扩展源
  2. [root@localhost ~]# yum -y install epel-release
  3. [root@localhost ~]# yum -y install ansible
  4. [root@localhost ~]# ansibl --version #检测版本
  • 常用命令集
  1. [root@localhost ~]# rpm ql ansible | grep bin
  2. /usr/bin/ansible #主程序
  3. /usr/bin/ansibleconfig
  4. /usr/bin/ansibleconnection
  5. /usr/bin/ansibleconsole
  6. /usr/bin/ansibledoc #查看配置文档
  7. /usr/bin/ansiblegalaxy
  8. /usr/bin/ansibleinventory
  9. /usr/bin/ansibleplaybook #定制自动化任务
  10. /usr/bin/ansiblepull
  11. /usr/bin/ansiblevault #文件加密工具
  12. ######
  13. ansible:临时命令执行工具,常用于临时命令的执行
  14. ansible-doc:Ansible模块功能查看工具
  15. ansible-playbook:Ansible定制自动化的任务集编排工具

配置文件

  1. ansible.cfg:主配置文件
  2. hosts:主机清单
  3. roles:角色目录
  4. [defaults]
  5. # some basic default values...
  6. #inventory = /etc/ansible/hosts 主机列表配置文件
  7. #library = /usr/share/my_modules/ 库文件存放位置
  8. #module_utils = /usr/share/my_module_utils/
  9. #remote_tmp = ~/.ansible/tmp 生成的临时py命令文件存放在远程主机的目录
  10. #local_tmp = ~/.ansible/tmp 本机的临时命令执行目录
  11. #forks = 5 默认的并发数
  12. #poll_interval = 15 默认的线程池
  13. #sudo_user = root 默认sudo用户
  14. #ask_sudo_pass = True 在执行sudo之前是否询问sudo密码.默认为False
  15. #ask_pass = True 每次执行ansible命令是否询问ssh密码,默认为False
  16. #transport = smart
  17. #remote_port = 22
  18. #module_lang = C
  19. #module_set_locale = False
  20. #host_ key_checking = False #检查对应服务器的host_ key ,建议取消注释。这样的话控制其他主机相连的时候就不用先ssh连一次
  21. #log_path=/var/log/ansible.log #日志文件,建议开启

主机清单

1.直接指明主机地址或主机名(需要能够解析)
2.定义一个主机组,调用的时候指定该组即可

  1. #存储位置
  2. /etc/ansible/hosts
  3. #定义方式
  4. [root@localhost ~]# vi /etc/ansible/hosts
  5. ...
  6. 主机ip地址
  7. 主机名
  8. [server1]
  9. www.salted1.com
  10. www.salted2.com
  11. [server2]
  12. www.edison1.com
  13. www.edison2.com
  14. [webserver]
  15. 192.168.244.[101:103]
  • ansible系列命令
  1. [root@node1 ~]# ansible doc #显示模块帮助
  2. -a #显示所有模块的文档,不建议使用
  3. -l --list #列出可用模块
  4. -s #显示指定模块的playbook片段
  5. [root@node1 ~]# ansible <host-patern> [-m module_name] [-a args]
  6. --version #显示版本
  7. -m #指定模块,默认为command
  8. --list #显示主机列表
  9. -k,--ask-pass #提示输入ssh连接密码
  10. -K,--ask-become-pass #提示输入sudo时的口令
  11. -C ##检查,并不执行
  12. -T, --timeout = TIMEOUT #执行命令的超时时间,默认10s
  13. -u, --user = REMOTE _USER #指定远程执行的用户
  14. -b, --become #代替旧版的sudo切换
  • ansible的Host-pattern
  1. # 匹配主机列表
  2. all:表示主机清单中所有主机
  3. [root@node1 ~]# ansible all -m p
  4. 通配符匹配
  5. [root@node1 ~]# ansible "*" -m ping
  6. [root@node1 ~]# ansible 192.168.244.* -m ping
  7. [root@node1 ~]# ansible "*ver" -m ping
  • 命令执行过程
  1. 加载自己的配置文件 默认/etc/ansible/ansible.cfg
  2. 加载自己对应的模块文件,如command
  3. 通过ansible将模块或命令生成对应的临时py文件,并将该文件传输至远程服务器的对应执行用户的家目录的 ~/.ansible/tmp/XXX/XXX.PY文件。
  4. 给文件+x执行
  5. 执行并返回结果
  6. 删除临时py文件,sleep 0 退出
  • 执行状态

    • 绿色:执行成功并且不需要做改变的操作
    • 黄色:执行成功并且对目标主机做变更
    • 红色:执行失败

常用模块

  • ping :检测主机间连通性
  1. [root@localhost ~]# ansible 192.168.244.135 -m ping
  2. 192.168.244.135 | SUCCESS => {
  3. "ansible_facts": {
  4. "discovered_interpreter_python": "/usr/bin/python"
  5. },
  6. "changed": false,
  7. "ping": "pong"
  8. }
  • command:默认模块可忽略

    • 此命令不支持$、< >、|、;、&等符号,用shell模块
  1. [root@localhost ~]# ansible 192.168.244.135 -m command -a "ls -l /data"
  2. [root@localhost ~]# ansible 192.168.244.135 -a "useradd lisi"
  • shell:和command相似,用shell执行命令
  1. [root@localhost ~]# ansible 192.168.244.135 -m shell -a "echo 123456 | passwd --stdin lisi"
  2. #调用bash执行命令,类似cat /etc/passwd | awk -F '{print $1,$2}' &> /tmp/data 这些复杂命令,即使使用shell也可能失败。解决办法:写到脚本里面然后copy到远程再执行
  • script:运行脚本
  1. -m script -a 'PATH/To/script_file'
  2. [root@localhost ~]# ansible 192.168.244.135 -m script -a '/root/test1.sh'
  • copy:从服务器复制文件到远程客户端
  1. [root@localhost ~]# ansible all -m copy -a 'src=/etc/hosts dest=/etc/hosts '
  2. 选项
  3. owner = #用户
  4. mode = #权限
  5. backup = yes/no #如果目标文件存在,默认覆盖然后备份
  6. 利用内容直接生成目标文件
  7. [root@localhost ~]# ansible all -m copy -a 'content="test content\n" dest=/data/test1.txt '
  • fetch:从客户端取文件至服务端,与copy相反,目录可先tar再获取

    • 只能抓一个文件
  1. [root@localhost ~]# ansible all -m fetch -a "src=/root/test1.sh dest=/data/scripts"
  2. 打包多个文件
  3. [root@localhost ~]# ansible all -m shell -a "tar jcf log.tar.xz /var/log/*.log"
  4. [root@localhost ~]# ansible all -m fetch -a "src=/root/log.tar.xz dest=/data"
  • file:对文件操作,设置文件属性
  1. [root@localhost ~]# ansible all -m file -a "path=/root/test1.sh owner=lisi mode=777"
  2. 创建软链接文件
  3. [root@localhost ~]# ansible all -m file -a "src=/data/test1 dest=/data/test1.link state=link"
  4. state:属性(link软链接)
  5. 创建空文件
  6. [root@localhost ~]# ansible all -m file -a "name=/data/test2 state=touch"
  7. 删除文件
  8. [root@localhost ~]# ansible all -m file -a "name=/data/test1 state=absent"
  9. 创建目录
  10. [root@localhost ~]# ansible all -m file -a "name=/data/dir1 state=directory"
  11. 删除目录
  12. [root@localhost ~]# ansible all -m file -a "name=/data/dir1 state=absent"
  • hostname:修改主机名

    • 但是/etc/hosts的主机名没修改成功
  1. [root@localhost ~]# ansible 192.168.244.101 -m hostname -a "name=node1"
  • cron:计划任务

    • 支持分时日月周
  1. 创建任务
  2. #每周1,3,5的每分钟警告一次
  3. [root@localhost ~]# ansible all -m cron -a "minute=* weekday=1,3,5 job='/usr/bin/wall FBI warning' name=warning "
  4. 删除任务
  5. [root@localhost ~]# ansible all -m cron -a "job='/usr/bin/wall FBI warning' name=warning state=absent"
  6. 注释任务
  7. [root@localhost ~]# ansible all -m cron -a "disabled=yes/True job='usr/bin/wall FBI warning' name=warning"
  8. 取消注释
  9. [root@localhost ~]# ansible all -m cron -a "disabled=no/False job='usr/bin/wall FBI warning' name=warning"
  • yum:管理包
  1. [root@localhost ~]# ansible 192.168.244.101 -m yum -a "name=httpd update_cache=yes"
  • service:管理服务
  1. [root@localhost ~]# ansible all -m service -a "name=httpd state=started enabled=yes "
  2. enabled:开机自启动
  3. [root@localhost ~]# ansible all -m service -a "name=httpd state=stopped enabled=no "
  • user:管理用户
  1. 创建用户
  2. [root@localhost ~]# ansible 192.168.244.101 -m user -a "name=nginx shell=/sbin/nologin system=yes home=/var/nginx groups=root,bin uid=80 comment='nginx server'"
  3. system:是否为系统用户
  4. shell:指定shell
  5. home:指定家目录
  6. group:主组
  7. groups:指定附家组
  8. comment:注释
  9. 删除用户
  10. [root@localhost ~]# ansible 192.168.244.101 -m user -a"name=nginx state=absent remove=yes"
  11. remove:删除用户的时候删除家目录数据
  • group:管理组
  1. 创建组
  2. [root@localhost ~]# ansible 192.168.244.101 -m group -a "name=testgroup system=yes"
  3. 删除组
  4. [root@localhost ~]# ansible 192.168.244.101 -m group -a "name=testgroup state=absent"

ansible系列命令

  1. - 列出所有已安装的galaxy
  2. ansible-galaxy list
  3. - 安装galaxy
  4. ansible-galaxy install geerlingguy.redis
  5. - 删除galaxy
  6. ansible-galaxy remove geerlingguy.redis
  • ansible-pull

  • 推送命令至远程主机,效率无限提升,对运维要求较高

  • ansible-playbook剧本

  • ansible-vault

    • 管理,加密,解密yml文件
加密yml文件(加密后不能直接运行,得先解密)
[root@localhost ~]#ansible-vault encrypt hello.yml

解密
[root@localhost ~]#ansible-vault decrypt hello.yml

查看加密文件
[root@localhost ~]#ansible-vault view hello.yml

编辑加密文件
[root@localhost ~]#ansible-vault edit hello.yml

修改口令
[root@localhost ~]#ansible-vault rekey hello.yml

创建新的加密yml文件
[root@localhost ~]#ansible-vault rekey new.yml
  • ansible-console

    • 交互式执行命令
    • 支持tab
root@test (2)[f:10] $
#执行用户@当前操作的主机组(当前组的主机数量)[f:并发数]$
#设置并发数: forks n 例如: forks 10
#切换组:cd 主机组例如:cd web
#列出当前组主机列表: list
#列出所有的内置命令: ?或help

示例:
root@all (2)[f:5]$ list
root@all (2)[f:5]$ cd webservers
root@appsrvs (2)[f:5]$ forks 10
root@appsrvs (2)[f:5]$ list
root@appsrvs (2)[f:5]$ yum name=httpd state=present
root@appsrvs (2)[f:5]$ service name=httpd state=started

2. playbook剧本

  • 由一个或多个”paly”组成的列表,采用YAML语言编写
  • 相当于脚本,ansible单条命令的集合(模块的集合)
  • 主要功能:将事先归并为一组的主机装扮成task定义好的角色,所谓task不过是调用ansible的一个个module
  • ansible playbook执行过程:

    • 将以编排好的任务集写进playbook
    • 通过ansible-playbook命令分拆任务集逐条执行ansible命令,按预定规则逐条执行

格式:

[root@localhost ~]# vi hello.yml
---
- hosts:webservers
  remote_user: root  #指定远程登录用户

  tasks:
      - name: hello
        command: hostname

[root@localhost ~]#ansible-palybook hello.yml

运行palybook

[root@localhost ~]#ansible-palybook hello.yml
-C --check:只检测,不真正执行
--list-hosts:列出运行任务的主机
--limit:主机列表 只针对主机列表中的主机执行
-v:显示过程 -vv,-vvv 更详细

[root@localhost ~]# ansible-playbook -C file.yml 只检测
[root@localhost ~]# ansible-playbook file.yml 
[root@localhost ~]# ansible-playbook file.yml --limit websrvs

YAML语言

  • YAML是一个可读性高的用来表达资料序列的格式。YAML参考了其他多种语言,包括: XML、C语言、Python、 Perl以及电子邮件格式RFC2822等。 Clark Evans在2001年在首次发表了这种语言,另外Ingy dot Net与Oren Ben-Kiki也是这语言的共同设计者
    YAML Ain’t Markup Language ,即YAML不是XML。不过,在开发的这种语言时, YAML的意思其实是: “Yet Another Markup Language” (仍是一种标记语言)

  • 特性:

    • YAML的可读性好
    • YAML和脚本语言的交互性好
    • YAML使用实现语言的数据类型
    • YAML有一个致的信息模型
    • YAML易于实现
    • YAML可以基于流来处理
    • YAML表达能力强,扩展性好
  • 更多的内容及规范参见http://www.yaml.org

  • 语法简介

1.在单一档案中,可用连续三个连字号(-)区分多个档案。 另外,还有选择性的连续三个点号( ... )用来表示档案结尾
2.次行开始正常写Playbook的内容, -般建议写明该Playbook的功能
3.使用#号注释代码
4.缩进必须是统一的 ,不能空格和tab混用
5.缩进的级别也必须是一致的,同样的缩进代表同样的级别,程序判别配置的级别是通过缩进结合换行来实现的
6.YAML文件内容和Linux系统大小写判断方式保持一致,是区别大小写的, k/v的值均需大小写敏感
7.k/v的值可同行写也可换行写。同行使用:分隔
8.v可是个字符串,也可是另一一个列表
9.一个完整的代码块功能需最少元素需包括name: task
10.一个name只能包括一个task
11.YAML文件扩展名通常为yml或yaml
list:列表,其所有元素均使用"-"打头

dictionar:字典,通常由多个key与value构成

#YAML的语法和其他高阶语言类似,并且可以简单表达清单、散列表、标量等数据结构。其结构( Structure )通过空格来展示,序列( Sequence )里的项用"-"来代表, Map里的键值对用":"分隔

核心元素

  • hosts:执行的远程主机列表

    • playbook中的每一个play的目的都是为了让某个或某些主机以某个指定的用户
      身份执行任务。hosts用于指定要执行指定任务的主机,须事先定义在主机清
      单中
  • tasks:任务列表

    • 注意: shell和command模块后面跟命令,而非key=value
    • 某任务的状态在运行后为changed时,可通过”notify” 通知给相应的
      handlers
    • 任务可以通过” tags “打标签,而后可在ansible-playbook命令.上使用-t指定进
      行调用 ```bash

      如果命令或脚本的退出码不为零

      解决1: tasks: -name: 忽略错误信息 shell: command || /bin/true

解决2: tasks: -name: 忽略错误信息 shell: command ignore_errors: True


- 
Varniables内置变量或自定义变量在playbook中调用

- 
Templates模板,可替换模板文件中的变量并实现一些简单逻辑的文件

- 
Handlers和notity结合使用,由特定条件触发的操作,满足条件方才执行,否<br />
则不执行

- 
tags标签指定某条任务执行,用于选择运行playbook中的部分代码。ansible<br />
具有幂等性,因此会自动跳过没有变化的部分,即便如此,有些代码为测试其<br />
确实没有发生变化的时间依然会非常地长。此时,如果确信其没有变化,就可<br />
以通过tags跳过此些代码片断<br />
ansible-playbook -t tagsname useradd.yml


```bash
[root@ansible ~]# vim test.yml
---
- hosts: websrvs 
  remote_ user: root
  tasks :
    - name: create newfile
      file: name=/ data/newfile state=touch
    - name: create new user 
      user: name-test2 system=yes shell=/ sbin/nologin
    - name: instal1 httpd service
      yum: name=httpd
    - name: copy index.html
      copy: src=/var/www/htm1/index.html dest=/var/www/htm1/
    - name: copy test.html
      copy: src=/var/www/htm1/test.html dest=/var/www/htm1/
    - name: start service
      service: name=httpd state=started enabled=yes

##复制文件一次只能复制一个

[root@ansible ~]# ansible-playbook  -C test.yml
[root@ansible ~]# ansible-playbook   test.yml

palybook案例

初始化服务端和客户端

  • 一台ansible主机,三台受控机node1,node2,node3

安装ansible

[root@ansible ~]# yum install epel-release ##我的主机已经都配备拓展源,不需要下载
[root@ansible ~]# yum install -y ansible

设置主机清单

[root@ansible ~]# vim /etc/ansible/hosts 

[webserver]
192.168.244.135
192.168.244.139
192.168.244.141

在ansible管理节点(即安装ansible的节点)上添加目标节点(即需要管理的节点)的ssh认证信息

[root@ansible ~]# ssh-keygen
[root@ansible ~]# ssh-copy-id 192.168.244.135
[root@ansible ~]# ssh-copy-id 192.168.244.139
[root@ansible ~]# ssh-copy-id 192.168.244.141

修改ansible主配置文件,使得第一次连接不用检查密钥

开启日志

[root@ansible ~]# vim /etc/ansible/ansible.cfg 
host_key_checking = False

[root@ansible ~]# vim /etc/ansible/ansible.cfg 
log_path=/var/log/ansible.log #日志文件,建议开启

验证连通性

[root@ansible ~]# ansible all -m ping

编写剧本

[root@ansible ~]# vim test.yml
---
- hosts: webserver
  remote_user: root

  tasks:
    - name: create new directory
      file: name=/data state=directory
    - name: create new file
      file: name=/data/test1 state=touch
    - name: create new user
      user: name=lisi shell=/sbin/nologin system=yes
    - name: install vsftpd
      yum: name=vsftpd 
    - name: start service
      service: name=vsftpd state=started enabled=yes

测试并执行

[root@ansible ~]# ansible-playbook  -C test.yml
[root@ansible ~]# ansible-playbook   test.yml

3. 企业级使用playbook

handlers和notify

  • handlers和notify结合使用触发条件
  • handlers(触发器)

    • 是task列表,只有当关注的资源发生变化时,才会采取相应的操作
  • notify

    • 与handlers配套使用。调用handlers中的定义的操作
    • 此action可用于在每个paly的最后被触发,可以避免多次有改变发生时每次都执行指定的操作,仅在所有的变化发生完成后一次性地执行指定操作

触发一个

案例

  • 给多台web服务器安装apache服务并将主控机配置文件复制过去
  • 主控机配置文件修改过后发送给被控机并让他重启httpd服务使得他配置文件生效
[root@ansible ~]# vim httpd.yml
---
- hosts: webserver
  remote_user: root

  tasks:
    - name: install httpd package
        yum: name=httpd 
    - name: copy config file
        file: src=/etc/httpd/httpd.conf dest=/etc/httpd/ backup=yes
        notify: restart service
    - name: service start
        service: name=httpd state=started enabled=yes

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

触发多个

[root@ansible ~]# vim httpd.yml
---
- host: webserver
  remote_user: root

  tasks:
    - name: add group nginx
      group: name=nginx 
      tags: group
    - name: add user nginx
      user: name=nginx group=nginx
    - name: install nginx
      yum: name=nginx enabled=yes
    - name: copy conf
      file: src=/root/conf.txt dest=/etc/nginx/nginx.conf
      notify:
        - restart nginx
        - check nginx process
  handlers:
    - name: restart nginx
      service: name=nginx state=restarted 
    - name: check nginx process 
      shell: killall -0 nginx > /tmp/nginx.log

tags

  • 标签,可以使我们执行playbook里面指定标签里的内容
  • 多个动作可以使用相同的标签
[root@ansible ~]# vim tags.yml
---
- hosts: webserver
  remote_user: root

  tasks:
    - name: install httpd package
      yum: name=httpd
      tags: httpdservice
    - name: copy config file
      flie: src=/etc/httpd/httpd.conf dest=/etc/httpd/httpd.conf
      notify: restart httpd service
    - name: start service
      service: name=httpd state=started enabled=yes
      tags: httpdservice
 handlers:
    - name: restart httpd service
      service: name=httpd state=restarted

[root@ansible ~]# ansible-palybook -t httpdservice httpd.yml

变量的使用

  • 变量名:仅由字母,数字和下划线组成,只能以数字开头

  • 变量来源

    • ansible setup facts 远程主机的所有变量都可以直接调用

    • 在/etc/ansible/hosts定义

      • 普通变量:主机组中的主机单独定义,优先级高于公共变量
      • 公共变量(组变量):针对主机分组中的所有主机定义统一变量
    • 通过命令行指定变量:ansible-palybook -e varname=value

    • 在palybook中定义 ``` vars:

  • var1: value1
  • var2: value2 ```
  • 在role中定义
  • 命令优先级:命令行变量>普通变量>主机变量

  • 独立的YML文件来专门存放定义的变量,便于维护

##建立存放变量文件
[root@ansible ~]# vim vars.yml
var1: httpd
var2: vsftpd

##调用变量文件
[root@ansible ~]# vim test_var.yml
---
- hosts: werbserver
  remote_user: root
  vars_files:
    - vars.yml

  tasks:
    - name: install package
      yum: name={{ var1 }}
    - name: install package
      yum: name={{ var2 }}
  • 查看远程主机变量
[root@ansible ~]# ansible webserver -m setup -a "filter=ansible_hostname"
filter: 过滤工具,支持通配符

[root@ansible ~]# anisble webserver -m setup -a "filter=*address*"

#将远程变量写进playbook
[root@ansible ansible]# ansible all -m setup -a "filter=ansible_fqdn"
[root@ansible ansible]# vim vars.yml 
---
- hosts: webserver
  remote_user: root

  tasks:
    - name: copy file
      file: name=/root/{{ ansible_fqdn }}.log state=touch
[root@ansible ansible]# ansible-playbook  vars.yml

[root@node1 ~]# ls
anaconda-ks.cfg  node1.log
[root@node2 ~]# 
[root@node2 ~]# ls
anaconda-ks.cfg  auth.smb  node2.log
  • 在playbook里设置变量
#设置单个变量
[root@ansible ~]# vim test.yml
---
- hosts: werbserver
  remote_user: root

  tasks:
    - name: install package
      yum: name={{ varname }}
    - name: start service
      service: name={{ varname }} state=started

[root@ansible ~]# anisble-palybook webserver -e 'varname=httpd' test.yml

#设置多个变量
[root@ansible ~]# vim test1.yml
---
- hosts: webserver
  remote_user: root

  tasks:
    - name: install package
      yum: name={{ varname1 }}
    - name: install package
      yum: name={{ varname2 }}

[root@ansible ~]# anisble-palybook webserver -e 'varname1=httpd varname2=vsftpd' test.yml   

#设置变量组
[root@ansible ~]# vim test2.yml
---
- hosts: werbserver
  remote_user: root

  vars:
    - varname1: httpd
    - varname2: vsftpd

  tasks:
    - name: install package
      yum: name={{ varname1 }}
    - name: install package
      yum: name={{ varname2 }}

[root@ansible ~]# anisble-palybook webserver test.yml
  • 设置普通变量和公共变量
#普通变量
[root@ansible ~]# vim /etc/ansible/hosts
[webserver]
192.168.244.100 http_port=81
192.168.244.101 http_port=82

[root@ansible ~]# vim test.yml
---
- hosts:webserver
  remote_user: root

  tasks:
    - name: set hostname
      hostname: name=www{{ http_port }}.com

#公共变量
[webserver]
192.168.244.100 http_port=81
192.168.244.101 http_port=82
[werserver:vars]
nodenmae=www
domainname=edu.com

[root@ansible ~]# vim test.yml
---
- hosts:webserver
  remote_user: root

  tasks:
    - name: set hostname
      hostname: name={{ nodename }}{{ http_port }}.{{ domainname }}

4. template管理nginx配置文件

template 模板

  • 文本文件(使用模板编程语言编写)
  • jinjia2语言

    • 数字(int,float)列表,元组,字典,布尔型
  • 算数运算
  • 比较操作
  • 逻辑运算
  • 流程控制

特点:

  • 只能用于playbook,不能作为单条命令被调用
  • 根据被控机当前系统状态来生成对应配置

案例1:

woker进程数量默认是自动,会根据主机cpu数量生成不同的worker进程数。考虑到客户机的cpu数量不一样,如果单纯的用copy模块将服务器的配置文件复制过去是行不通的,我们要用到template模块。

根据不同被控机的cpu数量产生不同的worker数

  • 服务器下载nginx
[root@ansible ansible]# yum install -y nginx
[root@ansible ansible]# systemct1 start nginx
[root@ansible ansible]#  ps aux | grep nginx
  • 服务器创建文件夹(建议与playbook平级)
[root@ansible ansible]# mkdir templates
[root@ansible ansible]# ls
ansible.cfg  data  hosts  roles  templates  vars.yml
  • 将nginx配置文件复制到templates目录下
[root@ansible ansible]# cp /etc/nginx/nginx.conf templates/nginx.conf.j2
##注意后缀名要改为j2
  • 编写palybook并执行
#node1节点cpu数量为1
#node2节点cpu数量为2
[root@ansible ansible]# cat test_template.yml
---
- hosts: webserver
  remote_user: root

  tasks:
    - name: install package
      yum: name=nginx
    - name: copy template
      template: src=nginx.conf.j2  dest=/etc/nginx/nginx.conf
      notify: restart service
    - name: start service
      service: name=nginx state=started enabled=yes
      tags: startservice

  handlers:
    - name: restart service
      service: name=nginx state=restarted
[root@ansible ansible]# ansible-playbook  test_template.yml

案例2

都是nginx服务,不同主机nginx端口号不同

  • node1:81

  • node2:82

  • 定义各自的端口变量(主机变量)

[webserver]
192.168.244.141 http_port=8080
192.168.244.135 http_port=9090
  • 修改templates下nginx配置文件
[root@ansible ansible]# vim templates/nginx.conf.j2 
....
   server {
        listen       {{ http_port }} default_server;
        listen       [::]:{{ http_port }} default_server;

....
  • 执行playbook
[root@ansible ansible]# ansible-playbook  test_template.yml
  • 查看不同节点的http端口号

when

  • 条件测试条件测试:如果需要根据变量、facts或此前任务的执行结果来做为某task执行与
    否的前提时要用到条件测试,通过when语句实现,在task中使用, jinja2的语法
    格式
  • when语句

    • 在task后添加when子句即可使用条件测试; when语句支持Jinja2表达式语法
示例:
tasks:
name: " shutdown RedHat flavored systems"
command: /sbin/shutdown -h now
when: ansible_ os_ family == "RedHat"

迭代:with_items

  • 当有需要重复性执行的任务时,可以使用迭代机制
  • 对迭代项的引用,固定变量名为{{ item }}
  • 要在task中使用with_ items给定要迭代的元素列表
  • 列表格式:
    字符串
    字典

利用迭代创建多个文件

[root@ansible ansible]# vim test_item.yml
---
- host: webserver
  remote_user: root

  tasks:
    - name: create some files
      file: name=/data/{{ item }} state=touch
      with_items:
        - file1
        - file2
        - file3

迭代加上when判断

[root@ansible ansible]# vim test_item.yml

---
- host: webserver
  remote_user: root

  tasks:
    - name: create some files
      file: name=/data/{{ item }} state=touch
      when:ansible_distribution_major_version == '7'
      with_items:
        - file1
        - file2
        - file3

迭代安装多个包

[root@ansible ansible]# vim test_item.yml

---
- host: webserver
  remote_user: root

  tasks:
    - name: install package
      yum: name={{ item }}
      with_items:
        - htop
        - sl
        - hping3

迭代嵌套子变量

迭代创建组创建用户,添加到不同的组中

[root@localhost ansible]# cat test_item.yml 
---
- hosts: webserver
  remote_user: root

  tasks:
    - name: create some group
      group: name={{ item }}
      with_items:
        - g1
        - g2
        - g3
    - name: create some user
      user: name={{ item.name }} group={{ item.group }}
      with_items:
        - {name: "user1" , group:  "g1"}
        - {name: "user2" , group:  "g2"}
        - {name: "user3" , group:  "g3"}

for循环、if语句

for循环 实现创建一个文件里面循环生成配置信息

  • yml文件
---
- hosts: webserver
  remote_user: root
  vars:
    ports:
      - 81
      - 82
      - 83

  tasks:
    - name: copy template conf
      template: src=for1.conf.j2 dest=/data/for1.conf

##或者
---
- hosts: webserver
  remote_user: root
  vars:
    ports:
      - listen_port: 81
      - listen_port: 82
      - listen_port: 83

  tasks:
    - name: copy template conf
      template: src=for1.conf.j2 dest=/data/for1.conf
  • 实现for循环的文件
[root@ansible templates]# cat for1.conf.j2
{% for p in ports% }
server{
        listen {{ p }}
}
{% endfor %}

##或者
{% for p in ports  %}
server{
        listen {{ p.listen_port }}
}
{% endfor %}
  • 多个键值对.yml
---
- hosts: webserver
  remote_user: root
  vars:
    ports:
      - web1:
        port:81
        name:web1.com
      - web2:
        port:82
        name:web2.com
      - web3:
        port:83
        name:web3.com

  tasks:
    - name: copy template conf
      template: src=for1.conf.j2 dest=/data/for1.conf
  • 实现for循环的文件
{% for p in ports  %}
server{
        listen {{ p.port }}
        servername {{ p.name }}
}
{% endfor %}

if嵌套在for循环里面用于条件判断

if条件:变量是否已定义

  • yml文件
只有web2变量的name定义
---
- hosts: webserver
  remote_user: root
  vars:
    ports:
      - web1:
        port:81
        #name:web1.com   
      - web2:
        port:82
        name:web2.com
      - web3:
        port:83
        #name:web3.com

  tasks:
    - name: copy template conf
      template: src=for1.conf.j2 dest=/data/for1.conf
{% for p in ports  %}
server{
        listen {{ p.port }}
{% if p.name is defined %}
        servername {{ p.name }}
{% endif %}
}
{% endfor %}

5. Roles

  • 简单来讲,roles就是通过分别将变量、文件、任务、模板及handlers放置于单独的目录中,并可以便捷地include去调用它们的一种机制。角色一般用于基于主机构建服务的场景中,但也可以是用于构建守护进程等场景中
  • 复杂场景:建议使用roles ,代码复用度高
  • 特点:

    • 变更指定主机或主机组时,建议使用roles
    • 如命名不规范维护和传承成本大
    • 某些功能需多个Playbook ,通过Include即可实现
  • 把不同的内容,分门别类放到不同的文件夹中,需要的时候再逐个调用,方便重复调用

roles目录结构及各自作用

 playbook.yml roles
 roles/
     nginx/
     mysql/
         project/  
         tasks/  
         files/  
         vars/  
         default/  
         templates/  
         handlers/
  • Roles各目录作用

    • /roles/project/ :项目名称,有以下子目录
    • files/ :存放由copy或script模块等调用的文件
    • templates/ : template模块查找所需要模板文件的目录
    • tasks/ :定义task,role的基本元素,至少应该包含一个名为main.yml的文件;其它的文
      件需要在此文件中通过include进行包含
    • handlers/ :至少应该包含一个名为main.ym的文件;其它的文件需要在此文件中通过
      include进行包含
    • vars/ :定义变量,至少应该包含一个名为main.yml的文件;其它的文件需要在此文件
      中通过include进行包含
    • meta/ :定义当前角色的特殊设定及其依赖关系,至少应该包含一 个名为main.yml的文
      件,其它文件需在此文件中通过include进行包含
    • default/ :设定默认变量时使用此目录中的main.yml文件

先创建一个roles目录(在/etc/ansible/roles也可以)

[root@ansible ansible]# tree roles/
roles/
├── httpd
├── memcache
├── mysql
└── nginx

roles部署一个nginx服务

  • 创建nginx组和用户

  • 安装nginx包

  • 将主机配置文件复制过去

  • 开启服务

  • 重启服务(handler)

  • 创建nginx目录下所需要的文件夹

[root@ansible ansible]# tree
.
├── nginx_roles.yml
└── roles
    ├── httpd
    ├── memcache
    ├── mysql
    └── nginx
        ├── handler
        ├── tasks 
        └── template
  • tasks下创建任务文件
[root@ansible ansible]# tree
├── tasks
   ├── group.yml
   ├── main.yml
   ├── service.yml
   ├── templ.yml
   ├── user.yml
   └── yum.yml

#创建组
[root@ansible tasks]# cat group.yml 
- name: create group
  group: name=nginx gid=80

#创建用户
[root@ansible tasks]# cat user.yml 
- name: create user
  user: name=nginx uid=80 group=nginx system=yes shell=/sbin/nologin


#安装包
[root@ansible tasks]# cat yum.yml 
- name: installl package
  yum: name=nginx

#复制配置文件
[root@ansible tasks]# cat templ.yml 
- name: copy conf
  template: src=template/nginx.conf.j2 dest=/etc/nginx/nginx.conf

#开启服务
[root@ansible tasks]# cat service.yml 
- name: start service
  service: name=nginx state=started enabled=yes

#创建一个main文件来指定任务顺序
[root@ansible tasks]# cat main.yml 
- include: group.yml
- include: user.yml
- include: yum.yml
- include: templ.yml
- include: service.yml
  • template下创建要传过去的文件
[root@ansible template]# ls
nginx.conf.j2
  • 编写playbook,记得要跟roles同级
[root@ansible ansible]# ls
nginx_roles.yml  roles

[root@ansible ansible]# cat nginx_roles.yml 
- hosts: webserver
  remote_user: root
  roles:
    - role: nginx
  • 执行角色
[root@ansible ansible]# ansible-playbook -C nginx_roles.yml
[root@ansible ansible]# ansible-playbook  nginx_roles.yml

roles部署apache服务

  • 定义httpd角色

    • 创建apache用户

    • 复制apache配置文件过去

  • 创建相关目录和文件

[root@localhost ansible]# tree
.
├── httpd_test.yml
└── roles
     ├── httpd
        ├── files
            └── httpd.conf
        └── tasks
            ├── copyfile.yml
            ├── main.yml
            └── user.yml

#创建apache用户
[root@ansible tasks]# cat user.yml 
- name: create user
  user: name=apache system=yes shell=/sbin/nologin

#复制apache配置文件过去
[root@ansible tasks]# cat copyfile.yml 
- name: copy files
  copy: src=httpd.conf dest=/data/ owner=apache

#main
[root@ansible tasks]# cat main.yml 
- include: user.yml
- include: copyfile.yml

6. roles企业高级用法

调用多个角色

[root@ansible ansible]# tree
.
├── httpd_test.yml
├── nginx_roles.yml
└── some_role.yml
├── roles
│   ├── httpd
│   │   ├── files
│   │   │   └── httpd.conf
│   │   └── tasks
│   │       ├── copyfile.yml
│   │       ├── main.yml
│   │       └── user.yml
│   ├── memcache
│   ├── mysql
│   └── nginx
│       ├── handler
│       │   └── restart_srvs.yml
│       ├── tasks
│       │   ├── group.yml
│       │   ├── main.yml
│       │   ├── service.yml
│       │   ├── templ.yml
│       │   ├── user.yml
│       │   └── yum.yml
│       └── template
│           └── nginx.conf.j2
[root@ansible ansible]# cp nginx_roles.yml some_role.yml
[root@ansible ansible]# vim some_role.yml 
- hosts: webserver
  remote_user: root
  roles:
    - role: httpd
    - role: nginx

[root@ansible ansible]# ansible-playbook some_role.yml

调用另一个角色的tasks

  • 在nginx角色下调用httpd的某一任务
[root@ansible ansible]# vim roles/nginx/tasks/main.yml 
- include: group.yml
- include: user.yml
- include: yum.yml
- include: templ.yml
- include: service.yml
- include: roles/httpd/tasks/copyfile.yml 
##注意这个yml文件里面文件路径为绝对路径
[root@ansible tasks]# vim copyfile.yml 
- name: copy files
  copy: src=/etc/httpd/conf/httpd.conf dest=/data/ owner=apache

在角色里面定义tags

[root@ansible ansible]# vim some_role.yml 
- hosts: webserver
  remote_user: root
  roles:
    - { role: httpd, tags: ['web','httpd'] }
    - { role: nginx, tags: ['web','nginx'] }
    - { role: app, tags: 'app' }

[root@ansible ansible]#  ansible-palybook -t web some_role.yml
#加上条件判断 
- hosts: webserver
  remote_user: root
  roles:
    - { role: httpd, tags: ['web','httpd'] }
    - { role: nginx, tags: ['web','nginx'], when : ansible_distribution_major_version == '7' }
    - { role: app, tags: 'app' }

终极案例

创建一个角色app,实现部署http服务

[root@ansible ansible]# tree
.
├── app_roles.yml
├── roles
│   ├── app
│   │   ├── files
│   │   │   └── vhosts.conf
│   │   ├── handlers
│   │   │   └── main.yml
│   │   ├── tasks
│   │   │   ├── copyfile.yml
│   │   │   ├── group.yml
│   │   │   ├── main.yml
│   │   │   ├── start.yml
│   │   │   ├── templ.yml
│   │   │   ├── user.yml
│   │   │   └── yum.yml
│   │   ├── templates
│   │   │   └── httpd.conf.j2
│   │   └── vars
│   │       └── main.yml
  • 首先在tasks下创建任务
#创建组
[root@ansible tasks]# cat group.yml 
- name: create group
  group: name=app system=yes 

#创建用户
[root@ansible tasks]# cat user.yml 
- name: create user
  user: name=app group=app shell=/sbin/nologin system=yes

#安装服务
[root@ansible tasks]# cat yum.yml 
- name: install package
  yum: name=httpd 

#复制模板
[root@ansible tasks]# cat templ.yml 
- name: copy conf
  template: src=httpd.conf.j2 dest=/etc/httpd/conf/httpd.conf
  notify: restart service

#复制文件
[root@ansible tasks]# cat copyfile.yml 
- name: copy file
  copy: src=vhosts.conf dest=/data/ owner=app

#开启服务
[root@ansible tasks]# cat start.yml 
- name: start service
  service: name=httpd state=started enabled=yes

#main文件
[root@ansible app]# cat tasks/main.yml 
- include: group.yml
- include: user.yml
- include: yum.yml
- include: templ.yml
- include: copyfile.yml
- include: start.yml
  • 创建handelers下的触发器
[root@ansible handlers]# cat main.yml 
- name: restart service
  service: name=httpd state=restarted
  • 创建变量,放在模板中
[root@ansible vars]# cat main.yml 
username: app
groupname: app
  • 创建模板文件
[root@ansible ansible]# cp /etc/httpd/conf/httpd.conf /root/ansible/roles/app/templates/httpd.conf.j2

#在模板文件里面修改使得带上变量名
。。。
Listen {{ ansible_processor_vcpus*10 }}
User {{ username }}
Group {{ groupname }}
。。。
  • 创建要复制到远程主机的文件
[root@ansible app]# touch files/vhosts.conf
  • 最后指定palybook并运行
[root@ansible ansible]# cat app_roles.yml 
- hosts: webserver
  remote_user: root
  roles:
    - app

[root@ansible ansible]# ansible-playbook -C app_roles.yml 
[root@ansible ansible]# ansible-playbook app_roles.yml