===== 2018 年版本已更新 =====
2018 鐵人賽傳送門:https://ithelp.ithome.com.tw/users/20103346/ironman/1473
GitBook 傳送門:https://www.gitbook.com/book/tsoliangwu0130/learn-ansible-and-jenkins-in-30-days/details
=========================
目前為止,我們在安裝 Jenkins 的遙控主機上只有額外安裝curl
這項工具。不過在未來的章節中,我們還會需要一些其他工具的幫忙。因此,我將在這個章節內新增幾個額外的 role,並介紹如何使用單一個 role 同時支援不同作業系統的主機。
在本系列文章一開始的簡介中,我們有提到 Jenkins 是一個可以幫我們從原始碼託管服務(在這次的教學系列文中將以 GitHub 作為範例)上維護產品的一個持續整合服務。因此為了讓 Jenkins 運行的主機可以使用對應的版本控制系統 - Git,我們接下來會寫另一個獨立的 role 來安裝 Git。
依照以下結構新增 git
這個 role:
workspace
├── Vagrantfile
├── ansible.cfg
├── inventory
├── playbook.yml
└── roles
├── curl
│ └── tasks
│ └── main.yml
├── git
│ └── tasks
│ └── main.yml
└── jenkins
├── meta
│ └── main.yml
└── tasks
└── main.yml
並在 roles/git/tasks/main.yml
中寫入以下內容:
---
- name: install git
yum:
name: git-all
when: ansible_distribution == 'CentOS'
- name: install git
apt:
name: git
when: ansible_distribution == 'Ubuntu' or ansible_distribution == 'Debian'
除了非常類似於之前 curl
這個 role 的安裝流程,這次我們在 role 裡面加上了系統判斷條件。由於 Ubuntu 跟 Debian 這兩種主要的 Linux 作業系統預設都是搭載 apt 做為套件管理,所以當我們今天只要運行這個 role,並判斷遙控主機是這兩個作業系統之一的時候,我們就會運行上面定義的任務。另外,由於 git
這個 role 理論上在未來被重複利用到的頻率會非常高,因此我在這裡同時也新增了在 CentOS 這套作業系統上使用 yum 安裝 Git 的方法(在 yum 套件管理系統中,Git 套件的名稱與 apt 系統並不相同),以增加這個 role 的使用靈活性。
同理,我們也可以將之前寫的 curl
略作更改:
---
- name: install curl
yum:
name: curl
when: ansible_distribution == 'CentOS'
- name: install curl
apt:
name: curl
when: ansible_distribution == 'Ubuntu' or ansible_distribution == 'Debian'
除此之外,由於在未來的章節中,我會讓 Jenkins 使用 Ansible-lint 這個語法檢查器來反向檢查我們所有的 Ansible playbook 及 roles,以確保我們每次放在 GitHub 上的程式碼都是最佳狀態。為了安裝 Ansible-lint,我們需要先安裝 pip 這個 Python 套件管理工具在主機上:
workspace
├── Vagrantfile
├── ansible.cfg
├── inventory
├── playbook.yml
└── roles
├── curl
│ └── tasks
│ └── main.yml
├── git
│ └── tasks
│ └── main.yml
├── jenkins
│ ├── meta
│ │ └── main.yml
│ └── tasks
│ └── main.yml
└── pip
└── tasks
└── main.yml
並在 roles/pip/tasks/main.yml
中寫入以下內容:
---
- name: Add EPEL repository
yum_repository:
name: EPEL
description: EPEL yum repo
baseurl: http://download.fedoraproject.org/pub/epel/$releasever/$basearch/
gpgkey: /etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-6
when: ansible_distribution == 'CentOS' and ansible_distribution_major_version == '6'
- name: install EPEL
yum:
name: epel-release
when: ansible_distribution == 'CentOS' and ansible_distribution_major_version == '7'
- name: install pip
yum:
name: python-pip
state: installed
update_cache: yes
when: ansible_distribution == 'CentOS'
- name: install pip
apt:
name: python-pip
update_cache: yes
when: ansible_distribution == 'Ubuntu' or ansible_distribution == 'Debian'
雖然 pip 也是在一般操作系統中非常常用的套件管理工具,但在 CentOS 作業系統中安裝 pip 會稍微比其他 Linux / Unix 的安裝步驟更加麻煩一點,我們會需要先安裝 EPEL (Extra Packages for Enterprise Linux) 才可以進行 pip 的安裝,而其中又以 CentOS 6.x 版本的安裝更加繁瑣。由於我們這次的重點並非討論其中的差異,因此我只將我個人的 role 在這裡分享給大家,若有興趣了解的讀者可以在網路上自行研究其差異 (e.g. CentOS 6.x, CentOS 7.x)。
接著,使用剛安裝好的 pip
來安裝 Ansible-lint:
workspace
├── Vagrantfile
├── ansible.cfg
├── inventory
├── playbook.yml
└── roles
├── ansible-lint
│ ├── meta
│ │ └── main.yml
│ └── tasks
│ └── main.yml
├── curl
│ └── tasks
│ └── main.yml
├── git
│ └── tasks
│ └── main.yml
├── jenkins
│ ├── meta
│ │ └── main.yml
│ └── tasks
│ └── main.yml
└── pip
└── tasks
└── main.yml
在 roles/ansible-lint/meta/main.yml
中添加 pip
為其角色依賴:
---
dependencies:
- { role: pip, become: yes }
接著在 roles/ansible-lint/tasks/main.yml
新增內容:
---
- name: install ansible-lint dependencies (via pip)
pip:
name: "{{ item }}"
with_items:
- markupsafe
- PyYAML
- name: install ansible-lint dependencies (via apt-get)
apt:
name: "{{ item }}"
update_cache: yes
with_items:
- python-keyczar
- python-httplib2
- python-jinja2
- python-paramiko
- python-setuptools
- python-six
- python-dev
when: ansible_distribution == 'Ubuntu' or ansible_distribution == 'Debian'
- name: install ansible-lint
pip:
name: ansible-lint
由於我們使用的是相當乾淨的環境,在安裝 Ansible-lint 之前,我們還必須要分別使用 apt 及 pip 來安裝一些 Ansible-lint 需要的 Python 依賴。在這裡順便介紹一下 Ansible 基本迴圈的寫法。我們可以使用 {{ item }}
跟 with_items
的組合來實現類似於一般程式語言中的 For 迴圈概念。Ansible 在進入帶有 with_items
迴圈的 task 後,會依序將 with_items
下的清單代入 {{ item }}
的位置。
最後,更新 jenkins
的 meta
依賴:
---
dependencies:
- { role: curl, become: yes }
- { role: git, become: yes }
- { role: ansible-lint, become: yes }
運行 playbook 後結果如下:
PLAY [ironman] *****************************************************************
TASK [setup] *******************************************************************
ok: [ironman]
TASK [curl : install curl] *****************************************************
skipping: [ironman]
TASK [curl : install curl] *****************************************************
ok: [ironman]
TASK [git : install git] *******************************************************
skipping: [ironman]
TASK [git : install git] *******************************************************
changed: [ironman]
TASK [pip : Add EPEL repository] ***********************************************
skipping: [ironman]
TASK [pip : install EPEL] ******************************************************
skipping: [ironman]
TASK [pip : install pip] *******************************************************
skipping: [ironman]
TASK [pip : install pip] *******************************************************
changed: [ironman]
TASK [ansible-lint : install ansible-lint dependencies (via pip)] **************
changed: [ironman] => (item=markupsafe)
changed: [ironman] => (item=PyYAML)
TASK [ansible-lint : install ansible-lint dependencies (via apt-get)] **********
changed: [ironman] => (item=[u'python-keyczar', u'python-httplib2', u'python-jinja2', u'python-paramiko', u'python-setuptools', u'python-six', u'python-dev'])
TASK [ansible-lint : install ansible-lint] *************************************
changed: [ironman]
TASK [jenkins : add jenkins key] ***********************************************
ok: [ironman]
TASK [jenkins : add jenkins repository] ****************************************
ok: [ironman]
TASK [jenkins : install jenkins] ***********************************************
changed: [ironman]
PLAY RECAP *********************************************************************
ironman : ok=7 changed=3 unreachable=0 failed=0
我們可以看到 Ansible 會根據對應的作業系統判斷哪些 tasks 可以被略過 (skipping
),這也是為什麼我們不需要特別為不同作業系統保存多份版本的 role,只要專注在同一個 role 中開發當前任務即可。另外,若仔細閱讀在終端機上顯示的 Ansible 安裝流程,應該會發現已經被安裝過的服務並不會一直重複被安裝。顯示為 changed
表示遙控主機在這次運行 playbook 的過程裡,Ansible 在該項 task 中對系統做了改變;ok
則表示該 task 的完成條件已滿足,所以 Ansible 並不會額外對主機做其他變動。特別需要解釋的是,若我們使用了 update_cache: yes
這項屬性來透過套件管理工具安裝服務。我們每次安裝服務前都會試圖將套件管理工具的版本都更新到當前最新版本,然後才進行安裝步驟,因此每次 task 的狀態都將會顯示為 changed
。
Ansible Galaxy 是一個 Ansible 官方提供的 Ansible Role 共享平台。所有 Ansible 的使用者都可以自由上傳自己的 role 與全世界的開發人員分享。在接到任務需求前,建議讀者可以花個幾分鐘在上面稍微瀏覽一下,看看是否有適合自己使用的現成 role 的可以立刻拿來使用。不過,為避免讀者混淆,這次就不特別介紹如何使用 ansible-galaxy
來調用其他開發者的 role,有興趣的讀者可以自行參閱官方使用教學。
目前版本 ansible 2.4.1.0 好像會把 ubuntu 的 ansible_distribution 誤判為 Mandriva 的 bug (issue #30693)
也就是說條件 ansible_distribution == 'Ubuntu'
這個條件會造成程式碼無法執行
以下是在 vagrant box hashicrop/precise64 的執行錯誤的部分
$ ansible-playbook --private-key .vagrant/machines/ironman/virtualbox/private_key -i inventory playbook.yml
TASK [pip : install EPEL]
fatal: [127.0.0.1]: FAILED! => {"failed": true, "msg": "The conditional check 'ansible_distribution == 'CentOS' and ansible_distribution_major_version == '7' failed. The error was: template error while templating string: unexpected char u\"'\" at 81. String: {% if ansible_distribution == 'CentOS' and ansible_distribution_major_version == '7 %} True {% else %} False {% endif %}\n\nThe error appears to have been in '/Users/roy.wu/e04/devopsDemo/roles/pip/tasks/main.yml': line 10, column 3, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n- name: install EPEL\n ^ here\n"}
以下是 ansible 判斷 vagrant box hashicrop/precise64 的結果
$ ansible all --private-key .vagrant/machines/ironman/virtualbox/private_key -i inventory -m setup | grep -i ansible_distribution
"ansible_distribution": "Mandriva",
"ansible_distribution_file_parsed": false,
"ansible_distribution_file_path": "/etc/lsb-release",
"ansible_distribution_file_variety": "Mandriva",
"ansible_distribution_major_version": "12",
"ansible_distribution_release": "precise",
"ansible_distribution_version": "12.04",
以下是在 vagrant box 執行cat /etc/lsb-release
的結果
$ cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=12.04
DISTRIB_CODENAME=precise
DISTRIB_DESCRIPTION="Ubuntu 12.04 LTS"
恩,我會在新版一併檢查,感謝你的測試~