30 June 2014

今天我們的一個舊專案,也開始採用 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 開發用不到)。但實在已經到臨界點了,再不用會受不了。

自動化建立開發環境

對於建置開發環境,我有個理想的目標,就是可以在一台新電腦裡,全自動安裝所有開發環境。當然,我們目前達不到這個水準,但希望能夠越自動化越好。我們的建置步驟大致上如下:

  1. 安裝 JDK, Eclipse, Gradle 等基本工具
  2. git clone 源始碼
  3. 執行 gradle cleanEclipse eclipse 指令建立所有 eclipse 用的 meta data
  4. 將專案匯入 Eclipse 內
  5. 安裝 VirtualBox, Vagrant, Ansible
  6. 執行 vagrant up 建立虛擬器,裡面包含所有需要的資料庫和其他服務
  7. 執行一個 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 還是個簡單又可信賴的好工具。


回響

可以用 Tag <I>、<B>,程式碼請用 <PRE>