Nomad/Consul/Nginxでスケールアウト出来るコンテナホスティング環境 on さくらのクラウド

# 概要

以下のプロダクトを組み合わせることで、Dockerコンテナ(等)のホスティング環境をつくってみました。

  - [Nomad](https://www.nomadproject.io)
  - [Consul](https://www.consul.io)
  - [ConsulTemplate](https://github.com/hashicorp/consul-template)
  - [Nginx](http://nginx.org)

全体図は以下のような感じです。

![nomad_on_sakura.gif](https://qiita-image-store.s3.amazonaws.com/0/20668/994802b7-df89-76e7-394a-6ea71e377364.gif "nomad_on_sakura.gif")

Terraformで一発で構築できるようになっています。定義は以下リポジトリで公開しています。
[Github:nomad-on-sakuracloud](https://github.com/yamamoto-febc/nomad-on-sakuracloud)

某サービスに似てる？？似てますね。
この方法なら全部自前で準備できるので、オンプレに構築することも可能ですよ。
それでは解説していきます。
なお、以下の解説では役割ごとにマシンが複数台ある構成にしていますが、最低限各役割に1台づつの構成でもOKです。

## 全体の流れ

### 前提条件

フロントエンドのIPアドレスをワイルドカードDNSとして登録しておきます。
ワイルドカードDNSを登録しておくことで、エンドポイントを動的に割り当てても名前解決ができるようになります。

**ワイルドカードDNSの登録例**

対象ドメイン名が`nomad.example.com`の場合、以下のようなワイルドカードレコード(A)を登録しておきます。

*.nomad.example.com [フロントエンドのグローバルIP]

フロントエンドが複数ある場合は複数のレコードを登録する or 仮想IPを割り当てるなどしましょう。

### コンテナ起動〜サービス提供まで

#### 1. Nomadサーバーで`nomad run`コマンドを実行しジョブ起動

![01.jpg](https://qiita-image-store.s3.amazonaws.com/0/20668/49d6035f-1c34-f1f0-358f-f587bdf7e8da.jpeg "01.jpg")

nomadサーバーにSSH接続して`nomad run`を実行します。
この時、nomadのサービス定義に`nomad-worker`というタグをつけておくと
以降のフェーズで自動でエンドポイントが割り当てられるという仕組みにしています。

#### 2. Nomadサーバーが適切なNomadエージェントにジョブ割り振り

![02.jpg](https://qiita-image-store.s3.amazonaws.com/0/20668/0e7f78e5-3712-e06e-b043-d7e4a4e68061.jpeg "02.jpg")

Nomadがジョブ定義に応じて適切なエージェントにジョブを割り振ります。

#### 3. ジョブを割り振られたエージェントがDockerコンテナ起動(ポートの自動割り当て)

![03.jpg](https://qiita-image-store.s3.amazonaws.com/0/20668/7a4776d1-d409-22f4-a7a5-b636e35d2fdf.jpeg "03.jpg")

エージェントはジョブが割り当てられると指定のDockerコンテナを起動します。
ジョブ定義(*.nomad)のポート設定に従いポートを割り当てます
なお、固定のポートを割り当てることもできますが、通常自動割り当てにしておきます。
自動割り当てにしておくことで、同じノード上で複数のコンテナを起動するということもできるようになります。


#### 4. Nomadエージェントがポート番号などのサービス情報をConsulに登録

![04.jpg](https://qiita-image-store.s3.amazonaws.com/0/20668/c6bda7b8-b89f-8fad-1488-a5cd9c097437.jpeg "04.jpg")

Nomadでのジョブ定義に従い、Consulへサービス設定が登録されます。
Consulはサービス設定に従い、指定のポートを死活監視します。
指定のDockerコンテナが起動し指定ポートへの疎通が確認できればConsulへIPアドレス/ポート番号などのサービス情報を登録してくれます。

#### 5. Consulのサービス情報変更を受け、ConsulTemplateが起動。Consulからサービス情報を読み取ってNginx設定ファイルを作成

![05.jpg](https://qiita-image-store.s3.amazonaws.com/0/20668/d7011fdf-f318-a764-dbe8-c92467a73d50.jpeg "05.jpg")

フロントエンドでは[ConsulTemplate](https://github.com/hashicorp/consul-template)が稼働しています。
Consulのサービス情報の変化をフックして起動します。
ConsulTemplateはConsulからサービス情報を読み取り、
「nomad-worker」というタグがつけられたサービスのIPアドレス/ポートに対してリバースプロキシの設定を行います。

**エンドポイントの割り当て**
リバースプロキシされるエンドポイントのURLにはnomadのジョブ定義で指定したサービス名が利用されます。
サービス名を`hogehoge`、対象ドメインを`nomad.example.com`とした場合、エンドポイントは`http://hogehoge.nomad.example.com`となります。
図のように複数のコンテナがNomadエージェントにて起動されている場合、
Nginxにてラウンドロビンされます。
なお、Nginx設定は以下のような設定がConsulTemplateにて生成されます。

```ConsulTemplateで生成されるconfファイル
upstream hogehoge {
    server [agent01のIP]:20xxx;
    server [agent02のIP]:20xxx;
    server [agent03のIP]:20xxx;
}
server {
        include /etc/nginx/proxy.conf;
        server_name hogehoge.nomad.example.com;
        location / {
                proxy_pass http://hogehoge;
        }
}
```


#### 6. Nginxリロード、以後割り当てられたエンドポイントへのアクセスはNomadエージェントへリバースプロキシされる

![06.jpg](https://qiita-image-store.s3.amazonaws.com/0/20668/0a3e525a-724a-7f04-bccc-14d88cef337a.jpeg "06.jpg")

あとはブラウザでエンドポイントにアクセスしてみるだけです。
なお、現状ではNomadエージェントもグローバルIPを持ち、ポートを公開しているため、
`[エージェントのグローバルIP]:[割り当てられたポート]`には直接アクセスできるようになっています。

# 構築方法

GitHubにTerraformの定義ファイルを置いています。
`Terraform for さくらのクラウド`を用いることで、さくらのクラウド上にコマンド1発でDockerホスティング環境が作れちゃいます。

構築方法の詳細は以下を参照ください。

[Github:nomad-on-sakuracloud](https://github.com/yamamoto-febc/nomad-on-sakuracloud)

# 実運用に向けて

以下のような課題があります。

  - 永続データの管理
  - セキュリティ
  - ログの管理

NomadエージェントのDockerに分散ストレージ対応のプラグインを入れたりするのもいいかと思います。
また、ログの管理については今後`current.sh`がHashicorp製品と統合されると思いますので、今の内に使っておくのもいいかもしれません。
(nomadでsystemタスクとして登録しておけばOK)

# 終わりに

いかがでしょうか？
類似の構成として、Mesos + Marathonやkubernetesがありますが、
それらと比較すると各プロダクトが基本的にgoの単一バイナリ形式なため、
マシン環境を整えるのが格段に楽だと思います。

現時点ではNomadサーバーにSSHで接続してのジョブ投入ですが、
Dockerリポジトリを自前で準備し、ちょっとしたAPIラッパーを用意すれば、
herokuのようにイメージをpushしたら即サービス公開、何てこともできるかもしれないですね！

以上です。