非プログラマー向けにプログラムを実行する環境をAmazon ECSで提供したお話

こんにちは。ヘルステックチームの近藤です。

先日、こんなお話を社内の方からいただきました。「月末にこれこれこういう作業をやっているのですが、これを短縮、単純、便利にする方法ってありますか?」と。まあ細かい話はともかくとして、これはいわゆる自動化案件です。プログラマーのおでましです!

しかし、このお話をした方はプログラマーではありません。ですので、「じゃあその作業を自動化したスクリプトを書いたので、ターミナルを立ち上げて、このコマンドを実行してください。あ、更新があったらGitHubからpullしてくださいね」なんて言うのははばかられます。

そこで私はAmazon EC2 Container Srvice(以下ECS)を用いることにしました。今回はECSでどういったものを提供したのかご紹介します。

その前にそもそも何故ECSなのか。以下のような思案がありました。

  • 前提として、提供したい解決法はスクリプトひとつを実行すれば十分なものであった
  • それに対し、ウェブアプリケーションでリッチなUIを提供するのはオーバースペックだし、サーバー費用もかかる
  • かといってローカルの環境で実行するとなると、動作する環境がない可能性がある(処理系が入っていない、ライブラリが入っていない、それらが古くなる、etc...)
  • ならばDockerを用い、仮想環境でそのスクリプトを実行すれば良い
  • しかしOS Xの環境ではDockerのためにVirtualBoxをインストールする必要があり、それなりにディスク容量を喰う
  • ならばコンテナーの環境はECSに委ねよう!

いやーAmazonには頭が上がりませんね。

クラスターを作成し、ECSインスタンスを登録する

ECSを利用するにあたり、ECSとは何なのか。そしてどのような仕組みで動作しているかを先に説明します。

ECSはコンテナー型仮想化ソフトウェアであるDockerを用いて、Dockerイメージからコンテナーを実行するための仕組みです。いわゆるクラウド的に。

それだけ聞けばなるほどと言う感じかもしれませんが、ECS独自の用語がいくつかあります。まずはタスク定義とタスクです。タスク定義とは、どのDockerイメージを用いてコンテナーを実行するか、という定義です。ポートマッピングなどのオプションも設定できます。そしてその定義を元に実行したものがタスク。つまりECSでは実行しているコンテナーをタスクと呼びます。

そしてこのタスクを実行する場所。それがクラスターと呼ばれるものです。ですので、タスクを実行するときは、「どのクラスター上で」「どのタスク定義を元にタスクを実行するか」を選ぶ必要があります。

ということでクラスターの作成から始めていきます。マネジメントコンソールから作成してももちろん良いのですが、クラスターがひとつも無いとウィザードによる諸々の作成が始まってしまうため、クラスターの作成はAWS CLIから行ってみます。

$ aws ecs create-cluster --region us-east-1 --cluster-name reraku-tools

{
   "cluster": {
       "status": "ACTIVE",
       "clusterName": "reraku-tools",
       "registeredContainerInstancesCount": 0,
       "pendingTasksCount": 0,
       "runningTasksCount": 0,
       "activeServicesCount": 0,
       "clusterArn": "arn:aws:ecs:us-east-1:******:cluster/reraku-tools"
   }
}

リージョンはどこでも良いのですが、後述するECRを使えるリージョンが限られており、今回はそのリージョンに合わせたかったので、とりあえずバージニア北部(us-east-1)を選びました。

これでクラスターは作られましたが、これだけではタスクを実行できません。実際にタスクを実行するためのEC2インスタンスが必要になります。それをECSインスタンスと呼び、そのインスタンスにはAmazon ECS Container Agentをインストールする必要があります。

ECSインスタンスのためのAMIもあるのですが、Amazon ECS Container Agent自体がDockerイメージになっているため、そのAMIを用いなくとも導入は比較的簡単です。詳しくはaws/amazon-ecs-agentをご参照ください。

なおAmazon ECS Container Agentはデフォルトだとdefaultという名前のクラスターに登録しに行こうとするようなので、/etc/ecs/ecs.config で以下のように設定しておいた方が良いでしょう。

# /etc/ecs/ecs.config
ECS_CLUSTER=reraku-tools

登録先のクラスターが正しく設定されており、Amazon ECS Container Agentがきちんと動作していれば、マネジメントコンソールで対象のクラスターのRegistered Container Instancesが1になっているはずです。

f:id:k0nd0:20160504190927p:plain

Dockerイメージを作成し、Amazon EC2 Container Registryに配置する

クラスターの準備が整ったら次はタスク定義…… と行きたいところですが、タスク定義はどのDockerイメージを使用するかを設定する必要があります。ですので、先にDockerイメージを作成し、ECSがアクセスできるところに配置します。

ECSがアクセスできればどこでも良いとは思いますが、ECSにはAmazon EC2 Container Registry(以下ECR)というおあつらえ向きのものがあるので、そちらを利用します。

ひとつ注意です。クラスターの作成の項でも言いましたが、すべてのリージョンでECRが使えるわけではありません。マネジメントコンソールのECSのメニューにRepositoriesがあればECRが使えます。

それではDockerイメージを格納するためのリポジトリを作成します。マネジメントコンソールから適当な名前で作成してください。すると下記のような画面が表示されます(一部の箇所は伏せています)。

f:id:k0nd0:20160504204812p:plain

あとは記載の通りの手順を手元で行えば、Dockerfileを元にDockerイメージを作成し、さきほど作成したリポジトリにDockerイメージが格納されるはずです。なおDockerfileの詳細は割愛します。

タスクを定義する

タスクを実行するためのクラスターおよびECSインスタンスを作成しました。タスクの元となるDockerイメージも配置しました。これであとはタスクを定義してあげれば実行のための準備が整います。

タスク定義はマネジメントコンソールのTask Definitionsから作成できます。さまざまな設定項目がありますが、Container nameおよびImageが必須なのでそちらを埋めます。Container nameは適当(今回は "resolve-the-problem" とします)、Imageは "******.dkr.ecr.us-east-1.amazonaws.com/resolve-the-problem:latest" のように入力します。あとはDockerイメージに合わせてWorking directoryやEntry pointを入力します。

なお、タスクの実行時にファイルの出力が伴う場合も当然あると思います。その場合、そのままだとコンテナーの中にだけそのファイルが出力され、取り出すことが出来なくなってしまうので、ECSインスタンスとコンテナーのファイルシステムを対応付けておく必要があります。

まずVolumesからAdd volumeをクリックし、NameおよびSource Pathを入力します。Source PathはECSインスタンス側のパスとなります。

f:id:k0nd0:20160504211818p:plain

Addボタンをクリックし、ボリュームを作成します。その上でコンテナーの設定のSTORAGE AND LOGGINGにあるMount pointsを設定します。入力するパスはコンテナー側のパスとなります。

f:id:k0nd0:20160504212213p:plain

これでコンテナーの /root/app/result ディレクトリへ書き出されたファイルはECSインスタンスの /home/ec2-user/resolve-the-problem/result ディレクトリから得られるようになります。

タスクを実行する

あとはタスクを実行するだけです。タスクの定義までがプログラマーが準備するところで、タスクを実行するところが利用者の方になります。ですので、その方の環境で何を用意すれば良いかも併せて説明します。

まずはAWS CLIです。結局CLI使うのかよ、という感じですが、マネジメントコンソールで操作してもらうのも難しい話ですし、ECSのタスクを実行するスクリプトだけ書き、それに実行権限を与え、ダブルクリックして使ってね、という方がよっぽどシンプルかなと思います。

ですので:

  1. IAMで利用者の方のユーザーを作成する
  2. AccessKeyIdおよびSecretAccessKeyを取得する
  3. AWS CLIをインストールする
  4. $ aws configure でAccessKeyIdおよびSecretAccessKeyを設定する

これでECSのタスクを実行することが出来るはずです。あとは:

$ aws ecs run-task --cluster reraku-tools --task-definition resolve-the-problem

を実行するだけで、resolve-the-problemタスク定義を元にしてreraku-toolsクラスターでタスクを実行します。あとはこれを:

#!/bin/bash
aws ecs run-task --cluster reraku-tools --task-definition resolve-the-problem

としてファイルに保存し、実行権限を付けておけば、手軽にタスクを実行できるようになりました。

タスクの実行結果の取り出し方あれこれ

これでめでたし、と言いたいところですが、さきほどまでで述べたのはタスクを実行するところまでです。まだタスクを実行した結果を取り出せていません。

今回私はコンテナーのボリュームを用い、出力されたファイルをECSインスタンスからも取り出せるようにしましたが、これでは利用者の方に「このアイコンをダブルクリックしたらプログラムが動くので、しばらく経ったらこのソフトウェア(SCPクライアント)を立ち上げてファイルが出力されているか確認してみてください」みたいなことを言うしかありません。というか実際そう言いました。実はECSを用いると決めるよりも前にスクリプトを書いていたため、出力がローカルファイルシステム前提になっており、それが足を引っ張った感じです。

結局利用者の方には上記のような説明をしましたが、後でいろいろと考えてみました。とりあえず思い浮かんだものは、「タスクの実行結果はメールで知らせる」というものです。例えば:

  • タスクが終わった時点でメールが送られ、いちいち利用者から結果を確認する必要がない
  • 実行結果の成否をメールに記載できる。失敗時はログを記載したメールをプログラマーに送れる
  • 出力されたファイルはメールに直接添付する。ファイル容量が大きければS3へアップロードし、ダウンロード用のURLを記載するなりで対応

ということが出来ます。スクリプトの結果がメール前提になってしまいますが、悪くはないかなと思います。

最後に

ひとまずECSを用いることによって、「じゃあその作業を自動化したスクリプトを書いたので、ターミナルを立ち上げて、このコマンドを実行してください。あ、更新があったらGitHubからpullしてくださいね」と説明するところが、「このアイコンをダブルクリックしたらプログラムが動くので、しばらく経ったらこのソフトウェア(SCPクライアント)を立ち上げてファイルが出力されているか確認してみてください」にはなりました。いつかは「このアイコンをダブルクリックしたらプログラムが動くので、そのうちメールが届いて結果を知らせてくれます」にしたいですね。

いかなる環境下でも統一された環境を提供できる仮想環境と、その仮想環境をホスティングできるサービスによって今回は実現し、その手順を紹介したわけですが、「他にもこういう方法があるよ」などがあれば是非教えていただければと思います。それでは。