部署的模式(三):使用docker部署

前面的兩部分介紹基礎(chǔ)設(shè)施即代碼以及部署的方式。和虛擬機相比,Docker作為操作系統(tǒng)級別的虛擬化技術(shù),特別適用微服務的部署場景。

http://realestate-com-au.github.io/intro-to-docker/#20

那么我們?nèi)绾问褂肈ocker去部署微服務呢,首先應該考慮解決下面的問題:

  1. 對于web app,該使用什么樣好的軟件構(gòu)建原則
  2. 采用何種0 downtime的部署模式
  3. 如何和現(xiàn)有的監(jiān)控、日志解決方案集成
  4. 如何構(gòu)建安全且高可用的private docker registry

使用Docker部署前的狀況


在使用Docker部署前,我們已基本實現(xiàn)新的應用微服務化,架構(gòu)上前后端分離,API通過Cloudformation去做部署,所有新的服務都部署在AWS上。一個API的持續(xù)交付的流程如下:

workflow
  1. 代碼提交,通過PR后進入持續(xù)交付流水線
  2. 通過測試后生成RPM包
  3. 通過Packer,在一個基礎(chǔ)鏡像的啟動新的服務器上安裝RPM包,并生成新的AMI
  4. 在CI上進行自動化部署,配置文件persist到private的git repo中,避免了維護中心化服務器的成本,deploy腳本從CI中獲取新生成的AMI ID,更新cloudformation template中l(wèi)aunchConfiguration
  5. cloudformation觸發(fā)自動化的不可變部署,完成更新。

這個流程中不好的地方在于:

  1. CI中打包兩次,并且生成AMI時間比較長
  2. AMI會保存到S3中,有成本

整體來看,使用AMI去部署稍微有點重,而且一旦要從AWS退出,移植會有一點點成本。使用容器來做部署看上去可以解決這些問題,2年前Docker已經(jīng)逐漸成熟,所以"領(lǐng)導"做了決定,咱們來用Docker替換AMI部署的方式。這樣縮短了持續(xù)交付時間,同時基本避免了在不同環(huán)境的差異,可移植性大大增加,更加靈活。

12 factors app


12factors app是我們在考慮使用Docker在生產(chǎn)環(huán)境部署的時候參考的原則。12factors是在Heroku上部署軟件時總結(jié)出來的12條好的實踐,其基本的原則是:

  1. 使用標準化流程自動配置。
  2. 系統(tǒng)/平臺最大的可移植性。
  3. 適合部署在現(xiàn)代的云計算平臺,從而在服務器和系統(tǒng)管理方面節(jié)省資源。
  4. 降低開發(fā)環(huán)境和生產(chǎn)環(huán)境的差異降至最低,并使用持續(xù)交付實施敏捷開發(fā)。
  5. 可以在工具、架構(gòu)和開發(fā)流程不發(fā)生明顯變化的前提下實現(xiàn)擴展。

具體的12條原則如下:

  1. 基準代碼:一份代碼,多份部署。意思就是對于每個應用都有單獨的版本管理的代碼庫,同時只針對主干開發(fā),對于不同的環(huán)境test/staging/production,只針對這一份代碼部署。在開發(fā)新feature時,最好不要采用feature branch的方式,因為merge的成本和測試的成本比較高,通過feature toggle去控制即可。
  2. 依賴:顯式聲明依賴。比如Ruby項目中的的Gemfile,Java項目中的Gradle配置中會聲明第三方的依賴。還有類似RPM的spec文件,會顯式的聲明對第三方類庫的依賴,比如ImageMagic。這樣的文件可以放在應用代碼庫中,這樣開發(fā)人員對于應用在生產(chǎn)環(huán)境中運行所需要的依賴就會比較清晰。
  3. 配置:在環(huán)境變量中存儲配置。將代碼和配置分離,我們是把每個環(huán)境的配置都放在不同的private git repo,當然你也可以用中心化的配置服務器,就是管理維護的成本比較高。
  4. 后端服務: 把后端服務(backing services)當作附加資源。比如郵件服務,依賴的靜態(tài)文件,或者數(shù)據(jù)庫。這樣做的好處是和依賴的服務松耦合,其它服務的部署、scale都不會影響到應用本身,其它服務的失敗,對于應用只有有限的影響,而且對于那些服務的修復,應用本身不需要再做額外的工作,如部署等。
  5. 構(gòu)建,發(fā)布,運行: 嚴格分離構(gòu)建和運行。直接修改處于運行狀態(tài)的代碼是非常不可取的做法,因為這些修改很難再同步回構(gòu)建步驟。所以打包和配置分開,只有在運行時才把這兩個結(jié)合起來。
  6. 進程: 以一個或多個無狀態(tài)進程運行應用。12factors的應用的進程必須是無狀態(tài)并且是不共享架構(gòu)(Shared Nothing Architecture)。需要持久化的數(shù)據(jù)保存在數(shù)據(jù)庫或者文件服務器(如S3)等。雖然很多服務器和LB都提供了sticky session的功能,但是最好不要使用,如果應用宕機,那么指向這臺應用服務器的session都會失敗,給用戶不好的使用體驗。
  7. 端口綁定:通過端口綁定提供服務。一些應用,比如Java應用,會使用Tomcat等容器,這樣會有額外的依賴,12factors應用應該自我加載,比如用內(nèi)嵌jetty/netty的方式啟動Java/Scala應用,它們對外以后端服務的形式暴露出去,比如Rails啟動的URL是localhost:5000,最前面的LB可以建立一個簡單的TCP Port Mapping 80:5000, 443:5000即可。
  8. 并發(fā): 通過進程模型進行擴展。Apache和Nginx都有類似的工作的模式,以進程為單位,將工作分配給不同進程類型,比如web進程處理http請求,worker中運行業(yè)務代碼處理具體的邏輯。除此之外,服務啟動時應該將自己交給系統(tǒng)的自啟動管理工具,System V Init、Upstart或者Systemd等,這樣不需要你自己去寫守護進程或者保存PID等,用統(tǒng)一的方式去處理啟動,重啟或者關(guān)閉進程。
  9. 易處理:快速啟動和優(yōu)雅終止可最大化健壯性。12 factors應用的進程必須是易處理(disposable)的,意思是說它們可以瞬間開啟或停止。并且在接受到停止信號(SIGTERM)時,不再接受新的請求,并且等當前的請求處理完成再停止服務。
  10. 開發(fā)環(huán)境與線上環(huán)境等價: 盡可能的保持開發(fā),預發(fā)布,線上環(huán)境相同。"It works on my machine"是程序員中間流行的一個笑話,如果可以通過類似Docker這樣的容器技術(shù)進行打包的話,可以盡量的避免這個問題。如此,你可以讓錯誤更早的呈現(xiàn),減少反饋的時間,讓上線更為順利。
  11. 日志: 把日志當作事件流。日志對于追蹤應用運行的狀況以及一些業(yè)務分析非常重要。有些時候日志會被存在服務器硬盤上,但是對應的處理這些日志,你需要添加額外的配置,比如logrotate之類,同時你對服務器的磁盤也產(chǎn)生了依賴。12 factors提倡將日志當做事件流,應用的輸入直接寫入stdout和stderror,然后將它們發(fā)送到諸如ElasticSearch以及Splunk之類的日志服務器中。還有一個好處是,開發(fā)人員可以直接從Splunk查找日志,而不需要運維去將日志從服務器拷貝下來,也不需要運維人員給開發(fā)人員授權(quán)訪問生產(chǎn)環(huán)境的服務器的ssh訪問權(quán)限。
  12. 管理進程: 后臺管理任務當作一次性進程運行。典型的例子就是schema migration或者data migration。這樣的任務應該和生產(chǎn)環(huán)境同一個環(huán)境下運行,并且必須和生產(chǎn)代碼保持一致。

如果回頭看下我們使用AMI時候的狀態(tài),很多已經(jīng)是符合12 factors原則了,但是我們需要在日志處理等方面使用Docker繼續(xù)改進。

使用Docker的一些考量


我們原本在生產(chǎn)環(huán)境下運行的服務,需要集成newrelic對服務器和應用做監(jiān)控(NewrelicSysmon和Newrelic Agent),同時日志需要發(fā)送到Splunk indexer集群?;谝延械幕A(chǔ)以及12 factors原則,應用以容器的方式在服務器上運行的形式如下:


[Two years of Docker at realestate.com.au](https://speakerdeck.com/mdub/two-years-of-docker-at-realestate-dot-com-dot-au)
  1. launchConfiguration中只需要一個存在Docker運行環(huán)境的基礎(chǔ)AMI
  2. Instance啟動后,在user-data順序啟動日志收集的容器(基于fluentd),運行Nginx容器以及app的容器
  3. 其中幾個容器必須都運行在同一網(wǎng)絡(luò)下,確保能連接到日志收集容器,并在輸出日志時tag自己的身份
  4. fluentd將接受到的日志,以及host instance的syslog一起通過splunk plugin發(fā)往splunk的fowarder,再由其轉(zhuǎn)發(fā)給splunk indexer。

如此則應用/服務本身對于服務器除了計算資源外,沒有任何的依賴,同時部署的方式也沿用基于cloudformation的immutable deployment,還有獲得了高可移植性以及多個環(huán)境的最大一致性。

高可用且安全的private docker registry


要使用Docker部署,就得保證私有的docker registry的可用性,以及安全。使用用戶名密碼的方式做驗證是不太合適的,因為這樣管理和維護的成本很高。docker registry v2支持 Bearer Token(JWT Token)的方式去做驗證。
如果你曾經(jīng)往dockerhub push過鏡像,那么當你在命令行使用docker login的時候,它做的事情是在~/.docker/config.json里面生成下面的格式的配置:

{
        "auths": {
                "https://index.docker.io/v1/": {
                        "auth": "Base64_encoded"
                }
        }
}                

這里auth中包含的字符串如果你用base64解碼后,可以驚奇的發(fā)現(xiàn)那里就是自己的用戶名和密碼:)。本地docker去訪問docker hub時,這個auth就是Authorization: - header中的部分,然后docker hub的registry再對這個進行驗證和授權(quán),確認你可以進行的操作。
對于高逼格企業(yè)內(nèi)部的私有docker registry,是不會用這樣的方式進行驗證的。一個合理的方式是使用JWT token。 JWT是允許按照聲明的格式去發(fā)布一個加密/簽名過的token,當用戶通過身份驗證后,它會生成下面的格式:

指明加密算法(支持對稱型以及非對稱型加密)以及token類型的Header:

{
    "typ": "JWT",
    "alg": "ES256",
}

包含用戶名(sub),以及token過期時間exp,權(quán)限act等的Payload:

{
  "exp": 1484102411,
  "nbf": 1484012411,
  "iat": 1484012411,
  "sub": "itsasecret",
  "aud": [
    "my.awesome.internal.docker.registry"
  ],
  "act": [
    "read"
  ]
}

Signature部分,根據(jù)算法的不同,如果是對稱型加密算法,那么簽名的部分知識對Header、PayLoad的base64編碼后的簽名,而使用RSA算法,則是使用私鑰去加密Header/PayLoad部分的內(nèi)容后用base64編碼的結(jié)果。然后這幾部分用.連接起來,構(gòu)成了整個的token,如下:

token: eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IlBZWU86VEVXVTpWN0pIOjI2SlY6QVFUWjpMSkMzOlNYVko6WEdIQTozNEYyOjJMQVE6WlJNSzpaN1E2In0.eyJpc3MiOiJhdXRoLmRvY2tlci5jb20iLCJzdWIiOiJqbGhhd24iLCJhdWQiOiJyZWdpc3RyeS5kb2NrZXIuY29tIiwiZXhwIjoxNDE1Mzg3MzE1LCJuYmYiOjE0MTUzODcwMTUsImlhdCI6MTQxNTM4NzAxNSwianRpIjoidFlKQ08xYzZjbnl5N2tBbjBjN3JLUGdiVjFIMWJGd3MiLCJhY2Nlc3MiOlt7InR5cGUiOiJyZXBvc2l0b3J5IiwibmFtZSI6InNhbWFsYmEvbXktYXBwIiwiYWN0aW9ucyI6WyJwdXNoIl19XX0.QhflHPfbd6eVF4lM9bwYpFZIV0PfikbyXuLx959ykRTBpe3CYnzs6YBK8FToVb5R47920PVLrh8zuLzdCr9t3w

JWT的工作流程如下:


當你使用這個token向registry發(fā)起請求時,其header中會包含Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IkJWM0Q6MkFWWjpVQjVaOktJQVA6SU5QTDo1RU42Ok40SjQ6Nk1XTzpEUktFOkJWUUs6M0ZKTDpQT1RMIn0.eyJpc3MiOiJhdXRoLmRvY2tlci5jb20iLCJzdWIiOiJCQ0NZOk9VNlo6UUVKNTpXTjJDOjJBVkM6WTdZRDpBM0xZOjQ1VVc6NE9HRDpLQUxMOkNOSjU6NUlVTCIsImF1ZCI6InJlZ2lzdHJ5LmRvY2tlci5jb20iLCJleHAiOjE0MTUzODczMTUsIm5iZiI6MTQxNTM4NzAxNSwiaWF0IjoxNDE1Mzg3MDE1LCJqdGkiOiJ0WUpDTzFjNmNueXk3a0FuMGM3cktQZ2JWMUgxYkZ3cyIsInNjb3BlIjoiamxoYXduOnJlcG9zaXRvcnk6c2FtYWxiYS9teS1hcHA6cHVzaCxwdWxsIGpsaGF3bjpuYW1lc3BhY2U6c2FtYWxiYTpwdWxsIn0.Y3zZSwaZPqy4y9oRBVRImZyv3m_S9XDHF1tWwN7mL52C_IiA73SJkWVNsvNqpJIn5h7A2F8biv_S2ppQ1lgkbw,registry前的反向代理服務器如Nginx上,可以運行jwt驗證的模塊,用public key解密token,并驗證內(nèi)容完整性、是否過期等,完成后再將內(nèi)容傳遞給registry,registry根據(jù)請求類型以及

 "act": [
    "read"
  ]

判斷是否有足夠的授權(quán),之后在允許用戶pull image。

這是從安全的角度考慮使用JWT+RSA去驗證用戶身份以及授權(quán),好處是維護的成本比較低,同時token在一段時間內(nèi)就過期,兼顧了安全型。

另一方面,從可用性的角度考慮,我們使用了S3作為registry的storage backend。最終的private docker registry架構(gòu)如下:

registry arch
  1. 當其中一個AWS賬戶下的某個用戶/role需要訪問registry權(quán)限時,你可以按照固定的格式修改config repo,提交PR,merge后自動部署到一個config s3 bucket。
  2. 一個定時運行的Lambda函數(shù)會從這個bucket讀取配置文件,并為具體的用戶/role生成訪問的token以及授權(quán),并將token保存在另外一個token的s3 bucket中,同時授予這個用戶/role訪問這個token文件的權(quán)限。
  3. 用戶在使用時,首先用自己的用戶名密碼獲得session,之后從token的bucket中下載token文件,通過docker login覆寫~/.docker/config.json,再進行pull/push docker image的操作。
  4. 在服務器(EC2 instance)上部署時需要給instance綁定instanceProfile讓它可以從token的bucket拿到這個token
  5. 為了加快訪問速度,可以直接從保存docker image的S3 bucket中拿到docker image的tar包

總結(jié)


目前,我們在生產(chǎn)環(huán)境已經(jīng)有100個左右的微服務使用docker去做部署(基于我們定制的一個類PaaS的部署工具)。如果我們回頭再來看這樣的使用docker的運行應用的模式,會發(fā)現(xiàn)似乎應用只需要服務器有運行時環(huán)境以及計算資源,如果我的應用邏輯簡單,那么有沒有可能由這樣一種服務:

  1. 它有一些standby的具有運行代碼運行時環(huán)境的容器
  2. 可以在請求過來時,以很短的時間(ms級別)內(nèi),啟動包含/部署代碼的容器然后處理請求。

這這樣的服務就像PaaS一樣,基礎(chǔ)設(shè)施對我不可見,服務器維護成本為零,同時只要關(guān)注在業(yè)務代碼即可。這就是現(xiàn)在經(jīng)常提到的Serverless的概念,下一節(jié)我們就來介紹Serverless、其目前適用的場景以及目前唯一比較成熟的Serverless服務AWS Lambda。

References


  1. 12factors app
  2. Packer
  3. JWT
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,634評論 19 139
  • Docker — 云時代的程序分發(fā)方式 要說最近一年云計算業(yè)界有什么大事件?Google Compute Engi...
    ahohoho閱讀 15,852評論 15 147
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 47,278評論 6 342
  • 就像“打江山容易,守江山難”一樣,一段親密關(guān)系從來不是“從此,王子和公主過上了幸福的生活”那樣簡單。以下是KY筆記...
    Ms倩閱讀 523評論 0 0
  • 雷雷有話要嘚吧閱讀 699評論 0 1

友情鏈接更多精彩內(nèi)容