まだプログラマーですが何か?

プログラマーネタとアスリートネタ中心。たまに作成したウェブサービス関連の話も http://twitter.com/dotnsf

タグ:swarm

この記事の続きです:
"Play with Docker" で遊ぶ(1)

前回は Play with Docker(以下 "PwD")というサービスを使うことで Docker をインストールすることなく、ブラウザだけで docker コマンドを体験できることを紹介しました。今回はこのサービスの標準機能として提供されている Docker Swarm を使ったクラスタリング環境構築の手順を確認してみます。

Docker Swarm は Docker に対応したネイティブ&軽量なクラスタリング用のツールです。Docker の標準機能の一部として提供されているので、Kubernetes のように Docker 導入の後から追加でインストールする必要なく(Swarm モードに切り替えるだけで)利用できます。

では早速 PwD で Docker Swarm を使ってみましょう。まずは前回同様、Docker Hub のアカウントで PwD にログインします:
2019041901


PwD のダッシュボード画面が表示されます。まずは前回同様に "+ ADD NEW INSTANCE" をクリックして Docker インスタンスを追加します:
2019042101


"node1" という名称の Docker ノードインスタンスが追加され、操作するためのコンソールも表示されました。ここまでは前回と同じです:
2019042102


今回はクラスタリング環境を構築したいので、更に2つの Docker ノードインスタンスを追加します(合計3つにします)。あともう2回 "+ ADD NEW INSTANCE" をクリックして、"node2" と "node3" を追加します。こんな感じで Docker ノードを気軽に増やせるのは本当に便利です。。
2019042103


この時点で3つの Docker が(独立した状態で)準備できました。ではこの3つの Docker ノードを使ってクラスタリング環境を構築します。今回は node1 を管理ノード、node2 と node3 をワーカーノードとするクラスタリング環境とします。

まずは管理ノードを準備します。node1 を選択して、node1 のコンソールに以下のコマンドを入力します:
$ docker swarm init --advertise-addr eth0

2019042104


成功すると出力結果に
$ docker swarm join --token XXXXXX...
という内容が表示されます。これはワーカーノードとなるノードで入力すべきコマンドです。これで管理ノードの準備はできました。


では次にワーカーノードをこの管理ノードに接続します。先程の管理ノードでのコマンド出力結果の "docker swarm join --token XXXXXX..." 部分をそのまま node2 のコンソールにコピー&ペーストして実行します:
2019042105


"This node joined a swarm as a worker." というメッセージが表示されれば正しくワーカーノードとしてクラスタリングに追加し、初期化できたことになります。

同様に node3 でも同じコマンドを実行して、ワーカーノードとしてクラスタリングに追加します:
2019042106


これで3つの Docker インスタンスノードによるクラスタリング環境が構築できているはずです。念の為、node1 ノードで以下のコマンドを実行します:
$ docker node ls

実行結果を見ると、node1, node2, node3 の3つのノードがクラスタリングを構成している様子が表示され、かつ node1 が "Leader" (管理ノード)と表示されているはずです:2019042107



では、このクラスタリング環境でアプリケーションを実行してみます。なお原則的には以下のコマンドはすべて管理ノードである node1 ノードのコンソールを使います。

改めて node1 ノードで nginx のイメージを使って 80 番ポートで、1インスタンスで、"web" という名前でサービスを作成します:
$ docker service create --replicas 1 --name web -p 80:80 nginx

初回はイメージのダウンロードもあるので少し時間がかかりますが、以下のようなメッセージが表示され、正しくサービスが作成されます:
2019042108


動作確認する前に、この時点でサービスの様子を確認します。node1 ノードで以下のコマンドを実行します:
$ docker service ls

"web" というサービスがレプリカ1つで定義され、そのうちの1つが動いている形で 80 番ポートで作成されている様子が確認できます:
2019042109


実際に動作確認してみます。80 番ポートで公開されているので、curl コマンドを使って以下のコマンドを実行します(このコマンドは node2 や node3 で実行しても確認できます):
$ curl http://localhost/

Nginx 標準のトップページの HTML が表示されます。正しくサービスが動いている様子が確認できました:
2019042110


では、現在1インスタンスで稼働しているこのサービスを複数インスタンスとなるように node1 でスケールアウトさせてみます:
$ docker service scale web=3

3インスタンスで稼働するように指定しました。少し待ちますが、3つのインスタンスが "running" となることを確認します:
2019042101


改めてnode1 でレプリカ数を確認します:
$ docker service ls

結果、以下のように3つのレプリカが定義されており、そのうちの3つが動いている様子が確認できます:
2019042102


最後に作成したサービス(web)を削除する方法を紹介しておきます。node1 で以下のコマンドを実行します:
$ docker service rm web

これで web サービスはクラスタリング環境から削除されました:
2019042103



PwD 環境を使ったクラスタリング環境構築と、簡単なサービス作成&スケールアウト&削除といった手順を紹介しました。 通常、同じことを行うには3台のマシンを用意した上で、それぞれに Docker をインストールしておかないとこのような動作確認はできないのですが、PwD を使うと Docker 導入の手順を省略することができる上、全ての操作を1つのブラウザ画面上で行うことができるという点が勉強しやすいと感じました。




"Play with Docker" (以下、"PwD")というサービスを使ってみました:
https://labs.play-with-docker.com/


2019041901


一言でいうと「ブラウザだけで docker コマンドを体験できるサービス」です。普通、Docker を使うには(当然ですが)Docker 環境が必要です。自分でインストールすることもできますし、各種クラウドサービスで提供されているものを使ってもいいです。自分でインストールせずにクラウドサービスの Docker サービスを利用する場合は別途手元のマシンに docker クライアントを導入&セットアップして使うことになります。一度セットアップしてしまえばいい話ではありますが、慣れてなかったり「そもそも Docker って何?」という人がこれから勉強するために用意することを考えると「ちと面倒」です。

この PwD は、そんな Docker のセットアップ手順をすっ飛ばして Docker を使うことができるサービスです。具体的には Docker Hub のアカウントを取得し、そのアカウントでログインするとブラウザ上で docker 他の各種コマンドが使えるターミナルが提供され、裏で動いている Docker エンジンや、オーケストレーション環境である Docker Swarm エンジンを利用することができる、というものです。

利用するにはブラウザで PwD サイトへアクセスし、"Login" と書かれたボタンをクリックして Docker Hub のアカウントでログインします(所有していない場合はここで作成してログイン):
2019041901


ログイン後に改めて同ページを参照し、今度は "Start" ボタンをクリックしてサービス開始です:
2019041902


PwD サービス画面に切り替わります。画面左上にこのセッションで使える残り時間が表示されています。カウントダウンも始まっていますが、約4時間使える、ということみたいです。この時点ではまだdocker インスタンスが1つも起動していません。まずは1つ使ってみましょう。"+ ADD NEW INSTANCE" と書かれた箇所をクリックします:
2019041903


"node1" という名称のインスタンスが追加されました。同時に画面右にコンソールが現れて、プロンプト状態になります。すでに docker エンジンは起動している状態になっていて、ここから docker コマンドなどを実行することができる状態になってます。簡単すぎる・・・:
2019041904


試しに "$ docker version" を実行してみました。サーバー側もちゃんと動いてそうです:
2019041905


では深く考えずに nginx のイメージからコンテナを1つ起動してみます:
$ docker pull nginx

$ docker run -d -p 80:80 nginx


そして起動中であろう nginx にアクセスしてみます:
$ curl http://localhost/

<!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>

  :

</html>

普通に使えてますね、これはすごい!

ほぼ準備することなく Docker コマンドが使えて、コンテナの実行もできて、勉強目的であればとてもいい環境だと思いました。

次回はこの PwD で(使えることになっている)オーケストレーション環境である Docker Swarm を使ってみる予定です。




きっかけはこのツイートでも紹介した、豊島屋の鳩サブレ缶がちょうどラズベリーパイを4つ格納できるような仕切りになっていることに気付いたことでした:
20190117


面白そうなので、本当にラズパイのケースにしてみよう、それもせっかくなのでこの鳩サブレ缶の中だけでクラスタリング環境を作ることに挑戦してみよう、と思いたちました。ちなみに私自身はクラスタ化とか専門ではなく、あまり得意な方ではないです。思いついた時点では構築方法として Docker Swarm か、Kubernetes か、まあその辺りが動けばいいな、くらいに考えていました。。

当初は「さてとりあえずラズパイを4台買って・・」と思っていましたが、冷静に考えるとスイッチングハブとか、USBハブとか、電源周りとか、ラズパイ本体以外でもそこそこのサイズの機器を収納する必要がありそうだと気づきました。というわけで、それらをどこか1つにまとめるとして、ラズパイ本体は3台体制で行けるかな・・・ という想定でお買い物しました:
IMG_3782


購入したモノリスト:
買ったモノ個数目的
Raspberry Pi 3 Model B3クラスタノードになるホスト
マイクロ USB ケーブル上記ラズパイの電源ケーブル
マイクロSDカード(64GB)上記ラズパイのストレージ
ラズパイケースプラスチックの仕切りの上にラズパイ本体を直接載せるのは熱的にまずいかと思ったので・・・
LANケーブル3台のラズパイを同一ネットワークに接続(結局、今回は使わず※)
スイッチングハブ1LANケーブルのハブ(結局、今回は使わず※)
USB ハブ電源ケーブルのハブ(結局、今回は使わず※)


※今回は結局無線 LAN で接続することにしたので有線の LAN ケーブルとハブを使わずに構築しました。また USB ハブは購入したのですが、この USB ハブ1つに3台のラズパイを接続すると電流量が足りませんでした。というわけで別途 USB からの電源変換プラグ3つと、テーブルタップ1つを買い足しています。

上記に含まれていないものとして、マイクロ SD カードにイメージを書き込むための Windows PC と、マイクロ SD カードを読み書きするための USB アダプタ、USB -> 電源プラグの変換アダプタ、各ホストでネットワークが有効になるまでの操作用に LCD モニタ、HDMI ケーブル、USB キーボードと USB マウスを使います(これらは所有していたものをそのまま使います)。加えてケースとなる 18 枚入りの鳩サブレー缶を用意しました。

また構築する環境は最近流行り(遅い?)の Kubernetes 、にしたかったのですが、訳あって Docker Swarm にしました。コンテナ・オーケストレーションとしてはほぼデファクトスタンダートになった Kubernetes を選びたかったのですが、とりあえず 2019/01/21 時点ではマスターノードを初期化する kubeadm init コマンドを実行した際に再現率 100% で Timeout エラーになってしまうようでした。


どうもこの Issue に近い現象だと思っています:
2019012101


ただ Issue そのものは Closed になっていて、でもスレッドを見ていると本当に治っているのかなんとも微妙・・ 一方で成功例も報告されているのでラズパイで発生する環境依存問題なのか、タイミングが悪かったのか・・・ なんとも言えないのですが、仮に Kubernetes の障害であったとしてもその修正を待つのも変なので、Docker Swarm によるクラスタリング環境構築を優先することに決めました。

というわけで、改めて3台のラズパイを使った Docker Swarm 環境の構築を目標に作業を進めてみます:
2019012200



【構成図】
このような最終型を目指すことにします:
2018012201

raspi001, raspi002, raspi003 の3台のラズベリーパイを Docker Swarm でクラスタリングします。raspi001 を管理ノード、raspi002 と raspi003 をワーカーノードとします。



【ラズパイノードの準備】
Docker Swarm 環境の核となる3台のノードをラズパイで作ります。前提として最新版の Raspbian OS をマイクロ SD カードに書き込んで起動し、初回起動時のセットアップは3台ぶん済んでいるものとします。また SSH も有効にしておいてください。そして以下の手順を3台それぞれに対して行います。

【ホスト名の変更】
今回は3台のラズパイに raspi001, raspi002, raspi003 という3つの名前をそれぞれ付与して使いますこのため、まずは各ラズパイのホスト名をそれぞれ変更します。
$ sudo vi /etc/hostname

raspberrypiraspi001  raspberrypi をホスト名に変更して保存


また raspi001, raspi002, raspi003 の名前でアクセスできるよう、/etc/hosts も変更しておきます(DNS などが有効であればそちらで対応いただくのがいいと思います):
$ sudo vi /etc/hosts

127.0.1.1       raspberrypiraspi001  raspberrypi をホスト名に変更

192.168.10.101  raspi001
192.168.10.102  raspi002
192.168.10.103  raspi003   raspi00X の名前でアクセスできるよう IP アドレスを指定して追加し保存

【SSH 鍵の共有】
この3台のラズパイ間ではパスワードなしで SSH 接続ができるように SSH 鍵を共有しておきます:
$ ssh-keygen -t rsa
$ ssh-copy-id raspi001
$ ssh-copy-id raspi002
$ ssh-copy-id raspi003

【Docker のインストール】
こちらで紹介した手順でラズパイに Docker をインストールします:
$ curl -sSL https://get.docker.com | sh

$ sudo usermod -aG docker pi

(このコマンドの後、一度ログアウトして再ログインする)

以上、ここまでの手順は3台のラズパイ全てで共通に行っておく必要があります。ちと面倒ですが、全てのラズパイに Docker をインストールするところまで実行しておきます。


【管理ノードの作成】
ここからは3台のラズパイの役割ごとに作業が変わる部分になります。まずは管理ノードを作成します。raspi001 において、自分のアドレスを指定して以下のコマンドを実行します:
$ docker swarm init --advertise-addr 192.168.10.101

Swarm initialized: current node (xenxp2w34x5dhvtdmq0ndwolk) is now a manager.

To add a worker to this swarm, run the following command:

    docker swarm join --token SWMTKN-1-0dg4im1kvb41odjj9zmhwug0bcvg6n8tt0d8548jxho5cp8cvo-df89pmegbdf4a3vy37apftv8r 192.168.10.101:2377

To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.


↑コマンドが成功すると青字のような出力になります。この中の太字部分(docker swarm join で始まる行)は後でワーカーノードで実行することになるコマンドなのでコピペできるようにしておきましょう。


【ワーカーノードの接続】
次にワーカーノード側(raspi002, raspi003)から管理ノードに接続します。上記の管理ノード作成時に実行したコマンドの実行結果をそのまま入力して実行します:
$ docker swarm join --token SWMTKN-1-0dg4im1kvb41odjj9zmhwug0bcvg6n8tt0d8548jxho5cp8cvo-df89pmegbdf4a3vy37apftv8r 192.168.1.153:2377

コマンドの実行が成功すると "This node joined a swarm as a worker." といったメッセージが表示されます。これを raspi002 と raspi003 の両方で(4台以上で構成する場合は全てのワーカーノードで)実行します。


【ノードの状態を確認】
ここまでの作業でこの環境が作れています:

2018012201


ノードの状態を確認してみます。raspi001 から以下のコマンドを実行します:
2019012202

$ docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
xenxp2w34x5dhvtdmq0ndwolk *   raspi001            Ready               Active              Leader              18.09.0
hztpov7qya7y99m4oifx73ugt     raspi002            Ready               Active                                  18.09.0
wksx0mlyu9wsilk9c4jigvcib     raspi003            Ready               Active                                  18.09.0

実行結果↑の青字部分をみると raspi001, raspi002, raspi003 がリストされ、全て Active になっています。また raspi001 の MANAGER STATUS が Leader とマークされており、ここが管理ノードになっていることが確認できました。


あとはこの3つのラズパイと電源ケーブル等をつなげて鳩サブレー缶に押し込みます:
IMG_3802


鳩サブレ版の(?) docker swarm 環境が完成しました!
IMG_3800



(参考)
https://blog.ruanbekker.com/blog/2018/10/23/setting-up-a-docker-swarm-cluster-on-3-raspberrypi-nodes/


このページのトップヘ