Route53のAliasレコードでURLスワップによるBlue-Greenデプロイをしてはいけない

Route53のAliasレコードはCNAMEみたいなものと思って、ALBなどに使っていたけれど、実際DNSクエリすると直接Aレコードとして返ってくる。 なので、AliasレコードをスワップしてBlue-Greenデプロイしようとすると、ピニングしているやつが入れ替え前の環境を向き続ける。 URLスワップはCNAMEでやらないといけない。ElasticBeanstalkなどは、ちゃんとCNAMEのスワップでやっていたから問題なかったのだ。

Kinesis Data Streams のパフォーマンス設計のメモ

Kinesis Data Streams のパフォーマンスの見積もりでミスをして、特定のシャードで GetRecords.IteratorAgeMilliseconds が徐々に拡大してく事態になった。

ストリームからの出力は RawデータバックアップのためのFirehoseと、ストリーム処理を行うKCLのMultiLangDaemonワーカーをECSのコンテナで行っている。 ストリームのスループットの制約は、1シャードあたり下記のようになっていて、これを超えると ProvisionedThroughputExceeded が起きるが、 今回、1分平均のメトリクスではあるが、シャードのIn/Outレコード数、サイズは到底これに到達しないスループットで稼働していた。

  • 書き込み
    • 1000 レコード/sec
    • 1MB/sec
  • 読み出し
    • 2MB/sec

そして、putRecordsの件数のベースが数%上がった時に、特定のシャードで遅延が生じた。 上のKinesisのデータ流量も、ECSのCPUUtilzation、MemoryUsage上も余裕のある値だったため、当初プログラムの問題を疑って ワーカーを再起動した。再起動で一時的に遅延が治まったものの、また遅延が発生し拡大した。 そこでワーカーの単位時間あたりの処理レコード数が頭打ちになっていることに気づいたので、 シャード数を増やすことで事態はおさまった。

これまでレコード数が増加したときは、GetRecordsのカウントも同様に上昇していたが、今回はGetRecordsのカウントが上がらずにガチャガチャしていることが、 ここに起因すると気づけなかったのが痛かった。また、 Firehoseを1つ設定している場合、OutgoingRecordsや、GetRecords のカウントは2倍になるので、 これも気づきを邪魔していたと思う。 デフォルトのメトリクスだけでなく、自前の部分のパフォーマンスもメトリクスにのせて監視する必要性を認識した。

今回、Kinesisのパフォーマンス上はかなりの余裕があるにも関わらず、ワーカーが極端なボトルネックになっているために 余裕をもたせたシャード数にするにあたって、見積もりよりかなり多くのシャード時間のコストが乗ってしまった。

AWS EFSのパフォーマンス設定についてのメモ

AWS EFSをバーストモードで使っていたら、クレジットを使い果たしそうになるという事態になった。 プロビジョンドスループットのモードに設定して事なきを得たが、プロビジョンドスループットは、東京リージョンで、 1MB/s あたり7.2USDかかる。

こうなった原因は、サイズ0のファイルを置いて、 複数台のEC2インスタンスからtouch するだけに使っていたため。

EFSのボリューム使用サイズが10GiB以下の場合、ベースラインスループットは0.5MiB/sなのに大して、バーストスループットは100MiB/sであり、 こういう使い方をしていると、盛大にクレジットを消費していくことがわかった。

プロビジョンドスループットモードに変更して気をつけないといけないのは、バーストできないということで、10MiB/s確保する設定にした場合、ここで頭打ちとなる。 バースト時に100MiB/sで動けていたのに対して、大きくスループットが落ちるという点で困るケースがありそう。

EFSはワークロードに合わせて、細やかなパフォーマンス設定が必要になるイメージ。 今回なんかは、プロビジョンドスループットモードにするより、バーストモードのまま、使用サイズによってベースラインスループットを上げる方が、 コスト、パフォーマンスともメリットがありそう。

試しに、EFS を 256GiB 使用した状態にして、 tc コマンドで帯域制限をかけ、 dd で書き込んだ際の消費クレジットのメトリクスを観察すると、 ベースラインスループットが 12.5MiB/sに上げられたようで、10MB/s で書き込むとクレジットは減らないが、15MB/s で書き込むとクレジットが減った。

tc コマンド使い方

$ tc qdisc add dev eth0 root tbf limit 30Mb buffer 1Mb rate 10Mbps
$ LANG=C dd of=/mnt/efs/dummy1 if=/dev/zero bs=16M count=16
16+0 records in
16+0 records out
268435456 bytes (268 MB) copied, 26.9938 s, 9.9 MB/s
$ sudo tc qdisc del dev eth0 root

AWS ソリューションアーキテクトアソシエイトを取得した

会社からAWS認定資格の補助が出ないので、出るようになるまで受験しないでおこうと思っていたが、 出るようになる気配はないので2019-02-26に受験して、結果は合格だった。

勉強時間は1週間くらいかけて、この本を1冊読み、練習問題を解いた。

徹底攻略 AWS認定ソリューションアーキテクト アソシエイト教科書 - インプレスブックス

ここ2年くらいはAWSを使っているので、勉強は十分と考えていた。 当日の朝6:00くらいに念の為、模試を受験した結果も88%とれたので大丈夫だろうと高をくくっていた。

総合スコア:  88%

トピックレベルスコア:
1.0  Design Resilient Architectures: 100%
2.0  Define Performant Architectures: 71%
3.0  Specify Secure Applications and Architectures: 83%
4.0  Design Cost-Optimized Architectures: 100%
5.0  Define Operationally-Excellent Architectures: 100%

試験は9:30からで、その時間に試験会場のキオスク端末を使ったのは僕一人だけだった。 問題は模試より難しいと感じた、というより模試やテキストに無かった問題が多く感じて、80%は取れないと思いながら回答した。 特に、サービスではRedshiftやKinesis、SQSあたりと、セキュリティまわりの勉強が足りなかった。 回答は制限時間を残して11:00には終わった。

結果は試験終了ボタンを押すと、即時発表され合格だった。 スコアは後からwebサイトで閲覧可能になった旨メールが来て 833 だった。スコアは 100 - 1000 の間で、720 以上が合格とのことなので、 ギリギリ合格というほどではなさそうだが、やはり良いスコアではなかった。 各セクションのスコアについては、すべて 十分な知識を有する としか記載されておらず不明。

次は夏までにプロフェッショナルを受験しようと思っているが、この試験自体が勉強のフィードバックにはならないのはイケてないと思う。

gstreamerでMJPEGからH.264などにトランスコードするときのメモ

Amazon Kinesis Video StreamsでPanasonicやAXISのMJPEGの映像をアップロードするメモ - 続かない日記

前回、MJPEG APIWebカメラからのイメージをH.264にして、Kinesis Video Streamsに送る際に、下のようなパイプラインを組んでいたが、 どうもフレームレートが合わなくて再生がうまく行かなかった。

$ gst-launch-1.0 souphttpsrc location=http://<webcam mjpeg api uri> ! jpegparse ! jpegdec ! queue ! x264enc tune=zerolatency ! h264parse ! kvssink stream-name=<stream name>

でわかったのは、 jpegparse フィルタが souphttpsrc から受けた時点で framerate=1/1にしてるということ

$ timeout -s INT 3 gst-launch-1.0 -v souphttpsrc location=<webcam mjpeg api uri> ! jpegparse ! fakesink
Setting pipeline to PAUSED ...
Pipeline is PREROLLING ...
/GstPipeline:pipeline0/GstJpegParse:jpegparse0.GstPad:src: caps = "image/jpeg\,\ parsed\=\(boolean\)true\,\ format\=\(string\)I420\,\ width\=\(int\)320\,\ height\=\(int\)240\,\ framerate\=\(fraction\)1/1"
/GstPipeline:pipeline0/GstFakeSink:fakesink0.GstPad:sink: caps = "image/jpeg\,\ parsed\=\(boolean\)true\,\ format\=\(string\)I420\,\ width\=\(int\)320\,\ height\=\(int\)240\,\ framerate\=\(fraction\)1/1"
Pipeline is PREROLLED ...
Setting pipeline to PLAYING ...
New clock: GstSystemClock
handling interrupt.
Interrupt: Stopping pipeline ...
Execution ended after 0:00:02.788853712
Setting pipeline to PAUSED ...
Setting pipeline to READY ...
Setting pipeline to NULL ...
Freeing pipeline ...
$

なので、jpegparseせずに、 image/jpeg,width=640,height=480,framerate=30/1 のように typefindfunction 使ってやったらフレームレートが合った。

じっさい Kinesis Video Streams にアップロードするときは、30フレもいらないのでこうした。

$ gst-launch-1.0 souphttpsrc location=<webcam mjpeg api uri> ! image/jpeg,width=640,height=480,framerate=30/1 ! jpegdec ! videorate ! video/x-raw,framerate=1/1 !  ! x264enc tune=zerolatency ! h264parse ! kvssink stream-name=<stream name>

Amazon Kinesis Video StreamsでPanasonicやAXISのMJPEGの映像をアップロードするメモ

Webカメラの映像をAmazon Kinesis Video Streamsにアップロードしてみている。

Kineisis Video StreamsではアップロードにKinesis ビデオストリーム プロデューサーライブラリ - Amazon Kinesis Video Streamsを使うと良いそうで、 gstreamerの kvssink というシンクエレメントのプラグインも用意されているので、これを試す。

github.com

プロデューサーライブラリはこれの kinesis-video-native-build というディレクトリで install-script を実行すると、 ローカルディレクトリ以下だけで動かせるよう、gstreamer 本体を含む必要なライブラリ一式ソースからダウンロードしてビルドしてくれる。 このへんの説明は Kinesis ビデオストリーム プロデューサーライブラリ - Amazon Kinesis Video Streams などに書いてある。

ところで、よくあるPanasonicやAXISというメーカーの監視カメラにはhttpでmjpegを取得するAPIが用意されているものがある。Panasonicに至っては、映像を取得するAPIはMJPEGしか無いものもある。 そこで、gstreamerでは souphttpsrc というソースエレメントを使うとhttpのリクエストを簡単に行える。 単純にMJPEGの映像を再生するだけであれば、このように簡単にできる。

$ gst-launch-1.0 souphttpsrc location=https://<user>:<passwd>@<webcam mjpeg api uri> ! jpegparse ! jpegdec ! autvideosink

MJPEGAPIのURIは、Panasonicなら http://<webcam ip>/nphMotionJpeg?Resolution=640x480 とか、 AXISなら http://<webcam ip>/axis-cgi/mjpg/video.cgi という感じ。

しかし、上に書いたプロデューサーライブラリのgstreamerには souphttpsrc が含まれていない。 ビルドするライブラリ一式の中に libsoup が入っていないためだ。 install-scriptにlibsoupのビルドを追加しても良いが、ただでさえinstall-scriptは時間がかかるので、kvssinkプラグインのみビルドして使うことにした。 aptでインストールできるgstreamerはsouphttpsrcが入っているので、これを使うようにDockerfileにした。 gstreamerでは GST_PLUGIN_PATH を設定してやるとプラグインが追加できる。 log4cplus-1.2.0とlibgtestはaptでインストールできなかったのでビルドした。これだと、install-scriptでビルドするより圧倒的に早いしプラグインも多い。

github.com

Docker ビルドし、実行したら、kvssink が使えるのでgstreamerのパイプラインを以下のようにつないで実行する

$ gst-launch-1.0 souphttpsrc location=http://<webcam mjpeg api uri> ! jpegparse ! jpegdec ! queue ! x264enc tune=zerolatency ! h264parse ! kvssink stream-name=<stream name>

とりあえず、これだけで、AWSコンソールでのプレビューはできる。

  • プレビューのためにh264エンコードしているが、kvssink自体はどんなコーデックのフラグメントでも送ってくれる。
  • h264エンコードのパラメータで、 tune=zerolatency にしないとうまく行かなかったが、PCのスペックのせいかもしれない。
  • このままではフレームレートは元のMJPEGと合ってないので、 videorate フィルタでフレームレート調整しないといけないかもしれない。

ngx_mrubyでアクティブヘルスチェックするTCPのロードバランサを作るメモ

半年くらい前の話、RDSのリードレプリカのロードバランサを置きたくて、TCPのロードバランサを試していた。Cloud Watchのメトリクスによって、振り分け対象から除外したかったので、アクティブヘルスチェックできるものを探していたが、

  • ELBのNetwork Load Balancer
  • HAProxy
  • nginx stream module

これらはポートでヘルスチェックするだけだったのでやめた。nginxはnginx Plusを購入したらできるっぽいが試さなかった。

HAProxyを試したときの設定方法はこれ Fargate による HAProxy で RDS postgres のロードバランシング - Qiita

あげくngx_rubyに行き着いて設定したが、結局、これ以降使わなくなってしまったが、自由度の高いTCPのロードバランサをさくっと立てられるのは、今後も何か役に立つかもしれないので思い出す用のメモ。

ngx_mrubyの設定はこれだけで、 mruby_stream_code ハンドラで待機してるサーバから1台選んでそれをアップストリームに設定してプロキシする。

    mruby_stream_init_code '
      u = Userdata.new
      u.server_map = JSON::parse open("/usr/local/nginx/conf/server_map.json").read
      u.backups = u.server_map["backup"].map{|e| e["endpoint"]}
    ';

    server {
        listen 5432;
        mruby_stream_code '
          u = Userdata.new
          upstreams = JSON::parse Redis.new("redis", 6379).get("upstreams")
          c = Nginx::Stream::Connection.new "pgservers"
          c.upstream_server = upstreams.length > 0 ?
            upstreams[rand upstreams.length] : u.backups[rand u.backups.length]
        ';
        proxy_pass pgservers;
    }

待機してるサーバは、ここではヘルスチェックの結果から、Redisにメモしている。

ヘルスチェック結果はRedis等に書くことで外だしできるので、ここにはその処理はない。 当初は、mruby無いでやる必要があると思って、mrubyでcloud watch API を呼ぶmrb_gemを書いた*1 が、整合性のあるデータストアで、mrubyのハンドラから読めれば何でも良い。これだけのためにRedisを立ち上げるのは大げさだと思う。

一回引っかかったのは、RDSのエンドポイントでサーバを記述していたときに、ヘルシーなサーバがいても、アップストリームがnilになるという問題。 これは、都度 getaddrinfo してIPアドレスでアップストリームを設定することで解決した。そして、まつもとりーさんにもこういう感じの使い方を想定している旨教えていただいた。

https://github.com/sabmeua/ngx_mruby_rdsproxy/commit/dcb10eb3ee33f04d7a14d9447269e5e207e765d4

以上で、こちらはRDSと同一VPCでfagateで起動することを想定してDockerfileにしたもの。ヘルスチェック用コンテナも含む GitHub - sabmeua/ngx_mruby_rdsproxy: Example load balancer for RDS using ngx_mruby