Linux伺服器 Linux伺服器

資工所專業課程上課教材

資工所專業課程 > 課程內容 > 專題 - 使用 Ansible 進行快速佈署 - 善用 playbook 變數

專題 - 使用 Ansible 進行快速佈署 - 善用 playbook 變數

上次更新日期 2020/11/08

雖然 playbook 很好用,但是如果每次要處理,都得要修改 playbook 主設定檔,實在很麻煩!因此, ansible 也提供可以內建變數的功能, 可以讓使用者類似修改設定檔即可,不用動到 ansible code 哩!此外,因為設定檔可能有機密資料,包括密碼資料等。因此, ansible 也內建密碼加密功能,可以提供管理者更有彈性的設計!將祕密資料分離開!同時還能自己設定密碼檔,方便自己記憶啊! 只要將密碼檔的權限設定好即可。

在 Playbook 內使用變數

變數設定向來就是重要的!不論是腳本程式還是一般傳統程式,將會變動的部份拆開到程式最上方,或者是直接獨立成為另一個設定檔, 都是很常見的技巧喔!

  1. 為什麼要使用變數與變數設定規則
    1. 有經驗的 IT 人員都知道,如果想要保持程式碼的乾淨與可重複利用,透過變數來處理是必須的!變數可以簡化很多設定的問題! 畢竟,你只要改變數內容,就能夠將整份程式碼重新跑出一個完全不同的資料!程式碼都不用改!這相當好用!

    2. 至於經常在 ansible 當中,用來作為變數的項目,大致上有這幾項:
      • 使用者帳號名稱
      • 所需要安裝/移除的軟體名稱
      • 需要管理的服務名稱 (重新啟動、關閉等)
      • 需要新建、移除等管理行為的檔名
      • 需要從網路上面下載的檔案 (大部分可能是 url) 等
    3. 在 ansible 當中,變數的命名規則大致上是這樣的:
      • 變數名稱開頭一定要是英文字元
      • 變數名稱只能包含英文字元、數字與底線而已。
  2. 變數設定的位置與位階:
    1. 變數設定的位階主要有幾個思考方向:
      1. 全域設定 (global scope):變數設定在指令列或者是 ansible 設定檔內 (一般就是 ansible.cfg)
      2. playbook 設定 (play scope):將變數設定於 playbook 檔案內
      3. 主機清單檔案設定 (host scope):將變數設定在主機清單列表 (inventory) 檔案內
    2. 如果有相同的變數名稱被設定到不同的地方時,那麼哪一個設定值會生效呢?基本上就跟上面的順序一樣:
      1. 以指令列模式為主
      2. 再來是 playbook 當中的設定
      3. 最後才是 inventory 檔案內的宣告。
  3. 實際進行變數的宣告-使用 playbook 裡面的 vars 以及 vars_files 的應用:
    1. 在 playbook 裡面設計變數,以 vars 來處理:

      最簡單的方法,就是透過在 playbook 裡面加入 vars: 來規範變數名稱與內容即可。舉例來說,當你要建立一個名為 alex 的用戶, 未來可能還有不同的用戶名稱要設計,可以這樣做看看:

      # 請注意必須使用正確的帳號與來到正確的目錄才行!
      $ whoami; pwd
      student
      /home/student/ansible-init
      
      $ cp user_add.yml var_user_add.yml
      $ vim var_user_add.yml
      ---
        - name: add user for managed hosts
          hosts: webserver1
          vars:
            username: alex
            password: $6$CoZmgPgw3LD3Lyi2$6ljLHiSZm0m/luHYwYlE2VVKOSAX8O1DdzFkOHEF7gEr..
            uid: 3102
          tasks:
            - name: add username "{{username}}"
              user:
                user: "{{username}}"
                uid: "{{uid}}"
                password: "{{password}}"
      
      $ ansible-playbook --syntax-check var_user_add.yml
      playbook: var_user_add.yml
      
      $ ansible-playbook var_user_add.yml
      
      PLAY [add user for managed hosts] ************************************************
      
      TASK [Gathering Facts] ***********************************************************
      ok: [webserver1]
      
      TASK [add username "alex"] *******************************************************
      changed: [webserver1]
      
      PLAY RECAP ***********************************************************************
      webserver1 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
      

      幾個重點說明:

      • 在 playbook 裡面,使用 vars: 來規範變數,在 var 底下則同樣使用 YAML 的語法, 設計好所需要的變數與變數值即可;
      • 要應用變數時,最好使用雙引號將兩個大括號包起來的樣式來處理。

      未來若需要修改使用者相關參數,直接改最上方的 vars: 底下的變數內容即可!會比較方便!

    2. 在 playbook 裡面設計變數,以外接 vars_files 來處理:

      上面的資料中,依舊需要修改到設計的 playbook 檔案內容,如果你希望未來不要動到 playbook 主設計檔,怕被其他小夥伴搞亂時, 其實可以透過外接的設定檔!舉例來說,你將某個設定值寫入到其他設定檔,而你的 playbook 直接呼叫該檔案即可, 這樣就可以避免動到 playbook 主檔案了!

      # 先將剛剛的檔案拆成兩部份,一部分還是 playbook,一部分則是完全僅變數
      $ cp var_user_add.yml var_file_user_add.yml
      $ mkdir vars
      $ cp var_file_user_add.yml vars/username.yml
      
      # 修改 playbook 主檔案:
      $ vim var_file_user_add.yml
      ---
        - name: add user for managed hosts
          hosts: webserver1
          vars_files: vars/username.yml
          tasks:
            - name: add username "{{username}}"
              user:
                user: "{{username}}"
                uid: "{{uid}}"
                password: "{{password}}"
      
      # 修改設定檔:
      $ vim vars/username.yml
        username: alex
        password: $6$CoZmgPgw3LD3Lyi2$6ljLHiSZm0m/luHYwYlE2VVKOSAX8O1DdzFkOHEF7gErUUWbvK...
        uid: 3102
      
      $ ansible-playbook --syntax-check var_file_user_add.yml
      $ ansible-playbook var_file_user_add.yml
      
      PLAY [add user for managed hosts] *****************************************************
      
      TASK [Gathering Facts] ****************************************************************
      ok: [webserver1]
      
      TASK [add username "alex"] ************************************************************
      ok: [webserver1]
      
      PLAY RECAP ****************************************************************************
      webserver1 : ok=2 changed=0 unreachable=0 failed=0  skipped=0  rescued=0  ignored=0
      

      同樣的,再次執行系統回報 OK,但是並不會修改到 alex 的任何資料!未來,你可以直接改 vars/username.yml 檔案內容, 然後直接執行 playbook 即可!相當容易修改!

  4. 實際進行變數的宣告-針對不同主機或主機群組,定義不同的變數值-inventory 內,或目錄設計
    1. 將變數直接藏在 inventory 檔案內 -- 官方不建議這樣做:

      有時候,你可能會針對不同的主機給予不同的設定值,例如,不同的 Server 他啟用的服務不相同,所以, 你或許會將同一個變數放在不同的主機上,但是給予個別相異的變數值。舉例來說:

      • website 主機群組需要安裝 httpd 軟體
      • devel 主機群組需要安裝 gcc 軟體
      • dbserver1 需要安裝 mariadb-server 軟體
      • 全部系統的預設管理員帳號稱為 alex
      $ vim inventory
      gitclient
      gitclient[01:20].ksu
      172.17.20.[1:30]
      
      [website]
      webserver1
      dbserver1  pkg=mariadb-server
      
      [devel]
      webserver1
      devnode1
      pronode1
      
      [mylab:children]
      website
      devel
      
      [website:vars]
      pkg=httpd
      
      [devel:vars]
      pkg=gcc
      
      [mylab:vars]
      admuser=alex
      

      未來你想要使用上面的變數來操作你的環境,就可以直接處理。只是,官方真的不建議這樣做,原因是,inventory 這個檔案會變得很複雜, 對於管理員來說,inventory 太複雜不是很好查閱,因為,主機名稱與變數搞在一起,很容易讓下一個承接者搞混,修改也不容易, 因此,上面的設定看看即可,知道有這種設計方式就好了。

    2. 將變數放置到 group_vars/ 以及 host_vars/ 『目錄底下』

      基本上,ansible 官網建議不同的主機名稱、群組所需要的變數,盡量放置在個別的目錄與設定檔當中!而設定檔很簡單, 就放在兩個底下的基本目錄即可 (目錄名稱不可以改變!):

      • group_vars/主機群組檔名:這個目錄放置類似 website, deve, mylab 等群組名稱
      • host_vars/主機名:這個目錄放置類似 dbserver1, webserver1 等主機名
      # 先將 inventory 內容修訂回來
      $ vim inventory
      gitclient
      gitclient[01:20].ksu
      172.17.20.[1:30]
      
      [website]
      webserver1
      dbserver1
      
      [devel]
      webserver1
      devnode1
      pronode1
      
      [mylab:children]
      website
      devel
      
      # 開始設計剛剛指定的兩個主機群組設計:
      $ mkdir group_vars
      $ vim group_vars/website
          pkg: httpd
      
      $ vim group_vars/devel
          pkg: gcc
      
      $ vim group_vars/mylab
          admuser: alex
      
      $ mkdir host_vars
      $ vim host_vars/dbserver1
          pkg: mariadb-server
      

      你必需要注意的是,在 inventory 裡面的變數設定,使用的是傳統的等號的設計。但是在 host_vars 以及 group_vars 目錄底下的檔案, 則是以 YAML 格式來設計的!前面保持空白,使用冒號分隔變數與變數值才對喔! 現在,我們將將上述的資料做個列表,相關的檔案有點類似這樣:

      ansible-init
      ↳-- ansible.cfg
      ↳-- group_vars
      ⇃   ↳-- devel
      ⇃   ↳-- mylab
      ⇃   ↳-- mwebsite
      ↳-- host_vars
      ⇃   ↳-- dbserver1
      

      讓我們開始來安裝這些需要的軟體看看:

      # 建立新的 playbook 檔案內容
      $ vim var_host_install.yml
      ---
        - name: setup servers package
          hosts:
            - website
            - devel
            - mylab
          tasks:
            - name: install packages "{{pkg}}" for different server
              yum:
                name: "{{pkg}}"
                state: latest
      
      $ ansible-playbook --syntax-check var_host_install.yml
      $ ansible-playbook var_host_install.yml
      
      PLAY [setup servers package] **********************************************************
      
      TASK [Gathering Facts] ****************************************************************
      ok: [devnode1]
      ok: [pronode1]
      ok: [webserver1]
      ok: [dbserver1]
      
      TASK [install packages "httpd" for different server] **********************************
      ok: [webserver1]
      changed: [devnode1]
      changed: [dbserver1]
      ok: [pronode1]
      
      PLAY RECAP ****************************************************************************
      dbserver1   : ok=2  changed=1  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0
      devnode1    : ok=2  changed=1  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0
      pronode1    : ok=2  changed=0  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0
      webserver1  : ok=2  changed=0  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0
      

      你可以很輕鬆的發現,不同的主機之間可以共用同一個 playbook,而且各自的軟體並不會衝突! 亦即 webserver1 可以安裝 httpd,devnode1 可以安裝 gcc,dbserver1 可以安裝 mariadb-server。 當然我們知道我們的系統全部的軟體都安裝在同一部主機上,不過,從上面的輸出,則可以明顯的發現, 事實上,不同的主機名稱,安裝的軟體確實是不一樣的!

  5. 實際進行變數的宣告-使用指令列與變數陣列
    1. 使用指令列模式處理變數值:

      事實上,除了在 playbook 與 host_vars/*, group_vars/* 裡面設定好變數名稱與變數值的 YAML 格式設計之外, 你也可以直接在指令列模式輸入變數喔!使用『 -e var=content 』的樣式來處理即可!缺點是,你就得要自己記得曾經下達過的變數內容了! 如下,我們來設計一下使用指令列模式的方式來建置新用戶好了!

      # 先建立需要的建立帳號的 playbook 內容
      $ vim var_cmd_user.yml
      ---
        - name: add "{{username}}" at "{{hostname}}"
          hosts: "{{hostname}}"
          tasks:
            - name: add username "{{username}}"
              user:
                user: "{{username}}"
                password: $6$CoZmgPgw3LD3Lyi2$6ljLHiSZm0m/luHYwYlE2VVKOSAX8O1DdzFkOHEF7g.....
      
      $ ansible-playbook --syntax-check var_cmd_user.yml \
      >  -e "hostname=webserver1 username=checkuser1"
      playbook: var_cmd_user.yml
      
      $ ansible-playbook  var_cmd_user.yml  \
      > -e "hostname=webserver1 username=checkuser1"
      
      PLAY [add "checkuser1" at "webserver1"] **********************************************
      
      TASK [Gathering Facts] ***************************************************************
      ok: [webserver1]
      
      TASK [add username "checkuser1"] *****************************************************
      changed: [webserver1]
      
      PLAY RECAP ***************************************************************************
      webserver1 : ok=2  changed=1  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0
      

      將變數放置到指令列模式的方案,就如上面這樣囉!

    2. 讓變數以陣列形式存在:

      變數也可以使用陣列的格式存在,例如某個變數檔案內容設計如下:

      # 建立變數內容檔案:
      $ vim vars/arrauser.yml
      users:
        melody:
          firstname: melody
          lastname: tsai
          homedir: /home/nis/melody
          password: $6$CoZmgPgw3LD3Lyi2$6ljLHiSZm0m/luHYwYlE2VVKOSAX8O1
        amanda:
          firstname: amanda
          lastname: tsai
          homedir: /home/nis/amanda
          password: $6$CoZmgPgw3LD3Lyi2$6ljLHiSZm0m/luHYwYlE2VVKOSAX8O1
      

      上述檔案會建置出幾個陣列變數,大致內容如下:

      users['melody']['firstname'] == melody
      users['melody']['lastname']  == tsai
      users['melody']['homedir']   == /home/nis/melody
      users['melody']['password']  == $6$CoZmgPgw3LD3Lyi2$6ljLHiSZm0m/luHYwYlE2VVKOSAX8O1
      
      users['amanda']['firstname'] == amanda
      users['amanda']['lastname']  == tsai
      users['amanda']['homedir']   == /home/nis/amanda
      users['amanda']['password']  == $6$CoZmgPgw3LD3Lyi2$6ljLHiSZm0m/luHYwYlE2VVKOSAX8O1
      
      # 另一種使用方式 (官網建議少用)
      users.melody.firstname  == melody
      

      嘗試使用這個設定檔吧!

      $ vim var_array_user.yml
      ---
        - name: add user by array mode
          hosts: webserver1
          vars_files: vars/arrauser.yml
          tasks:
            - name: add username "{{users['melody']['firstname']}}"
              user:
                user: "{{users['melody']['firstname']}}"
                password: "{{users['melody']['password']}}"
      
      $ ansible-playbook --syntax-check var_array_user.yml
      
      $ ansible-playbook  var_array_user.yml
      
      PLAY [add user by array mode] *********************************************************
      
      TASK [Gathering Facts] ****************************************************************
      ok: [webserver1]
      
      TASK [add username "melody"] **********************************************************
      changed: [webserver1]
      
      PLAY RECAP ****************************************************************************
      webserver1  : ok=2  changed=1  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0
      

      這種方式比較適合某些特別的狀態,一般狀態倒是很少使用陣列方式。

  6. 使用 debug 模組搭配 register 註冊變數,取得 ansible 輸出訊息:

    有時候我們可能會需要顯示某個模組的執行結果,而不是單純的 playbook 輸出正確與否而已。你當然可以使用 -vvv 來顯示過程, 只是,如此一來,就每個指令的結果都太詳細!如果你只需要某個指令運作的結果,可以透過『 register: 變數名 』來註冊某個變數, 然後再透過 debug 模組的 var 結果顯示即可!例如,上面的使用者陣列資料,你也可以將他修改成為底下這樣來運作喔:

    $ vim var_array_user.yml
    ---
      - name: add user by array mode
        hosts: webserver1
        vars_files: vars/arrauser.yml
        tasks:
          - name: add username "{{users['melody']['firstname']}}"
            user:
              user: "{{users['melody']['firstname']}}"
              password: "{{users['melody']['password']}}"
            register: output
          - name: show register output
            debug: var=output
    

    如上所示, register 是在第一個任務 (tasks) 裡面,且需要與模組在相同等級的排列位置上,註冊一個名為 output 的變數,這個變數名稱可以隨便你取。 然後新增一個任務,該任務主要就是 debug 模組 (你可以自行 ansible-doc debug 去查詢相關的參數) 顯示 output 的結果而已,相當簡單。

    $ ansible-playbook  var_array_user.yml
    
    PLAY [add user by array mode] ********************************************************
    
    TASK [Gathering Facts] ***************************************************************
    ok: [webserver1]
    
    TASK [add username "melody"] *********************************************************
    ok: [webserver1]
    
    TASK [show register output] **********************************************************
    ok: [webserver1] => {
        "output": {
            "append": false,
            "changed": false,
            "comment": "",
            "failed": false,
            "group": 3104,
            "home": "/home/melody",
            "move_home": false,
            "name": "melody",
            "password": "NOT_LOGGING_PASSWORD",
            "shell": "/bin/bash",
            "state": "present",
            "uid": 3104
        }
    }
    
    PLAY RECAP ***************************************************************************
    webserver1 : ok=3  changed=0  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0
    

    你就可以看到 output 的顯示結果了!

  7. 實做練習:
    1. 建立 remove_web_pkg_content.yml,內容主要在進行某些軟體的移除:
      • 針對的 hosts 為 webserver1
      • 建立名為 pkgs 變數,這個變數內容包含 httpd, php, mariadb-server, mariadb 四個軟體
      • 建立第 1 個任務,透過 file 模組,將 /var/www/html/index.html 刪除
      • 建立第 2 個任務 (tasks),透過 yum 模組,使用變數功能移除上述的軟體,且為自動移除 (autoremove)
    2. 檢查上述 playbook 語法,正確後,執行上述 playbook。
    3. 執行完畢之後,再以 ansible ad hoc 的 shell 模組,使用 rpm -q 去查詢看看上面的軟體是否已經順利移除?
    4. 新增一個名為 add_service.yml 的檔案,內容主要在建立某個服務:

      • 新增一個 play,主要的目的在增加 Server 內的一個服務
      • 這個 play 的主機應用在 webserver1 上面
      • 設定底下的變數:
        • 設定 pkgs 變數,內容有 httpd, mod_ssl, php 三個軟體名稱
        • 設定 service 變數,內容為 httpd 這個服務
        • 設定 fire_rule 變數,內容為 http 這個網路協定服務
        • 設定 content 變數,內容為『姓名 \n 學號 \n』
      • 設定任務:
        • 第一個任務為使用 yum 模組,安裝 pkgs 變數內的軟體,使用狀態為最新
        • 第二個任務為使用 service 模組,啟動 service 變數內的服務,且設定為開機啟動、目前立刻啟動
        • 第三個任務為使用 firewalld 模組,增加 fire_rule 變數內的服務,為持續且目前可應用的防火牆規則
        • 第四個任務為使用 copy 模組,讓 content 變數內的資料可以複製到 webserver1 的 /var/www/html/index.html 這個檔案內。

      • 新增一個 play,主要的目的在使用本機 (localhost) 連線到 webserver1 檢查網頁內容:
      • 這個 play 的主機應用在 localhost 上面
      • 因為不需要切換成為 root,所以務必設定『 become: false 』這個設定值!!
      • 設定任務:
        • 第一個任務為使用 uri 模組,檢測網址為 http://webserver1,狀態碼需要回傳 200(正常回應),同時, 註冊 (register) 一個名為 output 的變數,方便將訊息回傳
        • 第二個任務為使用 debug 模組,將 output 這個註冊的變數顯示出來即可。
    5. 上述檔案執行後的結果會有點像這樣:
      PLAY [add a service] *****************************************************************
      
      TASK [Gathering Facts] ***************************************************************
      ok: [webserver1]
      
      TASK [install packages] **************************************************************
      ok: [webserver1]
      
      TASK [start and enable] **************************************************************
      ok: [webserver1]
      
      TASK [firewalld] *********************************************************************
      ok: [webserver1]
      
      TASK [add index.html] ****************************************************************
      ok: [webserver1]
      
      PLAY [check web server] **************************************************************
      
      TASK [Gathering Facts] ***************************************************************
      ok: [localhost]
      
      TASK [check content] *****************************************************************
      ok: [localhost]
      
      TASK [show browser output] ***********************************************************
      ok: [localhost] => {
          "output": {
              "accept_ranges": "bytes",
              "changed": false,
              "connection": "close",
              "content_length": "20",
              "content_type": "text/html; charset=UTF-8",
              "cookies": {},
              "cookies_string": "",
              "date": "Fri, 06 Nov 2020 21:33:17 GMT",
              "elapsed": 0,
              "etag": "\"14-5b371c58d85c0\"",
              "failed": false,
              "last_modified": "Fri, 06 Nov 2020 15:21:50 GMT",
              "msg": "OK (20 bytes)",
              "redirected": false,
              "server": "Apache/2.4.37 (centos) OpenSSL/1.1.1c",
              "status": 200,
              "url": "http://webserver1"
          }
      }
      
      PLAY RECAP ***************************************************************************
      localhost   : ok=3  changed=0  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0
      webserver1  : ok=5  changed=0  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0
      

使用 ansible-vault 加密檔案

  1. 關於 ansible 的加密機制:
    1. 許多的機密資料,簡單的說,例如密碼資料,大部分都也是紀錄到 group_vars/ 或 host_vars/ 或是 playbook 本身, 以及許多外接的檔案等等。這些檔案預設都是以所有人都可查閱的方式存在的,也就是說,這些資料很容易不小心就流出去來的。 因此,ansible 確實需要使用加密機制來管理這些檔案比較妥當。
    2. ansible 提供了名為 ansible-vault 這個指令來進行檔案的編輯,編輯時,就能夠對這些被編輯的檔案加密了。 使用的加密機制為 AES256 喔!
  2. 關於 ansible-vault 的使用:
    1. 針對具有加密機制的檔案,可以使用底下的方式來建立、查詢與編輯:
      # 基本語法:
      $ export EDITOR=vim
      $ ansible-vault create file.yml   // 建立全新的加密檔案
      $ ansible-vault view   file.yml   // 查閱加密的檔案內容
      $ ansible-vault edit   file.yml   // 編輯加密的檔案內容 (一定會複寫一次)
      
      舉例來說,讓我們來建立一個名為 secret1.yml 的檔案看看: (建立的密碼為 mydic )
      $ ansible-vault create secret1.yml
      New Vault password:           <==輸入 mydic
      Confirm New Vault password:   <==輸入 mydic
      
      你得注意到,預設的 ansible-vault 只會主動呼叫 vi 指令,這是單純的 vi 而不是具有程式編輯的 vim 喔! 因此,建議在載入 bash 時,就主動的將 EDITOR 設定為 vim 比較妥當!
      $ vim ~/.bashrc
      export EDITOR=vim
      
      $ source ~/.bashrc
      
    2. 如果擔心密碼忘記,也能建立密碼檔,然後使用 --vault-password-file= 指定檔名
      $ vim mypassword.txt
      mydic
      
      $ chmod 600 mypassword.txt
      $ ansible-vault edit --vault-password-file=mypassword.txt secret1.yml
      
      這個 mypassword.txt 的檔案,建立只有你自己能夠讀寫的權限即可。當然,你也可以搬離這個目錄, 放置到你的家目錄的其他較為隱密的目錄去,就可以保存好你自己的密碼了。
    3. 使用 encrypt 以及 decrypt 進行加密與解密的動作:
      # 將已經存在的 user_add.yml 加密:
      $ ansible-vault encrypt --vault-password-file=mypassword.txt \
      >  add_service.yml --output=secret2.yml
      Encryption successful
      
      $ ll add_service.yml secret2.yml
      -rw-rw-r--. 1 student student  998 Nov  6 23:21 add_service.yml
      -rw-------. 1 student student 4372 Nov  8 00:36 secret2.yml
      
      $ cat secret2.yml
      $ANSIBLE_VAULT;1.1;AES256
      35366438653330333932363463613533343735366635663332643637636335356364353930363935
      3631396436663632626339333436386138663737313131370a643737303361653464636136383364
      31613763366231303565653934313739613262383366316163313562323463643962333831666230
      ....
      
      $ ansible-vault view --vault-password-file=mypassword.txt secret2.yml
      ---
        - name: add a service
          hosts:
            - webserver1
      ....
      
      這樣就進行了加密!相當簡單吧!那如果需要將加密的檔案解密呢?
      $ cat secret1.yml
      $ANSIBLE_VAULT;1.1;AES256
      30363939386632333666663234393436623138323061363637393234383065336437343061323738
      6133313065643739633538373437396663643334643536320a393432613361373537373535363963
      ....
      
      $ ansible-vault decrypt --vault-password-file=mypassword.txt secret1.yml
      $ cat secret1.yml
      ---
      - name: check
      ....
      
      如果你沒有使用 --output=newoutfile 的話,那麼原本的檔案就會被解密了喔!要注意!要注意!
    4. 使用 rekey 重建密碼:
      # 1. 將 secret1.yml 加密
      $ ansible-vault encrypt secret1.yml
      New Vault password:           <==輸入 mydic
      Confirm New Vault password:   <==輸入 mydic
      Encryption successful
      
      $ ansible-vault rekey secret1.yml
      Vault password:               <==輸入 mydic
      New Vault password:           <==輸入 gogodic
      Confirm New Vault password:   <==輸入 gogodic
      Rekey successful
      
      上述指令會將該檔案的密碼從 mydic 修訂成為 gogodic 喔!那如果你想要使用某個檔案的內容來修改成為正確的密碼, 可以使用 new-vault-password-file 來處理:

      $ ansible-vault rekey --new-vault-password-file=mypassword.txt secret1.yml
      Vault password:              <==輸入 gogodic
      Rekey successful
      
  3. 實做練習: 目的在將使用者帳密保密,然後使用 playbook 引入該設定檔來處理帳號新增
    1. 建立加密用的密碼檔,檔名為 secret3_vault.txt ,內容就是一個明碼的密碼,內容隨便你填寫。 只是權限一定要重新設定為 600 才好!
    2. 先用『 openssl passwd -6 』,然後輸入 mydic 讓系統自動產生一個 sha512 長度的密碼,並將該密碼記錄下來;
    3. 建立 secret3_user_profile.yml 的設定檔,此檔案必須要加密,加密的密碼採用 secret3_vault.txt 的內容, 至於此檔案的內容為:
        username: mysec1
        password: [[剛剛建立的密碼]]
      
    4. 建立名為 secret3_add_user.yml 的 playbook 檔案,內容有點類似這樣 (這個檔案不需要加密):
      ---
        - name: create user
          hosts:
            - webserver1
      
          vars_files:
            - secret3_user_profile.yml
      
          tasks:
            - name: create users "{{username}}"
              user:
                name: "{{username}}"
                password: "{{password}}"
      
    5. 開始執行 secret3_add_user.yml 的內容,分別使用底下三種方式執行看看:
      $ ansible-playbook secret3_add_user.yml
      $ ansible-playbook --ask-vault-pass secret3_add_user.yml
      $ ansible-playbook --vault-password-file=secret3_vault.txt secret3_add_user.yml
      
    6. 使用『 ssh mysec1@client1 』並輸入 mydic 這個密碼,看看能不能登入用戶端電腦?
  4. 最後,當你使用 ls -l 去查閱相關資料時,就可以發現,其實很多加密的檔案,預設的權限都會是 600 的喔!很有趣!

使用 ansible facts 探索 managed hosts 的實際資料

  1. 使用 setup 模組取得 managed host 的參數:
    1. 有些時候,我們得要先知道 managed host 的相關參數之後,才有辦法進行啟動、關閉、安裝軟體等任務,最簡單的方式, 透過 setup 這個模組就可以知道了!
      $ ansible webserver1 -m setup
      webserver1 | SUCCESS => {
          "ansible_facts": {
              "ansible_all_ipv4_addresses": [
                  "192.168.122.1",
                  "172.17.200.1"
              ],
              "ansible_all_ipv6_addresses": [
      webserver1 | SUCCESS => {
          "ansible_facts": {
              "ansible_all_ipv4_addresses": [
                  "192.168.122.1",
                  "172.17.200.1"
              ],
              "ansible_all_ipv6_addresses": [
                  "fe80::5718:f7d5:ab79:9643"
              ],
              "ansible_apparmor": {
                  "status": "disabled"
              },
              "ansible_architecture": "x86_64",
              "ansible_bios_date": "01/01/2007",
              "ansible_bios_version": "0.5.1",
              "ansible_cmdline": {
                  "BOOT_IMAGE": "(hd0,gpt2)/vmlinuz-4.18.0-147.el8.x86_64",
                  "crashkernel": "auto",
                  "quiet": true,
                  "rd.lvm.lv": "centos/swap",
                  "resume": "/dev/mapper/centos-swap",
                  "rhgb": true,
                  "ro": true,
                  "root": "/dev/mapper/centos-root"
              },
      .....
      
      如上所示,會列出這個主機的相關資料。其中比較有趣的,就是 ansible_facts 裡面的資訊!那個是系統基本資料。 這個 setup 的模組,也是每次進行 playbook 時,系統會主動執行的一個模組。
    2. 使用 gather_facts: no 取消 setup 模組:如果你已經知道你的 managed host 系統相關資訊,因此不想要執行 setup 模組時, 也能夠使用 gather_facts 來取消,簡易的設定方式如下:
      ---
        - name: some play book demo
          hosts: someserver
          gather_facts: no
          ...
      
      不過,一般建議保留預設值,還是讓 ansible 去檢查一下 managed host 的狀態會比較好。
  2. ansible_facts 檢測的內容
    1. 基本上,ansible_facts 如同上面 -m setup 的輸出結果一樣,大致上在檢查:
      • 主機名稱 (hostname)
      • 核心版本
      • 網路卡代號
      • IP 位址 (包括 IPv4 與 IPv6)
      • 作業系統的版本 (基本上,就是 distributions)
      • 環境變數的設定值
      • 跟硬體有關的,如 CPU、記憶體、磁碟容量等。
    2. 若想實際知道 ansible_facts 紀錄的資訊主要有哪些,可以透過 debug 模組來分析 ansible_facts 這個變數內容即可! 例如底下這個處理方案:
      $ vim hosts_facts.yml
      ---
        - name: list my managed hosts facts
          hosts: website
      
          tasks:
            - name: list managed hosts facts
              debug:
                var: ansible_facts
      
      $ ansible-playbook hosts_facts.yml
      
      PLAY [list my managed hosts facts] ***************************************************
      
      TASK [Gathering Facts] ***************************************************************
      ok: [dbserver1]
      ok: [webserver1]
      
      TASK [list managed hosts facts] ******************************************************
      ok: [webserver1] => {
          "ansible_facts": {
              "all_ipv4_addresses": [
                  "192.168.122.1",
                  "172.17.200.1"
              ],
      ....
      
      其實,跟 setup 模組內容幾乎是一模一樣耶!
    3. 那麼 ansible_facts 的資料如何取得與使用?其實它有這些變數名稱:
      變數名稱意義
      ansible_facts['hostname']主機名稱,例如 gitserver200
      ansible_facts['fqdn']主機的全名,例如 gitserver200.dic.ksu
      ansible_facts['default_ipv4']['address']IPv4 的 IP 位址
      ansible_facts['interfaces']全部網路卡的代號列表
      ansible_facts['devices']['vda']['partitions']['vda1']['size']相關的磁碟容量
      ansible_facts['dns']['nameservers']DNS 伺服器 IP 列表
      ansible_facts['kernel']核心版本
      例如,如果你覺得第一個 hosts_facts.yml 的資料量太大,超出我們需要的範圍,你需要的只有上面的幾個特殊資訊時, 基本上,可以這樣做:
      $ cp hosts_facts.yml hosts_facts2.yml
      $ vim hosts_facts2.yml
      ---
        - name: list my managed hosts facts
          hosts: website
      
          tasks:
            - name: list managed hosts facts
              debug:
                msg: |
                  The IPv4 IP address is {{ansible_facts['default_ipv4']['address']}}
                  The host name is {{ansible_facts['fqdn']}}
                  The kernel version is {{ansible_facts['kernel']}}
                  The network card is {{ansible_facts['interfaces']}}
      
      $ ansible-playbook hosts_facts2.yml
      
      PLAY [list my managed hosts facts] ***************************************************
      
      TASK [Gathering Facts] ***************************************************************
      ok: [webserver1]
      ok: [dbserver1]
      
      TASK [list managed hosts facts] ******************************************************
      ok: [webserver1] => {
          "msg": "The IPv4 IP address is 172.17.200.1\nThe host name is gitclient200.ksu \n
      The kernel version is 4.18.0-147.el8.x86_64 \nThe network card is ['lo', 'ens3', 
      'virbr0', 'virbr0-nic']\n"
      }
      ok: [dbserver1] => {
          "msg": "The IPv4 IP address is 172.17.200.1\nThe host name is gitclient200.ksu \n
      The kernel version is 4.18.0-147.el8.x86_64 \nThe network card is ['virbr0-nic', 
      'ens3', 'virbr0', 'lo']\n"
      }
      
      PLAY RECAP ***************************************************************************
      dbserver1  : ok=2  changed=0  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0
      webserver1 : ok=2  changed=0  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0
      
      資料量立刻少很多喔!
  3. 使用自訂的 ansible_fact 資料:
    1. 除了預設的 facts 之外,使用者可以自訂『自己主機本身』的 fact 資料,這些資料主要放置在 /etc/ansible/facts.d/*.fact 檔案! 例如底下的範例,在建立屬於本機自己的 fact 相關資料。
      # 先以 root 的身份進行建立 /etc/ansible/facts.d 目錄的功能
      # mkdir /etc/ansible/facts.d
      # setfacl -m u:student:rwx /etc/ansible/facts.d
      
      # 新增 /etc/ansible/fscts.d/myset.fact ,內容主要填寫軟體與服務名稱
      $ vim /etc/ansible/facts.d/myset.fact
      [pkgs]
      pkg_web = httpd
      pkg_db  = mariadb-server
      
      [srv]
      srv_web = httpd
      srv_db  = mariadb
      
      $ cd ~; ansible localhost -m setup | egrep -A3 -B3 'pkg|srv'
              "ansible_local": {
                  "myset": {
                      "pkgs": {
                          "pkg_db": "mariadb-server",
                          "pkg_web": "httpd"
                      },
                      "srv": {
                          "srv_db": "mariadb",
                          "srv_web": "httpd"
                      }
                  }
              },
      
    2. 接下來,如果要使用到上面的自訂變數,基本上會是這樣的:
      ansible_facts['ansible_local']['檔名']['中括號名稱']['變數名稱'] = 變數內容
      
      ansible_facts['ansible_local']['myset']['pkgs']['pkg_web'] = httpd
      ansible_facts['ansible_local']['myset']['pkgs']['pkg_db']  = mariadb-server
      
      ansible_facts['ansible_local']['myset']['srv']['srv_web'] = httpd
      ansible_facts['ansible_local']['myset']['srv']['srv_db']  = mariadb
      
      基本上,鳥哥自己認為,使用變數來處理即可。只是,既然也跟本機有關,了解一下有這種用法也是好的。
  4. 實做練習一:將自定的 myset.fact 檔案,複製到 managed host 上面
    1. 假定你預計讓 website 群組的主機具有 myweb.fact 的 ansible 本機設定,因此需要建立 myweb.fact 檔案, 這個檔案的內容大致上是這樣的:
      [web]
      pkg = httpd
      srv     = httpd
      state   = started
      enabled = true
      fire_srv = http
      
    2. 寫一隻名為 myweb_fact_go.yml 的 playbook,內容是主要針對 website 的主機群組,然後:
      • 設定兩個變數,分別是 src_file ,指向 myweb.fact 檔名,以及 dest_dir,位置在 /etc/ansible/facts.d/
      • 使用 file 模組,使用遞迴方式建立上述 dest_dir 這個目錄
      • 使用 copy 模組,將剛剛建立的檔案,複製到對方的 /etc/ansible/facts.d/myweb.fact 檔案。
    3. 執行上述 playbook 的內容,並觀察輸出資料是否正確執行了。
    4. 使用『 ansible website -m setup | less 』,然後觀察輸出資訊裡面有沒有剛剛指定的 myweb 相關的設定資訊?
  5. 實做練習二:透過每部 managed host 自己的 fact 來處理服務運作的五個步驟
    1. 要先知道上面 myweb.fact 的結果中,會出現的變數名稱類似: ansible_facts['ansible_local']['myweb']['web']['pkg'] = httpd 這樣的格式! 所以,要先知道已經有 5 個新建的 ansible_facts 變數喔!
    2. 複製 add_service.yml 這個以前建立的檔案,複製成 myweb_fact_go2.yml 的 playbook ,並將內部的設計, 全部改以 ansible_facts 的變數取代。
    3. 執行上述的 playbook,確認執行過程沒有問題!