クラウドとローカルをVPNでガッチャンコしたDockerネットワークを組んでみる

  • 2016-01-22 (Fri)
  • namikingsoft

クラウド上で組んだDockerのオーバーレイネットワークの中に、屋内ファイアウォール内のマシンで組んでたDockerを参加させることができないか、試してみた。

なぜに? (余談)

例えば、以前の記事のようにクラウドで組んだSparkクラスタを利用するために、Apache Zeppelin1を使ってみたい。最初は、SSHトンネルやSocksプロキシで手軽にできないだろうかと色々やってみたが、Sparkは一方通行な通信ではなく、双方向な通信を行うため、クラウドからローカルのネットワークに直接アクセス(pingなど)できる必要があるようで、動作するには至らなかった。

Zeppelin on Local

Spark関連は基本的に同じネットワーク内で動かすのが前提の仕様なため、Swarmクラスタと同じネットワーク内に入れる必要があるが、Zeppelinは、

  • 割りとメモリ食い(推奨4GBほど)のため、Sparkクラスタと同じホストでは、動かしたくない。かといって、専用のホストを用意するできるブルジョアではない。
  • クラウドのSparkクラスタは使い終わったらすぐに破壊したいが、Zeppelinはnotebookや設定のデータを内部で持つため、作った後や消す前の同期が少し面倒。

なので、できれば、ローカルネットワーク内にZeppelinを持っておきたい。

Zeppelin on Local Ideal

この記事でやること

上のApache Sparkの例は置いといて、今回は取り急ぎクラウドとローカルを跨いだSwarmクラスタを構築して、Dockerオーバーレイネットワークの疎通確認を行いたい。検証のため、以下の図の様な構成を準備して、オーバーレイネットワークの疎通を確認する。

Zeppelin on Local Ideal

要点
  • ローカル環境とクラウドのDockerをVPNを用いて、オーバーレイネットワークで繋ぎたい。
  • VPN接続には、SoftEther VPN2を使ってみる。
  • ローカル側とクラウド側1台ずつ、Swarm Masterをレプリケーションした。
    • クラウドのみでも、Swarmクラスタとして機能させたかったため。
    • ローカルの作業用PCは、いちいちVPNに接続しなくても、Swarmクラスタを操作できるようにするため。

事前準備

必要なソフトウェアのインストール

作業で使うPC(またはホスト)に以下のソフトウェアをインストールしておく。

  • docker (バージョン1.9以上, オーバーレイネットワーク機能を使う)
  • docker-compose (動作検証に使うだけなので任意)
  • Virtualbox (vagrantを併用してもよい)

DigitalOceanの登録とアクセストークンの取得

登録後、管理画面からdocker-machineとの連携に必要なアクセストークンを発行できる。

https://www.digitalocean.com/

ノード用のホストを用意する

今回の例では、DigitalOceanでノードを2台とローカルでVirtualbox1台を用意する。

Type Host OS Mem VPNのIP
DigitalOcean swarm-node0 ubuntu-15-10-x64 512MB 192.168.30.2 (手動)
DigitalOcean swarm-node1 ubuntu-15-10-x64 512MB 192.168.30.x (自動)
Vitualbox(local) swarm-local ubuntu-15-10-x64 - 192.168.30.y (自動)
備考
  • swarm-node0はマスターノードとして使う
  • ホスト名(hostname)は別になんでもよい
  • プライベートネットワークは無効にしておく
  • VPNのIPについては、VPN接続後に割り当てる。

SoftEther VPN Serverを動かす

swarm-node0にSSHなどでログインして、作業を行う。

Linux版については、SoftEther VPNのサイトからソースコードをダウンロードして、コンパイルする。後で自動化しやすいように、GUIやインタラクティブCUIを使わないように書いておく。

ダウンロード

# 必要なパッケージのインストール
apt-get install -y curl gcc make

# SoftEther VPN ソースのダウンロード
cd /usr/local/src
curl -LO http://jp.softether-download.com/files/softether/v4.19-9599-beta-2015.10.19-tree/Linux/SoftEther_VPN_Server/64bit_-_Intel_x64_or_AMD64/softether-vpnserver-v4.19-9599-beta-2015.10.19-linux-x64-64bit.tar.gz
tar xzf softether-vpnserver-v4.19-9599-beta-2015.10.19-linux-x64-64bit.tar.gz

コンパイルとインストール

# コンパイル
cd vpnserver
make i_read_and_agree_the_license_agreement

# PATH設定
export PATH="/usr/local/src/vpnserver:$PATH"
echo 'export PATH="/usr/local/src/vpnserver:$PATH"' >> /etc/profile

サービス登録と起動 (systemd)

# サービス登録
cat << EOS > /lib/systemd/system/vpnserver.service
[Unit]
Description=SoftEther VPN Server
After=network.target

[Service]
Type=forking
ExecStart=/usr/local/src/vpnserver/vpnserver start
ExecStop=/usr/local/src/vpnserver/vpnserver stop

[Install]
WantedBy=multi-user.target
EOS

# 自動起動設定&起動
systemctl enable vpnserver
systemctl start vpnserver

VPNサーバー設定

# 各種設定項目 (値は任意で決める)
export HUBNAME=cluster
export HUBPASS=password
export USERNAME=user
export USERPASS=something
export SHAREDKEY=sharedkey

# 仮想HUB作成
vpncmd localhost /SERVER /CMD HubCreate $HUBNAME \
  /PASSWORD:$HUBPASS && true

# SNAT&DHCP有効化
vpncmd localhost /SERVER /HUB:$HUBNAME /PASSWORD:$HUBPASS /CMD \
  SecureNatEnable

# ユーザー登録
vpncmd localhost /SERVER /HUB:$HUBNAME /PASSWORD:$HUBPASS /CMD \
  UserCreate $USERNAME \
  /GROUP:none \
  /REALNAME:none \
  /NOTE:none

# ユーザーパスワード設定
vpncmd localhost /SERVER /HUB:$HUBNAME /PASSWORD:$HUBPASS /CMD \
  UserPasswordSet $USERNAME \
  /PASSWORD:$USERPASS

# IPsec VPN有効化
vpncmd localhost /SERVER /CMD \
  IPsecEnable \
  /L2TP:yes \
  /L2TPRAW:no \
  /ETHERIP:yes \
  /PSK:$SHAREDKEY \
  /DEFAULTHUB:$HUBNAME

VPN設定のリファレンスは以下を参照。
https://ja.softether.org/4-docs/1-manual/6/6.4

SoftEther VPN Clientを動かす

全てのノードにSSHなどでログインして、作業を行う。
Serverと同じく、SoftEther VPNのサイトからソースコードをダウンロードして、コンパイルする。

ダウンロード

# 必要なパッケージのインストール
apt-get install -y curl gcc make

# SoftEther VPN ソースのダウンロード
cd /usr/local/src
curl -LO http://jp.softether-download.com/files/softether/v4.19-9599-beta-2015.10.19-tree/Linux/SoftEther_VPN_Client/64bit_-_Intel_x64_or_AMD64/softether-vpnclient-v4.19-9599-beta-2015.10.19-linux-x64-64bit.tar.gz
tar xzf softether-vpnclient-v4.19-9599-beta-2015.10.19-linux-x64-64bit.tar.gz
rm softether-vpnclient-v4.19-9599-beta-2015.10.19-linux-x64-64bit.tar.gz

コンパイルとインストール

# コンパイル
cd vpnclient
make i_read_and_agree_the_license_agreement

# PATH設定
export PATH="/usr/local/src/vpnclient:$PATH"
echo 'export PATH="/usr/local/src/vpnclient:$PATH"' >> /etc/profile

サービス登録と起動 (systemd)

# サービス登録
cat << EOS > /lib/systemd/system/vpnclient.service
[Unit]
Description=SoftEther VPN Client
After=network.target

[Service]
Type=forking
ExecStart=/usr/local/src/vpnclient/vpnclient start
ExecStop=/usr/local/src/vpnclient/vpnclient stop

[Install]
WantedBy=multi-user.target
EOS

# 自動起動設定&起動
systemctl enable vpnclient
systemctl start vpnclient

VPNサーバー設定

# 各種設定項目 (基本的にはServerの値と合わせる)
ACCOUNT=private
NICNAME=vlan0
SERVER="(swarm-node0のグローバルIP):443"
HUBNAME=cluster
USERNAME=user
USERPASS=something

# クライアント管理へのリモートログインを無効
vpncmd localhost /CLIENT /CMD RemoteDisable

# NIC作成 (この例だと、vpn_vlan0というインタフェースが作成される)
vpncmd localhost /CLIENT /CMD NicCreate $NICNAME

# アカウント作成
vpncmd localhost /CLIENT /CMD AccountCreate $ACCOUNT \
  /SERVER:$SERVER \
  /HUB:$HUBNAME \
  /USERNAME:$USERNAME \
  /NICNAME:$NICNAME

# アカウントパスワード設定
vpncmd localhost /CLIENT /CMD AccountPasswordSet $ACCOUNT \
  /PASSWORD:$USERPASS \
  /TYPE:standard

# アカウント自動起動設定
vpncmd localhost /CLIENT /CMD AccountStartupSet $ACCOUNT

# アカウント接続
vpncmd localhost /CLIENT /CMD AccountConnect $ACCOUNT

# swarm-node0では固定IP割り当て
ip addr add 192.168.30.2/24 dev vpn_$NICNAME

# その他ノードは自動割り当て
dhclient vpn_$NICNAME

swarm-node0だけは、DHCPでIPを振らずに固定IPを設定する。

VPN接続確認

SoftEther VPNのClientのセッティングが完了すると、以下の様な成果が出る。

  • 各ノードにvpn_vlan0というインタフェースができる。
    • vpn_vlan0を通して、pingなどの疎通ができるようになる。
  • 各ノードに192.168.30.0/24のネットワークのIPが割り当てられる。
    • 192.168.30.0/24はSoftEther VPNのデフォルト設定。
    • DHCPで割り当てると、192.168.30.10〜が振られる。

これから設置するConsulやDockerは、このネットワークにのせるように設定する。

各ノードでConsulを動かす

インストールとサービス登録手順については、以前の記事にまとめてあるので参照されたい。ここではConsulサーバーの設定手順を示す。

swarm-node0にてサーバーモードで動かす設定

SSHでログインして、作業を行う。

# 自分自身のVPNのIPを取得
export MY_VPN_IP=$(
  ip addr show vpn_vlan0 \
  | grep -o -e '[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+' \
  | head -n1
)

# 設定ファイルに書き出す
cat << EOS > /etc/consul.d/config.json
{
  "server": true,
  "bootstrap": true,
  "bind_addr": "$MY_VPN_IP",
  "datacenter": "swarm0",
  "ui_dir": "/var/local/consul/webui",
  "data_dir": "/var/local/consul/data",
  "log_level": "INFO",
  "enable_syslog": true
}
EOS

VPNネットワークであるvpn_vlan0のIPにバインドする。
設定が終わったら、自動起動設定と起動を行っておく。

systemctl enable consul
systemctl start consul

swarm-node1とswarm-localにてクライアントモードで動かす設定

SSHでログインして、作業を行う。

# 自分自身のvpnのipを取得
export my_vpn_ip=$(
  ip addr show vpn_vlan0 \
  | grep -o -e '[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+' \
  | head -n1
)

# 設定ファイルに書き出す
cat << eos > /etc/consul.d/config.json
{
  "server": false,
  "start_join": ["192.168.30.2"],
  "bind_addr": "$MY_VPN_IP",
  "datacenter": "swarm0",
  "ui_dir": "/var/local/consul/webui",
  "data_dir": "/var/local/consul/data",
  "log_level": "INFO",
  "enable_syslog": true
}
EOS

joinするIPは固定で割り当てたswarm-node0のものを指定。
設定が終わったら、自動起動設定と起動を行っておく。

systemctl enable consul
systemctl start consul

メンバー確認

各ノードのConsulが連携できているかを確認する。

$ consul members

Node     Address            Status  Type    Build  Protocol  DC
******  192.168.30.2:8301   alive   server  0.6.1  2         swarm0
******  192.168.30.10:8301   alive   client  0.6.1  2         swarm0
******  192.168.30.11:8301   alive   client  0.6.1  2         swarm0

WebUIで確認する場合、ローカルPCのOS設定などから、swarm-node0へVPN接続を行えば、ブラウザで閲覧できる。

open http://192.168.30.2:8500

各ノードでDockerを動かす

  • 全3ノードに対して、SSHでログインして作業を行う。
  • リモートからDockerを操作するため、TCPの2375ポートを使う。
  • リモートからSwarmマスターを操作するために、TCPの3375ポートを使う。
  • VPNネットワークがあるので、TLS認証は使わない。
  • ただし、vpn_vlan0以外はポートをファイアウォールで閉じておく。

インストール

wget -qO- https://get.docker.com/ | sh

ファイアウォール設定

iptables -A INPUT -i eth0 -p tcp -m tcp --dport 2375 -j DROP
iptables -A INPUT -i eth0 -p tcp -m tcp --dport 3375 -j DROP

DigitalOceanのノードのみでよい。

Dockerデーモン起動時の引数設定

vi /lib/systemd/system/docker.service

# 変更前
ExecStart=/usr/bin/docker daemon -H fd://
# 変更後
ExecStart=/usr/bin/docker daemon \
  -H=0.0.0.0:2375\
  --cluster-store=consul://localhost:8500 \
  --cluster-advertise=vpn_vlan0:2375 \
  -H fd://

変更後は見やすさのため複数行で書いているが、\を消して1行ぶっ続けで記述する。

Docker再起動

service docker restart

各ノードでSwarmコンテナを動かす

各ノードにSSHでログインして、Swarmコンテナを起動させる。

swarm-node0, swarm-local

# Swarm Manager
docker run -d --name=swarm-agent-master \
  -v=/etc/docker:/etc/docker --net=host --restart=always \
  swarm manage -H=0.0.0.0:3375 --replication \
    --strategy=spread --advertise=192.168.30.2:3375 consul://localhost:8500

# Swarm Agent
docker run -d --name=swarm-agent --net=host --restart=always \
  swarm join --advertise=192.168.30.2:2375 consul://localhost:8500

Swarm Managerについては、双方レプリケーションするために--replication引数をつける。

swarm-node1

# Swarm Agent
docker run -d --name=swarm-agent --net=host --restart=always \
  swarm join --advertise=192.168.30.x:2375 consul://localhost:8500

192.168.30.xのところには、swarm-node1がVPNのDHCPに割り当てられたのIPを入れる。

swarm-local

# Swarm Manager
docker run -d --name=swarm-agent-master \
  -v=/etc/docker:/etc/docker --net=host --restart=always \
  swarm manage -H=0.0.0.0:3375 --replication \
    --strategy=spread --advertise=192.168.30.y:3375 consul://localhost:8500

# Swarm Agent
docker run -d --name=swarm-agent --net=host --restart=always \
  swarm join --advertise=192.168.30.y:2375 consul://localhost:8500

192.168.30.yのところには、swarm-localがVPNのDHCPに割り当てられたIPを入れる。

動作確認

ローカルPCのOS設定にて、VPN接続を行う

Macであれば、SoftEther VPNのドキュメントを参考にして、設定を行う。入力値に関しては、この記事の通りにやった場合は以下のようになる。

  • サーバーアドレス: (swarm-node0のグローバルIP)
  • ユーザーID: user
  • パスワード: something
  • 共有シークレット: sharedkey

ローカルPCから、Swarmマスターに接続してみる

ターミナルなどから以下のコマンドを入力して、Swarmクラスタの接続状況を確認してみる。

$ docker -H=192.168.30.2:3375 info

Containers: 4
Images: 4
Role: primary
Strategy: spread
Filters: health, port, dependency, affinity, constraint
Nodes: 3
 swarm-local: 192.168.30.11:2375
  └ Status: Healthy
  └ Containers: 1
  └ Reserved CPUs: 0 / 1
  └ Reserved Memory: 0 B / 4.053 GiB
  └ Labels: executiondriver=native-0.2, kernelversion=4.2.0-25-generic, operatingsystem=Ubuntu 15.10, storagedriver=aufs
 swarm-node0: 192.168.30.2:2375
  └ Status: Healthy
  └ Containers: 2
  └ Reserved CPUs: 0 / 1
  └ Reserved Memory: 0 B / 513.4 MiB
  └ Labels: executiondriver=native-0.2, kernelversion=4.2.0-16-generic, operatingsystem=Ubuntu 15.10, storagedriver=aufs
 swarm-node1: 192.168.30.10:2375
  └ Status: Healthy
  └ Containers: 1
  └ Reserved CPUs: 0 / 1
  └ Reserved Memory: 0 B / 513.4 MiB
  └ Labels: executiondriver=native-0.2, kernelversion=4.2.0-16-generic, operatingsystem=Ubuntu 15.10, storagedriver=aufs
CPUs: 3
Total Memory: 5.055 GiB
Name: swarm-node0

ノードが3つ接続されていれば、クラウドとローカルを跨いだSwarmクラスタの構築に成功している状態になる。

ちなみに、-H=192.168.30.2:3375の部分は、環境変数DOCKER_HOST192.168.30.2:3375と設定しておけば省略できる。

各ノードにコンテナを置いてみる

オーバーレイネットワークの作成
export DOCKER_HOST="192.168.30.2:3375"
docker network create testnet

Swarmマスターへの接続であれば、デフォルトでドライバがoverlayに設定される。

docker-compose.yml
nginx:
  image: nginx
  net: testnet
  ports:
    - "8080:80"

portsを設定しておけば、コンフリクトを防ぐため、コンテナ配置が自然とバラける。

docker-compose up & scale
docker-compose up -d
docker-compose scale nginx=3
コンテナ配置確認
$ docker ps --format "{{.Names}}"

swarm-node0/****_nginx_3
swarm-node1/****_nginx_2
swarm-local/****_nginx_1

ちゃんとすべてのホストにコンテナが配置できることを確認。http://192.168.30.x:8080/各々をブラウザで打ちこめば、nginxのデフォルトページが表示されるはず。

あとは、コンテナの中にdocker execで入って、/etc/hostsの中を見れば、他のコンテナのIPが書いてあるはずなので、pingを打ってみたりで、オーバーレイネットワークの疎通を確認できる。


  1. ZeppelinはWeb上からSparkの操作をインタラクティブに行えたり、結果をビジュアライズできたりする、ノートブック系Webアプリ [return]
  2. TCP/IPベースのVPNで、動かしてみたら割とすんなり動作したので、これを使ってみた。PPTPdも試したが、Ubuntu15.10で上手く動作しなかったので、見送り [return]
comments powered by Disqus

この記事について

書いた人
Written by

namikingsoft

何かを残して逝きたい
フロントエンドエンジニア