DockerとSwarmで既存アプリをサーバーレス化してみよう！

# 元ネタ

[BUILDING SERVERLESS APPS WITH DOCKER](https://blog.docker.com/2016/06/building-serverless-apps-with-docker/)

# 概要

[Docker Blog](https://blog.docker.com)に面白い記事がありましたので紹介/解説です。

既存のWebアプリをDockerとswarmでサーバーレスにしてみるというものです。
詳細は上記の[元ネタ](https://blog.docker.com/2016/06/building-serverless-apps-with-docker/)を読んでいただければと思います。

当記事ではポイントごとに使われてるテクニックについてみていきます。

なお、それぞれのソースコードは以下のリポジトリで公開されています。

[GitHub/通常の構成(既存Webアプリ)のソースコード](https://github.com/docker/example-voting-app)
[GitHub/サーバーレス構成のソースコード](https://github.com/bfirsh/serverless-docker-voting-app)

**--- 6/26追記 ---**
上記のデモアプリはswarmクラスタ構成になっていません。
swarmで動くサンプルは[こちら](https://github.com/yamamoto-febc/serverless-docker-voting-app)に作成してみました。([変更差分](https://github.com/yamamoto-febc/serverless-docker-voting-app/commit/a19d138e0657cc4133c00ac8b0ee7e764e643442))
DockerToolbox(VirtualBox)で動かすサンプルですので、環境をお持ちの方はお試しください。
`Docker in Docker(Swarmクラスタ)`のサンプルとしてもご参照ください。
**--- 追記ここまで ---**

**--- 6/30追記 ---**
元ネタの日本語訳がクリエーションラインさんにて公開されていました。
http://www.creationline.com/lab/docker/14211
**--- 追記ここまで ---**


# 既存アプリをサーバーレスアーキテクチャ対応する

移行の対象となるアプリは「投票」アプリです。
機能としては

  - 投票画面で写真への投票を行い、
  - 結果表示画面で投票の集計結果を表示

するものです。

#### 投票画面
![vote.png](https://qiita-image-store.s3.amazonaws.com/0/20668/bd1982a8-09f3-6105-96f4-1038cced35be.png)


#### 結果表示画面

![result.png](https://qiita-image-store.s3.amazonaws.com/0/20668/665623a3-9339-678c-fd13-9fbbd316aea5.png)



このアプリは以下のような5つのサービス/サーバーから構成されています。
(DockerCon16で使われたvoteアプリです。ソースは[こちら](https://github.com/docker/example-voting-app)にあります)


![serverd](https://blog.docker.com/wp-content/uploads/Picture1.png "servered")
[出典:BUILDING SERVERLESS APPS WITH DOCKER](https://blog.docker.com/2016/06/building-serverless-apps-with-docker/)


それぞれの役割は以下の通りです。

|名称           | 実装         | 役割                       |
|--------------|-------------|----------------------------|
|voting app    | Python      | 投票画面                    |
|message queue | Redis       | ジョブキュー                 |
|worker        | .Net(C#)    | ジョブをデキューし、DBへINSERT |
|database      | PostgreSQL  | 投票結果を格納するDB          |
|result app    | Node.js     | 結果表示画面                 |

これを以下のような構成にしてみます。


![serverless](https://blog.docker.com/wp-content/uploads/Picture2.png "serverless")
[出典:BUILDING SERVERLESS APPS WITH DOCKER](https://blog.docker.com/2016/06/building-serverless-apps-with-docker/)

赤い背景のものは常時起動しておくサービス(サーバー）です。
緑色の部分がサーバーレス対応(必要に応じて起動)される部分です。
(ソースは[こちら](https://github.com/bfirsh/serverless-docker-voting-app)にあります)

それぞれの役割は以下の通りです。

|名称               | 実装         | 役割                       |
|------------------|-------------|----------------------------|
|entrypoint        | Golang      | HTTPリクエストに応じてDockerコンテナを起動|
|handle vote       | Python      | 投票画面                    |
|process vote task | Java        | 投票内容をDBヘINSERT         |
|database          | PostgreSQL  | 投票結果を格納するDB          |
|handle result     | Perl        | 結果表示画面                 |

以下でポイントごとにソースを見る/比較を行ってみます。

# ポイント

## リクエスト処理部分

`voting app`と`result app`という2つのサービスで処理していたのを、
`entrypoint`というサービスで受け、それぞれ担当するDockerコンテナを起動するという形になります。

#### リクエスト処理部の違い
![01_request.png](https://qiita-image-store.s3.amazonaws.com/0/20668/74a901e9-02f2-89c1-98f0-9cedb12b9182.png)


`voting app`と`result app`はそれぞれ何の変哲もないWebアプリです。
(`voting app`がPython/Flask、`result app`がNote.js/expressで作られている)

対して、`entrypoint`もWebアプリ(Go言語)なのですが、`net/http`でリクエストを受け、
`go-dcgi`というライブラリでDockerコンテナを起動するようになっています。

この部分のソースを見てみましょう。
非常に短いですので全文掲載しています。（コメントは私が追記したものです）

```go:entrypoint.go
package main

import (
	"net/http"

	"github.com/bfirsh/go-dcgi"
	"github.com/docker/engine-api/client"
	"github.com/docker/engine-api/types/container"
)

func main() {
	// Dockerクライアントの初期化
	cli, err := client.NewClient("unix:///var/run/docker.sock", "v1.23", nil, nil)
	if err != nil {
		panic(err)
	}

	// 起動するコンテナのネットワーク/ボリュームの設定
	hostConfig := &container.HostConfig{
		NetworkMode: "serverlessdockervotingapp_default",
		Binds:       []string{"/var/run/docker.sock:/var/run/docker.sock"},
	}

	// リクエストURLごとにDockerコンテナを起動して処理するハンドラを登録
	http.Handle("/vote/", &dcgi.Handler{
		Image:      "bfirsh/serverless-vote",
		Client:     cli,
		HostConfig: hostConfig,
		Root:       "/vote", // strip /vote from all URLs
	})
	http.Handle("/result/", &dcgi.Handler{
		Image:      "bfirsh/serverless-result",
		Client:     cli,
		HostConfig: hostConfig,
		Root:       "/result",
	})

	// 80番ポートで待ち受け開始
	http.ListenAndServe(":80", nil)
}
```

`go-dcgi`は指定のDockerクライアント/設定/イメージを用いてDockerコンテナ起動処理を行ってくれます。

### ポイント:コンテナ内からのコンテナ起動(docker.sockの受け渡し)

ポイントは`/var/run/docker.sock`の受け渡しを行っていることですね。
これを使ってDockerコンテナ内からDocker APIの呼び出しを行っています。
なお、このアプリ自体もDockerコンテナです。docker-composeで起動するときに、
Dockerホストの`var/run/docker.sock`を以下のようにボリューム指定しています。

```yaml:サーバーレス版のdocker-compose.yml
version: "2"

services:
  entrypoint:
    build: entrypoint
    ports:
      - 80:80
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock"

  db:
    image: postgres:9.4
```

Dockerホストdocker.sock〜`entrypoint`コンテナ〜HTTP処理用コンテナと
docker.sockが受け渡されています。

## 投票結果の登録

#### 投票結果登録部の違い
![02_vote.png](https://qiita-image-store.s3.amazonaws.com/0/20668/1bce3452-16ae-c8f9-34ea-7f284d007b2d.png)

通常のサーバー構成の場合、

  - 投稿画面からRedisへキューイング
  - Redisは常時起動している
  - workerがRedisからデキューし、DB登録処理を行う

という構成になっています。
３つのサービスが常時起動だったのですが、サーバーレス構成の場合はこれが毎回コンテナを起動して処理する形になります。

Webアプリのソース上はRedisへのキューイング処理がDocker起動処理に変わる程度の修正のみ行われています。

#### 投票結果登録部のソースの違い
![03_vote_src.png](https://qiita-image-store.s3.amazonaws.com/0/20668/4176a67d-8094-5449-042a-365d2f7be30e.png)

Redisの呼び出しもDockerクライアント処理もライブラリ化されているため
ソース自体の修正は少ないですね。

### ポイント:swarmクラスタ上で都度起動する利点

ポイントは、Dockerコンテナの起動はswarmで透過的にクラスタ上で実行されるという部分ですね。
swarm上で実行することで、クラスタ上の空きリソースの管理/ロードバランス処理などはswarmに任せてしまえます。
同時にリクエストがきたらswarmクラスタで処理できる分までは勝手にスケールしてくれます。

**--- 6/26追記 ---**
このデモアプリではswarmクラスタ構成になっていません。
swarmクラスタ構成で実行する場合はDockerソケット受け渡し部分など適切に変更する必要があります。
(例えば、swarm masterへのNW疎通確保、コンテナ内でDOCKER_HOST環境変数やDOCKER_CERT_PATH環境変数の設定など)
この辺りはこのデモアプリの[課題](https://github.com/bfirsh/serverless-docker#stuff-that-needs-working-on)とされています。

**--- 6/26追記:2回目 ---**
swarmで動くサンプルは[こちら](https://github.com/yamamoto-febc/serverless-docker-voting-app)に置いています。
swarm対応でどこを変えたかは[(Github/変更差分)](https://github.com/yamamoto-febc/serverless-docker-voting-app/commit/a19d138e0657cc4133c00ac8b0ee7e764e643442)を参照ください。
**--- 追記ここまで ---**

### その他の違い

あとはDBへの登録を行うworker/taskについても同様にRedisからのデキューを起点とするか、
外部からの起動を起点とするかの違いがありますが、ソース上はあまり違いはないですね。
その他細かな部分はソースを見比べてみてください。


# 終わりに

いかがでしたでしょうか？
Swarm上で都度Dockerコンテナを起動することでロードバランスやスケールはswarmに任せてしまえるし、
常時起動しておくサーバー(サービス)が少なくなることで運用作業も多少楽になる?と思います。

今回のサーバーレスアーキテクチャ化で利用したライブラリなどは以下のページにまとまっています。
[Github/Serverless Docker](https://github.com/bfirsh/serverless-docker)

また、docker v1.12ではマルチホストでのオーケストレーション機能Docker本体に組み込まれるため、
swarmクラスタの構築が容易になります。
この機会にDockerでのサーバーレスアーキテクチャを一度検討されてみてはいかがでしょうか？


以上です。