03.07 用開源軟件打造企業級 DevOps 工作流(二):版本控制

前言

本文章為系列文章的第二篇,之前已經寫了一篇 《 》,主要介紹了DevOps的基本概念以及一些組成要素。在這篇文章中,我們將介紹 版本控制系統 / VCS(Version Control System),除了介紹版本控制的基礎概念以外,我們還將介紹如何使用開源的 GitLab 來實現版本控制系統。

版本控制系統

版本控制系統主要是針對軟件開發過程中對代碼變更的管理,保證了代碼的可回溯、可審查、可管理等功能需求,而最終的目的,是可維護性。在上一篇文章中,我們舉例闡述了沒有版本管理工具會出現什麼問題,因此不會在本文贅述。其實主要問題就是沒有版本管理工具導致的可維護性的下降,從而導致各種不可預測的bug的出現。

版本的主要內容包括三部分:

  1. 檢入檢出控制(Check-In / Check-Out Control)
  2. 分支與合併(Branch / Merge)
  3. 歷史記錄(History)

檢入檢出控制

關於檢入檢出,我們可以理解為代碼與 版本控制系統數據庫(VCS Database) 的同步與交互過程(如下圖)。


用開源軟件打造企業級 DevOps 工作流(二):版本控制


檢出(Check-out) 相當於用戶將 版本控制系統數據庫(VCS Database) 的代碼同步了一份到本地,如果與本地代碼發生了衝突,會比對本地代碼並做相應處理(後面我們會討論合併)。

檢入(Check-in) 相當於是檢出的逆操作,也就是將本地代碼同步到遠端版本管理系統,其實是對代碼數據庫的一種更新,相當於一次升級,因此,VCS 需要對將要更新的代碼進行版本審查,並會拒絕將不合法的代碼升級(例如非最新升級)更新到數據庫中。

檢入檢出的設置保證了代碼數據庫的原子操作,不會因為兩個人同時提交代碼而造成衝突。更改代碼通常在本地進行,每一次更改完畢後會將最新的代碼提交到代碼數據庫,同時更新代碼倉庫,保證遠端代碼是最新的。

分支與合併

代碼的 分支(Branch) 概念是指同一“祖先“的代碼在不同的方向上各自進行更迭,互不干擾。不同分支的代碼經過不斷的迭代,雖然來自同一個“祖先”節點,但很可能演變成完全不同的功能和結構。

一個分支相當於是對某個節點的拷貝,同時也可以自己發展成新的版本。版本控制系統中有分支的概念是為了方便開發多個 功能(Feature) 時防止互相干擾,是一種解決代碼衝突的方式。


用開源軟件打造企業級 DevOps 工作流(二):版本控制


例如,某開發團隊需要開發 功能 A 和 功能 B ,而這兩個功能都需要在 文件 M 上進行修改。如果同時開發 功能 A 和 功能 B,則會很難協調,因為開發人員需要同時操作文件 M。這就跟要求兩個猴子吃同一根香蕉一樣困難,那麼我們為什麼不分成兩根香蕉、一隻猴吃一根呢?

因此,我們創建了兩個分支 A 和 B,兩個開發者分別開發,互不影響,互不干擾,這樣操作不會造成代碼衝突,開發起來非常和諧。

雖然我們解決了同時修改一個文件的問題,我們還需要將兩個開發好的功能整合起來,這就產生了 合併(Merge) 的概念。

如上面那張圖,當 A 功能 和 B 功能 都開發好了之後,分別為版本 A2 和版本 B2,需要將兩者整合在一起,產生新的版本 M2,這個整合的過程就是合併。

當然,合併的過程會產生 衝突(Conflict),也就是兩個分支同時修改了同一份代碼。一般的版本控制系統(例如 Git 或 SVN)會嘗試自動將代碼進行合併,例如非常明顯的增刪操作。然而,也有不能自動合併的情況,通常要求開發人員手動合併代碼,這叫 解決衝突(Resolve Conflict)。

開發過程中,我們的分支管理會有很多種策略,一般開發團隊會根據項目的需要和團隊的情況選擇合適的分支管理策略,這個後面會講。

歷史記錄

這個其實很好理解,代碼的所有變動,變動了什麼(What)、何處變更的(Where)、何時變的(When)、由誰提交的變更(Who)、為什麼變更(Why,提交註釋),這些都會體現在 歷史記錄(History) 中。

如果代碼出了問題、或者需要參考歷史功能等,開發者可以根據歷史記錄來回溯代碼,找到 Bug 發生的原因,以及理解歷史代碼的設計等等。這有助於幫助後續的開(jie)發(pan)者(xia)更加輕鬆地掌握需要管理的代碼。

分支管理策略

我們在前面著重介紹了一下分支與合併,這裡我們會介紹一下其衍生出來的分支管理策略,這對日常開發來說非常重要,因為不同的分支策略對項目開發來說有著深遠的影響。

分支管理策略可以看作是一種開發模式:團隊成員之間如何通過分支與合併操作來協同工作,將不同的功能整合在一起,開發測試環境如何隔離,生產環境上線後如何做相應更改等等。

下面我們將介紹幾種常見的分支管理策略。

主幹開發(Trunk Based Development)

主幹開發(Trunk Based Development) 簡稱為 TBD,是一種常見的分支管理策略,也是 Google 和 Facebook 等大型互聯網巨頭經常採用的策略之一。

主幹開發要求所有代碼都提交到 主幹(Master) 分支上,從而避免了開發者們看到的代碼是過時代碼的情況。只有當需要 發佈(Release) 的時候,主幹才創建一個當前節點的分支,作為發佈用。


用開源軟件打造企業級 DevOps 工作流(二):版本控制


對於主幹開發來說,開發者要求每天開發前都同步最新代碼,如果有與新提交代碼的衝突,需要在本地自己解決後重新提交到主幹上。這樣的好處在於,由於開發者的本地代碼基本是跟主幹同步的,因此合併時並不會有重大的變更,合併代碼的時候相對比較容易,不會花很多時間。

而這樣做的缺點也很明顯,如果很多人在項目上進行開發,會導致源源不斷的更新代碼提交到主幹,這會導致發佈的時候存在眾多提交而導致出現了 bug 難以追溯,進而難以修復,容易出現“一顆耗子屎壞了一鍋粥”的情況。

我們接下來要介紹的 Git Flow 分支管理策略就是來解決這個缺點的。

Git Flow

Git Flow 是一種 特性分支(Feature Branch) 策略。特性分支策略的概念跟主幹開發策略的概念是相對的,意思是不同的功能拉出一個分支單獨開發,開發好後再合併到主幹,保證功能之間互不影響和干擾。

Git Flow 是特性分支策略的一種,是 Vincent Driessen 在 《A Successful Git Branching Model》(nvie.com/posts/a-suc…) 中提出的一種分支模型(如下圖)。


用開源軟件打造企業級 DevOps 工作流(二):版本控制


簡單來說,Git Flow 要求有 Master(主幹)、Develop(開發)、Release(發佈)和 Hotfix(熱修復)幾個基礎分支。

每次需要開發新功能時,在 Develop 分支下拉出一個 Feature 分支(特性分支)來進行單獨開發,開發好後合併到 Develop 分支。當 Develop 分支開發到一定程度的時候,再將其合併到 Release 分支。

Release 分支是在生產環境的 Master 分支前的起 UAT 測試緩衝作用的預備分支。當 Release 分支準備好之後,就將其合併入 Master 主幹分支,這樣就相當於在生產環境上發佈了新版本了。

當線上版本出了 Bug 需要修復的時候,我們會在直接在 Master 分支上拉出一個 Hotfix 分支,在這個分支上直接做修復,然後合併回主幹。同時 Hotfix 上的幾次修復還會合併到 Develop 上的某個節點,以保存這次修復,這樣 Develop 與 Master 就基本保持了一致。

這是經典意義的 Git Flow,但在實際操作的時候我們並不一定會原封不動地完全照搬這個模式。例如,很多時候我們其實用不到 Release 分支,只存在 Master 和 Develop,這對於中小型項目來說比較靈活。有時候 Release 也被稱為 Test 測試分支。另外還有些變種,例如主幹開發與 Git Flow 的部分結合,在 Develop 分支上做主幹開發,每次需要部署到生產環境的時候就將其合併入 Release 或 Master 分支,以發佈正式版本。

Git or SVN?

Git 和 SVN(Subversion) 是兩個比較受歡迎的版本控制工具。兩者之間最大的區別是:SVN 是集中式的,而 Git 是分佈式的(如下圖)。


用開源軟件打造企業級 DevOps 工作流(二):版本控制


SVN 要求代碼倉庫只有一箇中心倉庫,所有開發者在做提交代碼的操作前,必須保證自己本地代碼與中心倉庫的代碼完全同步;而 Git 相對來說就靈活得多,Git 不要求所有開發者的本地代碼完全一致,在提交時只要求被 push 的倉庫分支與本地的一致就可以。

另外,SVN 的分支合併複雜且不健壯,因為 SVN 無法區分合並是人工操作還是自動合併的,因此將不會創建一個合併記錄節點;而 Git 則相反,會在合併後創建一個合併記錄節點,這增加了可回溯性。而且,用 Git 創建分支的成本很低,只需要 git branch <branch> 就可以了。/<branch>

由於分支合併的複雜性,SVN 通常不適合用作特性分支策略,而適合主幹開發模式(Google App Engine 就是 SVN 管理的)。

而 Git 則既適合主幹開發模式,又天生支持特性分支策略。所以,我們一般都採用更靈活的 Git 作為版本控制工具,Git 也是現在更為主流的選擇。

開源工具 GitLab


用開源軟件打造企業級 DevOps 工作流(二):版本控制


GitLab 簡介

GitLab 是一個開源的版本控制系統,使用 Git 作為代碼管理工具,並在此基礎上搭建了 Web 服務,它有著精美的 Web UI 界面,方便用戶操作使用。

GitLab 是用 Ruby 編寫的開源項目,有非常自由的 MIT 版權,允許二次開發並投入商業使用。

GitLab 支持 Git 代碼倉庫、權限管理、合併請求、Issues、Wiki、CI/CD 等非常多的強大功能。

GitLab 非常類似 GitHub,支持代碼倉庫、代碼合併、代碼審核等基礎功能,區別在於:GitHub 是一款雲端產品,而且項目大多為開源項目(私有倉庫有限制);而 GitLab 是開源產品,可以非常輕鬆的部署在任意一臺服務器上。

我們用 GitLab 作為我們 DevOps 工作流的原因主要在其強大的可視化界面和權限管理,相當於加強版的 Git 倉庫。因為人是視覺動物,可視化之後可以更加高效的處理各種複雜信息,GitLab 可以幫助我們做到可視化操作。

而對於企業開發來說,通常會有管理多個項目的需求,不同的項目也會有不同的開發者參與進來,因此有效地管理這些權限會是個需要注意的問題,而 GitLab 本身就支持相關的權限管理。

安裝 GitLab

安裝 GitLab 非常簡單,我們推薦的方式是用容器化工具 Docker 來進行安裝。如果您對 Docker 不熟悉,可以去網上查找一下相關資料(我相信會非常多),或者關注本系列即將介紹的 DevOps 容器篇,在這篇文章中,我們將著重介紹 Docker。

  • Docker Hub 上的 GitLab 首頁:hub.docker.com/r/gitlab/gi…
  • Docker 安裝 GitLab 官方教程:doc.gitlab.com/omnibus/doc…

在安裝之前,確保您已經在本機或者服務器上安裝了 Docker,能夠執行基本的 Docker 操作,例如docker ps。

<code>sudo docker run --detach \\ # --detach 表示是後臺運行
--hostname gitlab.example.com \\ # GitLab 中引用的 hostname,需要設置為服務器域名
--publish 443:443 --publish 80:80 --publish 22:22 \\ # 映射端口
--name gitlab \\ # 容器名稱
--restart always \\ # 容器掛了之後會自動重啟
--volume /srv/gitlab/config:/etc/gitlab \\ # 配置持久化
--volume /srv/gitlab/logs:/var/log/gitlab \\ # 日誌持久化
--volume /srv/gitlab/data:/var/opt/gitlab \\ # 數據持久化
gitlab/gitlab-ce:latest # 鏡像名稱
複製代碼/<code>

這是官方文檔中的 Docker 啟動命令,只需要在命令行中輸入以上命令,就可以啟動 GitLab 了。

稍微等待幾十秒,在瀏覽器中輸入 http://localhost 就可以看到 GitLab 的登陸頁面了。

使用 GitLab

這裡我們不打算詳細介紹 GitLab 的所有功能,我們只會簡單介紹 DevOps 工作流中的重要部分,主要是 Git 的版本控制部分:克隆倉庫、創建分支、合併分支。剩下的功能交給讀者閱讀官方文檔或者自己安裝體驗。

克隆倉庫

在代碼項目中,複製下面 SSH 或者 HTTP 的地址,例如 ssh://localhost/user/project1 ,在本地命令行中輸入如下內容。


用開源軟件打造企業級 DevOps 工作流(二):版本控制


<code>git clone ssh://localhost/user/project1
複製代碼/<code>

然後會在當前目錄創建一個該項目的目錄,並將文件從遠端拷貝下來。這裡會創建一個origin的remote記錄。可以通過如下方式看到。

<code>git remote -v
複製代碼/<code>

如果我們通過 merge request 的方式來進行版本控制管理,我們將還會用到 git remote 的操作。

創建分支

我們可以在界面中創建分支,但我們也可以在本地創建。

在本地項目中輸入如下內容創建 develop 分支。

<code>git branch develop
git checkout develop

# or

git checkout -b develop
複製代碼/<code>

然後更改一些代碼,並通過git commit提交代碼。接下來,將代碼推送到遠端服務器。

<code>git push origin develop
複製代碼/<code>

這樣,遠端就創建了一個 develop 分支,並且將本地 develop 分支上的commits更新到服務器上了。

合併分支

當我們想通過合併的方式將 develop 的更新同步到主幹 master 分支時,我們需要合併操作。

在本地,可以這樣操作。

<code>git checkout master
git merge develop
git push origin master
複製代碼/<code>

這樣就完成了主幹的合併。

然而,有些時候特別是比較大型的項目時,我們並不希望在 master 分支上進行修改,我們想強制要求通過合併代碼的方式來更新主幹分支。因此,我們需要加一個保護操作。我們可以在項目中的 Settings -> Repository -> Protected Branch 中設置保護主幹分支,也就是說,不允許 push 到 master (讀者請自行了解如何設置,這裡不贅述)。

如果做了這樣的保護限制,我們需要通過 merge request 的方式來合併分支。在 GitLab 中的項目首頁,點擊左側菜單的 Merge Requests,點擊 New merge request,選擇 Source branch 為 develop,Target branch 為 master,點擊 Compare branches and continue,進入提交合並請求頁面。在這個頁面中,你可以看到一些合併信息,包括這次合併有哪些 commits,哪些代碼有更改、更改了什麼,等等。點擊 Submit merge request,一個 merge request 就創建好了。然後,如果你是有權限的角色的話,你可以在這個合併請求頁面中點擊 Merge 同意這次請求,master 分支也就合併好了。

GitLab 在合併分支時加入的代碼審閱功能對於 Code Review 來說非常方便。通常來說有權限同意合併請求的開發者都是 Reviewer,需要在合併之前審閱提交的代碼,並根據審閱的結果同意或拒絕合併請求。

總結

本篇文章主要介紹了版本控制系統的主要概念,包括檢入檢出、分支合併、歷史記錄,以及兩種分支開發策略(主幹開發和 Git Flow),還比較了 Git 和 SVN。另外,本文還著重介紹了開源版本控制系統 GitLab,介紹了其概念、安裝和基本使用。這些知識,對於我們後面講 DevOps 工作流是非常重要的基礎。下一篇,我們將介紹持續集成(CI),這與版本控制系統息息相關,因為諸如 Jenkins 或者 GitLab CI/CD 可以用 GitLab 代碼倉庫作為源碼來源來自動構建產品。沒有版本控制系統,CI 的優勢會受到侷限。因此,版本控制系統是 DevOps 工作流中非常重要的模塊。

在後面的文章中,我們將繼續介紹 DevOp 的其他內容,包括持續集成、容器化、編排、網絡、以及如何將這一切串聯起來協同組成企業級的 DevOps 工作流。敬請期待後面的內容。


作者:MarvinZhang
鏈接:https://juejin.im/post/5e61b3b86fb9a07ce152d131


分享到:


相關文章: