电竞比分网-中国电竞赛事及体育赛事平台

分享

Docker編排工具調研:Rancher – 陳飛 – 碼農

 浸心閣 2016-03-03

Rancher提供了一套完整的Docker編排解決方案(重點是開源的)。功能上包括網(wǎng)絡,存儲,負載均衡,安全,服務發(fā)現(xiàn)和資源管理等。Rancher可以管理DigitalOceanAWS、OpenStack等云主機,自動創(chuàng)建Docker運行環(huán)境,實現(xiàn)跨云管理。使用上可以通過Web界面或者命令行方式進行操作。

下面介紹一下使用上的一些體會,并且分析一下一些有意思的特性的技術實現(xiàn)。先說下整體上的體驗,Rancher在使用上非常流暢,當然是指排除“功夫王”的影響之外。學習成本也比較小,稍微有一點Docker知識的同學,都能很快上手。

運行

Rancher提供了Docker鏡像,直接通過docker run -d --restart=always -p 8080:8080 rancher/server命令運行即可。除了標準的Docker鏡像之外,Rancher還提供了手動、Vagrant、Puppet、Ansible等方式安裝。

這里要提到一點,Rancher也是采用Master-Agent的方式,但是Rancher的Agent也是以容器方式運行的,虛機環(huán)境修改很少。

Rancher容器是采用的胖容器方式,會運行Cattle,Mysql,rancher-compose-executor,go-machine-service等幾個服務,以及crontab等定時任務。其中值得一提Cattle,Cattle是Rancher實現(xiàn)的一套基于Docker的編排引擎,初步上來看跟Docker Swarm類似,是用Java語言實現(xiàn)的,初步看代碼實現(xiàn)的功能還是比較多的。

另外需要注意,由于Rancher目前還僅支持管理墻外的云服務,所以在墻內運行的話會導致創(chuàng)建虛機失敗率非常高。另外Rancher運行至少需要1G的內存,這也導致小編在DO上創(chuàng)建512MB的Droplet運行Rancher服務失敗,辛辛苦苦排查了半天,如果你遇到下面類似的異常日志,那說明你得換一個大一點的Droplet:

2015-10-12 10:17:58,910 INFO    [main] [ConsoleStatus] [99/102] [43348ms] [8ms] Starting register
runtime/cgo: pthread_create failed: Resource temporarily unavailable
SIGABRT: abort
PC=0x6f8177 m=0

goroutine 0 [idle]:

goroutine 1 [running]:
runtime.systemstack_switch()
    /usr/local/go/src/runtime/asm_amd64.s:216 fp=0xc820024770 sp=0xc820024768
runtime.main()
    /usr/local/go/src/runtime/proc.go:49 +0x62 fp=0xc8200247c0 sp=0xc820024770
runtime.goexit()
    /usr/local/go/src/runtime/asm_amd64.s:1696 +0x1 fp=0xc8200247c8 sp=0xc8200247c0

goroutine 17 [syscall, locked to thread]:
runtime.goexit()
    /usr/local/go/src/runtime/asm_amd64.s:1696 +0x1

rax    0x0
rbx    0xc30488
rcx    0x6f8177
rdx    0x6
rdi    0x8c
rsi    0x8c
rbp    0x734b32
rsp    0x7ffe7ce05578
r8     0xa
r9     0x27ae880
r10    0x8
r11    0x202
r12    0x27b0bc0
r13    0x8ed580
r14    0x0
r15    0x8
rip    0x6f8177
rflags 0x202
cs     0x33
fs     0x0
gs     0x0
runtime/cgo: pthread_create failed: Resource temporarily unavailable
SIGABRT: abort
PC=0x6f8177 m=0

另外還有一個需要注意的,Rancher容器啟動之后并不能立即對外提供服務,Rancher內部服務啟動需要耗費一定的時間。從日志上看大概需要將近1分鐘的啟動時間,這個有待優(yōu)化??!所以如果你運行完容器之后,訪問界面拋出Connection Refuse Exception不要以為是啟動方式有問題,請耐心等待......

09:36:32.655 [main] INFO  ConsoleStatus - [DONE ] [50277ms] Startup Succeeded, Listening on port 8081

虛機

從Web界面上看,操作是非常簡便的,例如對于DO來說直接輸入APPKEY就可以完成Droplet的創(chuàng)建。目前Rancher還僅支持Ubuntu版本的虛機。從運行日志上看,Rancher在初始化虛機的時候做了這么幾件事情:

  • 通過定制化的Docker Machine創(chuàng)建虛機,完成Docker安裝
  • 初始化SSH證書
  • 上傳rancher.tar.gz到虛機
  • 拉取rancher/agent:v0.8.2鏡像

可以看出環(huán)境依賴非常少。Docker配置也比較標準,除了添加了TLS認證之外沒有額外的配置。主機配置也就是一些證書文件,沒有額外的配置。從這方面來看Rancher本身是比較容易支持擴展的,引入對阿里云的支持也會比較簡單。但是另外一方面,由于代碼架構上的原因,第三方開發(fā)支持云服務可能會比較糾結,比較可行的方式還是Rancher和各云服務提供商配合。這可能也會限制Rancher在國內的推廣。

另外創(chuàng)建出來的虛機僅支持根據(jù)ssh key方式登陸,不支持root密碼登陸。目前有兩種方式可以ssh登陸到虛機,一個是通過Rancher提供的Web SSH界面,一個是下載主機配置,使用里面的id_rsa文件來登陸ssh -i id_rsa root@<IP_OF_HOST>。

虛機支持按Tag屬性管理,不支持直接按組管理,對于多租戶場景可以通過Rancher提供的Management Environment功能來實現(xiàn)。

另外一個比較有意思的是Rancher提供了一個View in API功能,返回的是一個JSON格式數(shù)據(jù),其中l(wèi)inks字段包含了一些有關的接口地址,這個功能對二次功能的開發(fā)比較友好,可以通過接口API獲取API,這樣就不必學習接口調用方式、參數(shù)等等內容了。

{
"id": "1h1",
"type": "host",
"links": {
"self": "…/v1/projects/1a5/hosts/1h1",
"account": "…/v1/projects/1a5/hosts/1h1/account",
"clusters": "…/v1/projects/1a5/hosts/1h1/clusters",
"containerEvents": "…/v1/projects/1a5/hosts/1h1/containerevents",
"hostLabels": "…/v1/projects/1a5/hosts/1h1/hostlabels",
"instances": "…/v1/projects/1a5/hosts/1h1/instances",
"ipAddresses": "…/v1/projects/1a5/hosts/1h1/ipaddresses",
"loadBalancerHostMaps": "…/v1/projects/1a5/hosts/1h1/loadbalancerhostmaps",
"loadBalancers": "…/v1/projects/1a5/hosts/1h1/loadbalancers",
"physicalHost": "…/v1/projects/1a5/hosts/1h1/physicalhost",
"serviceEvents": "…/v1/projects/1a5/hosts/1h1/serviceevents",
"storagePools": "…/v1/projects/1a5/hosts/1h1/storagepools",
"stats": "…/v1/projects/1a5/hosts/1h1/stats",
"hostStats": "…/v1/projects/1a5/hosts/1h1/hoststats",
"containerStats": "…/v1/projects/1a5/hosts/1h1/containerstats",
},
"actions": {
"update": "…/v1/projects/1a5/hosts/1h1/?action=update",
"deactivate": "…/v1/projects/1a5/hosts/1h1/?action=deactivate",
},
"name": "rancher",
"state": "active",
"accountId": "1a5",
"agentState": null,
"computeTotal": 1000000,
"created": "2015-10-13T04:08:55Z",
"createdTS": 1444709335000,
"description": null,
"info": {
"osInfo": {

同時還支持基礎層面的監(jiān)控,而且是用WebSocket實現(xiàn)的,"url": "ws://104.236.151.239:8080/v1/hoststats"??创a應該是沒有用到什么第三方的監(jiān)控解決方案,比如ELK、Graphite之類的,應該Cattle自行實現(xiàn)的。猜測實現(xiàn)是在io.cattle.platform.host.stats.api。不過貌似不是特別穩(wěn)定,窗口切換后會導致監(jiān)控數(shù)據(jù)不再刷新。像下圖這種情況:

容器

容器編排本身并沒有什么特別的功能,基本是對Docker命令的封裝。其中有幾個特性值得關注,一個是Managed網(wǎng)絡,第二個是健康檢查,第三個是調度策略。下面一個個來看:

Managed網(wǎng)絡

Rancher在Docker本身提供的幾種網(wǎng)絡基礎上提供了一種叫做Managed的網(wǎng)絡特性,按照其官方說法是:

The Rancher network uses IPsec tunnelling and encryption for security

簡單理解就是在容器之間構建了一條私有網(wǎng)絡,只有容器與容器之間可以訪問。Rancher會為每個容器分配一個10.42.*.*的私有網(wǎng)絡地址,這個地址只在容器之間是可達的,對外不可見,有點類似一種純軟件上的VPC方式。

從上面的部署上看,Rancher并沒有對網(wǎng)絡有什么特殊的設置,它是如何實現(xiàn)的呢?從路由上可以看出,對于10.42網(wǎng)段的路由是通過docker0接口,docker0的特殊的配置了IP也包含了10.42網(wǎng)段。

root@rancher:~# ip route
default via 192.241.212.1 dev eth0
10.42.0.0/16 dev docker0  proto kernel  scope link  src 10.42.0.1
172.17.0.0/16 dev docker0  proto kernel  scope link  src 172.17.42.1
192.241.212.0/24 dev eth0  proto kernel  scope link  src 192.241.212.19

root@rancher:~# ip addr
4: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:21:6e:92:f5 brd ff:ff:ff:ff:ff:ff
    inet 172.17.42.1/16 scope global docker0
       valid_lft forever preferred_lft forever
    inet 10.42.0.1/16 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:21ff:fe6e:92f5/64 scope link
       valid_lft forever preferred_lft forever  

實現(xiàn)流量轉發(fā)應該是靠iptables實現(xiàn)的,規(guī)則如下:

Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination
CATTLE_PREROUTING  all  --  anywhere             anywhere
DOCKER     all  --  anywhere             anywhere             ADDRTYPE match dst-type LOCAL

Chain INPUT (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination
DOCKER     all  --  anywhere            !loopback/8           ADDRTYPE match dst-type LOCAL

Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination
CATTLE_POSTROUTING  all  --  anywhere             anywhere
MASQUERADE  all  --  172.17.0.0/16        anywhere
MASQUERADE  udp  --  172.17.0.4           172.17.0.4           udp dpt:ipsec-nat-t
MASQUERADE  udp  --  172.17.0.4           172.17.0.4           udp dpt:isakmp

Chain CATTLE_POSTROUTING (1 references)
target     prot opt source               destination
ACCEPT     all  --  10.42.0.0/16         169.254.169.250
MASQUERADE  tcp  --  10.42.0.0/16        !10.42.0.0/16         masq ports: 1024-65535
MASQUERADE  udp  --  10.42.0.0/16        !10.42.0.0/16         masq ports: 1024-65535
MASQUERADE  all  --  10.42.0.0/16        !10.42.0.0/16
MASQUERADE  tcp  --  172.17.0.0/16        anywhere             masq ports: 1024-65535
MASQUERADE  udp  --  172.17.0.0/16        anywhere             masq ports: 1024-65535

Chain CATTLE_PREROUTING (1 references)
target     prot opt source               destination
DNAT       udp  --  anywhere             anywhere             ADDRTYPE match dst-type LOCAL udp dpt:ipsec-nat-t to:10.42.241.7:4500
DNAT       udp  --  anywhere             anywhere             ADDRTYPE match dst-type LOCAL udp dpt:isakmp to:10.42.241.7:500
DNAT       tcp  --  anywhere             anywhere             ADDRTYPE match dst-type LOCAL tcp dpt:http-alt to:10.42.75.19:8080

Chain DOCKER (2 references)
target     prot opt source               destination
DNAT       udp  --  anywhere             anywhere             udp dpt:ipsec-nat-t to:172.17.0.4:4500
DNAT       udp  --  anywhere             anywhere             udp dpt:isakmp to:172.17.0.4:500

對iptables規(guī)則本身不是特別了解,所以只能轉包看看網(wǎng)絡包是如何被轉發(fā)的。構建一個兩個虛機的測試環(huán)境,創(chuàng)建容器之后,讓其中一個容器nc監(jiān)聽一個端口,然后在另外一臺虛機的容器中nc連接這個端口。在Docker自身的網(wǎng)絡模型中,這種方式是不能work的,因為在一個中隨意open一個端口,由于沒有在docker run中設置export端口,所以外界是無法訪問的。但是Rancher的這種網(wǎng)絡結構卻可以實現(xiàn)這一點。在虛機一上tcpdump -i eth0 host 45.55.29.83,在虛機一的容器中tcpdump -i eth0,這是可以發(fā)現(xiàn)實際上數(shù)據(jù)包是通過IPsec封裝傳輸?shù)侥繕巳萜鞯?,猜測實際網(wǎng)絡傳輸是UDP協(xié)議封裝的:

tcpdump -i eth0 host 45.55.29.83
12:10:44.663797 IP 192.241.212.19.1024 > 45.55.29.83.ipsec-nat-t: isakmp-nat-keep-alive
12:10:47.229600 IP 45.55.29.83.ipsec-nat-t > 192.241.212.19.ipsec-nat-t: UDP-encap: ESP(spi=0x09b0aceb,seq=0x2f), length 100
12:10:47.230028 IP 192.241.212.19.1024 > 45.55.29.83.ipsec-nat-t: UDP-encap: ESP(spi=0x0b788656,seq=0x10), length 100
12:10:47.230495 IP 45.55.29.83.ipsec-nat-t > 192.241.212.19.ipsec-nat-t: UDP-encap: ESP(spi=0x09b0aceb,seq=0x30), length 100
12:10:47.230579 IP 45.55.29.83.ipsec-nat-t > 192.241.212.19.ipsec-nat-t: UDP-encap: ESP(spi=0x09b0aceb,seq=0x31), length 100
12:10:47.230602 IP 45.55.29.83.ipsec-nat-t > 192.241.212.19.ipsec-nat-t: UDP-encap: ESP(spi=0x09b0aceb,seq=0x32), length 100
12:10:47.230665 IP 192.241.212.19.1024 > 45.55.29.83.ipsec-nat-t: UDP-encap: ESP(spi=0x0b788656,seq=0x11), length 100
12:10:47.231275 IP 192.241.212.19.1024 > 45.55.29.83.ipsec-nat-t: UDP-encap: ESP(spi=0x0b788656,seq=0x12), length 100
12:10:47.231511 IP 45.55.29.83.ipsec-nat-t > 192.241.212.19.ipsec-nat-t: UDP-encap: ESP(spi=0x09b0aceb,seq=0x33), length 100
12:10:47.511757 IP 45.55.29.83.ipsec-nat-t > 192.241.212.19.ipsec-nat-t: isakmp-nat-keep-alive
tcpdump -i eth0
16:10:47.229876 IP 10.42.116.141.36379 > 10.42.75.19.1234: Flags [S], seq 867849729, win 29200, options [mss 1460,sackOK,TS val 289931 ecr 0,nop,wscale 8], length 0
16:10:47.229941 IP 10.42.75.19.1234 > 10.42.116.141.36379: Flags [S.], seq 4135689986, ack 867849730, win 28960, options [mss 1460,sackOK,TS val 10776863 ecr 289931,nop,wscale 8], length 0
16:10:47.230535 IP 10.42.116.141.36379 > 10.42.75.19.1234: Flags [.], ack 1, win 115, options [nop,nop,TS val 289932 ecr 10776863], length 0
16:10:47.230623 IP 10.42.116.141.36379 > 10.42.75.19.1234: Flags [P.], seq 1:7, ack 1, win 115, options [nop,nop,TS val 289932 ecr 10776863], length 6
16:10:47.230638 IP 10.42.75.19.1234 > 10.42.116.141.36379: Flags [.], ack 7, win 114, options [nop,nop,TS val 10776863 ecr 289932], length 0
16:10:47.230642 IP 10.42.116.141.36379 > 10.42.75.19.1234: Flags [F.], seq 7, ack 1, win 115, options [nop,nop,TS val 289932 ecr 10776863], length 0
16:10:47.231246 IP 10.42.75.19.1234 > 10.42.116.141.36379: Flags [F.], seq 1, ack 8, win 114, options [nop,nop,TS val 10776863 ecr 289932], length 0
16:10:47.231546 IP 10.42.116.141.36379 > 10.42.75.19.1234: Flags [.], ack 2, win 115, options [nop,nop,TS val 289932 ecr 10776863], length 0

不得不說,這個特性非常贊!很多場景都迎刃而解,比如像測試環(huán)境快速構建就有這種需求。開發(fā)工程師完成編碼之后進行快速驗證,申請一個測試環(huán)境容器(假設是一個Ubuntu),第一步就是把代碼包上傳,然后改BUG后再上傳,比較土的做法就是nc。但是由于Docker的網(wǎng)絡限制,這種需求不太好支持。但是Rancher實現(xiàn)的這種虛擬網(wǎng)絡能夠很好的解決這類訴求。

健康檢查 && 調度策略

Rancher還自帶了一個Health Check功能,然而貌似并沒有看到實際作用。另外比較擔心如果管理規(guī)模比較大的容器集群時,健康檢查所帶來的主節(jié)點的負載壓力會不會較大,進而影響正常功能使用?

Rancher的調度策略也比較有意思,最開始以為也是什么CPU、Memory到了多少擴容多少個容器之類的策略。實際一看發(fā)現(xiàn)Rancher還是比較務實的,只是實現(xiàn)了類似Swarm的Affinity的調度功能,通過這個功能可以完成一些有狀態(tài)容器的編排,還是比較實用的,至少比根據(jù)CPU擴容靠譜多了。

當然Rancher還有很多有意思的特性,例如整合了docker compose(話說貌似docker compose就合到Rancher了),可以實現(xiàn)一些固定場景部署的自動化操作;還有Stack的概念,支持拓撲展示;這里就不一一介紹了,有興趣的同學可以嘗試一下。

服務發(fā)現(xiàn) && 負載均衡

提到容器編排肯定是少不了服務發(fā)現(xiàn)和負載均衡的,Rancher的服務發(fā)現(xiàn)是基于DNS實現(xiàn)的,具體實現(xiàn)是在/var/lib/cattle/bin/rancher-dns,比較有意思的是所有容器的DNS服務器都指向了169.254.169.250,然后又通過路由轉發(fā),實際是轉給了此容器對應的rancher/agent-instance:v0.4.1容器中的DNS服務器。

nameserver 169.254.169.250

169.254.169.250 dev eth0  scope link  src 10.42.84.2

至于為什么這么蹩腳的實現(xiàn),目前能想到的合理解釋就是為了實現(xiàn)只有l(wèi)ink的服務才能解析到域名。例如上圖中web容器link了memcached容器,這是在web容器中可以解析到memcached域名,而其他容器無法解析(雖然ip還是能夠ping的通)。

Rancher的負載均衡是通過HAProxy實現(xiàn)的,也比較簡單,可以在界面上直接創(chuàng)建一個負載均衡。而且負載均衡可以動態(tài)修改,容器創(chuàng)建也可以自動綁定到負載均衡器。這樣就可以實現(xiàn)雙向互動,比較方便。

附錄

Mysql表結構定義

+-----------------------------------------------+
| Tables_in_cattle                              |
+-----------------------------------------------+
| DATABASECHANGELOG                             |
| DATABASECHANGELOGLOCK                         |
| account                                       |
| agent                                         |
| agent_group                                   |
| auth_token                                    |
| certificate                                   |
| cluster_host_map                              |
| config_item                                   |
| config_item_status                            |
| container_event                               |
| credential                                    |
| credential_instance_map                       |
| data                                          |
| environment                                   |
| external_handler                              |
| external_handler_external_handler_process_map |
| external_handler_process                      |
| generic_object                                |
| global_load_balancer                          |
| healthcheck_instance                          |
| healthcheck_instance_host_map                 |
| host                                          |
| host_ip_address_map                           |
| host_label_map                                |
| host_vnet_map                                 |
| image                                         |
| image_storage_pool_map                        |
| instance                                      |
| instance_host_map                             |
| instance_label_map                            |
| instance_link                                 |
| ip_address                                    |
| ip_address_nic_map                            |
| ip_association                                |
| ip_pool                                       |
| label                                         |
| load_balancer                                 |
| load_balancer_certificate_map                 |
| load_balancer_config                          |
| load_balancer_config_listener_map             |
| load_balancer_host_map                        |
| load_balancer_listener                        |
| load_balancer_target                          |
| mount                                         |
| network                                       |
| network_service                               |
| network_service_provider                      |
| network_service_provider_instance_map         |
| nic                                           |
| offering                                      |
| physical_host                                 |
| port                                          |
| process_execution                             |
| process_instance                              |
| project_member                                |
| resource_pool                                 |
| service                                       |
| service_consume_map                           |
| service_event                                 |
| service_expose_map                            |
| setting                                       |
| snapshot                                      |
| snapshot_storage_pool_map                     |
| storage_pool                                  |
| storage_pool_host_map                         |
| subnet                                        |
| subnet_vnet_map                               |
| task                                          |
| task_instance                                 |
| user_preference                               |
| vnet                                          |
| volume                                        |
| volume_storage_pool_map                       |
| zone                                          |
+-----------------------------------------------+

    本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內容均由用戶發(fā)布,不代表本站觀點。請注意甄別內容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權內容,請點擊一鍵舉報。
    轉藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多