iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 12
1

我覺得 Ansible 學習上比較困難的地方在於它主要是學習設定檔的配置,若要實作,很難設計一個有意義的場景去試驗那些配置,若想要嘗試的模組種類過多,場景就會變得很複雜,而且修改配置參數後,想要馬上得到結果似乎也有點困難。這也跟經驗有關,如果是一個對於主機管理配置經驗豐富的維運人員,可能看到這些功能模組,立刻就可以想到它適用的地方,像我這種沒什麼經驗的人,經常會發生這個功能有什麼用、這個功能在做什麼、這個功能什麼情況下會被用到,諸如此類的疑惑。總之我想說的是,因為接下來的部分很難設計一個實作場景,所以我的作法會是以文件內容配合應試能力目標來作介紹,感覺會比較無聊一點。

ansible.cfg

除了 playbook 及 inventory 這兩個供 Ansible 專案使用的設定檔,Ansible 本身也有一個設定檔,路徑為 /etc/ansible/ansible.cfg。裡面有一些值可以以專案中的 ansible.cfg 覆寫,例如 inventory 設定檔的路徑。昨天在下 ansible-playbook 指令時用 -i 指定了 inventory 路徑,這個值可以直接指定在 ansible.cfg 中。請在昨天的專案目錄中新增一個 ansible.cfg 檔案,並加入以下內容

[defaults]

inventory=hosts

這樣就可以直接執行 ansible-playbook web.yml,不需要指定 inventory 路徑。關於 ansible.cfg 有那些值可以設定,可參考 /etc/ansible/ansible.cfg 註解的內容。

Inventory

先來看 inventory 設定檔的介紹。inventory 設定主要作兩件事,第一件事是把管理節點分類,在 playbook 就可以用這些「類」作為操作的對象。第二件事是設置各管理節點相關參數,例如 SSH port 號、登入名稱等等,這類型的參數稱為 behaviroal inventory parameter。

inventory 可以使用幾種不同的格式,一種是我們前面用的類似 INI 的格式,另一種是 YAML。以文件上的說法預設似乎是 INI,所以我們就用 INI 的格式來作介紹。

單台機器的設定,可以用以下這種格式,最前面是主機名稱,然後跟著兩個參數,port 和 IP。如果沒有主機名稱,找個別名來用也可以,但後面就要用 ansible_host 來指定主機 IP。還有那些 behavioral inventory parameter,可以參考 https://docs.ansible.com/ansible/2.6/user_guide/intro_inventory.html#list-of-behavioral-inventory-parameters。可以在單台主機中指定變數 (variable) 並賦值,一樣是用 key=value 的型式指定,變數可用於 playbook 中。

ansible.test.com ansible_port=5555 ansible_host=192.0.2.50 maxRequestsPerChild=808

接下來是分群 (group),類別名稱放在中括號 [ ] 裡面,下面再列出要放在該類別下的機器。類別可以有層級,以 [<group>:children] 的方式指定,一台機器也可以放在多個類別中。group 的變數則放在 [<group>:vars] 之下,範例如下:

[atlanta]
host1
host2

[raleigh]
host2
host3

[southeast:children]
atlanta
raleigh

[southeast:vars]
some_server=foo.southeast.example.com
halon_system_timeout=30
self_destruct_countdown=60
escape_pods=2

[usa:children]
southeast
northeast

usa 這個 group 有兩個子 group,分別是 southeast、northeast,southeast 也有兩個子群組 atlanta 和 raleigh,分別指定了兩台主機。而 southeast 這個 group 指定了四個變數。

Playbook

接下來我們來看文件中對 playbook 的介紹,以下是從文件中取出的 playbook 範例:

---
- hosts: webservers
  remote_user: jack
  become: yes
  tasks:
  - name: ensure apache is at the latest version
    yum:
      name: httpd
      state: latest
  - name: write the apache config file
    template:
      src: /srv/httpd.j2
      dest: /etc/httpd.conf

- hosts: databases
  remote_user: root

  tasks:
  - name: ensure postgresql is at the latest version
    yum:
      name: postgresql
      state: latest
  - name: ensure that postgresql is started
    service:
      name: postgresql
      state: started

這個 playbook 中有兩個 play,每個 play 的開頭部分有 hosts、remote_user、become 三個 directive,昨天已經介紹過 hosts 是指這個 play 要作用的主機,remote_user 則是以那個身份名稱登入遠端主機,become 的值用 yes 和 True 是相同的,在這裡表示要用特權身分執行之後的工作,這部分之後會再說明。

接下來看 tasks 的部分,每個 task 會先有 name,描述這個 task 的內容,它是註解的作用,也會成為 Ansible 輸出的一部分。name 也可以用於說明此 play 的整體作用,請參考昨天的範例。在 task 中可以指定不同的動作,依指定的動作呼叫不同的模組來執行這些動作,例如 yum、service 等等,格式是 module: option。這裡參考【應試目標能力】對重要的 task 模組略為說明,更多的模組可參考 https://docs.ansible.com/ansible/latest/modules/list_of_all_modules.html#all-modules

file

用來修改設定檔案的屬性,包括 symbolic link,範例如下:

- file:
    path: /etc/foo.conf
    owner: foo
    group: foo
    mode: 0644
- file:
    src: /file/to/link/to
    dest: /path/to/symlink
    owner: foo
    group: foo
    state: link

第二個範例昨天有看過相同的用法:

- name: enable configuration
  file: >
   dest=/etc/nginx/sites-enabled/default
   src=/etc/nginx/sites-available/default
   state=link

差別在於文件中的範例,file 的參數是 key: value 的型式,而昨天的範例是 key=valuekey: value 是 YAML 的格式,表示這裡的參數有點像是用 YAML 的鍵值方式送過去的,而 key=value 這種型式是以字串傳入參數,file 後面的 ">" 符號,就書裡面的說明是將下面的換行符號以空格取代,所以下面的三行整個是一個字串,這個字串再送給 file 作為參數。因為文件是用 YMAL 的 key: value 作為參數型式,那我們也就使用這種型式吧。

copy

用來將本機(控制台)或遠端(管理節點)的檔案複製到遠端的指定位置,範例如下:

- name: example copying file with owner and permissions
  copy:
    src: /srv/myfiles/foo.conf
    dest: /etc/foo.conf
    owner: foo
    group: foo
    mode: 0644
    backup: yes

- name: Copy a "sudoers" file on the remote machine for editing
  copy:
    src: /etc/sudoers
    dest: /etc/sudoers.edit
    remote_src: yes
    validate: /usr/sbin/visudo -cf %s

第二個範例的 remote_src: yes 表示這個檔案的來源是遠端機器,而不是本機。validate 是指在複製前會先用這個指令驗證,%s 會代入檔案名稱,但文件中沒有特別說怎麼判定驗證有無通過,若沒通過會發生什麼事。backup 會在目標檔案已存在時,先將檔案存一複本。

template

用來將本機的樣版處理過後複製到遠端的指定位置。所謂的樣版是指內容含有變數的檔案,而變數的內容則根據情況產生,例如使用者名稱、時間等等。Ansible 的樣版使用 Jinja2 引擎,Jinja2 相關部分之後會再說明。基本上 template 的參數和上面 copy 幾乎相同,但沒有 remote_src,因為樣版一定要在本地端處理過,裡頭的變數才會有值。昨天的範例中也有用到 Jinja2,這裡就不再提供其他範例。

ini_file

用來修改 INI 型式的檔案內容,可以單獨處理特定段落 (section) 的特定鍵值,範例如下:

- name: Ensure "temperature=cold is in section "[drinks]" in specified file
  ini_file:
    path: /etc/anotherconf
    section: drinks
    option: temperature
    value: cold
    backup: yes

會保證 /etc/anotherconf 出現如下設置。

[drinks]
temperature=cold

有一個 state 參數,若設成 absent,表示這個 option 或 section 要被刪除。

lineiinfile

利用正規表示式來修改指定檔案的內容,範例如下:

- lineinfile:
    path: /etc/selinux/config
    regexp: '^SELINUX='
    line: 'SELINUX=enforcing'

- lineinfile:
    path: /etc/sudoers
    state: absent
    regexp: '^%wheel'

上面的範例將 /etc/selinux/config 檔案位於行開頭的 SELINUX= 改成 SELINUX=enforcing,下面的範例將 /etc/sudoers 位於行開頭的 %wheel 刪掉。

patch

使用 GNU patch 工具來實施 patch,在遠端機器必須先安裝 GNU patch 工具。patch 範例如下:

- name: Apply patch to one file
  patch:
    src: /tmp/index.html.patch
    dest: /var/www/index.html

replace

利用正規表示式取代指定檔案中的字串,範例如下:

- replace:
    path: /etc/hosts
    regexp: '(\s+)old\.host\.name(\s+.*)?$'
    replace: '\1new.host.name\2'
    backup: yes

user

管理使用者帳號,包括新增、修改、刪除,也可以協助產生 SSH 金鑰,範例如下:

- name: Add the user 'johnd' with a specific uid and a primary group of 'admin'
  user:
    name: johnd
    comment: John Doe
    uid: 1040
    group: admin
    generate_ssh_key: yes
    ssh_key_bits: 2048
    ssh_key_file: .ssh/id_rsa

group

新增或刪除群組,範例如下:

- name: Ensure group "somegroup" exists
  group:
    name: somegroup
    state: present

command

在遠端機器上執行指令,範例如下:

- name: return motd to registered var
  command: cat /etc/motd
  register: mymotd

- name: Run the command if the specified file does not exist.
  command: /usr/bin/make_database.sh arg1 arg2
  args:
    creates: /path/to/database

# You can also use the 'args' form to provide the options.
- name: This command will change the working directory to somedir/ and will only run when /path/to/database doesn't exist.
  command: /usr/bin/make_database.sh arg1 arg2
  args:
    chdir: somedir/
    creates: /path/to/database

第一個範例中的 register: mymotd 是指將指令 cat /etc/motd 的結果儲存到 mymotd 這個變數中,變數的指定方式會再說明。從範例中看來有些參數要放在 args 下,例如 creates 參數接的是檔案名稱,表示這個檔案若存在,這一整個指令就不會執行。與其作用相反的是 removes 指令,若後面的參數檔案存在,這個指令才會被執行。

shell

command 一樣是用於在遠端機器上執行指令,但 shell 適用於指令中會用到 shell 功能,例如重導、管線這類的指令,請看範例:

- name: Change the working directory to somedir/ before executing the command.
  shell: somescript.sh >> somelog.txt
  args:
    chdir: somedir/

service

用來管理系統服務,像是 systemd、upstart 等,範例如下:

- name: Start service httpd, if not started
  service:
    name: httpd
    state: started

- name: Restart service httpd, in all cases
  service:
    name: httpd
    state: restarted

- name: Enable service httpd, and not touch the state
  service:
    name: httpd
    enabled: yes

name 是服務名稱,state 有四個值,started、stopped、 restarted、reloaded,其中 restarted 和 reloaded 一定會執行,而 started 和 stopped 則是看目前服務是否啟動而定。enabled 表示是否開機時就要帶起此服務。文件中有一句話 At least one of state and enabled are required,而且標粗,我想應該是說 state 的四個狀態一定要指定其中一種,若沒有用到 state 這個參數,那一定要有 enabled 參數。為什麼不是寫 At least one of state "or" enabled are required?文件的寫法我本來認為是 state 和 enabled 都一定要有,但是看它的範例,並沒有兩個同時設定,所以就把它解讀成 or。喔,或者它應該是 one of "state and enabled",state 和 enabled 至少要出現一個的意思。

systemd

管理 systemd 系統服務,範例如下。其實和 service 差不多,不過有些參數行為像是 daemon-reload 可能是 systemd 才有的。

- name: enable service httpd and ensure it is not masked
  systemd:
    name: httpd
    enabled: yes
    masked: no

- name: enable a timer for dnf-automatic
  systemd:
    name: dnf-automatic.timer
    state: started
    enabled: yes

- name: just force systemd to reread configs (2.4 and above)
  systemd:
    daemon_reload: yes

cron

管理 cron.d 和 crontab 排程工作內容,以及環境變數,可新增或刪除,範例如下:

- name: Ensure a job that runs at 2 and 5 exists. Creates an entry like "0 5,2 * * ls -alh > /dev/null"
  cron:
    name: "check dirs"
    minute: "0"
    hour: "5,2"
    job: "ls -alh > /dev/null"

- name: 'Ensure an old job is no longer present. Removes any job that is prefixed by "#Ansible: an old job" from the crontab'
  cron:
    name: "an old job"
    state: absent

- name: Creates an entry like "APP_HOME=/srv/app" and insert it after PATH declaration
  cron:
    name: APP_HOME
    env: yes
    value: /srv/app
    insertafter: PATH

這個模組除了 cron job 外還可以用來處理環境變數,有點意外。在新增排程工作時,寫到檔案裡的項目會加上 #Ansible: <name> 這樣的註解,name 的部分可由參數設定。

apt、yum

以 apt 或 yum 來管理套件,主要應該是安裝或移除吧。範例如下:

- name: Update repositories cache and install "foo" package
  apt:
    name: foo
    update_cache: yes

- name: Install apache httpd but avoid starting it immediately (state=present is optional)
  apt:
    name: apache2
    state: present
  environment:
    RUNLEVEL: 1

- name: install the latest version of Apache
  yum:
    name: httpd
    state: latest

- name: remove the Apache package
  yum:
    name: httpd
    state: absent

debconf

設定 .deb 套件,範例如下:

- name: set to generate locales
  debconf:
    name: locales
    question: locales/locales_to_be_generated
    value: en_US.UTF-8 UTF-8, fr_FR.UTF-8 UTF-8
    vtype: multiselect

- name: Accept oracle license
  debconf:
    name: oracle-java7-installer
    question: shared/accepted-oracle-license-v1-1
    value: 'true'
    vtype: select

這個我不太懂,所以請自行參考範例。

git

從 Git 儲存庫取出 (checkout) 需要的檔案,範例如下:

# Example git checkout from Ansible Playbooks
- git:
    repo: 'https://foosball.example.org/path/to/repo.git'
    dest: /srv/checkout
    version: release-0.22

version 可以是 HEAD 這類的引用,或者是提交的 hash 字串、分支名稱、標籤名稱等。

debug

在執行時印出述句訊息以作為 debug 用,像是變數內容(用兩個大括號 {{ }} 包起來的部分),範例如下:

# Example that prints the loopback address and gateway for each host
- debug:
    msg: "System {{ inventory_hostname }} has uuid {{ ansible_product_uuid }}"

今天介紹了 inventory 及 playbook 兩種設定檔的主要結構,以及在應試目標能力中提及比較重要的 playbook task。明天繼續介紹 handler 等其他在 playbook 中會用到的設置。


上一篇
[Day 11] Ansible (2)
下一篇
[Day 13] Ansible (4)
系列文
30 天準備 LPI DevOps Tools Engineer 證照30

尚未有邦友留言

立即登入留言