Locustで時間に応じて一定のRPSをかける

概要

Locustで時間に応じて一定のrpsを流す

結論

constant_pacingとLoadTestShapeを利用する。

バージョン

  • Locust v1.6.0

  • 120秒かけて0->100rpsまで上げる
  • 120秒間100rpsをかける
  • 120秒かけて100->1rpsまで下げる

1秒に1回タスクを実行するようにして、ユーザ数を増やしていけば目的のRPSを達成できる。

from locust import HttpUser, task, constant_pacing, LoadTestShape

class CustomShape(LoadTestShape):
    stages = [
        {"duration": 120, "users": 100, "spawn_rate": 1},
        {"duration": 240, "users": 100, "spawn_rate": 0},
        {"duration": 360, "users": 1, "spawn_rate": 1},
    ]

    def tick(self):
        run_time = self.get_run_time()

        for stage in self.stages:
            if run_time < stage["duration"]:
                tick_data = (stage["users"], stage["spawn_rate"])
                return tick_data

        return None


class GuestPcUser(HttpUser):
    wait_time = constant_pacing(1)

    host = "http://localhost:3000"

    @task
    def task(self):
        name = self.host

        self.client.get("/",)

結果

f:id:mMQnaZ7vL2DWkoU:20210726194836p:plain
Locustのレポート

参考情報

https://github.com/locustio/locust/issues/277#issuecomment-812831204
https://docs.locust.io/en/stable/api.html#locust.wait_time.constant_pacing
https://github.com/locustio/locust/blob/master/examples/custom_shape/stages.py

Locustで複数パス・複数ホストへリクエストし、それぞれ個別にGrafanaでグラフ化する

概要

負荷試験ツールのLocustを使ってWebシステムに負荷をかける。
その時、リクエストのホスト、パス毎に負荷試験結果をグラフ化したい。
Locustで上記を実現するにはどうすればよいか記録する。

バージョン

  • Locust v1.6.0
  • Grafana v8.0.x
  • Prometheus v2.28.0

前提知識

  • Locust利用経験
  • Grafana利用経験
  • Prometheus利用経験

LocustデフォルトのGUIについて

LocustはGUIを持つ。
https://docs.locust.io/en/stable/quickstart.html#locust-s-web-interface
デフォルトでは複数のリクエスト先に負荷をかけても合算結果のみ表示される。
f:id:mMQnaZ7vL2DWkoU:20210630183803p:plain
これをパスごとに結果を表示したい。

結論

Locut+Prometheus+Grafanaを利用する。

docker-compose.yaml

version: "3"

services:
  master:
    image: locustio/locust
    ports:
      - "8089:8089"
    volumes:
      - ./:/mnt/locust
    command: -f /mnt/locust/locustfile.py --master -u 10 -r 1

  worker:
    image: locustio/locust
    volumes:
      - ./:/mnt/locust
    command: -f /mnt/locust/locustfile.py --worker --master-host master

  prometheus:
    image: prom/prometheus
    container_name: prometheus
    volumes:
      - ./prometheus:/etc/prometheus
    command: "--config.file=/etc/prometheus/prometheus.yaml"
    ports:
      - 9090:9090
    restart: always

  grafana:
    image: grafana/grafana
    container_name: grafana
    volumes:
      - ./grafana/dashboards:/var/lib/grafana/dashboards/
      - ./grafana/datasource.yaml:/etc/grafana/provisioning/datasources/datasource.yaml
      - ./grafana/dashboard.yaml:/etc/grafana//provisioning/dashboards/dashboard.yaml
    ports:
      - 3000:3000
    environment:
      - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
      - GF_AUTH_ANONYMOUS_ENABLED=true
    restart: always

  locust-exporter:
    image: containersol/locust_exporter
    environment:
      - LOCUST_EXPORTER_URI=http://master:8089
    ports:
      - 9646:9646
    depends_on:
      - master

prometheus/prometheus.yaml

global:
  scrape_interval: 10s
  evaluation_interval: 10s
  external_labels:
    monitor: "codelab-monitor"

rule_files:

scrape_configs:
  - job_name: "prometheus"
    static_configs:
      - targets: ["localhost:9090"]
  - job_name: "locust"
    scrape_interval: 2s
    static_configs:
      - targets: ["locust-exporter:9646"]

grafana/datasource.yaml

# config file version
apiVersion: 1

datasources:
  # <string, required> name of the datasource. Required
  - name: Prometheus
    # <string, required> datasource type. Required
    type: prometheus
    # <string, required> access mode. proxy or direct (Server or Browser in the UI). Required
    access: proxy
    url: http://prometheus:9090
    editable: true

手順

1. Locustでリクエストをグループ化する

https://docs.locust.io/en/stable/writing-a-locustfile.html#grouping-requests
下記のように書くとstatisticsでnameごとに表示される。上記画像のようにパスごとに表示される。

# Statistics for these requests will be grouped under: /blog/?id=[id]
for i in range(10):
    self.client.get("/blog?id=%i" % i, name="/blog?id=[id]")

2. Prometheus,Locust-Exporter,Grafanaを用意する。

Prometheus,Grafanaは公式Dockerイメージがあるので利用する。(上記docker-comopse.yaml参照)
exporterは既存のものを見つけたので下記を利用する。
https://github.com/ContainerSolutions/locust_exporter

3. GrafanaのPrometheusデータソースの設定

4. Grafanaダッシュボードの用意

例えばRPSをパスごとに表示する場合は下記のようになる。
nameはLocustのグループで設定したものを記述する。

locust_requests_current_rps{name=\"hoge\"}

5. 負荷をかける

結果

下記のようにグループされたパス、ホストごとにグラフ化することができた。 f:id:mMQnaZ7vL2DWkoU:20210630191037p:plain

補足

locust_exporterを利用するときはLocustの--headlessオプションが使えない。
--headlessオプションを利用するときは下記ISSUEのような対応が必要になる。
https://github.com/ContainerSolutions/locust_exporter/issues/5

まとめ

Locustでリクエストパス、ホスト毎にグラフ化することができた。 ちなみに、Gatlingはデフォルトでシナリオ毎のグラフ化機能を持つ。

負荷試験ツールの選定時に考慮することまとめ

概要

負荷試験ツールの選定を行うときに考慮することをまとめる

機能

  • ログインが必要なページへの対応
     Cookie変更など
  • 負荷分散機能
     負荷試験クライアントマシンのリソース上限に達する場合は負荷分散を考慮する必要あり
  • 複数ユーザ対応
     ファイル読み込み等で複数のユーザ情報を再現する
  • CI化
  • 複数シナリオ対応
  • 複数シナリオの個別グラフ化
  • 公式のDockerイメージ

作業コスト

  • 実行手順の少なさ
  • 可視化作業の手順の少なさ
  • 初期環境構築手順の少なさ
  • 負荷試験実行結果収集コスト
  • グラフ作成コスト

可視化性

  • データ項目数
  • グラフ作成機能

金銭コスト

  • 有料か無料
  • SaaS版はあるか

保守コスト

  • 使用言語 プロダクトに親和性のある言語か
  • 日本語事例、ドキュメント
  • ドキュメント項目
  • GitHubスター数、コミッタ数

参考記事

下記エントリが網羅的にまとまっておりとても参考になる。
https://k6.io/blog/comparing-best-open-source-load-testing-tools/

複業の契約が半年間継続したのでやっていることをまとめる

概要

複業(副業)の契約が半年間継続しているので感想やTipsをまとめる。

案件獲得

スカウトサイトで単価と得意領域を提示。
スカウトが入るので話をすり合わせて契約する。
エージェントはなし。

勤務形態

契約方法の違いを確認し、準委任契約で契約。
契約時は契約書をよく読むこと。(契約書を読んで請負から準委任に変えていただいた。)

勤務時間、勤務ペース

昨今の自粛により時間が浮いてしまったため、その分を副業に当てている。
副業開始当初は土日に稼働していたが、疲れの蓄積や曜日感覚が狂うので週1は必ずフリーの日を設けるようにしている。
本業で重たい仕事が終わった後に副業すると頭が働かないので、平日は朝に稼働する。

担当範囲

チーム構成は6人程度のチーム
担当範囲は設計、クラウドインフラ構築、CI/CD構築、モニタリング構築、バックエンド実装、フロントエンド実装、技術調査など
使っている技術はReact,TypeScript,Go,Terraform,AWSなど

Tips

タスクの認識合わせ

日中は本業をしているので、非同期主体のコミュニケーションになる。 細かく認識合わせを行い、認識齟齬の防止、期待値のすり合わせをおこなう。
タスクを進める前に

  • なぜやるか?
  • 完了定義
  • 成果物

を書いて認識齟齬がないようにする。

わからないところ、認識を合わせたい所があれば、チケット上やPRなどオープンな場所でコミュニケーションする。

期待値の共有

  • どういう経験を積んできたか
  • どのような価値を提供できるか
  • どういう仕事にモチベーションがあるか
    を共有する。

稼働時間のズレ

副業参加なので他メンバーとの稼働時間の量、稼働のタイミングでズレがでてくる。
そのため、同期的な作業が必要なタスクよりは単独で進められるタスクのほうがやりやすく感じた。
わからない所があればオープンな場所で非同期コミュニケーションを取る。
話が込み入りそうなときは必要に応じて口頭のコミュニケーションを選択。

業務の改善提案

ここはやりやすかった、ここはやりにくかったのでこうしたい、などを提案しながら進める。

ドキュメント整備

少数のチームなので担当した範囲で今後も参照される箇所はドキュメント化する。

未経験の技術について

未経験の業務を担当する場合は負荷が増えることを事前に意識し、生活を調整する。
キャッチアップ方法は下記の流れでやることが多い。 https://mzqvis6akmakplpmcjx3.hatenablog.com/entry/2021/01/19/180025

健康

散歩、入浴、ストレッチ、筋トレはやっておく。
整体を開拓中。

その他感想

副業の目的を明確にする

何のために副業をやるのか、スキルアップなのか収入を増やしたいのか優先順位を明確にする。

開発規模の違い

本業が大規模開発なので開発規模の違いが勉強になった。
求められる性能、可用性、開発フローなどが異なるため、要件にあったものを提案する。

依存先が分散する

金銭面、評価面で依存先が分散するので、固執することなく俯瞰して仕事できる。

サイクルを回す

本業で学んだことを複業で活かし、複業で学んだことを本業で活かすことで仕事の理解が深まる。

まとめ

契約書をよく読み、自分の望む形態で契約する。
こまめに適切な手段でコミュニケーションする。
成果の認識を合わせ、成果を出す。  

AWS ECS構築時のメモ

概要

AWS ECSのデプロイフローを構築したときのメモを書き起こす。

terraform version0.13.2

CI/CD

GitHub Actions

aws-actions/amazon-ecs-render-task-definition@v1
及び
aws-actions/amazon-ecs-deploy-task-definition@v1
を利用する。

dev環境

ビルド後にaws-actions/amazon-ecs-render-task-definitionを利用し、aws-actions/amazon-ecs-deploy-task-definitionでデプロイする。

stg及びprd環境

デプロイしたいイメージを更新し、aws-actions/amazon-ecs-deploy-task-definitionでデプロイする。

ecs run-taskでjobを実行する

前提:特定のサービスに対してjobを実行する。 fargateタイプに対してecs run-taskする場合、セキュリティグループ及びサブネットを指定する必要がある。

$SUBNETS=$(aws ecs describe-services --services SERVICE --cluster CLUSTER --query "services[*].networkConfiguration.awsvpcConfiguration.join(',',subnets[])" --output text | sed 's/[^,]\+/"&"/g')
$SG=$(aws ecs describe-services --services SERVICE --cluster CLUSTER --query "services[*].networkConfiguration.awsvpcConfiguration.securityGroups" --output text | sed 's/[^,]\+/"&"/g')
$aws ecs run-task --cluster CLUSTER --launch-type "FARGATE" --overrides "containerOverrides=[{name="server",command=[\"bash\",\"-c\",\"set -a; ls;\"]}]" --network-configuration "awsvpcConfiguration={subnets=[$SUBNETS],securityGroups=[$SG]}" --task-definition TASK_DEF

Terraformのタスク定義について

Terraformでリソース管理する場合、デプロイごとにタスク定義が更新されるので、ignore_changesする必要がある。

resource "aws_ecs_service" "test" {
 ....

  lifecycle {
    ignore_changes = [task_definition]
  }
}

DevOpsチェックリスト技術編

概要

以前書いたメモを公開する。
DevOpsは技術面と文化面があるので、技術編としている。

バージョン管理・ブランチ戦略

  • Gitでバージョン管理している
  • ブランチ戦略を決定している 基本はGitHub flowもしくはgit flowに合わせる。

CI

  • 自動的にビルドできる
  • 自動的にリントできる
  • 自動的にスペルチェックできる
  • 自動的に単体テスト(テストサイズsmall)できる
  • 自動的に統合テスト(テストサイズmedium)できる
  • 自動的にシステムテスト(テストサイズlarge)できる
  • 自動的に脆弱性検査できる
  • 自動的に負荷試験できる
  • 上記項目の結果を通知できる

CD

  • 自動的にデプロイできる
  • リリースに不具合があった場合、1分以内にロールバックできる
  • デプロイ頻度・成功率を計測している

モニタリング

  • リリースに不具合があったときに、検知できる
  • APMを導入している
  • 上記をダッシュボード化している
  • 上記でエラーが発生したとき通知できる
  • フロントエンドパフォーマンスをモニタリングしている
  • 上記の低下を検知できる
  • アプリケーションログのダッシュボードがある
  • 上記でエラーが発生したとき通知できる
  • フロントエンドログのダッシュボードがある
  • 上記でエラーが発生したとき通知できる

IaC

  • インフラリソースをコード化している
  • インフラリソースに手動で変更を加えない運用になっている
  • システム内のリソースをコード化している
  • システム内のリソースに手動で変更を加えない運用になっている

参考

https://aws.amazon.com/jp/devops/what-is-devops/

https://cloud.google.com/devops

https://azure.microsoft.com/ja-jp/overview/what-is-devops/

https://testing.googleblog.com/2010/12/test-sizes.html

Amazon CloudWatch SyntheticsとAWS Chatbotで外形監視をSlackに通知する

概要

Amazon CloudWatch Synthetics + AWS Chatbotで外形監視の通知システムを作ったのでログに残す。

構成図

f:id:mMQnaZ7vL2DWkoU:20210427192432p:plain

作業内容

今回の通知は本番環境のみ利用するのでコード化せず、GUIで作成した。
作業詳細は公式ドキュメント参照。

https://docs.aws.amazon.com/chatbot/index.html
https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/CloudWatch_Synthetics_Canaries.html

大まかな手順は下記。

  1. 通知用のSNSトピック作成
  2. AWS ChatBotの作成とSNS連携
  3. Canary作成とSNS連携

Canaryはユースケースに応じたBluePrintが用意されている。今回はHeartBeatを選択。
f:id:mMQnaZ7vL2DWkoU:20210427194051p:plain

Canaryリソースの削除

Canaryを作ると下記リソースが自動で作られる。

  • CloudWatch Alarm
  • lambda(puppeteer実行)
  • S3(結果の保存)

今回の目的の場合、通知用のSNSトピックは自分で作る必要がある。 また、保存先のS3を自動作成されたもの以外にするときは、命名変更の都合上IAMリソースも手動で作成する必要がある。

関連リソースは自動で削除されないので探して手動で削除する。 下記ドキュメントには書いていないがIAM Policyも作られる。 https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/synthetics_canaries_deletion.htm

まとめ

AWS Chatbotを使うことで少ない手順でSlack連携できた。
Amazon CloudWatch Syntheticsを使うことで外形監視システムを導入できた。