地理的に分散したクラスターのエンジンとしてのDocker Swarm + Consul + Gobetween

前文



しばらく前に、ビデオをストリーミングするためのシステムを設計および展開するという課題に直面しました。 一番下の行は、ビデオストリームが再構築され、多くのメディアcdnプロバイダー(youtube、livestream、ustreamなど)および独自のrtmpおよびts宛先ポイントにストリーミングされるインスタンスの一括開始/停止でした。 各インスタンスは、開始する前に設定が必要でした 各クライアントに固有の情報が含まれていました。 また、システムが多数のリージョンで動作することも明らかでした(少なくともAmazonが存在するすべてのポイントで、最大で-サーバーをレンタルできる場所であればどこでも)。 また、主な要件は、最大で1〜2秒以内にインスタンスを起動することです。これにより、ユーザーに対して透過的になります。









2015年の初めに、Dockerがすぐに独自のクラスタリングシステムをリリースするという話が既にありました。 そして、2015年2月26日に彼女は出てきました。 これは私たちにとって特効薬であり、Swarmがプロジェクトに理想的に適合することがすぐに明らかになりました。 このプロジェクトは2015年5月に開始されました。







11か月後、2番目のより複雑なプロジェクトを開始しました。このプロジェクトでは、サードパーティメーカーの機器の静的エントリポイント(ip:ポート)を整理する必要がありました。







設計プロセスとその後の開発では、非標準のDocker swarmを使用しました。 そして、インフラストラクチャを動的に管理および調整する方法のアイデアが生まれました。 また、この旅の一環としてGobetweenプロジェクトが登場しました。これは、将来インフラを柔軟に管理し、簡単に複製できるようにするためのものです。







挑戦する



この記事では、分散システム構築の考え方と基本的な実装を共有したいと思います。

セキュリティの側面は特に省略します-ここでは、誰がどのように/何を構築するのが良いかを決定します。 一部はパスワード/証明書などで閉じられ、一部はパブリックネットワークと直接交差しないネットワークの管理部分を取り出します(vpc内に存在し、トンネルを介して接続されます)。







Standalone Swarmのdockerクラスターの概略図。







基本的にシステムを考慮してください-多数の領域として、1つのコア(uropavlyayuschey)として。







画像

この場合、1つのマスターと、consulクラスター内でアナウンスする多数のdockerホストを持つシステムを検討します。 なぜそう はい、実際には、Manageはそれ自体では値を表さないため、失うことを望まないデータは含まれていません。 実際には設定もありません。Consulクラスターから取得するdockerノードのリストのタスクスケジューラです。 さらに、そのようなインスタンスのインスタンスをいくつでも作成できますが、意味はありません。その理由を以下で説明します。







このシステムの最大の利点は、すべてが透過的で管理しやすく、スケーラブルであることです。 それでは、クラスタコンソールを設定することから始めましょう。







Consul Clusterのインストールと構成



出力では、このようなクラスター構造を取得します。

自動ウィザード選択によるRAFTの3つのノード。







sever1.consul.example.com 10.0.0.11 bootstrap consul server, consul agent sever2.consul.example.com 10.0.0.12 consul server, consul agent sever3.consul.example.com 10.0.0.13 consul server, consul agent
      
      





まず、サーバー(N).consul.example.comのConsulを削除します。







https://consul.ioから領事をダウンロードします

コンテナなし、UPSTARTの下の領事。







 $wget https://releases.hashicorp.com/consul/0.6.4/consul_0.6.4_linux_amd64.zip $unzip *.zip $mv consul /usr/sbin/consul
      
      





Consulが機能していることを確認します。







 $consul --version Consul v0.6.4 Consul Protocol: 3 (Understands back to: 1)
      
      





3つのサーバーすべてにインストールした後、初期ブート用にクラスターを準備します。







クラスタトークンを生成します。







 $consul keygen ozgffIYeX6owI0215KWR5Q==
      
      





Consulを起動するユーザーを作成します。







 $adduser consul
      
      





3つのサーバーすべてに必要なディレクトリを作成します。







 $mkdir -p /etc/consul.d/{bootstrap,server,client}
      
      





Consulデータを保存するディレクトリを作成します。







 $mkdir /var/consul $chown consul:consul /var/consul
      
      





クラスターの初期起動に使用するノード(sever1.consul.example.com)で、ブートストラップ構成を作成する必要があります。







 $vim /etc/consul.d/bootstrap/config.json
      
      





 { "bootstrap": true, "server": true, "datacenter": "production", "data_dir": "/var/consul", "encrypt": "", "log_level": "INFO", "encrypt":ozgffIYeX6owI0215KWR5Q==, "enable_syslog": true }
      
      





3つのサーバーすべてで 、サーバー構成/etc/consul.d/server/config.jsonを作成する必要があります。







サーバー1:







 { "bootstrap": false, "server": true, "datacenter": "production", "data_dir": "/var/consul", "encrypt": "ozgffIYeX6owI0215KWR5Q==", "log_level": "INFO", "enable_syslog": true, "start_join": ["10.0.0.12", "10.0.0.13"] }
      
      





Server2







 { "bootstrap": false, "server": true, "datacenter": "production", "data_dir": "/var/consul", "encrypt": "ozgffIYeX6owI0215KWR5Q==", "log_level": "INFO", "enable_syslog": true, "start_join": ["10.0.0.11", "10.0.0.13"] }
      
      





Server3:







 { "bootstrap": false, "server": true, "datacenter": "production", "data_dir": "/var/consul", "encrypt": "ozgffIYeX6owI0215KWR5Q==", "log_level": "INFO", "enable_syslog": true, "start_join": ["10.0.0.11", "10.0.0.12"] }
      
      





次に、すべてのサーバーでConsulのUPSTARTスクリプトを作成します:/etc/init/consul.conf:







 description "Consul server process" start on (local-filesystems and net-device-up IFACE=eth0) stop on runlevel [!12345] respawn setuid consul setgid consul exec consul agent -config-dir /etc/consul.d/server
      
      





初期クラスター起動



クラスターを初期化します(初期起動):sever1.consul.example.comで:







 #consul agent -config-dir /etc/consul.d/bootstrap -bind="IP_ADDRESS"
      
      





サービスが開始され、端末がキャプチャされます。 ブートストラップモードでは、サーバー自体がマスターとして自身を指定し、必要なすべてのデータ構造を初期化します。







他の2台のサーバー(sever2.consul.example.com、sever3.consul.example.com)は、単にサーバーモードでconsulを実行します。







 #start consul
      
      





これらのサーバーは、ブートストラップサーバーに接続します。 現時点では、3台のサーバーのクラスターがあり、そのうち2台は通常のサーバーモードで動作し、もう1台は初期化モードで動作します。つまり、他のサーバーに問い合わせることなくデータ配信を決定します。







これで、ブートストラップサーバーを停止し、標準サーバーモードでコンソールを再起動できます。







ブートストラップサーバーで、次をクリックします。







CTRL-C

領事は停止し、標準サーバーモードで再起動します。







sever1.consul.example.com:







 #consul start
      
      





次のコマンドを使用して、すべてがうまくいったことを確認します。







 #consul members -rpc-addr=10.0.0.11:8400
      
      





結論は次のとおりです。







 Node Address Status Type Build Protocol DC server1.consul.example.com 10.0.0.11:8301 alive server 0.6.4 2 production server2.consul.example.com 10.0.0.12:8301 alive server 0.6.4 2 production server3.consul.example.com 10.0.0.13:8301 alive server 0.6.4 2 production
      
      





これで、既製の効率的な領事クラスターができました。







フロントエンドクラスタ領事としてのロードバランサー



この場合、ロードバランサーを使用し、consulクラスターへのすべてのリクエストはロードバランサーを通過します。 Amazonの場合、リージョンは言うまでもなく、異なるVPCからのconsulエージェントの使用には、多くの未解決の問題があります(起動時に指定された外部IPの代わりに内部IPを発表します。同期を構成する-この段階での私の観点からは、合理的ではありません。







Consul Agentをインストールする



同様に、バランサーであるConsulが配置されるサーバーにダウンロードしてインストールします。

次に、エージェント構成を作成します。







 $vim /etc/consul.d/server/config.json
      
      





 { "server": false, "datacenter": "production", "data_dir": "/var/consul", "ui_dir": "/home/consul/dist", "encrypt": "ozgffIYeX6owI0215KWR5Q==", "log_level": "INFO", "enable_syslog": true, "start_join": ["10.0.0.11", "10.0.0.12", "10.0.0.13"] }
      
      





エージェントの新興企業を作成します。







 description "Consul server process" start on (local-filesystems and net-device-up IFACE=eth0) stop on runlevel [!12345] respawn setuid consul setgid consul exec consul agent -config-dir /etc/consul.d/agent
      
      





エージェントを開始します。







 $start consul
      
      





何が起こったかを確認してください:







 $consul members -rpc-addr=10.0.0.11:8400
      
      





開始後、出力は次のようになります。







 Node Address Status Type Build Protocol DC server1.consul.example.com 10.0.0.11:8301 alive server 0.6.4 2 production server2.consul.example.com 10.0.0.12:8301 alive server 0.6.4 2 production server3.consul.example.com 10.0.0.13:8301 alive server 0.6.4 2 production lb.consul.example.com 10.0.0.1:8301 alive client 0.6.4 2 production
      
      





Gobetweenをバランサーとして構成します。



前の記事で、LBの機能の一部について説明しました。

この場合、ローカルにインストールされたConsulエージェントと一緒にEXECディスカバリーを使用するのが最も簡単です-これにより、内部からconsulクラスターを確認できます(結局、将来的にノードの一部を追加/削除し、額を再構成する必要がないことが判明する可能性があります)。







最新のリリースをダウンロードします(バージョンは後で変更される可能性がありますので、しばらくお待ちください):







 $wget https://github.com/yyyar/gobetween/releases/download/0.3.0/gobetween_0.3.0_linux_amd64.tar.gz $tar -xvf gobetween_0.3.0_linux_amd64.tar.gz $cp gobetween_0.3.0_linux_amd64/gobetween /usr/sbin
      
      





それでは、利用可能なconsulバックエンドサーバーのリストを決定するスクリプトを作成しましょう。これは、確認済みで、consulに登録され、CONSULサービスによってマークされたサーバーのリストを返します。







構成およびスクリプト回復用のディレクトリを作成します







 $mkdir /etc/gobetween/
      
      





検出スクリプトを作成します。







 $vim /etc/gobetween/consul_node_discovery.sh
      
      





そのようなコンテンツ:







 #!/bin/bash curl -Ss http://0.0.0.0:8500/v1/catalog/service/consul |jq '.[] | .Address' |sed 's/"//g'| sed 's/$/:8500/'
      
      





スクリプト出力を手動で実行する場合、次のようになります。







 10.0.0.11:8500 10.0.0.12:8500 10.0.0.13:8500
      
      





稼働中のサーバーのリストを取得するこのタイプでは、helchecksを使用しません-これをクラスター自体の領事に任せます。 Gobetween自体を構成します。







 $vim /etc/gobetween/gobetween.toml
      
      





 [logging] level = "info" output = "stdout" [api] enabled = true bind = ":8888" [api.basic_auth] login = "admin" password = "admin" [defaults] max_connections = 0 client_idle_timeout = "0" backend_idle_timeout = "0" backend_connection_timeout = "0" [servers.consul] bind = "10.0.0.1:8500" protocol = "tcp" balance = "iphash" max_connections = 0 client_idle_timeout = "10m" backend_idle_timeout = "10m" backend_connection_timeout = "5s" [servers.consul.access] default = "deny" rules = [ "allow 99.99.99.1/24", # region1 docker nodes ip`s pool "allow 199.199.199.1/24", # region 2 docker nodes pool "allow 200.200.200.200/32", #mage node "allow 99.99.98.1/32 " #region-1 load balancer ] [servers.consul.discovery] failpolicy = "keeplast" interval = "10s" timeout = "5s" kind = "exec" exec_command = ["/etc/gobetween/consul_node_discovery.sh"]
      
      





upstartスクリプト/etc/init/gbetween.confを作成します。







 $vim /etc/init/gbetween.conf
      
      





 # gobetween service description "gobetween" env DAEMON=/usr/sbin/gobetween env NAME=gobetween env CONFIG_PATH=/etc/gobetween/gobetween.toml export GOMAXPROCS=`nproc` start on runlevel [2345] stop on runlevel [!2345] kill signal INT respawn respawn limit 10 5 umask 022 expect stop respawn script exec $DAEMON -c $CONFIG_PATH 2>&1 end script post-stop script pid=`pidof gobetween` kill -9 $pid end script
      
      





そしてバランサーを実行します:







 $start gobetween
      
      





これでクラスターができました。http:// lb_ip:8888 / servers / consulに移動し、consulサーバーのリストが正常に決定されたことを確認できます。







Dockerノードのインストール(Dockerを備えたサーバー)



ノードは2つのサブネットに存在します。







 99.99.99.1/24 - region 1 199.199.199.1/24 -region 2
      
      





Amazonの外部ELASTIC IP-50.50.50.50







Docker



そのため、サーバーにdockerをインストールする手順を繰り返す必要はありません。 ここで読むことができます 。 特定の問題にのみ焦点を当てます。 また、このガイドはUbuntuの12ブランチと14ブランチで機能します。Ubuntu16でも同じdockerデーモンの設定が必要ですが、それらの設定は少し異なります。







まず、Dockerデーモンを構成して起動します。







dockerデーモンの初期化行を編集します。







 vim /etc/default/docker
      
      





コメントするために、以下の行、残りの行を追加する必要があります。







 DOCKER_OPTS="-H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock --label region=region1 "
      
      





次に、Dockerを再起動します。







 $service docker restart
      
      





その後、必要な設定でdockerデーモンが起動したことを確認する必要があります。







 $ps ax |grep docker
      
      





次のようなものが表示されるはずです。







 10174 ? Ssl 264:59 /usr/bin/docker daemon -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock --label region=region
      
      





Swarm join



Dockerが既にインストールされているサーバーにDocker Swarmをインストールします。 私はDocker Swarmをコンテナで使用しません。すべてが透明で、必要に応じて正確に初期化して、シンプルなアップスタートを使用して制御できるときが好きです。







したがって、swormでバイナリファイルを取得する最も簡単な方法は、そのイメージをローカルマシンにダウンロードし、イメージを解凍して、可能な限り解凍することです。 私は自分でそれを収集することを好みます。 読者に任せる最高の方法を決めてください。







既にDocker Swarmのバイナリがあり、それをdockerでカスタムサーバーにコピーしたと想像してください。 次は、upstartスクリプトを記述してSwarmを構成するだけです。







 $vim /etc/init/swarm.conf ```upstart description "Consul server process" start on (local-filesystems and net-device-up IFACE=eth0) stop on runlevel [!12345] respawn setuid root setgid root exec bash -c 'swarm join --ttl 20s --heartbeat 4s --advertise=SERVER_IP:2375 consul://50.50.50.50:8500/swarmcluster/ '
      
      





SERVER_IPは、自分で簡単に抽出できます。Ansibleを使用してAmazonでサーバーを作成するときにElastic IPに置き換えて挿入されます。 これはまさに、SWARM ManageがこのDockerホストに接続するIPです。







次に、群れ結合を開始します。







 start swarm
      
      





次のようなリクエストで確認することが可能です







 $curl -Ss http://50.50.50.50:8500/v1/kv/swarmcluster/docker/swarm/nodes/?keys&seperator=/&dc=production
      
      





そして答えを得る必要があります:







 $ ["swarmcluster/docker/swarm/nodes/SERVER_IP:2375"]
      
      





また、クラスターを任意の数のリージョンにスケーリングします。 各地域のドッカーデーモンラベル(ラベル)の設定​​を変更します。 各サーバーに複数のラベルを追加することもできます(サーバーをリージョンに分割すると便利です。たとえば、プロセッサのパフォーマンス、メモリサイズ、ディスクの種類など)。







Swarm Manageをインストールする



そこで、フィードマネージャーのインストールに直接進みます。 実際、Swarm Joinをインストールすることと大差ありません。







もう一度、手順を繰り返してバイナリをサーバーにコピーしてから、UPSTARTスクリプトを作成します。







 $vim /etc/init/swarm.conf
      
      





 description "Consul server process" start on (local-filesystems and net-device-up IFACE=eth0) stop on runlevel [!12345] respawn setuid root setgid root exec bash -c 'swarm manage -H tcp://$0.0.0.0:2377 -strategy "binpack" consul://50.50.50.50:8500/swarmcluster/'
      
      





SWARM_MANAGE_IPは、管理のIPです。 この例では、200.200.200.200です。 -strategyを詳しく見てみましょう。このオプションは、サンプルのすべてのパラメーターに対応するノードによるコンテナーの分布を決定します。 binpack戦略では、最初のノードが最初にコンテナで満たされ、次に2番目のノードだけが満たされます。 1時間に何百ものコンテナの起動/停止がある場合、これにより断片化が回避され、クラスターから不要なノードを削除できます。







コンテナ配布戦略には3つのタイプがあります。







spread-最小負荷ノードへの配布

binpack-最も高密度のコンテナ包装

ランダム-何も言うことはありません-すべてが明確です:)それはデバッグのためにのみ使用されます。







最後にswarm manageを実行します:







 $service swarm start
      
      





そして何が起こったのかを確認してください:







 $docker -H 0.0.0.0:2377 info
      
      





次のようなものを取得します:







 Containers: 1 Images: 3 Server Version: swarm/1.2.4 Role: primary Strategy: binpack Filters: health, port, dependency, affinity, constraint Nodes: 3 host1: 99.99.99.1:2375 └ Status: Healthy └ Containers: 0 └ Reserved CPUs: 0 / 8 └ Reserved Memory: 0 B / 16.46 GiB └ Labels: executiondriver=, kernelversion=3.13.0-86-generic, operatingsystem=Ubuntu 14.04.4 LTS, region=region-1, storagedriver=devicemapper └ Error: (none) └ UpdatedAt: 2016-08-21T14:40:03Z host3: 99.99.99.2:2375 └ Status: Healthy └ Containers: 0 └ Reserved CPUs: 0 / 2 └ Reserved Memory: 0 B / 3.86 GiB └ Labels: executiondriver=native-0.2, kernelversion=3.13.0-74-generic, operatingsystem=Ubuntu 14.04.3 LTS, region=region-1, storagedriver=devicemapper └ Error: (none) └ UpdatedAt: 2016-08-21T14:40:42Z host3: 199.199.199.1:2375 └ Status: Healthy └ Containers: 1 └ Reserved CPUs: 0 / 2 └ Reserved Memory: 512 MiB / 3.86 GiB └ Labels: executiondriver=native-0.2, kernelversion=3.13.0-74-generic, operatingsystem=Ubuntu 14.04.3 LTS, region=region-2, storagedriver=devicemapper └ Error: (none) └ UpdatedAt: 2016-08-21T14:40:55Z Kernel Version: 3.13.0-44-generic Operating System: linux CPUs: 12 Total Memory: 24.18 GiB Name: lb.ourcoolcluster.com
      
      





実際、既にコンテナを実行できます:







 $docker tcp://0.0.0.0:2377 run -d -P -e constraint:region==region-1 hello-world
      
      





フィルターとポリシーの詳細については、 こちらをご覧ください







そのため、地域やその他のデータに応じてコンテナを実行できるクラスターがあります。 次は? -各地域のエントリポイントを整理してください。







地域ごとにエンターポイントを作成する



次に、Sandalone Swarmの内部メカニズムのみでディスカバリサービスを構築してみましょう。 コンテナを起動するときにラベルを使用します。 手作業で行うか、必要なコンテナを起動すると同時に独自のエンジンを記述して、残りのAPIを使用してBalancerを操作できます。 このスキームでは、LBは以前に存在していなかった地域で新しいサービスの開始時に一度設定されます。 その後、負荷の増加に応じてサービスレプリカを安全に開始し、コンテナが落ちたときに停止することができます。バランサはサービスを提供するノードのリストの検出を行います。







また、必要に応じて、地域の領事のレプリカとSwarm管理のレプリカを簡単に保持できます。 しかし、最も単純なスキームを検討します。







すべてがどのように機能するかの一般的なスキーム:







画像

Gobetweenのインストールは省略し、 起動時に使用する構成のみを指定します。







 [logging] level = "info" output = "stdout" [api] enabled = true bind = ":8888" [api.basic_auth] login = "admin" password = "admin" [defaults] max_connections = 0 client_idle_timeout = "0" backend_idle_timeout = "0" backend_connection_timeout = "0"
      
      





これは、バランサーを開始するのに十分です。 残りのAPIを使用して後続のすべての手順を実行します-通常、特別なサービスがこれに関与します。 また、テストを簡素化するために、各Dockerサーバーでファイル/ tmp / testを作成し、そこで各サーバーに固有の情報を書き留めます。 たとえば、「host1」および「host2」







たとえば、コンテナを実行します。







 $docker run -l service=region-1.nginx -d -p 22001:80 -e constraint:region==region-1 -v /tmp/test:/usr/share/nginx/html:ro nginx $docker run -l service=region-1.nginx -d -p 22001:80 -e constraint:region==region-1 -v /tmp/test:/usr/share/nginx/html:ro nginx
      
      





リージョンregion-1に2つ以上のノードがある場合、コンテナーが起動します(Docker Swarmはマッピング用のポートの可用性をチェックします)。 リージョン内の1つのドッカーノードの場合、次の方法で2つのコンテナーを実行できます。







 $docker run -l service=region-1.nginx -d -p 22002:80 -e constraint:region==region-1 -v /tmp/test:/usr/share/nginx/html:ro nginx $docker run -l service=region-1.nginx -d -p 22001:80 -e constraint:region==region-1 -v /tmp/test:/usr/share/nginx/html:ro nginx
      
      





コンテナは適切な地域で開始され、機能しています。 次に、バランサーをセットアップします。







 $curl --user admin:admin -XPOST "http://50.50.50.50:8888/servers/r1nginx" --data ' { "bind":"LB_IP:LB_PORT", "protocol": "tcp", "balance": "weight", "max_connections": "0", "client_idle_timeout": "10m", "backend_idle_timeout": "10m", "backend_connection_timeout": "1m" "healthcheck": { "kind": "ping", "interval": "2s", "timeout": "1s" }, "discovery": { "kind": "docker", "docker_endpoint" : "http://50.50.50.50:2377", "docker_container_private_port" : "80", "docker_container_label":"service=region-1.nginx" } } '
      
      





どこで:

LB_IP-バランサーが実行されているサーバー上の検証者に向かっているIPアドレス。

LB_PORT-バランサーが実行されているサーバー上のサーバーに向かっているLB_IPのTCPポート。







これで、何が起こったかを確認できます。







 $curl -sS http://LB_IP:LB_PORT host1 $curl -sS http://LB_IP:LB_PORT host2
      
      





そのため、Docker Swarmスタンドアロンで地理的に分散したクラスターを構築する、最も単純であると同時に非常に機能的な方法の1つを検討しました。 トラブルシューティングと同様に、インストールは非常にシンプルで透過的です。 この記事を読んで、このタイプのシステムの構築と運用の多くの側面をカバーできないことに気づき、読者に問題を別の角度から強制するという目標を追求しました-必要な充足度と禁欲主義。簡単に行うのは難しいが、簡単で調和のとれた難しい。







セキュリティとHAの問題は読者の裁量に任せました。おそらく興味があるなら、それらを以降の記事でカバーしようと思います。










All Articles