在 HipChat 依然主流、Slack 尚未席捲市場的那個年代,那時建立 ChatBot 的工具還不像現在這麼豐富、中文教學資源也比較匱乏,但在當時就已有不少公司藉由 HipChat + ChatBot 的方式實現自動化及 ChatOps。由於透過此種方式,團隊成員只要在即時通訊工具中呼叫 ChatBot 即可自動完成許多重複性工作,可說是 Dev 與 Ops 兩方都十分喜愛的工具。
本文就讓我們試著運用 GitLab CI 搭配 Mattermost slash command
實現 ChatOps,試著在沒有 ChatBot 的狀況下,快速建立 ChatOps。(本文撰寫時,使用的 GitLab 版本為 13.8 。)
(本文同步發表於 Medium。)
過程紀錄
要透過 GitLab 實現 ChatOps 非常的簡單,基本概念就是將 GitLab CI Pipeline 視為是一個幫你執行自動化腳本的平台,在 CI Pipeline 中規劃各種 CI Job,接著再透過 Mattermost slash command
去 Trigger 特定的 CI Job 幫我們完成想要執行的重複性工作。
以下先說明必要的設定步驟。
啟用 Mattermost slash commands
首先請參考艦長上一篇文章《GitLab 啟用 Project integration - Mattermost slash commands》的步驟,建立一個專案,並且啟用此專案的 Project integration - Mattermost slash commands
。
完成後,可以按文章的範例,試著在 Mattermost 上嘗試執行 Slash command。例如我建立的 Slash command 是 gitlab-ops
,下圖便是我執行 /gitlab-ops help
得到的結果。
如果能得到這樣的回應,即代表 Slash command 有串接成功。
第一個 ChatOps CI Job
接著我們要為此專案規劃 CI Pipeline,身為工程師我們的第一個範例當然是 Hello World 啦!
讓我們修改 .gitlab-ci.yml
,輸入如下的內容。
# 為 CI Pipeline 設置 Stage
stages:
- chatops
# CI Job Name 命名為 hello-world
hello-world:
stage: chatops
# 限制此 CI Job 只能被 Slash command 來 Trigger
only: [chat]
# 老樣子,想要執行的動作寫在 script: 內。
script:
- echo "Hello World"
送出 Commit,順利更新 CI Pipeline 之後,我們就能回到 Mattermost 試著 Trigger 它。要在 Mattermost 執行的指令為 /<Slash command> run < CI Job name>
,因此以我的狀況為例,我要執行的指令會是 /gitlab-ops run hello-world
,下圖便是我執行得到的結果。
傳遞變數
下一步,我們要多補上一些變化,讓我們不只可以做到 Hello World,而是讓 ChatOps 能回傳我們輸入的任何字串。繼續修改 .gitlab-ci.yml
,增加另一項 CI Job。
echo:
stage: chatops
only: [chat]
script:
- echo $CHAT_INPUT
這次在 Mattermost 除了原本的指令,後面我們還要補上想要傳遞給 GitLab CI 的字串,即是 /<Slash command> run < CI Job name> <arguments>
,只要在 Slash command 的最後再串上一個 <arguments>
即可。同樣以我的示範為例,指令即是 /gitlab-ops run echo Hi GitLab Hero!!
,同樣下圖是得到的結果。
規劃你的 CI Job
做完上一個步驟,我們就已經將基本步驟都說明完畢,剩下的就是大家各自的功課了。為何會這麼說呢?因為其實我們可以發現 Gitlab 提供的 ChatOps 功能,就只是讓你可以在 Mattermost 直接去 Trigger 特定 project 之 CI Job。
因此 ChatOps 或者該說是 Slash command 能幫你完成哪些工作,完全取決於你所規劃的 CI Job 內容。在前面的範例我們只讓它 echo "Hello World"
,當然就只能得到 Hello World。那假如我們規劃的 CI Job 是部署應用程式,想當然我們就可以透過 ChatOps 執行 Deploy 的動作,下面我們就用 Deploy 的 CI Job 為例,示範執行的 Slash command 會是 /gitlab-ops run prod-deploy chengweichen.com
。
prod-deploy:
stage: chatops
only: [chat]
tags:
- "runner-for-prod"
script:
- cd /ansible/playbooks
- ansible-playbook deploy/show-who-trigger-prod-deploy.yml
- ansible-playbook deploy/deploy-by-container.yml -e project_name="$CHAT_INPUT"
如上案例,這次的 CI Job 可以幫艦長完成 chengweichen.com 的 Prod-deploy。當我透過 Slash command 來 Trigger 這項 CI Job 時,GitLab CI Runner 就會去執行我事先準備好的 Ansible playbook,例如 show-who-trigger-prod-deploy.yml
就是我多設了一個權限驗證的 Playbook,它會檢查是誰 Trigger 這個 CI Job,這個人是否真的有權限進行 Prod-deploy,而 deploy-by-container.yml
就會幫我在目標 Server 上以 Container 完成應用程式的 Deploy。在這個範例中,我輸入的 <arguments>
即是 chengweichen.com
,在 CI Job 中 <arguments>
會被帶入為變數 $CHAT_INPUT
。如此一來我在設計 Ansible playbook 時,就可以規劃讓 deploy-by-container.yml
會根據我輸入不同的 -e project_name="$CHAT_INPUT"
而去 Deploy 不同的專案。藉由善用 <arguments>
,我們就可以更靈活的規劃 CI Job,讓 ChatOps 執行的工作更具備彈性與多功能。
【小提醒】在 GitLab 官方文件「Best practices for ChatOps CI jobs」,其中也有一段提醒要注意 ChatOps CI jobs 的 Access control,因此建議大家在 CI Job 的
script:
那一段可以加上一個動作是使用者或權限驗證的動作,例如官方建議可以利用 CI Pipeline 的預設變數GITLAB_USER_ID
,確保只有你允許的 GitLab User 可以執行後續的動作。
同理,再舉一個案例。
service-status:
stage: chatops
only: [chat]
tags:
- "runner-for-prod"
script:
- cd /ansible/playbooks
- ansible-playbook monitoring/server.yml -e project_name="$CHAT_INPUT"
- ansible-playbook monitoring/service.yml -e project_name="$CHAT_INPUT"
- ansible-playbook monitoring/application.yml -e project_name="$CHAT_INPUT"
這個案例是另一個 CI Job,專門負責用來監控特定專案的運作狀況,分別從 Server、Service 及 Application 三個層面著手檢查特定服務是否正常上線。當應用程式發生異常時,團隊成員就可以透過 Slash command 再次 Trigger 此 CI Job 驗證服務是否真的發生異常,並透過不同角度的 Monitoring 了解問題會是出在哪裏。
透過這兩個案例,相信各位應該能理解艦長想表達的意思。透過 GitLab 建立的 ChatOps 機制能做到哪些事情,完全取決你如何規劃 CI Job。因此你不止要考慮 CI Job 本身,連帶 CI Runner、Runner 的權限與安全性、Runner 需要安裝哪些軟體或環境配置、執行各項自動化工作需要哪些權限⋯⋯等都需要規劃;但回過頭來想,這不就是我們原本在規劃 GitLab CI Pipeline 一直在做的事情嗎?如此看來,GitLab 透過 Slash command 這種方式來快速實現 ChatOps 著實是一個不錯的想法。
控制回傳的 Output
前面的範例中,我們都有成功得到 ChatOps 的執行結果 Output,將它拿去和 GitLab CI 的 CI Job 執行 Log 對照,會發現兩者是相同的。
(ChatOps 回傳至 Mattermost 的 Output。)
(GitLab CI Job 的執行 Log。)
由於該範例的 CI Job 做的事情很少,因此執行的 Log 可以全數回傳,但如果你的 CI Job 會產生大量的 Log,就有可能會遇到下圖 The output is too large to be sent back directly!
的狀況。
因此對於會產生大量 Log 的 CI Job,我們可以利用 Custom section 的做法,在 CI Job 中控制只有哪些 Log 需要回傳至 Output 中。
參考官方文件,讓我們再次修改前述的範例如下。
echo:
stage: chatops
only: [chat]
script:
- echo "你看不見我"
- echo -e "section_start:$( date +%s ):chat_reply\r\033[0K\n$( ls -la )\nsection_end:$( date +%s ):chat_reply\r\033[0K"
- echo "你還是看不見我"
透過 Slash command 執行後會得到下面的結果。只有其中的 $( ls -la )
的內容被回傳顯示在 Mattermost,script:
的第一行與第三行「你看不見我」皆不會顯示在 Output。
如上案例,如果想要控制 ChatOps 回傳的 Output 我們可以透過建立名為 chat_reply
的 Custom section,將必須回傳結果的動作或資訊放在其中。
下面我們示範另一個範例,我們將原本寫在同一行的 Custom section 分為多行,並且這次也要回傳我們輸入的 $CHAT_INPUT
。
echo:
stage: chatops
only: [chat]
script:
- echo "你看不見我"
- echo -e "section_start:`date +%s`:chat_reply\r\e[0K"
- ls -la
- echo $CHAT_INPUT
- echo -e "section_end:`date +%s`:chat_reply\r\e[0K"
- echo "你還是看不見我"
結果如下圖,可以看見我輸入的 $CHAT_INPUT
字串為「你看得見我~」也接續 ls -la
成功顯示在 Output。
【小提醒】一個 CI Job 只可以有一組名為
chat_reply
的 Custom section。
只要善用這種方式,即可巧妙的控制 Outout,在規劃 ChatOps 的 CI Job 時,也別忘了規劃如何 Output。
最後,為了要讓我們的 ChatOps 變得更好用,建議大家也可以設置一個名為 help
的 CI Job。而這個 Job 就是你的使用文件啦,當使用者忘記有哪些 CI Job 可以使用時,可以試著呼叫 /<Slash command> run help
回傳 ChatOps 的 README 或其他能夠提示如何使用 ChatOps 的說明文字,幫助大家都能直接在 Mattermost 中查閱使用方式。
結語
本文與大家分享了如何在 GitLab 運用 Slash command 實現 ChatOps,但本文也只是一個簡單的入門指引,因為就如文章內容提過的「透過 GitLab 建立的 ChatOps 機制能做到哪些事情,完全取決你如何規劃 CI Job」。如何規劃、管理並維護 CI Job 才是真正困難的地方,相比之下串接 Slash command 根本是小菜一碟。
當然,除了本文介紹的 Slash command 之外,我們依然可以使用 ChatBot 在 GitLab 上實現 ChatOps,畢竟進階的 ChatBot 工具可以做到更多人性化的介面互動,但這邊還是要提醒大家反思,不要因為 ChatBot 的多功能而混淆了重點,關鍵還是在於「為何我們需要 ChatOps?」,以及「這項工具會交由哪些使用者來運用?」希望大家都能找到最適合你們團隊的 ChatOps 實踐方式。
你的企業、團隊也有在使用 ChatOps 嗎?歡迎與我分享、或是來到 DevOps Taiwan Community 公開分享你的經驗喔。