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

  • 139
    いいね
  • 1
    コメント

元ネタ

BUILDING SERVERLESS APPS WITH DOCKER

概要

Docker Blogに面白い記事がありましたので紹介/解説です。

既存のWebアプリをDockerとswarmでサーバーレスにしてみるというものです。
詳細は上記の元ネタを読んでいただければと思います。

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

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

GitHub/通常の構成(既存Webアプリ)のソースコード
GitHub/サーバーレス構成のソースコード

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

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

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

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

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

するものです。

投票画面

vote.png

結果表示画面

result.png

このアプリは以下のような5つのサービス/サーバーから構成されています。
(DockerCon16で使われたvoteアプリです。ソースはこちらにあります)

serverd
出典:BUILDING SERVERLESS APPS WITH DOCKER

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

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

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

serverless
出典:BUILDING SERVERLESS APPS WITH DOCKER

赤い背景のものは常時起動しておくサービス(サーバー)です。
緑色の部分がサーバーレス対応(必要に応じて起動)される部分です。
(ソースはこちらにあります)

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

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

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

ポイント

リクエスト処理部分

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

リクエスト処理部の違い

01_request.png

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

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

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

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を以下のようにボリューム指定しています。

サーバーレス版の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

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

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

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

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

投票結果登録部のソースの違い

03_vote_src.png

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

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

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

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

--- 6/26追記:2回目 ---
swarmで動くサンプルはこちらに置いています。
swarm対応でどこを変えたかは(Github/変更差分)を参照ください。
--- 追記ここまで ---

その他の違い

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

終わりに

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

今回のサーバーレスアーキテクチャ化で利用したライブラリなどは以下のページにまとまっています。
Github/Serverless Docker

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

以上です。