kakakakakku blog

Weekly Tech Blog: Keep on Learning!

Lambda を軸にサーバレスを解説した「実践 AWS Lambda」を読んだ

AWS Summit Tokyo 2017 の先行発売でゲットした「実践 AWS Lambda」をさっそく読んだ.Lambda の解説だけではなく,サーバレスの文脈で導入事例が増えてきたアーキテクチャ(ユースケース)の紹介がされていたり,サーバレスのデプロイツールとして AWS SAM の紹介がされていたりして,非常に良書だった.Amazon を見ると,一般発売は明日 6/9 になっていたので,気になる人はすぐに買うと良いのでは!

実践AWS Lambda ~「サーバレス」を実現する新しいアプリケーションのプラットフォーム~

実践AWS Lambda ~「サーバレス」を実現する新しいアプリケーションのプラットフォーム~

Chapter 3 AWS Lambdaの使い方

3-7 バージョニングとエイリアス

Lambda 関数の ARN にバージョンを付与して識別できるのは知らなかった.また,エイリアスを付けて運用できるのも知らなかった.実際に運用している Lambda 関数を見てみたところ,確かにバージョンなしの ARN もあったし,バージョンありの ARN もあった.ちなみに Lambda 関数は Apex でデプロイをしているため,Apex の実装を確認したら自動的に current エイリアスを付与するようになっていた.おおお!

  • arn:aws:lambda:ap-northeast-1:111111111111:function:sample
  • arn:aws:lambda:ap-northeast-1:111111111111:function:sample:$LATEST
  • arn:aws:lambda:ap-northeast-1:111111111111:function:sample:50
  • arn:aws:lambda:ap-northeast-1:111111111111:function:sample:current

docs.aws.amazon.com

3-10 環境変数の利用

暗号化ヘルパーを使うと,環境変数を KMS で暗号化できるのを知った.今のところ,暗号化が必要な環境変数を使っていないため,普通に Apex の project.json でベタ打ちして運用していた.

docs.aws.amazon.com

Chapter 4 プログラミングモデル

4-5 Lambda関数作成時のポイント

特定の言語に依存せず,対応言語全て (Node.js / Python / Java / C#) の解説があるのは素晴らしかった.また Lambda の特性に合ったアーキテクチャに関しては,AWS Summit Tokyo 2017 で講演があった内容とも似ていた.

speakerdeck.com

Chapter 8 サーバレスアプリケーション開発のデプロイ

個人的に1番良かったのは AWS SAM の紹介がされていた点だった.今まで Lambda のデプロイなら Apex を使って,API Gateway が必要なときは Serverless Framework を使っていたけど,AWS から公式に提供された AWS SAM はずっと気になっていてまだ使ったことが無かった.現状サポートされているリソースは Lambda と API Gateway と DynamoDB とのことだが,CloudWatch Events もサポートされると良いなと思う.Apex もここはサポートしていなくて,自動化できなかった.

github.com

本書には記載がなかったけど,AWS SAM で Tracing を設定すれば X-Ray も合わせてデプロイとのこと.気になる!

www.slideshare.net

誤植

初版だからかもしれないけど,typo 系の誤植があって少し気になった.実際に5箇所を発見したため,マイナビに報告をしておいた.確認してもらえれば,以下のサポートサイトに反映されるのかな?と期待している.

book.mynavi.jp

まとめ

  • Lambda を軸にサーバレス全体の理解を改めて整理できた
  • 実際に Lambda + API Gateway + DynamoDB を使うチュートリアルも紹介されていて未経験者にも参考になる内容だった
  • AWS SAM の紹介があり Lambda の運用まで考慮された解説範囲の広さだった

関連記事

先週参加した AWS Summit Tokyo 2017 の参加レポートも参照してもらえればと!

kakakakakku.hatenablog.com

実際に運用している Lambda + Apex の事例も前にまとめていて参照してもらえればと!

kakakakakku.hatenablog.com

AWS Summit Tokyo 2017 に参加して2日間ずっと AWS のことを考えていた

2017/05/30(火) ~ 2017/06/02(金) の期間で,計4日間開催されていた AWS Summit Tokyo 2017 に参加してきた.僕は後半2日間に参加して,非常に満足度が高く,もっともっと AWS を使い倒さなきゃ!とモチベーションが上がった.セッションを聞くだけではなく,ブースで話を聞いてみたり,AWS 認定資格取得者専用ラウンジに行ってみたり,様々な楽しみ方ができたのも良かった.簡単にメモをまとめておこうと思う.

aws.amazon.com

2017/06/01(木)

Speed matters - Amazon Kinesis が実現するストリーミングデータのリアルタイム分析

  • 多くのデータは「持続的に」生成される
  • 新しいデータほど,意思決定において価値が高い
  • Kinesis
    • Kinesis Streams
    • Kinesis Analytics
    • Kinesis Firehose
      • 2ヶ月以内に東京リージョンで使えるようになる
  • 細かく制御をするなら Kinesis Streams,フルマネージドに運用するなら Kinesis Firehose を使う
  • シャードを複数回リードできるため,この点はキューと特性が異なる
  • split-shard コマンド

Kinesis は全然使ったことがないので,話を聞いてきた.Firehose が東京リージョンで使えるようになる(やっと!)というのは期待できる.あと,途中で "Auto-Scaled Streams" という話があったはずだけど,詳細を聞き逃してしまったので,資料公開を待ちたいところ.あと Kinesis の事例として有名なスシローの話も出ていた.AWS をフル活用したアーキテクチャになっていて,これはクラウド導入を悩んでいる大企業にも刺さるなーと思った.

(資料公開待ち)

AWS Shield と AWS Lambda@Edge で構築するセキュアで柔軟性の高いアプリケーション

  • AWS Edge Services
    • CloudFront
    • Shield
    • Lambda@Edge
  • CloudFront を静的コンテンツと動的コンテンツの両方の配信で使う
    • エンドユーザーの近くを TLS の終端にする
    • 1秒間の TTL も使えるため,短い時間のキャッシュでも,スパイク時の負荷を下げられる
  • Lambda はリージョン内のイベントとして使うが,Lambda@Edge はエッジロケーションのイベントとして使える
    • 訪問者バリデーション
      • Bot 判定
      • アクセスログの UserAgent 判定
      • レスポンス生成
      • HTTP Strict Transport Security (HSTS) でヘッダーを書き換えることもできる
  • AWS Shield
    • Standard Protection
    • Advande protection
      • 攻撃されたときにレポートを提供する
      • 24時間体制でサポートをする
      • 請求保護もする
  • AWS WAF
    • 特定の IP を一定時間をブロックすることができる

CloudFront 以外は使う機会がなく,特にエッジロケーションで軽量な処理が行える Lambda@Edge は,かなり未来な感じがするので気になる.

(資料公開待ち)

Cloud connect the world as a Glue

  • SRE Team とは
  • JP / US / UK 3拠点で一緒に会うことは難しい
  • Hybrid & Multi Cloud な構成になっている
    • JP : SAKURA
    • US : AWS
    • UK : GCP
  • あえてフルマネージドサービスを使わずに,仮想サーバを使うことで,環境間の差異をなくしている
  • グローバルなサービスを作るときは距離を意識する(超えられない壁)

AWS フル活用!という事例ではなかったし,他の勉強会でも似た話を聞いたことがあったため,新しい情報はあまりなかったけど,何度見てもメルカリの SRE Team は強いなという印象だった.

speakerdeck.com

Startup CTO Night with Amazon CTO ~Werner Vogels による公開技術レビュー~

chikaku,Axelspace,Repro,3社の CTO がビジネス概要とアーキテクチャを発表し,Werner のレビューを受けるというイベントだった.聞いてる側としては非常に参考になったけど,レビューされる側は相当大変だ.「アップロード機能をセキュアにするために直接 S3 じゃなくて API を経由している」という話に対して「なぜ S3 ではダメなの?」とズバっと質問が出たときはちょっと背筋が凍った.全体を通して,アーキテクチャよりも,コスト計算を強く意識するべきというメッセージを感じ取ったし,そういう目線で経営に参画してこそ CTO なんだろうなとも感じた.

2017/06/02(金)

全部教えます!サーバレスアプリのアンチパターンとチューニング

  • なぜ遅いのか?
    • プログラムの問題
    • コンピューティングリソースの不足
    • コールドスタート
    • アーキテクチャの問題
    • 同時実行数
  • コンピューティングリソースの不足
    • メモリ設定はコンピューティングリソース全体の設定
      • CPU バウンドな処理の場合は,メモリを増やす
  • コールドスタート
    • VPC を利用する場合は,ENI を作成するため,10-30秒も掛かってしまう
    • CloudWatch で取得できる Duration の値には,ENI の作成時間,コンテナ作成,ランタイム起動時間も含まれない
      • 言い換えれば,課金対象にならないということ
  • アーキテクチャの問題
    • API Gateway を Kinesis のバックエンドとして API にすることもできる
    • Lambda では RDBMS を使わずに DynamoDB などを使うと良い
  • 同時実行数
    • 平均実行時間とイベント数から,同時実行数が算出できる
    • 上限緩和も可能だが,現在の最大値は 1000 なので,ほとんどの場合は到達しないのでは

以前にも似たようなスライドを見たことがあったが,改めて理解をし直すことができて良かった.会場も満席で,注目度の高さが感じられた.同時実行数の計算ロジックは初耳だったが,ちゃんとドキュメントには書かれていた.

docs.aws.amazon.com

今週発売になる「実践 AWS Lambda」は,ブースで先行購入することができた!毎日売り切れになっていたようなので,買えて良かった.積読せずに,最優先で読もうと思う.

実践AWS Lambda ~「サーバレス」を実現する新しいアプリケーションのプラットフォーム~

実践AWS Lambda ~「サーバレス」を実現する新しいアプリケーションのプラットフォーム~

(資料公開待ち)

Amazon EC2 Innovation at Scale

  • Director of Product Management, EC2
  • C4 ➔ C5
    • C5 は Intel Skylake が乗っていて,そろそろ発表できそう
  • R4 と X1
    • R4 : GiB : cCPU = 8 : 1
    • X1 : GiB : cCPU = 16 : 1
    • 今年後半には 4TB の RAM を乗せた X1E を出す予定
  • コスト最適化
    • リザーブドインスタンス
      • スタンダード
      • コンバーティブル
    • スポットインスタンス
  • Amazon Lightsail
    • 気軽に開発環境が欲しい場合
    • AWS Summit Tokyo 2017 の初日に東京リージョンで使えることを発表した

EC2 のプロダクトマネージャーという仕事はかなり面白そうだと思った.去年の re:Invent のキーノートも見ていたし,リザーブドインスタンスとスポットインスタンスの話もある程度は理解していたため,あまり新しい情報は無かった.個人的に Amazon Lightsail を使う場面があるかどうかはわからないけど,東京リージョンで使えるようになったことは嬉しいことで,EC2 に敷居の高さを感じている人に使ってみて欲しいと思う.

(資料公開待ち)

Amazon EC2 Performance Deep Dive

  • プロセッサ関連
    • vCPU
      • ハイパースレッドの論理コア
      • ようするに,物理コアを仮想的に2個に分割する
      • よって,物理コアを2倍すると vCPU になる
    • ハイパースレッド無効化
      • lscpu で CPU レイアウトを確認する
      • 後半の論理 CPU をオフラインにする
      • もしくは chcpu コマンドを使ったり,GRUB の maxcpu カーネルパラメータを設定することもできる
    • P-state と C-state
    • NUMA バランシングサポート
  • ストレージ関連
    • EBS 最適化
    • st1 と sc1
    • インスタンスストアを適材適所に使う
  • ネットワーク関連
    • 拡張ネットワーキング
    • ENA カーネルドライバをインストールする
    • プレイスメントグループ
  • パフォーマンスクレジット
    • CPU と EBS だけではなく,r4 インスタンスにはネットワークのクレジットがある
  • ハードウェアアシスト仮想化 (HVM)
    • 余計な仮想化のオーバヘッドを無くす
    • なるべく HVM を使うことを推奨している
  • RHEL6 の カーネルは 2.6.32 で,2009年に登場したものなので,新しくする
  • チューニングをするときは,まず CPU から行うと良い

4日目で1番聞きたかったセッションに参加できた.プロセッサ関連の話は個人的に未知の領域で,実際に lscpu コマンドなどを試してみたりして勉強してみたいと思う.「詳解システムパフォーマンス」も買ったまま読めてないし,楽しみだ.ストレージとネットワークの話は,ある程度知っていた内容だったかな.困ったときにすぐインスタンスタイプを上げるのではなく,チューニングができるのかを考える癖を付けたい.

(資料公開待ち)

AWS マネージドサービスで実現する CI/CD パイプライン

  • AWS Code 4兄弟
    • CodeCommit
    • CodeBuild
    • CodeDeploy
    • CodePipeline
  • CodeStar
  • BlackDay Gate
    • デプロイしてはダメな日付の場合にデプロイを止める機能
    • DynamoDB にレコードを入れておいて,合致する場合はデプロイを止める

個人的に Rails のデプロイ環境を作るなら GitHub + CircleCI (or Jenkins) + Capistrano のような構成にすることが多いし,実際に Code 4兄弟を使ったことがなく,セッションを聞いてみた.最近発表された CodeStar もどういうレイヤーの開発者が使うんだろう?という疑問がある.簡単に環境が作れることは良いことだけども.あと途中に話が出てた CloudWatch Events と Lambda を使ったシンセティックトラフィック(外形監視とは違う?)の AWS Reference Architecture は,調べても情報が見つけられなかった.詳細が気になるので,もしドキュメントがあれば読みたい.

(資料公開待ち)

その他

AWS 認定資格を取得している人だけが入れるラウンジがあって,そこで休憩などをしていた.早めに行ったこともあって,ピンバッチ以外に傘をもらうことができて,嬉しすぎた.雨の日にドヤ顔で使おうと思う.早い時間だと飲み物もお菓子も提供されてるんだけど,あっという間になくなってしまうので,もう少し定期的な補給があると良かったように思う.

今さら後悔しているのが AWS DevOps Challenge に参加しなかったことで,ああ,これは参加しておけば良かったなと残念な気持ちになっている.絶対楽しいし,学べるし,チームワークも大切だし,挑戦したかった.次回こそは!

gameday-japan.connpass.com

まとめると,2日間非常に楽しめた!お疲れさまでした!

Data Migration Night に登壇してデータ移行の Tips を紹介してきた

今日は Data Migration Night に参加してきた & LT 枠で登壇してきた!

atnd.org

ChatWork がデータマイグレーションに使った技術の話

  • CQRS + Event Sourcing System
  • Aurora にある17億メッセージを HBase にマイグレーションする
  • 基本マイグレーションと差分マイグレーションに分類した
  • Aurora の binlog を直接取得して差分マイグレーションに使った
  • メトリクスを取得して,マイグレーションの成功をちゃんと確認するようにした
  • Aurora から Spark で HBase に書き込むときのパーティションを試行錯誤した

17億メッセージを管理する Aurora って,どんなインスタンスタイプを使ってるんだろう?質疑応答では Room ID でパーティショニングをしていると言っていた.あと資料に “巨大部屋” という表現があって笑った.Spark も HBase も使ったことがなくて,詳細はわからなかったけど,分散システムの特性を理解した上でマイグレーションを考えたという話は参考になった.

(資料公開待ち)

オンプレから AWS 移行で変えた3つの意識

  • コスト意識
    • RI をインスタンスの在庫確保として使う
  • AWS オペレーション意識
    • インフラ専任エンジニアの撤廃
    • IAM で細かく制御して,オペミスが起きないようにした
  • システムダウン意識
    • フルマネージドを使うようにした
    • サーバダウンを許容し,復旧できることを重要視した

インフラ専任を撤廃していくのは施策としては非常に興味深かった.もっと事例を聞いてみたかった.また RDS で適用必須なメンテナンスが入るとサービスダウンに繋がるので,前段で書き込みを止めて対応しているとのこと.メンテナンスを選択できれば良いんだろうけど,無停止前提だと厳しいよなぁ.

www.slideshare.net

2000万アカウントの無停止データ移行の裏側

大きな問題無く移行できたのは凄いと思う.個人的にはレプリケーション遅延も気になるし,循環更新も気になる.懇親会で話を聞いたら「今はもう役目を終えた」と言っていて,完全に移行専用のアーキテクチャとのこと.強い!あと open-replicator は全く知らなかったので,調べる.

speakerdeck.com

LT : どのように MySQL on EC2 から Aurora に移行したのか

僕は CyberAgent Developers Blog に投稿した記事の一部 + α を紹介した.Aurora に特化した内容はあまり話さず,データ移行の Tips を中心に話したけど,比較的反応もあって良かった!

developers.cyberagent.co.jp

資料も up した!

speakerdeck.com

LT : AWS のデータ移行ソリューション

  • AWS Direct Connect
  • AWS Snowball
  • AWS Snowmobile
  • などなど

(資料公開待ち)

関連記事

labotech.dmm.com

登壇写真

撮影してもらった!素晴らしい会場だったし雰囲気ある ( ゚д゚)ノシ

f:id:kakku22:20170523002440j:plain

Mackerel で ECS の動的ポートマッピングに対応したタスクのメトリクスを取得する

前回書いた記事に続き,Mackerel を使って ECS のメトリクスを取得する方法を検証していて,今回は「パターン2 : コンテナインスタンスに mackerel-agent をインストールする」の検証結果をまとめる.前提としては前回と同じで,ALB の動的ポートマッピングを使う.

  • パターン1 : mackerel-agent タスクをコンテナインスタンスごとに起動する
  • パターン2 : コンテナインスタンスに mackerel-agent をインストールする

kakakakakku.hatenablog.com

メリデメ

最初に「パターン2」のメリデメを整理しておく.「パターン1」では実現できなかった「タスク特有のメトリクスを取得できる」点が大きなメリットで,今回この「パターン2」を採用することにした.

  • メリット
    • mackerel-plugin-docker でコンテナインスタンスのメトリクスを取得できる
    • mackerel-plugin-docker が Docker API からメトリクスを取得するため,動的ポートマッピングでタスクが増えた場合も,基本的なメトリクスは取得できる
    • mackerel-plugin-gostats など,任意のプラグインを使って,動的ポートマッピングでタスクが増えた場合も,タスク特有のメトリクスを取得できる
      • 少し工夫が必要になる(本記事のポイント)
  • デメリット
    • mackerel-agent のインストールは cloud-init で頑張る(逆に cloud-init でプロビジョニングができるのはメリットと言えるかも?)
    • Mackerel のグラフ生成が難しい

構成図

「パターン2」の構成は以下のようになる.コンテナインスタンスに mackerel-agent をインストールしている点が「パターン1」とは異なる.

f:id:kakku22:20170518202609j:plain

前提

今回は mackerel-plugin-gostats を使って Golang のメトリクスを取得するため,前提として API に fukata/golang-stats-api-handler を導入しておく必要がある.実装は割愛するが,エンドポイントとしては /stats を用意した.

github.com

github.com

コンテナインスタンスの起動設定に指定する cloud-init 実装例

あくまで実装例として紹介するが,コンテナインスタンスに最低限必要になるプロビジョニングを全て cloud-init で実行した.こうすると Chef や Ansible を実行する必要もなく,Auto Scaling で負荷に応じて自動的に増減させることができる.AMI にする案もあるが,ECS で使う ECS-Optimized AMI も定期的に更新するだろうし,今はこのままにしている.

cloud-init に実装した内容をザックリと箇条書きにすると,以下のようになる.

  • timezone
    • タイムゾーンを変更する
  • write_files
    • /etc/mackerel-agent/mackerel-agent.conf を生成する
    • 動的ポートマッピングに対応したメトリクスを取得するための /root/ecs_gostats.sh を生成する
  • runcmd
    • mackerel-agentmackerel-agent-plugins をインストールする
    • /etc/ecs/ecs.config にクラスタ名を書き込む

cloud-init の実装例としては,以下のようになる.ロール名 / コンテナ名 / クラスタ名などは,適宜読み替えてもらえればと!

#cloud-config
timezone: "Asia/Tokyo"

write_files:
 - path: /etc/mackerel-agent/mackerel-agent.conf
   permissions: 0644
   content: |
     apikey = "xxx"
     roles = [ "yyy:zzz" ]
     include = "/etc/mackerel-agent/conf.d/*.conf"
     [host_status]
     on_start = "working"
     [plugin.metrics.gostats]
     command = "/root/ecs_gostats.sh"
     [plugin.metrics.docker]
     command = "/usr/bin/mackerel-plugin-docker"

 - path: /root/ecs_gostats.sh
   permissions: 0755
   content: |
     #!/bin/sh
     if [ "${MACKEREL_AGENT_PLUGIN_META}" != "1" ];then
       for PORT in $(docker ps --format='{{.Ports}}' --filter name=api | cut -f1 -d- | cut -f2 -d:); do
         /usr/bin/mackerel-plugin-gostats -port ${PORT} -path=/stats -metric-key-prefix=gostats.${PORT}
       done
     else
       /usr/bin/mackerel-plugin-gostats -metric-key-prefix=gostats.#
     fi

runcmd:
 - curl -fsSL https://mackerel.io/file/script/amznlinux/setup-yum.sh | sh
 - sudo yum install -y mackerel-agent
 - sudo yum install -y mackerel-agent-plugins
 - echo AUTO_RETIREMENT=1 >> /etc/sysconfig/mackerel-agent
 - mkdir /etc/mackerel-agent/conf.d/
 - service mackerel-agent start
 - sh -c 'echo ECS_CLUSTER=api-cluster >> /etc/ecs/ecs.config'

cloud-init の詳細は以下に載っている.

docs.aws.amazon.com

Amazon Linux に mackerel-agent をインストールする手順は以下に載っている.

mackerel.io

mackerel-agent.conf の詳細は以下に載っている.

mackerel.io

ecs_gostats.sh

今回のポイントは,この ecs_gostats.sh だと思う.

#!/bin/sh
if [ "${MACKEREL_AGENT_PLUGIN_META}" != "1" ];then
  for PORT in $(docker ps --format='{{.Ports}}' --filter name=api | cut -f1 -d- | cut -f2 -d:); do
    /usr/bin/mackerel-plugin-gostats -port ${PORT} -path=/stats -metric-key-prefix=gostats.${PORT}
  done
else
  /usr/bin/mackerel-plugin-gostats -metric-key-prefix=gostats.#
fi

まず,動的ポートマッピングに対応するため docker ps の結果から,ポートを抽出する.以下の例で言うと 32768 / 32770 / 32771 となる.

PORTS
0.0.0.0:32768->8080/tcp
0.0.0.0:32770->8080/tcp
0.0.0.0:32771->8080/tcp

次にそのポートごとに -metric-key-prefix オプションを使って,メトリクスを送信する.具体的には -metric-key-prefix=gostats.${PORT} とする.ポート番号 32768 を例にすると,実際取得できるメトリクスのキーは以下のようになる.

gostats.32768.gc.gc_num
gostats.32768.gc.gc_pause_per_second
gostats.32768.gc.gc_per_second
gostats.32768.heap.heap_idle
gostats.32768.heap.heap_inuse
gostats.32768.heap.heap_released
gostats.32768.heap.heap_sys
gostats.32768.memory.memory_alloc
gostats.32768.memory.memory_stack
gostats.32768.memory.memory_sys
gostats.32768.operation.memory_frees
gostats.32768.operation.memory_lookups
gostats.32768.operation.memory_mallocs
gostats.32768.runtime.cgo_call_num
gostats.32768.runtime.goroutine_num

さらに,重要なのは MACKEREL_AGENT_PLUGIN_META と言う環境変数の存在で,実は今まで知らなかった.簡単に言うと MACKEREL_AGENT_PLUGIN_META=1 でプラグインを実行すると,グラフ定義を出力する仕様になっている.今回はメトリクス名にポート番号を含めているため,グラフ定義も変更する必要がある.具体的には -metric-key-prefix=gostats.# と書いて,ポートの部分をワイルドカードで表現することができる.

すると,以下のようなグラフ定義を出力することができる.

  • gostats.#.gc
  • gostats.#.heap
  • gostats.#.memory
  • gostats.#.operation
  • gostats.#.runtime

JSON の部分は可読性のために整形しているが,実際はこんなグラフ定義を出力することができる.

$ MACKEREL_AGENT_PLUGIN_META=1 /usr/bin/mackerel-plugin-gostats -metric-key-prefix=gostats.#
# mackerel-agent-plugin
{
    "graphs": {
        "gostats.#.gc": {
            "label": "Gostats.# GC",
            "unit": "float",
            "metrics": [
                {
                    "name": "gc_num",
                    "label": "GC Num",
                    "stacked": false
                },
                {
                    "name": "gc_per_second",
                    "label": "GC Per Second",
                    "stacked": false
                },
                {
                    "name": "gc_pause_per_second",
                    "label": "GC Pause Per Second",
                    "stacked": false
                }
            ]
        },
        "gostats.#.heap": {
            "label": "Gostats.# Heap",
            "unit": "bytes",
            "metrics": [
                {
                    "name": "heap_sys",
                    "label": "Sys",
                    "stacked": false
                },
                {
                    "name": "heap_idle",
                    "label": "Idle",
                    "stacked": false
                },
                {
                    "name": "heap_inuse",
                    "label": "In Use",
                    "stacked": false
                },
                {
                    "name": "heap_released",
                    "label": "Released",
                    "stacked": false
                }
            ]
        },
        "gostats.#.memory": {
            "label": "Gostats.# Memory",
            "unit": "bytes",
            "metrics": [
                {
                    "name": "memory_alloc",
                    "label": "Alloc",
                    "stacked": false
                },
                {
                    "name": "memory_sys",
                    "label": "Sys",
                    "stacked": false
                },
                {
                    "name": "memory_stack",
                    "label": "Stack In Use",
                    "stacked": false
                }
            ]
        },
        "gostats.#.operation": {
            "label": "Gostats.# Operation",
            "unit": "integer",
            "metrics": [
                {
                    "name": "memory_lookups",
                    "label": "Pointer Lookups",
                    "stacked": false
                },
                {
                    "name": "memory_mallocs",
                    "label": "Mallocs",
                    "stacked": false
                },
                {
                    "name": "memory_frees",
                    "label": "Frees",
                    "stacked": false
                }
            ]
        },
        "gostats.#.runtime": {
            "label": "Gostats.# Runtime",
            "unit": "integer",
            "metrics": [
                {
                    "name": "goroutine_num",
                    "label": "Goroutine Num",
                    "stacked": false
                },
                {
                    "name": "cgo_call_num",
                    "label": "CGO Call Num",
                    "stacked": false
                }
            ]
        }
    }
}

ワイルドカードの仕様はよく理解できていなくて,難しいなと感じた.#* の違いはなんだろう?とか,個数制限があるのは # だけ?とか.ドキュメントとしては以下に載っている.

mackerel.io

mackerel.io

ホストグラフ : カスタムメトリック(成功 ○)

この時点で,既に動的ポートマッピングに対応したメトリクスが取得できているため,まずは「ホストグラフ : カスタムメトリック」を確認する.以下のように,ちゃんとポート別にメトリクスが確認できているし,タスク数の増加にも対応できている.ただし,これはまだコンテナインスタンスごとのメトリクスなので,実際にはあまり使わなそう.

f:id:kakku22:20170518203032p:plain

ロールグラフ : カスタムメトリック(失敗 ×)

ロールグラフは完全に厳しくて,ワイルドカード指定で表示することができなかった.改善要望出したいなぁ.

f:id:kakku22:20170518202545p:plain

ロールグラフ : カスタマイズグラフ(微妙 △)

こうなったらもうカスタマイズグラフで頑張るしかなさそうだ!ということになり,カッとなって作った.role 関数を使って,ポートの部分を * で表現したらうまくグラフ化できた.けど,シンプルとは言えないし,ツライ.

https://mackerel.io/embed/orgs/xxx/advanced-graph?query=role('yyy:zzz', 'custom.gostats.*.memory.memory_sys')&title=custom.gostats.*.memory.memory_sys&period=1h
https://mackerel.io/embed/orgs/xxx/advanced-graph?query=role('yyy:zzz', 'custom.gostats.*.memory.memory_sys')&title=custom.gostats.*.memory.memory_sys&period=1d
https://mackerel.io/embed/orgs/xxx/advanced-graph?query=role('yyy:zzz', 'custom.gostats.*.memory.memory_sys')&title=custom.gostats.*.memory.memory_sys&period=1w

カスタマイズグラフを大量に作ってダッシュボードを用意した.

f:id:kakku22:20170518202526p:plain

カスタマイズグラフに関しては以下に載っている.

mackerel.io

去年のアドベントカレンダーでカスタマイズグラフで試行錯誤したのを思い出してしまった.

kakakakakku.hatenablog.com

まとめ

今回実現した方法が正しいのかよくわからないし,もっと良い方法があるのかもしれないし,Mackerel が正式に ECS をサポートしてくれるかもしれないけど,現状この「パターン2」で考えていたことを実現することができた.とは言え,まだ検証環境で ECS を動かしている段階で,実環境に投入したらまた別の問題に気付くかもしれない.もっと良い方法があれば教えて欲しい.

  • コンテナインスタンスに mackerel-agent をインストールした
  • コンテナインスタンスのメトリクスを取得できた
  • タスクのポートをワイルドカードで表現することで,タスクのメトリクスも取得できるようになった
  • グラフは一筋縄では実現できず,カスタマイズグラフで頑張った

謝辞

困ったときに相談するとすぐに助けてくれる id:okzk に今回はアイデアをたくさん頂いた!感謝!

okzk.hatenablog.com

関連記事

kakakakakku.hatenablog.com

kakakakakku.hatenablog.com

mackerel-agent タスクをコンテナインスタンスごとに起動して ECS のメトリクスを取得する

最近 Mackerel を使って ECS のメトリクスを取得する方法を検証している.ポイントとしては,ALB の動的ポートマッピングを使うところで,コンテナインスタンスの中で複数のタスク(コンテナ)が起動されるため,全てのタスクのメトリクスを取得する必要がある.

パターン

Mackerel を使って ECS をモニタリングする場合,アーキテクチャとしては大きく2パターンが考えられる.

  • パターン1 : mackerel-agent タスクをコンテナインスタンスごとに起動する
  • パターン2 : コンテナインスタンスに mackerel-agent をインストールする

今回の記事では「パターン1 : mackerel-agent タスクをコンテナインスタンスごとに起動する」の検証結果をまとめようと思う.

メリデメ

最初に「パターン1」のメリデメを整理しておく.

  • メリット
    • コンテナインスタンスをプロビジョニングしなくても Mackerel が使える
    • mackerel-agent タスクからコンテナインスタンスのメトリクスを取得できる
    • mackerel-plugin-docker が Docker API からメトリクスを取得するため,動的ポートマッピングでタスクが増えた場合も,基本的なメトリクスは取得できる
  • デメリット
    • 動的ポートマッピングで起動されたタスク特有のメトリクス(mackerel-plugin を使って取得するようなもの)を取得できない

構成図

まず,今回は以下のような構成で検証をした.実際に動かすタスクは Golang で書いたサンプル API で,API Service に乗せた.

f:id:kakku22:20170516004413j:plain

ECS : タスク定義

今回 mackerel-agent という名前でタスク定義を作成した.タスク定義に必要な設定は,全てドキュメントに載っていたので,参考になった.ただし,ドキュメントには -role オプションの記載が無かったため,ここは別途追加している.

mackerel.io

タスク定義の画面としては,以下のようになり,ザッとポイントを挙げておく.

  • コンテナの定義
    • コンテナイメージ
      • mackerel/mackerel-agent を利用する
    • 環境変数
      • apikey = xxx
      • enable_docker_plugin = true
      • auto_retirement = false
      • opts = -v -role=yyy:zzz
    • マウントポイント
      • Docker ホストの /var/run/docker.sock を参照できるようにする
      • Docker ホストの /var/lib/mackerel-agent/ を参照できるようにする
  • ボリューム
    • /var/run/docker.sock
    • /var/lib/mackerel-agent/

f:id:kakku22:20170516004445p:plain

ECS : タスク定義 (JSON)

もし AWS CLI でタスク定義を作成する場合は,以下のように実行する.

$ aws ecs register-task-definition --cli-input-json file://register-task-definition.json

register-task-definition.json はこんな感じに定義した.

{
  "family": "mackerel-agent",
  "containerDefinitions": [
    {
      "name": "mackerel-agent",
      "image": "mackerel/mackerel-agent",
      "cpu": 0,
      "memory": 128,
      "essential": true,
      "mountPoints": [
        {
          "containerPath": "/var/run/docker.sock",
          "sourceVolume": "docker-sock"
        },
        {
          "containerPath": "/var/lib/mackerel-agent/",
          "sourceVolume": "mackerel-agent"
        }
      ],
      "environment": [
        {
          "name": "apikey",
          "value": "xxx"
        },
        {
          "name": "auto_retirement",
          "value": "false"
        },
        {
          "name": "enable_docker_plugin",
          "value": "true"
        },
        {
          "name": "opts",
          "value": "-v -role=yyy:zzz"
        }
      ]
    }
  ],
  "volumes": [
    {
      "host": {
        "sourcePath": "/var/run/docker.sock"
      },
      "name": "docker-sock"
    },
    {
      "host": {
        "sourcePath": "/var/lib/mackerel-agent/"
      },
      "name": "mackerel-agent"
    }
  ]
}

JSON で1点注意するところがあり,管理コンソールでタスク定義 (JSON) を見ると,mountPoints の値が "readOnly": null となっている.このままだと以下のエラーが出たため,削除してから実行した.

$ aws ecs register-task-definition --cli-input-json file://register-task-definition.json

Parameter validation failed: Invalid type for parameter containerDefinitions[0].mountPoints[0].readOnly, value: None, type: <type 'NoneType'>, valid types: <type 'bool'>
Invalid type for parameter containerDefinitions[0].mountPoints[1].readOnly, value: None, type: <type 'NoneType'>, valid types: <type 'bool'>

ECS : サービス定義

サービス定義で意識するのは,タスク配置制約を One Task Per Host (distinctInstance) にするところで,この設定にすることで,コンテナインスタンスに対して必ず1タスクが配置されるようになる.ちなみに 4/26 時点だと,管理コンソールから distinctInstance でタスクを配置しても,コンテナインスタンスに複数のタスクが配置されるというバグがあり,AWS サポート側に報告した.現在は既に修正されている.

f:id:kakku22:20170516004510p:plain

docs.aws.amazon.com

mackerel-plugin-docker とは?

mackerel-agent タスクの環境変数で enable_docker_plugin = true を設定することで,mackerel-plugin-docker が使えるようになる.これは,簡単に言うと Docker のソケットファイル docker.sock に対して Docker API を叩くことによってメトリクスを取得していて,複数コンテナが起動している場合でも問題なく対応できる.メトリクス名としては -name-format name というオプションがデフォルトで設定されているため,コンテナ名で区別することができる.ただ,取得できるメトリクスは限られていて,CPU 関連,メモリ関連,IO 関連となる.

github.com

実際に Mackerel でメトリクスを確認した

上記のように mackerel-agent タスクを起動すると,すぐにメトリクスを確認できるようになる.Docker Memory のグラフを見るとわかりやすいが,途中でサービス定義を変更して,コンテナインスタンスに配置するタスク数を増やしても,ちゃんとメトリクスを取得できていた.

  • Docker CPU
  • Docker Memory
  • Docker BlkIO Bytes
  • Docker BlkIO IOPS

f:id:kakku22:20170516004539p:plain

mackerel-agent タスクだと難しいこと

上記のように,動的ポートマッピングを使う場合でも,タスクごとに CPU 関連などの基本的なメトリクスを取得することができるが,例えば gostats など,mackerel-plugin を使って取得するようなタスク特有のメトリクスを取得できないという課題がある.動的ポートマッピングを使わないのであれば,ポートが決まっているし,Docker の link オプションを使えば良いが,今回の要件を満たすのは難しそうだった.

ロールグラフも難しい

あくまでこれは,コンテナインスタンスのメトリクスなので,Auto Scaling のことを考えると,コンテナインスタンスをあまり意識したくなく,ロール単位でメトリクスを見たくなってくる.ただ,今の状態だとメトリクス名がコンテナごとに異なるため,ロールグラフを作れなかった.もしかしたら,拡張グラフ機能を使えばできるのかもしれないけど,うまく書けなかった.

mackerel.io

まとめ

  • Mackerel を使って ECS をモニタリングする場合,アーキテクチャとしては大きく2パターンある
  • 今回は「パターン1 : mackerel-agent タスクをコンテナインスタンスごとに起動する」を検証した
  • mackerel-agent 用のタスク定義はドキュメントを参照して環境変数やボリュームを設定する
  • 動的ポートマッピングを使う場合でも,タスクごとに基本的なメトリクスは取得できるが,タスク特有のメトリクスは取得できなかった
  • ロール単位でメトリクスを見る方法もうまく実現できなかった
  • 結果として「パターン1」の採用は見送ることにした

次回

実際に採用することにした「パターン2 : コンテナインスタンスに mackerel-agent をインストールする」の検証結果をまとめる予定!

最近書いた ECS 関連記事

kakakakakku.hatenablog.com

kakakakakku.hatenablog.com

kakakakakku.hatenablog.com