かまたま日記3

プログラミングメイン、たまに日常

GitHub Actionsでテストを分割する

やりたいこと

GitHub Actionsでテストケースを分割して、複数ノードで実行する

CircleCIだとプラットフォーム側で対応してくれていますがGH Actionsだと見当たらなかったので同等のことをやるための設定のメモです。

設定例

  rspec:
    runs-on: ubuntu-latest
    strategy:
      max-parallel: 2
      matrix:
        ci_node_total: [2]
        ci_node_index: [0, 1]
    steps:
      - uses: actions/checkout@v2
      - uses: ruby/setup-ruby@v1
      - name: Bundle install
        run: bundle install
      - name: DB setup
        run: bundle exec rake db:setup
      - name: Rspec
        run: |
          ruby -e 'Dir.glob("spec/**/*_spec.rb").each{|f| puts f}' | awk "NR%${CI_NODE_TOTAL}==${CI_NODE_INDEX} {print}" > /tmp/tests-to-run
          cat /tmp/tests-to-run
          bundle exec rspec $(cat /tmp/tests-to-run)
        env:
          CI_NODE_TOTAL: ${{ matrix.ci_node_total }}
          CI_NODE_INDEX: ${{ matrix.ci_node_index }}

GitHub ActionsにはBuild Matrix という機能があって、本来はOSのバージョンと言語のバージョンの複数のマトリクステストを実施するための機能なのですが、これを使って ci_node_totalci_node_index という2つのパラメタを登録します。

  • ci_node_total: ノードの合計, max-parallel と同じ数にする必要がある
  • ci_node_index: ノードのインデックス, 0始まりで ci_node_total-1 で終わる配列

これで、 rspec のJobは (2, 0) と (2, 1) という2つの設定で実施されることになります

f:id:kamatama_41:20200518161853p:plain

あとはindex毎にspec配下のファイルを分割してrspecコマンドに渡すだけです。CircleCIだとsizeの大きさとか各exampleのかかった時間とかでいい感じに平準化してくれますが、GitHub Actionsにはそういう機能は無いので、単にspecファイルを2分割して渡すのみです。

ちなみに、Go言語の場合はこんな感じでテストを分割実行できます

$ go test $(go list ./... | awk "NR%${CI_NODE_TOTAL}==${CI_NODE_INDEX} {print}")

2019年振り返り、2020年の抱負

2019年は書かなかったので、2年ぶりの振り返り記事になります。

転職

退職しました4 - かまたま日記3

一番のトピックはこれでしょうか。前職では色々心残りもありましたが、春にSEQSENSEという警備ロボットを作っているベンチャー企業に転職しました。

現職では主にバックエンド全般を担当していて、GoとかRailsとかTerraformとかを使って仕事しています。ここ数ヶ月は特にロボットのカメラからの動画をWebRTCで配信する機能をメインで見ていて、RTPとかMatroskaとかVP8とかの仕様に詳しくなりました。

去年の8月にロボットの商用化が始まったばかりでまだまだ改善点がたくさんあるので、興味のあるかたはぜひともお声掛けください。

www.wantedly.com

OSS活動

Embulkの内部構造に詳しくなるために、Embulkのプラグインを2つ作ったり

kamatama41.hatenablog.com

kamatama41.hatenablog.com

現職でTravis CIの設定をコード化するためにTerraformのプラグインを作ったりしていました

kamatama41.hatenablog.com

オーナー移管してしまいましたが、tfenvみたいなヒット作をいつかまた作りたいです。

子育て

子供も2歳になり、喋ったり走り回ったりできるようになりました。 週末は行けるときは色んな所に行くようにしているのですが、東京あそびマーレという八王子のアミューズメント施設にはとてもお世話になりました。オススメです。

asobimare.jp

最近は物や動物の名前を覚えたり、数を数えたりできるようになってきたので、動物園や博物館みたいなモノと触れ合う的なところに行く機会を増やしているところです。

麻雀

2018年後半くらいから競技麻雀に猛烈にハマっており、AbemaTVで麻雀の動画を見たり麻雀の本を読み漁ったり都内の某雀荘に足繁く通ったり家でも天鳳やったりと、最近は趣味の時間をほぼ麻雀にベットしている状態です。大会にもいくつか出ているのですが、一番結果が出たのは麻雀最強戦2019で東東京最強位決定戦まで行けたことでしょうか。東東京の試合では準決勝で敗れたのですが、負けた相手が後に最強戦ファイナルまで進出した方だったので、まあ仕方ないかなと。今年は何かしらの結果を出したいです。

2020年の抱負

  • 何かしらの勉強会・カンファレンスで登壇をする
    • 自分の知識向上と会社のプレゼンスアップのため
  • ダイエット (5kg以上減量する)
    • 直近の健康診断で、メタボリック診断されてしまったため。。
  • 天鳳鳳凰卓に行く *1
  • マチュア最強位決定戦に進出する

過去の振り返り

*1:7段になる

Nginxで同じポートでUDPとTCPをリッスンして別のバックエンドにプロキシする

たとえばNLBを使ってUDPをロードバランシングする場合、ヘルスチェックのために、UDPをリッスンするのと同じポートでTCPをリッスンする必要があります *1

そういう場合、バックエンドがNginxの場合は以下のような map$protocol を使った方法で振り分けることが出来ます。ここでは5001番がヘルスチェック用のポートです

stream {
  map $server_port:$protocol $backend {
    5000:UDP "example.com:5000";
    5000:TCP "127.0.0.1:5001";
  }

  server {
    listen 5000 udp;
    listen 5000;
    proxy_pass $backend;
  }

  # Health check
  server {
    listen 5001;
    return "OK";
  }
}

余談

このmapでbackendを切り替える方法は。Nginxが動的にホスト名の名前解決をしてくれない問題の対策にもなります。 (httpの方でよく使う set $backend example.com と同じ効果を得られる)

*1:デフォルトの場合。設定でヘルスチェックを別ポートにすることも可能

Travis CIの設定用のTerraform providerを作った話

この記事はTerraform Advent Calendar 2019 の9日目の記事です。

私の所属しているSEQSENSEではTravis CIを主に利用しています。 CIサービスを使うにあたって、ビルドで使う環境変数SSH鍵などの管理は課題の一つです。

一つ一つ手動で設定していってもいいのですが、リポジトリ数が多くなると

  • リポジトリを横断して一括で設定を変更するのが大変
  • どのリポジトリに何のクレデンシャルが使われているのかがわからないので、うかつにRevokeすると変なところでビルドが落ちる

などの問題が発生したりします。 それを解決するためにTravis CIの設定をコードナイズして一括管理するためのTerraform providerを作成しました。

github.com

よかったら、使ってみてください! *1

使い方

これはオレオレprovider (正式名称はThird-party plugin) のため、バイナリをGitHubのReleasesから落として来る必要があります。

curlなんかを使ってCLIでインストールする場合はこんな感じでしょうか。

$ latest=$(curl -s https://api.github.com/repos/kamatama41/terraform-provider-unofficial-travis/releases/latest | jq -r ".name")
$ os=$(uname | tr '[:upper:]' '[:lower:]')
$ curl -LO https://github.com/kamatama41/terraform-provider-unofficial-travis/releases/download/${latest}/terraform-provider-utravis_${latest}_${os}_amd64.zip
$ unzip terraform-provider-utravis_${latest}_${os}_amd64.zip && rm terraform-provider-utravis_${latest}_${os}_amd64.zip

インストール後、実際のTFファイルの書き方はこんな感じになっています。

# Configure the unofficial Travis Provider (utravis)
provider "utravis" {
  base_url = "https://api.travis-ci.com/"
  token = "${var.travis_api_token}"
}

# Add an environment variable to the repository
resource "utravis_env_var" "my-repo" {
  slug = "myuser/my-repository"
  name = "FOO"
  value = "bar"
  public = true
}

# Add a private key to the repository
resource "utravis_key_pair" "my-repo" {
  slug = "myuser/my-repository"
  value = "${file("~/.ssh/id_travis_rsa")}"
}
  • プロバイダ名は utravis
    • base_url でアクセスするAPIを指定する (travis-ci.com or travis-ci.org)
    • tokenTravis CIのコンソールで取得する
  • utravis_env_var環境変数を設定するためのリソースです
    • public フラグでTravisのコンソール上で表示するかどうかを指定できます (デフォルトはfalse)
  • utravis_key_pairSSH Keyを登録するためのリソースです。 value秘密鍵の中身を指定します

注意

  • 両リソースのValueはSHA-256でハッシュ化された値をstateファイルに保存して生のデータではどこにも保存しませんが、valueを渡すまでののクレデンシャルの管理には十分注意してください *2
  • Travis CIにはブランチごとに環境変数を出し分けるEnvironment variables per branch という機能が少し前に追加されましたが未対応です

*1:そもそもTravisを会社で使ってるというのを(ry

*2:無いとは思いますが、直接tfファイルに書くなどは当然NG

AWS Savings Plans メモ

AWSのSavings Plansのセミナーに参加してきたのでそのメモ。

Fargateに対しても割引が効いたりするし、インスタンスタイプの垣根が無いので*1、そろそろM6が出そうなので、M5のRI買うのやめとこうみたいなのが発生しないのがイイですね。

Savings Plansとは?

  • AWSの計算リソース割引プラン、リザーブインスタンス(RI)より柔軟性が高い
  • 1時間あたりのコミットメント金額 に対して適用される
  • Compute Savings Plans と EC2 Instance Savings Plansがある
    • Compute Savings Plans: より柔軟なプラン。インスタンスファミリー、リージョン、OSに関わらず割引が適用。Fargateの割引もされる
    • EC2 Instance Savings Plans: 割引率が高いもの、インスタンスファミリー、リージョン、OSの指定が必要、Fargateは対応外 (今のRIと同じ)
      • 違いとしてはインスタンス単位ではなく コミットメント金額 単位で購入するという違いがある
  • 割引率は、インスタンスファミリー、Fargateによって違う(https://aws.amazon.com/jp/savingsplans/pricing/) ので、自分で計算するのは面倒。
    • Recommendationsで直近 7 or 30 or 60 日の使用量をベースに推奨値を出してくれるので、それに従うのが良い
  • 割引は一番安くなるように (割引率が高いものから順番に) 適用される
  • RDSは対象外

*1:Compute Savings Plansの場合

俺的00年代アニソン 前編

昔90年代 でやったやつの00年代版です。そろそろ2010年代も終わるのでやってみました。 今回は特にランキングは付けず思い入れのある曲を紹介していきます。紹介したいのが多くなったので続きます (たぶん..)

続きを読む

Datadog LogsをCLIでtailするツールを作った

github.com

モチベーションとしては、Datadog LogsのWebコンソールはメッセージの全文が長いと全部表示されなかったり普通のtailと違って時間の降順に並んだりで微妙に見づらかったりするので、CLItail -fコマンド感覚で見れて雑にgrepとかしたいと思って作りました。

インストール

Releases からバイナリをダウンロードしてパスを通してください。
あと多分 go get -u github.com/kamatama41/taildog でもインストールできると思います。

使い方

まず、DD_API_KEYDD_APP_KEY (application key) の2つの環境変数を設定する必要があります。DatadogのWebコンソールから取得してきてください。

全ログを追跡する

$ taildog

クエリで絞り込む

$ taildog -q "service:my-app"

一定期間のログを表示する (追跡はしない、 max 1000行)

$ taildog -q "service:my-app" --from 2019-07-10T11:00:00Z --to 2019-07-10T11:00:05Z

出力のフォーマットを変える

Goのtemplateのスタイルで記述します。使用できる項目はDatadogのLog Query APIのレスポンスのcontentの内容に準拠します。 デフォルトは "{{.Timestamp}} {{.Host}} {{.Service}} {{.Message}}" です。

$ taildog -q "service:my-app" -f "{{.Timestamp}} {{.Message}}"

注意

  • 実際にログがインジェストされてからAPIで見れるようになるまで、微妙にタイムラグ (5~30秒くらい) があります。
  • Log Query APIは300回/時間/組織のレートリミットがあるので*1、複数人で長時間使うとエラーになる可能性があります。
  • 上記の理由からデフォルトで15秒おきにポーリングするようにしていて、そこまでリアルタイム性は高くないです。

よかったら使ってみてください〜!

*1:必要に応じで拡張はできるそうです