GeekFactory

int128.hatenablog.com

2022年のお仕事まとめ

今年やっていた仕事をまとめてみた。

デプロイパイプラインの再構築

現職では、Pull Request を作るとマイクロサービス一式のプレビュー環境が Kubernetes にデプロイされるようになっている。メインブランチにマージせずに動作確認やレビューができるため、漸進的に開発していく上で重要な仕組みになっている。

歴史的経緯から以下の課題を抱えていた。

この仕組みを以下の技術スタックで再構築した。

  • GitHub Actions (TypeScript)
  • Kaniko
  • Argo CD

現職では大規模な monorepo を運用しているため、GitHub Actions の制約で苦労している。例えば、GitHub Actions では paths filter がサポートされているが、Pull Request の差分が300ファイルを超える場合は最初の300ファイルしか評価されない問題がある。

開発体験にはまだまだ課題がたくさんあるため、2023年も引き続き改善に取り組んでいきたい。

データパイプラインの再構築

現職では、開発、テスト、カスタマーサポート、データ分析に使うデータベースが本番環境から日次同期されるようになっている。これは本番に近いデータを使って開発する上で重要な仕組みで、みんなが同じものを見るという文化の形成にも役立っている。日々の生活では空気のような存在になっているが、以下の課題を抱えていた。

  • 日々の開発やデータ分析を支える基盤であるにも関わらず動作が不安定
  • いつどのスナップショットが開発環境に入るべきといった仕様の一貫性がなく、明文化もされていなかった
  • 乱開発されたスクリプト

長い歴史を抱える仕組みではよくある話であろう。

この仕組みを以下の技術スタックで再構築した。

  • AWS Step Functions, EventBridge, ECS Fargate Task
  • Aurora S3 export
  • Go (aws-sdk-go-v2)
  • Sentry, Datadog

EventBridge は大きな可能性を秘めた仕組みであることを実感した。複数の AWS アカウントをまたいでイベントを連携したり、イベントを Slack などの HTTPS API に送ったり、といった多様な使い方を学べて面白かった。

2022年で概ね移行が完了したが、データ基盤まわりはまだやり残したことがあるので、2023年も引き続き取り組んでいく。

Aurora major version upgrade の改善

現職では主に Amazon Aurora (PostgreSQL) データベースを採用しており、マイクロサービスのオーナーが各データベースを管理している。定期的に新しいメジャーバージョンに対応する必要がある。マイナーバージョンに比べてメジャーバージョンは注意点が多いため、開発者の負担も大きくなる傾向がある。

  • 互換性のない変更が含まれる可能性がある
  • ある程度のダウンタイムがある(サービスの停止が必要)
  • 手順が複雑でややこしい

これまで Terraform でメジャーバージョンを上げていたが、以下の課題があった。

  • クラスタやデータベースに pending maintenance(保留中のメンテナンス)がある場合は terraform apply が失敗する
  • Terraform のパラメータを正しく書かないと terraform apply が失敗する。terraform plan では気づかない
    • 例えば、現行バージョンと新しいバージョンの parameter group を両方指定する必要があるが、直感的には分かりにくい
  • terraform apply が失敗した後の復旧手順が複雑

開発者の負担を軽減するため、Aurora API を呼び出してメジャーバージョンを上げる仕組みを用意した。具体的には、Step Functions, ECS Fargate Task, Go (aws-sdk-go-v2) で以下を実装している。

  • クラスタやデータベースの pending maintenance をすべて適用する
  • クラスタのメジャーバージョンを上げる
  • データベースに対して ANALYZE を実行する

これでだいぶ体験が良くなった。頻度の低い仕事とはいえ、すべてマイクロサービスで必要になる作業なので今後も改善していきたい。

GitHub Actions self-hosted runner の改善

現職では大規模な monorepo で self-hosted runner を運用している。マイクロサービス(もはやマイクロとは呼べないアプリケーションもある)によっては Pull Request を作成した契機で数十件のジョブが並列実行されることもあるため、大量のジョブをいかに早く安く安定して捌いていくことが重要になってくる。

2021年に actions-runner-controller を導入したが、2022年は主に以下の取り組みを行っていた。

  • Karpenter によるジョブ起動待ちの改善
  • EC2 Instance store による安定性やパフォーマンスの改善
  • Horizontal Runner Autoscaler のチューニング
  • EC2 や Datadog コストの最適化
  • 安定性やコストのモニタリング

いずれもチームの同僚による貢献が大きい。私は最後のモニタリングを中心に取り組んでいた。

2022年に取り組んでいた安定性の問題としては以下が挙げられる。

  • actions-runner-controller や actions/runner イメージのバージョンアップによる障害
  • ジョブのメモリ使用量が増えたことによる Container OOM
  • ノードの集積率が上がることによる Node OOM
  • EC2 spot interruption によるジョブの終了
  • GitHub Actions(コントロールプレーン)の障害

2023年はチームで SLO を見ながら継続的に開発体験とコストを最適化していく仕組みを模索していきたい。

開発体験を改善していくコラボレーション

2022年はチーム外の人たちと一緒に取り組む仕事が増えた。例えば、

といったものがある。 仕組みを改善することで開発組織全体に役立つチャンスはまだまだあるので、2023年も引き続きコラボしていきたい。

Step Functions の結果を EventBridge 経由で Slack に通知する

下記の記事で EventBridge から Slack への通知が紹介されていたため、Step Functions で試してみました。

dev.classmethod.jp

基本的な流れは記事で説明されているので省略します。Step Functions 固有の内容だけ本記事で説明します。

Step Functions のイベント

EventBridge では state machine の ARN やステータスを絞り込めます。例えば、以下のように記述すると全 state machine の成功と失敗のイベントを受け取ります。

{
  "source": ["aws.states"],
  "detail-type": ["Step Functions Execution Status Change"],
  "detail": {
    "status": ["SUCCEEDED", "FAILED"]
  }
}

実際に Step Functions から受け取ったイベントを貼っておきます。

{
  "executionArn":"arn:aws:states:us-west-2:ACCOUNT:execution:helloworld:7cdd674c-eaac-12de-b073-549373ca07fa",
  "stateMachineArn":"arn:aws:states:us-west-2:ACCOUNT:stateMachine:helloworld",
  "name":"7cdd674c-eaac-12de-b073-549373ca07fa",
  "status":"SUCCEEDED",
  "startDate":1643530763937,
  "stopDate":1643530764140,
  "input":"{\n    \"Comment\": \"Insert your JSON here\"\n}",
  "inputDetails":{"included":true},
  "output":"\"World\"",
  "outputDetails":{"included":true}
}

inputoutput には state machine の入出力が設定されます。state machine で通知本文を出力する使い方がありそうです。

イベントの内容をダンプするには Input Transformer で以下を指定します。Slack にそのまま JSON がポストされます。

{"json":"$.detail"}
{
  "channel": "ID",
  "text": <json>
}

Slack の通知内容

以下のように state machine execution へのリンクを付けておくと便利です。

{
    "blocks": [
        {
            "type": "section",
            "text": {
                "type": "mrkdwn",
                "text": "<status> `<output>`"
            }
        },
        {
            "type": "context",
            "elements": [
                {
                    "type": "mrkdwn",
                    "text": "https://us-west-2.console.aws.amazon.com/states/home?region=us-west-2#/executions/details/<executionArn>"
                }
            ]
        }
    ]
}

Renovate で特定のパッケージを同時に上げる

Renovate で特定のパッケージ群のバージョンを同時に上げる方法を調べたのでメモです。

解決したい課題

TypeScript GitHub Action で以下のパッケージのバージョンアップを個別に受け取るので CI が失敗してしまう。auto merge できない。

  • jest
  • jest-circus
  • ts-jest

解決策

Group all packages starting with abc together in one PR を参考にしました。Renovate の設定で matchPackageNames, groupName を指定すると1つの PR にまとめてバージョンアップされます。

具体的には以下のように設定します。

  "packageRules": [
    {
      "matchPackageNames": ["jest", "ts-jest"],
      "matchPackagePrefixes": ["jest-"],
      "groupName": "jest"
    }
  ]

これで複数のパッケージをまとめた PR を受け取れました。

github.com