一種在Swarm集群中實現IP保持的方法

fxmo7195 8年前發布 | 11K 次閱讀 Docker

Docker在1.12版本中開始集成Swarmkit,本文研究在Swarmkit管理的集群中實現IP保持的方法。

Swarmkit簡介

Swarmkit是Docker公司推出的Docker集群管理和容器編排工具,自Docker1.12版本,開始集成到Docker-engine里發布。Swarmkit是從Swarm項目發展而來。作為Docker自身的編排系統,可提供服務編排,集群管理和調度功能。

Swarmkit中節點分為兩類

1. 工作節點負責通過執行器運行任務。Swarmkit的默認執行器為Docker容器執行器(Docker Container Executor);

2. 管理節點負責接收和響應用戶的請求,將集群狀態調節成最終狀態。

上圖給出了Docker中Swarm集群的組織方式,集群中可包含多個manager節點,這些 manager選舉一個節點作為leader節點。由leader節點負責整個集群的管理工作。如果該leader節點出現故障,剩余manager節點會重新選舉,新的leader節點會接管集群的管理工作,原leader節點在恢復后可重新加入集群,此時身份是普通manager節點。

Swarmkit在服務編排方面可實現服務狀態一致性,為服務按指定策略進行升級及重啟等。簡單來說,在創建服務后,Swarmkit根據服務類型確定task的數目,并生成相應task,定期檢查以保證task數目穩定,并按照調度策略將這些任務分派到各個工作節點上。如果有節點不可用,Leader節點會將該節點上的任務遷移到其它可用節點上繼續運行,還可以根據用戶命令增加或縮減任務的數目。具體使用方法參見官方文檔,下圖給出了Swarmkit的內部結構示意圖。Swarmkit中通過libnetwork實現對網絡資源的管理。

問題提出

從運維角度來看,為提高運維效率,希望容器在重啟或者遷移之后,可以保持容器IP地址不變。這樣運維人員可通過具體的靜態IP地址來對容器進行維護,升級等操作。

現在的實際情況是,在Docker.10之后的版本中,創建容器時可以通過在命令行使用–ip選項來指定其IP地址。但是目前Swarmkit的實現中,在為某個服務的task分配IP時,并不支持指定IP地址,而是采用隨機分配的方式。在任務需要重啟或遷移時,并不能保證前后兩次任務對應容器所得到的IP地址相同。要提高swarm集群的運維效率,需要一種能保持任務對應容器IP地址的方法。

實現思路

通過對Docker文檔及代碼進行閱讀,發現在為每個任務創建容器時,容器名稱由三部分組成,詳見下方代碼( daemon/cluster/executor/container/container.go name() )。

return strings.Join([]string{c.task.ServiceAnnotations.Name, fmt.Sprint(c.task.Slot), c.task.ID}, “.”) 

其中服務名稱與Slot的值是確定的,任務的ID由系統自動生成,每一次重啟或遷移都會變化。可以通過容器名稱的前兩部分唯一標識容器所執行的任務,這里將其稱為任務標識 (Swarmkit具體實現中對任務的標識與此類似,不過用到的是ServiceID)。

解決方案的主要思路就是將容器的IP地址與任務標識關聯起來,在系統中保存任務標識與Swarmkit為該任務分配IP地址之間的映射關系。在任務重啟或遷移時,為新生成的容器分配之前保存在映射關系中的IP地址,這樣就可以保持一個任務對應的IP地址不變。

另一個需要考慮的問題是在leader節點不可用后,如何在新的leader節點上恢復之前所保存的映射關系。一種方案是將對應關系寫入圖1中的狀態存儲,另一種方案是在leader初始化時從task列表中重建對應關系。這里選擇的是后一種方案,因為第一種方案還要學習protobuffer的使用及Swarmkit的分布式存儲實現代碼,實現起來更復雜。

IP保持的具體實現

這里主要講一下為實現IP保持所要修改代碼的位置及功能。

  1. 注釋libnetwork中檢查指定IP地址合法性語句
    在libnetwork代碼中注釋掉為指定IP的容器進行IP地址合法性檢查部分的語句,該語句檢查為容器分配的指定IP地址是否可用。原因是容器在停止后,會對其IP地址進行回收,但這種回收并不是實時的。如果新創建的容器在創建時,該IP地址還沒有被回收,容器創建會由于資源沖突而失敗。實際上擁有該IP地址的老容器已經停止,所以這樣做并不會影響服務的正確性。該修改位于getAddress 函數中。
  2. 增加保存映射關系的數據結構
    映射關系的保持,在networkAllocator 中,將部分網絡信息保存在一個名字為network 的局部結構中。為保存映射關系,在該結構中增加一個map,其key為前面提到的可唯一標識一個任務的任務標識,其value值為該任務對應的IP地址。
  3. 增加分配IP功能函數
    在為一個任務分配IP地址時,首先根據網絡名稱,查找到保存對應網絡信息的數據結構,并在該結構中新增的map里進行查找。如果命中,說明該任務之前已經被分配過IP地址,這次可能是該任務進行重啟或遷移,此時直接將已經分配過的IP地址再分配給該任務。如果沒有命中,說明該任務是首次執行,這時直接調用libnetwork的接口為其分配一個未用的IP地址。該功能通過修改AllocateTask 函數和增加allocateTaskIP 函數實現。
  4. 增加記錄已分配IP功能函數
    在為一個任務分配了IP地址后,還需要對新增map中的數據進行更新。即將以任務標識為key,以分配的IP地址為value值的映射保存在map中。如果后續需要對任務進行重啟或遷移,就可以通過分配函數實現IP保持。該功能通過修改AllocateTask 函數和增加recordTaskIP 函數實現。
  5. 修改回收IP功能函數
    為實現IP保持,還需要將對回收任務IP地址部分的代碼進行修改,以防止對已經分配過的IP地址進行回收。因為IP地址一旦被回收,就有可能被分配給新的任務。這樣就無法實現IP保持了。該功能對原有的releaseEndpoints 函數進行了修改。
  6. 增加映射關系初始化功能函數
    該函數負責Leader改變后映射數據的重建。節點在被選為leader后,將負責集群后續的各項管理工作。為實現IP保持,需要在leader的初始化工作中重建映射關系。該項工作由修改doNetworkInit 函數和增加IpmapsInit 函數實現,該函數遍歷保存在一致性存儲中的所有task,對每個任務,從其保存的網絡信息中讀取已分配的IP地址,將這些IP與任務標識的映射關系保存到網絡中新增的map中,這樣就實現了對映射關系的重建。

編譯安裝方法(以 Docker1.12.1版本為例)

(1)從github上克隆Docker項目,并切換到1.12.1版本,或者直接下載Docker1.12.1的源代碼。

(2) 完成代碼修改。

(3)進入項目目錄,輸入make shell,會安裝對應1.12.1版本的開發環境,并進入開發環境所在容器。

(4)輸入hack/make.sh binary會編譯生成二進制可執行文件。在bundle/latest目錄下。

(5)安裝方法,在編譯生成二進制文件后,將文件復制到系統的/usr/bin或/usr/local/bin目錄下。宿主機系統最好是剛安裝完同版本的Docker或未安裝Docker,否則可能會出現Docker啟動不成功。

效果測試

  1. 在三臺VM上安裝新編譯的Docker程序,三個節點都是manager,這里省略了創建集群和服務的過程
root@vm-1476374349871:/home/ubuntu# docker service ls

ID            NAME        REPLICAS  IMAGE   COMMAND

7huiwv4vgrs7  helloworld  10/10     alpine  ping www.docker.com

root@vm-1476374349871:/home/ubuntu# docker node ls

ID                           HOSTNAME                                        STATUS  AVAILABILITY  MANAGER STATUS

bn68rlizdmnchk14fd1ft9izx    vm-1473648642890.vm-14736486428901473648642890  Ready   Active        Reachable

bu5cc1nx0aj29q984kkngeftd *  vm-1476374349871                                Ready   Active        Leader

cm1k85xgzxivrazge4qqteotv    vm-1473648611529.vm-14736486115291473648611529  Ready   Active        Reachable

root@vm-1476374349871:/home/ubuntu#

2. 將副本數目增加到10,在其中的一臺VM上的task如下

[root@vm-1473648611529 ~]# docker ps

CONTAINER ID        IMAGE               COMMAND                 CREATED             STATUS              PORTS               NAMES

971b6cca77c4        alpine:latest       "ping www.docker.com"   2 minutes ago       Up 2 minutes                            helloworld.2.8hnyq37xrkkvoc1ddzoxxacv5

8773911a47fc        alpine:latest       "ping www.docker.com"   2 minutes ago       Up 2 minutes                            helloworld.3.9egflo60nvh1i96l1y110ejg7

1315a7fb46d1        alpine:latest       "ping www.docker.com"   2 minutes ago       Up 2 minutes                            helloworld.5.e25s0snjn960zu6xlmt3a8cqq

[root@vm-1473648611529 ~]#

3. 其中helloworld.3的eth0的IP為10.0.0.3

[root@vm-1473648611529 ~]# docker exec -ti 8773911a47fc sh

/ # ifconfig

eth0      Link encap:Ethernet  HWaddr 02:42:0A:00:00:03  

      inet addr:10.0.0.3  Bcast:0.0.0.0  Mask:255.255.255.0

      inet6 addr: fe80::42:aff:fe00:3%32520/64 Scope:Link

      UP BROADCAST RUNNING MULTICAST  MTU:1450  Metric:1

      RX packets:29 errors:0 dropped:0 overruns:0 frame:0

      TX packets:8 errors:0 dropped:0 overruns:0 carrier:0

      collisions:0 txqueuelen:0 

      RX bytes:2334 (2.2 KiB)  TX bytes:648 (648.0 B)



eth1      Link encap:Ethernet  HWaddr 02:42:AC:12:00:03  

      inet addr:172.18.0.3  Bcast:0.0.0.0  Mask:255.255.0.0

      inet6 addr: fe80::42:acff:fe12:3%32520/64 Scope:Link

      UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1

      RX packets:214 errors:0 dropped:0 overruns:0 frame:0

      TX packets:235 errors:0 dropped:0 overruns:0 carrier:0

      collisions:0 txqueuelen:0 

      RX bytes:19918 (19.4 KiB)  TX bytes:22174 (21.6 KiB)



lo        Link encap:Local Loopback  

      inet addr:127.0.0.1  Mask:255.0.0.0

      inet6 addr: ::1%32520/128 Scope:Host

      UP LOOPBACK RUNNING  MTU:65536  Metric:1

      RX packets:4 errors:0 dropped:0 overruns:0 frame:0

      TX packets:4 errors:0 dropped:0 overruns:0 carrier:0

      collisions:0 txqueuelen:1 

      RX bytes:334 (334.0 B)  TX bytes:334 (334.0 B)



/ # exit

[root@vm-1473648611529 ~]#

4. 將該節點設為不可用,該VM上的任務會重新分配到另外兩臺機器上

[root@vm-1473648611529 ~]# docker node ls

ID                           HOSTNAME                                        STATUS  AVAILABILITY  MANAGER STATUS

bn68rlizdmnchk14fd1ft9izx    vm-1473648642890.vm-14736486428901473648642890  Ready   Active        Reachable

bu5cc1nx0aj29q984kkngeftd    vm-1476374349871                                Ready   Active        Leader

cm1k85xgzxivrazge4qqteotv *  vm-1473648611529.vm-14736486115291473648611529  Ready   Active        Reachable

[root@vm-1473648611529 ~]# docker node update --availability drain cm1k85xgzxivrazge4qqteotv

cm1k85xgzxivrazge4qqteotv

[root@vm-1473648611529 ~]# docker node ls

ID                           HOSTNAME                                        STATUS  AVAILABILITY  MANAGER STATUS

bn68rlizdmnchk14fd1ft9izx    vm-1473648642890.vm-14736486428901473648642890  Ready   Active        Leader 

bu5cc1nx0aj29q984kkngeftd    vm-1476374349871                                Ready   Active        Reachable

cm1k85xgzxivrazge4qqteotv *  vm-1473648611529.vm-14736486115291473648611529  Ready   Drain         Reachable

[root@vm-1473648611529 ~]# docker service ls

ID            NAME        REPLICAS  IMAGE   COMMAND

7huiwv4vgrs7  helloworld  10/10     alpine  ping www.docker.com

[root@vm-1473648611529 ~]#

5. 其中helloworld.3分配到了leader節點上(helloworld.3.后面的任務ID是不一樣的)

[root@vm-1473648642890 ~]# docker ps

CONTAINER ID        IMAGE               COMMAND                 CREATED              STATUS              PORTS               NAMES

7308b277a77d        alpine:latest       "ping www.docker.com"   About a minute ago   Up About a minute                       helloworld.3.95dodap792m1l2d9of2tg6lt0

8b78496370c3        alpine:latest       "ping www.docker.com"   6 minutes ago        Up 6 minutes                            helloworld.6.auphkstyrt0yrrqpile2wjatz

243d15b67aaa        alpine:latest       "ping www.docker.com"   6 minutes ago        Up 6 minutes                            helloworld.8.e9v6zdshztpvik79zt9tooei9

4d84734f60f9        alpine:latest       "ping www.docker.com"   6 minutes ago        Up 6 minutes                            helloworld.10.4rbworz9sa4mvmby6aj2ys8m6

c1a833410e3a        alpine:latest       "ping www.docker.com"   6 minutes ago        Up 6 minutes                            helloworld.7.5y4urb3mnn9395hzzkxp1ffgs

[root@vm-1473648642890 ~]#

6. 查看一下它的eth0的IP,還是10.0.0.3

[root@vm-1473648642890 ~]# docker exec -ti 7308b277a77d sh

/ # ifconfig

eth0      Link encap:Ethernet  HWaddr 02:42:0A:00:00:03  

      inet addr:10.0.0.3  Bcast:0.0.0.0  Mask:255.255.255.0

      inet6 addr: fe80::42:aff:fe00:3%32684/64 Scope:Link

      UP BROADCAST RUNNING MULTICAST  MTU:1450  Metric:1

      RX packets:8 errors:0 dropped:0 overruns:0 frame:0

      TX packets:8 errors:0 dropped:0 overruns:0 carrier:0

      collisions:0 txqueuelen:0 

      RX bytes:648 (648.0 B)  TX bytes:648 (648.0 B)



eth1      Link encap:Ethernet  HWaddr 02:42:AC:12:00:06  

      inet addr:172.18.0.6  Bcast:0.0.0.0  Mask:255.255.0.0

      inet6 addr: fe80::42:acff:fe12:6%32684/64 Scope:Link

      UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1

      RX packets:101 errors:0 dropped:0 overruns:0 frame:0

      TX packets:125 errors:0 dropped:0 overruns:0 carrier:0

      collisions:0 txqueuelen:0 

      RX bytes:9416 (9.1 KiB)  TX bytes:11674 (11.4 KiB)



lo        Link encap:Local Loopback  

      inet addr:127.0.0.1  Mask:255.0.0.0

      inet6 addr: ::1%32684/128 Scope:Host

      UP LOOPBACK RUNNING  MTU:65536  Metric:1

      RX packets:4 errors:0 dropped:0 overruns:0 frame:0

      TX packets:4 errors:0 dropped:0 overruns:0 carrier:0

      collisions:0 txqueuelen:1 

      RX bytes:334 (334.0 B)  TX bytes:334 (334.0 B)



/ # exit

[root@vm-1473648642890 ~]#

7. 重新將剛才的VM設為可用,并將leader重啟

[root@vm-1473648611529 ~]# docker node update --availability active cm1k85xgzxivrazge4qqteotv

cm1k85xgzxivrazge4qqteotv

[root@vm-1473648611529 ~]#

[root@vm-1473648642890 ~]# reboot

8. 在其它VM上查看,大約半分鐘后,已經選舉出新的leader,且task的數目已經恢復到10個

[root@vm-1473648611529 ~]# docker node ls

ID                           HOSTNAME                                        STATUS  AVAILABILITY  MANAGER STATUS

bn68rlizdmnchk14fd1ft9izx    vm-1473648642890.vm-14736486428901473648642890  Down    Active        Unreachable

bu5cc1nx0aj29q984kkngeftd    vm-1476374349871                                Ready   Active        Leader

cm1k85xgzxivrazge4qqteotv *  vm-1473648611529.vm-14736486115291473648611529  Ready   Active        Reachable

[root@vm-1473648611529 ~]# docker service ls

ID            NAME        REPLICAS  IMAGE   COMMAND

7huiwv4vgrs7  helloworld  10/10     alpine  ping www.docker.com

[root@vm-1473648611529 ~]#

9. 此時原leader節點上的任務已經遷移到重新變為可用的節點上

[root@vm-1473648611529 ~]# docker ps

CONTAINER ID        IMAGE               COMMAND                 CREATED             STATUS              PORTS               NAMES

1d3a252664d8        alpine:latest       "ping www.docker.com"   23 seconds ago      Up 22 seconds                           helloworld.3.25sfbbqwqggvpdwbst8dgqmza

8ca053e810c1        alpine:latest       "ping www.docker.com"   23 seconds ago      Up 22 seconds                           helloworld.6.3z3dy1w216eu2833sdpxjz7mr

a1744b36a51d        alpine:latest       "ping www.docker.com"   24 seconds ago      Up 23 seconds                           helloworld.10.28ikt9ajopam1lf11c6vm0b91

981ba2a3ffec        alpine:latest       "ping www.docker.com"   24 seconds ago      Up 23 seconds                           helloworld.7.7ce9f4ia66op0pi08h897vsw8

7af8cbd1e07c        alpine:latest       "ping www.docker.com"   30 seconds ago      Up 27 seconds                           helloworld.8.339nbcym1ttyvnytm6ls65zuu

[root@vm-1473648611529 ~]#

10. 再查看一個helloworld.3的eth0的IP, 仍然是10.0.0.3

[root@vm-1473648611529 ~]# docker exec -ti 1d3a252664d8 sh

/ # ifconfig

eth0      Link encap:Ethernet  HWaddr 02:42:0A:00:00:03  

      inet addr:10.0.0.3  Bcast:0.0.0.0  Mask:255.255.255.0

      inet6 addr: fe80::42:aff:fe00:3%32585/64 Scope:Link

      UP BROADCAST RUNNING MULTICAST  MTU:1450  Metric:1

      RX packets:26 errors:0 dropped:0 overruns:0 frame:0

      TX packets:8 errors:0 dropped:0 overruns:0 carrier:0

      collisions:0 txqueuelen:0 

      RX bytes:2036 (1.9 KiB)  TX bytes:648 (648.0 B)



eth1      Link encap:Ethernet  HWaddr 02:42:AC:12:00:06  

      inet addr:172.18.0.6  Bcast:0.0.0.0  Mask:255.255.0.0

      inet6 addr: fe80::42:acff:fe12:6%32585/64 Scope:Link

      UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1

      RX packets:218 errors:0 dropped:0 overruns:0 frame:0

      TX packets:224 errors:0 dropped:0 overruns:0 carrier:0

      collisions:0 txqueuelen:0 

      RX bytes:20106 (19.6 KiB)  TX bytes:20984 (20.4 KiB)



lo        Link encap:Local Loopback  

      inet addr:127.0.0.1  Mask:255.0.0.0

      inet6 addr: ::1%32585/128 Scope:Host

      UP LOOPBACK RUNNING  MTU:65536  Metric:1

      RX packets:4 errors:0 dropped:0 overruns:0 frame:0

      TX packets:4 errors:0 dropped:0 overruns:0 carrier:0

      collisions:0 txqueuelen:1 

      RX bytes:334 (334.0 B)  TX bytes:334 (334.0 B)



/ # exit

[root@vm-1473648611529 ~]#

 

 

來自:http://dockone.io/article/1861

 

 本文由用戶 fxmo7195 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!