1. 程式人生 > >Docker Swarm系列——7.Swarm服務呼叫

Docker Swarm系列——7.Swarm服務呼叫


在這篇文章中,大家將會了解在Swarm中,服務與服務之間如何互相呼叫。通過前面幾篇文章的學習,我們已經能夠熟練地部署服務,並在瀏覽器中成功訪問。但是,我們一直以來部署的都是單個服務,還沒接觸到例如,在web中呼叫api,在api中呼叫mysql,接下來,我們就從網路開始,一探究竟。

1. 網路名稱

還記得我們最開始通過docker network create -d overlay --attachable httpnet建立的httpnet網路嗎?這一步究竟做了什麼?如果不清楚,docker教給我們一個方法inspect

$ docker network inspect httpnet

[
...
    {
        "Name": "httpnet",
        "IPAM": {
            "Config": [
                {
                    "Subnet": "10.0.1.0/24",
                    "Gateway": "10.0.1.1"
                }
            ]
        },
...
    }
]

我們只截取了對網路httpnet分析最重要的一部分,仔細看一下,我們會發現如下新的資訊:

  • Subnet子網,凡是註冊到此網路上的服務或容器,docker都會從10.0.1.0/24中自動分配一個或多個內部IP給它。
  • Gateway閘道器,httpnet網路的閘道器是10.0.1.1

預設在同一個子網(內網),任何IP都可以ping通,任何PORT都對外開放,這就是本質上容器間或服務間互相訪問呼叫的方式。

2. 服務名稱

我們已經知道,本質上容器間或服務間都是通過ip:port的形式進行訪問或呼叫,但是我們同時也清楚,容器的生命週期始於建立終於銷燬,且容器的啟停很頻繁,那就意味著一個服務中容器的真實IP隨時都可能改變。

因此,docker提供給我們的方式,通過命令的編寫就能看出來,那就是名稱,包括網路名稱、stack名稱、服務名稱、容器名稱等。那讓我們看看,服務名稱背後又是什麼玄機?

$ docker service inspect http-v1

[
...
        "Spec": {
            "Name": "http-v1",
            "Networks": [
                {
                    "Target": "httpnet"
                }
            ],
            "Replicated": {
                    "Replicas": 2
                }
        }
        "Endpoint": {
            "Spec": {
                "Mode": "vip",
                "Ports": [
                    {
                        "Protocol": "tcp",
                        "TargetPort": 80,
                        "PublishedPort": 80,
                        "PublishMode": "ingress"
                    }
                ]
            },
            "VirtualIPs": [
                {
                    "NetworkID": "m8hsd6mdesxey1m0sm6ipjxrt",
                    "Addr": "10.0.1.7/24"
                }
            ]
...
    }
]

我們只截取了對服務http-v1分析最重要的一部分,仔細看一下,我們會發現如下新的資訊:

  • Mode機制,vip代表對服務中所有容器的代理,不是直接訪問,而是通過vip地址。
  • Addr地址,vip的地址為10.0.1.7,它代表服務的地址,但不是真實的容器地址。

我們可以任意進入一個容器裡面看看:

$ docker exec -it http-v1.2.7xp9liu4tn21hzogtqhyswk5n /bin/sh

# traceroute http-v1
traceroute to http-v1 (10.0.1.7), 30 hops max, 46 byte packets
 1  10.0.1.7 (10.0.1.7)  0.029 ms  0.672 ms  0.024 ms
 
# curl http-v1
{"version":"v1","hostname":"6e54dd2e8007","address":"10.0.1.9"}/app

# curl http-v1
{"version":"v1","hostname":"4d51a5226238","address":"10.0.1.8"}/app

# curl 10.0.1.7
{"version":"v1","hostname":"6e54dd2e8007","address":"10.0.1.9"}/app

# curl 10.0.1.7
{"version":"v1","hostname":"4d51a5226238","address":"10.0.1.8"}/app
graph LR
A(curl http-v1)-->|dns, vip|B(10.0.1.7)
B(10.0.1.7)-->|輪詢|C(10.0.1.8, 10.0.1.9)

不難看出,一個服務永遠只對應一個VIP,而VIP通過輪詢每次返回一個真實的容器IP,且VIP負責維護真實容器的所有IP(建立、銷燬)。

graph LR
A(curl 172.16.0.15)-->|iptables, ipvs|B(10.0.1.7)
B(10.0.1.7)-->|輪詢|C(10.0.1.8, 10.0.1.9)

此外,如果一個服務對外暴露了埠,也可以通過外部ip:port的方式進行呼叫。但是這種方式不僅捨近求遠,還增加了IP地址轉換的效能損耗,而且因為叢集中每個節點的ip:port都可以訪問,那該選擇一個固定的還是每次從所有的節點中隨機?如果一個節點不可用又怎麼辦?

至此,我們終於弄明白了在【2.Swarm服務發現】文章中未深入介紹的服務發現和負載均衡原理,同時,如果在一個服務中想要呼叫另外一個服務,只需要在連線字串中加入另外一個服務的名稱和埠即可。