見出し画像

Amazon Lightsailでコンテナが使えるようになったので検証してみた

こんにちは、varu3です。そういえばちゃんと報告をしていませんでしたが、今年の9月からnote株式会社でSREとして入社しました。今後ともよろしくお願いいたします 🙇

この記事は noteのみんな Advent Calendar 2020 の9日目の記事となります。

普段からnoteにもっと技術系の記事が増えればいいな〜と思っているので、アドベントカレンダーの中では空気を読まずに技術系の内容となります。恐縮です😅😅😅

この記事を書いていたら少々長くなったので要約すると、年末年始で時間のあるWebエンジニアのみんな〜〜〜〜〜、AWS Lightsailでコンテナイメージが使えるようになって超気軽にWEBアプリを公開できるようになったから開発しようぜ〜〜〜みたいな内容です。

tl;dr

・Amazon Lightsail Containerで気軽に低コストでコンテナをデプロイできるようになった
・Rails(Puma)のイメージを用意して起動することができた。nginxとの併用も可能。
・中身はAWSのサービス(ALB, Fargate等)を組み合わせたものだった
・datadog-agentを起動することでメトリクスを取得できた
・ちょっとしたアプリケーションなら十分実用性があるが、細かいことしようとするならば素直にECSかEKSにしよう

Amazon Lightsailとは

Amazon LightsailはAWSにプライベートサーバを作ることができるサービスです。仮想マシン、SSD ベースのストレージ、データ転送、DNS 管理、静的 IP アドレス、ロードバランサーなどが提供されています。

Lightsailで提供されているそれぞれの機能と同様のサービスはAWSにあるのですが、Lightsailはバックエンドを隠匿してAWSにあまり詳しくない人でも使いやすい形で提供されているのが特徴です。

従来はVPS(仮想インスタンス)としての利用のみだったのですが、11月のアップデートによってコンテナをデプロイすることができるようになりました。

コンテナをLightsailにデプロイするだけで、自動的にHTTP(S)のエンドポイントが付与されるようになり、さらに容易に便利に気軽にWebアプリケーションを公開しやすくなりました。

コンテナサービスの構成と用語を整理

AWSのサービスを触る上で用語を予め整理しておきましょう。特にコンテナ界隈では "デプロイメント" や "サービス" という用語が頻繁に登場するので、正しく理解しておくことが重要です。

この辺りの情報は公式ドキュメントに非常にわかりやすくまとまっているので1度目を通しておくとよさそうです。ところで、Lightsailは本家のAWSのドキュメントと違ったフォーマットなんですね。

https://lightsail.aws.amazon.com/ls/docs/en_us/articles/amazon-lightsail-container-services

これによると構成は次のようになっています。

画像1

ドキュメントに書かれている内容で重要そうなところをまとめてみました。

サービスでPower(Nodeのスペック)とScale(Nodeの数)を設定します
・各Nodeのコンテナは全て同じ物が配置されます
・ただし、1サービスあたりのNode数は20つまで

各サービスにデプロイメントを作成します。デプロイメントによってコンテナの設定をバージョン管理します。設定するのは次の項目です。
・起動するコンテナ名、コンテナイメージ
・コンテナが起動した時に実行するコマンド
・コンテナの環境変数
・開放するコンテナのポート

注意事項
・パブリックアクセスが可能となるコンテナはデプロイメントの中で1つだけ
・1 デプロイメントあたりのコンテナ数は10つまで

お金の話

あと気になるのはコストですね。どのような課金体系になっているのかを確認しておきます。先述の公式ドキュメントの "Pricing"の項目と次のドキュメントに詳細が書かれています。

https://aws.amazon.com/jp/lightsail/pricing/
https://aws.amazon.com/jp/lightsail/faq/

画像2

最低月額$7というのは素晴らしい低価格です。同じAWSのサービスでコンテナをデプロイするFargateやALBを利用すると大体 $30/月弱くらいかかってしまいますし。

こちらのページも重要そうな項目をまとめておきます。

1ノードあたりで料金が加算される
・月当たり500GBのデータ転送量が含まれている。超えた場合は 0.14 USD/GB (東京リージョン)が発生する
・さらにマネージドデータベース、CDN、ロードバランサー等の機能を利用することで、それぞれ月額が加算されていく

注意事項
・月末までに Lightsail コンテナサービスを削除した場合(利用が1ヶ月に満たなかった場合)、使用した合計時間数に基づいて按分計算された料金が請求される
コンテナサービスが無効の場合でも料金が発生する。費用を発生しないようにするには削除する。

AWSさんのドキュメントはFAQにさらっと重要そうな内容が書かれていたりすることもあるので、よく目を通しておかないとね😅 って感じです。

Railsアプリをコンテナサービスにデプロイしてみる

それではここからは実際にLightsailコンテナサービスにDockerイメージをデプロイしてみましょう。

デプロイ方法は次の方法があります。
ローカルマシンからCLI経由でコンテナイメージをPushする方法
・DockerHubなどのパブリックリポジトリに置いて使用する方法
(2020年12月現在、Lightsail コンテナサービスでサポートされているのはパブリックコンテナレジストリのみ)

今回はローカルマシン経由でPushする方法を試してみます。

aws cliは最新版を利用して、さらにlightsailを操作するためのプラグインを入れます。
・aws cliのインストールはこちら
・プラグインの導入手順はこちら

# プラグインを入れる
$ curl "https://s3.us-west-2.amazonaws.com/lightsailctl/latest/darwin-amd64/lightsailctl" -o "/usr/local/bin/lightsailctl"
$ chmod +x /usr/local/bin/lightsailctl
$ xattr -c /usr/local/bin/lightsailctl

Railsのscaffoldで5秒で簡単なアプリを作ってみます。

$ rails new todo \                                                                                   1590ms
 --skip-action-mailer \
 --skip-action-mailbox \
 --skip-action-text \
 --skip-active-storage \
 --skip-action-cable 
$ bin/rails generate scaffold task content:text
$ bin/rails webpacker:install

$ bin/rails s # railsサーバを起動する

これで localhost:3000にアクセスするとこんな感じ。

画像3

まずはLightsailにサービスを作っておきます。

$ aws lightsail create-container-service \                                                                                                                                      10.1s
 --service-name varu3-rails-app \
 --power nano \
 --scale 1

このサービスを作った時点でパブリックなURLが発行されるので Railsのconfig.hosts に追記しておきます。今回は便宜上、設定を簡略化したいのでRAILS_ENV=development にしておきます。(パブリックに公開されるのでRAILS_ENV=development にするには注意が必要です)

# file: config/environments/development.rb

Rails.application.configure do
 ...
 config.hosts << "<host名>"
 ...
end

次にDockerfileを用意して

FROM ruby:2.6.6

RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \
   && echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list \
   && apt-get update \
   && apt-get install -y --no-install-recommends \
      nodejs \
      yarn \
   && apt-get clean \
   && rm -rf /var/lib/apt/lists/*


WORKDIR /app
COPY . /app

RUN yarn install  \
 && gem install bundler \
 && bundle install

CMD ["bin/rails""s""-b""0.0.0.0"]

これをビルドしておきます。

$ docker build . -t todo:latest

ビルドしたコンテナイメージをPushします。

$ aws lightsail push-container-image \                                                                                                                                          419ms
 --service-name varu3-rails-app \
 --image todo:latest \
 --label rails

Pushしたイメージは以下のコマンドで確認できます。イメージ名が変わるので注意が必要です。

$ aws lightsail get-container-images --service-name varu3-rails-app                                                                                                            1037ms
{
   "containerImages": [
       {
           "image"":varu3-rails-app.rails.1",
           "digest""XXX",
           "createdAt""XXX"
       }
   ]
}

で、このイメージを利用してデプロイメントの設定ファイルを作ります。まずは雛形を出力しておいて…

$ aws lightsail create-container-service-deployment --generate-cli-skeleton
{    
   "serviceName""",
   "containers": {
       "KeyName": {
           "image""",
           "command": [
               ""
           ],
           "environment": {
               "KeyName"""
           },
           "ports": {
               "KeyName""HTTP"
           }
       }
   },
   "publicEndpoint": {
       "containerName""",
       "containerPort"0,
       "healthCheck": {
           "healthyThreshold"0,
           "unhealthyThreshold"0,
           "timeoutSeconds"0,
           "intervalSeconds"0,
           "path""",
           "successCodes"""
       }
   }
}

これに必要な項目を埋めていきます。こんな感じ。

{
   "containers": {
       "api": {
         "image"":varu3-rails-app.rails.1"#先ほどのイメージ名
         "environment": {
             "RAILS_LOG_TO_STDOUT""true",
             "RAILS_ENV""development" #検証用なのでdevelopmentとしています
           },
           "ports": {
               "3000""HTTP"
           }
       }
   },
   "publicEndpoint": {
       "containerName""api",
       "containerPort"3000,
       "healthCheck": {
          "healthyThreshold"2,
          "unhealthyThreshold"2,
          "timeoutSeconds"3,
          "intervalSeconds"5,
          "path""/",
          "successCodes""200"
       }
   }
}

lightsail_container.jsonという名前で保存して、以下を実行します。

​$ aws lightsail create-container-service-deployment \ 
--service-name varu3-rails-app \
--cli-input-json file://$(pwd)/lightsail_container.json

# 確認(stateがDeployedになったら完了)
$ aws lightsail get-container-service-deployments \ 
--service-name varu3-rails-app

そしてエンドポイントにアクセスしてみます。

# 200で返ってくる
$ curl https://varu3-rails-app.XXXX.ap-northeast-1.cs.amazonlightsail.com/tasks

というわけで本当に10分くらいでアプリをデプロイすることができました。よかったですね。

と、ここまではまあ普通の内容なのでここからは応用編ということで詳細をみていくこととします。

コンテナ間通信

コンテナ間で通信をするにはそれぞれのコンテナでポートを開いて、 localhost を経由することで通信が可能となります。

例として nginx をForward ProxyとしてRails(Puma) へ疎通させるデプロイメントを作ってみます。Lightsailではパブリックエンドポイントしか持つことができないので、Railsで使う際にはnginxでルーティングやアクセスを制御できると非常に便利ですよね。

まずは nginxの設定ファイルを用意します。Port 80でListenしてlocalhostの3000へ流すようにします。

# files/webapp.conf

server {
   listen       80;
   server_name  _;

   location / {
       proxy_set_header Host $http_host;
       proxy_set_header X-Real-IP $remote_addr; 
             
       proxy_pass http://localhost:3000;
   }

   location = /50x.html {
       root   /usr/share/nginx/html;
   }
  
   error_page   500 502 503 504  /50x.html;
}

続いて、Dockerfileを用意します。

FROM nginx:latest
ADD files/webapp.conf /etc/nginx/conf.d/default.conf

# 検証なので nginx-debugで起動する
CMD ["nginx-debug""-g""daemon off;"]

Railsの時と同様にビルドしてプッシュします。

$ docker build . -t nginx-lightsail:latest
$ aws lightsail push-container-image --service-name varu3-rails-app --image nginx-lightsail:latest --label nginx

pushできたら返ってきたイメージ名を利用して、デプロイメントの設定ファイルを作ります。パブリックエンドポイントの向き先を nginx の port 80に変更します。

{
   "containers": {
       "api": {
           "image"":varu3-rails-app.rails.X",
           "environment": {
               "RAILS_LOG_TO_STDOUT""true",
               "RAILS_ENV""development"
           },
           "ports": {
               "3000""HTTP"
           }
       },
       "nginx": {
           "image"":varu3-rails-app.nginx.X",
           "ports": {
               "80""HTTP"
           }
       }
   },
   "publicEndpoint": {
       "containerName""nginx",
       "containerPort"80,
       "healthCheck": {
           "healthyThreshold"2,
           "unhealthyThreshold"2,
           "timeoutSeconds"3,
           "intervalSeconds"5,
           "path""/",
           "successCodes""200"
       }
   }
}

これでデプロイします。

$ aws lightsail create-container-service-deployment --service-name varu3-rails-app --cli-input-json file://(pwd)/lightsail_container.json

先ほどのエンドポイントにリクエストすると nginxで返しているのがわかります。

$ curl -I https://varu3-rails-app.6r4d6qd44j3a0.ap-northeast-1.cs.amazonlightsail.com/
HTTP/2 200
server: nginx/1.19.5
...

例えば特定のIPからしかアクセスさせたくないときやBASIC認証を入れたい時など、nginxを利用したい場面があるのでこれは便利ですね。

コンテナ間でのファイル共有・ディスク永続化

コンテナ間でのボリュームを共有してファイルのやりとりができないかなと思って試してみたのですができませんでした。
また、Lightsailの機能でストレージディスクの追加があるのですが、こちらもコンテナサービスでは使用できないようでした。

コンテナからRDBを使うにはどうしたらいいの?って話ですがLightsailにはデータベースの機能がある(おそらくRDS)ので、素直にこれを使うのが一番楽かなと思います。

元々コンテナ自体はステートレスで扱う想定にしておくものなので、ディスクの永続化はできなくてもいいんですが、ファイルの共有などはできたらさらに嬉しいです。どちらも2020年12月現在の情報ですのでこれはアップデートに期待したいところですね。

Datadogコンテナエージェントを入れてみる

Lightsailはアラートが飛ばせるとはいえ、コンソール画面からCPUとメモリ使用率のメトリクスしかなく大変心許ない仕様でした。

ところが、こちらの記事にあるようにLightsail Containerは(薄々気づいていたけど)Fargateで動いているらしく、mackerel-agentでメトリクスが取得できるようです。ということは同じく監視ツールであるDataDogでもいけるんじゃね?と思ったのでレッツ検証です。

先ほどのRails + nginxのあるサービスにさらにdatadog-agentのコンテナを追加します。

{
   "containers": {
...
       "datadog": {
           "image""datadog/agent:latest",
           "environment": {
               "DD_API_KEY""XXXXXXXXXx,
               "ECS_FARGATE": "true"
           }
       }
   },
...
}

環境変数にはECS_FARGATEとDD_API_KEYを設定します。これでデプロイしてみましょう。

画像4

はい、あっさりとメトリクスを取得することができました。ただしコンテナ名が内部的なものとなってしまっているので、どのコンテナがどのイメージを使ったものかがわかりにくいです。まあ何もないよりは良いかなということで。

まとめ: AWSでまずコンテナを扱うならこれで良いんじゃないか

格安でかつ手軽にアプリケーションをデプロイする基盤として十分に実用性できるものだと感じました。例えばEC2で運用しているWAFを使ったサービスや、ちょっとしたAPI、Wordpressで作ったブログなどをコンテナ化して置く場所としては十分選択肢としてありだと思います。

ただし以下の様に微妙な点もありました。

・コンテナイメージを取ってくるリポジトリがパブリックリポジトリしか対応していない。プライベートリポジトリ(ECRとか)が使えたら嬉しい
・コンテナログを追うのが辛い。コンソール画面からだと1行ずつチョロチョロと表示されて遅いですし、CLIだとNextPageTokenで一つずつ追わないといけない仕様
・コンテナ間のファイルのやりとりができない
・メトリクスがCPU使用率とメモリ使用率のみ
・エンドポイントがパブリックのみ。VPCからのみアクセスできるプライベートなエンドポイントとかあったら嬉しい
・デプロイにかかる時間が若干遅い時がある

まだリリースされたばかりだからなのか、Lightsailのeasy思想ゆえなのかはわかりませんが、痒いところに手が届かない部分があります。周辺ツールやアップデートで改良されることもあるかと思いますので、この辺りは今後に期待したいですね。コンテナの細かな制御をしたい場合や複数コンテナを組み合わせたマイクロサービスを展開したい場合には素直にECS/EKSを用いることをオススメします。

最後に

というわけで新しい気になるサービスがリリースされたのでざっと触ってみたら大変楽しく検証することができました。Lightsailコンテナの中身が(おそらく)FargateだってわかったのでSSM Agent入れてゴニョゴニョしたらシェルにも入れそうですし、その辺りもまた調べた結果がわかればまとめてみようと思います。

こういった新しいサービスをドキュメントを眺めたりするだけではフワッとした理解のままになってしまうのですが、ざっと触っておくと感触が掴めますし、必要になった時に自信を持ってオススメできるので大変良いですね。

私自身が個人でコンテナを使うなら、AWSだとLightsail一択かな〜って思うくらいの好感触だったんですが、先日のre:inventでAWS Lambdaでもコンテナイメージをサポートしたのがアナウンスされたっぽくてこっちも気になっています。

AWS Lambda の新機能 – コンテナイメージのサポート
https://aws.amazon.com/jp/blogs/news/new-for-aws-lambda-container-image-support/

俺たちの戦いはまだまだこれからだ!という感じですね。コンテナを完全に理解した、までの先は長そうです。

というわけで noteのみんな Advent Calendar 2020 9日目でした。

おまけのおまけ

ヘルスチェックのログ

172.26.46.26 - - [07/Dec/2020:15:01:38 +0000"GET / HTTP/1.1" 200 612 "-" "ELB-HealthChecker/2.0" "-"
172.26.28.211 - - [07/Dec/2020:15:01:38 +0000"GET / HTTP/1.1" 200 612 "-" "ELB-HealthChecker/2.0" "-"
172.26.1.169 - - [07/Dec/2020:15:01:38 +0000"GET / HTTP/1.1" 200 612 "-" "ELB-HealthChecker/2.0" "-"
172.26.46.26 - - [07/Dec/2020:15:01:43 +0000"GET / HTTP/1.1" 200 612 "-" "ELB-HealthChecker/2.0" "-"
172.26.28.211 - - [07/Dec/2020:15:01:43 +0000"GET / HTTP/1.1" 200 612 "-" "ELB-HealthChecker/2.0" "-"
172.26.1.169 - - [07/Dec/2020:15:01:43 +0000"GET / HTTP/1.1" 200 612 "-" "ELB-HealthChecker/2.0" "-"

HTTPのレスポンスヘッダ

$ curl -I https://varu3-rails-app.XXXXXXX.ap-northeast-1.cs.amazonlightsail.com/healthcheck
HTTP/2 503
server: awselb/2.0
date: Mon, 07 Dec 2020 16:14:19 GMT
content-type: text/html
content-length: 162

エンドポイントも明らかにALBでしたね。

この記事が気に入ったらサポートをしてみませんか?