• Ansible 入门指南


    上篇文章Ansible 入门指南 - 安装及 Ad-Hoc 命令使用介绍的额是 Ad-Hoc 命令方式,本文将介绍 Playbook 方式。

    Playbook 译为「剧本」,觉得还挺恰当的。

    playbook 执行语法

    Playbook 译为「剧本」,觉得还挺恰当的。那么 play 那我就译为 「场景」吧。playbook 由一个或多个 ‘plays’ 组成.它的内容是一个以 ‘plays’ 为元素的列表

    playbook 基础

    可以为 playbook 中的每一个 play,个别地选择操作的目标机器是哪些,以哪个用户身份去完成要执行的步骤(called tasks)

    $ ansible-playbook  -h
    Usage: ansible-playbook [options] playbook.yml [playbook2 ...]
    
    Runs Ansible playbooks, executing the defined tasks on the targeted hosts.
    

    palybook 小栗子

    在指定的目标主机上执行定义好的 tasks

    playbook.yml 常包含下面几个关键字:

    • hosts:为主机的IP,或者主机组名,或者关键字all
    • remote_user: 以哪个用户身份执行。
    • vars: 变量
    • tasks: playbook的核心,定义顺序执行的动作 action。每个action调用一个ansbile module
    • action 语法: module: module_parameter=module_value
    • 常用的 moduleyumcopytemplate等,module在 ansible 的作用,相当于 bash 脚本中yumcopy这样的命令。
    • 每一个 task 必须有一个名称 name,这样在运行 playbook 时,从其输出的任务执行信息中可以很好的辨别出是属于哪一个 task 的
    • handers: 是 playbook 的 event ,默认不会执行,在 action 里触发才会执行。多次触发只执行一次。

    一个简单的示例:
    deploy.yml 的功能为 web 主机部署 apache, 其中包含以下部署步骤:

    • 安装apache包;
    • 拷贝配置文件httpd,并保证拷贝文件后,apache服务会被重启;
    • 拷贝默认的网页文件index.html;
    • 启动apache服务;
    ---
    - hosts: centos
      vars:
        httpd_port: 8080
        max_clients: 200
      remote_user: root
      tasks:
      - name: ensure apache is at the latest version
        yum: name=httpd state=present
    
      - name: Write the configuration file
        template: src=templates/httpd.conf.j2 dest=/etc/httpd/conf/httpd.conf
        notify:
        - restart apache
        ignore_errors: False
    
      - name: Write the default index.html file
        template: src=templates/index.html.j2 dest=/var/www/html/index.html
        ignore_errors: False
    
      - name: ensure apache is running
        service: name=httpd state=started
    
      handlers:
        - name: restart apache
          service: name=httpd state=restarted
    

    运行ansible-playbook deploy.yml即可执行。

    tasks

    taks 参数其实是有不同写法的,当参数比较少时,可用key=value的方式

    tasks:
      - name: make sure apache is running
        service: name=httpd state=running
    

    tasks 参数比较多时,为了美观和不易出错,用 yml的字典传参比较好:

    tasks:
      - name: make sure apache is running
        service:
          name: httpd
          state: running
    

    task中每个action会调用一个module,在module中会去检查当前系统状态是否需要重新执行。

    • 如果本次执行了,那么 action 会得到返回值 changed;
    • 如果不需要执行,那么 action 得到返回值ok

    module 的执行状态的具体判断规则由各个 module 自己决定和实现的。例如,”copy” module的判断方法是比较文件的checksum,代码如下:

    ansbile-playbook 常用命令

    查看脚本影响到的 hosts

    下面这条命令,指定 inventory 文件,列出 hosts 列表,并不会去执行定义的 tasks,观察 host 是否配置正确很有用:

    ansible-playbook -i inventory/slave_init.yml execute_slave_init.yml --list-hosts
    

    查看输出的细节

    ansible-playbook playbook.yml  --verbose
    

    并行执行脚本

    ansible-playbook playbook.yml -f 10
    

    输入密码

    playbook 中使用到了 become,执行playbook时可以加上--ask-become-pass参数:

    ansible-playbook deploy.yml --ask-become-pass
    

    yml 语法简介

    • 大小写敏感
    • 使用缩进表示层级关系
    • 缩进时不允许使用Tab键,只允许使用空格。
    • 缩进的空格数目不重要,只要相同层级的元素左侧对齐即可
    • # 表示注释,从这个字符一直到行尾,都会被解析器忽略
    • YAML 还有一个小的怪癖. 所有的 YAML 文件(无论和 Ansible 有没有关系)开始行都应该是 ---. 这是 YAML 格式的一部分, 表明一个文件的开始.
    • 列表中的所有成员都开始于相同的缩进级别, 并且使用一个 "- " 作为开头(一个横杠和一个空格)
    • 一个字典是由一个简单的 键: 值 的形式组成(这个冒号后面必须是一个空格)
    • Ansible 使用 “{{ var }}” 来引用变量,foo: "{{ variable }}"

    参考

    Play vs Playbook

    其实在一个Playbook文件中还可以有针对两组server进行不同的操作,例如给web安装http服务器,和给lb安装mysql放在一个文件中:

    ---
    #安装apache的play
    - hosts: web
      remote_user: root
      tasks:
      - name: ensure apache is at the latest version
        yum: pkg=httpd state=latest
    
    # 安装mysql server的play
    - hosts: lb
      remote_user: root
      tasks:
      - name: ensure mysqld is at the latest version
        yum: pkg=mariadb state=latest
    

    上面例子中针对每一组 server 的所有操作就组成一个 play,一般一个 playbook 中只包含一个 play,play 的内容称为 tasks,即任务。

    参考:

    响应事件 Hanlder

    handlers与tasks不同,tasks会默认的按定义顺序执行每一个task,handlers则不会,它需要在tasks中被调用,才有可能被执行。

    Handlers will only be fired for tasks which report a changed state.
    只有当 task 执行状态显示是 changed 时,handler 动作才会被触发

    Tasks中的任务都是有状态的,changed或者ok。 在Ansible中,只在task的执行状态为 changed 的时候,才会执行该task调用的handler。

    在所有的task列表执行之后执行,如果有多个task notify同一个handler,那么 handlers 也只执行一次。

    什么情况下使用handlers呢?

    如果你在tasks中修改了apache的配置文件。需要重起apache。此外还安装了apache的插件。那么还需要重起apache。像这样的应该场景中,重起apache就可以设计成一个handler.

    当一个文件的内容被改动时,重启两个 services:

    - name: template configuration file
      template: src=template.j2 dest=/etc/foo.conf
      notify:
         - restart memcached
         - restart apache
    
    • notify 下列出的即是 handlers.
    • Handlers 也是一些 task 的列表,通过名字(name)来引用。
    • Handlers 是由通知者进行 notify, 如果没有被 notify,handlers 不会执行。
    • 不管有多少个通知者进行了 notify,等到 play 中的所有 task 执行完成之后,handlers 也只会被执行一次.
    handlers:
        - name: restart memcached
          service:  name=memcached state=restarted
        - name: restart apache
          service: name=apache state=restarted
    

    此外,还有个注意点:

    • handlers是按照在handlers中定义个顺序执行的,而不是安装notify的顺序执行的。比如,handlers 定义的顺序是1>2>3,notify 的顺序是3>2>1,实际执行顺序:1>2>3.

    总结,Handlers 最佳的应用场景是用来重启服务,或者触发系统重启操作.除此以外很少用到。

    参考

    变量

    playbook 中常用的集中变量:

    1. 在 playbook 中,用户自定义的变量
    2. 无需用户定义,ansible 在执行 playbook 之前,去远程主机上搜集的关于远程主机的系统信息变量
    3. task 运行的结果「注册」为一个变量来使用,这个变量叫做「注册变量」
    4. 允许用户在执行的时候传入变量的值,这时候用到的是「额外变量」

    playbook 中定义的变量

    在 playbook 中,通过关键字 vars 自定义变量,用 {{}} 引用变量。

    将变量放在单独的文件中

    通过 vars_files 关键字指定了变量文件:

    ---
    - hosts: centos
      vars:
        httpd_port: 80
      vars_files:
          - ./vars_servers.yml
      remote_user: root
      tasks:
      - debug:
          msg: "http_port: {{httpd_port}}"
      - debug:
          msg: "x86 passwd: {{x86.password}}"
      - debug:
          msg: "arm passwd: {{arm.password}}"
          # 也可以用 arm['password'] 表示
    

    专门存放变量的文件:

    # vars_servers.yml
    x86:
        password: 123
    arm:
        password: 456
    

    远程节点的系统变量(facts)

    ansible 通过 module setup 收集主机的系统信息,这些收集到的系统信息叫做 facts,这些facts可以直接以变量的形式使用。

    哪些 facts 变量可以引用的?通过如下命令行调用setup module 可以查看:

    ansible all -m setup -u root
    

    可以看到它输出的变量信息有很多!

    复杂的facts变量的使用可以用如下两种形式:

    • {{ ansible_ens3["ipv4"]["address"] }}
    • {{ ansible_ens3.ipv4.address }}

    好用的一些 facts 变量

    • ansible_hostname 指定的 host 名称
    • ansible_default_ipv4.address 主机真实的 ipv4 地址,小网IP

    ansible_os_family 查看系统类型的变量

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

    在 playbook 中,如果不收集系统信息,那么上面的变量就不能再 playbook 中使用了,但是有时候关闭会加快执行的效率:

    
    - hosts: all
      gather_facts: no
    

    注册变量 register

    将某个 task 执行的结果「注册」为一个变量。后面的 action 就可以使用它

    ---
    - hosts: centos
      tasks:
          - name: ls /tmp
            shell: ls -l /tmp
            register: result
            ignore_errors: True
    
          - name: echo result when rc==5
            shell: echo "{{result}}"
            when: result.rc == 5
    
          - name: debug show stdout
            debug:
              msg: "{{result.stdout}}"
    

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

    参考

    命令行传递变量 --extra-vars

    ---
    - hosts: "{{hosts}}"
      remote_user: "{{user}}""
    
      tasks:
        - debug: msg="{{hosts}}""
    

    命令输入变量:

    ansible-playbook extra_learn.yml --extra-vars "{'hosts':'x86','user':‘’michael'}"
    # or
    ansible-playbook extra_learn.yml --extra-vars "hosts=x86 user=michael"
    

    playbook 中的逻辑控制语句

    • when:条件判断,类似编程语言中的 if
    • loop:循环,类似编程语言中的 while
    • block:将几个 task 组成一块代码,便于针对一组操作进行异常处理等

    条件语句 wehn

    例如,在某个特定版本的系统上装包,或者只在磁盘空间满了的文件系统上执行清理操作。这些操作在Playbook中用when语句实现。

    主机为Debian Linux立刻关机

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

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

    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”还可以转换返回值的类型:

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

    循环语句 loop

    标准循环 with_items

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

    - name: add several users
      user: name="{{ item }}" state=present groups=wheel
      with_items:
         - michael
         - qq
    

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

    vars:
      userlist: ["micahel", "qq"]
    tasks:
      -name: add several user
       user:
         name: "{{ item }}"
         state: present
         groups: wheel
       with_items: "{{userlist}}"
    

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

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

    对哈希表使用循环 with_dict

    这个例子不仅演示了 with_dict 用法,还使用循环安装了 RPM 包

    ---
    - hosts: centos
      vars:
        users:
          michael:
            name: michael xiang
            phone: 123
          qq:
            name: qq huang
            phone: 456
    
        rpms:
            - httpd
            - lrzsz
            - vim
            - git
    
      tasks:
        - name: print phone records
          debug: msg="User {{item.key }} is {{ item.value.name }} {{item.value.phone}}"
          with_dict: "{{ users }}"
    
        - name: install rpms
          yum: name="{{item}}" state=installed
          with_items: "{{rpms}}"
    

    对文件列表使用循环 with_filegloab

    with_fileglob 可以以非递归的方式来模式匹配单个目录中的文件.如下面所示:

    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/*
    

    参考

    块语句

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

     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
    

    ansible 示例

    参考

  • 相关阅读:
    Linux用户空间与内核地址空间
    [Linux内存]——内核地址空间
    使用 GDB 调试多进程程序
    Linux GDB常用命令
    GDB常用命令使用
    GDB调试原理——ptrace系统调用
    不可见乱码 怎么消除
    Vue use的理解
    Element-UI 下边框,表格错位问题
    sort 排序传递更多参数
  • 原文地址:https://www.cnblogs.com/michael-xiang/p/10461518.html
Copyright © 2020-2023  润新知