Spring BootアプリケーションをAWSにデプロイする仕組み

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

よく訓練されたアップル信者、都元です。花粉が落ち着いてきた気がします。やっと春の空気を胸いっぱいに吸い込めます。

前回、Spring Bootで書いたアプリケーションをGradle、CloudFormation、Elastic Beanstalkを使ってデプロイするデモをご紹介しました。今回はその詳細をご紹介します。

Gradle

前回のデモでも示した通り、env/personal.gradle というファイルに環境固有の設定を記述しています。具体的には、AWSのアクセスプロファイル名(~/.aws/credentialsに記述してあるもの)や、デプロイ先のリージョン等です。

このファイルはbuild.gradle内で読み込まれ、その情報をタスク実行時に利用します。

gradle-aws-plugin

このサンプルシステムをAWSにデプロイするには、Gradle上で下記のような手順を踏みます。

  1. bootRepackage: Spring Bootアプリケーションをビルドする。成果物はjarファイル。
  2. createBundle: アプリケーションバンドルの作成。成果物はzipファイル。
  3. uploadBundle: アプリケーションバンドルのS3へのアップロード。
  4. awsCfnUploadTemplate: CloudFormationテンプレートのS3へのアップロード。
  5. awsCfnMigrateStack: CloudFormationに対して、CreateStack または UpdateStack を投げる。

この手順はGradleによるビルドスクリプトで記述してありますが、一部の操作はGradleの標準機能ではないため、spring-boot-gradle-plugingradle-aws-pluginというプラグインを利用しています。

このプラグインを導入することにより、上に挙げた bootRepackage 等のタスクが利用できます。

また、ステップ4及び5で利用するのが、下記で説明するCloudFormationテンプレートです。

CloudFormation

AWS CloudFormationは、JSON形式で記述したテンプレート(AWSインフラの設計図)を入力とし、そのテンプレートに従ってAWSの環境構築を行う仕組みです。詳しくは過去エントリー「CloudFormation入門」をご覧ください。

今回利用した仕組みでは、このCloudFormationを使ってアプリケーションの動作環境を用意し、その上にアプリケーションをデプロイしています。このテンプレートファイルは、/src/main/cloudformation/berserker.template に配置してあります。

このテンプレートでは7つのパラメータを受け取ります。

パラメータ 解説
KeyName 起動するEC2インスタンスに設定するSSH鍵名。EC2に登録済みである必要があります。
CIDEPrefix ネットワーク環境としてVPCを構築しますが、そのIPアドレス帯域の先頭2オクテット分(ex. 10.123.0.0/16 を利用したければ 10.123)を指定します。
DBUsername RDSに作成するMySQLのユーザ名。
DBPassword 上記ユーザのパスワード。
EnvironmentType 環境の起動タイプを指定し、起動するインフラ規模を調整します。詳細は後述。
SolutionStack アプリケーションのデプロイにはAWS Elastic beanstalkを利用しています。そのSolution Stack名を指定します。これも後述。
BerserkerVersionLabel アプリケーションのバージョン名を記述します。これも後述します。

EnvironmentType

EnvironmentTypeは local, development, production の3択です。localは特殊なので後回しで。

developmentproductionは、単純にインフラ規模の違いです。開発環境であれば、RDSのMultiAZはfalseで構いませんが、本番環境はtrueにしたいでしょう。その他、EC2インスタンスのタイプやAutoScalingの数等、任意にコントロールすると良いと思います。(これは、テンプレートを作る人が気にすればOKな話です。)

localは「ローカル開発環境で起動するアプリケーションが必要とするAWSリソース」のみを構築するモードです。わかりづらいですよね。

S3やDynamoDB、SQS、SNS等は、頑張ればローカル環境に代替実装を用意できます。しかし、そこを頑張るのではなく、AWS上にリソースを用意してしまって、ローカル環境からAWSのリソースを使おう、という魂胆です。

そのような目的であるため、維持コストが高くてローカル環境に実装を用意しやすいリソース(具体的にはRDSやEC2インスタンスですね)は起動せず、維持コストが低くてローカル環境に実装を用意しづらいリソースだけを構築するのが local モードです。

Solution stack (AWS Elastic beanstalk用語)

Beanstalkでは、Java, Node.js, PHP, Python, Ruby等、様々なアプリケーションの動作環境を提供しています。その環境の種類(及びバージョン)のことを Beanstalk 用語で Solution stack と呼びます。

本プロジェクトはJavaのプロジェクトですが、そのアプリケーションをDockerコンテナの上で動かすように作ってありますので、Solution stack としては 64bit Amazon Linux 201x.0x v2.x.x running Docker 1.x.x といったものを使います。

すなわち SolutionStackパラメータには 64bit Amazon Linux 2016.03 v2.1.0 running Docker 1.9.1 を設定します。(執筆時点での最新。しかし意外と早くバージョンアップしていくので各時点で利用できる最新版を利用してみてください。)

BerserkerVersionLabel

今回ご紹介するCloudFormationテンプレートは s3://elasticbeanstalk-${Region}-${AccountId}/eb-apps/berserker/berserker-${BerserkerVersionLabel}.zip というファイルをBeanstalkにデプロイします。複数のバージョンをこのS3バケットに置いておき、BerserkerVersionLabel パラメータを書き換えることで、自由にデプロイするバージョンをコントロールできます。

AWSインフラの情報をアプリケーションに伝える仕組み

今回の例では、CloudFormationでRDSのMySQLデータベースインスタンスを作成しますが、そのホスト名やDBユーザ名、パスワード等は動的に決まるパラメータです。従って、アプリケーションの設定ファイル(propertiesファイル等)としてソースコードと一緒に管理できません。

このような情報は、アプリケーションの立場としては、環境変数に入っているものとして実装します。具体的には、Springの設定クラスであるDataSourceConfigurationのように記述することにより、環境変数を読みだしています。

次に、Beanstalkには、起動時にConfiguration Optionsを与えることによって、それをアプリケーション動作環境の環境変数に設定してくれる機能があります。

その大元として、CloudFormationでは、Beanstalkのenvironmentに対して JDBC_CONNECTION_STRING, DB_USERNAME, DB_PASSWORD, SPRING_PROFILES_ACTIVE という4つの設定を定義しています。

まとめ

  1. env/personal.gradle に環境固有の設定定義をする。
  2. Gradleがアプリケーションのビルドを行い、諸々をS3にアップロードする。
  3. Gradleから設定値をパラメータ経由でCloudFormationに伝える。
  4. CloudFormationは、設定値をOption settings経由でBeanstalk Environmentに伝える。
  5. Beanstalkは、設定値を環境変数経由でアプリケーションに伝える。

このような仕組みをきちんと整備することにより、ポータビリティの高いシステム開発が実現できますね。