持續(xù)部署 Microservices 的實踐和準(zhǔn)則

本篇為 Thoughtworks 洞見博客的投稿: http://insights.thoughtworkers.org/the-practices-and-principles-of-continuous-deployment-microservices/绒极。 感謝 TW 編輯們幫我校稿和排版

當(dāng)我們討論 Microservice 架構(gòu)時,我們通常會和 Monolithic 架構(gòu)(單體架構(gòu) ) 構(gòu)進(jìn)行比較凿蒜。

Monolithic and Miroservice

在 Monolithic 架構(gòu)中宾袜,一個簡單的應(yīng)用會隨著功能的增加、時間的推移變得越來越龐大幢泼。當(dāng) Monoltithic App 變成一個龐然大物,就沒有人能夠完全理解它究竟做了什么导匣。此時無論是添加新功能噪生,還是修復(fù) Bug ,都是一個非常痛苦月培、異常耗時的過程。

Microservices 架構(gòu)漸漸被許多公司采用(AmazoneBay瞭稼、Netflix),用于解決 Monolithic 架構(gòu)帶來的問題腻惠。其思路是將應(yīng)用分解為小的环肘、可以相互組合的 Microservices。 這些 Microservices通過輕量級的機(jī)制進(jìn)行交互集灌,通常會采用基于 HTTP 協(xié)議的服務(wù)廷臼。

每個 Microservices 完成一個獨立的業(yè)務(wù)邏輯,它可以是一個 HTTP API 服務(wù)绝页,提供給其他服務(wù)或者客戶端使用荠商。也可以是一個 ETL 服務(wù),用于完成數(shù)據(jù)遷移工作续誉。每個 Microservices 除了在業(yè)務(wù)獨立外莱没,也會有自己獨立的運行環(huán)境,獨立的開發(fā)酷鸦、部署流程饰躲。

這種獨立性給服務(wù)的部署和運營帶來很大的挑戰(zhàn)。因此持續(xù)部署(Continuous Deployment)是 Microservices 場景下一個重要的技術(shù)實踐臼隔。本文將介紹持續(xù)部署 Microservices 的實踐和準(zhǔn)則:

實踐:

  1. 使用 Docker 容器化服務(wù)
  2. 采用 Docker Compose 運行測試

準(zhǔn)則:

  1. 構(gòu)建適合團(tuán)隊的持續(xù)部署流水線
  2. 版本化一切
  3. 容器化一切

使用 Docker 容器化服務(wù)

我們在構(gòu)建和發(fā)布服務(wù)的時候嘹裂,不僅要發(fā)布服務(wù)本身,還需要為其配置服務(wù)器環(huán)境摔握。使用 Docker 容器化微服務(wù)寄狼,可以讓我們不僅發(fā)布服務(wù),同時還發(fā)布其需要的運行環(huán)境。容器化之后泊愧,我們可以基于 Docker 構(gòu)建我們的持續(xù)部署流水線:

dockerize

上圖描述了一個基于 Ruby on Rails (簡稱:Rails) 服務(wù)的持續(xù)部署流水線伊磺。我們用 Dockerfile 配置 Rails 項目運行所需的環(huán)境,并將 Dockerfile 和項目同時放在 Git 代碼倉庫中進(jìn)行版本管理删咱。下面 Dockerfile 可以描述一個 Rails 項目的基礎(chǔ)環(huán)境:

FROM ruby:2.3.3
RUN apt-get update -y && \
    apt-get install -y libpq-dev nodejs git
WORKDIR /app
ADD Gemfile /app/Gemfile
ADD Gemfile.lock /app/Gemfile.lock
RUN bundle install
ADD . /app
EXPOSE 80
CMD ["bin/run"]

在持續(xù)集成服務(wù)器上會將項目代碼和 Dockerfile 同時下載(git clone)下來進(jìn)行構(gòu)建(Build Image)屑埋、單元測試(Testing)、最終發(fā)布(Publish)痰滋。此時整個構(gòu)建過程都基于 Docker 進(jìn)行摘能,構(gòu)建結(jié)果為 Docker Image,并且將最終發(fā)布到 Docker Registry敲街。

在部署階段团搞,部署機(jī)器只需要配置 Docker 環(huán)境,從 Docker Registry 上 Pull Image 進(jìn)行部署聪富。

在服務(wù)容器化之后莺丑,我們可以讓整套持續(xù)部署流水線只依賴 Docker,并不需要為環(huán)境各異的服務(wù)進(jìn)行單獨配置墩蔓。

使用 Docker Compose 運行測試

在整個持續(xù)部署流水線中梢莽,我們需要在持續(xù)集成服務(wù)器上部署服務(wù)、運行單元測試和集成測試奸披。Docker Compose 為我們提供了很好的解決方案昏名。

Docker Compose 可以將多個 Docker Image 進(jìn)行組合。在服務(wù)需要訪問數(shù)據(jù)庫時阵面,我們可以通過 Docker Compose 將服務(wù)的 Image 和 數(shù)據(jù)庫的 Image 組合在一起轻局,然后使用 Docker Compose 在持續(xù)集成服務(wù)器上進(jìn)行部署并運行測試。

docker-compose

上圖描述了 Rails 服務(wù)和 Postgres 數(shù)據(jù)庫的組裝過程样刷。我們只需在項目中額外添加一個 docker-compose.yml 來描述組裝過程:

db:
  image: postgres:9.4
  ports:
    - "5432"
service:
  build: .
  command: ./bin/run
  volumes:
    - .:/app
  ports:
    - "3000:3000"
dev:
  extends:
    file: docker-compose.yml
    service: service
  links:
    - db
  environment:
    - RAILS_ENV=development
ci:
  extends:
    file: docker-compose.yml
    service: service
  links:
    - db
  environment:
    - RAILS_ENV=test

采用 Docker Compose 運行單元測試和集成測試:

docker-compose run -rm ci bundle exec rake

構(gòu)建適合團(tuán)隊的持續(xù)部署流水線

當(dāng)我們的代碼提交到代碼倉庫后仑扑,持續(xù)部署流水線應(yīng)該能夠?qū)Ψ?wù)進(jìn)行構(gòu)建、測試置鼻、并最終部署到生產(chǎn)環(huán)境镇饮。

為了讓持續(xù)部署流水線更好的服務(wù)團(tuán)隊,我們通常會對持續(xù)部署流水線做一些調(diào)整箕母,使其更好的服務(wù)于團(tuán)隊的工作流程储藐。例如下圖所示的,一個敏捷團(tuán)隊的工作流程:

agile team workflow

通常團(tuán)隊會有業(yè)務(wù)分析師(BA)做需求分析嘶是,業(yè)務(wù)分析師將需求轉(zhuǎn)換成適合工作的用戶故事卡(Story Card)钙勃,開發(fā)人員(Dev)在拿到新的用戶故事卡時會先做分析,之后和業(yè)務(wù)分析師聂喇、技術(shù)主管(Tech Lead)討論需求和技術(shù)實現(xiàn)方案(Kick off)辖源。

開發(fā)人員在開發(fā)階段會在分支(Branch)上進(jìn)行開發(fā),采用 Pull Request 的方式提交代碼,并且邀請他人進(jìn)行代碼評審(Review)同木。在 Pull Request 被評審?fù)ㄟ^之后浮梢,分支會被合并到 Master 分支跛十,此時代碼會被自動部署到測試環(huán)境(Test)彤路。

在 Microservices 場景下,本地很難搭建一整套集成環(huán)境芥映,通常測試環(huán)境具有完整的集成環(huán)境洲尊,在部署到測試環(huán)境之后,測試人員(QA)會在測試環(huán)境上進(jìn)行測試奈偏。

測試完成后坞嘀,測試人員會跟業(yè)務(wù)分析師、技術(shù)主管進(jìn)行驗收測試(User Acceptance Test)惊来,確認(rèn)需求的實現(xiàn)和技術(shù)實現(xiàn)方案丽涩,進(jìn)行驗收。驗收后的用戶故事卡會被部署到生產(chǎn)環(huán)境(Production)裁蚁。

在上述團(tuán)隊工作的流程下矢渊,如果持續(xù)部署流水線僅對 Master 分支進(jìn)行打包、測試枉证、發(fā)布矮男。在開發(fā)階段 (即:代碼還在分支) 時,無法從持續(xù)集成上得到反饋室谚,直到代碼被合并到 Master 并運行構(gòu)建后才能得到反饋毡鉴,通常會造成“本地測試成功,但是持續(xù)集成失敗”的場景秒赤。

因此猪瞬,團(tuán)隊對僅基于 Master 分支的持續(xù)部署流水線做一些改進(jìn)。使其可以支持對 Pull Request 代碼的構(gòu)建:

team workflow

如上圖所示:

  • 持續(xù)部署流水線區(qū)分 Pull Request 和 Master入篮。 Pull Request 上只運行單元測試陈瘦, Master 運行完成全部構(gòu)建并自動將代碼部署到測試環(huán)境。
  • 為生產(chǎn)環(huán)境部署引入手動操作崎弃,在驗收測試完成之后再手動觸發(fā)生產(chǎn)環(huán)境部署甘晤。

經(jīng)過調(diào)整后的持續(xù)部署流水線可以使團(tuán)隊在開發(fā)階段快速從持續(xù)集成上得到反饋,并且對生產(chǎn)環(huán)境的部署有更好的控制饲做。

版本化一切

版本化一切线婚,即將服務(wù)開發(fā)、部署相關(guān)的系統(tǒng)都版本化控制盆均。我們不僅將項目代碼納入版本管理塞弊,同時將項目相關(guān)的服務(wù)、基礎(chǔ)設(shè)施都進(jìn)行版本化管理。

對于一個服務(wù)游沿,我們一般會為它單獨配置持續(xù)部署流水線饰抒,為它配置獨立的用于運行的基礎(chǔ)設(shè)施。此時會涉及兩個非常重要的技術(shù)實踐:

  • 構(gòu)建流水線即代碼
  • 基礎(chǔ)設(shè)施即代碼

構(gòu)建流水線即代碼诀黍。通常我們使用 Jenkins 或者 Bamboo 來搭建配置持續(xù)部署流水線袋坑,每次創(chuàng)建流水線需要手動配置,這些手動操作不易重用眯勾,并且可讀性很差枣宫,每次對流水線配置的改動并不會保存在歷史記錄中,也就是說我們無從追蹤配置的改動吃环。

在今年上半年也颤,團(tuán)隊將所有的持續(xù)部署流水線從 Bamboo 遷移到了 BuildKite,BuildKite 對構(gòu)建流水線即代碼有很好的支持郁轻。下圖描述了 BuildKite 的工作方式:

build pipeline as code

在 BuildKite 場景下翅娶,我們會在每個服務(wù)代碼庫中新增一個 pipeline.yml 來描述構(gòu)建步驟。構(gòu)建服務(wù)器(CI Service)會從項目的 pipeline.yml 中讀取配置好唯,生成構(gòu)建步驟竭沫。例如,我們可以使用如下代碼描述流水線:

steps:
  -
    name: "Run my tests"
    command: "shared_ci_script/bin/test"
    agents:
      queue: test
  - wait
  -
    name: "Push docker image"
    command: "shared_ci_script/bin/docker-tag"
    branches: "master"
    agents:
      queue: test
  - wait
  -
    name: "Deploy To Test"
    command: "shared_ci_script/bin/deploy"
    branches: "master"
    env:
      DEPLOYMENT_ENV: test
    agents:
      queue: test
  - block
  - name: "Deploy to Production"
    command: "shared_ci_script/bin/deploy"
    branches: "master"
    env:
      DEPLOYMENT_ENV: prod
    agents:
      queue: production

在上述配置中渠啊, command 中的步驟 ( 即:test输吏、docker-tag、deploy ) 分別是具體的構(gòu)建腳本替蛉,這些腳本被放在一個公共的 shared_ci_script 代碼庫中贯溅,shared_ci_script 會以 git submodule 的方式被引入到每個服務(wù)代碼庫中。

經(jīng)過構(gòu)建流水線即代碼方式的改造躲查,對于持續(xù)部署流水線的任何改動都會在 Git 中被追蹤它浅,并且有很好的可讀性。

基礎(chǔ)設(shè)施即代碼镣煮。對于一個基于 HTTP 協(xié)議的 API 服務(wù)基礎(chǔ)設(shè)施可以是:

  • 用于部署的機(jī)器
  • 機(jī)器的 IP 和網(wǎng)絡(luò)配置
  • 設(shè)備硬件監(jiān)控服務(wù)(CPU姐霍,Memory 等)
  • 負(fù)載均衡(Load Balancer)
  • DNS 服務(wù)
  • AutoScaling Service (自動伸縮服務(wù))
  • Splunk 日志收集
  • NewRelic 性能監(jiān)控
  • PagerDuty 報警

這些基礎(chǔ)設(shè)施我們可以使用代碼進(jìn)行描述,AWS Cloudformation 在這方面提供了很好的支持典唇。我們可以使用 AWS Cloudformation 設(shè)計器或者遵循 AWS Cloudformation 的語法配置基礎(chǔ)設(shè)施镊折。下圖為一個服務(wù)的基礎(chǔ)設(shè)施構(gòu)件圖,圖中構(gòu)建了上面提到的大部分基礎(chǔ)設(shè)施:

infrastruture as code

在 AWS Cloudformation 中介衔,基礎(chǔ)設(shè)施描述代碼可以是 JSON 文件恨胚,也可以是 YAML 文件。我們將這些文件也放到項目的代碼庫中進(jìn)行版本化管理炎咖。

所有對基礎(chǔ)設(shè)施的操作赃泡,我們都通過修改 AWS Cloudformation 配置進(jìn)行修改寒波,并且所有修改都應(yīng)該在 Git 的版本化控制中。

由于我們采用代碼描述基礎(chǔ)設(shè)施升熊,并且大部分服務(wù)遵循相通的部署流程和基礎(chǔ)設(shè)施俄烁,基礎(chǔ)設(shè)施代碼的相似度很高。 DevOps 團(tuán)隊會為團(tuán)隊創(chuàng)建屬于自己的部署工具來簡化基礎(chǔ)設(shè)施配置和部署流程级野。

容器化一切

通常在部署服務(wù)時页屠,我們還需要一些輔助服務(wù),這些服務(wù)我們也將其容器化勺阐,并使用 Docker 運行卷中。下圖描述了一個服務(wù)在 AWS EC2 Instance 上面的運行環(huán)境:

deploy via docker

在服務(wù)部署到 AWS EC2 Instance 時矛双,我們需要為日志配置收集服務(wù)渊抽,需要為服務(wù)配置 Nginx 反向代理。

按照 12-factors 原則议忽,我們基于 fluentd懒闷,采用日志流的方式處理日志。其中 logs-router 用來分發(fā)日志栈幸、splunk-forwarder 負(fù)責(zé)將日志轉(zhuǎn)發(fā)到 Splunk 愤估。

在容器化一切之后,我們的服務(wù)啟動只需要依賴 Docker 環(huán)境速址,相關(guān)服務(wù)的依賴也可以通過 Docker 的機(jī)制運行玩焰。

總結(jié)

Microservices 給業(yè)務(wù)和技術(shù)的擴(kuò)展性帶來了極大的便利,同時在組織和技術(shù)層面帶來了極大的挑戰(zhàn)芍锚。由于在架構(gòu)的演進(jìn)過程中昔园,會有很多新服務(wù)產(chǎn)生,持續(xù)部署是技術(shù)層面的挑戰(zhàn)之一并炮,好的持續(xù)部署實踐和準(zhǔn)則可以讓團(tuán)隊從基礎(chǔ)設(shè)施抽離出來默刚,關(guān)注與產(chǎn)生業(yè)務(wù)價值的功能實現(xiàn)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末逃魄,一起剝皮案震驚了整個濱河市荤西,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌伍俘,老刑警劉巖邪锌,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異癌瘾,居然都是意外死亡觅丰,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進(jìn)店門柳弄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來舶胀,“玉大人概说,你說我怎么就攤上這事∠ィ” “怎么了糖赔?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長轩端。 經(jīng)常有香客問我放典,道長,這世上最難降的妖魔是什么基茵? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任奋构,我火速辦了婚禮,結(jié)果婚禮上拱层,老公的妹妹穿的比我還像新娘弥臼。我一直安慰自己,他們只是感情好根灯,可當(dāng)我...
    茶點故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布径缅。 她就那樣靜靜地躺著,像睡著了一般烙肺。 火紅的嫁衣襯著肌膚如雪纳猪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天桃笙,我揣著相機(jī)與錄音氏堤,去河邊找鬼。 笑死搏明,一個胖子當(dāng)著我的面吹牛鼠锈,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播熏瞄,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼脚祟,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了强饮?” 一聲冷哼從身側(cè)響起由桌,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎邮丰,沒想到半個月后行您,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡剪廉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年娃循,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片斗蒋。...
    茶點故事閱讀 38,599評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡捌斧,死狀恐怖笛质,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情捞蚂,我是刑警寧澤妇押,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站姓迅,受9級特大地震影響敲霍,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜丁存,卻給世界環(huán)境...
    茶點故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一肩杈、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧解寝,春花似錦扩然、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至嘉抓,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間晕窑,已是汗流浹背抑片。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留杨赤,地道東北人敞斋。 一個月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像疾牲,于是被迫代替她去往敵國和親植捎。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,465評論 2 348

推薦閱讀更多精彩內(nèi)容