tl;dr
自作のsinatraアプリがAWS ECSで動いた図
— sasaki (@yysaki) 2019年3月18日
コンテナ考えることが多くて難しいな pic.twitter.com/7Rkl5ZYHB1
図のように、AWS初心者がECSコンテナ上にRDBをRead/WriteするWebアプリを動かすまでの手順です。
TODO:
- 自作WebアプリのDockerイメージをFargateタスクとして起動させた
- WebアプリからRDSへのアクセス方法
- System ManagerのSecureStringを環境変数にとりコンテナからRDSにアクセス方法
前置き
Sinatra
未経験からRuby on Railsを学んで仕事につなげるまでの1000時間メニュー をやっています。
未経験ではないので記載どおりの時間はかけていませんが自学するのにバランスがよいと思っています。
学習の過程でDBアクセスの伴うSinatraアプリケーションを作りました。
URL: https://github.com/yysaki/sinatra-workout-recording
AWS
また並行してAWSについて手を動かしてます。
普段触らないモダンな環境がどんなものか知りたいというのがモチベーションです。
1月ごろから手を動かしていましたようです:
dotinstallの流れで設置したS3バケットにRoute 53でドメインひもづけたやつhttps://t.co/YVJOR0WxIf
— sasaki (@yysaki) 2019年1月15日
今回Sinatraアプリの一環でこれのDockerイメージを作ったので、AWSのコンテナ上で動かしてみたくなりました。
Amazon EKSが東京リージョンに対応した 話もありますが、ハマりどころが多そうなのでまずは手始めにECSから。
実際に手を動かしてみると、
- RDBとECSが接続するためファイアウォールをどう設定するか
- 秘密情報であるDBの接続情報をどうコンテナに渡すか
などが疑問になり、解決できたので書きます。
AWSの下準備
これら本題ではありませんが、自分の理解度の確認のため書きます。
AWSアカウントの初期設定
最初の IAM 管理者のユーザーおよびグループの作成 - AWS Identity and Access Management
アカウントを作成したらこの記事に従ってやります。
ルートユーザーを使わずにAdministrator用アカウントを使用するのが記事中でベストプラクティスとされているので、セキュリティステータスを埋めておきます:
まだある程度手を動かしてからこの記事に気づきました。
AWSアカウントを取得したら速攻でやっておくべき初期設定まとめ - Qiita
大変参考になりますのでおいおい追従していきたいです。
AWS CLIのインストール
ECRのpushに使うのでAWS CLIをインストールしておきます。
AWS CLI のインストールと設定 - Amazon Kinesis Data Streams
以下のようにCLIのインストール、初期設定をします。
pip install awscli
aws configure
リージョンはap-northeast-1を指定します
成功すれば ~/.aws/ 配下にconifgおよびcredentialsが保存されます。
ECSのチュートリアルをやっておく
Fargate を使用した Amazon ECS の使用開始 - Amazon Elastic Container Service
この手順で一度サービスを稼働させて、Fargateのクラスターや ecsTaskExecutionRole IAMロールを用意しておきます。
ECRにDockerイメージを仕込む
Amazon ECR における Docker の基本 - Amazon ECR
これはCLIで簡単に作業が完結できました。
「(オプション) イメージの Amazon Elastic Container Registry へのプッシュ」を参考にリポジトリへpushします:
$(aws ecr get-login --no-include-email)
aws ecr create-repository --repository-name sinatra-workout-recording
docker pull yysaki/sinatra-workout-recording
docker tag yysaki/sinatra-workout-recording {aws_account_id}.dkr.ecr.ap-northeast-1.amazonaws.com/sinatra-workout-recording
docker push {aws_account_id}.dkr.ecr.ap-northeast-1.amazonaws.com/sinatra-workout-recording
ECSタスクを起動してRDSに接続させる
ここからが本題です。
まずRDSを立てる
MySQL DB インスタンスを作成して MySQL DB インスタンス上のデータベースに接続する - Amazon Relational Database Service
この手順の通り進めてMySQLインスタンスを作成します。
このとき、「開発/テスト - MySQL」のユースケースを選び、「RDS 無料利用枠の対象オプションのみを有効化」をチェックして作成すれば無料枠で済みます。
RDSにアクセスできるようにする
通常はファイアウォールによりRDSに直接アクセスすることはできません。
そこで、「VPCセキュリティグループ」から図のようにローカルIPのCIDRを指定することでローカルからも接続できるようになります。
環境変数の保持
DBアカウント情報は秘匿すべき情報なので、ソースコードに記載せず環境変数で取り扱うやりかたを計画しました。
今回のsinatraのアプリではactiverecordを使いますが、環境変数DB_URLでRDSへの接続文字列を渡すことにします。
ActiveRecord::Base.establish_connection(ENV['DB_URL'])
書式は mysql2://{DBユーザ名}:{DBパスワード}@{エンドポイント}/{DB名}
で、一つの環境変数で設定できるので便利でした。
ECSにサービスを作成する前にECSコンテナにDBアクセスの情報を安全に渡す仕組みが必要なのですが、以下の記事がためになりました。
【祝!】FargateでもECSにごっつ簡単に環境変数に機密情報を渡せるようになりました!
AWS Systems ManagerのパラメータストアにSecureStringとして保持し、これをECSタスクから読み出す仕組みです。
ごくごく最近導入された機能のようで手間が少なくて助かります。
図のように設定を行います:
公式ドキュメントとしてはこれです:
機密データの指定 - Amazon Elastic Container Service
環境変数を設定したECSタスクを立てる
ECSのタスク定義を行います。
先程のSecureStringを読めるよう、以下のjsonによる「ユーザによる管理」タイプのポリシーを定義し、 ecsTaskExecutionRole ロールに付与します:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ssm:GetParameters",
"secretsmanager:GetSecretValue",
"kms:Decrypt"
],
"Resource": "*"
}
]
}
また、タスク定義を作成する際、「コンテナの定義」 > 「コンテナの編集」 > 「環境」の環境変数に対して図のように設定します。
その他は先の「ECSのチュートリアルをやっておく」の要領でECSタスク定義して、サービスを稼働させればよいです。
コンテナからRDSに接続できるようにして動作確認
VPCセキュリティグループの設定を行ってRDSに接続できるようにします。
- ECSタスクがアクティブになったら「詳細」タブにある「Public IP」を見て、RDSのVPCセキュリティグループのインバウンドのルールに追記する
- ただしIPアドレスは可変なので、安定稼働させるうえで改善の余地が大いにあると感じます
以上で設定が完了しタスクが稼働していますので「Public IP」にブラウザからアクセスすることでアプリの動作が確認できました。
ECS -> RDSへの接続については課題を感じるので、どう自動化するかは今後の課題として調べていきたいとお思います。
なお料金
RDBは無料枠がありますが、ECSでは起動タスクに対する課金を避けられない認識です。
しかしFargate起動タスクはvCPUを細かく刻めるので、お試しに安く使えて便利でした。
0.5GB 0.25vCPUのFargateを稼働させた課金額のslack通知の様子です:
初めの1.08$はRoute53のドメイン管理で、残りがECSのぶんです。
丸一日稼働させても0.41$程度で済みました。