SwarmKit(Docker社製オーケストレーションツール) 触ってみた

**2016/6/16追記**
Docker本体にSwarmKitとの連携機能が追加されてました。
[PullRequest:GH-23361](https://github.com/docker/docker/pull/23361)
SwarmKit単体で使うというより、DockerCLIとの統合する方向のようですね。

docker CLIに以下のようなコマンドが追加されました。

 - `swarm init`
 - `swarm join`
 - `swarm leave`
 - `swarm update`

swarmクラスタノード管理用のコマンドも追加されています。

  - `node_accept `
  - `node_reject `
  - `node_promote `
  - `node_demote `
  - `node_inspect `
  - `node_update `
  - `node_tasks `
  - `node_ls `
  - `node_rm `

swarmクラスタ上でのジョブ実行用コマンドも追加です。

  - `service create`
  - `service inspect`
  - `service update`
  - `service remove`
  - `service tasks`

最後に、複数サービスから構成されるアプリケーション用に`Docker Stacks`機能(コマンド)も追加されていました。

  - `stack`
  - `deploy`

各コマンドの詳細、ドキュメントは[こちら](https://github.com/docker/docker/blob/9acf97b72a4d5ff7b1bcad36fb19b53775f01596/docs/reference/commandline/index.md#swarm-swarm-commands)です。

`Docker Stacks`についてのドキュメントは[こちら](https://github.com/docker/docker/blob/ae816baa3c36aaec5e3b7c31425b620b4709ab50/experimental/docker-stacks.md)です。

参考までに`swarm init`と`swarm join`のヘルプは以下のようなものとのことです。

# swarm init

	Usage:	docker swarm init [OPTIONS]

	Initialize a Swarm.

	Options:
	      --auto-accept value   Acceptance policy (default [worker,manager])
	      --force-new-cluster   Force create a new cluster from current state.
	      --help                Print usage
	      --listen-addr value   Listen address (default 0.0.0.0:2377)
	      --secret string       Set secret value needed to accept nodes into cluster

Initialize a Swarm cluster. The docker engine targeted by this command becomes a manager
in the newly created one node Swarm cluster.

# swarm join

	Usage:	docker swarm join [OPTIONS] HOST:PORT

	Join a Swarm as a node and/or manager.

	Options:
	      --help                Print usage
	      --listen-addr value   Listen address (default 0.0.0.0:2377)
	      --manager             Try joining as a manager.
	      --secret string       Secret for node acceptance

Join a node to a Swarm cluster. If the `--manager` flag is specified, the docker engine
targeted by this command becomes a `manager`. If it is not specified, it becomes a `worker`.

---2016/6/16追記 End---

---

何気なくDocker社のリポジトリを見てたら[SwarmKit](https://github.com/docker/swarmkit)なるものがありました。
調べてみたら発表されたばかりみたいであまり資料がありませんでした。

#### 紹介記事(英語)

  - [First Look at Docker SwarmKit](https://blog.replicated.com/2016/06/08/first-look-at-swarmkit/)

気になったので早速使ってみることにしました。

## SwarmKit is 何？

一言で言うと(分散)マルチホスト環境でのDockerコンテナのオーケストレーションを行ってくれるツールとのことです。
類似のプロダクトとしてはHashicorpの[nomad](https://www.nomadproject.io)やMesos+Marathonがありますね。

私のつたない英語訳だと誤解を招きそうなので、READMEから引用しておきますので詳細はこちらを参照してください。

>*SwarmKit* is a toolkit for orchestrating distributed systems at any scale. It includes primitives for node discovery, raft-based consensus, task scheduling and more.

> Its main benefits are:

> - **Distributed**: *SwarmKit* uses the [Raft Consensus Algorithm](https://raft.github.io/) in order to coordinate and does not rely on a single point of failure to perform decisions.
> - **Secure**: Node communication and membership within a *Swarm* are secure out of the box. *SwarmKit* uses mutual TLS for node *authentication*, *role authorization* and *transport encryption*, automating both certificate issuance and rotation.
> - **Simple**: *SwarmKit* is operationally simple and minimizes infrastructure dependencies. It does not need an external database to operate.

ConsulやNomadと同じくRaft実装みたいですね。
機能一覧は[Features](https://github.com/docker/swarmkit#features)に記載されています。


## セットアップ

### 環境

以下ではMac(OSX)で作業しています。Dockerは`Docker for Mac(beta)`を利用しています。
それ以外の環境の方は適宜読み替えてください。

### セットアップ
SwarmKitのバイナリでの配布は行われていないようでした。
Githubのリポジトリからソースを取得してビルドしました。

```bash:コード取得〜ビルドまで
# ソース取得
go get -d github.com/docker/swarmkit/...
cd $GOPATH/src/github.com/docker/swarmkit

# 確認
go test $(go list ./... | grep -v vendor)

# ビルド
make binaries
```

`bin`ディレクトリの下に以下のファイルが作成されます。

```bash:make_binariesの結果
bin/
├── protoc-gen-gogoswarm
├── swarm-bench
├── swarmctl
└── swarmd
```

以下ではswarmdとswarmctlにパスが通った状態とします。

## swarmクラスタのセットアップ

swarmdを使ってみます。
swarmdのヘルプは以下のような感じでした。

```bash:swarmdのヘルプ
$ swarmd --help
Run a swarm control process

Usage:
  bin/swarmd [flags]

Flags:
  -c, --ca-hash string              Specifies the remote CA root certificate hash, necessary to join the cluster securely
      --election-tick value         Defines the amount of ticks (in seconds) needed without a Leader to trigger a new election (default 3)
      --engine-addr string          Address of engine instance of agent. (default "unix:///var/run/docker.sock")
      --force-new-cluster           Force the creation of a new cluster from data directory
      --heartbeat-tick value        Defines the heartbeat interval (in seconds) for raft member health-check (default 1)
      --hostname string             Override reported agent hostname
      --join-addr string            Join cluster with a node at this address
      --listen-control-api string   Listen socket for control API (default "/var/run/docker/cluster/docker-swarmd.sock")
      --listen-debug string         Bind the Go debug server on the provided address
      --listen-remote-api string    Listen address for remote API (default "0.0.0.0:4242")
  -l, --log-level string            Log level (options "debug", "info", "warn", "error", "fatal", "panic") (default "info")
      --manager                     Request initial CSR in a manager role
  -s, --secret string               Specifies the secret token required to join the cluster
  -d, --state-dir string            State directory (default "/var/lib/docker/cluster")
  -v, --version                     Display the version and exit
```

### 最初のノードの作成

以下のコマンドで最初のノードを作成します。

```bash:最初のノード作成
$ swarmd -d /tmp/node-1 --listen-control-api /tmp/manager1/swarm.sock --hostname node-1
```

![first_node.jpg](https://qiita-image-store.s3.amazonaws.com/0/20668/552dbab0-9141-b5f7-9b19-dcc8533cfa18.jpeg "first_node.jpg")


続いて２つノードを追加してみます。

```bash:ノード追加
$ swarmd -d /tmp/node-2 --hostname node-2 --join-addr 127.0.0.1:4242
$ swarmd -d /tmp/node-3 --hostname node-3 --join-addr 127.0.0.1:4242
```

![second_node.jpg](https://qiita-image-store.s3.amazonaws.com/0/20668/445bab64-bce9-e9e6-bfd6-38ac25382299.jpeg "second_node.jpg")


管理作業は`swarmctl`コマンドで行うようです。
Swarmクラスタ内のノード一覧を表示してみます。

```bash:一覧表示
# swarmctlコマンドの接続先
$ export SWARM_SOCKET=/tmp/manager1/swarm.sock
# 一覧表示(ls)
$ swarmctl node ls
```

![swarmctl_ls.jpg](https://qiita-image-store.s3.amazonaws.com/0/20668/b80ecb43-6c67-930e-be78-dd96946bcb0b.jpeg "swarmctl_ls.jpg")


3ノードがSwarmクラスタに参加している状態となりました。

### Swarmクラスタでのサービス実行

クラスタ上でサービスを実行するのは`swarmctl service`サブコマンドを使うようです。help表示は以下のようになりました。

```bash:swarmctl_service
$ swarmctl help service
Service management

Usage:
  bin/swarmctl service [command]

Aliases:
  service, svc


Available Commands:
  inspect     Inspect a service
  ls          List services
  create      Create a service
  update      Update a service
  remove      Remove a service

Global Flags:
  -n, --no-resolve      Do not try to map IDs to Names when displaying them
  -s, --socket string   Socket to connect to the Swarm manager (default "/tmp/manager1/swarm.sock")

Use "swarmctl service [command] --help" for more information about a command.
```

では早速何か起動してみましょう。ここでは例としてredisを起動してみます。

#### サービス起動

```サービスbash:起動
$ swarmctl service create --name redis --image redis:3.0.5
```

起動できましたか？

#### サービス起動確認

`swarmctl service ls`コマンドで確認しましょう。

```bash:確認
$ swarmctl service ls
ID                         Name   Image        Instances
--                         ----   -----        ---------
2v5ima52ubn1szmrw5qxwiy00  redis  redis:3.0.5  1
```

起動しているみたいですね。

#### サービス詳細確認

`swarmctl service inspect [サービス名]`で詳細確認できるようです。

```bash:swarmctl_inspect
$ swarmctl service inspect redis
ID                : 2v5ima52ubn1szmrw5qxwiy00
Name              : redis
Instances         : 1
Template          
 Container        
  Image           : redis:3.0.5

Task ID                      Service    Instance    Image          Desired State    Last State               Node
-------                      -------    --------    -----          -------------    ----------               ----
f1r7p3b5m3hxvi7ggd14yppxe    redis      1           redis:3.0.5    RUNNING          RUNNING 3 minutes ago    node-1
```


ついでにdockerコマンド側からも起動状況を見てみましょう。

```bash:docker_ps
$ docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED              STATUS              PORTS               NAMES
e57a1aca66f3        redis:3.0.5         "/entrypoint.sh redis"   About a minute ago   Up About a minute   6379/tcp            redis.1.f1r7p3b5m3hxvi7ggd14yppxe
```

redisコンテナが起動してるのが確認できると思います。

### サービスUPDATE

サービスのインスタンス数を増やすなど、属性の更新ができるようです。
インスタンス数を6に増やしてみます。

```bash:update実施
# 増やしてみるよ
$ swarmctl service update redis --instances 6
2v5ima52ubn1szmrw5qxwiy00

# 確認してみるよ
$ swarmctl service inspect redis
ID                : 2v5ima52ubn1szmrw5qxwiy00
Name              : redis
Instances         : 6
Template          
 Container        
  Image           : redis:3.0.5

Task ID                      Service    Instance    Image          Desired State    Last State                Node
-------                      -------    --------    -----          -------------    ----------                ----
f1r7p3b5m3hxvi7ggd14yppxe    redis      1           redis:3.0.5    RUNNING          RUNNING 7 minutes ago     node-1
37xq1iluq89q9ln140way2wuj    redis      2           redis:3.0.5    RUNNING          RUNNING 18 seconds ago    node-3
chvth7r9ol2nhll0o6basebap    redis      3           redis:3.0.5    RUNNING          RUNNING 18 seconds ago    node-2
6d79qrshr74d1x7x0y57hercd    redis      4           redis:3.0.5    RUNNING          RUNNING 18 seconds ago    node-2
etfpywpqz9zi0x90mhbzly5zs    redis      5           redis:3.0.5    RUNNING          RUNNING 18 seconds ago    node-1
43fackr1v05f6w7yuwc2uf2px    redis      6           redis:3.0.5    RUNNING          RUNNING 18 seconds ago    node-3

```

増えてそうですね。nodeの分散もできているようです。
dockerコマンド側からも見てみましょう。

```bash:docker_psで確認
$ docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED              STATUS              PORTS               NAMES
44747f6bf347        redis:3.0.5         "/entrypoint.sh redis"   About a minute ago   Up About a minute   6379/tcp            redis.6.43fackr1v05f6w7yuwc2uf2px
655a2bf55185        redis:3.0.5         "/entrypoint.sh redis"   About a minute ago   Up About a minute   6379/tcp            redis.4.6d79qrshr74d1x7x0y57hercd
e1a1e2e993bc        redis:3.0.5         "/entrypoint.sh redis"   About a minute ago   Up About a minute   6379/tcp            redis.3.chvth7r9ol2nhll0o6basebap
43bc0449a374        redis:3.0.5         "/entrypoint.sh redis"   About a minute ago   Up About a minute   6379/tcp            redis.2.37xq1iluq89q9ln140way2wuj
9cbb0c1a419a        redis:3.0.5         "/entrypoint.sh redis"   About a minute ago   Up About a minute   6379/tcp            redis.5.etfpywpqz9zi0x90mhbzly5zs
e57a1aca66f3        redis:3.0.5         "/entrypoint.sh redis"   9 minutes ago        Up 9 minutes        6379/tcp            redis.1.f1r7p3b5m3hxvi7ggd14yppxe
```

今回はすべてのノードが同じホストのため6インスタンスすべてが見えていますね。
おそらく各ノード別マシンでswarmクラスタを組めば、docker psも次ノードの分のみ表示されるのだと思います。(まだ試してません)

`swarmctl service update`では、実行イメージの更新だったり、ローリングアップデートもできるようです。

### ノードの停止(drain)

ノードを停止して、サービスのインスタンスが再配置される様子を見てみましょう。

```bash:node停止_再配置
# node-1を停止
$ swarmctl node drain node-1

# 確認
$ swarmctl node ls
ID             Name    Membership  Status  Availability  Manager status
--             ----    ----------  ------  ------------  --------------
08ru842rs9yam  node-1  ACCEPTED    READY   DRAIN         REACHABLE *
185o4l4u6nnqh  node-3  ACCEPTED    READY   ACTIVE        
3ebkf6w3m1dnd  node-2  ACCEPTED    READY   ACTIVE        
```

#### 再配置の確認

node-1を停止しましたので6インスタンスがnode-2とnode-3に割り振られるはずです。
確認してみましょう。

```bash:swarmctl_再配置確認
$ swarmctl service inspect redis
ID                : 2v5ima52ubn1szmrw5qxwiy00
Name              : redis
Instances         : 6
Template          
 Container        
  Image           : redis:3.0.5

Task ID                      Service    Instance    Image          Desired State    Last State               Node
-------                      -------    --------    -----          -------------    ----------               ----
6dfvxo1hx0ml7wvuly76qzox7    redis      1           redis:3.0.5    RUNNING          RUNNING 2 minutes ago    node-2
37xq1iluq89q9ln140way2wuj    redis      2           redis:3.0.5    RUNNING          RUNNING 9 minutes ago    node-3
chvth7r9ol2nhll0o6basebap    redis      3           redis:3.0.5    RUNNING          RUNNING 9 minutes ago    node-2
6d79qrshr74d1x7x0y57hercd    redis      4           redis:3.0.5    RUNNING          RUNNING 9 minutes ago    node-2
93vr14ppi1quw5iekzuct8r5i    redis      5           redis:3.0.5    RUNNING          RUNNING 2 minutes ago    node-3
43fackr1v05f6w7yuwc2uf2px    redis      6           redis:3.0.5    RUNNING          RUNNING 9 minutes ago    node-3
```

node-1で起動しているインスタンスがなくなり、node-2とnode-3に再配置されました。

## おまけ:swarmctlサブコマンドごとのヘルプ

ビルドが面倒な方のためにヘルプを貼り付けておきます。
サブコマンド、サブサブコマンドについてはオプションが指定できそうなものは記載しています。
(記載が無いもので知りたいものがあれば@yamamoto-febcまで連絡ください。載せます。)

### `swarmctl`

```bash:swarmctl_help
$ swarmctl --help
Control a swarm cluster

Usage:
  bin/swarmctl [command]

Available Commands:
  node        Node management
  service     Service management
  task        Task management
  version     Print version number of swarm.
  network     Network management
  cluster     Cluster management

Flags:
  -n, --no-resolve      Do not try to map IDs to Names when displaying them
  -s, --socket string   Socket to connect to the Swarm manager (default "/var/run/docker/cluster/docker-swarmd.sock")

Use "swarmctl [command] --help" for more information about a command.

```

### `swarmctl node`

```bash:swarmctl_node
$ swarmctl node --help
Node management

Usage:
  swarmctl node [command]

Available Commands:
  accept      Accept a node into the cluster
  remove      Remove a node
  inspect     Inspect a node
  ls          List nodes
  activate    Activate a node
  pause       Pause a node
  drain       Drain a node
  promote     Promote a node to a manager
  demote      Demote a node from a manager to a worker

Global Flags:
  -n, --no-resolve      Do not try to map IDs to Names when displaying them
  -s, --socket string   Socket to connect to the Swarm manager (default "/var/run/docker/cluster/docker-swarmd.sock")

Use "swarmctl node [command] --help" for more information about a command.

```

### `swarmctl service`

```bash:swarmctl_service
$ swarmctl service --help
Service management

Usage:
  swarmctl service [command]

Aliases:
  service, svc


Available Commands:
  inspect     Inspect a service
  ls          List services
  create      Create a service
  update      Update a service
  remove      Remove a service

Global Flags:
  -n, --no-resolve      Do not try to map IDs to Names when displaying them
  -s, --socket string   Socket to connect to the Swarm manager (default "/var/run/docker/cluster/docker-swarmd.sock")

Use "swarmctl service [command] --help" for more information about a command.

```

### `swarmctl service create`

```bash:swarmctl_service_create
$ swarmctl service create --help
Create a service

Usage:
  bin/swarmctl service create [flags]

Flags:
      --args value                  container args (default [])
      --constraint value            Placement constraint (node.labels.key==value) (default [])
      --cpu-limit string            CPU cores limit (e.g. 0.5)
      --cpu-reservation string      number of CPU cores reserved (e.g. 0.5)
      --env value                   container env (default [])
      --image string                container image
      --instances uint              number of instances for the service (default 1)
      --label value                 service label (key=value) (default [])
      --memory-limit string         memory limit (e.g. 512m)
      --memory-reservation string   amount of reserved memory (e.g. 512m)
      --mode string                 one of replicated, global (default "replicated")
      --name string                 service name
      --network string              network name
      --ports value                 ports (default [])
      --restart-condition string    condition to restart the task (any, failure, none) (default "any")
      --restart-delay string        delay between task restarts (default "5s")
      --restart-max-attempts uint   maximum number of restart attempts (0 = unlimited)
      --restart-window string       time window to evaluate restart attempts (0 = unbound) (default "0s")
      --update-delay string         delay between task updates (0s = none) (default "0s")
      --update-parallelism uint     task update parallelism (0 = all at once)

Global Flags:
  -n, --no-resolve      Do not try to map IDs to Names when displaying them
  -s, --socket string   Socket to connect to the Swarm manager (default "/tmp/manager1/swarm.sock")
```

### `swarmctl task`

```bash:swarmctl_task
$ swarmctl task --help
Task management

Usage:
  swarmctl task [command]

Available Commands:
  ls          List tasks
  inspect     Inspect a task
  remove      Remove a task

Global Flags:
  -n, --no-resolve      Do not try to map IDs to Names when displaying them
  -s, --socket string   Socket to connect to the Swarm manager (default "/var/run/docker/cluster/docker-swarmd.sock")

Use "swarmctl task [command] --help" for more information about a command.
```

### `swarmctl network`

```bash:swarmctl_network
$ swarmctl  network --help
Network management

Usage:
  swarmctl network [command]

Available Commands:
  inspect     Inspect a network
  ls          List networks
  create      Create a network
  remove      Remove a network

Global Flags:
  -n, --no-resolve      Do not try to map IDs to Names when displaying them
  -s, --socket string   Socket to connect to the Swarm manager (default "/var/run/docker/cluster/docker-swarmd.sock")

Use "swarmctl network [command] --help" for more information about a command.

```

### `swarmctl network create`

```bash:swarmctl_network_create
$ swarmctl network create --help
Create a network

Usage:
  bin/swarmctl network create [flags]

Flags:
      --driver string        Network driver
      --gateway value        Gateway IP addresses for network segments (default [])
      --ip-range value       IP ranges to allocate from within the subnets (default [])
      --ipam-driver string   IPAM driver
      --name string          Network name
      --opts value           Network driver options (default [])
      --subnet value         Subnets in CIDR format that represents a network segments (default [])

Global Flags:
  -n, --no-resolve      Do not try to map IDs to Names when displaying them
  -s, --socket string   Socket to connect to the Swarm manager (default "/tmp/manager1/swarm.sock")
```

### `swarmctl cluster`

```bash:swarmctl_cluster
$ swarmctl  cluster --help
Cluster management

Usage:
  swarmctl cluster [command]

Available Commands:
  inspect     Inspect a cluster
  ls          List clusters
  update      Update a cluster

Global Flags:
  -n, --no-resolve      Do not try to map IDs to Names when displaying them
  -s, --socket string   Socket to connect to the Swarm manager (default "/var/run/docker/cluster/docker-swarmd.sock")

Use "swarmctl cluster [command] --help" for more information about a command.

```

### `swarmctl cluster update`

```bash:swarmctl_cluster_update
$ swarmctl cluster update
Error: cluster name missing
Usage:
  bin/swarmctl cluster update <cluster name> [flags]

Flags:
      --autoaccept value           Roles to automatically issue certificates for (default [])
      --certexpiry duration        Duration node certificates will be valid for (default 2160h0m0s)
      --heartbeatperiod duration   Period when heartbeat is expected to receive from agent
  -h, --help                       help for update
      --secret value               Secret required to join the cluster (default [])
      --taskhistory int            Number of historic task entries to retain per instance or node

Global Flags:
  -n, --no-resolve      Do not try to map IDs to Names when displaying them
  -s, --socket string   Socket to connect to the Swarm manager (default "/tmp/manager1/swarm.sock")
```

## まとめ

[README](https://github.com/docker/swarmkit)から一通りSwarmKitを触ってみました。
これまではSwarmクラスタの構築に別途KVS(etcdやconsul)の構築が必要でしたが、swarmdがそのあたりを隠蔽してくれているようです。

ネットワーク周りやデータボリューム周りなど、マルチホストでクラスタを組む場合はいろいろ気になりますが、まだまだ開発中のようで情報が少ないです。


[nomad](https://www.nomadproject.io)やMesos+Marathonなどと比べてのメリットもまだよくわかっていません。(私が読み解けてないだけかもしれないです、、、)

もう少し開発状況を見守ってみようと思います。

以上です。