今天我們的一個舊專案,也開始採用 Vagrant 了。前二個月我們已經在一個新專案裡使用 Vagrant 來輔助開發環境的建立,那次的使用經驗還不錯,所以這一次連舊專案也導入了 Vagrant。
Vagrant 是一個管理虛擬器的工具 (主要是 VirtualBox ),你可以寫個設定檔,就可以建立一個完全隔離,客製的虛擬器。通常它都是用來開發,可確保每個開發員的環境都相同。一般常聽到的做法是整個開發環境都放在虛擬器裡,特別是 script 語言。script 語言執行環境及函式庫通常和 OS 綁在一起,因此要同時安裝不同版本很痛苦,得靠特別的工具來做。而且 script 通常是直譯,不用編譯,所以只要將程式碼複製或與虛擬器分享就可以直接執行。
不過,這是 script 語言的做法。我們公司的主力是 Java。Java 需要編譯、需要 Eclipse 這類大型的 IDE,放進虛擬器這做法不切實際。而且 Java 的開發環境一直都是隔離的,每個專案都自行設定 classpath 與 JDK 版本 ,而執行期也是完全獨立的 JVM 互不干擾。因此對 Java 生態圈來說,很少人需要額外的虛擬器來做隔離。
程式碼的開發上,Vagrant 對我們來說沒用。不過,對於軟體所依賴的外部資源,像是資料庫,利用 Vagrant 來管理就很不錯,最少我們在最近的兩個專案中嘗到了一些甜頭。
其實一般 server side 的軟體,大多是只配一個 RDBMS,在開發期間就在自己的開發機上裝一台,然後很多個專案共用那台資料庫伺服器。這樣的方案很簡單,我們也是這樣一路走來。但隨著專案大了起來,會開始裝 redis 啦,cassandra 啦,elasticsearch 啦等等越來越多的外部資源,然後專案的開發人員也變多。要每個人都裝這麼多種的資料庫,還要設定正確,難度越來越高。本來我也是很鐵齒的不想找這類的工具來解決 (再加上 Java 開發用不到)。但實在已經到臨界點了,再不用會受不了。
自動化建立開發環境
對於建置開發環境,我有個理想的目標,就是可以在一台新電腦裡,全自動安裝所有開發環境。當然,我們目前達不到這個水準,但希望能夠越自動化越好。我們的建置步驟大致上如下:
- 安裝 JDK, Eclipse, Gradle 等基本工具
- git clone 源始碼
- 執行
gradle cleanEclipse eclipse
指令建立所有 eclipse 用的 meta data - 將專案匯入 Eclipse 內
- 安裝 VirtualBox, Vagrant, Ansible
- 執行
vagrant up
建立虛擬器,裡面包含所有需要的資料庫和其他服務 - 執行一個 script 建立空白的資料庫
大致上是這樣,離 一鍵安裝到底 還有段距離,但是需要安裝的軟體只剩下一些基本的工具,而過程中也不用再去改設定檔啊、路徑啊、port 幾號之類的事,整件事已簡化很多。當然自動化是要付出代價的,上述中的第三步是自動產生 eclipse 環境,這 script 需要隨著開發的進行不斷的維護。而第六步,執行 vagrant up
來建立可用的虛擬器也要花工夫建置。
Vagrant Provision with Ansible
來說說第六步的細節,我們的設定檔分兩塊,一個是 Vagrant 本身的設定檔 Vagrantfile
,這個很單純,只是設設記憶體用多大,要開多少 port 等等。另一大塊是 Ansible。Ansible 是一個 python 為基礎的自動化配置與部署的工具,一般多用在大量的伺服器環境管理上。我們則把它用在開發環境的配置 (provision),一方面藉由它來自動化配置開發用的,那複雜的資料庫架構,另一方面也會利用同樣的設定檔,配置正式機的環境。
我自己的使用經驗是,維護 Ansible 的設定檔 (稱做 playbook) 相當的花時間,因為要達到 idempotent provision 需要花很多的工夫在每個安裝指令上。idempotent provision 講白話就是 --- 不論指令重覆執行幾次,最後配置的結果會一樣,而且不會有副作用。例如你的 playbook 裡配置了 MySql,其中有一步是要改它設定檔中的 port。你要確保不管執行幾次,最後的 port 都是你想要的。如果設定檔的 port 改到了,MySql 就得重啟套用新的改變,如果已經改過了,那就要唯持不變,而已經在執行的 MySql 則不該重啟。
花時間維護 Ansible 的 playbook 我個人認為相當值得,因為不僅未來都可自動化配置,playbook 本身也是個文件。知道整個系統是怎麼裝起來的,調了多少參數是很重要的。以往我安裝伺服器都會寫安裝文件,把每個步驟都寫下來。現在這一步省了,playbook 就是個可讀,也能執行的文件。
Provision vs. Deployment
在導入 Vagrant 與 Ansible 的過程中,最讓我疑惑的就是它們的設定檔要寫到多遠? Ansible 幾乎是萬能的,什麼都能執行。我可以用 playbook 配置 (Provision) MySql 伺服器,那麼我要在 playbook 裡部署 (Deploy) 資料庫的 schema 嗎?理論上一個完整的開發環境應該要建立一個空白的資料庫 (權限、table 什麼的都建立好了),這樣開發才能正式開始。
對於這個疑惑,我後來有了答案,理論上可以包含 deploy 的步驟,但 deploy schema 這樣的操作要做到 idempotent 太過困難,尤其是開發過程中對資料庫會有很多試誤,刪刪改改很多次,維護這些小修改是在浪費時間。因此我目前的做法是 Vagrant/Ansible 只做到 provision,不碰 deployment。Ansible 我們會拿來做 deployment,但那是之於一版一版穩穩上線的正式機,而不是不斷修修改改的開發機。
所以對於 schema 的建立,我們則是額外用另一個自動的 script 或程式來執行,它每次都是全部刪掉再重建一個全新的。不用管 idempotent 的要求。
小結
本文大概簡介了一下我們怎麼使用 Vagrant。我們不拿它做開發環境,但利用它來管理開發所需要的外部資源,而配置虛擬器則是採用 Ansible 這個工具。我們公司的開發機器都是 Mac mini,少部份是 Linux。Vagrant + Ansible 的組合讓我們在異質 OS 環境下也能共用同樣的設定檔,建立同樣的虛擬器,這非常不錯。
Vagrant 不是什麼新技術,甚至在後起之秀 Docker 的威脅下,已經岌岌可危。也許之後 Docker 穩定成熟之後,我們會考慮轉移。但現階段 Vagrant 還是個簡單又可信賴的好工具。