LoginSignup
2

More than 5 years have passed since last update.

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

Last updated at Posted at 2017-03-27

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のスケールは意味がない

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2