<input id="ohw05"></input>
  • <table id="ohw05"><menu id="ohw05"></menu></table>
  • <var id="ohw05"></var>
  • <code id="ohw05"><cite id="ohw05"></cite></code>
    <label id="ohw05"></label>
    <var id="ohw05"></var>
  • Ansible基礎:一篇入門

    Ansible

    官網文檔:

    https://docs.ansible.com/ansible/latest/

    https://docs.ansible.com/ansible/latest/user_guide/index.html

    安裝

    yum安裝

    配置yum源

    $ cat /etc/yum.repos.d/ansible.repo
    [epel]
    name=epel
    baseurl=http://mirrors.aliyun.com/epel/7Server/x86_64/
    enable=1
    gpgcheck=0
    

    安裝ansible

    $ yum -y install ansible
    

    如果有pip工具,也可以運行pip install ansible下載。使用pip下載的ansible不提供默認配置文件/etc/ansible/ansible.cfg。

    安裝完成檢查

    $ ansible --version
    ansible 2.9.25
      config file = /etc/ansible/ansible.cfg
      configured module search path = [u'/home/test/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
      ansible python module location = /usr/lib/python2.7/site-packages/ansible
      executable location = /bin/ansible
      python version = 2.7.5 (default, Oct 14 2020, 14:45:30) [GCC 4.8.5 20150623 (Red Hat 4.8.5-44)]
    

    Ansible參數補全功能(可選)

    從Ansible 2.9版本開始,ansible支持命令的選項補全功能,它依賴于python的argcomplete插件

    $ yum -y install python-argcomplete			# 安裝
    $ activate-global-python-argcomplete		# 激活
    

    激活后,就可以按兩次tab補全命令和選項了。

    環境配置

    配置主機名解析

    在master節點的/etc/hosts文件中,添加上ip主機名對應關系

    $ cat /etc/hosts
    127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
    ::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
    192.168.175.100 master
    192.168.175.101 node1
    192.168.175.102 node2
    

    配置主機互信

    ansible默認采用ssh連接,ssh連接要么使用密碼認證,要么使用公鑰認證。

    這里采用公鑰認證,實現免密登錄,后續使用ansible方便一些。

    $ ssh-keygen -t rsa -f ~/.ssh/id_rsa -N ''
    $ ssh-keyscan 192.168.175.101 >> ~/.ssh/known_hosts 2>/dev/null
    $ ssh-keyscan 192.168.175.102 >> ~/.ssh/known_hosts 2>/dev/null
    $ sshpass -p'hello123' ssh-copy-id root@192.168.175.101
    

    測試:

    [root@master ~]# ssh 192.168.175.101
    Last login: Wed Jan  5 12:24:18 2022 from 192.168.175.1
    [root@node1 ~]# exit
    logout
    Connection to 192.168.175.101 closed.
    [root@master ~]#
    

    發現不用輸入密碼即可直接登錄其它主機。


    安裝、環境配置完成后,ansible還只能控制本機,不能實現對其它節點的批量控制。

    若想實現它的真正功能,必須對它進行配置。

    下面開始了解ansible的幾個重要的配置文件。

    ansible的配置文件

    全局配置文件

    ansible默認的全局配置文件是/etc/ansible/ansible.cfg,可認為是全局配置的入口。

    Ansible支持4種方式指定配置文件,它們的解析順序從上到下

    • ANSIBLE_CFG 環境變量指定的配置文件
    • ansible.cfg 當前目錄下的ansible.cfg
    • ~/.ansible.cfg 家目錄下的.ansible.cfg
    • /etc/ansible/ansible.cfg 默認全局配置文件

    修改默認配置文件/etc/ansible/ansible.cfg:

    $ cat /etc/ansible/ansible.cfg | grep -vE "^$|^#"
    [defaults]
    inventory      = /etc/ansible/hosts			# inventory文件:ansible管理的主機清單
    library        = /usr/share/my_modules/
    forks          = 5					# ansbile的并發連接數
    sudo_user      = root
    remote_port    = 22
    host_key_checking = False
    timeout = 10
    log_path = /var/log/ansible.log
    

    文件中其它的可配置項:

    stdout_callback = debug        # 可將輸出變得人性化;默認輸出會擠在一行,配置后會換行輸出;
    

    inventory主機文件

    inventory文件默認路徑是/etc/ansible/hosts,在這里配置目標主機,ansible便可以對其進行控制。

    /etc/ansible/hosts文件里配置node主機名或ip:

    $ cat hosts
    [default]				#	主機分組
    node1					#	192.168.175.101
    node2					#	192.168.175.102
    

    可以通過/etc/ansible/ansible.cfg文件修改inventory的默認路徑:inventory = /etc/ansible/hosts。如果將該配置指定為目錄,便可以使用多個inventory文件來管理節點,一般很少動這個。

    通常,不會修改默認的路徑。如果有自定義的inventory文件,可以直接在ansible命令行中使用-i選項指定:

    # ansible -i /tmp/my_inventory.ini ...
    # ansible-playbook -i /tmp/my_inventory.ini ...
    

    查看inventory

    列出ansiblek可管理的所有主機

    $ ansible-inventory --graph all			# 指定文件:ansible-inventory -i /etc/ansible/hosts --graph all
    @all:						# all是默認的主機組,包含所有主機
      |--@default:
      |  |--node1
      |  |--node2
      |--@ungrouped:
    

    運行ansible命令

    完成上述的基本配置后,即可以開始使用ansible來批量管理主機了,這里的管理方式為命令行方式(又稱為Ad-hoc方式)。

    Ad-hoc方式運行ansible的命令格式:ansible 主機組/主機 -m 模塊 -a 參數

    選項解析:

    -m:	指定調用的模塊
    -a:	向模塊傳遞的參數,模塊不需要則可省略;參數需要使用引號包圍
    

    此外ansible命令還可以帶上其它選項:

    -i:	指定本次的inventory路徑,指定該參數則后面不加主機組/主機
    -e:	設置變量,格式為'var1="aaa" var="bbb"'
    -v/vv/vvv:	命令輸出的打印級別
    

    ansible的批量管理功能依靠各個模塊來完成,ansible提供了幾千個模塊(其中ansible團隊自己維護大約100多個核心模塊),每個模塊完成各自的作用。

    下面用ping模塊和debug模塊來演示一下ansible的基礎功能。

    ping模塊

    ping模塊是ansible最基礎模塊之一,可用于檢測遠程主機是否在線。

    命令:ansible 主機組/主機 -m ping

    返回值:changed、ping

    命令:ansible all -m ping

    [root@master ansible]$ ansible all -m ping
    node2 | SUCCESS => {
        "ansible_facts": {
            "discovered_interpreter_python": "/usr/bin/python"
        },
        "changed": false,
        "ping": "pong"
    }
    node1 | SUCCESS => {
        "ansible_facts": {
            "discovered_interpreter_python": "/usr/bin/python"
        },
        "changed": false,
        "ping": "pong"
    }
    

    debug模塊

    官網說明:https://docs.ansible.com/ansible/latest/collections/ansible/builtin/debug_module.html

    debug模塊用于輸出或調試一些變量或數據。該模塊共有三個參數msg、var、verbosity。

    msg:打印配置的信息
    var:打印變量值
    verbosity:運行級別,設置為3,則-vvv或更高才會打印輸出
    

    命令:

    ansible all -m debug -a 'msg="hello world"'

    ansible all -e 'str="hello world"' -m debug -a 'var=str'

    ansible all -v -m debug -a 'msg="hello world" verbosity=1'

    示例:

    [root@master ansible]$ ansible all -m debug -a 'msg="hello world"'
    node1 | SUCCESS => {
        "msg": "hello world"
    }
    node2 | SUCCESS => {
        "msg": "hello world"
    }
    [root@master ansible]$ ansible all -e 'str="hello world"' -m debug -a 'var=str'
    node1 | SUCCESS => {
        "str": "hello world"
    }
    node2 | SUCCESS => {
        "str": "hello world"
    }
    
    [root@master ansible]$ ansible all -m debug -a 'msg="hello world" verbosity=1'
    node1 | SKIPPED
    node2 | SKIPPED
    [root@master ansible]$ ansible all -v -m debug -a 'msg="hello world" verbosity=1'
    Using /etc/ansible/ansible.cfg as config file
    node1 | SUCCESS => {
        "msg": "hello world"
    }
    node2 | SUCCESS => {
        "msg": "hello world"
    }
    

    playbook

    playbook是一個yaml格式的文件,由一個或多個play按順序列表的方式組成。每個play運行一個或多個task,每個task調用一個模塊module

    playbook、play、task的關系

    • playbook中可以定義一個或多個play
    • 每個play中可以定義一個或多個task
      • 其中還可以定義兩類特殊的task:pre_tasks和post_tasks
      • pre_tasks表示執行執行普通任務之前執行的任務列表
      • post_tasks表示普通任務執行完之后執行的任務列表
    • 每個play都需要通過hosts指令指定要執行該play的目標主機
    • 每個play都可以設置一些該play的環境控制行為,比如定義play級別的變量
    playbook01

    編寫一個playbook并執行

    playbook使用yaml語法格式組織各種playtask規則。

    下面使用ping模塊和debug模塊編寫一個playbook文件如下:

    # cat test.yaml
    ---
    - name: play1						# play的名稱,非必須
      hosts: all						# 指定目標主機
      gather_facts: false					# 收集目標主機信息,默認值true,非必須
      tasks:						# tasks聲明任務列表
        - name: task1					# task任務名稱,非必須
          ping:						# 模塊
            data: "pong task1"				# 模塊參數
        - name: task2
          ping:
            data: "pong task2"
    - name: play2
      hosts: all
      gather_facts: false
      tasks:
        - name: task1
          debug:
            msg: "hello task1 in play2"
        - name: task2
          debug:
            msg: "hello task2 in play2"
    

    注意所有的-:符號后面均需要接一個空格

    執行playbook的命令是ansible-playbook test.yaml

    該命令同樣支持像ansile命令一樣的多個選項,如-e-i-v

    $ ansible-playbook -v test.yaml			
    Using /etc/ansible/ansible.cfg as config file
    
    PLAY [play1] **********************************************************************************************************************
    
    TASK [task1] **********************************************************************************************************************
    ok: [node2] => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": false, "ping": "pong task1"}
    ok: [node1] => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": false, "ping": "pong task1"}
    
    TASK [task2] **********************************************************************************************************************
    ok: [node1] => {"changed": false, "ping": "pong task2"}
    ok: [node2] => {"changed": false, "ping": "pong task2"}
    
    PLAY [play2] **********************************************************************************************************************
    
    TASK [task1] **********************************************************************************************************************
    ok: [node1] => {
        "msg": "hello task1 in play2"
    }
    ok: [node2] => {
        "msg": "hello task1 in play2"
    }
    
    TASK [task2] **********************************************************************************************************************
    ok: [node1] => {
        "msg": "hello task2 in play2"
    }
    ok: [node2] => {
        "msg": "hello task2 in play2"
    }
    
    PLAY RECAP ************************************************************************************************************************
    node1                      : ok=4    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
    node2                      : ok=4    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
    

    playbook文件各配置指令及含義

    yaml文件中,使用-表示一個列表元素,多個key:value表示一個字典。

    每個playbook使用列表來組織多個playplay內同樣使用列表來組織多個task;play和task自身則采用字典的方式組織,即多個鍵值對。

    在playbook頂層使用- xxx:表示這是一個play;每個play中必須包含hoststasks指令。

    hosts指令用來指定要執行該play的目標主機,可以是主機名、主機組或者其它多種方式。

    tasks指令用來指定該play中包含的任務列表,每個任務使用- xxx:方式表示。

    name指令用來設置play和task的名稱,值具有唯一性。

    gather_facts指令用來收集目標主機的信息,由setup模塊提供。默認情況下,每個play都先執行這個特殊任務,收集完信息才開始其它任務。如果后續任務中用不到該信息,則可以禁止掉該任務,提升效率。

    向模塊傳遞參數

    yaml中,向模塊傳遞參數的方式總結為字符串和數組兩種方式。

    還是以debug模塊為例。

    數組方式如上面test.yaml文件里的傳參,即key: value的形式:

    ......
    - name: task1
          debug:
            msg: "hello task1 in play2"
            verbosity=1
    

    字符串方式,由于yaml的語法規則(字符串換行將自動轉換為空格),又有不同的書寫形式:

    ---
    - name: debug
      hosts: all
      gather_facts: false
      tasks:
        - name: task1
          debug:
            msg="hello task1" verbosity=1			# 參數寫成一行
        - name: task2	
          debug:
            msg="hello task2"				# 參數寫成多行
            verbosity=1
        - name: task3
          debug: |		# 豎線|,將保留字符串的換行符,否則將自動轉換成空格,在一些模塊中很有用,如shell
            msg="hello task3"						
            verbosity=1
        - name: task4
          debug: >						# 符號>,效果和直接寫成多行一樣
            msg="hello task4"
            verbosity=1
    

    還可以直接使用指令args指明參數:

    ......
    - name: task1
          debug:
          args:
            msg: "hello task1 in play2"
    

    默認的任務執行策略

    /etc/ansible/ansible.cfg文件的forks配置,決定ansible執行任務的并發連接數。

    假如forks配置為5,那么ansible第一次將同時連接5個node節點執行任務。其中若有節點提前執行完任務, 則ansible會新建一個新進程,來連接下一個節點執行任務。

    forks是保證最多有N個節點同時執行任務。

    常見模塊

    shell模塊

    說明:

    shell模塊接收shell命令,命令后可跟空格分割的參數列;

    必須傳入自由格式的命令,或者cmd參數;

    它十分類似command模塊,但是它在遠程主機上通過shell(比如/bin/bash)來運行命令;

    shell模塊相比command模塊,支持解析特殊符號<>|;&等。

    參數:

    Parameter Choices/Defaults 說明
    chdir path 運行腳本前,切換到相關目錄
    cmd string 需要運行的命令,后跟可選的參數
    creates path 若一個文件或目錄存在,則跳過該步驟
    removes path 若一個文件或目錄不存在,則跳過該步驟
    executable path 改變執行命令的解釋器,如/bin/bash、/usr/bin/expect、/usr/bin/python;絕對路徑
    free_form string 自由格式的命令(即命令字符串,沒有相關參數,直接寫在shell模塊后面即可)
    stdin string Set the stdin of the command directly to the specified value.
    stdin_add_newline boolean Choices:noyes Whether to append a newline to stdin data.
    warn boolean Choices:noyes Whether to enable task warnings.

    示例:

    ---
    - name: shell
      hosts: all
      gather_facts: no
      tasks:
        - name: task1
          shell:
            hostname
        - name: task2
          shell:
            cmd: date +"%F %T"
        - name: task3
          shell: 
            cmd: pwd
            chdir: /etc/sysconfig
        - name: task4
          shell:
            cmd: ls /tmp
            creates: /tmp
        - name: task5
          shell:
            cmd: print('hello world')
            executable: /usr/bin/python
    

    注意chdir參數只支持下面的方式:

    - name: task3
          shell: 
            cmd: pwd
            chdir: /etc/sysconfig
    - name: task3
          shell: pwd				# free_from格式需要采用args參數,顯式指定chdir參數
          args:
            chdir: /etc/sysconfig
    # 下面的方式將發生錯誤
    - name: task3
          shell: pwd
            chdir: /etc/sysconfig
    

    Ad-hoc方式:

    $ ansible all -m shell -a "ls -l | wc -l"
    $ ansible all -v -m shell -a "ls chdir=/tmp "
    

    https://docs.ansible.com/ansible/latest/collections/ansible/builtin/shell_module.html#ansible-collections-ansible-builtin-shell-module

    script模塊

    說明:

    script模塊接受一個腳本名稱,后面可跟空格分割的參數列;

    支持自由格式的命令,或者cmd參數;

    將本地腳本傳輸到遠程主機上執行;

    在遠程主機上使用shell環境執行腳本;

    該模塊不需要python,類似raw模塊。

    參數:

    Parameter Choices/Defaults 說明
    chdir path 運行腳本前,切換到遠程主機的相關目錄
    cmd string 需要運行的腳本路徑,后跟可選的參數
    creates path 若一個文件或目錄存在,則跳過該步驟
    removes path 若一個文件或目錄不存在,則跳過該步驟

    示例:

    - name: Run a script with arguments (free form)
      ansible.builtin.script: /some/local/script.sh --some-argument 1234
    
    - name: Run a script with arguments (using 'cmd' parameter)
      ansible.builtin.script:
        cmd: /some/local/script.sh --some-argument 1234
    
    - name: Run a script only if file.txt does not exist on the remote node
      ansible.builtin.script: /some/local/create_file.sh --some-argument 1234
      args:
        creates: /the/created/file.txt
    
    - name: Run a script only if file.txt exists on the remote node
      ansible.builtin.script: /some/local/remove_file.sh --some-argument 1234
      args:
        removes: /the/removed/file.txt
    
    - name: Run a script using an executable in a non-system path
      ansible.builtin.script: /some/local/script
      args:
        executable: /some/remote/executable
    
    - name: Run a script using an executable in a system path
      ansible.builtin.script: /some/local/script.py
      args:
        executable: python3
    

    Ad-hoc:

    $ ansible all -m script -a "/tmp/hello.sh world"
    $ ansible all -m script -a "/tmp/hello.sh world creates=/tmp"
    

    https://docs.ansible.com/ansible/latest/collections/ansible/builtin/script_module.html#ansible-collections-ansible-builtin-script-module

    hostname模塊

    說明:

    設置系統的主機名

    參數:

    Parameter Choices/Defaults 說明
    name string / required 設置主機名

    示例:

    - name: Set a hostname
      ansible.builtin.hostname:
        name: web01
    
    - name: Set a hostname specifying strategy
      ansible.builtin.hostname:
        name: web01
        use: systemd
    

    https://docs.ansible.com/ansible/latest/collections/ansible/builtin/hostname_module.html

    在playbook中設置變量

    vars指令可在play或task中設置變量,可以設置一個或多個。可以采用字典或列表的形式定義變量。

    字典變量的定義和引用

    定義:

    vars:
        foo1:
          a: hello
          b: world
        foo2:
          a: aaa
          b: bbb
    

    引用:

    使用點號或方括號,在yaml文件使用jinja2語法引用,需要加單雙引號,否則解析yaml的時候將報錯

    - name: task1
      debug:
        msg: "{{ foo1.a }} {{ foo1['b'] }}"		# 注意{{}}是jinja2的語法,在yaml文件中需要使用引號引起來,單雙引號都行
    - name: task2
      debug:
        var: foo1.a,foo1['b']			# debug模塊的var參數,多個值使用逗號分隔,且無需花括號,前后加不加引號均可
    - name: task3
      debug:
        msg: '{{ foo2.a }} {{ foo2.b }}'
    

    示例:

    ---
    - name: vars play
      hosts: all
      gather_facts: no
      vars:
        foo1:
          a: hello
          b: world
        foo2:
          a: aaa
          b: bbb
      tasks:
        - name: task1
          debug:
            msg: "{{ foo1.a }} {{ foo1['b'] }}"				
        - name: task2
          debug:
            var: foo1.a,foo1['b']
        - name: task3
          debug:
            msg: '{{ foo2.a }} {{ foo2.b }}'
    

    列表變量的定義和引用

    定義:

    vars:
        foo:
          - a: hello
            b: world
          - a: aaa
            b: bbb
    

    引用:

    - name: task1
      debug:
        msg: "{{ foo[0].a }} {{ foo[0].b }}"
    - name: task2
      debug:
        msg: "{{foo[1]['a']}} {{foo[1]['b']}}"
    

    引用變量時,使用點號比較方便,但如果變量名本身帶點,則盡量選擇方括號的方式。

    when指令進行條件判斷

    when指令是ansible提供的唯一一個通用條件指令。when指令后的變量引用不需要雙花括號,當when指令的值為true時,執行任務。

    yaml文件如下:

    ---
    - name: when
      hosts: all
      gather_facts: no
      vars:
        foo: test
      tasks:
        - name: task1
          when: foo == "test"			# when指令的變量可直接引用
          debug:
            msg: "hello"
        - name: task2
          when: foo == "dev"
          debug:
            msg: "world"
    

    示例:

    從輸出中可看出,任務task2由于條件不滿足自動跳過

    [root@master ansible]# ansible-playbook -v when.yaml
    Using /etc/ansible/ansible.cfg as config file
    
    PLAY [when] ***********************************************************************************************************************
    
    TASK [task1] **********************************************************************************************************************
    ok: [node1] => {
        "msg": "hello"
    }
    ok: [node2] => {
        "msg": "hello"
    }
    
    TASK [task2] **********************************************************************************************************************
    skipping: [node1] => {}
    skipping: [node2] => {}
    
    PLAY RECAP ************************************************************************************************************************
    node1                      : ok=1    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0
    node2                      : ok=1    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0
    

    多個判斷條件:

    - name: task3
      when: foo == "dev" or foo == "test"			# 邏輯或
      debug:
        msg: "hello world"
    - name: task4
      when: (foo == "dev") or (foo == "test")		# 支持括號括起來
      debug:
        msg: "hello world"
    - name: task5
      when: (foo == "dev") and (foo == "test")		# 邏輯與
      debug:
        msg: "hello world"
    - name: task6
      when:							
        - foo == "dev"					# 邏輯與的另一種組織方式
        - foo == "test"
      debug:
        msg: "hello world"
    - name: task7
      when: foo != "dev"					# 邏輯否
      debug:
        msg: "hello world"
    

    loop循環結構

    loop指令中的各項元素將以item變量名進行迭代。

    直接迭代列表

    迭代簡單列表:

    列表元素為字符串

    - name: task1
      shell: "{{ item }}"
      loop:
        - hostname
        - "uptime -p"
    

    示例:

    [root@master ansible]# cat loop.yaml
    ---
    - name: loop
      hosts: all
      gather_facts: no
      tasks:
        - name: task1
          shell: "{{ item }}"
          loop:
            - hostname
            - "uptime -p"
    [root@master ansible]# ansible-playbook -v loop.yaml
    Using /etc/ansible/ansible.cfg as config file
    
    PLAY [loop] ***********************************************************************************************************************
    
    TASK [task1] **********************************************************************************************************************
    changed: [node2] => (item=hostname) => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "ansible_loop_var": "item", "changed": true, "cmd": "hostname", "delta": "0:00:00.020436", "end": "2022-01-09 15:05:17.126971", "item": "hostname", "rc": 0, "start": "2022-01-09 15:05:17.106535", "stderr": "", "stderr_lines": [], "stdout": "node2", "stdout_lines": ["node2"]}
    changed: [node1] => (item=hostname) => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "ansible_loop_var": "item", "changed": true, "cmd": "hostname", "delta": "0:00:00.361005", "end": "2022-01-09 15:05:17.409020", "item": "hostname", "rc": 0, "start": "2022-01-09 15:05:17.048015", "stderr": "", "stderr_lines": [], "stdout": "node1", "stdout_lines": ["node1"]}
    changed: [node2] => (item=uptime -p) => {"ansible_loop_var": "item", "changed": true, "cmd": "uptime -p", "delta": "0:00:00.006628", "end": "2022-01-09 15:05:17.610052", "item": "uptime -p", "rc": 0, "start": "2022-01-09 15:05:17.603424", "stderr": "", "stderr_lines": [], "stdout": "up 3 weeks, 2 days, 19 hours, 42 minutes", "stdout_lines": ["up 3 weeks, 2 days, 19 hours, 42 minutes"]}
    changed: [node1] => (item=uptime -p) => {"ansible_loop_var": "item", "changed": true, "cmd": "uptime -p", "delta": "0:00:00.074039", "end": "2022-01-09 15:05:18.006283", "item": "uptime -p", "rc": 0, "start": "2022-01-09 15:05:17.932244", "stderr": "", "stderr_lines": [], "stdout": "up 3 weeks, 2 days, 19 hours, 42 minutes", "stdout_lines": ["up 3 weeks, 2 days, 19 hours, 42 minutes"]}
    
    PLAY RECAP ************************************************************************************************************************
    node1                      : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
    node2                      : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
    

    迭代hash哈希列表:

    列表元素為字典

    - name: task1
      shell:
        cmd: "{{ item.cmd }}"
        creates: "{{ item['condition'] }}"				# 兩種引用方式均可
      loop:
        - { cmd: 'hostname', condition: '/tmp' }			# 這個循環將被跳過
        - { cmd: 'uptime', condition: '/aaa' }					
    

    示例:

    [root@master ansible]# cat loop.yaml
    ---
    - name: loop
      hosts: all
      gather_facts: no
      tasks:
        - name: task1
          shell:
            cmd: "{{ item.cmd }}"
            creates: "{{ item.condition }}"
          loop:
            - { cmd: 'hostname', condition: '/tmp' }		# 這個循環將被跳過,["skipped, since /tmp exists"],任務沒有跳過
            - { cmd: 'uptime', condition: '/aaa' }				
    [root@master ansible]#
    [root@master ansible]# ansible-playbook -v loop.yaml
    Using /etc/ansible/ansible.cfg as config file
    
    PLAY [loop] ***********************************************************************************************************************
    
    TASK [task1] **********************************************************************************************************************
    ok: [node2] => (item={u'cmd': u'hostname', u'condition': u'/tmp'}) => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "ansible_loop_var": "item", "changed": false, "cmd": "hostname", "item": {"cmd": "hostname", "condition": "/tmp"}, "rc": 0, "stdout": "skipped, since /tmp exists", "stdout_lines": ["skipped, since /tmp exists"]}
    changed: [node2] => (item={u'cmd': u'uptime', u'condition': u'/aaa'}) => {"ansible_loop_var": "item", "changed": true, "cmd": "uptime", "delta": "0:00:00.024675", "end": "2022-01-09 15:21:23.286758", "item": {"cmd": "uptime", "condition": "/aaa"}, "rc": 0, "start": "2022-01-09 15:21:23.262083", "stderr": "", "stderr_lines": [], "stdout": " 15:21:23 up 23 days, 19:58,  2 users,  load average: 0.13, 0.14, 0.20", "stdout_lines": [" 15:21:23 up 23 days, 19:58,  2 users,  load average: 0.13, 0.14, 0.20"]}
    ok: [node1] => (item={u'cmd': u'hostname', u'condition': u'/tmp'}) => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "ansible_loop_var": "item", "changed": false, "cmd": "hostname", "item": {"cmd": "hostname", "condition": "/tmp"}, "rc": 0, "stdout": "skipped, since /tmp exists", "stdout_lines": ["skipped, since /tmp exists"]}
    changed: [node1] => (item={u'cmd': u'uptime', u'condition': u'/aaa'}) => {"ansible_loop_var": "item", "changed": true, "cmd": "uptime", "delta": "0:00:00.386491", "end": "2022-01-09 15:21:30.029184", "item": {"cmd": "uptime", "condition": "/aaa"}, "rc": 0, "start": "2022-01-09 15:21:29.642693", "stderr": "", "stderr_lines": [], "stdout": " 15:21:30 up 23 days, 19:58,  2 users,  load average: 0.90, 0.51, 0.35", "stdout_lines": [" 15:21:30 up 23 days, 19:58,  2 users,  load average: 0.90, 0.51, 0.35"]}
    
    PLAY RECAP ************************************************************************************************************************
    node1                      : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
    node2                      : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
    

    迭代字典

    loop指令無法直接迭代字典,需要使用過濾器dict2items進行轉換,如下:

    - name: Using dict2items
      ansible.builtin.debug:
        msg: "{{ item.key }} - {{ item.value }}"
      loop: "{{ tag_data | dict2items }}"
      vars:
        tag_data:
          Environment: dev
          Application: payment
    

    上例中,通過迭代tag_data來打印它的key和value。

    when指令和loop指令同時使用時,先進行循環,再在每個循環中進行條件判斷。

    notify和handlers

    ansible中的一個重要概念changed,它表示目標狀態是否發生改變,即本次任務是否執行、執行后是否影響結果。如果changed=1,則表示目標狀態發生改變;如果changed=0,則表示目標狀態未發生改變,或者任務沒有執行。

    ansible若監視到changed=1,就會觸發notify指令所定義的handler。handler,也是一個task,只是定義在handlers中,需要notify來觸發執行。

    handlers的使用與tasks使用一樣,notify和hanlders中任務名稱必須一樣

    當一個play中所有任務都執行完成后,handler才會執行。好處是可以多次觸發notify,但最后只執行一次對應的handler。

    示例:

    ---
    - name: notify and handlers
      hosts: all
      gather_facts: no
      tasks:
        - name: task1
          shell: uptime
          notify: hello
      handlers:
        - name: hello
          debug:
            msg: "hello world"
    

    執行結果:

    [root@master ansible]# ansible-playbook notify.yaml
    
    PLAY [notify and handlers] ********************************************************************************************************
    
    TASK [task1] **********************************************************************************************************************
    changed: [node2]
    changed: [node1]
    
    RUNNING HANDLER [hello] ***********************************************************************************************************
    ok: [node2] => {}
    
    MSG:
    
    hello world
    ok: [node1] => {}
    
    MSG:
    
    hello world
    
    PLAY RECAP ************************************************************************************************************************
    node1                      : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
    node2                      : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
    

    組織playbook

    將所有play全部寫在一個yaml文件中,固然可行,但是可讀性、維護性太差。

    比較好的做法是,將同類任務的play放在一個文件中。多個任務,則寫成多個文件,最后使用一個入口文件來引用這些任務文件。

    假設入口文件名為main.yaml,在該文件中使用import_playbook指令引用其它playbook:

    ---
    - import_playbook: "init_server/aaa.yaml"
    - import_playbook: "init_server/bbb.yaml"
    - import_playbook: "init_server/ccc.yaml"
    - import_playbook: "init_server/ddd.yaml"
    

    執行方式不變:ansible-playbook main.yaml

    組織各類內容:task、handler、變量

    上面介紹了使用playbook指令來引入多個playbook文件,從而提高可讀性和維護性。

    其實,ansible還提供了更加規范的方式,來組織更多的內容,即rolecolllection,collection這里暫不涉及。

    ansible可組織的內容包括:

    • playbook
    • task
    • variable
    • handler(handler也是一個task,只是編寫在handlers內部)
    • role

    可組織的意思是說,可將相同內容定義在同一個文件中,然后使用相關指令來引入指定文件內容。

    • 引入palybookimport_playbook
    • 引入taskhandlerimport_tasksinclude_tasks
    • 引入variablevars_filesinclude_vars
    • 引入roleimport_roleinclude_roleroles

    import和include兩種引入方式有所區別,前者為靜態加載(在playbook解析的階段,內容將寫入到引入的位置),后者為動態加載(解析階段不引入,而是在執行階段才引入)。

    組織tasks

    可將task單獨寫在一個文件中,然后在play里使用import_tasksinclude_tasks模塊引入。

    示例:

    編寫一個tasks文件tasks.yaml:

    ---
    - name: task1
      debug:
        msg: "hello"
    - name: task2
      debug:
        msg: "world"
    

    在playbook中引入:

    ---
    - name: play1
      hosts: node1
      gather_facts: no
      tasks:
        - name: task1 & task2
          import_tasks: tasks.yaml		# include_tasks模塊也行
    

    在循環中引入tasks文件,必須使用include_tasks指令

    ---
    - name: play1
      hosts: node1
      gather_facts: no
      tasks:
        - name: loop tasks
          include_tasks: tasks.yaml
          loop:
            - one
            - two
    

    組織handlers

    一般情況下playbook的handlers如下,在task中使用handler的任務名來觸發

    ---
    - name: play1
      hosts: node1
      gather_facts: no
      tasks:
        - name: task1
          shell: uptime
          notify: h1
        - name: task2
          shell: date
          notify: h2
      handlers:
        - name: h1
          debug:
            msg: "run h1"
        - name: h2
          debug:
            msg: "run h2"
    

    可將handler單獨編寫在一個文件中,如下:

    $ cat handler1.yaml
    ---
    - name: handler1
      debug:
        msg: "run handler1"
    $ cat handler2.yaml
    ---
    - name: handler2
      debug:
        msg: "run handler2"
    
    

    然后在playbook中使用import_tasksinclude_tasks指令來引入,同時在notify中修改對應的handler名:

    ---
    - name: play1
      hosts: node1
      gather_facts: no
      tasks:
        - name: task1
          shell: uptime
          notify: handler1				#	handler1為靜態引入,notify使用handler名
        - name: task2
          shell: date
          notify: h2				#	handler2為動態引入,notify使用引入的任務名
      handlers:
        - name: h1
          import_tasks: handler1.yaml		#	import_tasks靜態引入
        - name: h2
          include_tasks: handler2.yaml		#	include_tasks動態引入
    

    組織變量

    前面已經介紹了如何在playbook中使用var指令直接設置變量,除了這個方法,ansible還支持將變量單獨放在一個文件中,然后在play中使用vars_files指令或include_vars模塊來引入該變量文件。也可以在命令行中,使用-e選項(--extra_vars)來設置變量或引入變量文件。

    vars_filesplay級別的指令,用于play中,在playbook解析階段引入變量文件;

    include_vars是任務模塊(類似模塊一樣),用在tasks中定義一個引入變量的任務,只有該任務執行之后,才會創建變量;

    -e選項在命令行中,全局有效;

    vars_files示例

    變量文件varfile.yaml,變量的定義一樣,使用yaml或json格式,可采用字典或列表的形式。

    ---
    foo:
      a: hello
      b: world
    

    playbook文件如下,使用vars_files指令來引入:

    ---
    - name: play1
      hosts: node1
      gather_facts: no
      vars_files:
         - varfile.yaml				#	多個變量文件,使用列表形式即可
      tasks:
        - name: task1
          debug:
            msg: "{{foo.a}} {{foo.b}}"
    

    include_vars模塊

    include_vars是模塊提供的功能,它是一個手動創建的任務,和shell、debug等模塊一樣。所以只有當任務執行完后,相關變量才會創建。

    下面介紹幾個用法。

    引入一個文件:

    ---
    - name: play1
      hosts: node1
      gather_facts: no
      vars_files:
         - varfile.yaml
      tasks:
        - name: task1
          include_vars: varfile.yaml		# 引入之后,可在后續的任務中使用
        - name: task2
          debug:
            msg: "{{foo.a}} {{foo.b}}"
    

    引入多個文件,可采用循環:

    - name: task1
      include_vars: "{{ item }}"
      loop:
      	- varfile1.yaml
      	- varfile2.yaml
    

    還可以引入目錄,使用條件和其它參數控制引入的變量文件。這里不展開了,該模塊有很多參數和用法,具體可參考官網:

    https://docs.ansible.com/ansible/latest/collections/ansible/builtin/include_vars_module.html#ansible-collections-ansible-builtin-include-vars-module

    -e選項

    ansible-playbook命令的-e選項或--extra-vars選項也可以用來定義變量或引入變量文件:

    # 定義單個變量
    $ ansible-playbook -e 'var1="value1"' xxx.yml
    
    # 定義多個變量
    $ ansible-playbook -e 'var1="value1" var2="value2"' xxx.yml
    
    # 引入單個變量文件
    $ ansible-playbook -e '@varfile1.yml' xxx.yml
    
    # 引入多個變量文件
    $ ansible-playbook -e '@varfile1.yml' -e '@varfile2.yml' xxx.yml
    

    使用role

    上面將各類內容放在單獨的文件中,然后使用相關指令或模塊將其引入。ansible中,有一種更為規范的組織方式,即role

    使用role,即可無需手動使用這些指令或模塊了。按照role指定的文件或目錄存放對應的內容,ansible就會自動引入。

    role的文件結構

    ansible-galaxy init role1命令,可以快速創建一個role框架。

    $ cd /etc/ansible/roles
    $ ansible-galaxy init role01
    $ tree role01
    role01
    ├── defaults
    │   └── main.yml
    ├── files		    # 外部文件,放入此處的文件,在role的各種任務中直接無需使用全路徑
    ├── handlers
    │   └── main.yml	    # 存放handler
    ├── meta
    │   └── main.yml	    # 該role依賴的先行role。定義在此處的role將在該role運行前執行
    ├── README.md
    ├── tasks
    │   └── main.yml	    # 存放任務
    ├── templates		    # 模板文件,放入此處的文件,在role的各種任務中無需使用全路徑		
    ├── tests
    │   ├── inventory
    │   └── test.yml
    └── vars
        └── main.yml	    # 存放變量
    

    在相應目錄及文件下編寫對應內容,然后還需要提供一個入口playbook文件。在入口playbook文件中,使用import_rolesinclude_roleroles指令來引入role。最后使用ansible-playbook命令執行入口文件,即可執行定義在role中的各種任務了。

    enter.yml文件如下:

    ---
    - name: enter for all roles
      hosts: node1
      gather_facts: no
      roles:
        - role01					# 多個role可使用列表一起列出
    

    編寫一個role

    定義role的變量

    etc/ansible/roles/role01/vars/main.yml文件是role定義變量或引入變量文件的地方。

    etc/ansible/roles/role01/defaults/main.yml文件是role定義默認變量的地方,優先級較低,當然也可以引入文件。

    ---
    # vars file for role01
    foo1:
      a: hello
      b: world
    

    定義role的task

    /etc/ansible/roles/role01/tasks/main.yml文件是role定義task或者引入task文件的地方。

    ---
    # tasks file for role01
    - name: task1
      debug:
        msg: "{{ foo1.a }} {{ foo1.b }}"
      notify: handler1
      changed_when: true				# 該指令使得chenged=1,觸發notify
    

    定義role的handler

    /etc/ansible/roles/role01/handles/main.yml文件是role定義handler或者引入handler文件的地方。

    ---
    # handlers file for role01
    - name: handler1
      debug:
        msg: "run handler1"
    

    執行:

    $ ansible-playbook  enter.yml
    
    PLAY [enter for all roles] **********************************************************************************************************
    
    TASK [role01 : task1] ***************************************************************************************************************
    changed: [node1] => {}
    
    MSG:
    
    hello world
    
    RUNNING HANDLER [role01 : handler1] *************************************************************************************************
    ok: [node1] => {}
    
    MSG:
    
    run handler1
    
    PLAY RECAP **************************************************************************************************************************
    node1                      : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
    

    playbook的執行順序

    playbook從上往下執行各個play,在每個play內,從上往下執行各個task

    一個節點執行任務的順序如下:

    • 解析配置/etc/ansible/ansible.cfg
    • 解析inventory
    • gather_facts任務
    • pre_tasks任務
    • pre_tasks任務觸發的handler
    • roles指令加載的role
    • task指令中的任務
    • roles和tasks觸發的handler
    • post_tasks指令中的任務
    • post_tasks中任務觸發的handler

    多個節點時,ansible在所有節點上執行完成前一個任務后,才進入下一個任務的執行流程。handlers是在所有節點上的所有任務執行完成后,開始執行。

    • 從inventory中選擇執行play的相關主機
    • 連接到遠程主機,通常使用ssh方式
    • 拷貝相關模塊到遠程主機,并開始執行

    理解委托任務

    委托是指將原本在一個節點上執行的任務,委托給另一個節點執行。

    要想理解委托,需要先了解ansible任務的執行過程:默認情況下,ansible先選擇執行任務的主機,后連接該主機,再拷貝相關模塊,并在該遠程主機上執行模塊。

    但是在任務中進行了委托后,實際連接主機和執行模塊動作將發生改變。比如將node1主機的任務委托給node2,ansible會根據hosts指令選擇遠程主機node1后,然后根據delegate_to指令,連接到node2節點,并將相關模塊拷貝到node2并在node2上執行。

    以下使用dalegate_to指令做個演示

    test.yaml文件:

    ---
    - name: play1
      hosts: node1
      gather_facts: no
      tasks:
        - name: task1
          shell: hostname
          delegate_to: node2			# 委托node2執行shell模塊
    

    執行ansible-playbook -vvv test.yaml,然后查看具體的執行日志。

    再將delegate_to指令去除,執行ansible-playbook -vvv test.yaml命令查看不委托的執行過程,兩者對比很容易發現ansible連接的主機為委托的主機。

    $ ansible-playbook -vvv test.yaml
    

    打印輸出日志如下,也可在/var/log/ansible.log中查看。

    ansible-playbook 2.9.25
      config file = /etc/ansible/ansible.cfg
      configured module search path = [u'/root/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
      ansible python module location = /usr/lib/python2.7/site-packages/ansible
      executable location = /usr/bin/ansible-playbook
      python version = 2.7.5 (default, Oct 14 2020, 14:45:30) [GCC 4.8.5 20150623 (Red Hat 4.8.5-44)]
    Using /etc/ansible/ansible.cfg as config file
    host_list declined parsing /etc/ansible/hosts as it did not pass its verify_file() method
    script declined parsing /etc/ansible/hosts as it did not pass its verify_file() method
    auto declined parsing /etc/ansible/hosts as it did not pass its verify_file() method
    Parsed /etc/ansible/hosts inventory source with ini plugin
    Skipping callback 'actionable', as we already have a stdout callback.
    Skipping callback 'counter_enabled', as we already have a stdout callback.
    Skipping callback 'debug', as we already have a stdout callback.
    Skipping callback 'dense', as we already have a stdout callback.
    Skipping callback 'dense', as we already have a stdout callback.
    Skipping callback 'full_skip', as we already have a stdout callback.
    Skipping callback 'json', as we already have a stdout callback.
    Skipping callback 'minimal', as we already have a stdout callback.
    Skipping callback 'null', as we already have a stdout callback.
    Skipping callback 'oneline', as we already have a stdout callback.
    Skipping callback 'selective', as we already have a stdout callback.
    Skipping callback 'skippy', as we already have a stdout callback.
    Skipping callback 'stderr', as we already have a stdout callback.
    Skipping callback 'unixy', as we already have a stdout callback.
    Skipping callback 'yaml', as we already have a stdout callback.
    
    PLAYBOOK: test.yaml *****************************************************************************************************************
    1 plays in test.yaml
    
    PLAY [play1] ************************************************************************************************************************
    META: ran handlers
    
    TASK [task1] ************************************************************************************************************************
    task path: /etc/ansible/test.yaml:6
    <node2> ESTABLISH SSH CONNECTION FOR USER: None
    <node2> SSH: EXEC ssh -C -o ControlMaster=auto -o ControlPersist=60s -o Port=22 -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o ConnectTimeout=10 -o ControlPath=/root/.ansible/cp/0d67807d8c node2 '/bin/sh -c '"'"'echo ~ && sleep 0'"'"''
    <node2> (0, '/root\n', '')
    <node2> ESTABLISH SSH CONNECTION FOR USER: None
    <node2> SSH: EXEC ssh -C -o ControlMaster=auto -o ControlPersist=60s -o Port=22 -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o ConnectTimeout=10 -o ControlPath=/root/.ansible/cp/0d67807d8c node2 '/bin/sh -c '"'"'( umask 77 && mkdir -p "` echo /root/.ansible/tmp `"&& mkdir "` echo /root/.ansible/tmp/ansible-tmp-1642857067.57-71643-202799162287554 `" && echo ansible-tmp-1642857067.57-71643-202799162287554="` echo /root/.ansible/tmp/ansible-tmp-1642857067.57-71643-202799162287554 `" ) && sleep 0'"'"''
    <node2> (0, 'ansible-tmp-1642857067.57-71643-202799162287554=/root/.ansible/tmp/ansible-tmp-1642857067.57-71643-202799162287554\n', '')
    <node1> Attempting python interpreter discovery
    <node2> ESTABLISH SSH CONNECTION FOR USER: None
    <node2> SSH: EXEC ssh -C -o ControlMaster=auto -o ControlPersist=60s -o Port=22 -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o ConnectTimeout=10 -o ControlPath=/root/.ansible/cp/0d67807d8c node2 '/bin/sh -c '"'"'echo PLATFORM; uname; echo FOUND; command -v '"'"'"'"'"'"'"'"'/usr/bin/python'"'"'"'"'"'"'"'"'; command -v '"'"'"'"'"'"'"'"'python3.7'"'"'"'"'"'"'"'"'; command -v '"'"'"'"'"'"'"'"'python3.6'"'"'"'"'"'"'"'"'; command -v '"'"'"'"'"'"'"'"'python3.5'"'"'"'"'"'"'"'"'; command -v '"'"'"'"'"'"'"'"'python2.7'"'"'"'"'"'"'"'"'; command -v '"'"'"'"'"'"'"'"'python2.6'"'"'"'"'"'"'"'"'; command -v '"'"'"'"'"'"'"'"'/usr/libexec/platform-python'"'"'"'"'"'"'"'"'; command -v '"'"'"'"'"'"'"'"'/usr/bin/python3'"'"'"'"'"'"'"'"'; command -v '"'"'"'"'"'"'"'"'python'"'"'"'"'"'"'"'"'; echo ENDFOUND && sleep 0'"'"''
    <node2> (0, 'PLATFORM\nLinux\nFOUND\n/usr/bin/python\n/usr/bin/python2.7\n/usr/libexec/platform-python\n/usr/bin/python\nENDFOUND\n', '')
    <node2> ESTABLISH SSH CONNECTION FOR USER: None
    <node2> SSH: EXEC ssh -C -o ControlMaster=auto -o ControlPersist=60s -o Port=22 -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o ConnectTimeout=10 -o ControlPath=/root/.ansible/cp/0d67807d8c node2 '/bin/sh -c '"'"'/usr/bin/python && sleep 0'"'"''
    <node2> (0, '{"osrelease_content": "NAME=\\"CentOS Linux\\"\\nVERSION=\\"7 (Core)\\"\\nID=\\"centos\\"\\nID_LIKE=\\"rhel fedora\\"\\nVERSION_ID=\\"7\\"\\nPRETTY_NAME=\\"CentOS Linux 7 (Core)\\"\\nANSI_COLOR=\\"0;31\\"\\nCPE_NAME=\\"cpe:/o:centos:centos:7\\"\\nHOME_URL=\\"https://www.centos.org/\\"\\nBUG_REPORT_URL=\\"https://bugs.centos.org/\\"\\n\\nCENTOS_MANTISBT_PROJECT=\\"CentOS-7\\"\\nCENTOS_MANTISBT_PROJECT_VERSION=\\"7\\"\\nREDHAT_SUPPORT_PRODUCT=\\"centos\\"\\nREDHAT_SUPPORT_PRODUCT_VERSION=\\"7\\"\\n\\n", "platform_dist_result": ["centos", "7.9.2009", "Core"]}\n', '')
    Using module file /usr/lib/python2.7/site-packages/ansible/modules/commands/command.py
    <node2> PUT /root/.ansible/tmp/ansible-local-71634Zeeqqp/tmpWHppzz TO /root/.ansible/tmp/ansible-tmp-1642857067.57-71643-202799162287554/AnsiballZ_command.py
    <node2> SSH: EXEC sftp -b - -C -o ControlMaster=auto -o ControlPersist=60s -o Port=22 -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o ConnectTimeout=10 -o ControlPath=/root/.ansible/cp/0d67807d8c '[node2]'
    <node2> (0, 'sftp> put /root/.ansible/tmp/ansible-local-71634Zeeqqp/tmpWHppzz /root/.ansible/tmp/ansible-tmp-1642857067.57-71643-202799162287554/AnsiballZ_command.py\n', '')
    <node2> ESTABLISH SSH CONNECTION FOR USER: None
    <node2> SSH: EXEC ssh -C -o ControlMaster=auto -o ControlPersist=60s -o Port=22 -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o ConnectTimeout=10 -o ControlPath=/root/.ansible/cp/0d67807d8c node2 '/bin/sh -c '"'"'chmod u+x /root/.ansible/tmp/ansible-tmp-1642857067.57-71643-202799162287554/ /root/.ansible/tmp/ansible-tmp-1642857067.57-71643-202799162287554/AnsiballZ_command.py && sleep 0'"'"''
    <node2> (0, '', '')
    <node2> ESTABLISH SSH CONNECTION FOR USER: None
    <node2> SSH: EXEC ssh -C -o ControlMaster=auto -o ControlPersist=60s -o Port=22 -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o ConnectTimeout=10 -o ControlPath=/root/.ansible/cp/0d67807d8c -tt node2 '/bin/sh -c '"'"'/usr/bin/python /root/.ansible/tmp/ansible-tmp-1642857067.57-71643-202799162287554/AnsiballZ_command.py && sleep 0'"'"''
    <node2> (0, '\r\n{"changed": true, "end": "2022-01-22 21:11:09.433351", "stdout": "node2", "cmd": "hostname", "rc": 0, "start": "2022-01-22 21:11:09.333195", "stderr": "", "delta": "0:00:00.100156", "invocation": {"module_args": {"creates": null, "executable": null, "_uses_shell": true, "strip_empty_ends": true, "_raw_params": "hostname", "removes": null, "argv": null, "warn": true, "chdir": null, "stdin_add_newline": true, "stdin": null}}}\r\n', 'Shared connection to node2 closed.\r\n')
    <node2> ESTABLISH SSH CONNECTION FOR USER: None
    <node2> SSH: EXEC ssh -C -o ControlMaster=auto -o ControlPersist=60s -o Port=22 -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o ConnectTimeout=10 -o ControlPath=/root/.ansible/cp/0d67807d8c node2 '/bin/sh -c '"'"'rm -f -r /root/.ansible/tmp/ansible-tmp-1642857067.57-71643-202799162287554/ > /dev/null 2>&1 && sleep 0'"'"''
    <node2> (0, '', '')
    changed: [node1 -> node2] => {
        "changed": true, 
        "cmd": "hostname", 
        "delta": "0:00:00.100156", 
        "end": "2022-01-22 21:11:09.433351", 
        "invocation": {
            "module_args": {
                "_raw_params": "hostname", 
                "_uses_shell": true, 
                "argv": null, 
                "chdir": null, 
                "creates": null, 
                "executable": null, 
                "removes": null, 
                "stdin": null, 
                "stdin_add_newline": true, 
                "strip_empty_ends": true, 
                "warn": true
            }
        }, 
        "rc": 0, 
        "start": "2022-01-22 21:11:09.333195"
    }
    
    STDOUT:
    
    node2
    META: ran handlers
    META: ran handlers
    
    PLAY RECAP **************************************************************************************************************************
    node1                      : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
    

    到此為止,ansible的基礎算是過了一遍,對ansible的基礎概念、用法有了一個大概的了解,作為入門是夠了。

    但是ansible的內容和細節還有很多,包括不限于:

    • inventory更加復雜的定義方式

    • 各種級別的指令

    • 眾多類型的變量和其作用

    • 各種條件判斷

    • 各類循環,以及其它控制流程

    • 文件如何加載解析、任務執行的順序及方式

    • 更多的模塊、插件

    • role、vault、jinja2

    • 配置文件

    • 二次開發

    后續將進一步分享...

    posted @ 2022-02-04 18:02  wuenwuen  閱讀(324)  評論(0編輯  收藏  舉報
    国产美女a做受大片观看