我們談論關于架構的話題所意,總離不開微服務淮逊,而微服務又會引向容器和Docker。它們都是能簡化龐大而復雜應用的構建過程扁眯。本周希云和大家分享壮莹,如何在Giant Swarm上運行Docker化的Ruby on Rails應用。
對于微服務姻檀,沒有一個像apt-get這樣的工具命满,不禁讓人問:“我怎樣安裝這個新東西?” 答案是:“你安裝不了微服務绣版〗禾ǎ” 更具體來說是,“你不可能一下子就用上它杂抽≌┗#”
“一個系統(tǒng)的架構是最難改變的”,“微服務”這個新事物也不是一顆銀彈缩麸,即使在今天也沒有人能輕易就重構一個復雜的系統(tǒng)铸磅,尤其是對于像用了Rails框架構造出來的龐大系統(tǒng)。
Docker承諾的一個特性是:“減少開發(fā)杭朱、測試和生產環(huán)境之間的差異” 阅仔,然而,在生產環(huán)境運行Docker并不簡單弧械,所以我們將研究Giant Swarm這樣的工具是如何簡化這個部署過程的八酒。
剛開始,我會把一個簡單的應用Docker化刃唐,這個應用是一個NoSQL數(shù)據(jù)庫ArangoDb的ODM羞迷。你能在Github上找到這個應用,如果你要自己嘗試這個例子画饥,你的機器上需要裝好Docker衔瓮,Ruby 2版本以上,還有一個用于部署的Giant Swarm賬號抖甘,不需要單獨安裝數(shù)據(jù)庫热鞍,將會在本地使用一個容器作為數(shù)據(jù)庫。
?各個擊破
在我們制作Docker容器之前,先看一下這個應用的本身碍现,以及我們將如何實現(xiàn)Docker化,我們手頭上的應用是一個普通的Web工程:
一個前端
通過OAuth2使用GitHub登錄
調用外部接口(GitHub)
后臺長期運行的任務
主數(shù)據(jù)庫
任務隊列
可以把這些功能都放到一個容器中米奸,但是這樣會得不償失昼接。例如,會失去應用和數(shù)據(jù)庫分離帶來的可擴展性悴晰。遵循在每個容器只運行一個進程的原則慢睡,把應用分成5個容器:
Nginx會作為前端代理服務器,在我們的例子中它會提供靜態(tài)資源铡溪,在更復雜的應用中漂辐,它可能會作為訪問控制或者為后端服務提供負載均衡。
第二層是Rails應用棕硫,它會運行在一個簡單的web服務中髓涯,這里用的是Puma。
Sidekiq也會運行在一個獨立的容器中哈扮,如果你有一個以上的隊列纬纪,需要為每一個隊列創(chuàng)建一個容器。
一個安裝了Redis作為任務隊列的容器滑肉。
一個安裝了ArangoDB作為主數(shù)據(jù)庫的容器包各。
以下這幅圖幫助我們去理解這個架構,以及組件之間的通訊:
把Sidekiq放到一個單獨的容器運行和微服務架構還相差很遠靶庙,但這已經(jīng)使這個應用有不錯的隔離性问畅,使每個服務都在各自的工作進程中。
??各個組件
我們已經(jīng)指定了各個容器的功能六荒,現(xiàn)在就要動手創(chuàng)建它們了护姆。Docker容器是基于Dockerfile構建的,這個文件描述了每一個構建步驟恬吕。前面說到我們需要五個容器签则,對應地需要五個Dockerfile。
不過幸運的是铐料,這些容器可以共享同一個鏡像渐裂。我們不需要額外的定制鏡像就可以直接用了,你能找到各種應用的鏡像钠惩,當然種類最多的還是數(shù)據(jù)庫柒凉。
??數(shù)據(jù)庫
我們將使用官方的Redis和ArangoDB鏡像,通過以下命令運行:
# 會運行Redis并把端口暴露到宿主機
$ docker run --name redis-dredis
# 會運行ArangoDB并把端口暴露到宿主機
$ docker run --name arangodb-darangodb/arangodb
這兩個命令會從官方的鏡像庫獲取到鏡像篓跛,以后臺方式(-d)啟動膝捞,并且分配了一個名稱(—name),它們都會分配到一個卷還有默認的端口愧沟。對于ArangoDB蔬咬,至少應該為生產環(huán)境配置權限認證的設置鲤遥。
??Nginx 前端代理
記住,Docker容器應該是被看作不可變的林艘,改變應該發(fā)生在構建時而不是運行時盖奈,這個要在每次更改時重新構建鏡像。對于Nginx前端狐援,需要這樣一個更改:指定一個配置文件來代理Rails應用钢坦。因為Docker鏡像每次構建時都使用一個已經(jīng)存在的鏡像,我們使用了官方的Nginx鏡像作為基礎鏡像:
FROM nginx
RUN rm -rf /usr/share/nginx/html
COPY public /usr/share/nginx/html
COPY config/deploy/nginx.conf /etc/nginx/conf.d/default.conf
Dockerfile的開頭總是FROM語句啥酱,它告訴Docker要繼承于哪個基礎鏡像爹凹。另外我們只需要COPY這個public目錄和配置到鏡像中,正如我前邊所說镶殷,容器應該被看作不可變的禾酱,每當需要改變這些資源時,應該創(chuàng)建一個新的鏡像批钠。Nginx的配置如下:
server { ? ?listen80; ? ?server_name ?localhost; ? ?location / { ? ? ? ?root ? ? ?/usr/share/nginx/html; ? ? ? ?index ? ? index.html index.htm; ? ? ? ?try_files$uri/index.html$uri.html$uri@upstream; ? ?} ? ?location @upstream { ? ? ? ?proxy_pass http://rails-app:8080; ? ?}}
還有一件事應該提一下:這個rails-app主機名從哪里來的呢宇植?Docker將提供兩種方法去連接容器(我們會解釋這點),一串環(huán)境變量和/etc/hosts文件。在這個例子中埋心,我們使用了/etc/hosts指郁。
? Rails應用和Sidekiq Worker
現(xiàn)在添加Nginx代理的后端服務:Rails應用。官方有個Rails Dockerfile拷呆,但不會用它闲坎,因為它會安裝一些我們不需要的組件,更糟的是它安裝bundle的時候沒用—deployment參數(shù)茬斧。盡管如此腰懂,還是用它作為指引:
FROM ruby:2.1.5
# 如果Gemfile被修改過則拋出錯誤
RUN bundle config --global frozen1
RUN mkdir -p /usr/src/appWORKDIR /usr/src/appCOPY Gemfile /usr/src/app/COPY Gemfile.lock /usr/src/app/RUN bundle install --deploymentCOPY . /usr/src/app/ENV RAILS_ENV productionEXPOSE8080CMD ["/usr/src/app/bin/rails","server","-p","8080"]
不用Docker我們可以部署類似Capistrano之類的應用,而現(xiàn)在项秉,需要在遠程服務器上操作的步驟绣溜,我們可以在構建Docker鏡像時就完成了。諸如安裝gem包和復制代碼到服務器娄蔼,通過這樣怖喻,有了一個在任何地方任何時間都能啟動的容器,而且它的狀態(tài)和我們最初構建它的時候一模一樣岁诉。
Sidekiq Worker的Dockerfile基本和上邊的一樣锚沸,比直接復制這個Dockerfile更好的方式是,定義一個公用的基礎鏡像涕癣,用于構建Rails應用和Sidekiq Worker哗蜈。
??構建容器
Docker期待的是一個Dockerfile而例子中已經(jīng)有三個了,把每個Dockerfile加上了一個有意義的后綴,但是使用Docker命令時會重命名這些文件距潘。如果有一個工具可以用來實現(xiàn)這個炼列,那就是Rake:
namespace :dockerdotask :build => ['docker:build:web','docker:build:app','docker:build:worker','assets:clobber'] ?namespace :builddotask :web => ['assets:precompile','assets:clean']dosh'ln -snf Dockerfile.web Dockerfile'sh'sudo docker build -t "registry.giantswarm.io/yoshida/gh-recommender-web" .'sh'rm -f Dockerfile'end ? ?task :app => ['assets:precompile','assets:clean']dosh'ln -snf Dockerfile.app Dockerfile'sh'sudo docker build -t "registry.giantswarm.io/yoshida/gh-recommender-app" .'sh'rm -f Dockerfile'end ? ?task :workerdosh'ln -snf Dockerfile.worker Dockerfile'sh'sudo docker build -t "registry.giantswarm.io/yoshida/gh-recommender-worker" .'sh'rm -f Dockerfile'end ?endend
web和app的構建都需要使用RAILS_ENV=production,因為要這些文件都是給生產環(huán)境而不是開發(fā)環(huán)境生成的音比。-t參數(shù)會指定目標鏡像的倉庫名稱唯鸭,這對下一步把鏡像推到云上是必須的。
??轉移到云上
目前為止我們已經(jīng)有了一個完整的本地環(huán)境硅确,這很好,但是如果想真正要的是對外的環(huán)境明肮,至少還要幾步菱农。
任何人都可以配置服務器來運行基于Docker的應用。但是這樣的話就要面對各種挑戰(zhàn):把容器鏈接在一起柿估,擴展容器循未,管理跨節(jié)點的容器,還有更多秫舌。幸運的是的妖,你可以直接使用Giant Swarm,這些它都幫你考慮了足陨。首先你需要獲取一個邀請碼嫂粟,你注冊之后就可以使用swarm命令行工具去配置你本地的機器了。第一件要做的事是創(chuàng)建一個swarm.json:
{ ?"name":"github_recommender", ?"components":{ ? ?"arangodb":{ ? ? ?"image":"arangodb/arangodb", ? ? ?"ports":["8529/tcp"], ? ? ?"volumes":[ ? ? ? ?{ ? ? ? ? ?"path":"/data", ? ? ? ? ?"size":"5 GB"} ? ? ?]}, ? ?"nginx":{ ? ? ?"image":"registry.giantswarm.io/yoshida/gh-recommender-web", ? ? ?"ports":["80/tcp"], ? ? ?"domains":{ ? ? ? ?"80/tcp":["gh-recommender.gigantic.io"]}, ? ? ?"links":[ ? ? ? ?{ ? ? ? ? ?"component":"rails-app", ? ? ? ? ?"target_port":"8080/tcp"} ? ? ?]}, ? ?"rails-app":{ ? ? ?"image":"registry.giantswarm.io/yoshida/gh-recommender-app", ? ? ?"ports":["8080/tcp"], ? ? ?"env":["RAILS_ENV=production","SECRET_KEY_BASE=$secret_key_base","REDIS_URL=redis://redis:6379","GITHUB_KEY=$github_key","GITHUB_SECRET=$github_secret"], ? ? ?"links":[ ? ? ? ?{ ? ? ? ? ?"component":"arangodb", ? ? ? ? ?"target_port":"8529/tcp"}, ? ? ? ?{ ? ? ? ? ?"component":"redis", ? ? ? ? ?"target_port":"6379/tcp"} ? ? ?]}, ? ?"redis":{ ? ? ?"image":"redis", ? ? ?"ports":["6379/tcp"]}, ? ?"sidekiq-worker":{ ? ? ?"image":"registry.giantswarm.io/yoshida/gh-recommender-worker", ? ? ?"env":["RAILS_ENV=production","SECRET_KEY_BASE=$secret_key_base","REDIS_URL=redis://redis:6379"], ? ? ?"links":[ ? ? ? ?{ ? ? ? ? ?"component":"arangodb", ? ? ? ? ?"target_port":"8529/tcp"}, ? ? ? ?{ ? ? ? ? ?"component":"redis", ? ? ? ? ?"target_port":"6379/tcp"} ? ? ?]}}}
這里定義了整個應用和組件之間的關聯(lián)關系墨缘,回想一下Nginx的配置星虹,我們使用了http://rails-app:8080作為后端地址,這就是我們定義的地方镊讼。rails-app組件會被鏈接到Nginx組件宽涌,同樣,REDIS_URL也被關聯(lián)到了redis組件蝶棋。
如果不想在swarm.json中放置敏感信息(例如Github OAuth2的token)卸亮,可以單獨在一個swarmvars.json文件中定義這些變量:
{"GIANT_SWARM_USER/dev": {"github_key":"GITHUB_KEY","github_secret":"GITHUB_SECRET","secret_key_base":"SECRET_KEY_BASE"}}
可以使用例如$github_key關聯(lián)這些變量到swarm.json,當應用在Giant Swarm上運行時玩裙,各個容器會使用適當?shù)摹猯ink和—env選項兼贸。為了使所有服務都能從外部訪問,我們需要指定域名到至少一個組件献酗,Nginx是我們的入口寝受,所以把域名指定到它上。
在啟動應用之前罕偎,首先需要上傳鏡像到Giant Swarm的鏡像庫上(當然你也可以推到Docker Hub上很澄,但可能你不想你的鏡像能被公開訪問):
$ docker push registry.giantswarm.io/yoshida/gh-recommender-web
$ docker push registry.giantswarm.io/yoshida/gh-recommender-app
$ docker push registry.giantswarm.io/yoshida/gh-recommender-worker
網(wǎng)絡狀況會直接影響這個上傳過程,一旦上傳完成,就可以用這個命令啟動所有容器:
$ swarm up
這個命令會從倉庫中獲取所有需要用到的鏡像甩苛,然后以適合的參數(shù)啟動各個容器蹂楣,收集所有容器的日志,并且在http://gh-recommender.gigantic.io下部署好了應用讯蒲。整個過程異常簡潔痊土。
如果已經(jīng)到了這步,恭喜你墨林!
??擴容
現(xiàn)在我們?yōu)槊總€組件都使用了一個容器赁酝,當你的應用吸引了更多的用戶,或者突然發(fā)生了不可預見的事件需要更加多的資源旭等。傳統(tǒng)的做法是添加更多的服務器酌呆,需要一系列的人工操作:啟動機器,搭建好環(huán)境并且添加節(jié)點到負載均衡中搔耕。使用Giant Swarm的話隙袁,添加一個實例非常簡單:
$ swarm scaleup github_recommender/gh-recommender/rails-app
這樣減輕了很多技術負擔,但是它并不能使你的應用魔法般地就支持水平擴展弃榨,當它在數(shù)據(jù)庫應用上就更加復雜了菩收,你還需要研究怎樣使應用支持擴展。但是這樣至少你可以專注于這塊鲸睛,而不需要擔心基礎設施的細節(jié)了娜饵。
??結論
文中談及的就是這些,在這主題下官辈,還有更多的東西可以討論和學習划咐。分享的目的是希望起碼可以帶大家入門,如果想走得更遠钧萍,這里有幾點建議主題褐缠,是文中沒有提及但密切關聯(lián)的:
容器能在本地開發(fā)環(huán)境使用,但本文并沒有涉及如何實現(xiàn)风瘦。
無論在本地或者生產環(huán)境下队魏,調試容器都是一個比較大的問題。正如它的其它方面万搔,這個也沒有銀彈胡桨,可能也永遠不會有。這也是需要注意的地方瞬雹。
在Docker世界中昧谊,安全也是一個大問題,使用Giant Swarm會有所幫助酗捌,我們需要熟悉容器和Docker可能帶來的安全性問題呢诬。這里說的不是安全漏洞涌哲,而是與傳統(tǒng)部署方式之間的不同,例如像安裝或管理虛擬機那樣尚镰。
此外阀圾,希云強烈建議大家自己打包鏡像,不要依賴于公共鏡像庫狗唉。否則最終你會需要很多不同的鏡像初烘。例如,例子中的五個容器就需要三個不同的Linux分發(fā)版分俯。
嘗試在每個容器中只啟動一個進程肾筐,雖然這是Docker官方的建議,但無疑這是有爭議的缸剪,是否必須要這樣做局齿,應該具體問題具體分析,根據(jù)實際情況做出決定橄登。
如了解更多Docker相關知識,請觀看培訓視頻:https://csphere.cn/training讥此!
如需要Docker相關產品拢锹,請訪問希云官網(wǎng)首頁:https://csphere.cn!
cSphere1.0版本已發(fā)布萄喳,正式商用卒稳!歡迎咨詢 400-686-1560