MAGELLAN 用ウェブアプリケーションの作り方

はじめに

このドキュメントでは、MAGELLAN で動くウェブアプリケーションの作り方を解説します。

MAGELLAN が提供する magellan-proxy というツールを使うと、様々な言語やフレームワークで開発する(された)ウェブアプリケーションを、MAGELLAN で簡単に動かすことができます。例えば、Ruby の Ruby on Rails アプリケーションや、Java の Spring Boot アプリケーションなどです。

MAGELLAN では、MAGELLAN で動くアプリケーションのことを Worker と呼んでいます。Worker は、MAGELLAN 上で、Docker コンテナとして動きます。

このため、Worker の開発では、前提とする知識として、使用する言語やフレームワークによるウェブアプリケーション開発の知識のほかに、Docker の知識も必要になります。ただし、このドキュメントでは、これらについては解説していません。

このドキュメントでは、次に挙げる 3 点について解説します。

magellan-proxy を使った Worker の組み立て方

magellan-proxy とは何か

magellan-proxy は、MAGELLAN での利用を前提としていない一般のウェブアプリケーションを MAGELLAN で動かすためのツールです。もちろん、ウェブアプリケーションが、どのような言語やフレームワークで実装されているかは問いません。

tips用アイコン

とはいえ、守っていただきたい最低限のルールはあります。これについては、「Worker 開発のポイント」と「留意事項」でまとめています。

下図は、ウェブブラウザー - Worker 間の通信仕様です。ウェブブラウザーから Worker へのリクエストは、MAGELLAN を経由します(Worker への直接の通信はできません)。このとき、HTTP/HTTPS は、独自プロトコルのリクエストに変換されて、Worker に届きます。レスポンスは、その逆です。独自プロトコルが、HTTP/HTTPS に変換されます。

ウェブブラザ - Worker 間の通信仕様図

このように、Worker は MAGELLAN 独自プロトコルに対応しなければなりません。このため、一般のウェブアプリケーションは、そのままでは MAGELLAN 上で正しく動きません。

magellan-proxy は、下図のように、MAGELLAN とウェブアプリケーションの間に入り、MAGELLAN 独自プロトコルと HTTP 間の通信を中継します。これにより、ウェブアプリケーションは、MAGELLAN に対応することなく、そのままで MAGELLAN 上で正しく動かせます。

magellan-proxy 概念図

このほかに、Worker 宛ての(トピックが worker/ で始まる)Publish メッセージも中継します。この Publish メッセージは、POST メソッドの HTTP リクエストに変換して、ウェブアプリケーションへ送ります。

magellan-proxy の Publish メッセージの中継図

magellan-proxy の使い方

magellan-proxy は、下図のように、Worker 内で動作させます。

magellan-proxy 概念図

このため、Worker 用 Dockerfile に、「magellan-proxy のインストール」(1)と「magellan-proxy の実行」(2)を行う記述を追加する必要があります。

Dockerfile の記述例を示します。

FROM: ruby:2.3

. . .(中略). . .

# magellan-proxy のインストール <-- (1)
ADD https://github.com/groovenauts/magellan-proxy/releases/download/v0.1.4/magellan-proxy-0.1.4_linux-amd64 /usr/app/magellan-proxy
RUN chmod +x /usr/app/magellan-proxy

# magellan-proxy を実行 <-- (2)
CMD ["/usr/app/magellan-proxy", "--port", "3000", "bundle", "exec", "rails", "server"]
  • magellan-proxy のインストール

    ADD https://github.com/groovenauts/magellan-proxy/releases/download/v0.1.4/magellan-proxy-0.1.4_linux-amd64 /usr/app/magellan-proxy
    RUN chmod +x /usr/app/magellan-proxy
    

    ADD 命令で、/usr/app に、magellan-proxy をインストールしています。その後、RUN 命令で、chmod +x コマンドを実行して、インストールした magellan-proxy を実行できるようにしています。

  • magellan-proxy を実行
    Docker コンテナ起動時に実行するコマンドを magellan-proxy 経由で実行します。magellan-proxy 実行時の書式は、次のとおりです。

    magellan-proxy [オプション] Docker コンテナ起動時に実行するコマンド
    

    指定可能なオプションは、次のとおりです。

    オプション 説明
    --port
    ポートバインディングのためのオプションです。アプリケーションが待ち受けているポート番号を指定します。80 番の場合は省略可能です。
    例)--port 3000
    --num
    HTTP リクエストの最大並行数を指定します。
    例)--num 5
    --publish
    Worker 宛ての Publish メッセージを HTTP の POST メソッドに変換して送る際のパスを指定します。
    例)--publish /pub

    例えば、magellan-proxy を利用しない時のコンテナ起動時のアプリケーション実行方法が、次のようになっていたとします。

    CMD ["bundle", "exec", "rails", "server"]
    

    magellan-proxy を使用する場合は、次のように指定します。

    CMD ["/usr/app/magellan-proxy", "--port", "3000", "bundle", "exec", "rails", "server"]
    

Publish メッセージの中継について

magellan-proxy は、Worker 宛ての(トピックが worker/ で始まる)Publish メッセージも中継します。

magellan-proxy の Publish メッセージの中継図

Worker 宛ての Publish メッセージは、次表のような HTTP リクエストに変換します。なお、このリクエストに対するレスポンスは、magellan-proxy が読み捨てます。

項目 内容
メソッド POST
リクエスト URI
--publish オプションで指定されたパス
クエリ
topic=<Publish メッセージのトピック>
例)topic=worker/foo/bar
HTTP ヘッダ (*)
Content-Type: application/octet-stream
X-MAGELLAN-MQTT: 1
メッセージボディ Publish メッセージ

*: HTTP ヘッダは、一部のみ記載。

このため、Worker で Publish メッセージに対応する場合は、特定パス(--publish オプションに指定したパス)の POST リクエストを処理すれば良いことになります。

Worker 開発のポイント

Worker は、The Twelve-Factor App日本語 ) の方法論に準拠して作成することを推奨します。

この The Twelve-Factor App は、次のようなウェブアプリケーションを作り上げるための方法論です。

  • セットアップ自動化のために 宣言的な フォーマットを使い、プロジェクトに新しく加わった開発者が要する時間とコストを最小化する。
  • 下層のOSへの 依存関係を明確化 し、実行環境間での 移植性を最大化 する。
  • モダンな クラウドプラットフォーム 上への デプロイ に適しており、サーバー管理やシステム管理を不要なものにする。
  • 開発環境と本番環境の 差異を最小限 にし、アジリティを最大化する 継続的デプロイ を可能にする。
  • ツール、アーキテクチャ、開発プラクティスを大幅に変更することなく スケールアップ できる。

The Twelve-Factor App

これは、MAGELLAN のようなクラウドプラットフォームで動く、モダンなウェブアプリケーション開発のベストプラクティスと言えます。

この方法論は、その名が示すとおりに、12(Twelve)の要因(factor)にまとめられています。

# 要因 概要
1 コードベース バージョン管理されている1つのコードベースと複数のデプロイ
2 依存関係 依存関係を明示的に宣言し分離する
3 設定 設定を環境変数に格納する
4 バックエンドサービス バックエンドサービスをアタッチされたリソースとして扱う
5 ビルド、リリース、実行 ビルド、リリース、実行の3つのステージを厳密に分離する
6 プロセス アプリケーションを1つもしくは複数のステートレスなプロセスとして実行する
7 ポートバインディング ポートバインディングを通してサービスを公開する
8 並行性 プロセスモデルによってスケールアウトする
9 廃棄容易性 高速な起動とグレースフルシャットダウンで堅牢性を最大化する
10 開発 / 本番一致 開発、ステージング、本番環境をできるだけ一致させた状態を保つ
11 ログ ログをイベントストリームとして扱う
12 管理プロセス 管理タスクを1回限りのプロセスとして実行する

(要因と概要の文言は、The Twelve-Factor App からの引用)

ここでは、Worker 開発で特に考慮すべき 5 つの重要な Factor について紹介します。

設定

設定を環境変数に格納する

III. 設定 (The Twelve-Factor App)

本番環境、ステージング環境、開発環境などの環境によって変わるような情報は、プログラムコード内に直接記述せず、環境変数で管理してください。

  • データベースや Memcached などのバックエンドサービスへの接続情報
  • Google Cloud Platform や Twitter などの外部サービスの認証情報
  • デプロイごとに変化する値

MAGELLAN では、Worker ごとに環境変数を管理する機能を提供しています。この機能を使って環境変数を設定すると、Worker からその環境変数が読み取れるようになります。

tips用アイコン

MAGELLAN が提供する Google Cloud SQL を利用する場合は、その接続に関する情報が環境変数に自動設定されます。設定される環境変数は、次表のとおりです。

環境変数 内容
MYSQL_HOST データベースの IP アドレス
MYSQL_PORT データベースのポート番号
MYSQL_DATABASE データベース名
MYSQL_USERNAME データベースのユーザ名
MYSQL_PASSWORD 上記ユーザのパスワード

これらの環境変数は、環境変数管理機能での設定は不要です。設定不要で、Worker から読み取ることができます。

ポートバインディング

ポートバインディングを通してサービスを公開する

VII. ポートバインディング (The Twelve-Factor App)

Worker は、ウェブサーバーを組み込んだアプリケーションとして構成し、ポートバインディングを通してサービスを公開してください。

ウェブサーバーのアプリケーションへの組み込みは、The Twelve-Factor App の「依存関係 」の Factor に従い実施してください。

例えば、Ruby on Rails の場合は、Gemfile に組み込むウェブサーバーを宣言し、bundle install コマンドを使って組み込みます。次の例は、Pumaウェブサーバを組み込む際の Gemfile での記述です。

gem 'puma'

また、Worker は、Docker コンテナとして動作しますので、コンテナ起動時に組み込んだウェブサーバーを起動させてください。Dockerfile の CMD 命令を使います。公開するポート番号は、何番でも構いません。

次の例は、Ruby on Rails アプリケーションに組み込んだ Pumaウェブサーバーを起動する CMD 命令の記述です。ここでも、The Twelve-Factor App の「依存関係 」の Factor に従い、bundle exec コマンドを使用して、組み込んだ Pumaウェブサーバを起動しています。なお、公開するポート番号は、この例では何も指定していませんので、デフォルトの 3000 となります。

CMD ["bundle", "exec", "rails", "s", "Puma"]

クライアントから Worker へのアクセスは、MAGELLAN 経由となりますが、このときのポート番号は、HTTP は 80、HTTPS は 443 と固定です。このため、ポート 80 / 443 と Worker のポートをバインドさせなければなりません。

これには、magellan-proxy のポートバインディング機能(--port オプション)を使います。

magellan-proxy を使ったポートバインディングの様子

次の例は、先ほどの Ruby on Rails アプリケーションの公開ポート番号 3000 を MAGELLAN の公開ポート(80/443)にバインドさせる記述です。magellan-proxy に、--port オプションを追加しています。

CMD ["/usr/app/magellan-proxy", "--port", "3000", "bundle", "exec", "rails", "s", "Puma"]

並行性

プロセスモデルによってスケールアウトする

VIII. 並行性 (The Twelve-Factor App)

MAGELLAN のスケールアウト機能は、Docker コンテナを複数台起動させることで実現します。

よって、メモリを共有することを前提にして、アプリケーションを書かない(スレッドや Fiber のみ による並列化を前提にしない)でください。

ログ

ログをイベントストリームとして扱う

XI. ログ (The Twelve-Factor App)

Worker のログは、ファイルに書き出すのではなく、標準出力に書き出してください。

MAGELLAN では、標準出力をログとして収集します。この収集されたログは、MAGELLAN コンソールで確認できます。

Ruby on Rails アプリケーションの場合は、Rails 12factor を利用すると、ログ(logger)の書き出しを標準出力に切り替えることができます。利用はとても簡単です。Gemfile に次の記述を追加するだけです。

gem 'rails_12factor', group: :production

管理プロセス

管理タスクを1回限りのプロセスとして実行する

XII. 管理プロセス (The Twelve-Factor App)

次に挙げるような管理タスクは、1 回限りのプロセスとして実行させてください。

  • データベースのマイグレーションを実行する。(例:Djangoにおける manage.py migrate やRailsにおける rake db:migrate
  • 任意のコードを実行したり、生のデータベースに対してアプリケーションのモデルを調査したりするために、コンソール(REPLシェルとも言われる)を実行する。多くの言語ではインタプリタを引数なしで実行する(例:pythonperl)ことでREPLを利用できるが、別のコマンドを用意している場合もある(例:Rubyでの irb や Railsでの rails console)。
  • アプリケーションのリポジトリにコミットされた1回限りのスクリプトを実行する(例:php scripts/fix_bad_records.php)。

XII. 管理プロセス (The Twelve-Factor App)

この 1 回限りの管理プロセスは、Worker と全く同じ環境で実行されるべきです。

MAGELLAN では、この管理プロセス実行のための Migration Command 機能を提供しています。

機能名 説明
Migration Command 1 Worker リリース時の初回 1 回だけ登録されたコマンドを実行します。
Migration Command 2 Worker リリースごとに毎回 1 回だけ登録されたコマンドを実行します。

留意事項

Worker 開発時に留意すべき事項があります。以下の点に留意して、開発を進めてください。

  • イメージ名について
    Worker 用の Docker イメージ名は、バージョン管理のために、タグやダイジェストを使って、イメージ名を適切に設定してください。詳しくは、「Worker 開発の留意事項」の「イメージ作成」の項を参照してください。

  • Worker への URI について
    Worker にアクセスするための URI に、/ping というパスは使用できません。このパスは、MAGELLAN で予約されています。MAGELLAN システム内で、Worker との疎通確認のために使用します。詳しくは、「Worker 開発の留意事項」の「Worker へのパス」の項を参照してください。

  • MQTT の Topic について
    Worker 宛に Publish したいメッセージの Topic は、必ず worker/ で始めてください。worker/ で始まらない Topic は、Worker に Publish されません。詳しくは、「Worker 開発の留意事項」の「Worker へのトピック」の項を参照してください。