Dockerでインターネット越しにネットワークを組む

この記事は [なんか書く太郎｜GWアドベントカレンダー](https://gw-advent.9wick.com/calendars/31) 5日目です。
昨日は @yskkuwahara さんの[JavascriptのGeneratorで簡易対話式チャットボットもどき](https://qiita.com/yskkuwahara/items/6c394e597e8e4a8be98c)でした。

# Dockerでインターネット越しにネットワークを組む

自宅サーバーとVPSを使ってサーバーを立てているのですが、VPSのコンテナから自宅のコンテナにアクセスしたくなったのでVPN越しにDockerのoverlay networkを組んでみました。

全部で80コンテナぐらい動かしているのですが、それぞれのstackで動いているdbとredashを繋いだりコンテナを監視しようとしたりすると大変です。
使うとこだけ個別に穴を開けていくと、それぞれでセキュリティの対応やportの設定等をしないといけないため面倒ですよね。
そこでVPNでインターネットにお漏らししたくない通信は隠しつつ、コンテナ間でネットワークを組むことでホストのport設定も気にしなくていいようにしてみました。

![スクリーンショット 2019-04-30 2.11.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/147445/f4cdc523-b2eb-3875-92ae-2167b0d21178.png)

Swarmモードは今回使用しておりません。

使用したバージョンはこちらです。

```
HOST Ubuntu 18.04.1
Docker 18.09.0
etcd 3.2.17
```

記事中のIPアドレス、token、name等は全て実際に作業したものと置き換えております。

## VPN構築

OpenVPNを使用しました。
OpenVPN自体インストールは簡単なので省きますが、
`client-to-client` オプションでclient間での通信を有効にし、
`client-config-dir` でクライアントごとにのIPを固定にする必要があります。

今回の構成はこんな感じです

```
node1 192.168.1.20
node2 192.168.1.21
node3 192.168.1.22
```

VPNクライアント間のping

```
$ ping 192.168.1.20
PING 192.168.1.20 (192.168.1.20) 56(84) bytes of data.
64 bytes from 192.168.1.20: icmp_seq=1 ttl=64 time=11.3 ms
64 bytes from 192.168.1.20: icmp_seq=2 ttl=64 time=11.7 ms
64 bytes from 192.168.1.20: icmp_seq=3 ttl=64 time=11.6 ms
```

ホスト間のping
<img width="658" alt="スクリーンショット 2019-04-30 7.36.56.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/147445/da27d70f-17e0-944d-78ed-35cc00b05f1c.png">

VPNかましても十分早いですね

## etcd

Dockerに使うKVSにはetcdを使用しました。
他にはconsul | etcd | ZooKeeperが使えるみたいです。

http://docs.docker.jp/swarm/networking.html

```conf:/etc/default/etcd
ETCD_NAME="******"
ETCD_INITIAL_CLUSTER_STATE="new"
ETCD_INITIAL_CLUSTER_TOKEN="etcd-hoge-cluster"
ETCD_INITIAL_CLUSTER="node1=http://192.168.1.20:2380,node2=http://192.168.1.21:2380,node3=http://192.168.1.22:2380"
ETCD_INITIAL_ADVERTISE_PEER_URLS="http://192.168.1.22:2380"
ETCD_ADVERTISE_CLIENT_URLS="http://192.168.1.22:2379"
ETCD_LISTEN_CLIENT_URLS="http://192.168.1.22:2379"
ETCD_LISTEN_PEER_URLS="http://192.168.1.22:2380"
```

こんな感じで全ノードに対してinitialクラスターを設定します。
うまくいくとクラスタがhealthyになります。

```
$ etcdctl -C http://192.168.1.22:2379 cluster-health
member ****** is healthy: got healthy result from http://192.168.1.20:2379
member ****** is healthy: got healthy result from http://192.168.1.22:2379
member ****** is healthy: got healthy result from http://192.168.1.21:2379
cluster is healthy
```

クラスタのリーダーは以下のコマンドでみれます。
試しにリーダーをぶっ殺すと別のノードにリーダーが移ったりして面白いです。

REF: [etcd総選挙を眺めてみる](https://qiita.com/ksato9700/items/9b44a95ce27ac23a94e1)

```
$ etcdctl -C http://192.168.1.22:2379 member list
******: name=node1 peerURLs=http://192.168.1.20:2380 clientURLs=http://192.168.1.20:2379 isLeader=false
******: name=node2 peerURLs=http://192.168.1.22:2380 clientURLs=http://192.168.1.22:2379 isLeader=false
******: name=node3 peerURLs=http://192.168.1.21:2380 clientURLs=http://192.168.1.21:2379 isLeader=true
```

１点ハマった点は一回クラスタ作ったあとに全ノード止めてクラスタの設定を変更しようと思ったのですが、一回クラスタを作ってしまうと `ETCD_DATA_DIR` にクラスタの状態が保持されてしまうのでやり直したい場合はディレクトリの中身を消す必要があります。これをやらないと前のクラスタの設定が引き継がれてしまいます。

## Docker

全ノードのdockerdの設定にetcdの接続情報を入れていきます。
それぞれのノードで動いているetcdのアドレスを設定していきます。

```json:daemon.json
{
  "cluster-store": "etcd://192.168.1.22:2379",
  "cluster-advertise": "192.168.1.22:2376"
}
```

設定をしたらdaemonを再起動して反映させましょう。
無事起動したら実施にoverlay networkを作ります。
適当なノードで以下のコマンドでネットワークを作ってみます。

```:node1
$ docker network create --driver overlay overlay-test-net
$ docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
*******        bridge              bridge              local
*******        host                host                local
*******        none                null                local
*******        overlay-test-net    overlay             global
```

ネットワークが作られたのがわかります。
別のノードでも `docker network ls` をしてみると上で作ったネットワークが見えていてちょっと感動します。


実際にコンテナにnetworkを繋いでみて疎通ができるか確認してみましょう。
nginxのコンテナを立ててみます。

```
$ docker run --name nginx-test --rm --net overlay-test-net nginx
```

別ノードから疎通を試してみます。

```
# ping nginx-test
PING nginx-test (10.0.1.2) 56(84) bytes of data.
64 bytes from nginx-test.overlay-test-net (10.0.1.2): icmp_seq=1 ttl=64 time=12.1 ms
64 bytes from nginx-test.overlay-test-net (10.0.1.2): icmp_seq=2 ttl=64 time=11.9 ms
64 bytes from nginx-test.overlay-test-net (10.0.1.2): icmp_seq=3 ttl=64 time=12.0 ms
```

意外とping早いです。
curlも返ってきます。

```
# curl nginx-test
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
```

## まとめ

VPN越しにoverlay networkを組んでみました。
これでインターネット越しでコンテナ間に閉じたネットワークを作ることができるようになりました。