Linux伺服器 Linux伺服器

資工所專業課程上課教材

資工所專業課程 > 課程內容 > 專題 - 使用 Ansible 進行快速佈署 - 環境設定與 ad hoc

專題 - 使用 Ansible 進行快速佈署 - 環境設定與 ad hoc

上次更新日期 2020/10/23

一般來說,系統管理員或者是軟體開發者,可能經常需要配置系統,讓系統 up 或 down。而且也可能需要調校系統。 通常有經驗的 IT 人員,會透過撰寫 shell script 來處理例行的任務,以方便自己的事務處理。然而一般 shell script 不好閱讀, 許多時候腳本也需要 copy 到不同的目的伺服器,因此會造成錯亂。Ansible 可以透過統一控管的方式來處理這些例行工作, 讓管理員可以更快速的佈署系統,此外,Ansible 的設定也比較易讀,方便未來查詢使用。

本週課程主要針對 ansible 的元件介紹,同時設定 control node 與 managed hosts 之間的網路溝通方式, 最終以 ansible ad hoc 單一指令運作的方式來運行 ansible。

Ansible 元件與安裝

『自動化』是每個 IT 人員心裡面最想要達成的境界!只要下達幾個簡單的指令,系統就可以好好的活下去! 只要下達固定的幾個指令,同事們傳來的需求單就能快速搞定!只要幾個指令,客戶需要的 VM 與服務,就可以立刻完成搞定! 如果能這樣,當然是最好!

為了達成這樣的目的,過去許多前輩都會寫腳本 (shell script) 來完成,但是這樣的管理方式有點問題!大部分的 shell script 都是很即時寫的, 很多時候過了一陣子,就不能用了!也無法跨不同的 Linux distribution,所以,每次都要重新改寫 shell script,也真是麻煩! 另外,這樣的 shell script 如果要用在某些特定任務的處理,結果任務處理失敗時,該腳本該如何從斷點重新執行?這都是大問題!

Ansible 的出現,就是在解決這樣的問題。只是,我們就得要知道 Ansible 的元件,同時得要知道每個元件負責哪些任務, 同時也知道一下關於 Ansible 的許多專有名詞,後面運作起來,會比較快樂些!

  1. 為什麼我們需要開發自動化軟體?
    1. 系統管理員經常自己寫一些 document 或者是一些腳本,然後按照這些腳本文件來進行系統的管理。 只是,更多的時候,管理員會依據自己的記憶與經驗,直接去搞系統,結果就是,如果某個步驟一時忘記而沒有輸入, 後續可能會造成許多問題!例如忘記 systemctl enable service 時,該服務在下次開機,恐怕就不會自動啟動啊!
    2. 基礎設施即程式 (Infrastructure as Code):比較好的自動化思考,應該是要設計出讓系統可以讀取的文件, 讓你的基礎設施 (底層硬體或作業系統或簡易的服務設定) 就像程式碼一樣,很容易讀、修改與運作。
    3. 總之,目的就是要減低人員操作的錯誤!
  2. Ansible 的元件:
    1. Ansible 各元件的關聯性:Ansible 會用到的元件以及各元件的關係如下圖所示: Ansible 各元件的關係
    2. 控制端點與主機 (control node and host):
      • 使用者透過在 control node 上面的 ansible 元件,下達指令來操作自動化管理指令。這個 control node 主要透過 ssh 協定, 直接連線到要被管理的系統上 (host) 進行操作任務。 host 只要能夠讓 control node 透過 ssh 連線進去即可, 其他不需要安裝!
      • 管理員安裝 ansible 的機器為控制端 (control node),在控制端上面還有 ansible 的其他外掛模組, 也可以透過指令列來操作系統
      • 所有的 host 主機名稱或 IP 都是記載在主機清單 (inventory) 上,那清單也只是個檔案,透過這個清單記載, 控制端很容易就找到 host 來進行管理了。
    3. Ansible 引擎 (Ansible Automation Engine):就如同上面講的, Ansible 引擎其實就是 control node 囉! 上圖當中右邊的 HOSTS 就是主機群,而這些主機群的分類或者是紀錄,就是在清單 (inventory) 裡面。 模組 (modules) 就是已經被前輩們開發好的某些特定功能的指令,你可以自行呼叫來使用即可喔!

    4. Ansible Playbook:寫過 shell script 都知道,要將腳本寫好,得要加上一堆註解說明。 playbook 就是類似 shell script, 將一堆任務寫在一起,丟給 ansible 直接執行即可。因此,上面談到的模組運作,也是寫入到這裡來!

    5. 也就是說,使用者透過修改 palybook,引用了 ansible 提供的模組功能,撰寫好需要的步驟,同時將需要管理的主機名稱寫入到清單列表中, 接下來,就將 playbook 丟進 ansible ,就搞定了。

  3. 安裝 Ansible 與觀察前驅軟體 -- 針對 control node 的安裝:
    1. 觀察前驅軟體:因為 ansible 主要透過 python 來達成,因此,你的 control node 與 host,都需要安裝 python 喔! 在 CentOS8 上面,ansible 需求是 3.6 以上版本,或 2.7 以上版本。確認版本的方式:
      # rpm -q platform-python
      platform-python-3.6.8-23.el8.x86_64
      
      # 如果沒有安裝,那就自己安裝起來吧!
      # yum install platform-python
      
    2. 開始安裝 Ansible:直接使用 CentOS 慣用的 yum 來安裝即可
      # 1. ansible 在額外的軟體倉儲中,所以需要安裝額外的 release 喔!
      # yum search release-ansible
      centos-release-ansible-29.noarch : Ansible 2.9 packages from the CentOS ConfigManagement...
      
      # yum install centos-release-ansible-29
      
      # yum repolist
      軟體庫 ID                      軟體庫名稱
      AppStream                      CentOS-8 - AppStream
      BaseOS                         CentOS-8 - Base
      centos-ansible-29              CentOS Configmanagement SIG - ansible-29
      extras                         CentOS-8 - Extras
      
      # yum config-manager --disable centos-ansible-29
      
      # ansible --version
      ansible 2.9.14
        config file = /etc/ansible/ansible.cfg
        configured module search path = ['/root/.ansible/plugins/modules', 
              '/usr/share/ansible/plugins/modules']
        ansible python module location = /usr/lib/python3.6/site-packages/ansible
        executable location = /usr/bin/ansible
        python version = 3.6.8 (default, Apr 16 2020, 01:36:27) [GCC 8.3.1 20191121 
             (Red Hat 8.3.1-5)]
      
    3. 初步檢查本機 (localhost) 的安裝狀態,要注意的是,預設情境下,安裝好 ansible 就有 setup 這個 module 了! 所以,直接來檢查看看:
      # ansible -m setup localhost
      localhost | SUCCESS => {
          "ansible_facts": {
              "ansible_all_ipv4_addresses": [
                  "172.17.200.254",
                  "192.168.40.200",
                  "192.168.122.1"
              ],
      .....
      
      # ansible -m setup localhost | grep python_version
              "ansible_python_version": "3.6.8",
      
    4. 在 host 上面安裝 ansible 可以呼叫的 python 模組:基本上, ansible 只要安裝在 control node 上面即可, 在被管理的主機 (managed host) 上面,是不需要安裝 ansible 的。不過, ansible 主要使用 python 程式進行所有任務, 因此,若能夠在 host 上面預先安裝好需要的 python 模組時,效率會比較好。相關可以預先安裝的軟體如下:
      # yum install platform-python python36 python3-libselinux python3-dnf
      
      事實上,一般來說,如果安裝 GUI 伺服器環境,上述軟體幾乎已經安裝妥當。但是如果是一般最小安裝時, 應該還是需要這個安裝的動作的喔!
  4. 實做練習:
    1. 登入 control node 系統,切換成為 root 的身份之後,先找找看, yum 裡面有哪個參數,可以將系統所有的軟體倉儲名稱通通列出來?
    2. 嘗試安裝 links 這個軟體?是否回報找不到呢?
    3. 找到一個名為 PowerTools 的軟體倉儲,將他設定為 enable 看看。
    4. 再次安裝 links 軟體,是否可以順利的安裝呢?
    5. 嘗試列出剛剛安裝的 ansible 這個軟體所屬的所有檔名列表
    6. 承上,只需要列出設定檔 (/etc 開頭的)

Ansible control node 與 managed hosts 之間的連線處理

  1. 伺服器間的名稱規劃:
    1. 如果不用 DNS 大架構的系統,事實上,在 server/client 之間,最簡單就是透過 /etc/hosts 來規劃主機名稱與 IP 的對應了! 我們在未來會用到許多的主機,但是,事實上我們卻只有一部 client。為了達成這些功能,建議將 client 的名稱做個比較多的別名, 就可以持續處理了:
    2. 處理用戶端別名:
      # vim /etc/hosts
      ....
      192.168.40.200  gitserver200.ksu gitserver
      172.17.200.254  control ansible
      172.17.200.1    gitclient200.ksu gitclient client1 webserver1 dbserver1 devnode1 pronode1
      
      # scp -p /etc/hosts client1:/etc
      
      這樣一來, Server/Client 兩端的系統,主機名稱就一致了!
  2. 伺服器使用 ssh 連線的機制:
    1. 在 managed host 上面,建立非特權帳號:

      一般建議不要使用 root 登入遠端主機,使用非特權用戶 (unprivilege) 連線到遠端主機會比較好!而如果需要轉換身份的話,才使用 sudo 來處理即可。 假設 managed host 上面的管理人員一般操作帳號身份為 sysadm 好了,請先建立該帳號,該帳號給予 itismyadm 這樣的密碼好了

      # 注意,是在 client 端進行才對喔!請在 managed host,也就是用戶端啟用的系統進行如下任務:
      # useradd sysadm
      # echo itismyadm | passwd --stdin sysadm
      
      # 讓這個用戶不要在圖形界面登入,若有圖形界面時:
      # vim /var/lib/AccountsService/users/sysadm
      [User]
      SystemAccount=true
      
      在下次重新登出某個用戶時,你就會發現到 sysadm 並不會出現在圖形界面的帳號當中了!比較保險!
    2. 在 control node 上面,使用 student 嘗試用 sysadm 登入 managed host:

      使用 student 身份,在 control node 端,透過 ssh 連線到 managed host ,免密碼的方式如下:

      # 1. 需要密碼的直接登入看看,注意喔,是使用 student 身份:
      $ whoami
      student
      
      $ ssh sysadm@client1
      The authenticity of host 'client1 (172.17.200.1)' can't be established.
      ECDSA key fingerprint is SHA256:waLgFyX/O5YwIlj939NQAnu16Wt5pM2mKVp8NFDGfCM.
      Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
      Warning: Permanently added 'client1,172.17.200.1' (ECDSA) to the list of known hosts.
      sysadm@client1's password:  <==輸入 sysadm 的密碼
      
      $ whoami
      sysadm
      
      $ exit
      
      # 2. 開始處理 student 的金鑰系統
      $ whoami
      student
      
      $ ssh-keygen
      
      $ ll ~/.ssh
      -rw-------. 1 student student 2610 Oct 23 21:30 id_rsa
      -rw-r--r--. 1 student student  578 Oct 23 21:30 id_rsa.pub  <==這就是公鑰!
      -rw-r--r--. 1 student student  182 Oct 23 21:27 known_hosts
      
      # 3. 上傳金鑰到 client1 這部主機的 sysadm 這個帳號上面:
      $ ssh-copy-id -i ~/.ssh/id_rsa.pub sysadm@client1
      sysadm@client1's password:
      
      # 4. 嘗試登入到 client1 系統,並請不要離開 client1 系統:
      $ ssh sysadm@client1
      
      $ whoami
      sysadm
      
    3. 建立一般用戶的特權帳號轉換功能,以 sudo 免密碼的處理:

      一般帳號要切換身份成為系統管理員 root 有兩個方法,一個是使用 su 指令,另一個則是使用 sudo。因為 ansible 必需要取得 managed host 的 root 權限,才能夠對 mananged host 進行系統管理啊!所以,我們在 managed host 上面的 sysadm 必需要能夠切換身份成為 root 才行。 一般建議使用非密碼就可操作 sudo 的帳號權限!也因為這權限太大,所以我們才會將這個帳號特別隱藏起來啊!處理方案也不難, 這樣搞看看:

      # 1. 先讓你的 managed host 上面的 sysadm 變成 root 才行!
      $ whoami
      sysadm
      
      $ su -
      
      # visudo
      root    ALL=(ALL)       ALL
      sysadm  ALL=(ALL)       NOPASSWD: ALL
      
      # exit
      
      # 2. 開始測試 sysadm 的切換行為:
      $ sudo tail -n 3 /etc/shadow
      tcpdump:!!:18318::::::
      student:$6$WZKIz7J0uKAzw2vP$FMFdI3zx.eOWqrVOckm93P5BWQPbQZ.zB59N5...
      sysadm:$6$hpSxTdYHmoWk6V/O$PodyLcHS5uqsS1YWF7vVadTgibGoVmJAyWlifk...
      
      如此一來,你的 client1 的 sysadm 就具有管理員任務導向的功能了!
  3. 實做練習:
    1. 在 managed host 建立一個名叫 webadm 的帳號,帳號的密碼為 itismyadm
    2. 在 control node 的 student,同樣可以透過剛剛上面介紹的方法,以 webadm 這個帳號來登入系統, 預計用來管理 ansible 的遠端登入、管理功能。

Ansible 管理的主機列表與設定檔規劃

  1. 操作 ansible 的身份:
    1. ansible 的重點,是透過 control node 去管理 managed host,所以,需要 root 權限的,只是在 managed host 上面而已, 在 control node 可以使用一般帳號來操作的!
    2. ansible 一般也建議根據你的專案需求,自己建立不同的目錄去管理自己所需要的專案與設定。在我們的設計當中,假設系統中的 student 為最主要的 ansible 操作者喔!因此,所有的動作都應該是以 student 身份去進行,而所有的設定,都以 student 家目錄的資料為主較佳。
    3. 本章節預計設計一個初步的 ansible 操作,因此預計建立目錄名稱如下:
      $ whoami
      student
      
      $ hostname
      gitserver200.ksu
      
      $ mkdir ansible-init
      $ ll -d /home/student/ansible-init
      drwxrwxr-x. 2 student student 6 Oct 23 21:56 /home/student/ansible-init/
      
      $ cd ansible-init
      
  2. 設計管理 managed host 清單列表:
    1. 一般系統規範的主機清單,可以參考 /etc/ansible/hosts 內容,裡面也有一些範本。基本上,主機的清單可以取名為 hosts ,也可以取名為 inventory。 因為上表的規劃說明中,主機清單列表設計為 inventory,所以我們就設定為 inventory 好了。
    2. inventory 內部的主機名稱設計,大致上可以分為底下這幾類:
      # 1. 單一主機,沒有分類的效果,其實是放在 ungrouped 群組當中
      gitclient
      gitclient[01:20].ksu    <==代表 gitclient01.ksu, gitclient02.ksu... gitclient20.ksu
      172.17.20.[1:30]        <==代表 172.17.20.1, 172.17.20.2... 172.17.20.30
      
      # 2. 歸類於 website 的群組與 devel 群組
      [website]
      webserver1
      dbserver1
      
      [devel]
      webserver1
      devnode1
      pronode1
      
      # 3. 成立一個『父群組』,名稱為 mylab,其中 mylab 的子群組包含 devel 與 website:
      [mylab:children]
      website
      devel
      
    3. 實做練習,請將上面的設定值,使用 student 身份,實際寫入到 control node 的檔案中:
      /home/student/ansible-init/inventory
    4. 使用底下的指令來檢查一下, ansible 是否找到正確的主機名稱了呢?
      # 1. 相關語法為:
      $ ansible [hostname|groupname] -i inventory_filename --list-hosts
      
      # 2. 檢查是否具有 webserver1 這部主機?
      $ pwd
      /home/student/ansible-init
      
      $ ansible webserver1 -i inventory --list-hosts
        hosts (1):
          webserver1
      
      # 3. 檢查 devel 群組內有幾部受管理的主機名稱?
      $ ansible devel -i inventory --list-hosts
        hosts (3):
          webserver1
          devnode1
          pronode1
      
      # 4. 檢查一下系統管理的所有主機
      $ ansible all -i inventory --list-hosts
        hosts (55):
          gitclient
          gitclient01.ksu
          gitclient02.ksu
          gitclient03.ksu
      ....
          172.17.20.30
          webserver1
          dbserver1
          devnode1
          pronode1
      
      # 5. 檢查一下,沒有分到其他類別,而是各自存在的主機名稱?
      $ ansible ungrouped -i inventory --list-hosts
        hosts (51):
          gitclient
          gitclient01.ksu
          gitclient02.ksu
      .....
          172.17.20.29
          172.17.20.30
      
      出現上面的字樣,就代表你的主機設定沒有問題啦!要注意主機的數量喔!
    5. 兩個預設存在的群組:
      • all:代表所有的主機
      • ungrouped:代表沒有分類的主機
    6. 一個預設存在的主機名稱為: localhost!就是 127.0.0.1:
      $ ansible localhost --list-hosts
        hosts (1):
          localhost
      
    7. 小節:預設的 ansible inventory 檔名為 /etc/ansible/hosts,不過,通常建議自行修改清單檔案,如果要使用清單檔案, 可以透過 ansible -i inventory_file 來處理。另外,也可以透過底下的 ansible 設定檔來搞定這個清單檔案的位置。
  3. Ansible 設定檔,主要用於規範 (1)主機清單與 (2)操作者環境
    1. 再次強調 ansible 的作用:ansible 主要是讓管理員,可以在 control node 對 managed host 進行管理與維護的動作! 而通常 ansible 是透過 ssh 去連線到 managed host 進行管理的行為!所以,我們就得要知道:
      • 主機的清單列表在哪裡?這個剛剛上一個小段落講過了!
      • managed host 內的非特權用戶、非特權用戶轉成 root 的 sudo 免密碼方法設計
      在我們的案例中,基本上使用的用戶方式為:
         student@gitserver   -----(ssh)----->   sysadm@client1
                                                   sudo 設計免密碼
      
    2. ansible 的設定檔主要就是在設定上面的資料!設定檔的內容大致上有點像底下這樣:
      [defaults]                  # 預設值
      inventory = ./inventory
      remote_user = sysadm
      ask_pass = false
      
      [privilege_escalation]      # 特權升級
      become = true
      become_method = sudo
      become_user = root
      become_ask_pass = false
      
      我們大致上都已經設定好清單檔案與使用者操作的連動特性了,因此,上面的設計效果應該是很容易理解!大致功能為:
      • inventory:規劃主機清單列表的檔名
      • remote_user:登入到遠端主機的帳號名稱,這個帳號必須存在 managed host 才行。你當然也能寫上 root ,不過不建議啊!
      • ask_pass:連線到 managed host 時,需要提示密碼嘛?我們已經設計了金鑰系統的免密碼登入,所以這裡填寫不要 (false)
      • become:是否需要進行非特權帳號的特權升級?
      • become_method:主要是 sudo 與 su 兩者,建議使用 sudo,因為這樣就不需要流出 root 密碼。
      • become_user :一般就是 root 無誤!
      • become_ask_pass:有沒有設定免密碼的 sudo 呢?這就是設計的重點!
    3. 基本上,放置設定檔的位置相當多!不過,主要以底下的查詢順序為主喔:
      1. ./ansible.cfg:優先找 ansible 執行指令工作目錄下的檔名,這個最優先!也最建議使用!
      2. ~/.ansible.cfg:使用者個人家目錄下的隱藏檔,個人設定,找不到 ./ansible.cfg 時,才會嘗試使用到這個檔案
      3. /etc/ansible/ansible.cfg:前面兩個都不存在,才會使用這一個!
  4. 實做練習:
    1. 請在 control node 上面,透過 student 的身份,將上面的設定檔寫入到正確的位置,記得,檔名預設為 ansible.cfg 喔!
    2. 請使用『 ansible --version 』查詢一下,目前的設定檔 (config file) 是否為正確的 student 所在目錄下的檔名?
    3. 請使用『 ansible devel --list-hosts 』查詢一下,是否有我們剛剛設定的主機名稱清單出現?
    4. 編輯 ansible.cfg ,將『 become_ask_pass = true 』之後,再次『 ansible devel --list-hosts 』,會出現什麼訊息? 若需要輸入密碼,隨便填寫不要緊!不會對你的系統有影響的!
    5. 承上,做完之後,再將該設定改回『 become_ask_pass = false 』

開始使用 ansible: 使用模組與 ad hoc 指令模式

  1. 基本指令語法:
    1. 在開始使用 ansible 的 playbook 之前,我們先以簡單的特設指令 (ad hoc command) 方式來測試一下 ansible 的功能。 基本上,我們可以將 ansible 的 ad hoc 特設指令想成直接以 ssh 連線到遠端執行指令的模樣,只是不一樣的是, ansible 基本上不是使用 bash shell, 而是使用一些內建的模組,透過這些模組 (module) 的功能,來管理整個任務的執行流程,這樣才有辦法重複執行同一個指令, 也不會造成系統的傷害!
    2. 基本指令語法如下:
      $ ansible [host|group] -m module [-a 'module args']
      
      也就是說,透過某個模組的功能,讓某部主機或某群組主機,進行某個設定。而模組也可能會有額外的參數, 那就是搭配 -a '模組參數' 來處理了。
    3. 假設你想要知道 client1 這部主機有沒有在網路上活著,可以使用 ping 這個指令。而 ansible 提供了一個名為 ping 的模組, 可以用來了解我們跟 managed host 之間的網路有沒有問題。處理方式如下:
      $ ansible webserver1 -m ping
      webserver1 | SUCCESS => {
          "ansible_facts": {
              "discovered_interpreter_python": "/usr/libexec/platform-python"
          },
          "changed": false,		# 沒有任何改變
          "ping": "pong"		# 回應 pong,所以有成功接觸
      }
      
      上述的意思是,webserver1 回傳訊息為成功 (SUCCESS),底下說明模組運作的狀態。比較常見的有 changed (是否有資料被改變), 而 ping 模組的功能就是回應了 pong 喔!乒乓球啦!
  2. 觀察模組細項資料:
    1. 上面的 ping 模組到底內容是什麼?我們可以透過 ansible-doc 來檢查:
      $ ansible-doc ping
      > PING    (/usr/lib/python3.6/site-packages/ansible/modules/system/ping.py)
      
              A trivial test module, this module always returns `pong' on
              successful contact. It does not make sense in playbooks, but
              it is useful from `/usr/bin/ansible' to verify the ability to
              login and that a usable Python is configured. This is NOT ICMP
              ping, this is just a trivial test module that requires Python
              on the remote-node. For Windows targets, use the [win_ping]
              module instead. For Network targets, use the [net_ping] module
              instead.
      
        * This module is maintained by The Ansible Core Team
      OPTIONS (= is mandatory):
      
      - data
              Data to return for the `ping' return value.
              If this parameter is set to `crash', the module will cause an exception.
              [Default: pong]
              type: str
      
      
      SEE ALSO:
            * Module net_ping
                 The official documentation on the net_ping module.
                 https://docs.ansible.com/ansible/2.9/modules/net_ping_module.html
            * Module win_ping
                 The official documentation on the win_ping module.
                 https://docs.ansible.com/ansible/2.9/modules/win_ping_module.html
      
      
      AUTHOR: Ansible Core Team, Michael DeHaan
              METADATA:
                status:
                - stableinterface
                supported_by: core
      
      
      EXAMPLES:
      
      # Test we can logon to 'webservers' and execute python with json lib.
      # ansible webservers -m ping
      
      # Example from an Ansible Playbook
      - ping:
      
      # Induce an exception to see what happens
      - ping:
          data: crash
      
      
      RETURN VALUES:
      
      ping:
          description: value provided with the data parameter
          returned: success
          type: str
          sample: pong
      
      甚至還有範例 (examples) 介紹,所以,只要找到模組名稱,應該就有辦法查詢到模組參數了吧! ping 比較簡單,沒有相關的參數,只會回應成功或失敗,然後回傳 pong 這樣而已。
    2. 查詢目前 ansible 的所有模組以及特殊模組名稱:
      $ ansible-doc -l
      
      # 查詢關於 copy 相關的模組:
      $ ansible-doc -l | grep copy
      bigip_file_copy                                Manage files in datastores o...
      ce_file_copy                                   Copy a file to a remote clou...
      copy                                           Copy files to remote locatio...
      ec2_ami_copy                                   copies AMI between AWS regio...
      ec2_snapshot_copy                              copies an EC2 snapshot and r...
      .....
      
      $ ansible-doc copy
      .....
      = dest
              Remote absolute path where the file should be copied to.
              If `src' is a directory, this must be a directory too.
              If `dest' is a non-existent path and if either `dest' ends with "/"
              or `src' is a directory, `dest' is created.
              If `dest' is a relative path, the starting directory is determined
              by the remote host.
              If `src' and `dest' are files, the parent directory of `dest' is not
              created and the task fails if it does not already exist.
      .....
      - group
              Name of the group that should own the file/directory, as would be
              fed to `chown'.
              [Default: (null)]
              type: str
      .....
      - mode
              The permissions of the destination file or directory.
      .....
      - owner
              Name of the user that should own the file/directory, as would be fed
              to `chown'.
              [Default: (null)]
              type: str
      .....
      - src
              Local path to a file to copy to the remote server.
              This can be absolute or relative.
      .....
      
      所以,可以查詢到相關的參數!如果繼續往下看,依舊會有很多的 EXAMPLES 的展示!只是, 那都是之後我們會談到的 playbook 的樣式就是了!
  3. 一個案例:
    1. 假設我們在 /etc/hosts 裡面,增加了名為 gateway 的主機名稱,這個主機名稱對應到 192.168.40.254 去, 更改完畢之後,想要將這個檔案傳送到其他用戶端主機去,怎麼做?過去都用 scp 慢慢一個一個主機複製吧! 現在可以使用 ansible 的 copy 模組來處理看看。首先,在 control node 上面,用 root 的身份去處理 /etc/hosts 吧:
      # vim /etc/hosts
      ....
      192.168.40.254  gateway
      192.168.40.200  gitserver200.ksu gitserver
      172.17.200.254  control ansible
      172.17.200.1    gitclient200.ksu gitclient client1 webserver1 dbserver1 devnode1 pronode1
      
    2. 剛剛查詢到的 copy 模組,需要有來源 (src) 與目標 (dest),當然還有其他的設計, 不過,我們這裡直接使用這兩個參數就好:
      $ whoami
      student
      
      $ pwd
      /home/student/ansible-init
      
      $ ansible webserver1 -m copy -a 'src=/etc/hosts dest=/etc/hosts'
      webserver1 | CHANGED => {
          "ansible_facts": {
              "discovered_interpreter_python": "/usr/libexec/platform-python"
          },
          "changed": true,
          "checksum": "423f985239e1e7eb5d52becf62dad1fac3e3b475",
          "dest": "/etc/hosts",
          "gid": 0,
          "group": "root",
          "md5sum": "677944b84b84bd729177b22a039b94c2",
          "mode": "0644",
          "owner": "root",
          "secontext": "system_u:object_r:net_conf_t:s0",
          "size": 344,
          "src": "/home/sysadm/.ansible/tmp/ansible-tmp-1603491420.0144467-50255-2646872...",
          "state": "file",
          "uid": 0
      }
      
      這樣就複製好了!不但如此,還告訴你整個流程!那麼,如果再執行一次,會有什麼後果呢?來測試看看:
      $ ansible webserver1 -m copy -a 'src=/etc/hosts dest=/etc/hosts'
      webserver1 | SUCCESS => {
          "ansible_facts": {
              "discovered_interpreter_python": "/usr/libexec/platform-python"
          },
          "changed": false,
          "checksum": "423f985239e1e7eb5d52becf62dad1fac3e3b475",
          "dest": "/etc/hosts",
          "gid": 0,
          "group": "root",
          "mode": "0644",
          "owner": "root",
          "path": "/etc/hosts",
          "secontext": "system_u:object_r:net_conf_t:s0",
          "size": 344,
          "state": "file",
          "uid": 0
      }
      
      果然,就如同 ansible 自己說的,重複執行指令倒是不會出問題!系統會主動的告知,並沒有任何改變喔! 因為 changed 是 false 的!
  4. 常用模組列表:
    1. 檔案相關模組:
      • copy :從 control node 複製資料到 managed host
      • file :設定檔案權限或其他參數
      • lineinfile :判斷搜尋某一個資料行是否在或不在某個檔案中
      • synchronize: 使用 rsync 進行資料同步
    2. 軟體安裝與移除等相關模組:
      • package: 依據 distribution 的版本,自動判斷需要的軟體管理指令
      • yum: 使用 yum
      • apt: 使用 apt-get 等軟體功能 (debian/ubuntu)
      • dnf: 使用 dnf (fedora/redhat)
      • pip: 透過 PyPI 管理 python 的可安裝模組
    3. 系統管理模組:
      • firewalld: 使用 firewalld 管理 managed host 防火牆
      • reboot: 對 managed host 重新開機
      • service: 對服務管理
      • user: 新增/移除/管理使用者帳號與密碼
    4. 網路工具模組:
      • get_url: 從 http, https, ftp 等網站下載檔案資料
      • nmcli: 就是管理網路參數設定的模組
      • uri:與網路服務互動
  5. 實做練習:預計在 managed host 上面,新增一個名為 demouser 的帳號,此帳號 uid 設計為 3000,密碼設定為 itismyuser
    1. 先使用 ansible-doc 的功能去查詢一下『 user 』這個模組的相關內容,找出 (1)使用者名稱 (2)UID設定與 (3)密碼設定的參數
    2. 根據上述的說明,發現到密碼設定需要使用額外的功能,所以,請參考 這個檔案 的內容,先『 pip3 install passlib 』,然後以『 python3 -c "from passlib.hash import sha512_crypt; import getpass; print(sha512_crypt.using(rounds=5000).hash(getpass.getpass()))" 』 這個指令,完成密碼的取得
    3. 實際在 student 的身份下,使用 ansible 的 ad hoc 功能,直接建立這個帳號
    4. 重複建立帳號的指令,看看會出現什麼結果呢?

開始使用 ansible: 使用 command / shell 模組

  1. 某些小功能,可能 ansible 沒有針對這些小功能開發模組。又有可能你要進行的動作,根本不需要動到 ansible 的設定! 舉例來說,你只想要知道剛剛上面的測試指令有沒有成功而已,那怎辦?難道需要自己搞一個模組?不需要喔!直接使用 command 這個模組就好了!
  2. command 模組功能:
    1. 主要的目的,其實是在提供給管理員一個 debug 的功能!管理員不需要直接登入 managed host, 直接以 command 模組,就能夠處理 managed host 的內部指令操作了。
    2. 基本語法如下:
      $ ansible [host|group] -m command -a '在遠端主機執行的指令'
      
    3. 使用 id demouser 測試一下用戶是否存在?同時使用 cat /etc/hosts 看看剛剛建立的內容是否正常?
      $ ansible webserver1 -m command -a 'id demouser'
      webserver1 | CHANGED | rc=0 >>
      uid=3000(demouser) gid=3000(demouser) groups=3000(demouser)
      
      $ ansible webserver1 -m command -a 'cat /etc/hosts'
      webserver1 | CHANGED | rc=0 >>
      127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
      ::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
      
      192.168.40.254  gateway
      192.168.40.200  gitserver200.ksu gitserver
      172.17.200.254  control ansible
      172.17.200.1    gitclient200.ksu gitclient client1 webserver1 dbserver1 devnode1 pronode1
      
      $ ansible webserver1 -m command -a 'id demouser; cat /etc/hosts'
      webserver1 | FAILED | rc=1 >>
      id: extra operand ‘cat’
      Try 'id --help' for more information.non-zero return code
      
      前面兩個動作成功倒是沒有太大問題,最後一個指令將兩者銜接起來,怎麼會失敗呢?
    4. 基本上, command 模組一次只執行一個指令,其他的資料都被當成是該指令的參數,所以, 使用 command 模組時,是不能使用常見的 shell 功能的!如果要使用 shell 功能,或者是要使用 shell 內建的指令, 那就得要使用 shell 模組了。
  3. shell 模組功能:
    1. shell 模組可以取代 command 模組,可以運作的功能比較強大!例如上面提到的連續指令下達:
      $ ansible webserver1 -m shell -a 'id demouser; cat /etc/hosts'
      webserver1 | CHANGED | rc=0 >>
      uid=3000(demouser) gid=3000(demouser) groups=3000(demouser)
      127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
      ::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
      
      192.168.40.254  gateway
      192.168.40.200  gitserver200.ksu gitserver
      172.17.200.254  control ansible
      172.17.200.1    gitclient200.ksu gitclient client1 webserver1 dbserver1 devnode1 pronode1
      
      你可以看到,它就成功了!
  4. 該不該使用 command/shell?
    1. ansible 強調,在 ansible 管理機制下,所有的動作應該都可以被重複執行,同時動作應該是可以被追蹤的!但是, 使用 command 或 shell 模組時,內接的指令實際上無法進行追蹤!因此, ansible 不建議使用這些 command 或 shell。
    2. 不過,就系統管理的角度來看,某些時刻你可能會去 managed host 看一下某些重要資訊,此時, 透過 command/shell 的簡易功能,你可以不用 ssh 而是使用 ansible 自動化幫你管理這些主機資訊, 應該比自己下達 ssh 指令來的更容易管理啊!
    3. 預設的情況下,如果你沒有加上任何模組時,那麼 -a 後面家的參數,預設就會以 -m command 模組去嘗試執行喔!
  5. 實做練習: 想要讓使用者在文字界面登入時,可以看到『 This server managed by ansible 』的字樣
    1. 使用 ansible-doc 的功能找到 copy 這個模組,裡面的 content 參數有什麼意義?
    2. 使用 ansible 的 command 功能,列出 webserver1 的 /etc/motd 這個檔案裡面目前有什麼東西?
    3. 將上面的文字『 This server managed by ansible 』的字樣,以 ansible 的方式,複寫到 webserver1 的檔案中。
    4. 重複執行一次,會是什麼結果?
    5. 重複以 command 功能,列出 webserver1 的 /etc/motd 這個檔案裡面目前有什麼東西?
    6. 嘗試到 gitclient 主機上面使用 demouser 登入,看看會不會出現上面輸入的這段文字呢?

參考資料