自從docker的出現(xiàn)拟糕,web架構(gòu)方式也出現(xiàn)了新的變化累提。這幾年一直在關(guān)注docker尘喝,但是從未實踐過。最近有點時間斋陪,決定實踐下朽褪,用以評估今后架構(gòu)轉(zhuǎn)型的可行性。
寫本文的目的是无虚,分享自己的實踐缔赠,希望能讓初學(xué)者能少走點冤枉路。
這里就不再千篇一律的講什么docker的原理的友题,既然是初學(xué)者嗤堰,那些說實話沒什么用。初學(xué)者更需要的是通過實踐度宦,窺到docker的外形踢匣。
為保持精煉告匠,文章會隱藏一些細(xì)節(jié),如果不明白离唬,請留言后专。
實踐過程中,發(fā)現(xiàn)docker這個大玩具输莺,知識點還是非常多的戚哎。
所以在實踐前做了一些知識儲備:
Docker — 從入門到實踐:https://yeasy.gitbooks.io/docker_practice/content/introduction/what.html
這是一本好書,讓我縮短了知識儲備的時間模闲,非常建議初學(xué)者還是先仔細(xì)研讀建瘫。
不過細(xì)節(jié)部分講的不是特別細(xì),很多知識還是得各種查資料尸折。Docker文檔:https://docs.docker.com/
查資料用的consul文檔:https://www.consul.io/docs/guides/index.html
如果你選擇consul啰脚,這個是必讀的,這家伙比傳說中的復(fù)雜多了实夹。consul的docker鏡像:https://hub.docker.com/r/progrium/consul/
包裝consul的鏡像橄浓,簡化了consul的部署,這也是docker的魅力亮航。《Nodejs微服務(wù)架構(gòu)》
比較務(wù)實的一本書荸实,從書里找到了一些靈感。比如Seneca可能是下一步會做的技術(shù)選型缴淋,去集中化的方式避免了服務(wù)發(fā)現(xiàn)的繁瑣准给。
架構(gòu)
下面廢話不多說,架構(gòu)大概是這樣的重抖。很簡單露氮,沒有那種高來高去的東西,新手需要的是情境無關(guān)钟沛。
對于架構(gòu)的考慮:
- 首先架構(gòu)應(yīng)該是,簡單的恨统,可迭代演進(jìn)的叁扫。這樣可操作性和可維護性會更強。
- 談架構(gòu)畜埋,無外乎就是高可用和可擴展莫绣,脫離這兩個都是耍流氓。還有就是省錢悠鞍,動不動20臺服務(wù)器对室,創(chuàng)業(yè)公司傷不起。所以,解決好了就是好架構(gòu)软驰。
- 監(jiān)控方案是后續(xù)迭代演進(jìn)的事,你必須要保證你的系統(tǒng)正常運轉(zhuǎn)心肪,才能縮短開發(fā)周期锭亏。留出更多的時間,你可以做這些重要的事硬鞍。
- 關(guān)于負(fù)載均衡器慧瘤,有很多備選方案,現(xiàn)在云服務(wù)這么發(fā)達(dá)固该,可選的方案也很多锅减,甚至有跨機房的負(fù)載均衡。比自己搭nginx+keepalived要方便的多伐坏。
- 選擇consul怔匣,用于服務(wù)發(fā)現(xiàn),解決的是服務(wù)互訪的問題桦沉。
- 沒有集群方案每瞒,這里沒有考慮使用集群方案,比如swarm之類的纯露,在實踐過程中剿骨,發(fā)現(xiàn)配置過于繁瑣,更不用說zookeeper了埠褪。
- 為什么沒有使用consul-template浓利,雖然很巧妙,但是這兩個服務(wù)集成的耦合度過高钞速。nginx模板配置繁瑣贷掖,這會極大增加運維成本。不太像是一個適用與生產(chǎn)環(huán)境的成熟方案玉工。
架構(gòu)原理
第一步羽资,所有應(yīng)用啟動之后會向consu集群注冊自己,注冊的信息包括
- 所屬數(shù)據(jù)中心 DC1
- 所屬數(shù)據(jù)中心的宿主機節(jié)點
- 所屬節(jié)點的服務(wù)遵班,服務(wù)訪問方式ip屠升,端口
如何注冊?很多方式狭郑,比較簡單的方式是腹暖,應(yīng)用在啟動的時候往consul 注冊Api發(fā)送注冊服務(wù)信息。
可以使用shell或者應(yīng)用程序來發(fā)送翰萨。比如nodejs 可以引入node-consul庫來發(fā)送注冊信息脏答。
后期consul會負(fù)責(zé)服務(wù)節(jié)點的健康檢查。
第二步,當(dāng)應(yīng)用間存在訪問時殖告,如Api網(wǎng)關(guān)訪問微服務(wù)阿蝶,web應(yīng)用訪問微服務(wù),微服務(wù)之間互訪黄绩。這里可以使用consul Api定期請求服務(wù)狀態(tài)的方式羡洁,來獲取可用的節(jié)點,后面會詳細(xì)介紹爽丹。請求到節(jié)點后還可以在應(yīng)用程序級別做一些負(fù)載均衡策略筑煮。沒有使用dns的原因的,dns使用起來也不是很方便粤蝎,配置起來也很繁瑣真仲。
基礎(chǔ)工作
安裝docker
首先是安裝docker,mac下安裝很簡單初澎,其他環(huán)境除了安裝過程不一樣秸应,后續(xù)基本一樣。
https://docs.docker.com/docker-for-mac/
安裝之后有GUI界面可以用谤狡,可以讓新手快速使用起來灸眼。
安裝后的配置
如果你還沒有注冊 docker hub.,按提示注冊即可墓懂。
登陸完后焰宣,進(jìn)入Preferences...
添加阿里云docker hub鏡像:https://45599kaw.mirror.aliyuncs.com
也可以自己注冊一個阿里云,開通容器云服務(wù)捕仔,鏡像是免費送的匕积。
打開終端,實驗下是否成功
$ docker search nginx
出現(xiàn)登陸提示:
登陸請注意榜跌,登錄名使用注冊時的用戶名闪唆,千萬別用郵箱。
安裝虛擬機
安裝VirtualBox
作為實驗性項目钓葫,使用VirutalBox可以快速構(gòu)建你想要的物理環(huán)境悄蕾,而且docker和virtualbox搭配的很好,使用docker-machine可以非常簡單的管理所有虛擬機础浮。
開始
好了帆调,萬事具備。現(xiàn)在我們開始創(chuàng)建虛擬機豆同。
使用docker工具包自帶的 docker-machine工具番刊,可以幫你快速創(chuàng)建一個docker宿主機。
在這個架構(gòu)中影锈,我們一共只需要創(chuàng)建3臺宿主機
docker-machine命令后面會用的比較頻繁芹务,所以我們改個短點的名字蝉绷。
這里我用zsh,bash類似枣抱。
$ vi ~/.zshrc
#增加
alias dm="docker-machine”
依次創(chuàng)建3臺虛擬機
$ dm create -d "virtualbox” node1
$ dm create -d "virtualbox” node2
$ dm create -d "virtualbox" node3
ip是自動分配的熔吗,不出意外的話,會得到下面對應(yīng)的ip(如果真出意外了佳晶,就改改ip吧)磁滚。
宿主機 node1: 192.169.99.100
宿主機 node2: 192.169.99.101
宿主機 node3: 192.169.99.102
$ dm ls
NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
node1 - virtualbox Running tcp://192.168.99.100:2376 v17.06.0-ce
node2 - virtualbox Running tcp://192.168.99.101:2376 v17.06.0-ce
node3 - virtualbox Running tcp://192.168.99.102:2376 v17.06.0-ce
第一臺宿主機配置
宿主機node1
我們新開一個終端
$ dm ssh node1
這個命令可以快速登入node1宿主機
$ sudo vi /etc/docker/daemon.json
{
"experimental" : true,
"registry-mirrors" : [
"https://45599kaw.mirror.aliyuncs.com"
]
}
雖然可以架設(shè)regsiter私服,但是使用起來的麻煩程度宵晚,遠(yuǎn)遠(yuǎn)超過重復(fù)下載帶來的代價。所以不用糾結(jié)了维雇,就這么整淤刃,非常簡單。
改完之后吱型,重啟docker
$ sudo /etc/init.d/docker restart
執(zhí)行命令后有提示錯誤逸贾,不用理會。
配置consul-server
啟動第一臺 consul-server津滞,非常簡單铝侵,一條命令搞定,這就是docker的魅力触徐。
$ docker run -h node1 --name consul -d -v /data:/data --restart=always\
-p 8300:8300 \
-p 8301:8301 \
-p 8301:8301/udp \
-p 8302:8302 \
-p 8302:8302/udp \
-p 8400:8400 \
-p 8500:8500 \
progrium/consul -server \
-bootstrap-expect 3 \
-advertise 192.168.99.100
下面來解釋下各個參數(shù)
-h 節(jié)點名字
--name 容器(container)名稱咪鲜,后期用來方便啟動關(guān)閉,看日志等撞鹉,這個一定要寫
-d 后臺運行
-v /data:/data 使用宿主機的/data目錄映射到容器內(nèi)部的/data,用于保存consul的注冊信息疟丙,要不docker 一重啟,數(shù)據(jù)是不保留的鸟雏。
--restart=always 這個可以活得長一點
下面幾個參數(shù)都是consul集群用的享郊,非集群模式可以不使用。
-p 8300:8300
-p 8301:8301
-p 8301:8301/udp
-p 8302:8302
-p 8302:8302/udp \
progrium/consul 鏡像名稱孝鹊,本地沒有就自動從公共docker庫下載
后面的都是consul的參數(shù):
-server \ 以服務(wù)節(jié)點啟動
-bootstrap-expect 3 \ 預(yù)期的啟動節(jié)點數(shù)3炊琉,最少是3,要不達(dá)不到cluster的效果
-advertise 192.168.99.100 告訴集群又活,我的ip是什么苔咪,就是注冊集群用的
執(zhí)行完畢后 ,使用docker ps看下皇钞,是否運行正常悼泌。docker logs就不用看了,里面各種警告和錯誤夹界,其實那都是假象馆里。
但是consul cluster你必須明白隘世,只有3個consul-server節(jié)點都啟動正常了,整個集群才能正常啟動鸠踪。
打開 http://192.168.99.100:8500/
但是你看不到下面的consul標(biāo)簽丙者,因為集群還沒有都起來。
配置下一臺consul-server
開啟一個新的終端
$ dm ssh node2 #進(jìn)入node2宿主機
增加daemon.json营密,重啟docker械媒,不再贅述。
$ docker run -h node2 --name consul -d -v /data:/data --restart=always\
-p 8300:8300 \
-p 8301:8301/udp \
-p 8302:8302 \
-p 8302:8302/udp \
-p 8400:8400 \
-p 8500:8500 \
progrium/consul -server \
-advertise 192.168.99.101 \
-join 192.168.99.100
這里多了一個參數(shù)
-join 192.168.99.100 代表的是加入node1建立好的consul-server
好评汰,已經(jīng)加入了纷捞,但是集群還是沒有完備。
用同樣的方法配置 最后一臺
新開終端進(jìn)入node3
$ docker run -h node3 --name consul -d -v /data:/data --restart=always\
-p 8300:8300 \
-p 8301:8301/udp \
-p 8302:8302 \
-p 8302:8302/udp \
-p 8400:8400 \
-p 8500:8500 \
progrium/consul -server \
-advertise 192.168.99.102 \
-join 192.168.99.100
consul配置完畢
檢查是否成功
沒出意外的話被去,就看到下面的界面
http://192.168.99.100:8500/
但是我有預(yù)感主儡,意外的可能性比較大。如果不成功惨缆,可以留言糜值,這里面細(xì)節(jié)比較多。坯墨。寂汇。
開始啟動應(yīng)用
這里拿最簡單的nginx服務(wù)作為演示,情境無關(guān)捣染。
啟動nginx
進(jìn)入3個節(jié)點
執(zhí)行
$ docker run -d -p 80:80 --name nginx nginx
在node1骄瓣,node2,node3中, 分別執(zhí)行以下命令
$ curl -X PUT -d '{"id": "nginx","name": "nginx","address": "192.168.99.100","port": 80,"checks": [{"http": "http://192.168.99.100/","interval": "5s"}]}' http://127.0.0.1:8500/v1/agent/service/register
$ curl -X PUT -d '{"id": "nginx","name": "nginx","address": "192.168.99.101","port": 80,"checks": [{"http": "http://192.168.99.101/","interval": "5s"}]}' http://127.0.0.1:8500/v1/agent/service/register
$ curl -X PUT -d '{"id": "nginx","name": "nginx","address": "192.168.99.102","port": 80,"checks": [{"http": "http://192.168.99.102/","interval": "5s"}]}' http://127.0.0.1:8500/v1/agent/service/register
好了耍攘,我們啟動了3個nginx累贤,并將它們都注冊到了consul.
健康檢查
獲取狀態(tài)
http 命令工具 httpie,可以自行安裝少漆,替代curl臼膏,可以高亮格式化返回的json
$ http http://192.168.99.100:8500/v1/health/checks/nginx
HTTP/1.1 200 OK
Content-Type: application/json
Date: Sun, 30 Jul 2017 11:41:59 GMT
Transfer-Encoding: chunked
X-Consul-Index: 20
X-Consul-Knownleader: true
X-Consul-Lastcontact: 0
[
{
"CheckID": "service:nginx",
"Name": "Service 'nginx' check",
"Node": "node1",
"Notes": "",
"Output": "HTTP GET http://192.168.99.100/: 200 OK Output: <!DOCTYPE html>\n<html>\n<head>\n<title>Welcome to nginx!</title>\n<style>\n body {\n width: 35em;\n margin: 0 auto;\n font-family: Tahoma, Verdana, Arial, sans-serif;\n }\n</style>\n</head>\n<body>\n<h1>Welcome to nginx!</h1>\n<p>If you see this page, the nginx web server is successfully installed and\nworking. Further configuration is required.</p>\n\n<p>For online documentation and support please refer to\n<a href=\"http://nginx.org/\">nginx.org</a>.<br/>\nCommercial support is available at\n<a href=\"http://nginx.com/\">nginx.com</a>.</p>\n\n<p><em>Thank you for using nginx.</em></p>\n</body>\n</html>\n",
"ServiceID": "nginx",
"ServiceName": "nginx",
"Status": "passing"
},
{
"CheckID": "service:nginx",
"Name": "Service 'nginx' check",
"Node": "node2",
"Notes": "",
"Output": "HTTP GET http://192.168.99.101/: 200 OK Output: <!DOCTYPE html>\n<html>\n<head>\n<title>Welcome to nginx!</title>\n<style>\n body {\n width: 35em;\n margin: 0 auto;\n font-family: Tahoma, Verdana, Arial, sans-serif;\n }\n</style>\n</head>\n<body>\n<h1>Welcome to nginx!</h1>\n<p>If you see this page, the nginx web server is successfully installed and\nworking. Further configuration is required.</p>\n\n<p>For online documentation and support please refer to\n<a href=\"http://nginx.org/\">nginx.org</a>.<br/>\nCommercial support is available at\n<a href=\"http://nginx.com/\">nginx.com</a>.</p>\n\n<p><em>Thank you for using nginx.</em></p>\n</body>\n</html>\n",
"ServiceID": "nginx",
"ServiceName": "nginx",
"Status": "passing"
},
{
"CheckID": "service:nginx",
"Name": "Service 'nginx' check",
"Node": "node3",
"Notes": "",
"Output": "HTTP GET http://192.168.99.102/: 200 OK Output: <!DOCTYPE html>\n<html>\n<head>\n<title>Welcome to nginx!</title>\n<style>\n body {\n width: 35em;\n margin: 0 auto;\n font-family: Tahoma, Verdana, Arial, sans-serif;\n }\n</style>\n</head>\n<body>\n<h1>Welcome to nginx!</h1>\n<p>If you see this page, the nginx web server is successfully installed and\nworking. Further configuration is required.</p>\n\n<p>For online documentation and support please refer to\n<a href=\"http://nginx.org/\">nginx.org</a>.<br/>\nCommercial support is available at\n<a href=\"http://nginx.com/\">nginx.com</a>.</p>\n\n<p><em>Thank you for using nginx.</em></p>\n</body>\n</html>\n",
"ServiceID": "nginx",
"ServiceName": "nginx",
"Status": "passing"
}
]
可以看到3個nginx的服務(wù)節(jié)點都是passing狀態(tài),這時候你可以選擇一個使用了示损。
制造一些事故
進(jìn)入node3
$ docker kill nginx
進(jìn)入node2
$ docker kill consul
再次檢查
$ http http://192.168.99.100:8500/v1/health/checks/nginx
[
{
"CheckID": "service:nginx",
"Name": "Service 'nginx' check",
"Node": "node1",
"Notes": "",
"Output": "HTTP GET http://192.168.99.100/: 200 OK Output: <!DOCTYPE html>\n<html>\n<head>\n<title>Welcome to nginx!</title>\n<style>\n body {\n width: 35em;\n margin: 0 auto;\n font-family: Tahoma, Verdana, Arial, sans-serif;\n }\n</style>\n</head>\n<body>\n<h1>Welcome to nginx!</h1>\n<p>If you see this page, the nginx web server is successfully installed and\nworking. Further configuration is required.</p>\n\n<p>For online documentation and support please refer to\n<a href=\"http://nginx.org/\">nginx.org</a>.<br/>\nCommercial support is available at\n<a href=\"http://nginx.com/\">nginx.com</a>.</p>\n\n<p><em>Thank you for using nginx.</em></p>\n</body>\n</html>\n",
"ServiceID": "nginx",
"ServiceName": "nginx",
"Status": "passing"
},
{
"CheckID": "service:nginx",
"Name": "Service 'nginx' check",
"Node": "node2",
"Notes": "",
"Output": "HTTP GET http://192.168.99.101/: 200 OK Output: <!DOCTYPE html>\n<html>\n<head>\n<title>Welcome to nginx!</title>\n<style>\n body {\n width: 35em;\n margin: 0 auto;\n font-family: Tahoma, Verdana, Arial, sans-serif;\n }\n</style>\n</head>\n<body>\n<h1>Welcome to nginx!</h1>\n<p>If you see this page, the nginx web server is successfully installed and\nworking. Further configuration is required.</p>\n\n<p>For online documentation and support please refer to\n<a href=\"http://nginx.org/\">nginx.org</a>.<br/>\nCommercial support is available at\n<a href=\"http://nginx.com/\">nginx.com</a>.</p>\n\n<p><em>Thank you for using nginx.</em></p>\n</body>\n</html>\n",
"ServiceID": "nginx",
"ServiceName": "nginx",
"Status": "passing"
},
{
"CheckID": "service:nginx",
"Name": "Service 'nginx' check",
"Node": "node3",
"Notes": "",
"Output": "Get http://192.168.99.102/: dial tcp 192.168.99.102:80: connection refused",
"ServiceID": "nginx",
"ServiceName": "nginx",
"Status": "critical"
}
]
可以看到渗磅,當(dāng)consul服務(wù)掛掉一個的時候,并不影響nginx服務(wù)的健康狀況检访。其中有一個nginx已經(jīng)處于critical狀態(tài)始鱼。這樣我們就有足夠的信息不選擇不健康的節(jié)點。
總結(jié)
好了脆贵,終于寫完了医清。總的來說卖氨,這個已經(jīng)算是極簡的架構(gòu)了会烙。當(dāng)然负懦,docker的生命周期遠(yuǎn)不止這些,比如ci柏腻,發(fā)布上線纸厉,灰度發(fā)布等。docker遠(yuǎn)沒有傳說中那么簡單五嫂,美妙颗品。docker有很多的好處,但是需要DevOPS做很多的工作沃缘。
在實踐過程中躯枢,我發(fā)現(xiàn)有一個或許是更優(yōu)的架構(gòu)。那就是seneca的方案槐臀,使用事件相應(yīng)作為微服務(wù)的提供方式闺金,這樣就避免了服務(wù)發(fā)現(xiàn)這件事,完全不需要注冊服務(wù)峰档,選擇服務(wù)這么麻煩。也不用為服務(wù)發(fā)現(xiàn)服務(wù)搭建一個集群確保其高可用寨昙。