Athenaのパーティション項目の型とパーティションプロジェクションの奇妙な関係

Athenaのパーティションプロジェクションを試していたらちょっとハマったのでメモしておく

パターン1

S3には /app_log/year=2020/month=06/day=05/hour=04/ のような場所にログをおいている

year, month, day, hourを Glueテーブル上の型で stringにする パーティションプロジェクション上の型はintegerにする(stringがないので) たとえば projection.month.type = "integer"

SELECT * FROM my_db.app_log
WHERE year = '2020' AND month = '06' AND day = '05' AND hour = '04'
LIMIT 10

のように検索するとヒットする。 しかし

SELECT * FROM my_db.app_log
WHERE year = '2020' AND month = '06'
LIMIT 10

はヒットしない!

パターン2

S3には /app_log/year=2020/month=06/day=05/hour=04/ のような場所にログをおいている

year, month, day, hourを Glueテーブル上の型で intにする
パーティションプロジェクション上の型はintegerにする(stringがないので)
たとえば projection.month.type = "integer"

SELECT * FROM my_db.app_log
WHERE year = 2020 AND month = 6 AND day = 5 AND hour = 4
LIMIT 10

のように検索するとヒットしない!

SELECT * FROM my_db.app_log
WHERE year = '2020' AND month = '06' AND day = '05' AND hour = '04'
LIMIT 10

のように検索してもヒットしない!

パターン3

S3には /app_log/year=2020/month=6/day=5/hour=4/ のような場所にログをおく。つまり 0 を抜いた

year, month, day, hourを Glueテーブル上の型で intにする
パーティションプロジェクション上の型はintegerにする
たとえば projection.month.type = "integer"

SELECT * FROM my_db.app_log
WHERE year = 2020 AND month = 6 AND day = 5 AND hour = 4
LIMIT 10

のように検索するとヒットする

SELECT * FROM my_db.app_log
WHERE year = 2020 AND month = 6
LIMIT 10

もヒットする。

つまり、S3のパーティションにあたる値が0がついていない数値でかつ、 Glueテーブルとパーティションプロジェクションの型が数値型でないといけない!!

じゃあ S3の ディレクトリを /app_log/year=2020/month=06/day=05/hour=04/ から /app_log/year=2020/month=6/day=5/hour=4/ に変更しようとしても、0抜き指定はfluentdのオプション上できない。https://docs.fluentd.org/v/0.12/output/file#time_slice_format
※追記
よくよく考えると、もしfluentdがTime#strftimeを使っているなら year=%Y/month=%-m/day=%-d/hour=%-H で0抜きができそう。

パターン4

S3には /app_log/dt=2020-06-05-04/ のような場所にログをおく 。
dt=YYYY-mm-dd-HHのような形式は https://docs.aws.amazon.com/athena/latest/ug/partitions.html でも記されているので公式に近い。

dtを Glueテーブル上の型で stringにする パーティションプロジェクション上の型はdateにする。 projection.dt.type = "date"

SELECT * FROM my_db.app_log
WHERE dt > '2020-06-05-04'
LIMIT 10

のように検索するとヒットする

SELECT * FROM my_db.app_log
WHERE dt > '2020-06'
LIMIT 10

でもヒットする

まとめ

パターン4が妥当かな

kustomize 2.1.0の変更点

kustomize 2.1.0で機能追加、変更点があったのでまとめる
kustomize/v2.1.0.md at master · kubernetes-sigs/kustomize · GitHub

プラグイン機構の導入

kustomize の generator / transformer の振る舞いを変えてみる | TECHSCORE BLOG に詳しく書かれているのでご参照ください

resources expanded, bases deprecated

basesがdeprecatedになり、代わりにresourcesを使うようになった。

今までは例えばoverlayes/production/kustomization.yaml の中で

bases:
- ../base
patches:
- cpu_count.yaml

のように書いていたのが、今後は以下のように書くことが推奨される

resources:
- ../base
patches:
- cpu_count.yaml

replicas field

deployment.yamlの中のreplicasの値を変更することはよくあるので、kustomization.yaml の中でreplicas数を指定できるようになった

今までは以下のようにpatchファイルを書いていたが

kind: Deployment
metadata:
  name: deployment-name
spec:
  replicas: 5

今後は kustomization.yaml の中で以下のように書けるようなった

replicas:
- name: deployment-name
  count: 5

envs field

環境変数が書かれたファイルからConfigmapやSecretを生成するために、 configMapGeneratorsecretGeneratorenvフィールドがあったがdeprecatedになった。代わりにenvsフィールドが追加された。

以前

configMapGenerator:
  - name: my-configmap
     env: env.txt

今後

configMapGenerator:
  - name: my-configmap
    envs:
      - env.txt

kustomize v3.4.0でenvを指定すると Error: json: unknown field "env"というエラーが出るので、envsに変える必要がある。

1000/02/29をRubyでinvalidな日付として扱う方法、もしくはRubyで1582年以前の暦をユリウス暦ではなくグレゴリオ暦で扱う方法

結論

RubyのDateクラスには startを引数に指定できるメソッドがある。 docs.ruby-lang.org startはグレゴリオ暦をつかい始めた日をあらわすユリウス日を表す。 引数のstartにDate::GREGORIANを指定すると、常にグレゴリオ暦で処理してくれる。

Date.valid_date?(1000, 2, 29, Date::GREGORIAN)
=> false

Date.parse('1000-2-29', true, Date::GREGORIAN)
=> ArgumentError (invalid date) 

経緯

MySQLは日付カラムに 1000-2-29 をいれるとinvalidな日付として扱われエラーになるが、RubyのDate#valid_date?メソッドを使って判定するとtrueが返ってくる。
これはMySQL先発グレゴリオ暦を使っているからだった*1

先発グレゴリオ暦とは、1582年から施行されたグレゴリオ暦暦法を、1582年以前にも適用したものである。

なのでユリウス暦だと1000-2-29 は存在するが、グレゴリオ暦だと 1000-2-29 は存在しないということらしい。

で、本題だが、Ruby1000-2-29をinvalidな日付として判定する方法がないか探してみたところ、上の結論に行きついた。

参考

MySQL :: MySQL 5.6 リファレンスマニュアル :: 12.8 MySQL で使用されるカレンダー

tech.furyu.jp

*1:標準SQLがそうなのでPostgreSQLも先発グレゴリオ暦を使っている

Amazon Kinesis Data Firehoseの料金が高かった

TL;DR

  • ログ1行のサイズが5KB以下なら5KBとして料金計算される

経緯

仕事でAmazon Kinesis Data Firehoseを使う機会があり、本番導入してみたところ料金が高かった。一日数千円。。

改めてログ量、ログサイズから自分で計算してみた。
CloudWatchからFirehoseの IncomingBytesIncomingRecordsが見れるのでそれをもとに https://aws.amazon.com/jp/kinesis/data-firehose/pricing/ のページを見ながら計算したが、請求額と合わない。

で、AWSサポートにお問い合わせしたところ、自分の料金計算方法の認識が間違っていた。
Firehoseの料金ページには以下が書かれている。

料金は Amazon Kinesis Data Firehose に取り込まれたデータの量に基づきます。
データの量は、このサービスに送信したデータレコードの数に、直近の 5 KB の倍数に切り上げた各レコードのサイズを乗算した値として計算されます

つまり、どんなにログ1行のサイズが小さくても5KBとして計算される。 そしてCloudWatchに表示されているIncomingBytesは実際のログサイズであり、5KBへ切り上げ後の値ではない。

結局、5KBに切り上げされると実際のログサイズで計算したよりも10倍以上高くなってしまうので、Firehoseを使うのは諦めて、FluentdでS3に直接置くように変更した。悲しい。
あとちゃんと料金の計算方法を把握しないとだめですね(当たり前)。

monday.comのURLをSlackに貼ったら展開されるようにした

monday.comというとても便利なタスク管理ツールがあります。 以下でも紹介記事を書きました。 tech.studyplus.co.jp

monday.comは(当たり前ですが)要ログインなので、monday.comのURLをSlackに貼っても展開されません。 それだととても不便なので、URLを展開するツールを作りました github.com

参考にしたツール

Closedなesaの記事URLをSlackに貼ったら展開されるようにした - pixiv inside を元に作りました。とても分かりやすくて助かりました。ありがとうございます。

デプロイ方法

Heroku

https://github.com/akira-kuriyama/monday-unfurly のREADMEにDeploy to Herokuボタンをつけているので、それを使って簡単にデプロイできます。 MONDAY_API_V2_TOKENが必要になりますが、 以下の手順で取得できます。(参考)

  1. monday.comにログインし、左下のアバターアイコンをクリック
  2. メニューからAdminを選択(要admin権限)
  3. API sectionを選択
  4. API v2 Tokenを生成し、コピー

コンテナ

今回、自分はHerokuではなく、GCPのCloud Runにデプロイしました。(そのためにDockerfileもリポジトリにおいてます) Cloud Runはコンテナをサーバーレス環境で実行できるサービスで、負荷に応じて自動的にオートスケール、負荷がない場合はサービスは起動されない(課金されない)といった特徴を持っています。herokuやAWS Lambdaと似てますね。 ただ、休眠状態からの起動はherokuよりは速い気がしました。 あと無料枠がかなりあるのでこのツールを動かすくらいなら無料でできそうです。

で、Cloud Run、めっちゃデプロイ簡単なんですよね。

$ gcloud beta run deploy --image {Docker Imageの場所のURL}

これだけです。これでそのコンテナにアクセスするためのURLが返ってきます。最高ですね。

Slackへの設定方法

Unfurling links in messages | Slack を参考にするか、 Closedなesaの記事URLをSlackに貼ったら展開されるようにした - pixiv inside を読むと分かりやすいと思います。

その他

monday.comのAPIがGraphQLでめっちゃ使いやすかった。 クエリをテストするための画面も用意されていて素敵。

さいごに

monday.comめっちゃ便利なので使ってくれ!!

Spinnakerについて調べたのでまとめる

Spinnakerについて簡単に調べたので自分用にまとめておく

Spinnaker とは

Spinnakerとは、マルチクラウドに対応した継続デリバリのプラットフォームです。 デプロイパイプラインの管理機能を備え、Red/Blackデプロイ(Blue/Greenデプロイ)やカナリアリリースにも対応しています。 2015年にNetflix によってOSS化され、GoogleMicrosoftを始め、様々な企業やコミュニティが開発に参加しています。

特徴

  • Spinnakerは、「Immutable Infrastructure」の原則に基づいて構築されており、クラウドネイティブ アプリの高速かつ信頼性の高いデプロイのための基盤を提供します。
    • Immutable Infrastructureとは、一度Deployをしたインスタンスやコンテナには二度と手を加えず、新たにDeployを行う場合はインスタンスやコンテナを作り直すという考え方です。Immutable Infrastructureを採用することでConfiguration driftを避け、安全かつ高速なRollbackが実現できます。
  • デプロイは、予測可能で安全であり、必要なときに簡単にロールバックすることができます。
  • テスト、QA、本番など、複数の開発環境にまたがる複雑なデプロイパイプラインを作成することができます。
  • デプロイパイプラインはパラレル実行とシリアル実行に対応してます スクリーンショット 2019-05-29 11.29.43.png

デプロイパイプラインがGUIからでしか定義できない時代もありましたが、現在は設定ファイルに定義できます。

機能一覧

CI Integrations

パイプラインの実行トリガーとして、「Docker Registry」「Cron」「Git」「Jenkins」「Werker」「Travis」「Spinnakerの他のPipeline」「Pub/Sub」「Webhook」「手動」などを設定できます。

VM Bakery

同梱されているPackerを使ってImageを焼くことができる

Deployment Strategies

以下のようなビルドインデプロイメント戦略が選択できる - highlander(既存のインスタンスに新しいコードをデプロイするTraditionalなやり方。ダウンタイムが発生する) - Red/Black(Blue/Green) - Rolling Red/Black - カナリアリリース - もしくは独自のデプロイメント戦略

マルチクラウド対応

Spinnakerは、kubernetesに対してのCD機能が標準的に実装されているだけではなく、他のクラウドプロバイダに対してにもCD機能が実装されています。(つまりKubernetesデプロイ専用ミドルウェアというわけではない)

Amazon Web Services(AWS) Microsoft Azure Google Cloud Platform Cloud Foundry Kubernetes OpenStack など カスタマイズにより他のクラウドプロバイダ追加も可能です

Manual Judgments

手動判断ステージでは、デプロイする前に手動の承認を必要とすることができます スクリーンショット 2019-05-24 10.05.23.png

カナリアリリース

Spinnakerを使うことで、カナリアリリースした成果物の任意のメトリクスの計測、スコア化を自動で行い、予め決めた合格点を満たすか自動判定できる。そしてその判定をクリアした成果物だけ本番にデプロイがされ、クリアしなかった場合は、ロールバックを自動で行う。 カナリアリリースした成果物の任意のメトリクスの計測、スコア化を行う機構はAutomated Canary Analysis(ACA)と呼ばれていて、kayentaというソフトウェアがそれを担当している。 計測する際のメトリクスは、DataDog、Stackdriver、New Relic、 Prometheus等々から取得できる。 そしてSpinnaker上からどのメトリクスを使うか、またそのメトリクスのしきい値(つまり合格とみなすしきい値)を設定できる。

参考: GoogleとNetflix、カナリアリリース分析ツール「Kayenta」オープンソースで公開

Notifications

email, Slack, SMS(via Twilio)等を使ってイベント通知ができる

Role-based Access Control

OAuth, SAML, LDAP, X.509 certs, Google groups, Azure groups, or GitHub teamsを使用してプロジェクトやアカウントへのアクセスに制限をかけることができます。

パイプライン実行時間の制限

特定の時間のみパイプラインの実行がされるように制限することができます。 たとえば、トラフィックがピークではない時間帯や、適切な担当者がロールアウトを監視しているときに実行するように、時間帯を指定することができます(ホワイトリスト形式)

Chaos Monkey Integration

故意にインスタンスを終了させることによってアプリケーションが問題なく動作するかテストすることができる

クラスタ管理機能

Spinnakerはクラスタ管理機能も提供しています。 クラスタの「サーバグループ」「ロードバランサ」「セキュリティグループ」などに対する管理操作を行えます。 またSpinnakerはクラスタ自体の可視化をすることができます。

Monitoring Integrations

モニタリングサービス Datadog、Prometheus、Stackdriverなどと連携したモニタリングを行えます。 またこれらのmetricsをカナリアリリース時に使用できます。

パイプラインテンプレート

https://www.1915keke.com/entry/2018/05/19/124103 パイプラインのテンプレートを作っておいてそれをもとにパイプラインを作ることができる。 特徴としてはテンプレートをもとに作ったパイプラインは設定変更できない。 テンプレートのほうでパイプラインの各フェーズで入力可能な変数を定義できる。(たとえばレプリカ数はパイプライン作成時に決めるなど)

フィードバック

拡張性

Spinnakerの機能だけでは対応できないエッジケースのために、Spinnakerを拡張することもできる 例えばパイプライン中にカスタムステージを作成したり、UIに独自のリンクやボタンを追加するなど。 Typescript/Reactの知識があればフロントエンドの拡張を、Java(JVM)の知識があればバックエンドの拡張を行うことが可能

参考資料:

GKEでspinnakerを動かしてみたい場合

以下の英語ドキュメントを参考にするとよいです Continuous Delivery Pipelines with Spinnaker and Google Kubernetes Engine 日本語ドキュメントのほうは手順に抜けがあるっぽいので基本は英語のドキュメントのほうをみたほうがよさげ。

解説資料

https://tech.plaid.co.jp/builderscon-2018/ https://www.youtube.com/watch?v=ejcJXjhfG2I&feature=youtu.be Multicloud deploy with Spinnaker https://www.ossnews.jp/oss_info/Spinnaker

参考資料

Compute Engine での Spinnaker の実行  |  ソリューション  |  Google Cloud

SpinnakerによるContinuous Delivery - Mercari Engineering Blog

Circle CIのcacheは先頭一致したkeyのcacheが使われる

たとえば以下の設定になっていたとします

- restore_cache:
    keys:
      - gem-cache-v1-{{ arch }}-{{ .Branch }}-{{ checksum "Gemfile.lock" }}
      - gem-cache-v1-{{ arch }}-{{ .Branch }}
      - gem-cache-v1

https://circleci.com/docs/2.0/caching/#restoring-cache

どのようなルールでcacheが使われるかというと、上から順にkey名が先頭一致したcacheが使われます。 公式ドキュメントには以下のように書かれています。

CircleCI restores caches in the order of keys listed in the restore_cache step. Each cache key is namespaced to the project, and retrieval is prefix-matched. The cache will be restored from the first matching key. If there are multiple matches, the most recently generated cache will be used.

なので、 masterブランチで key名にgem-cache-v1-{{ arch }}-{{ .Branch }}-{{ checksum "Gemfile.lock" }}を指定していて、 他のブランチが、 key名に gem-cache-v1-{{ arch }}-{{ .Branch }}-{{ checksum "Gemfile.lock" }}-test を指定したcacheを作成したら、masterブランチは gem-cache-v1-{{ arch }}-{{ .Branch }}-{{ checksum "Gemfile.lock" }}-test のcacheを使ってしまう。気をつけましょう。