這是文字記錄
從第二次提交開始,任何程式碼都將成為遺留程式碼,因為最初的想法開始偏離嚴酷的現實。這既不好也不壞,這是一個難以爭辯的既定事實,而且必須接受。這個過程的一部分是重構。將基礎設施重構為程式碼。讓故事從如何在一年內重構 Ansible 並且不瘋狂開始。
遺產的誕生
第一天:零號病人
從前有一個有條件的項目。它有一個開發團隊和營運工程師。他們正在解決同一個問題:如何部署伺服器和運行應用程式。問題是每個團隊都以自己的方式解決這個問題。在這個專案中,決定使用 Ansible 在開發和營運團隊之間同步知識。
第 89 天:遺產的誕生
他們自己沒有註意到這一點,他們想盡可能地做到最好,但結果卻成了遺產。這是怎麼發生的?
- 我們這裡有一項緊急任務,讓我們進行一次骯髒的駭客攻擊,然後修復它。
- 您不必編寫文檔,這裡發生的一切都很清楚。
- 我了解 Ansible/Python/Bash/Terraform!看我怎麼躲!
- 我是一名完整的 Stack Overflow 開發人員,從 stackoverflow 複製了這個,我不知道它是如何運作的,但它看起來很酷並且解決了問題。
結果,你可能會得到一類難以理解的程式碼,沒有文檔,不清楚它是做什麼的,是否需要它,但問題是你需要開發它,修改它,添加拐杖和支持,使情況變得更加糟糕。
- hosts: localhost
tasks:
- shell: echo -n Z >> a.txt && cat a.txt
register: output
delay: 1
retries: 5
until: not output.stdout.find("ZZZ")
第 109 天:意識到問題
最初構想和實現的IaC模型不再滿足使用者/業務/其他團隊的要求,對基礎設施進行更改的時間也不再可接受。此時此刻,我們明白是時候採取行動了。
IaC重構
第 139 天:你真的需要重構嗎?
在急於重構之前,您必須回答一些重要問題:
- 為什麼你需要這一切?
- 你有時間嗎?
- 知識夠用嗎?
如果你不知道如何回答這些問題,那麼重構就會在開始之前就結束,或者只會變得更糟。因為有經驗(
第 149 天:準備重構
首先要做的就是準備。決定我們要做什麼。為此,我們進行溝通、尋找問題領域並找出解決方法。我們以某種方式記錄由此產生的概念,例如一篇匯合的文章,這樣當問題出現時“什麼是最好的?”或“哪個是正確的?”我們還沒有迷失方向。就我們而言,我們堅持這個想法 分而治之:我們將基礎設施分解成小塊/磚塊。這種方法允許您採用一個孤立的基礎設施,了解它的作用,透過測試覆蓋它並對其進行更改,而不必擔心會破壞任何內容。
事實證明,基礎設施測試成為基石,這裡值得一提的是基礎設施測試金字塔。與正在開發中的想法完全相同,但對於基礎設施而言:我們正在從檢查縮排等簡單事物的廉價快速測試轉向部署整個基礎設施的昂貴的全面測試。
Ansible 測試嘗試
在我們描述如何涵蓋專案的 Ansible 測試之前,我將描述我之前有機會使用的嘗試和方法,以便了解所做決策的背景。
第-997 天:SDS 規定
我第一次測試 Ansible 是在一個開發 SDS(軟體定義儲存)的專案中。關於這個主題有一篇單獨的文章
第 #-701 天:Ansible 和測試廚房
Ansible 測試想法的發展是使用現成的工具,即 test kitchen/kitchen-ci 和 inspec。這個選擇是由對 Ruby 的了解決定的(更多詳細信息,請參閱關於 Habré 的文章:
總體而言,該解決方案有效,但由於不均勻性,因此存在一些沉積物。當測試人數增加到 13 個基本角色和 2 個組合較小角色的元角色時,測試突然開始運行 70 分鐘,幾乎延長了 2 倍。談論 XP(極限編程)實踐很困難,因為…沒有人願意等70分鐘。這就是改變方法的原因
第#-601天:Ansible 和 molecular
從概念上講,這與 testkitchen 類似,只是我們將角色測試移至 docker 並更改了堆疊。結果,20個角色的時間減少到穩定的25-7分鐘。
透過將測試角色數量增加到 17 個並檢查 45 個角色,我們在 28 個 jenkins 從機上運行了 2 分鐘。
第 167 天:為專案新增 Ansible 測試
最有可能的是,不可能很快完成重構任務。這個任務應該是可衡量的,這樣你就可以把它分成小塊,然後用茶匙一塊一塊地吃掉大象。必須了解自己是否朝著正確的方向前進,還有多久。
一般來說,如何完成並不重要,你可以寫在一張紙上,你可以在衣櫃上貼貼紙,你可以在 Jira 中創建任務,或者你可以打開 Google Docs 並記下當前狀態。腿的生長是因為這個過程不是立竿見影的,而是漫長而乏味的。不太可能有人希望你在重構過程中失去想法、感到疲倦、不知所措。
重構很簡單:
- 吃。
- 睡覺。
- 碼。
- IaC 測試。
- 重複
我們重複此操作,直到達到預期目標。
可能無法立即開始測試所有內容,因此我們的首要任務是從 linting 和檢查語法開始。
第 181 天:綠色建構大師
Linting 是邁向 Green Build Master 的一小步。這幾乎不會破壞任何東西,但它允許您調試流程並在 Jenkins 中進行綠色構建。這個想法是在團隊中養成習慣:
- 紅色測試很糟糕。
- 我來修復一些東西,同時使程式碼比你之前更好。
第 193 天:從 linting 到單元測試
建立了將程式碼放入 master 的過程後,您可以開始逐步改進的過程 - 用啟動角色替換 linting,您甚至可以在沒有冪等性的情況下做到這一點。您需要了解如何應用角色以及它們如何運作。
第 211 天:從單元測試到整合測試
當大多數角色都被單元測試覆蓋並且所有內容都被檢查時,您可以繼續添加整合測試。那些。測試的不是基礎設施中的單一磚塊,而是它們的組合,例如完整的實例配置。
使用 jenkins,我們產生了許多並行檢查角色/劇本的階段,然後是容器中的單元測試,最後是整合測試。
Jenkins + Docker + Ansible = 測試
- 檢查儲存庫並產生建置階段。
- 並行運行 lint playbook 階段。
- 並行運行 lint 角色階段。
- 並行運行語法檢查角色階段。
- 並行運行測試角色階段。
- 棉絨的作用。
- 檢查對其他角色的依賴性。
- 檢查語法。
- 建立docker實例
- 運行 molecular/default/playbook.yml。
- 檢查冪等性。
- 運行整合測試
- 完
第 271 天:巴士因素
最初,重構是由兩三個人組成的小組進行的。他們審查了master中的程式碼。隨著時間的推移,團隊掌握瞭如何編寫程式碼和程式碼審查的知識,這有助於傳播有關基礎設施及其工作原理的知識。這裡的亮點是審稿人是根據時間表一一選出的,即您有可能會進入一個新的基礎設施。
而且這裡應該要很舒服。進行審查很方便,可以在框架內查看已完成的任務以及討論的歷史記錄。我們整合了jenkins + bitbucket + jira。
但因此,審查並不是萬能的;不知何故,我們進入了主代碼,這使我們的測試失敗了:
- get_url:
url: "{{ actk_certs }}/{{ item.1 }}"
dest: "{{ actk_src_tmp }}/"
username: "{{ actk_mvn_user }}"
password: "{{ actk_mvn_pass }}"
with_subelements:
- "{{ actk_cert_list }}"
- "{{ actk_certs }}"
delegate_to: localhost
- copy:
src: "{{ actk_src_tmp }}/{{ item.1 }}"
dest: "{{ actk_dst_tmp }}"
with_subelements:
- "{{ actk_cert_list }}"
- "{{ actk_certs }}"
然後他們修復了它,但沉積物仍然存在。
get_url:
url: "{{ actk_certs }}/{{ actk_item }}"
dest: "{{ actk_src_tmp }}/{{ actk_item }}"
username: "{{ actk_mvn_user }}"
password: "{{ actk_mvn_pass }}"
loop_control:
loop_var: actk_item
with_items: "{{ actk_cert_list }}"
delegate_to: localhost
- copy:
src: "{{ actk_src_tmp }}/{{ actk_item }}"
dest: "{{ actk_dst_tmp }}"
loop_control:
loop_var: actk_item
with_items: "{{ actk_cert_list }}"
第 311 天:加快測試速度
隨著時間的推移,測試越來越多,建置運行速度越來越慢,最壞的情況下最多需要一個小時。在其中一次回顧中,有這樣一句話:“有測試很好,但速度很慢。”因此,我們放棄了虛擬機器上的整合測試,並針對 Docker 進行了調整,以使其更快。我們也用 ansible verifier 取代了 testinfra,以減少使用的工具數量。
嚴格來說,有一系列措施:
- 切換到碼頭工人。
- 刪除角色測試,該測試由於依賴性而重複。
- 增加奴隸數量。
- 試運行順序。
- 掉毛能力 全部 透過一個命令在本地進行操作。
結果jenkins上的Pipeline也統一了
- 生成建置階段。
- Lint 全部並行。
- 並行運行測試角色階段。
- 完成。
汲取的經驗教訓
避免全域變數
Ansible 使用全域變量,形式中有部分解決方法
讓我舉一個例子。讓我們有 role_a
и role_b
# cat role_a/defaults/main.yml
---
msg: a
# cat role_a/tasks/main.yml
---
- debug:
msg: role_a={{ msg }}
# cat role_b/defaults/main.yml
---
msg: b
# cat role_b/tasks/main.yml
---
- set_fact:
msg: b
- debug:
msg: role_b={{ msg }}
- hosts: localhost
vars:
msg: hello
roles:
- role: role_a
- role: role_b
tasks:
- debug:
msg: play={{msg}}
有趣的是,劇本的結果將取決於並不總是顯而易見的事情,例如角色列出的順序。不幸的是,這是 Ansible 的本質,最好的方法就是使用某種協議,例如,在一個角色中,只使用該角色中描述的變數。
BAD: 使用全域變數。
# cat roles/some_role/tasks/main.yml
---
debug:
var: java_home
好:五 defaults
定義必要的變量,然後僅使用它們。
# cat roles/some_role/defaults/main.yml
---
r__java_home:
"{{ java_home | default('/path') }}"
# cat roles/some_role/tasks/main.yml
---
debug:
var: r__java_home
前綴角色變數
BAD: 使用全域變數。
# cat roles/some_role/defaults/main.yml
---
db_port: 5432
好:在變數的角色中,使用以角色名稱為前綴的變數;透過查看清單,這將使您更容易理解正在發生的情況。
# cat roles/some_role/defaults/main.yml
---
some_role__db_port: 5432
使用循環控制變數
BAD: 在迴圈中使用標準變數 item
,如果此任務/劇本包含在某處,這可能會導致意外行為
---
- hosts: localhost
tasks:
- debug:
msg: "{{ item }}"
loop:
- item1
- item2
好:在循環中重新定義變數 loop_var
.
---
- hosts: localhost
tasks:
- debug:
msg: "{{ item_name }}"
loop:
- item1
- item2
loop_control:
loop_var: item_name
檢查輸入變數
我們同意使用變數前綴;檢查它們是否按照我們的預期定義,例如沒有被空值覆蓋,這並不是多餘的
好:檢查變數。
- name: "Verify that required string variables are defined"
assert:
that: ahs_var is defined and ahs_var | length > 0 and ahs_var != None
fail_msg: "{{ ahs_var }} needs to be set for the role to work "
success_msg: "Required variables {{ ahs_var }} is defined"
loop_control:
loop_var: ahs_var
with_items:
- ahs_item1
- ahs_item2
- ahs_item3
避免哈希字典,使用扁平結構
如果一個角色期望其參數之一包含雜湊/字典,那麼如果我們想要更改其中一個子參數,則需要覆蓋整個雜湊/字典,這會增加配置複雜度。
BAD:使用哈希/字典。
---
user:
name: admin
group: admin
好:使用平面變數結構。
---
user_name: admin
user_group: "{{ user_name }}"
創造冪等劇本和角色
角色和劇本必須是冪等的,因為減少配置漂移和對破壞某些東西的恐懼。但如果您使用分子,那麼這是預設行為。
避免使用指令 shell 模組
使用 shell 模組會產生命令式描述範式,而不是宣告式描述範式,後者是 Ansible 的核心。
透過分子測試你的角色
分子是一個非常靈活的東西,我們來看幾個場景。
分子多實例
В molecule.yml
在部分 platforms
您可以描述許多可以部署的主機。
---
driver:
name: docker
platforms:
- name: postgresql-instance
hostname: postgresql-instance
image: registry.example.com/postgres10:latest
pre_build_image: true
override_command: false
network_mode: host
- name: app-instance
hostname: app-instance
pre_build_image: true
image: registry.example.com/docker_centos_ansible_tests
network_mode: host
因此,這些主機可以 converge.yml
使用:
---
- name: Converge all
hosts: all
vars:
ansible_user: root
roles:
- role: some_role
- name: Converge db
hosts: db-instance
roles:
- role: some_db_role
- name: Converge app
hosts: app-instance
roles:
- role: some_app_role
Ansible驗證器
在分子中,可以使用 ansible 檢查實例是否已正確配置,而且,自版本 3 以來,這一直是預設值。它不像 testinfra/inspec 那麼靈活,但我們可以檢查文件的內容是否符合我們的期望:
---
- name: Verify
hosts: all
tasks:
- name: copy config
copy:
src: expected_standalone.conf
dest: /root/wildfly/bin/standalone.conf
mode: "0644"
owner: root
group: root
register: config_copy_result
- name: Certify that standalone.conf changed
assert:
that: not config_copy_result.changed
或部署服務,等待其可用並進行冒煙測試:
---
- name: Verify
hosts: solr
tasks:
- command: /blah/solr/bin/solr start -s /solr_home -p 8983 -force
- uri:
url: http://127.0.0.1:8983/solr
method: GET
status_code: 200
register: uri_result
until: uri_result is not failed
retries: 12
delay: 10
- name: Post documents to solr
command: /blah/solr/bin/post -c master /exampledocs/books.csv
將複雜的邏輯放入模組和插件中
Ansible提倡聲明式方法,因此當你進行程式碼分支、資料轉換、shell模組時,程式碼變得難以閱讀。為了解決這個問題並保持簡單易懂,透過創建自己的模組來解決這種複雜性並不是多餘的。
總結提示和技巧
- 避免全域變數。
- 角色變數的前綴。
- 使用循環控制變數。
- 檢查輸入變數。
- 避免使用哈希字典,使用扁平結構。
- 創建冪等劇本和角色。
- 避免使用指令 shell 模組。
- 透過分子測試你的角色。
- 將複雜的邏輯放入模組和插件中。
結論
即使您擁有 IaC,您也無法直接重構專案的基礎架構。這是一個漫長的過程,需要耐心、時間和知識。
外部連結
- 幻燈片
如何測試 Ansible 並且不要發瘋 - 視頻資料
如何測試 Ansible 並且不要發瘋 我從測試 200 行基礎設施程式碼中學到了什麼 Ansible:120 個月內將 18 個虛擬機器配置從 Coreos 遷移到 Centos 測試分發時如何用拐杖折斷自行車 測試我是否可以,或者 YML 程式設計師是否夢想測試 Ansible? 精彩的 IaC 測試文章、演講和連結列表 交叉貼文 Angielski版本
UPD1 2020.05.01 20:30 — 對於劇本的初步分析,您可以使用 callback_whitelist = profile_tasks
了解什麼是長期有效的。然後我們透過
UPD2 2020.05.03 16:34 -
來源: www.habr.com