• ansible自动化运维


    在管理机器上安装ansible工具

    yum  install ansible

    将需要管理的主机加入absible,的主机管理清单配置文件/etc/ansible/hosts

    格式:inventory文件遵循INI文件风格,中括号中的字符为组名。 可以将同一个主机同时归并到多个不同的组中;此外,当如若 目标主机使用了非默认的SSH端口,还可以在主机名称之后 使用冒号加端口号来标明

    1)不分组方式 #不推荐这样使用

    192.168.136.5:2222  #冒号后面的2222表示端口号

    192.168.136.6

    2)分组方式

    [web]   # web表示组名,分组以后可以直接通过组名来进行管理组里面的主机

    192.168.136.5

    192.168.136.6

    [opt]

    192.168.136.[7:10]   #这表示范围,从192.168.136.7到192.168.136.10之间的所有主机

    [webservers]

    www1.magedu.com

    www2.magedu.com

    ansible管理主机一般最好是先基于key验证之后管理起来会比较方便

    步骤:

    1)生成公私钥对

    ssh-keygen

    2)将公钥复制到所有主机

    可以编写脚本:完成这一步

    也可以用ssh-copy-id  <主机ip>即可


    ansible 配置文件

    Ansible 配置文件/etc/ansible/ansible.cfg (一般保持默认)

    [defaults]

    #inventory      = /etc/ansible/hosts  # 主机列表配置文件

    #library  = /usr/share/my_modules/ # 库文件存放目录

    #remote_tmp  = $HOME/.ansible/tmp  #临时py命令文件存放在远程主 机目录

    #local_tmp      = $HOME/.ansible/tmp # 本机的临时命令执行目录

    #forks          = 5   # 默认并发数

    #sudo_user      = root  # 默认sudo 用户

    #ask_sudo_pass = True  #每次执行ansible命令是否询问ssh密码

    #ask_pass      = True

    #remote_port    = 22

    #host_key_checking = False  # 检查对应服务器的host_key,建议取消 注释

    #log_path = /var/log/ansible.log   #会把你执行的命令生成日志计入在/var/log/ansible.log文件中,建议把这一项启用起来,


    ansible

    功能介绍

    基于模块化,远程管理主机,并且是基于ssh方式连接主机的方式,安全性高

    格式

    <host-pattern> [-m module_name] [-a args]

    特点

    1)模块化:调用特定的模块,完成特定任务

    2)有Paramiko,PyYAML,Jinja2(模板语言)三个关键模块

    3)支持自定义模块

    4)基于Python语言实现

    5)部署简单,基于python和SSH(默认已安装),agentless

    6)安全,基于OpenSSH

    7)支持playbook编排任务

    8)幂等性:一个任务执行1遍和执行n遍效果一样,不因重复执行带来意外情况

    9)无需代理不依赖PKI(无需ssl)

    10)可使用任何编程语言写模块

    11)YAML格式,编排任务,支持丰富的数据结构

    12)较强大的多层解决方案

    ansable系列命令

    ansible    

    ansible-doc  

    ansible-playbook    

    ansible-vault    

    ansible-console  

    ansible-galaxy  

    ansible-pull

    ansible-doc 查看模

    块帮助用法

    ansible-doc –l  列出所有模块

    ansible-doc ping  查看指定模块帮助用法

    ansible-doc –s  ping 查看指定模块帮助用法

    ansible命令执行过程

    1. 加载自己的配置文件 默认/etc/ansible/ansible.cfg

    2. 加载自己对应的模块文件,如command

    3. 通过ansible将模块或命令生成对应的临时py文件,并将该 文 件传输至远程服务器的对应执行用户 $HOME/.ansible/tmp/ansible-tmp-数字/XXX.PY文件

    4. 给文件+x执行

    5. 执行并返回结果

    6. 删除临时py文件,sleep 0退出

    执行状态:

    绿色:执行成功并且不需要做改变的操作

    黄色:执行成功并且对目标主机做变更

    红色:执行失败

    选项

    说明

    --version

    显示版本

    -m module

    指定模块,默认为command

    -v

    详细过程 –vv  -vvv更详细

    --list-hosts

    显示主机列表,可简写—list

    --ask-pass

    提示连接密码,默认Key验证

    -C,   --check

    检查,并不执行

    -T  --timeout=TIMEOUT

    执行命令的超时时间,默认10s

    -u   --user=REMOTE_USER

    执行远程执行的用户

    -b --become

    代替旧版的sudo 切换

    模块介绍

    模块名成

    功能介绍及常用参数

    ping

    一个简单的测试模块,这个模块总是在成功的接触中返回“乒乓”。这是不合理的

    但是它对于验证登录的能力和可用的python来说是有用的。

    配置。这不是ICMP ping,这只是一个简单的测试模块。对于Windows目标,使用

    (win_ping)模块。

    用法: anisble   主机    -m    ping

    command

    命令的模块接受命令名,后面是空格分隔的参数列表。给定的

    命令将在所有选定的节点上执行。它不会通过shell进行处理,所以变量

    “$ HOME”和操作,比如“<“”、“”>“”,“”|“”、“”;“和”不会起作用(使用shell模块

    如果您需要这些特性)。

    常见选项:

    1),chdir  <切换目录>   格式: ansible  主机组   -m command -a  ‘chdir=/app/   ls  -al   /app ’

    2),creates <判断如果文件不存在就执行后面的命令>  格式:  ansible  主机组   -m command -a  ‘creates=/etc/fstab   ls -al /app’

    3),removes <判断如果文件存在就执行后面的命令>  格式: ansible  主机组   -m  command -a  'removes=/etc/fstab  ls-al  /app/'

    shell

    shell的模块接受命令名,后面是空格分隔的参数列表。这几乎是

    与命令模块完全一样,但是在远程节点上通过shell(/bin/sh')运行命令

    支持 特殊符号$ HOME”和操作,比如“<“”、“”>“”,“”|“”、“”;“

    在执行命令是尽量使用单引号,

    其他用法基本跟command模块相同

    script

    脚本的模块接受脚本名称,发送到远程主机上执行,

    格式:ansible 主机组   -m  script   -a   ‘/app/f1 .sh ’

    copy

    copy'模块将一个文件从本地或远程机器复制到远程机器上的一个位置。使用com

    fetch模块将文件从远程位置复制到本地框。如果你需要变量插值

    在复制的文件中,使用模板模块。

    注意:copy模块复制文件如果目录不存在会自动创建目录

    常用参数:

    content:<把自定义内容复制到远程主机文件中>  格式 : content=“df -h ls hostname   dest=/app/f1.sh”

    src:<本机源文件>

    dest:<指定复制到目标主机的某个目录或者覆盖某个文件>  

    backup:<文件复制到远程主机如果文件已存在可以将源文件做备份,再覆盖>  格式: backup=yes /no  yes表示做备份,no表示不做备份

    directory_mode :<复制整个目录>

    mode:<更改复制过去的文件权限>    格式: mode=644......

    owner:<更改复制过去的文件属主>   格式:owner=wang

    格式:ansible 主机组   -m copy -a  ‘src=/etc/fstab dest=/app/  backup=yes mode=644 owner=wang’

    fetch

    这个模块的工作方式类似于复制,但反过来。它用于从远程机器获取文件

    将它们存储在本地目录下

    常用参数:

    src:<指定远程主机的文件>

    dest:<指定本地主机的目录>

    格式:ansible  主机组  -m fetch -a  ‘src=/etc/fstab  best=/app/’

    file

    支持在被控主机创建空文件,创建软连接,删除文件等作

    常用选项:

    创建空文件:path=<指定创建文件路径>   一般配合state=touch使用

    创建软连接:src=<指定原始文件路径>  一般配合path=<创建到那个目录>,state=link<连接>

    创建目录:path=<指定路径>    一般配合state=directory使用

    删除文件:  path=<指定文件路径>  一般配合state=absent使用

    设置文件权限:mode<设置文件权限>     owner<设置文件属组>

    格式:

    创建文件

    ansible  主机组 -m file -a ‘path=/app/testfile state=touch’

    创建软连接

    absible 主机组  -m file -a ‘src=/app/testfile path=/tmp/testfile-link  stare=link’

    创建目录

    absible 主机住  -m file -a 'path=/app/dir1  state=directory'

    删除文件

    ansible 主机组 -m file -a ‘path=/app/testfile state=absent’e

    hostname

    更改被控主机端的主机名

    格式:

    ansible 主机   -m hostname -a 'name=名字'

    cron

    创建计划任务

    minute<分钟>

    day <天>

    hour<小时>

    weekday<周>

    month<月>

    job<执行的操作> 注意:命令需要写全路径

    name<计划任务的名字>

    disabled=yes <yes表示禁用计划任务,no表示启用计划任务>

    start=absent <表示删除计划任务>

    例子:

    ansible 主机组  -m cron -a 'minute=*/5 hour=12  weekday=0,6  job="/usr/sbin/wall  is jobs " name="test job"'

    此命令原理:  创建一个名为test job计划任务 每周六周天,凌晨12点没5分钟执行一次wall广播 is job

    禁用,启用计划任务

    ansible 主机组  -m cron -a 'disabled=yes/on job="执行的操作"  name="计划任务名字"'

    删除计划任务

    ansible  主机组  -m cron -a 'start=adsent  job="执行的操作" name="计划任务名字"'

    yum

    管理软件包模块

    安装软件包

    name=包名

    start=absent  卸载包

    start=latest   装最新版

    updata_cache=yes  更新yum缓存

    service

    管理服务状态模块

    常用选项

    name=<指定包名>

    state=<指定运行状态>   如:stopped<停止>,started<启动>,restarted<重启服务>, reloaded<重读配置文件> ,

    enabled=yes  表示把服务设置为开机自动重启

    格式:

    设置服务开机自动启动并且启动

    ansible 主机组  -m  service -a  'name=httpd state=started  enabled=yes'

    user

    创建管理用户

    常用选项:

    name=<指定用户名>

    comment=<描述信息>

    uid=<指定uid>

    home=<指定家目录路径>

    group=<指定 主组>

    groups=<指定辅助组>

    system=yes<yes表示创建的是系统用户>

    shell=<指定shell类型>

    remove=yes <yes表示把家目录等等相关数据全删除>

    例子:

    创建普通用户

    ansible 主机组  -m user -a 'name="test" uid=2000 home=/app/test  group=wang groups=root,bin '

    创建系统用户   <注意:默认创建系统用户会创建家目录>

    ansible 主机组  -m user -a 'name=sys  system=yes  shell=nologin'

    删除用户并把家目录删除

    ansible 主机组  -m user -a 'name="test"  state=absent remove=yes’

    group

    管理组

    name=<指定组名>

    state=<指定操作>

    例子

    创建组

    ansible 主机组  -m group -a 'name=group1  '

    删除组

    ansible 主机组 -m group -a 'name=group1 state=absent'

    setup

    显示所有被控主机的,所有主机信息,例如:IP,主机名,版本,等等信息

    常用选项

    filter="*信息*"<作用用来匹配想要查看的信息>

    例子:

    ansible 主机组 -m setup  -a 'filter="*hostname*"'   注意匹配时需要把匹配的值放在双星”*“号里面

    匹配主机列表的方式:

    All :表示所有Inventory中的所有主机  

    ansible all –m ping

    *  :通配符  

    ansible  “*”  -m ping    #匹配所有主机执行ping模块操作

    ansible  192.168.1.* -m ping  #匹配192.168.1.这个网段的所有主机,执行平模块操作

    ansible  “*srvs”  -m ping  #匹配主机组名后面srvs的主机组执行ping模块操作

    或关系:  

    ansible “websrvs:appsrvs”  -m ping  #这表示匹配两个主机组里面的所有主机

      ansible  “192.168.1.10:192.168.1.20”  -m ping   #这是只针对两台主机执行ping模块操作

    逻辑与

    ansible “websrvs:&dbsrvs” –m ping   在websrvs组并且在dbsrvs组中的主机,才执行ping模块操作

    逻辑非

    ansible 'websrvs:!dbsrvs' –m ping   #在websrvs组,但不在dbsrvs组中的主机 ,执行平模块中操作

    综合逻辑

    ansible 'websrvs:dbsrvs:&appsrvs:!ftpsrvs' –m ping  #匹配在websrvszu或者在dbsrvs组,并且在appsrvs组,但不在ftpsrvszu里的主机,才会执行ping模块操作

    正则表达式

    ansible “~(web|db).*.magedu.com” –m ping  #匹配以带有web关键字后面跟任意字符.magedu.com的主机组,或者带有db关键字后面跟任意字符.magedu.com的主机组,执行ping模块操作


    ansible系列命令

    Ansible-vault

    功能:管理加密解密yml文件

    ansible-vault [create|decrypt|edit|encrypt|rekey|view]

    ansible-vault encrypt hello.yml 加密

    ansible-vault decrypt hello.yml 解密

    ansible-vault view hello.yml 查看

    ansible-vault edit  hello.yml 编辑加密文件

    ansible-vault rekey  hello.yml 修改口令

    ansible-vault create new.yml 创建新文件


    Ansible-console:2.0+新增,可交互执行命令,支持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 appsrvs

    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


    常用选项示例:

    查看主机组名里包括多少台主机

    anisble  主机分组名   --list-host

    #普通用户与root用户配置

    [root@localhost python]# cat /etc/ansible/hosts
    # This is the default ansible 'hosts' file.
    #
    # It should live in /etc/ansible/hosts
    #
    # - Comments begin with the '#' character
    # - Blank lines are ignored
    # - Groups of hosts are delimited by [header] elements
    # - You can enter hostnames or ip addresses
    # - A hostname/ip can be a member of multiple groups

    # Ex 1: Ungrouped hosts, specify before any group headers.

    ## green.example.com
    ## blue.example.com
    ## 192.168.100.1
    ## 192.168.100.10
    10.60.32.149

    # Ex 2: A collection of hosts belonging to the 'webservers' group

    ## [webservers]
    ## alpha.example.org
    ## beta.example.org
    ## 192.168.1.100
    ## 192.168.1.110

    # If you have multiple hosts following a pattern you can specify
    # them like this:

    ## www[001:006].example.com

    # Ex 3: A collection of database servers in the 'dbservers' group

    ## [dbservers]
    ##
    ## db01.intranet.mydomain.net
    ## db02.intranet.mydomain.net
    ## 10.25.1.56
    ## 10.25.1.57

    # Here's another example of host ranges, this time there are no
    # leading 0s:

    ## db-[99:101]-node.example.com
    [docker]
    10.60.32.149
    [k8s]
    10.60.36.220
    10.60.36.210
    [k8s:vars]
    ansible_ssh_user=www
    ansible_ssh_port=22
    ansible_sudo_pass=123456

    ansible-playbook 使用详解

    一、什么是playbooks
    playbooks是ansible的脚本、如同shell脚本一样,它是控制远程主机的一系列命令的集合,通过YAML语言编写。执行一些简单的任务,我们可以使用ad-hoc命令就可以解决,对于一些较复杂的任务,ad-hoc就不能胜任了,这时候playbooks就派上用场了,在playbooks中可以编排有序的执行过程,甚至可以在多组机器间来回有序的执行特定的步骤,并且可以同步或异步发起任务。

    二、YAML语法
    1、文件开始符

    1
    ---

    2、数组

    1
    2
    3
    - name
    - hosts
    - user

    3、字典

    1
    2
    name: restart apache
    service: name=httpd state=restarted

    字典与字典的嵌套:

    1
    2
    3
    vars:
      http_port: 80
      max_clients: 200

    字典与数组的嵌套:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    tasks:
      - name: ensure apache is at the latest version
        yum: pkg=httpd state=latest
      - name: write the apache config file
        template: src=/srv/httpd.j2 dest=/etc/httpd.conf
        notify:
        - restart apache
      - name: ensure apache is running
        service: name=httpd state=started

    此外,Ansible 使用 "{{ var }}"来引用变量.,如果一个值以 "{" 开头, YAML 将认为它是一个字典, 所以我们必须引用它, 像这样:foo: "{{ variable }}"

    三、playbooks的基本用法
    最基本的playbook分为四部分:

    • 定义主机和用户

    1
    2
    hosts
    users
    • 定义 playbook 执行需要的变量

    1
    variable
    • 定义任务

    1
    tasks
    • 定义响应事件

    1
    handlers

    简单示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    ---
    - name: Install apache
      hosts: webservers
      user: root
      gather_facts: false
      vars:
        http_port: 80
        max_clients: 200
      tasks:
      - name: ensure apache is at the latest version
        yum: pkg=httpd state=latest
      - name: write the apache config file
        template: src=/srv/httpd.j2 dest=/etc/httpd.conf
        notify:
        - restart apache
      - name: ensure apache is running
        service: name=httpd state=started
      handlers:
        - name: restart apache
          service: name=httpd state=restarted

    说明:

    • name参数对此playbook实现的功能的概述,执行时会输出name变量值。

    • hosts参数指定了在哪些主机上执行playbook

    • user参数指定在远程主机上使用什么用户执行任务

    • gather_facts参数指定了下面任务执行前,是否先执行setup模块获取远程主机相关信息,这些信息在task中可以作为变量使用

    • vars参数指定了变量

    • task参数指定了任务,这里有3个任务。name参数是对具体任务的描述,在执行过程中会输出

    • handlers参数指定一个响应事件,当template: src=/srv/httpd.j2 dest=/etc/httpd.conf这个任务执行状态是changed时才会触发。

     1、主机和用户

    key 含 义
    hosts 为主机的IP,或者主机组名,或者关键字all
    user 在远程以哪个用户身份执行。
    become 切换成其它用户身份执行,值为yes或者no
    become_method 与became一起用,指可以为‘sudo’/’su’/’pbrun’/’pfexec’/’doas’
    become_user 与bacome一起用,可以是root或者其它用户名

    一般用法:

    1
    2
    3
    ---
    - hosts: webserver, [all]
      user: root

    还可以在每个 task 中,定义远程执行用户

    1
    2
    3
    4
    5
    6
    7
    ---
    - hosts: webserver
      user: root
      tasks:
      - name: test connection
        ping:
        remote_user: root

    也支持 sudo 方法,在 task中同样支持,在sudo需要密码时,可以加上选项 –ask-sudo-pass

    1
    2
    3
    4
    5
    6
    7
    8
    ---
    - hosts: all
      user: test
      sudo: yes
      task:
        - service: name=nginx state=started
          sudo: yes
          sudo_user: root

    2、任务列表
    tasks 是从上到下顺序执行,如果中间发生错误,整个 playbook 便会中断。
    每一个 task 是对module的一次调用,通常会带有特定参数,参数可以使用变量。
    每一个 task 有一个 name 属性,name 值会在命令行中输出,以提示用户,如果没有定义,aciton 的值会作为输出信息来标记task。

    语法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    tasks:
      - name: make sure apache is running
        service: name=httpd state=running
    // 如果参数过长,可以使用空格或者缩进分隔为多行
    tasks:
      - name: copy ansible inventory file to client
        copy: src=/etc/ansible/hosts dest=/etc/ansible/hosts
              owner=root group=root mode=0644
    // 或者使用 yaml 的字典作为参数
    tasks:
      - name: copy ansible inventory file to client
        copy:
          src: /etc/ansible/hosts
          dest: /etc/ansible/hosts
          owner: root
          group: root
          mode: 0644
    // 大部分的模块都是使用 `key-value` 这种格式的,其中有两个比较特殊,command 和 shell 模块。
    tasks:
      - name: disable selinux
        command: /sbin/setenforce 0
    tasks:
      - name: run this command and ignore the result
        shell: /usr/bin/command || /bin/true
    tasks:
      - name: run some command and ignore the reslut
        shell: /usr/bin/somecommadn
        ignore_error: True

    执行状态:
    task中每个action会调用一个module,在module中会去检查当前系统状态是否需要重新执行。
    如果本次执行了,那么action会得到返回值changed
    如果不需要执行,那么action得到返回值ok

    3、响应事件
    每个主流的编程语言都会有event机制,那么handler就是playbook的event。
    Handlers里面的每一个handler,也是对module的一次调用。而handlers与tasks不同,tasks会默认的按定义顺序执行每一个task,handlers则不会,它需要在tasks中被调用,才有可能被执行。Tasks中的任务都是有状态的,changed或者ok。 在Ansible中,只在task的执行状态为changed的时候,才会执行该task调用的handler,这也是handler与普通的event机制不同的地方。
    什么情况下使用handlers呢?
    如果你在tasks中修改了apache的配置文件。需要重起apache。此外还安装了apache的插件。那么还需要重起apache。像这样的应该场景中,重启apache就可以设计成一个handler。

    特性:

    • 一个handler最多只执行一次

    在所有的任务里表执行之后执行,如果有多个task notify同一个handler,那么只执行一次。
    在下面的例子里apache只执行一次

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    ---
    - hosts: lb
      remote_user: root
      vars:
          random_number1: "{{ 10000 | random }}"
          random_number2: "{{ 10000000000 | random }}"
      tasks:
      - name: Copy the /etc/hosts to /tmp/hosts.{{ random_number1 }}
        copy: src=/etc/hosts dest=/tmp/hosts.{{ random_number1 }}
        notify:
          - call in every action
      - name: Copy the /etc/hosts to /tmp/hosts.{{ random_number2 }}
        copy: src=/etc/hosts dest=/tmp/hosts.{{ random_number2 }}
        notify:
          - call in every action
     
      handlers:
      - name: call in every action
        debug: msg="call in every action, but execute only one time"
    • action是Changed ,才会执行handler

    只有当TASKS种的action的执行状态是changed时,才会触发notify handler的执行。
    下面的脚本执行两次,执行结果是不同的:
    第一次执行是,tasks的状态都是changed,会触发两个handler
    第二次执行是,
    第一个task的状态是OK,那么不会触发handlers"call by /tmp/hosts",
    第二个task的状态是changed,触发了handler"call by /tmp/hosts.random_number"

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    //示例代码
    ---
    - hosts: lb
      remote_user: root
      vars:
          random_number: "{{ 10000 | random }}"
      tasks:
      - name: Copy the /etc/hosts to /tmp/hosts
        copy: src=/etc/hosts dest=/tmp/hosts
        notify:
          - call by /tmp/hosts
      - name: Copy the /etc/hosts to /tmp/hosts.{{ random_number }}
        copy: src=/etc/hosts dest=/tmp/hosts.{{ random_number }}
        notify:
          - call by /tmp/hosts.random_number
     
      handlers:
      - name: call by /tmp/hosts
        debug: msg="call first time"
      - name: call by /tmp/hosts.random_number
        debug: msg="call by /tmp/hosts.random_number"
    • 按Handler的定义顺序执行

    andlers是按照在handlers中定义个顺序执行的,而不是安装notify的顺序执行的。
    下面的例子定义的顺序是1>2>3,notify的顺序是3>2>1,实际执行顺序:1>2>3

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    ---
    - hosts: lb
      remote_user: root
      gather_facts: no
      vars:
          random_number1: "{{ 10000 | random }}"
          random_number2: "{{ 10000000000 | random }}"
      tasks:
      - name: Copy the /etc/hosts to /tmp/hosts.{{ random_number1 }}
        copy: src=/etc/hosts dest=/tmp/hosts.{{ random_number1 }}
        notify:
          - define the 3nd handler
      - name: Copy the /etc/hosts to /tmp/hosts.{{ random_number2 }}
        copy: src=/etc/hosts dest=/tmp/hosts.{{ random_number2 }}
        notify:
          - define the 2nd handler
          - define the 1nd handler
     
      handlers:
      - name: define the 1nd handler
        debug: msg="define the 1nd handler"
      - name: define the 2nd handler
        debug: msg="define the 2nd handler"
      - name: define the 3nd handler
        debug: msg="define the 3nd handler"

    4、变量
    playbook中常用变量的几种情况:

    • 用户自定义的变量

    用户可以在Playbook中,通过vars关键字自定义变量,使用时用{{ }}引用以来即可。
    示例:

    1
    2
    3
    4
    5
    6
    7
    8
    ---
    - hosts: web
      vars:
        http_port: 80
      remote_user: root
      tasks:
      - name: insert firewalld rule for httpd
        firewalld: port={{ http_port }}/tcp permanent=true state=enabled immediate=yes
    • 把变量放在单独的文件中

    当变量比较多的时候,或者变量需要在多个playbook中重用的时候,可以把变量放到一个单独的文件中。通过关键字var_files把文件中定义的变量引入playbook中,使用变量的方法和在本文件中定义的变量相同。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    - hosts: web
      remote_user: root
      vars_files:
          - vars/server_vars.yml
      tasks:
      - name: insert firewalld rule for httpd
        firewalld: port={{ http_port }}/tcp permanent=true state=enabled immediate=yes
     
    #cat vars/server_vars.yml
    http_port: 80

    定义复杂的变量
    当变量的值不是简单的字符串或者数字,而是一个字典,语法如下

    1
    2
    3
    foo:
      field1: one
      field2: two

    访问复杂变量中的子属性,可以利用中括号或者点号:

    1
    2
    3
    foo['field1']
    foo.field1
    • 系统变量(facts)

    ansible会通过module setup来收集主机的系统信息,这些收集到的系统信息叫做facts,这些facts信息可以直接以变量的形式使用。
    有哪些facts变量可以引用呢?在命令行上通过调用setup module命令可以查看:

    1
    $ ansible all -m setup -u root

    系统变量在playbook中可以直接使用:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    ---
    - hosts: all
      user: root
      tasks:
      - name: echo system
        shell: echo {{ ansible_os_family }}
      - name install ntp on Debian linux
        apt: name=git state=installed
        when: ansible_os_family == "Debian"
      - name install ntp on redhat linux
        yum: name=git state=present
        when: ansible_os_family == "RedHat"

    对于复杂的、多层级的facts变量,可以通过下面的两种方式的任意一种访问复杂的变量中的子属性:
    中括号:

    1
    {{ ansible_ens3["ipv4"]["address"] }}

    点号:

    1
    {{ ansible_ens3.ipv4.address }}

    关系facts变量,一旦关闭之后就不用调用了。关闭方法:

    1
    2
    - hosts: webserver
      gather_facts: no
    • 注册变量

    把task的执行结果也可以作为一个变量值。这个时候就需要用到“注册变量”,将执行结果注册到一个变量中,待后面的action使用。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    ---
    - hosts: web
      tasks:
         - shell: ls
           register: result
           ignore_errors: True
         - shell: echo "{{ result.stdout }}"
           when: result.rc == 5
         - debug: msg="{{ result.stdout }}"

    注册变量经常和debug module一起使用,这样可以得到更多action的输出信息,帮助用户调试。

    • 命令行中传递的变量

    为了使Playbook更灵活、通用性更强,允许用户在执行的时候传入变量的值,这个时候就需要用到“额外变量”。
    定义命令行变量
    在test.yml文件里,hosts和user都定义为变量,需要从命令行传递变量值。

    1
    2
    3
    4
    5
    ---
    - hosts: '{{ hosts }}'
      remote_user: '{{ user }}'
      tasks:
      ...

    在命令行里面传值得的方法:

    1
    ansible-playbook testyml --extra-vars "hosts=web user=root"

    还可以用json格式传递参数:

    1
    ansible-playbook test.yml --extra-vars "{'hosts':'vm-rhel7-1', 'user':'root'}"

    还可以将参数放在文件里面:

    1
    ansible-playbook test.yml --extra-vars "@vars.json"

    5、如何执行playbook

    语法:

    1
    $ ansible-playbook deploy.yml

    查看输出的细节

    1
    $ ansible-playbook playbook.yml --verbose

    查看该脚本影响哪些hosts

    1
    $ ansible-playbook playbook.yml --list-hosts

    并行执行脚本

    1
    $ ansible-playbook playbook.yml -f 10

    四、playbooks的高级用法
    1、逻辑控制

    • when: 条件判断语句,类似于变成语言中的if

    主机为Debian Linux立刻关机:

    1
    2
    3
    4
    tasks:
      - name: "shutdown Debian flavored systems"
        command: /sbin/shutdown -t now
        when: ansible_os_family == "Debian"

    根据action的执行结果,来决定接下来执行的action

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    tasks:
      - command: /bin/false
        register: result
        ignore_errors: True
      - command: /bin/something
        when: result|failed
      - command: /bin/something_else
        when: result|success
      - command: /bin/still/something_else
        when: result|skipped

    远程中的系统变量facts变量作为when的条件,用“|int”还可以转换返回值的类型:

    1
    2
    3
    4
    5
    ---
    - hosts: web
      tasks:
        - debug: msg="only on Red Hat 7, derivatives, and later"
          when: ansible_os_family == "RedHat" and ansible_lsb.major_release|int >= 6

    条件表达式

    基本:

    1
    2
    3
    tasks:
        - shell: echo "This certainly is epic!"
          when: epic

    否定:

    1
    2
    3
    tasks:
        - shell: echo "This certainly isn't epic!"
          when: not epic

    变量定义:

    1
    2
    3
    4
    5
    tasks:
        - shell: echo "I've got '{{ foo }}' and am not afraid to use it!"
          when: foo is defined
        - fail: msg="Bailing out. this play requires 'bar'"
          when: bar is not defined

    数值表达:

    1
    2
    3
    4
    tasks:
        - command: echo {{ item }}
          with_items: [ 0, 2, 4, 6, 8, 10 ]
          when: item > 5

    与Include一起用:

    1
    2
    - include: tasks/sometasks.yml
      when: "'reticulating splines' in output"

    与Role一起用:

    1
    2
    3
    - hosts: webservers
      roles:
         - { role: debian_stock_config, when: ansible_os_family == 'Debian' }
    • loop: 循环语句,类似于编程语言的中的while

    标准循环
    为了保持简洁,重复的任务可以用以下简写的方式:

    1
    2
    3
    4
    - name: add several users
      user: name={{ item }} state=present groups=wheel
      with_items:
         - testuser1

    如果你在变量文件中或者 'vars' 区域定义了一组YAML列表,你也可以这样做:

    1
    2
    3
    4
    5
    6
    vars:
      somelist: ["testuser1", "testuser2"]
    tasks:
      -name: add several user
       user: name={{ item }} state=present groups=wheel
       with_items: "{{somelist}}"

    使用 'with_items' 用于迭代的条目类型不仅仅支持简单的字符串列表.如果你有一个哈希列表,那么你可以用以下方式来引用子项:

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

    注意:如果同时使用 when 和 with_items (或其它循环声明),when声明会为每个条目单独执行。

    嵌套循环:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    - name: give users access to multiple databases
      mysql_user: name={{ item[0] }} priv={{ item[1] }}.*:ALL append_privs=yes password=foo
      with_nested:
        - [ 'alice', 'bob' ]
        - [ 'clientdb', 'employeedb', 'providerd']
    或者
    - name: give users access to multiple databases
      mysql_user: name={{ item.0 }} priv={{ item.1 }}.*:ALL append_privs=yes password=foo
      with_nested:
        - [ 'alice', 'bob' ]
        - [ 'clientdb', 'employeedb', 'providerd']

    对字典使用循环:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    ---
    vars:
      users:
        alice:
          name: Alice Appleworth
          telephone: 123-456-7890
        bob:
          name: Bob Bananarama
          telephone: 987-654-3210
    tasks:
      - name: Print phone records
        debug: msg="User {{ item.key }} is {{ item.value.name }} ({{ item.value.telephone }})"
        with_dict: "{{users}}"

    对文件列表使用循环
    with_fileglob 可以以非递归的方式来模式匹配单个目录中的文件.如下面所示:

    1
    2
    3
    4
    5
    6
    7
    tasks:
        # first ensure our target directory exists
        - file: dest=/etc/fooapp state=directory
        # copy each file over that matches the given pattern
        - copy: src={{ item }} dest=/etc/fooapp/ owner=root mode=600
          with_fileglob:
            - /playbooks/files/fooapp/*
    • block: 把几个tasks组成一块代码,便于针对一组操作的异常处理等操作。

    多个action组装成块,可以根据不同条件执行一段语句 :

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    tasks:
         - block:
             - yum: name={{ item }} state=installed
               with_items:
                 - httpd
                 - memcached
             - template: src=templates/src.j2 dest=/etc/foo.conf
             - service: name=bar state=started enabled=True
           when: ansible_distribution == 'CentOS'
           become: true
           become_user: root

    组装成块处理异常更方便:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    tasks:
      - block:
          - debug: msg='i execute normally'
          - command: /bin/false
          - debug: msg='i never execute, cause ERROR!'
        rescue:
          - debug: msg='I caught an error'
          - command: /bin/false
          - debug: msg='I also never execute :-('
        always:
          - debug: msg="this always executes"

    2、重用playbook

    Playbook支持两种重用机制,一种是重用静态Playbook脚本,另外一种是类似于编程语言中函数的机制。

    • include语句 - 重用静态的Playbook脚本,使用起来简单、直接。

    Include语句的功能,基本的代码重用机制。主要重用tasks。同时Include可将tasks分割成多个文件,避免Playbook过于臃肿,使用户更关注于整体的架构,而不是实现的细节上。
    普通用法
    像其它语言的Include语句一样,直接Include:

    1
    2
    3
    4
    5
    6
    7
    8
    ---
    # possibly saved as tasks/firewall_httpd_default.yml
     
      - name: insert firewalld rule for httpd
        firewalld: port=80/tcp permanent=true state=enabled immediate=yes
    main.yml文件中调用include的方法:
    tasks:
        - include: tasks/firewall_httpd_default.yml

    高级用法-使用参数
    include文件中还可以定义参数
    被include的文件tasks/firewall_httpd_default.yml中,使用{{ port }}定义了一个名字为port的参数。

    1
    2
    3
    ---
      - name: insert firewalld rule for httpd
        firewalld: port={{ port }}/tcp permanent=true state=enabled immediate=yes

    传参数的各种方法
    在执行的playbook传参数,可以加在行尾,使用空格分隔:

    1
    2
    3
    4
    tasks:
      - include: tasks/firewall.yml port=80
      - include: tasks/firewall.yml port=3260
      - include: tasks/firewall.yml port=423

    还可以使用yml的字典传参数:

    1
    2
    3
    4
    5
    6
    7
    tasks:
      - include: wordpress.yml
        vars:
            wp_user: timmy
            ssh_keys:
              - keys/one.txt
              - keys/two.txt

    还可以把一条task简写成成一个类JSON的形式传参数:

    1
    - { include: wordpress.yml, wp_user: timmy, ssh_keys: [ 'keys/one.txt', 'keys/two.txt' ] }

    当然在playbook中已经定义了的参数,就不需要再显示传入值了,可以直接写成下面的:

    1
    2
    3
    4
    5
    6
    7
    ---
    - hosts: lb
      vars:
        port: 3206
      remote_user: root
      tasks:
        - include: tasks/firewall.yml
    • role语言 - Playbook的"函数机制",使用方法稍复杂、功能强大。是Playbook脚本的共享平台ansible galaxy主要的分享方式

    Role是比include更强大灵活的代码重用和分享机制。Include类似于编程语言中的include,是重用单个文件的,功能有限。
    而Role类似于编程语言中的“Package”,可以重用一组文件形成完整的功能。例如安装和配置apache,需要tasks实现安装包和拷贝模版等,httpd.conf和index.html的模版文件,和handler文件实现重起功能。这些文件都可以放在一个role里面,供不同的playbook文件重用。
    Ansible非常提倡在playbook中使用role,并且提供了一个分享role的平台Ansible Galaxy, https://galaxy.ansible.com/, 在galaxy上可以找到别人写好的role。
    在ansible中,通过遵循特定的目录结构,就可以实现对role的定义:
    下面的目录结构定义了一个role:名字为myrole。在site.yml,调用了这个role。
    role的目录结构:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    site.yml
    roles/
    ├── myrole
        ├── tasks
        │   └── main.yml
        ├── handlers
        │   └── main.yml
        ├── defaults
        │   └── main.yml
        ├── vars
        │   └── main.yml
        ├── files
        ├── templates
        ├── README.md
        ├── meta
        │   └── main.yml
        └── tests
            ├── inventory
            └── test.yml

    site.yml中调用role

    1
    2
    3
    4
    ---
    - hosts: webservers
      roles:
         - myrole

    ansible并不要求role包含上述所有的目录及文件,根据role的功能需要加入对应的目录和文件。下面是每个目录和文件的功能。
    如果 roles/x/tasks/main.yml 存在, 其中列出的 tasks 将被添加到 play 中,所以这个文件也可以视作role的入口文件,想看role做了什么操作,可以从此文件看起。
    如果 roles/x/handlers/main.yml 存在, 其中列出的 handlers 将被添加到 play 中
    如果 roles/x/vars/main.yml 存在, 其中列出的 variables 将被添加到 play 中
    如果 roles/x/meta/main.yml 存在, 其中列出的 “角色依赖” 将被添加到 roles 列表中
    roles/x/tasks/main.yml中所有tasks,可以引用 roles/x/{files,templates,tasks}中的文件,不需要指明文件的路径。
    在写role的时候,一般都要包含role入口文件roles/x/tasks/main.yml,其它的文件和目录,可以根据需求选择加入。
    参数在role中是如何定义的呢
    定义一个带参数的role,名字是myrole,那么目录结构为:

    1
    2
    3
    4
    5
    main.yml
    roles
      role_with_var
        tasks
          main.yml

    在roles/myrole/tasks/main.yml中,使用{{ }}定义的变量就可以了

    1
    2
    3
    ---
    - name: use param
      debug: msg="{{ param }}"

    使用带参数的role
    那么在main.yml就可以用如下的方法使用myrole

    1
    2
    3
    4
    5
    ---
    - hosts: webservers
      roles:
        - { role: myrole, param: 'Call some_role for the 1st time' }
        - { role: myrole, param: 'Call some_role for the 2nd time' }

    或者写成YAML字典格式:

    1
    2
    3
    4
    5
    6
    7
    ---
    - hosts: webservers
      roles:
        - role: myrole
          param: 'Call some_role for the 1st time'
        - role: myrole
          param: 'Call some_role for the 2nd time'

    role指定默认的参数
    指定默认参数后,如果在调用时传参数了,那么就使用传入的参数值.如果调用的时候没有传参数,那么就使用默认的参数值.
    指定默认参数很简单,以上面的role_with_var为例

    1
    2
    3
    4
    5
    6
    7
    main.yml
    roles:
      myrole
        tasks
          main.yml
        defaults
          main.yml

    在roles/myrole/defaults/main.yml中,使用yml的字典定义语法定义param的值,如下:
    param: "I am the default value"
    这样在main.yml中,下面两种调用方法都可以

    1
    2
    3
    4
    5
    ---
    - hosts: webservers
      roles:
        - role_with_var
        - { role: role_with_var, param: 'I am the value from external' }

    role与条件语句when一起执行,下面的例子中,my_role只有在RedHat系列的server上才执行。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    ---
    - hosts: webservers
      roles:
        - { role: my_role, when: "ansible_os_family == 'RedHat'" }
    同样也可以写成YAML字典格式
    ---
    - hosts: webservers
      roles:
        - role: my_role
          when: "ansible_os_family == 'RedHat'"
    roles和tasks的执行顺序

    如果一个playbook同时出现role和tasks,他们的调用顺序如下:
    pre_tasks > role > tasks > post_tasks

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    ---
    - hosts: lb
      user: root
      pre_tasks:
        - name: pre
          shell: echo 'hello'
      roles:
        - { role: some_role }
     
      tasks:
        - name: task
          shell: echo 'still busy'
     
      post_tasks:
        - name: post
          shell: echo 'goodbye'
    执行的结果为:
    PLAY [lb] **********************************************************************
     
     
    TASK [setup] *******************************************************************
    ok: [rhel7u3]
     
    TASK [pre] *********************************************************************
    changed: [rhel7u3]
     
    TASK [some_role : some role] ***************************************************
    ok: [rhel7u3] => {
        "msg": "Im some role"
    }
     
    TASK [task] ********************************************************************
    changed: [rhel7u3]
     
    TASK [post] ********************************************************************
    changed: [rhel7u3]
     
    PLAY RECAP *********************************************************************
    rhel7u3                    : ok=5    changed=3    unreachable=0    failed=0

    3、tags的用法
    如果playbook文件比较大,在执行的时候只是想执行部分功能,这个时候没有有解决方案呢?Playbook提供了tags便签可以实现部分运行。
    tags的基本用法
    例如,文件example.yml如何所示,标记了两个tag:packages和configuration

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    tasks:
      - yum: name={{ item }} state=installed
        with_items:
           - httpd
        tags:
           - packages
      - name: copy httpd.conf
        template: src=templates/httpd.conf.j2 dest=/etc/httpd/conf/httpd.conf
        tags:
           - configuration
      - name: copy index.html
        template: src=templates/index.html.j2 dest=/var/www/html/index.html
        tags:
           - configuration

    那么我们在执行的时候,如果不加任何tag参数,那么会执行所有的tasks

    1
    $ ansible-playbook example.yml

    指定执行安装部分的tasks,则可以利用关键字tags

    1
    $ ansible-playbook example.yml --tags "packages"

    指定不执行packages部分的task,则可以利用关键字skip-tags

    1
    $ ansible-playbook example.yml --skip-tags "configuration"

    特殊的Tags:“always”
    tags的名字是用户自定义的,但是如果你把tags的名字定义为“always”,那么就有点特别了。只要在执行playbook时,没有明确指定不执行always tag,那么它就会被执行。
    在下面的例子中,即使你只指定执行packages,那么always也会被执行。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    tasks:
      - debug: msg="Always print this debug message"
        tags:
          - always
      - yum: name= state=installed
        with_items:
           - httpd
        tags:
           - packages
      - template: src=templates/httpd.conf.j2 dest=/etc/httpd/conf/httpd.conf
        tags:
           - configuration

    指定运行packages时,还是会执行always tag对应的tasks

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    $ ansible-playbook tags_always.yml --tags "packages"
    “tagged”,“untagged”和“all”
    tasks:
      - debug: msg="I am not tagged"
        tags:
          - tag1
      - debug: msg="I am not tagged"
    分别指定--tags为“tagged”,“untagged”和“all”试下效果吧:
    $ ansible-playbook tags_tagged_untagged_all.yml --tags tagged
    $ ansible-playbook tags_tagged_untagged_all.yml --tags untagged
    $ ansible-playbook tags_tagged_untagged_all.yml --tags all

    在include中和role中使用tags
    include语句指定执行的tags的语法:

    1
    2
    - include: foo.yml
      tags: [web,foo]

    调用role中的tags的语法为:

    1
    2
    roles:
      - { role: webserver, port: 5000, tags: [ 'web', 'foo' ] }

    4、playbook文件加密

    ansible-vault命令可以对配置文件进行基于密码的加密,防止敏感信息泄漏。

    加密已存在文件

    1
    $ ansible-vault encrypt site.yml

    加密并创建文件

    1
    $ ansible-vault create filename

    执行加密后的playbook

    1
    $ ansible-playbook site.yml --ask-vault-pass

    解密

    1
    $ ansible-vault decrypt site.yml

    五、编译安装nginx的playbook的实例

    playbooks目录结构:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    $ tree playbooks
    playbooks
    ├── group_vars
    ├── roles
    │   └── nginx
    │       ├── files
    │       │   ├── nginx-1.8.0.tar.gz
    │       │   └── pcre-8.33.tar.gz
    │       ├── handlers
    │       │   └── main.yml
    │       ├── tasks
    │       │   └── main.yml
    │       └── templates
    │           └── default.conf
    └── site.yml

    yml文件内内容如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    $ cat playbooks/site.yml
    ---
    - name: Install Nginx
      hosts: test
      user: admin
     
      roles:
        - nginx
     
    $ cat playbooks/roles/nginx/tasks/main.yml
    ---
    - name: Copy nginx software
      copy: src=nginx-1.8.0.tar.gz dest=/app/admin/soft/nginx-1.8.0.tar.gz
     
    - name: Copy pcre software
      copy: src=pcre-8.33.tar.gz dest=/app/admin/soft/pcre-8.33.tar.gz
     
    - name: Install Pcre
      shell: cd /app/admin/soft;tar zxf pcre-8.33.tar.gz;cd pcre-8.33;./configure;make;make install
      sudo: yes
     
    - name: Install Nginx
      shell: cd /app/admin/soft;tar zxf nginx-1.8.0.tar.gz;cd nginx-1.8.0;./configure --user=admin --group=app --prefix=/app/admin/nginx --with-http_stub_status_module --with-http_ssl_module;make;make install
     
    - name: Copy nginx configuration
      template: src=default.conf dest=/app/admin/nginx/conf/nginx.conf
      notify: restart nginx
    [admin@wts-vm-01 ~]$ cat playbooks/roles/nginx/handlers/main.yml
    ---
    - name: restart nginx
      shell: /app/admin/nginx/sbin/nginx
      sudo: yes

    六、其它
    官方例子
    Ansible官方提供了一些比较常用的、经过测试的Playbook例子:
    https://github.com/ansible/ansible-examples
    Playbook分享平台:
    https://galaxy.ansible.com/

  • 相关阅读:
    python 协程
    python 打印乘法表
    python 线程池
    python 队列
    开发react 应用最好用的脚手架 create-react-app
    React面试题
    修改了背景透明度
    低门槛彻底理解JavaScript中的深拷贝和浅拷贝
    Web Worker 使用教程
    Vue2 实现时空穿梭框功能模块
  • 原文地址:https://www.cnblogs.com/mylovelulu/p/9297847.html
Copyright © 2020-2023  润新知