インターネットメモ帳

文章を書く練習をする場所

イベント運営にterraformを使う

この記事は Recruit Advent Calendar 2021 の16日目の記事です。

初めに

普段業務ではクラウド使ったプロダクト開発のあれこれをやっていますが、たまに社内ISUCONやAWS GameDayの運営もやっていたりしています。

積み重ねが生み出した新たな一歩——若手が力を発揮した「R-ISUCON 2021 Summer」 | リクルート メンバーズブログ

社内イベントを実施する上でコンテンツ自体の完成度も重要ですが、円滑にイベントを進行させるもの大事です。

円滑に進行を行うための一、つに運営と参加者のコミュニケーションパスの作成があります。

最近では嬉しいことに社内でイベントを開くと90人近く集まってくれてチームも20チーム以上出来ることがあります。

が、チーム数が多いと準備に必要な量も比例してい多くなります。

f:id:int_tt:20211216211736p:plain

更にチームごとに個別の情報を送ったりする必要があるので手動でやるには骨が折れます。

terraformを使う

これらを準備するためにscript書いたりポータルで情報をやり取りできるようにしたりやり方は色々あるかと思います。

社内ISUCONの場合、競技用の環境をAWSで作成するためにTerraformを使っていたので、「折角ならTerraformでチャンネル作ったりメッセージも投稿出来たほうが便利じゃない?」と思い、Terraformを使ってみようと考えてみました。

slack provider

Terraformで利用できるslack providerはコミュニティのものがすでにいくつか存在しています。僕がやりたかったことは大きく以下のとおりです。

  • 各チーム向けのprivateチャンネルの作成
  • 各チャンネルに対してチームメンバーと運営の招待

コミュニティのものを利用しても良かったのですが、普段terraformを使っているけどproviderの仕組みのことまでちゃんと知らないなと思い、せっかくなので自作してみました。

https://github.com/int-tt/terraform-provider-slack

また、プロバイダーの作成に関しては以下のを記事がとても参考になりました

Terraform Provider実装 入門(1): Custom Providerの基礎 - febc技術メモ

使い方

使い方はとてもシンプルです。以下のようなCSVとterraformのコードを準備して実行するだけです。

もちろんCSVはなくても利用可能です。

id,email
team-A,example@example.com
locals {
  teams        = csvdecode(file("./team-list.csv"))
  team_names = distinct([for t in local.teams : t.id])
}
resource "slack_channel" "team-channel" {
  for_each = toset(local.team_names)
  name    = format("event-name-", trim(replace(lower(each.value), "/[・、 ]/", "-"), "!"))
  private  = true
}

data "slack_user_with_email" "team-member" {
  for_each = { for team in local.teams : team.email => team }
  email    = each.value.email
}

resource "slack_channel_invite" "team-member" {
  for_each   = { for team in local.teams_member : team.email => team }
  channel_id = slack_channel.team-channel[each.value.id].id
  user_id    = data.slack_user_with_email.team-member[each.value.email].id
}

これでチャンネル作成と招待を一気に実行することが出来ます。

また terraform destroy を使うことで一気にチャンネルのarchiveも実行できます。

やりたいけど出来てないこと

先程slackのメッセージも投稿できるようにしたいって書いてましたが、まだ出来ておらず、イベント運営のときは温かみのある対応をしていました。

APIで投稿するときにはmarkdownでシュッと書いて投稿するのは出来ないので、次のイベント運営の時に気力があれば対応したいなと思います

おわりに

実際に使ってみて感想は便利だったのであってよかったなーというのと、terraform-plugin-sdk よく出来ているなーと感じました。

イベント運営以外にも普段のプロダクト開発におけるslackチャンネル作成や入退室の管理にも使えそうだなと思ったので機会があればやってみたいなと思いました。

prometheus-to-sdを調べた時のメモ

Prometheus to stackdriver

LTする上で prometheus text formatをstackdriver向けに変換してくれる prometheus-to-sd を調べた時のメモです。何もわからんとこから調べたので、もし間違ってたら指摘してもらえると助かります

https://github.com/GoogleCloudPlatform/k8s-stackdriver/tree/master/prometheus-to-sd

prometheus text formatとは

prometheusが扱えるmetrcisのフォーマット

prometheus.io

text formatの仕組み

go_gc_duration_seconds{quantile="0.0"} 0.0

metric_name [
-   "{" label_name "=" `"` label_value `"` { "," label_name "=" `"` label_value `"` } [ "," ] "}"
- ] value [ timestamp ]

https://ja.wikipedia.org/wiki/EBNF EBNFの読み方不明だったので参考

  • metric_name: metrics名
  • label_name="label_value"(optional): ラベル名とラベルの値 カンマでつなげることで複数定義可能
  • value: 値、中身はGoのParseFloat()でパースできる値である必要がある。数値以外では Nan +Inf -Inf が使える。
  • timestamp(optional): timestamp

go_gc_duration_seconds{quantile="0.0"} 0.0 の場合

  • metric_name: go_gc_duration_seconds
  • label: name=quantile="0.0"
  • value: 0.0

となる。labelはパーセンタイル等、同一Metricsだが、ラベル付をしたい時任に利用する

HELPとTYPE

これとは別に #HELP#TYPE が存在している

# HELP $MetricName $Description というような形でMetricsに対して説明を付与することができる

# TYPE $MetricName $MetricType というような形でMetricsがどんなMetricsなのかを付与できる。

付与できる一覧は以下

  • Counter: カウンター、単調増加する値、完了したタスク数のような値で使う
  • Gauge: ゲージ、単一の数値、メモリ使用量など
  • Histogram: ヒストグラム
  • Summary: 概要、リクエスト期間等でサンプリングできるもの、パーセンタイル等で使える
  • Untyped: 型なし

prometheus.io

Stackdriveに変換される上でのポイント

prometheus to stackdirverにて変換するときは以下のように変換する

MetricType

  • COUNTER OR HISTOGRAM -> CUMULATIVE
  • OTHER -> GAUGE

github.com

func extractMetricKind(mType dto.MetricType) string {
  if mType == dto.MetricType_COUNTER || mType == dto.MetricType_HISTOGRAM {
  return "CUMULATIVE"
  }
  return "GAUGE"
}

ValueType

stackdriverで扱う値は ParseFloatでパースできるかどうかしか見てないが、StackdriverのValue Typeでは型があるので変換される

cloud.google.com

  • すでにcustomMetricsが定義されいる場合: 定義されている型に従う(おそらく)
  • MetricTypeがHISTOGRAMの場合 -> DISTRIBUTION
  • それ以外: INT64

github.com

func extractValueType(mType dto.MetricType, originalDescriptor *v3.MetricDescriptor) string {
  // If MetricDescriptor is created already in the Stackdriver use stored value type.
  // This is going to work perfectly for "container.googleapis.com" metrics.
  if originalDescriptor != nil {
  // TODO(loburm): for custom metrics add logic that can figure value type base on the actual values.
    return originalDescriptor.ValueType
  }
  if mType == dto.MetricType_HISTOGRAM {
    return "DISTRIBUTION"
  }
  return "INT64"
}

https://cloud.google.com/monitoring/api/v3/metrics-details?hl=ja#metric-kinds

https://github.com/int-tt/prometheus-test

concourseのMetricsをstackdriverに送ってみる(Prometheus -> stackdirver)

これはRecruit Engineers Advent Calendar22日目の記事です。(許して

最近はConcourse CIを利用してCI/CD環境を準備することが多いのですが、ちゃんと使えば使うほど、プロジェクトの中でも重要度が高まり、ちゃんと運用しないと辛いことになってしまいます。 CI/CD環境もちゃんとモニタリングして運用していくぞ!ということで色々調べていくとconcourseではメトリクスエンドポイントが提供されているのを見つけました。

公式の利用例も公開されていて、かっちょいいダッシュボードが見えるようになっています。 https://metrics.concourse-ci.org/dashboard/db/concourse

メトリクスは

  • InfluxDB
  • NewRelic
  • Prometheus
  • Datadog
  • Riemann

のフォーマットで提供されていますが、直近ではstackdriverの検証もやっており、concourseのためだけにdatadogやPrometheusを準備するのは面倒だったため、Googleが公開しているPrometheus-to-sd を利用してみます。

exmapleとしてkube-state-metrics をstackdriverに送るサンプルが公開されているため、これをもとに弄ってみます。

https://github.com/GoogleCloudPlatform/k8s-stackdriver/blob/master/prometheus-to-sd/kubernetes/prometheus-to-sd-kube-state-metrics.yaml

apiVersion: v1
kind: Pod
metadata:
  name: kube-metrics
spec:
  hostNetwork: true
  containers:
  - name: kube-state-metrics
    image: gcr.io/google-containers/kube-state-metrics:v1.4.0
    ports:
      - name: http-metrics
        containerPort: 8080
    readinessProbe:
      httpGet:
        path: /healthz
        port: 8080
      initialDelaySeconds: 5
      timeoutSeconds: 5
    resources:
      requests:
        memory: 200Mi
        cpu: 100m
      limits:
        memory: 300Mi
        cpu: 200m
  - name: prometheus-to-sd
    image: gcr.io/google-containers/prometheus-to-sd:v0.3.2
    ports:
      - name: profiler
        containerPort: 6060
    command:
      - /monitor
      - --stackdriver-prefix=custom.googleapis.com
      - --source=kube-state-metrics:http://localhost:8080
      - --pod-id=$(POD_NAME)
      - --namespace-id=$(POD_NAMESPACE)
    env:
      - name: POD_NAME
        valueFrom:
          fieldRef:
            fieldPath: metadata.name
      - name: POD_NAMESPACE
        valueFrom:
          fieldRef:
            fieldPath: metadata.namespace

concourseの環境はGKE上にhelmで準備します。 https://github.com/helm/charts/tree/master/stable/concourse

今回は 面倒なので prometheusのmetricsをstackdriverに送るところまでがスコープなので 、 prometheus.enabled にして web-deployment.yaml に以下のcontainerを追加します。 promethusをenableするとserviceも作成されるので Prometheus-to-sd はdeploymentで準備してservice経由で取得するのが正しい気もするが、省略します

        - name: prometheus-to-sd
          image: gcr.io/google-containers/prometheus-to-sd:v0.3.2
          ports:
            - name: profiler
              containerPort: 6060
          command:
            - /monitor
            - --stackdriver-prefix=custom.googleapis.com
            - --source=concourse:http://localhost:9391
            - --pod-id=$(POD_NAME)
            - --namespace-id=$(POD_NAMESPACE)
          env:
            - name: POD_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
            - name: POD_NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
 

これで設定は終了、これをGKEにデプロイして同じプロジェクトのstackdriver MonitoringからMetricsを確認するとメトリクスが飛んで出来てるのが確認できます

f:id:int_tt:20181225085335p:plain

TODO

  • pipelineを準備してちゃんと試せていないのでPrometheusと同等のMetricsがちゃんと取得できてるか確認する
  • 仕組みちゃんと調べる
  • prometheusもstackdriverもまだnoobなので途中経過を書く