Docker Swarmのingressにserviceをアタッチして負荷分散とかしてるってことをこの目で確かめる

こんにちは。
株式会社クラスアクト インフラストラクチャ事業部の大塚です。

この記事ではDocker Swarm環境にデフォルトで作成されるingressにserviceをデプロイ。コンテナ間通信を可能にし、更に負荷分散をしているところを確認してみます。


# ingressとは

ザックリ行ってしまうと、
- ホスト間のオーバレイネットワークを構築するもの
- これがあるとホストを跨ったコンテナ間の通信が出来る
- 良い感じにロードバランシングしてくれる

って感じだと思っています。（間違っていたらすみません<m(__)m>）
コンテナオーケストレーションツールとしてkubernetesが有名ですが、そちらでもingressはありますね。恐らくニュアンス的にはSwarmもkuberenetesも変わらないと思っています。
※関係ないですがオーバレイネットワークって響きが最高にかっこいいですよね。厨二病を刺激されます。


# ingressを指定してserviceをデプロイする

デフォルトのingressを指定してserviceをデプロイする為にはdocker service createに--publishオプションを指定してあげると良い様子。
以下の場合、コンテナの数が常に3つになるように、ホストの8080ポートに接続があった場合httpdコンテナの80ポートに転送するようにしています。

```
root@docker:~# docker service create --replicas=3 --name httpd --publish publish
ed=8080,target=80 httpd:latest
x57l3av545tbxvqffbowlwgjw
overall progress: 3 out of 3 tasks
1/3: running
2/3: running
3/3: running
verify: Service converged

root@docker:~# docker ps
CONTAINER ID   IMAGE                    COMMAND                  CREATED         STATUS         PORTS                                                                                            NAMES
c03147d2f83c   httpd:latest             "httpd-foreground"       3 minutes ago   Up 3 minutes   80/tcp                                                                                           httpd.1.t5zt9glbr4tlgjofhcozjz7bi
521b69dd00e9   nginx:latest             "/docker-entrypoint.…"   31 hours ago    Up 31 hours    80/tcp                                                                                           my-nginx.3.hux0zd2goc6974s25n4vvafcj
8240d0af95a3   portainer/portainer-ce   "/portainer"             7 days ago      Up 31 hours    0.0.0.0:8000->8000/tcp, :::8000->8000/tcp, 0.0.0.0:9000->9000/tcp, :::9000->9000/tcp, 9443/tcp   portainer

root@docker:~# docker service ls
ID             NAME       MODE         REPLICAS   IMAGE          PORTS
x57l3av545tb   httpd      replicated   3/3        httpd:latest   *:8080->80/tcp
g4inx6fpvjzb   my-nginx   replicated   5/5        nginx:latest   *:80->80/tcp

root@docker:~# docker service ps httpd
ID             NAME      IMAGE          NODE             DESIRED STATE   CURRENT STATE           ERROR     PORTS
t5zt9glbr4tl   httpd.1   httpd:latest   docker           Running         Running 9 minutes ago
l9y0iwo3ig2d   httpd.2   httpd:latest   swarm-worker01   Running         Running 9 minutes ago
rbra2u5gg8s9   httpd.3   httpd:latest   swarm-worker02   Running         Running 9 minutes ago
```

各コンテナのIPアドレスを見てみましょう。
Leader
```
root@docker:~# docker inspect httpd.1.t5zt9glbr4tlgjofhcozjz7bi | grep IPAddress
            "SecondaryIPAddresses": null,
            "IPAddress": "",
                    "IPAddress": "10.0.0.16",
```

worker01
```
root@swarm-worker01:~# docker inspect httpd.2.l9y0iwo3ig2dib0du7wh16wpf | grep IPAddress 
            "SecondaryIPAddresses": null,
            "IPAddress": "",
                    "IPAddress": "10.0.0.17",
```

worker02
```
root@swarm-worker02:~# docker inspect httpd.3.rbra2u5gg8s9bpps6ijvv7g19 | grep IPAddress
            "SecondaryIPAddresses": null,
            "IPAddress": "",
                    "IPAddress": "10.0.0.18",
```

ingressのネットワークを見てみます。
コンテナと同じネットワークにいるということと、Containersの部分でhttpd.1を認識していることから、httpdコンテナはこれに繋がっていると思って間違いないでしょう。
```
root@docker:~# docker network inspect ingress
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "10.0.0.0/24",
                    "Gateway": "10.0.0.1"
                }
            ]
        },
中略
        "Containers": {
            "521b69dd00e926866e1cf341b369a65e00288c8e982a8bdae7b5d7631c1eb7fc": {
                "Name": "my-nginx.3.hux0zd2goc6974s25n4vvafcj",
                "EndpointID": "2c9e925936cec8c030a031693c3f834123f0a3afe3bab3c24b9486739dec4d57",
                "MacAddress": "02:42:0a:00:00:0a",
                "IPv4Address": "10.0.0.10/24",
                "IPv6Address": ""
            },
            "c03147d2f83cc0f90706913ac78f56f85e3ed16afe7b3e0b771bb9447603d4a6": {
                "Name": "httpd.1.t5zt9glbr4tlgjofhcozjz7bi",
                "EndpointID": "6c6403c4ff692e275004245921571befc4701e816b560fd079f6d741bb530afd",
                "MacAddress": "02:42:0a:00:00:10",
                "IPv4Address": "10.0.0.16/24",
                "IPv6Address": ""
            },
            "ingress-sbox": {
                "Name": "ingress-endpoint",
                "EndpointID": "1f85e65c9f18ca7aded3222893dc2ae63dfcaa37609a0e6c39ae1a072027495c",
                "MacAddress": "02:42:0a:00:00:02",
                "IPv4Address": "10.0.0.2/24",
                "IPv6Address": ""
            }
        },
```

つまり今のイメージは以下となるということになります。
![docker-ページ23.drawio (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3219385/03d99dae-456b-dccd-dad5-79531de3d849.png)

# ingressに繋がっているコンテナ同士で疎通が取れるか確認する

httpd.1からhttpd.2と3に対してping疎通を確認します。
以下のログから疎通が取れていることが分かると思います。
```
root@c03147d2f83c:/usr/local/apache2# ping 10.0.0.17
PING 10.0.0.17 (10.0.0.17): 56 data bytes
64 bytes from 10.0.0.17: icmp_seq=0 ttl=64 time=4.238 ms
64 bytes from 10.0.0.17: icmp_seq=1 ttl=64 time=0.802 ms
64 bytes from 10.0.0.17: icmp_seq=2 ttl=64 time=0.784 ms
64 bytes from 10.0.0.17: icmp_seq=3 ttl=64 time=0.707 ms
64 bytes from 10.0.0.17: icmp_seq=4 ttl=64 time=0.874 ms
^C--- 10.0.0.17 ping statistics ---
5 packets transmitted, 5 packets received, 0% packet loss
round-trip min/avg/max/stddev = 0.707/1.481/4.238/1.380 ms

root@c03147d2f83c:/usr/local/apache2# ping 10.0.0.18
PING 10.0.0.18 (10.0.0.18): 56 data bytes
64 bytes from 10.0.0.18: icmp_seq=0 ttl=64 time=5.083 ms
64 bytes from 10.0.0.18: icmp_seq=1 ttl=64 time=0.745 ms
64 bytes from 10.0.0.18: icmp_seq=2 ttl=64 time=0.639 ms
64 bytes from 10.0.0.18: icmp_seq=3 ttl=64 time=0.703 ms
64 bytes from 10.0.0.18: icmp_seq=4 ttl=64 time=0.966 ms
^C--- 10.0.0.18 ping statistics ---
5 packets transmitted, 5 packets received, 0% packet loss
round-trip min/avg/max/stddev = 0.639/1.627/5.083/1.731 ms
```

# ingressが負荷分散をしていることを確認する

確認の仕方として、それぞれのhttpdコンテナのindex.htmlをそれぞれ固有のものに変更。
ホストに対してhttp接続を何度かしてみて出力される内容が定期的に変わる（ingressからロードバランシングして、表面上Webブラウザから接続しているホストは同じだけど、実際にアクセスしているコンテナは別になっている）ことを確認してみたいと思います。

現状、それぞれのホストにWebブラウザで8080ポートを指定してアクセスすると以下の様な出力結果となります。
このままではどのhttpdコンテナに接続しているのかわかりません。
![001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3219385/fdb4a970-19b9-1830-ee7c-643f0245da13.png)

仕込みを入れていきたいと思います。
apache2のindex.htmlを書き換えていきます。apache2のindex.htmlは/usr/local/apache2/htdocsにあるみたいです。
コンテナにviコマンドは無いのでインストールし、index.htmlを編集します。
編集後、apacheに設定反映を行うためrestartを実施します。
※エラーっぽいのが出てますが、設定反映されていることは確認できたため、今回はスルー。

Leader
```
root@docker:~# docker ps
doCONTAINER ID   IMAGE                    COMMAND                  CREATED          STATUS          PORTS                                                                                            NAMES
c03147d2f83c   httpd:latest             "httpd-foreground"       12 minutes ago   Up 12 minutes   80/tcp                                                                                           httpd.1.t5zt9glbr4tlgjofhcozjz7bi
521b69dd00e9   nginx:latest             "/docker-entrypoint.…"   31 hours ago     Up 31 hours     80/tcp                                                                                           my-nginx.3.hux0zd2goc6974s25n4vvafcj
8240d0af95a3   portainer/portainer-ce   "/portainer"             7 days ago       Up 31 hours     0.0.0.0:8000->8000/tcp, :::8000->8000/tcp, 0.0.0.0:9000->9000/tcp, :::9000->9000/tcp, 9443/tcp   portainer
root@docker:~# docker exec -it httpd.1.t5zt9glbr4tlgjofhcozjz7bi /bin/bash
root@c03147d2f83c:/usr/local/apache2# cd /usr/local/apache2/htdocs
root@c03147d2f83c:/usr/local/apache2/htdocs# ls -ltr
total 4
-rw-r--r-- 1 501 staff 45 Jun 11  2007 index.html
root@c03147d2f83c:/usr/local/apache2/htdocs# cat index.html
<html><body><h1>It works!</h1></body></html>
root@c03147d2f83c:/usr/local/apache2/htdocs# apt update
root@c03147d2f83c:/usr/local/apache2/htdocs# apt upgrade
root@c03147d2f83c:/usr/local/apache2/htdocs# apt-get install vim
root@c03147d2f83c:/usr/local/apache2/htdocs# vi index.html
root@c03147d2f83c:/usr/local/apache2/htdocs# cat index.html 
<html><body><h1>It works on node:192.168.2.177!</h1></body></html>
root@c03147d2f83c:/usr/local/apache2/htdocs# /usr/local/apache2/bin/apachectl restart 
AH00557: httpd: apr_sockaddr_info_get() failed for c03147d2f83c
AH00558: httpd: Could not reliably determine the server's fully qualified domainerName' directive globally to suppress this message
```

worker01
```
root@swarm-worker01:~# docker ps
CONTAINER ID   IMAGE          COMMAND                  CREATED          STATUS          PORTS     NAMES
7f81733ca153   httpd:latest   "httpd-foreground"       21 minutes ago   Up 21 minutes   80/tcp    httpd.2.l9y0iwo3ig2dib0du7wh16wpf
1f9410c4ecb2   nginx:latest   "/docker-entrypoint.…"   31 hours ago     Up 31 hours     80/tcp    my-nginx.5.9nm6wov2o74y9uzfetpbv4b6d
ff92cb76fad6   nginx:latest   "/docker-entrypoint.…"   31 hours ago     Up 31 hours     80/tcp    my-nginx.2.1r2rq8q5a5uiy0ywybgin8pjb
root@swarm-worker01:~# docker exec -it httpd.2.l9y0iwo3ig2dib0du7wh16wpf /bin/bash
root@7f81733ca153:/usr/local/apache2# cd /usr/local/apache2/htdocs
root@7f81733ca153:/usr/local/apache2/htdocs# ls -ltr
total 4
-rw-r--r-- 1 501 staff 45 Jun 11  2007 index.html
root@7f81733ca153:/usr/local/apache2/htdocs# cat index.html
<html><body><h1>It works!</h1></body></html>
root@7f81733ca153:/usr/local/apache2/htdocs# apt update
root@7f81733ca153:/usr/local/apache2/htdocs# apt upgrade
root@7f81733ca153:/usr/local/apache2/htdocs# apt-get install vim
root@7f81733ca153:/usr/local/apache2/htdocs# vi index.html
root@7f81733ca153:/usr/local/apache2/htdocs# cat index.html
<html><body><h1>It works on node:192.168.2.188!</h1></body></html>
root@7f81733ca153:/usr/local/apache2/htdocs# /usr/local/apache2/bin/apachectl restart
AH00557: httpd: apr_sockaddr_info_get() failed for 7f81733ca153
AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 127.0.0.1. Set the 'ServerName' directive globally to suppress this message
```

worker02
```
root@swarm-worker02:~# docker ps
CONTAINER ID   IMAGE          COMMAND                  CREATED          STATUS          PORTS     NAMES
41f566a7b21e   httpd:latest   "httpd-foreground"       31 minutes ago   Up 31 minutes   80/tcp    httpd.3.rbra2u5gg8s9bpps6ijvv7g19
0507bb89e81b   nginx:latest   "/docker-entrypoint.…"   31 hours ago     Up 31 hours     80/tcp    my-nginx.4.zprs5gbh2krbrakov75trn2e2
8b531cf804d8   nginx:latest   "/docker-entrypoint.…"   31 hours ago     Up 31 hours     80/tcp    my-nginx.1.mk4z1xgsd04kc5hb8uiwk593w
root@swarm-worker02:~# docker exec -it httpd.3.rbra2u5gg8s9bpps6ijvv7g19 /bin/bash
root@41f566a7b21e:/usr/local/apache2# cd /usr/local/apache2/htdocs
root@41f566a7b21e:/usr/local/apache2/htdocs# ls -ltr
total 4
-rw-r--r-- 1 501 staff 45 Jun 11  2007 index.html
root@41f566a7b21e:/usr/local/apache2/htdocs# cat index.html
<html><body><h1>It works!</h1></body></html>
root@41f566a7b21e:/usr/local/apache2/htdocs# apt update
root@41f566a7b21e:/usr/local/apache2/htdocs# apt upgrade
root@41f566a7b21e:/usr/local/apache2/htdocs# apt-get install vim
root@41f566a7b21e:/usr/local/apache2/htdocs# vi index.html
root@41f566a7b21e:/usr/local/apache2/htdocs# cat index.html
<html><body><h1>It works on node:192.168.2.199!</h1></body></html>
root@41f566a7b21e:/usr/local/apache2/htdocs# /usr/local/apache2/bin/apachectl restart
AH00557: httpd: apr_sockaddr_info_get() failed for 41f566a7b21e
AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 127.0.0.1. Set the 'ServerName' directive globally to suppress this message
```

仕込みが完了したらWebブラウザでコンテナが稼働しているホストに何度か接続してみます。
今回はそれぞれ3回ずつ接続をしてみました。
接続するたびに出力されている内容が変わっていることが分かると思います。
ingressがロードバランシングを行い、接続しているコンテナが変わっていることが分かると思います。
![002.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3219385/943ba1db-4156-78d4-3c80-b6c68fccf73d.png)
![003.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3219385/07dfd887-80cc-a94d-afbf-f9f27d534a90.png)
![00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3219385/89749769-3987-2baa-918c-abb003474d9e.png)

ホスト192.168.2.177のみ着目した通信の流れですが、イメージとして以下の様になります。
赤くしたところが通信の可能性のある経路となります。
![docker-ページ24.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3219385/6823493e-aa3f-b2b9-f905-48efeba69699.png)