docker コンテナの起動/停止を繰り返していると、EC2 へ接続できなくなる現象でハマったので覚え書き。
現象
elasticsearch と連動するアプリケーションを docker コンテナで動かしている環境で、コンテナの起動/停止をしているとあるタイミングで EC2 への接続が切れてしまう現象が発生しました。
- docker-compose でコンテナ起動/停止
- 一定の確率で発生する
- EC2 への ssh 接続がつながらなくなる
- EC2 再起動しても復旧しない
- EC2 再起動中に ping をかけておくと、起動直後に一瞬疎通するが、その後ダンマリになる
アプリケーションの実行環境は以下のような構成。
アプリ用の VPC へピア接続された保守用 VPC 内の踏台 EC2 からアクセスしています。
解析
EC2 起動直後、しばらくしてからネットワークがつながらなくなるため、EC2 起動時にコンテナを自動起動していた(restart=always) のを廃止してみたが、現象変わらず。
なので、アプリケーション自体の線は除外しました。
問題発生時の EC2 の状態が確認できないと何も分からないため、Elastic IP を割り当てて接続したところ、無事 ssh 接続成功。
ということは、踏台 EC2 からの接続のみの問題となりそうです。
問題のサーバに入れたので、いろいろ状態の情報を収集してみます。
# ネットワークインターフェースの状態
$ ifconfig
$ ip route
# ブリッジの状態
$ brctl show
# iptables の状態
$ sudo iptables -L
$ sudo iptables -L -t nat
# docker network の状態
$ docker network ls
$ docker network inspect xxxxxx_default
# docker コンテナの状態
$ docker ps
$ docker inspect xxxxxx
原因
上記のコマンドによる取得結果を、正常時と問題発生時で比較していると、コンテナの起動/停止の繰り返しで、docker-compose が作るネットワークのアドレスが一つずつ変わっていくのが分かりました。
それで、あるアドレスが設定された場合に問題が発生しているようです。
172.31.0.0/16
原因は、アクセス元である保守用 VPC のアドレスと、docker network でブリッジに設定したアドレスがかぶってしまったときに、ルーティングがおかしくなってしまうというものでした。
docker network は内部で iptables を書き換えるので(★)、この影響を受けていたことになります。
$ sudo iptables -L -t nat
:
Chain POSTROUTING (policy ACCEPT)
target prot opt source destination
MASQUERADE all -- ip-172-31-0-0.ap-northeast-1.compute.internal/16 anywhere ★
MASQUERADE all -- ip-172-17-0-0.ap-northeast-1.compute.internal/16 anywhere
docker のネットワークは、デフォルトでは 172.18.0.0/16
から順番に 172.19.0.0/16
、172.20.0.0/16
、と採番するようで、これがたまたまアクセス元の VPC とかぶってました。アクセス元の VPC は AWS のデフォルト VPC だったので、デフォルト設定で動かしていると案外遭遇してしまうかも。
docker network のアドレス設定については、以下のサイトが参考になりました。
Dockerはネットワークを31個しか作れない
https://ashphy.hateblo.jp/entry/2020/05/23/223354
デフォルトで以下のアドレスがプールされていて、順番に利用されるようです。
- "172.17.0.0/16"
- "172.18.0.0/16"
- "172.19.0.0/16"
- "172.20.0.0/16"
- "172.21.0.0/16"
- "172.22.0.0/16"
- "172.23.0.0/16"
- "172.24.0.0/16"
- "172.25.0.0/16"
- "172.26.0.0/16"
- "172.27.0.0/16"
- "172.28.0.0/16"
- "172.29.0.0/16"
- "172.30.0.0/16"
- "172.31.0.0/16"
- "192.168.0.0/20"
- "192.168.16.0/20"
- "192.168.32.0/20"
- "192.168.48.0/20"
- "192.168.80.0/20"
- "192.168.96.0/20"
- "192.168.112.0/20"
- "192.168.128.0/20"
- "192.168.144.0/20"
- "192.168.160.0/20"
- "192.168.176.0/20"
- "192.168.192.0/20"
- "192.168.208.0/20"
- "192.168.224.0/20"
- "192.168.240.0/20"
※結構、大きな帯域使ってますね。。
対処
docker network で利用するアドレスプールを指定できるようなので、これをアクセス元 VPC にかぶらないように設定しました。
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "10"
},
"live-restore": true,
"default-address-pools": [
{ "base":"192.168.0.0/16", "size":24 } ★
],
"max-concurrent-downloads": 10
}
分かってしまえば単純な問題ですが、これはなかなか気づきにくいなと思います。
最初にアプリケーションの原因を考えていたので、だいぶ遠回りしました。。
(docker ネットワークの勉強になったのでヨシとします)
// EOF