ecspresso v1 をリリースします(v0.99.x をリリースしているのでお試しください)

おかげさまで各所でご利用頂いている(模様の) Amazon ECS デプロイツール ecspresso ですが、リリースしてからもうすぐ3年になりますし、ここで一部非互換の修正を入れた v1 を出そうとしています。

非互換な点は少ないのでほとんどの方にはそのまま使って頂けると思いますが、下記を確認した上で v0.99.x を試して頂いて、もし何か問題があればフィードバックを頂けると幸いです。

(2020-10-30 追記) v1.0.0 をリリース済です。

github.com

(2020-09-25 23:00 追記) Homebrew で 0.99.x がインストールできるようになりました。 tap ecspresso@0.99 by mashiike · Pull Request #9 · kayac/homebrew-tap · GitHub

$ brew install kayac/tap/ecspresso@0.99

既に v0.x がインストールされている場合は conflict するので、一旦 unlink して link し直してください。

$ brew unlink ecspresso
$ brew link ecspresso@0.99

v0.x に戻したい場合は逆操作です。

$ brew unlink ecspresso@0.99
$ brew link ecspresso

v1 リリース後は @0.99 は削除されるので、単に brew upgrade ecspresso で v1 へ更新されるようになる予定です。 (追記ここまで)

v1 で非互換になる点

deploy --update-service (true) がデフォルトになります

従来 、service 定義ファイルを更新した場合にそれを ECS service に反映するには、ecspresso deploy --update-service とオプションを指定する必要がありました。

v1 では --update-service はデフォルトで true になります。v0.x と同様、service 定義に変更があっても deploy 時に適用したくない場合、deploy --no-update-service として明示する必要があります。

次に説明する、service 定義の desiredCount が反映されるようになる件との組み合わせで、意図しない挙動になる場合がありますのでご注意ください。

service 定義に記述された desiredCount を解釈するようになります

Does not `desiredCount` field work? · Issue #131 · kayac/ecspresso · GitHub

これまでは service 定義に desiredCount 属性が記述されていても無視されていました。v1 では service 定義に desiredCount 属性が存在して null ではない数値が記載されている場合に限り、deploy 時にその値を使用するようになります。deploy --tasks N が指定されていればそちらが優先されます。

ecspress deploy 実行時に実際に ECS task に指定される desiredCount は、以下の優先順位で決定されます。

  1. --tasks で指定された値
  2. service 定義に desiredCount で定義されている値
  3. (現在の service の desiredCount と変更なし)

これまで通りの挙動を期待する場合は、service 定義に desiredCount が指定されていないことを確認してください。

service / task 定義ファイルの path を config ファイルからの相対で解釈するようになります

feature request: resolve definition files path relatively from config.yml · Issue #140 · kayac/ecspresso · GitHub

例えば現在 config.yaml に以下のように記述されていている場合、

service_definition: ecs-service-def.json
task_definition: ecs-task-def.json

ecs-(service|task)-def.json は ecspresso 実行時のカレントディレクトリに存在している必要があります。

v1 では config ファイルからの相対で解釈するようになるため、

$ ecspresso --config path/to/config.yml

として実行した場合は ecs-(service|task)-def.json は ecspresso 実行時のカレントディレクトリではなく、config.yml と同一ディレクトリ (path/to) に存在しているものとして実行します。


以上、よろしくお願いします!

ISUCON10予選に参加して不通過でした

Webアプリケーションパフォーマンスチューニングコンテスト ISUCON http://isucon.net/、記念すべき10回大会の予選に参加して、あと3チーム100点弱の差で不通過に終わりました。悔しい!

チームメイトは会社の同僚の @acidlemon (ISUCON 3の出題、ISUCON 4, 7 のチームメイト), @mackee_w (macopy, 実はチームを組んだのは初) です。

序盤から中盤

  • ベンチを回して MySQL が重いねー(いつものことだ) と把握
  • acidlemon
    • estate の範囲検索になっているカラムを = 条件で取れるように 0〜49999 -> 0, 50000〜100000 -> 1 のようにクラスわけ(verify がたまにコケるのを解消できず取り込めず
    • interpolateParams=true (server side prepare を無効)
    • select * をやめて id だけまず取得して where id in
  • macopy
    • featureがLIKE検索で複数条件に一致をやっていたのを別テーブルに逃して縦持ち
    • index 追加
    • geo 検索でポリゴンに座標が含まれているかを MySQL ではなく Go 側で判断 (後回しにしたら試行もできず…
  • fujiwara
    • OS は Ubuntu と予告されていたので各種ツール (alp, percona-toolkit, notify_slack, netdataなど) のセットアップ用 mitamae を流して初期設定
    • MySQL を軽くチューニング
    • nginx.conf で bot に 503 を返すように正規表現
    • Regexp::Assemble でまとめてこんなのを "(I(SUCON(bot((-Mobile)?|-Image\/)|FeedSeeker(Beta)?|Coffee)|supider((-image)?\+)?)|crawler \(https:\/\/isucon\.invalid\/(support\/faq\/|help\/jp\/)|Mediapartners-ISUCON|isubot))"

今回はとにかく検索が重く (単発ではそこまで重くないけど10000〜30000行読む必要がある数十msのクエリが大量に飛ぶ)、MySQL の負荷を index では軽減しきれず、序盤〜中盤まで全くスコアを伸ばせず苦しい展開でした。初期スコアは500程度、いろいろやっても600ぐらい。

大抵の ISUCON では序盤にいくつかの定番の手を打つと上がるみたいなジャブが効く感じがあるのですが、今回は全然ダメージを与えられている気がしないという、アプリケーションとしては比較的単純な作りなのに心を折りに来る良問でしたね…

また途中、自分が用意した deploy.sh がカレントディレクトリからでないと正常に動かないという雑な作りなため、macopy がデプロイしたつもりができてない (のでスコアが何も変わらない) というので revert を連発することになり、1時間ほど消費するというトラブルもありました。

中盤から終盤

時間的にはもう終盤に近く、まったくスコアが伸びないと心が先に折れるので、力押しでスコアを出そうと MySQLレプリケーションを組んで3台構成にしました。

  • isucon1: nginx, app, mysql (writer)
  • isucon2: app, mysql (reader)
  • isucon3: app, mysql (reader)

として、isucon2, 3 にレプリケーションを設定。アプリケーションからは 127.0.0.1 に接続する reader 用の db 接続を用意し、Search 系のクエリはそちらに切り替え。レプリ遅延回避のため多少小ネタを入れます。

  • ベンチの最初に叩かれる /initialize でデータが初期化されるため、その遅延が解消するまで initialize のレスポンスを sleep(10) して粘る
  • /initialize が終わると整合性チェックが User-Agent: isucon-verify で飛んでくるので、このアクセスはすべて 1 台目で処理するように nginx を設定
    • 1台目は reader 接続も 127.0.0.1 (=writer) を向いているので確実に遅延なしで読めます

これで1000を超えるぐらいまでなんとか伸ばして一息入れました。

更に、データの更新はほぼないので MySQL のクエリキャッシュが効きそう、ということで有効化して1300ぐらい? CPU が若干使い切れてなさそうだったので db.SetMaxOpenConns(10)db.SetMaxOpenConns(30) にしたらまた少し。slowlog はもう見ないことにして無効化。

あと残り2時間程度、ここからもう MySQL そのものをどうにかするのは無理筋と判断して、acidlemon と macopy に、それぞれ chair と estate の検索をオンメモリにする実装を並行で取りかかってもらいました。

先に取りこんだ estate の検索は fail したので切り戻し、残り30分を切ったところで chair の検索オンメモリ化の投入になんとか成功して 1800 越え、再起動試験をして残り10分で 2074、競技中の最高スコアをマークしたところで打ち止めとなりました。

終戦

最終的には、一般枠予選通過まで84点差、あと3チーム上回れば…という惜しいところで敗戦しました。

アクセスログみる限り、追試でのベンチ走行では競技中の最終ベンチよりも10%ほど多くレスポンスを返せていたので、あと何度かベンチを回していたらブレで100点程度上がっていたかも知れず、通過の可能性も僅かながらあったはずなので、粘りが足りなかったですね。ISUCON 6 の時は最後の1分を切ったところでベンチを開始して、それが fail せずに通ったことで予選を通過できていたのに…

ISUCON10 オンライン予選 全てのチームのスコア(参考値) : ISUCON公式Blog

検索のオンメモリ化ももう1,2時間早く判断して取りかかっていれば、estate, chair の両方を動かすことができた可能性が高く、これは判断の遅れです。そもそも予選通過ボーダー付近は毎年混み合うので、そこで揉み合っていた時点でベンチのスコアのブレの運要素を排除しきれず、つまりは実力不足でしたね。

おわりに

ISUCONでは予選通過できたのが7まで (8は出題、9, 10 が予選落ち) ということで、2年連続で本選の地を踏めず、悔しい思いです。今年も楽しい問題でした。運営の皆様、トラブルで開始が遅れたにもかかわらず、当日中に結果の発表まで大変お疲れ様でした!

GitHub Actions の Composite Run Steps で ecspresso / lambroll をインストールする action を作った

要約

GitHub Actions に Amazon ECS デプロイツール ecspressoAWS Lambda デプロイツール lambroll を簡単にインストール action を用意しました。

steps で以下の指定をするだけです。(Linux 環境専用です)

- uses: kayac/ecspresso@v0
- uses: fujiwara/lambroll@v0

もうちょっと詳しく

これまで GitHub Actions の action は基本的に Node で書く必要があったのですが、やりたいことはバイナリをダウンロードして展開してコピーするだけなのになんで Node を書かないといけないのか……(Linux以外の環境を考えると仕方ないとはいえ)、と面倒くささが先に立って、これまで action を作らないままでした。

が、先日 Composite Run Steps が使えるようになり、これは要するに自分の steps に shell script を書くかのように定義した action.yml を置いておくだけでよいという、顧客が本当に欲しかったものだったのでした。最高。

github.blog

ソースはこれだけで、見ての通り単に curl で落として展開して install するだけ、です。

https://github.com/kayac/ecspresso/blob/master/action.yml

inputs:
  version:
    description: "A version of ecspresso"
    default: "v0.17.3"
runs:
  using: "composite"
  steps:
    - run: |
        FILENAME=ecspresso-${{ inputs.version }}-linux-amd64
        cd /tmp
        curl -sLO https://github.com/kayac/ecspresso/releases/download/${{ inputs.version }}/${FILENAME}.zip
        unzip ${FILENAME}.zip
        sudo install ${FILENAME} /usr/local/bin/ecspresso
        rm -f ${FILENAME} ${FILENAME}.zip
      shell: bash

使う側は他の action と同様、step で uses: kayac/ecspresso@v0 などとするだけです。便利。

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: kayac/ecspresso@v0
        with:
          version: v0.17.3
      - run: |
          ecspresso deploy --config config.yaml

どうぞご利用ください。

FirehoseのHTTP配信機能でMackerelにメトリックを投稿する

先日、Amazon Kinesis Data Firehose が任意の HTTP エンドポイントに対しての配信機能をサポートしました。

aws.amazon.com

従来の S3 / Redshift / Elasticsearch Service などのマネージドなリソースに対してデータを配信する機能に加えて、自分で作った HTTP(s) のエンドポイントに対しても Firehose からデータを投げ込んでくれる機能です。

Amazon Kinesis Data Firehose now supports data delivery to Datadog

既に Datadog や New Relic へもマネージドでデータを配信できていて、それらに送りたいデータはとりあえず Firehose に流しておけば、実際の送信やバッファリングやリトライは Firehose が面倒を見てくれるという嬉しい状態ですね。

で、ここで思ったのは「Mackerelにも対応したい」

…ということで作ってみました。

github.com

Firehose に Mackerel のサービスメトリックを流して、この HTTP endpoint を設定すると Mackerel API に送信してくれるものです。

fujiwara/ridge を使うことで、ひとつのバイナリで単体での Go の http server としても動きますし、Lambda (API Gateway / ALB) としても動くようになっています。

動かしてみる

普通にビルドしてどこかのサーバーで動かしてもよいのですが、Firehose の配信先は https が必須で多少面倒かもしれません。ここではとりあえず Amazon API Gateway で動かすことにします。Go が必要です。

$ GOARCH=amd64 GOOS=linux make
$ ROLE_ARN=arn:aws:iam::123456789012:role/lambda make deploy

Linux (Lambda) 用のバイナリをビルドして、適当な IAM Role (権限は AWSLambdaBasicExecution のみで十分です) を指定して fujiwara/lambroll でデプロイできる Makefile がサンプルとして入っています。function の定義は以下で、Role 以外に必要な設定は特にありません。

{
  "Description": "A PoC of Firehose HTTP endpoint for Mackerel",
  "FunctionName": "firehose-http-endpoint-for-mackerel",
  "Handler": "firehose-http-endpoint-for-mackerel",
  "MemorySize": 128,
  "Role": "{{ must_env `ROLE_ARN` }}",
  "Runtime": "go1.x",
  "Timeout": 30,
  "TracingConfig": {
    "Mode": "PassThrough"
  }
}

デプロイした Lambda を使う API Gateway の HTTP Integration を設定します。Route は $default をこの関数に向けるだけです。

f:id:sfujiwara:20200803104848p:plain
API Gateway HTTP integration

Firehose の Destination 設定は以下の通りにしてください。

  • HTTP endpoint URL: 作成した API Gateway の URL。path は /service (https://xxxx/service
  • Content encoding: Disabled
  • Access key: Mackerel の API Key
  • Parameters:
    • service: メトリックを投稿する Mackerel のサービス名
  • Buffer interval: 何秒でも動作はしますがラグが少ない方がよいので 60

Firehose に投稿したいサービスメトリックを送ります。

$ aws firehose put-record --delivery-stream-name mackerel-endpoint \
    --record "Data=Base64にしたサービスメトリックの値"

値の形式は JSON {"name":"metric.name","time":1596382129,"value":27759} とテキスト形式(mackrel plugin が出力するタブ区切りのもの) metric.name 27759 1596382129 に対応しています。

あとは Firehose が60秒ごとにまとめて API Gateway を叩いて来るタイミングで、Mackerel API へ送信されるはずです。

うれしいこと

直接 Mackerel API を叩いたり mkr コマンドなどで送信するのと比べて、Firehose を介することで嬉しいことはなにかというと…

  • Mackerel のメンテナンス時、送信失敗時のリトライ、バッファリングを Firehose に任せられる
    • 最大 7200 秒までリトライ可能
    • 失敗したデータは S3 にバックアップも可能
  • Mackerel API key を送信元に配る必要がない
    • Firehose 側に設定されていれば OK
    • 送信元には Firehose に対してのアクセス権が与えられていればよい
  • mkr / mackerel-agent などのツールがなくても aws-cliSDK のみで送信できる

ぐらいでしょうか。mackerel-agent を EC2 で動かしていれば再送やバッファリングは自動で行われますが、直接 API を叩いて値を送信する場合は失敗時のケアを自分でやらないとメトリックが欠落してしまいます。特に Lambda などから送信する場合は状態を維持するのが面倒なので、欠落の心配が減るのがいいですね。

ご要望

Firehose http endpoint を使って Mackerel へメトリックを送るコードを書いてみましたが、これはあくまで PoC ということで、Mackerel の公式で http endpoint がサポートされてほしいなと思います。そうすると Firehose の設定で公式 endpoint に向けるだけで済みますし、API を直接アクセスする以外のメトリック送信手段として AWS のユーザーには嬉しいんじゃないでしょうか。ご検討いただければ幸いです。

複数の Terraform state を結合する tfstate-merge を書いて20個以上の state を大統一した

あるところに、ひとつの Web サービスの本番環境が 20 個以上の Terraform state で管理されているリポジトリがありました。

おもに AWS 上で動いている、大変年代物かつ巨大なモノリスのサービスです。最初は何もコード管理されていない状態から徐々に Terraform を導入したのですが、その際に「一度に全部管理はできないから、徐々に Terraform 化していこう」という方針で管理を始めたところ…

  • 新機能を追加するのでそのリソースを別 state で管理
  • 改修があるのでそれらのリソースを別 state で管理

を繰り返すうちに、中に数個程度しかリソースがない state がどんどん増殖し、いつの間にやら20個以上になってしまったという状態です。ちなみにリソース定義は合計で 500 程度ありました。

さすがにここまで分割されてしまうと相互参照はできないし、VPC / Subnet / Security Group などの共通で使っているリソースはいちいち data resource として定義するのも面倒だし、何度も同じことを書かないといけないし、と利点がなさ過ぎたため、統合を考えました。

tfstate-merge

github.com

最近 tfstate-lookup というコマンド/ライブラリを書いて、JSON 形式の tfstate (version 4) にはちょっと仲良くなっていたので、こんな感じで結合したらいけるんじゃないかなと雑に書いてみたのが tfstate-merge コマンドです。

これは 2つの state ファイルを引数に取り、結合するコマンドです。Ruby 製(コアモジュール縛り)なので、手元に ruby があれば実行できると思います。

$ tfstate-merge aaa.tfstate bbb.tfstate

とすると、bbb.tfstate の中の resource をすべて aaa.tfstate にマージした物を標準出力に出力します。

何も考えずに結合してしまうと、同一の名前で実体は別のリソースがあった場合に壊してしまうため、簡単なチェック機能を持っています。

  • 通常の resource (state では mode=managed) については、同じ名前があったらエラー
  • data resource (mode=data) については、同じ名前の場合は attributes が同一でなければエラー
  • terraform version が揃っていなければエラー (念のため)
  • state version 4 (terraform v0.12) のみ対応

エラーがなければ、最後に aaa.tfstate の serial 属性を ++ して出力します。

tfstate-merge による統合作業

実際にどのような作業を行うかの例を説明します。

tfstate-merge は remote state には対応していないため、一旦結合したい state file をローカルに持ってきた上で作業します。

作業は結合先の .tf が存在している場所で行います。

$ tfstate-merge -i.bak terraform.tfstate xxx.tfstate
resource data.aws_vpc.main is duplicated

2つの tfstate を結合します。オプションで -i.bak とすると、作業前の terraform.tfstate を terraform.tfstate.bak として保存し、結合後の内容を terraform.tfstate に上書きします。

data resource が重複している場合は警告が標準エラー出力に出力されるので、その後の調整の参考にしてください。運が悪いことに managed resource name が重複している場合は、一度 terraform state mv でリソース名を変更してから再挑戦するのがよいでしょう。

xxx.tfstate で管理している .tf をコピーしてきて、その中の terraform, provider の定義、重複している data resource を削除します。

そして terraform plan を行うと、No changes. Infrastructure is up-to-date. になるはずです。(ならなければ頑張って調整しましょう…)

最後に、追加した .tf をリポジトリにコミットし、tfstate を remote へ書き戻したら作業完了です。

まとめ

  • 複数の Terraform state file を結合する tfstate-merge を書きました
  • 過度に分割しすぎてしまった state をまとめたい場合にご利用ください

ecspresso と lambroll で tfstate からの値参照に対応した

Amazon ECS デプロイツール ecspressoAWS Lambad デプロイツール Lambroll で、設定ファイル中に tfstate (Terraform state file)の値を検索して使えるようにしました。

これまで面倒だったところ

ecspresso ではサービスとタスク定義、lambroll では関数定義を JSON で用意します。 その JSON の中では {{ env "FOO" }} のような記法で環境変数を展開してから読み込む機能があります。

デプロイや環境ごとに可変になる部分を、デプロイ時に環境変数によって外から与えることで定義ファイルをいちいち更新しなくていいという作りになっています。

しかし、たとえば ecspresso のサービス定義には以下のように、subnet, security group, LB target group などの ID が必要になります。

{
  "launchType": "FARGATE",
  "networkConfiguration": {
    "awsvpcConfiguration": {
      "subnets": [
        "subnet-03476ecc408d3e5e9",
        "subnet-027dd89f0c35a6323"
      ],
      "securityGroups": [
        "sg-0feded3d26f6360e1"
      ]
    }
  },
  "loadBalancers": [
    {
      "containerName": "app",
      "containerPort": 80,
      "targetGroupArn": "arn:aws:elasticloadbalancing:ap-northeast-1:0123456789012:targetgroup/app/d8b8346d68b86bf5"
    }
  ]
}

これらのリソースの管理は ecspresso / lambroll の管轄外のため、ファイルにべた書きすると謎の IDになってしまいます。

別途なんらかの方法で環境変数に設定してもいいのですが、個人的にはこれらのリソースは Terraform で管理しているので、Terraform のリソース名を参照できると便利なのでは!と思い立ち、実装してみました。

Terraform は、リソース定義に使用する .tf ファイルと実体のリソースを結びつける中間状態を terraform.tfstate という JSON で管理しています。State - Terraform by HashiCorp

このファイルを検索すれば、リソース名から実体のIDを検索できます。

tfstate 参照でどのよう変わるか

ecspresso

ecspresso (v0.14.0〜) で設定ファイルに plugins で tfstate の読み込み先を追加します。

terraform.tfstate はローカルファイルのほか、S3 の remote state にも対応しています (.terraform/terraform.tfstate に保存されているもの)

plugins:
- name: tfstate
  config:
    path: terraform.tfstate

これで、サービス定義を以下のように記述できます。

{
  "launchType": "FARGATE",
  "networkConfiguration": {
    "awsvpcConfiguration": {
      "subnets": [
        "{{ tfstate `aws_subnet.az-a.id` }}",
        "{{ tfstate `aws_subnet.az-b.id` }}"
      ],
      "securityGroups": [
        "{{ tfstate `aws_security_group.default.id` }}"
      ]
    }
  },
  "loadBalancers": [
    {
      "containerName": "app",
      "containerPort": 80,
      "targetGroupArn": "{{ tfstate `aws_lb_target_group.app.arn` }}"
    }
  ]
}

対応する terraform の .tf ファイルはたとえば以下のように記述されていて、これを terraform apply すると実体のリソースが作成/更新され、tfstate には実体のリソースが持っているIDなどの属性が保存されています。

resource "aws_subnet" "az-a" {
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.0.0.1/24"
  availability_zone = "ap-northeast-1a"

  tags = {
    Name = "az-a"
  }
}

リポジトリで人間が管理するファイルには謎の ID が出現しなくなりました。

lambroll

lambroll (v0.5.0〜) では、実行時に --tfstate オプションで tfstate のファイルのパスを指定します。

{
  "FunctionName": "hello",
  "Handler": "index.handler",
  "MemorySize": 128,
  "Role": "{{ tfstate `aws_iam_role.lambda.arn` }}",
  "Runtime": "nodejs12.x",
  "Timeout": 3,
  "TracingConfig": {
    "Mode": "PassThrough"
  }
}
$ lambroll --tfstate terraform.tfstate deploy --log-level debug --dry-run
2020/04/02 11:37:41 [info] lambroll v0.5.1
2020/04/02 11:37:41 [info] starting deploy function hello2
2020/04/02 11:37:41 [info] creating zip archive from .
2020/04/02 11:37:41 [debug] -rw-rw-r--        145 2019-10-28T09:43:31Z index.js
2020/04/02 11:37:41 [info] zip archive wrote 278 bytes
2020/04/02 11:37:41 [info] updating function configuration **DRY RUN**
2020/04/02 11:37:41 [debug]
{
  FunctionName: "hello2",
  Handler: "index.handler",
  MemorySize: 128,
  Role: "arn:aws:iam::0123456789012:role/hello_lambda_role",
  Runtime: "nodejs12.x",
  Timeout: 3,
  TracingConfig: {
    Mode: "PassThrough"
  }
}

使っている技術

tfstate を読むために、tfstate-lookup というパッケージ/コマンドを作りました。

github.com

コマンドとして使うと tfstate からリソース名 (+属性名) で値を抽出できますし、Go のパッケージとしても利用できます。

$ tfstate-lookup -s .terraform/terraform.tfstate aws_vpc.main.id
vpc-1a2b3c4d

$ tfstate-lookup aws_vpc.main
{
  "arn": "arn:aws:ec2:ap-northeast-1:123456789012:vpc/vpc-1a2b3c4d",
  "assign_generated_ipv6_cidr_block": false,
  "cidr_block": "10.0.0.0/16",
  "default_network_acl_id": "acl-001234567890abcde",
  "default_route_table_id": "rtb-001234567890abcde",
  "default_security_group_id": "sg-01234567890abcdef",
  "dhcp_options_id": "dopt-64569903",
  "enable_classiclink": false,
  "enable_classiclink_dns_support": false,
  "enable_dns_hostnames": true,
  "enable_dns_support": true,
  "id": "vpc-1a2b3c4d",
  "instance_tenancy": "default",
  "ipv6_association_id": "",
  "ipv6_cidr_block": "",
  "main_route_table_id": "rtb-001234567890abcde",
  "owner_id": "123456789012",
  "tags": {
    "Name": "main"
  }
}

実は terraform 単体でも output を定義すれば terraform output ... で表示はできるのですが、検索したいリソースについていちいち output を定義する必要があってだいぶ面倒だったので、自前で tfstate を parse するコードになっています。

内部では go-jq を使って、クエリを組み立てて json を検索するだけ、というシンプルな作りです。go-jq 便利! github.com

まとめ

ecspresso v0.14、lambroll v0.5 で、設定ファイルから tfstate の値を参照できるようになりました。

Terraform で AWS のリソースを管理しつつ、ECS や Lambda の管理には ecspresso / lambroll を利用している、という環境では便利かと思います。どうぞご利用ください。

SRE NEXT 2020 基調講演で「Webサービスを1日10回デプロイするための取り組み」を話しました

SRE NEXT 2020 という、国内初の SRE に特化したカンファレンスで基調講演をご依頼いただいて、「Webサービスを1日10回デプロイするための取り組み」を話してきました。

sre-next.dev

speakerdeck.com

これまでいろいろ発表はしてきましたが、基調講演というのは初めてで、さて何を話したほうがいいのか悩みました。とはいえ自分は基本的に技術でなにかを解決する話しかできないので、かなり現場寄りの話です。いつもどおり。

時間の関係でいろいろ省略した部分があるのですが、懇親会で聞かれた CircleCI に移行して増えたコストの話については LobiのCIをJenkinsからCircleCIに移行したはなし - KAYAC engineers' blog にあるとおり

数万円程度だったCI関連費用は、およそ2倍の十数万円になりました。

ぐらいなので、Jenkins サーバのお守りに掛かる諸々のコストがなくなったことや、体験の向上を考えると見合った投資だろうと思っています。


一日参加して、どれもいい発表だったのですが、印象に残った発表の感想を軽く書いておきます。

  • 基調講演 分散アプリケーションの信頼性観測技術に関する研究
    • まさに NEXT を見据えた研究で、オープニングに相応しい内容でした
  • パフォーマンスを最大化するための SRE のオンボーディング事例
    • どんなに人を集めても力を発揮できる受け入れ体制がないと何もならないので、組織的にそこを改善していくのは重要ですね
  • ZOZO MLOps のチームリーディングとSRE(Engineering)
    • まずひとつリリースまで持っていく、次にそれがまぐれでないことを証明するために複数で成功させる、というチームとしてのミッションが明確で、こういうリーダーの下で働けたら楽しいだろうな、と思わされました
    • 最後は技術で殴る必要がある (物事を解決できないで理想だけ語っても意味がない) というのもまさにその通り

懇親会でもいろいろな人と話ができて楽しかったですね。でもみんなもうちょっと野菜食べたほうがいいと思います。(この写真は懇親会終了時)

最高に楽しいカンファレンスでした。運営の皆さん、参加者の皆さん、ありがとうございました!