以下针对Ansible 2.1.1.0,对其他版本的Ansible可能不适用。

  1. Ansible是的执行原理是将python代码传输到远端,然后在远端执行。因此:

    • 如果你要写Ansible的module,想要对远端的某个文件写入,只需要在module的代码里:

      with open(file_path, 'w') as f:
           f.write(content)
      

      像上述这样就可以了。

    • 如果你写的module需要使用一些外部的资源,要注意这些外部资源Ansible是不会自动帮你传输到远端的。比如我曾经想调用一个python文件:

      remote_hostname | FAILED! => {
          "changed": false, 
          "failed": true, 
          "module_stderr": "", 
          "module_stdout": "File \"/tmp/ansible_C9llBm/ansible_module_mymodule.py\", line 10, in <module>\r\n    import default\r\nImportError: No module named default\r\n", 
          "msg": "MODULE FAILURE", 
          "parsed": false
      }
      
  2. Ansible现在不支持从一个module中调用其他module。所以如果是用到多个module的组合的话,还是写在playbook里吧。

    If you are asking ‘how can I have a module execute other modules’ … you want to write a role.

  3. Ansible是可以对每个host使用不同的password等设置的。例子如下:

    [real_group]
    remote_host1 ansible_connection=ssh ansible_ssh_user=root1 ansible_ssh_pass=password1
    remote_host2 ansible_connection=ssh ansible_ssh_user=root2 ansible_ssh_pass=password2
    

    如果你觉得上述方式不直观,也可以把每个机器单独设为一个group,再把所有机器用一个group包起来,就像这样:

    [remote_host1]
    remote_hostname1
    
    [remote_host1:vars]
    ansible_connection=ssh
    ansible_ssh_user=root
    ansible_ssh_pass=password1
    
    [remote_host2]
    remote_hostname2
    
    [remote_host2:vars]
    ansible_connection=ssh
    ansible_ssh_user=root
    ansible_ssh_pass=password2
    
    [real_group:children]
    remote_host1
    remote_host2
    
  4. Ansible的module只能获取传递进去的参数,而没法知道hosts里面定义的内容,即如果想要module根据host的信息来搞事情,也必须要通过参数传递进去(这个design的module开发体验很差!)。

  5. 想要在python代码里执行Ansible的playbook,可参考How to use Ansible 2.0 Python API to run a Playbook?

  6. Ansible的playbook可以插入Jinja2的template,template的变量的内容会被playbook里面的vars里面的对应变量给替代(vars也可以定义在inventory里面)。例如: In hosts:

    [remote_host1]
    remote_hostname1
    
    [remote_host1:vars]
    ansible_connection=ssh
    ansible_ssh_user=root
    ansible_ssh_pass=password1
    check_dir=/tmp
    

    In playbook.yml:

    ---
    - hosts: remote_host1
      tasks:
      - name: List files in given dir
        command: ls {{check_dir}}
        register: out
    
      - name: stdout
        debug: msg=out.stdout_lines
    

    使用ansible-playbook playbook.yml -i hosts执行结果如下:

    ➜  demo_ansible git:(master) ✗ ansible-playbook playbook.yml -i hosts
    
    PLAY [remote_host1] ************************************************************
    
    TASK [setup] *******************************************************************
    ok: [remote_hostname1]
    
    TASK [List files in given dir] *************************************************
    changed: [remote_hostname1]
    
    TASK [stdout] ******************************************************************
    ok: [remote_hostname1] => {
        "out.stdout_lines": [
            "ansible_NOelFI", 
            "ansible.txt", 
            "DataDir_-usr-local-staf-data-STAF.tmp", 
            "hsperfdata_root", 
            "jffi3823846495174998735.tmp", 
            "ssh-2Rbtrunh1OaM", 
            "STAFIPC_STAF", 
            "STAF.tmp", 
            "systemd-private-06v5Nw", 
            "yum_save_tx.2016-09-23.14-37.2smryq.yumtx"
        ]
    }
    
    PLAY RECAP *********************************************************************
    remote_hostname1           : ok=3    changed=1    unreachable=0    failed=0   
    
  7. 使用Ansible中的local_action模块来在本地执行自定义的模块,需要注意在hosts中设置正确的ansible_python_interpreter (我这里把所有额外的dependencies都放在了本地,所以其他的远程机器没有做过多的设置)。

  8. 在playbook中添加strategy: debug的设置可以在程序抛出异常时进入debug模式(有点像pdb),但只能看到playbook那一层的stack(官方文档),通常用来看vars,比如:

    ---
    - hosts: remote_host1
      strategy: debug
      gather_facts: no
      tasks:
      - include: generate_template.yml
        vars:
          templates:
            server/pipeline_set:
              parallelIngestionPipelines: 4
    
  9. 在playbook中include另一个yml文件,并且要传递一些参数时,最好像上面的例子那样添加一个section vars再在里面添加参数,否则Ansible会有Warning message的提示(以后的版本可能非法)。

  10. 在自定义的module中可以存储一些内容到vars里面:

   module.exit_json(
       ansible_facts={
           "generate_template_path": generate_template_path
       }
   )
  1. Ansible的fetch模块只能对单个文件进行操作(为啥copy模块就可以复制整个文件夹,差评!),如果想要拿整个文件夹,则可以使用synchronize模块。如果要避免synchronize模块需要手动填写password的问题(synchronize模块和Ansible用的是不同的socket,所以还需要输密码,再次差评!),我用的是下面这种work around:
   - shell: (cd ; find . -maxdepth 10 -type f) | cut -b 3-
     register: files_to_copy

   - fetch: src=/ dest=
     with_items: ''