Edited at

Elixir/Phoenix環境をElasticBeanstalk Custom Platformで構築してみた

More than 1 year has passed since last update.


ElasticBeanstalk Custom Platformとは?

今までのElasticBeanstalkでは事前定義済みの環境にアプリケーションをデプロイすることで環境を構築していたが、2017/02/22のリリースでCustom Platformを定義することが可能になった。

これまでもベースAMIをもとにカスタマイズした環境を利用することは可能だったが、AMIの管理(ビルドやバージョン管理、それらを利用したデプロイなど)は自力で行う必要があった。

今回リリースされたCustom PlatformはPackerを利用したAMIのビルドやバージョン管理、デプロイをマネージドサービスとして提供している。

なので、ためしにElixir/Phoenixを起動するCustom Platformを構築してみた。(してみたというレベルに見合わない時間がかかったけど)

副産物としてerlangのAmazon Linux用のrpmができた。sonodar/erlang-rpm

作成したCustom PlatformのテンプレートやプロビジョニングソースはGithubにあげておきました。

ソースの解説は一番最後に書いてあります。まずはCustom Platformを動かすところから。


Phoenixアプリケーションのビルド方針


  • Exrm(Elixirのビルドライブラリ)を利用したコンパイル済みバイナリのデプロイではなく、ソースをデプロイしてElasticBeanstalk(以降、EB)環境上でソースのビルドを行うものとする。

  • DBおよびecto(Elixirのスキーママイグレーションライブラリ)は使わない構成とする


    • ecto使う事自体は簡単なのでIssueを追加しておいた



  • brunch(Phoenixのアセットコンパイル用npmパッケージ)も使わない構成とする


Custom Platformのビルド


awsebcliのインストール

$ brew install awsebcli


テンプレートソースのclone

$ git clone https://github.com/sonodar/elasticbeanstalk-phoenix-platform.git

$ cd elasticbeanstalk-phoenix-platform


ElasticBeanstalk Custom Platformのビルド

以下のコマンドでローカルディレクトリに.elasticbeanstalk/config.ymlファイルが作成される。

$ ebp init -r ap-northeast-1

Enter Platform Name
(default is "elasticbeanstalk-phoenix-platform"):

この時点ではまだプラットフォームの作成は行われていない。

プラットフォームの作成は以下のコマンドで行う。

初回実行時にPacker実行用のElasticBeanstalk環境(Custom Platform Builder)も自動的に作成される。

$ ebp create

INFO: createPlatform is starting.
INFO: Initiated platform version creation for 'elasticbeanstalk-phoenix-platform/1.0.0'.
INFO: Creating Packer builder environment 'eb-custom-platform-builder-packer'.
INFO: Starting Packer building task.
INFO: Creating CloudWatch log group '/aws/elasticbeanstalk/platform/elasticbeanstalk-phoenix-platform'.
# ... 中略 ...
INFO: 'packer build' finished.
INFO: Successfully built AMI(s): 'ami-93a5fef4' for 'arn:aws:elasticbeanstalk:ap-northeast-1:xxxxxxxxxxxx:platform/elasticbeanstalk-phoenix-platform/1.0.0'
INFO: Creating CloudWatch log group '/aws/elasticbeanstalk/platform/elasticbeanstalk-phoenix-platform'.
INFO: Successfully built AMI(s): 'ami-93a5fef4' for 'arn:aws:elasticbeanstalk:ap-northeast-1:xxxxxxxxxxxx:platform/elasticbeanstalk-phoenix-platform/1.0.0'
INFO: Packer built AMIs: ami-93a5fef4.
INFO: Successfully created platform version 'elasticbeanstalk-phoenix-platform/1.0.0'.

作成された環境の確認。StatusReadyとなっていればプラットフォームの作成が成功している。

$ ebp list

arn:aws:elasticbeanstalk:ap-northeast-1:xxxxxxxxxxxx:platform/elasticbeanstalk-phoenix-platform/1.0.0 Status: Ready


今回用のサンプルPhoenixプロジェクトの作成

Macの場合はおもむろに以下のコマンドを打つ。

brew install elixir

mix local.hex
mix archive.install https://github.com/phoenixframework/archives/raw/master/phoenix_new.ez
mix phoenix.new eb_phoenix --no-brunch --no-ecto
cd eb_phoenix

依存ライブラリをインストールするかどうか聞かれるのでy応答

Fetch and install dependencies? [Yn] y

動作確認。以下のコマンドを実行して http://localhost:4000 にアクセス。

mix phoenix.server # 停止は Ctrl+C を2回

こんな画面が出れば成功。

Hello-EbPhoenix.png


サンプルアプリケーションを作成したCustom Platformで起動

eb initコマンドで初期化する。platform選択で13) Custom Platformを選択。

$ cd eb_phoenix

$ eb init -r ap-northeast-1
Select an application to use
1) Custom Platform Builder
2) [ Create new Application ]
(default is 2):

Enter Application Name
(default is "eb_phoenix"): eb-phoenix

Select a platform.
1) Node.js
2) PHP
3) Python
4) Ruby
5) Tomcat
6) IIS
7) Docker
8) Multi-container Docker
9) GlassFish
10) Go
11) Java
12) Packer
13) Custom Platform
(default is 1): 13

Select a platform.
1) elasticbeanstalk-platform-phoenix (Owned by: xxxxxxxxxxxx)
(default is 1): 1
Do you want to set up SSH for your instances?
(Y/n): Y

Select a keypair.
1) sonodar
2) [ Create new KeyPair ]
(default is 1): 1

eb createコマンドでEB環境を作成する。今回はお試しなのでシングル構成(-sオプション)

$ eb create -s

Enter Environment Name
(default is eb-phoenix-dev): eb-phoenix-dev
Enter DNS CNAME prefix
(default is eb-phoenix-dev): eb-phoenix-dev
Creating application version archive "app-84db-170327_132118".
Uploading eb-phoenix/app-84db-170327_132118.zip to S3. This may take a while.
Upload Complete.
Application eb-phoenix has been created.
Environment details for: eb-phoenix-dev
Application name: eb-phoenix
Region: ap-northeast-1
Deployed Version: app-84db-170327_132118
Environment ID: e-7fshvxwyad
Platform: arn:aws:elasticbeanstalk:ap-northeast-1:xxxxxxxxxxxx:platform/elasticbeanstalk-platform-phoenix/1.0.0
Tier: WebServer-Standard
CNAME: eb-phoenix-dev.ap-northeast-1.elasticbeanstalk.com
Updated: 2017-03-27 04:21:21.816000+00:00
Printing Status:
INFO: createEnvironment is starting.
INFO: Using elasticbeanstalk-ap-northeast-1-xxxxxxxxxxxx as Amazon S3 storage bucket for environment data.
INFO: Created EIP: 52.197.66.130
INFO: Created security group named: awseb-e-7fshvxwyad-stack-AWSEBSecurityGroup-1T9MF5U46H463
INFO: Waiting for EC2 instances to launch. This may take a few minutes.
INFO: Adding instance 'i-065353dcfcb113901' to your environment.
INFO: Successfully launched environment: eb-phoenix-dev

以上。ElasticBeanstalkのHTTPエンドポイントにアクセスしてみるとPhoenixの画面が見れる。


Custom Platform テンプレートソース解説

TODO: あとで書く


ポイント


  • ElasticBeanstalkのデプロイシーケンスを理解する

  • PhoenixアプリケーションのDaemon/Service化にはUpstartを利用

  • ElasticBeanstalkのEnvironmentをパースしてファイルに出力

  • 今回のためにErlangのrpmを作成した (sonodar/erlang-rpm)


ハマったところ


ebp listebp deleteコマンドはCustom Platform Builderが起動していないとエラーになる。

Custom Platform BuilderはCustom Platformのビルド時以外は不要のため、普段は環境を落としておいたほうがいいのだが、落とした状態でebpコマンドを実行すると以下のように意味不明なエラーメッセージとともに失敗する。

ERROR: TypeError :: cannot concatenate 'str' and 'list' objects


ebp createコマンドはIAMのCreateRole権限がないと失敗する

ebp createを初めて実行した際にPacker実行用環境であるCustom Platform Builderが利用するaws-elasticbeanstalk-custom-platform-ec2-roleというIAMロールも自動生成される。

CreateRole権限がないとエラーになるため権限のあるアカウントか、以下のポリシーをアタッチしておくこと。

{

"Version": "2012-10-17",
"Statement": [
{
"Sid": "GrantCreateRole",
"Action": [
"iam:Create*"
],
"Effect": "Allow",
"Resource": "*"
}
]
}

それか、あらかじめaws-elasticbeanstalk-custom-platform-ec2-roleをEC2用のロール(インスタンスプロファイル付き)として以下のポリシーで作っておく。


信頼関係

{

"Version": "2008-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}


インラインポリシー(EB_Custom_Platform_Builder_Policy)

{

"Version": "2012-10-17",
"Statement": [
{
"Sid": "PackerEC2Access",
"Action": [
"ec2:AttachVolume",
"ec2:AuthorizeSecurityGroupIngress",
"ec2:CopyImage",
"ec2:CreateImage",
"ec2:CreateKeypair",
"ec2:CreateSecurityGroup",
"ec2:CreateSnapshot",
"ec2:CreateTags",
"ec2:CreateVolume",
"ec2:DeleteKeypair",
"ec2:DeleteSecurityGroup",
"ec2:DeleteSnapshot",
"ec2:DeleteVolume",
"ec2:DeregisterImage",
"ec2:DescribeImageAttribute",
"ec2:DescribeImages",
"ec2:DescribeInstances",
"ec2:DescribeRegions",
"ec2:DescribeSecurityGroups",
"ec2:DescribeSnapshots",
"ec2:DescribeSubnets",
"ec2:DescribeTags",
"ec2:DescribeVolumes",
"ec2:DetachVolume",
"ec2:GetPasswordData",
"ec2:ModifyImageAttribute",
"ec2:ModifyInstanceAttribute",
"ec2:ModifySnapshotAttribute",
"ec2:RegisterImage",
"ec2:RunInstances",
"ec2:StopInstances",
"ec2:TerminateInstances"
],
"Effect": "Allow",
"Resource": "*"
},
{
"Sid": "BucketAccess",
"Action": [
"s3:Get*",
"s3:List*",
"s3:PutObject"
],
"Effect": "Allow",
"Resource": [
"arn:aws:s3:::elasticbeanstalk-*",
"arn:aws:s3:::elasticbeanstalk-*/*"
]
},
{
"Sid": "CloudWatchLogsAccess",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
"logs:DescribeLogStreams"
],
"Effect": "Allow",
"Resource": "arn:aws:logs:*:*:log-group:/aws/elasticbeanstalk/platform/*"
}
]
}

[追記]

arn:aws:iam::aws:policy/AWSElasticBeanstalkCustomPlatformforEC2Roleというマネージドポリシーがあるからそれを使えばいいみたいだ。内容は上記インラインポリシーと同じ。


Custom Platform用のリポジトリとアプリケーション用のリポジトリは分けないといけない

Custom Platformテンプレート用のGitリポジトリとデプロイするソースのリポジトリは分けないといけない模様。

ソースリポジトリのサブディレクトリにCustom Platformビルド用のディレクトリを作成してebp initなどを実行すると以下のエラーになる。

$ ebp init

ERROR: This command is not supported for Application workspaces.

リポジトリの.elasticbeanstalk/config.ymlファイルに以下の記述があるので、ここを見ているようだ。


.elasticbeanstalk/config.yml

global:

# ... 略
workspace_type: Platform


拡張ヘルスチェック・・・?

よく知らないのだけど、ElasticBeanstalkの拡張ヘルスチェック機能というのがある。PIDファイルを監視してくれるらしい。

PhoenixはPIDをファイルに吐いてくれない。

ここを参考にUpstartの定義ファイルを作った。


参考


Dockerを使わない理由(あくまで個人的なイメージです)


  • イメージの管理が面倒(Dockerイメージ用のCI/CDが必要)

  • td-agentを使いたい場合、マルチコンテナ構成になって面倒

  • Dockerボリュームのレイテンシが気になる

  • インスタンスストア(エフェメラルディスク)を使いたい

  • プラットフォームカスタマイズの頻度が高い

  • EB on Dockerの場合、スケールはEC2単位?


    • ECSと違い、コンテナ単位でのスケールができない?

    • そもそも余剰リソースがなければDockerのスケールは意味がない