DockerとECSで初めてマイクロサービス化(するチュートリアルを)してみた

こんにちは。大阪オフィスのかずえです。マイクロサービス/Docker/ECSの取っ掛かりとして、モノリシックアプリケーションをマイクロサービスに分割するというチュートリアルをやってみました。

このチュートリアルがリリースされたのが2018年初頭のようで、 2019年現在のECSコンソールの画面構成が変わっていて戸惑ったり、単純に知識不足でハマった箇所があったので、そういった箇所をレポートします。今後このチュートリアルをやってみる方の一助になれば幸いです。

前置き そもそもなぜ今マイクロサービスとかDockerとかが注目されてるの

マイクロサービスとは

アプリケーションの各コンポーネントがそれぞれサービスとして独立しており、各サービス間はAPIを介してやりとりするような形態のアーキテクチャの事を指します。

対立する概念としてモノリシックアーキテクチャがあります。モノリシック=一枚岩という意味で、マイクロサービスとは逆に各コンポーネント間の独立性が保たれていないアーキテクチャのことを指します。

近年のアプリケーション開発の現場では、リリースしてその後の改修は無し、ということはほとんどありません。リリース後も継続的にPDCAサイクルを回し改良を求められるケースが増えています。

モノリシックアーキテクチャはこのような継続的な改良に不向きです。コンポーネント間の独立性が保たれていないため改修の影響範囲が最大でアプリケーション全体になり、「こんなところにも影響があった」「全然関係なさそうな機能でエラーが発生した」「そもそもどこに影響あるか特定するのが大変」ということが起こりやすいからです。大規模なアプリケーション、長年改修を続けてきたアプリケーションほどそういったことが起きやすいと言えるでしょう。

一方、マイクロサービスアーキテクチャではコンポーネント間がAPIを境目に独立しているので、APIの振る舞いさえ変えなければ内部のコンポーネントの改修の影響範囲はそのコンポーネント内に抑えることができます。なんならプログラミング言語ごとごっそり変えちゃう、みたいなこともできます。そのため前述のモノリシックで起きがちな問題を抑えることができ、PDCAのサイクルを高速に回すことが可能になります。このように近年のアプリケーション開発現場の要件にマッチしているため、マイクロサービスは注目されているのです。

いちエンジニアから見れば、いわゆる技術的負債の増加をできる限り抑えてハッピーになる手段、とも言えるでしょう。 芋づる式に増える影響範囲、バグの対応に追われて小さな機能改修にも大きな工数を割かれて「なにやってんだろ俺」と疲弊するといった経験、ありませんか。私はあります(たくさん)。そういう場面を減らしどんどん新機能をリリースしてビジネスサイドに喜んでもらおう、みたいなことが実現できるのではないでしょうか。

Dockerとは

すごく端的に説明すると、マイクロサービスを実現するのに適している仮想化技術、です。他の仮想化技術に比べて軽量に動作します。コンテナと呼ばれる仮想環境単位で各マイクロサービスをホストし、APIで連携しあいます。

ECS(Amazon ECS)とは

Amazon Elastic Container Serviceの略です。すごく端的に説明すると、Dockerコンテナを管理するためのawsのサービス、です。マイクロサービスごとにコンテナをドコドコ作っていると管理が大変になります。また需要に応じて特定のコンテナをスケールアウト(イン)させたい、可用性を維持したい、というようなニーズも出てくると思います。そういった手間を軽減してくれるサービスです。

前置きはこのくらいにして。チュートリアルやってみた際に私が詰まった箇所をレポートしていきます。

補足

このチュートリアルには動画もあります。ですのでわからなくなったらこちらを確認するのも良いかと思います。

筆者の環境

  • macOS Mojave 10.14.3
  • Docker version 18.09.1, build 4c52b90
  • aws-cli/1.16.93 Python/2.7.10 Darwin/18.2.0 botocore/1.12.83

チュートリアル内容

モジュール 1 – モノリスのコンテナ化

イメージのビルドでエラーになる1

$ docker build -t api
"docker build" requires exactly 1 argument.
See 'docker build --help'.

Usage:  docker build [OPTIONS] PATH | URL | -

Build an image from a Dockerfile

コマンドの文法エラーです。引数が足りていません。

チュートリアルに「. が重要であることに注意してください。」と書かれている通り、末尾に「 .」が必要です。

$ docker build -t api .

イメージのビルドでエラーになる2

$ docker build -t api .
Sending build context to Docker daemon  8.704kB
Step 1/6 : FROM mhart/alpine-node:7.10.1
Get https://registry-1.docker.io/v2/mhart/alpine-node/manifests/7.10.1: unauthorized: incorrect username or password

ビルドするイメージの元となるイメージをmhart/alpine-nodeから取得するところで、認証失敗エラーになっています。

私の場合、Docker for Macでログアウトするとエラー解消しました。

イメージを ECR にプッシュするところでエラーになる

以下のようなメッセージが表示されます。

$ tag does not exist: (アカウントID).dkr.ecr.(リージョン).amazonaws.com/api:latest

エラーメッセージの通り、前段の「イメージにタグを付ける」で(v1というタグを付けましたが、)latestというタグを付けていないため発生しています。 動画でもlatestタグを付けているので、改めてlatestタグを付けてからdocker pushしましょう。

$ docker tag api:latest (アカウントID).dkr.ecr.(リージョン).amazonaws.com/api:latest

モジュール 2 – モノリスのデプロイ

起動タイプの互換性の選択ってなに?

「ステップ3:タスク定義を記述する」ですが、チュートリアルでは[Create new Task Definition]の後はタスク定義名を入れるフォームが出てくることになっています。ですが実際やってみると「起動タイプの互換性の選択」が出てきます。

ここはEC2を選択でOKです。

※この設定項目の意味について知りたい方は、こちらをどうぞ。

Auto Scalingの設定

「ステップ 6: サービスとしてモノリスをデプロイする」です。

3画面目のフォームは「Auto Scaling (オプション)」ですが、こちらはチュートリアルには記載がありません。

ここはデフォルトのまま「サービスの必要数を直接調整しない」でOKです。

サービスの作成でエラーになる

「ステップ 6: サービスとしてモノリスをデプロイする」です。

「サービスの作成」ボタン押下後、こんな感じで Service: AWSServiceDiscovery; Status Code: 400; Error Code: NamespaceNotFound となった場合は、戻るボタンで戻り、ネットワーク構成 > サービスの検出 (オプション) > サービスの検出 (オプション)のチェックボックスのチェックを外してください。

テストしても全般503になる

「ステップ 7: モノリスをテストする」です。

チュートリアルに記載されているどのURLにアクセスしても503エラーになる場合、タスクがうまく立ち上がっていない可能性があります。 ECSコンソールのクラスターページ「タスク」タブに移動し、状況を確認してみてください。 Runningステータスのタスクが0件で、Stoppedのステータスのタスクに沢山タスクが表示されていないでしょうか?Stoppedのどれかのタスクのidをクリックして詳細画面に遷移し、原因を確認しましょう。

画面下部コンテナ欄の状況がSTOPPEDとなっており、詳細欄に以下のようなメッセージが記載されていないでしょうか。

CannotPullContainerError: Error response from daemon: manifest for (アカウントID).dkr.ecr.(リージョン).amazonaws.com/api:v1 not found

「ステップ3:タスク定義を記述する」で作成したタスク定義の中のコンテナのイメージのタグが間違っている可能性があります。「〜api:v1」を指定していませんか?こちらはモジュール1でpushしたECRリポジトリイメージのURLである必要がありますので、モジュール1でlatestタグをpushしていた場合はそちらを指定する必要があります。

タスク定義は一度作成すると変更できません。「新しいリビジョンの作成」から新リビジョンを作成してください。そしてそのバージョンを指定するように注意して再度サービスの作成フォームを進んでみてください。

http://[DNS name]/api/posts が Not Found になる

「ステップ 7: モノリスをテストする」です。

「/api/users」「/api/threads」は結果が返ってくるのに、「/api/posts」は Not Found になります。

これはNot Foundで大丈夫です!エンドポイントのNode.JSのプログラムを眺めていただければお分かりになるかと思いますが、postsに関してはusersやthreadsとは異なりドリルダウンしたエンドポイントしかありません。「/api/posts/in-thread/1」や「/api/posts/by-user/2」などで結果が返って来ればOKです。

モジュール 3 - モノリスの分割

tagはlatestにしておきましょう

「ステップ 3:各サービスのイメージをビルドしタグを付ける」です。 モジュール1と同じく、チュートリアルではタグ値がv1となっていますが、latestを付けるようにしておきましょう。モジュール4でlatestタグを使っているためです。

モジュール 4 - マイクロサービスのデプロイ

ステップ 1:サービスのタスク定義を記述する の設定箇所がわからない

コンソールで設定する場合、記載されている項目をどこに設定するのか戸惑うかもしれません。 要はモジュール2と同じように設定すれば良いです。

モジュール 5 – クリーンアップ

特に詰まった部分はありませんでした。

おわりに

DockerとECSの雰囲気、またECSでマイクロサービス化する際の基本的な方法が理解できました!