【ZOZOTOWNマイクロサービス化】API Gatewayを自社開発したノウハウ大公開! - ZOZO TECH BLOG

【ZOZOTOWNマイクロサービス化】API Gatewayを自社開発したノウハウ大公開!

ogp

はじめに

こんにちは。ECプラットフォーム部のAPI基盤チームに所属している籏野 @gold_kou と申します。普段は、GoでAPI GatewayやID基盤(認証マイクロサービス)の開発をしています。

ZOZOテクノロジーズでは、2020年11月5日にZOZO Technologies Meetup〜ZOZOTOWNシステムリプレイスの裏側〜を開催しました。その中で発表されたAPI Gatewayによるマイクロサービスへのアクセス制御に関して、当日話せなかった内容も含めて、API Gatewayについてこの記事で網羅的にまとめました。

API Gatewayやマイクロサービスに興味ある方、「API Gateway」という言葉は知っているけど中身はよく分からないという方向けの記事なので、読んでいただけると幸いです。

ZOZOTOWNのリプレイス

ZOZOTOWNがこれからも成長を続けるために、開発効率・運用性・拡張性・柔軟性・回復性の確保を見据えて、技術面や環境面でも刷新するためのリプレイスを進めています。

マイクロサービス化の目的

ZOZOTOWNの開発では、レガシシステムのリプレイスに伴い、モノリシックな開発からマイクロサービス開発への移行を推進しています。ただし、マイクロサービス化はあくまで手段であり、それ自体は我々の目的ではありません。マイクロサービス化の過程において、健全な開発組織の文化醸成を行い、最終的には組織全体のパフォーマンスの向上を目的としています。

ストラングラーパターン

ZOZOTOWNは最初にリリースされてから15年以上が経過しています。大規模なZOZOTOWNを一度に全てリプレイスするのは困難です。

そこで、ストラングラーパターンを採用しています。ストラングラーパターンは、レガシシステムを徐々に新しいシステムに置き換えて移行する方法です。今回ご紹介するAPI Gatewayがストラングラーファサードの役割を担っています。

strangler pattern

API Gateway概要

そもそものAPI Gatewayについて説明します。

API Gatewayとは

API Gatewayとは、クライアントとAPI群の間に設置される、APIリクエストを各アプリケーション(マイクロサービスおよびレガシアプリケーション)へルーティングするアプリケーションです。

以下は、API Gatewayを使用した、ZOZOTOWNのマイクロサービス化の一例を示した図です。

what is api gateway

マイクロサービス化による問題(API Gatewayを導入しない場合)

マイクロサービス化自体は、API Gatewayやサービスメッシュを導入せずとも可能です。しかしながら、下記の問題を抱える可能性があります。

  • マイクロサービス側で同じような処理が複数箇所で実装される
    • 認証/認可
  • クライアント側で同じようなリクエスト制御が複数箇所で実装される
    • リトライ
    • タイムアウト
  • クライアントとマイクロサービス間のネットワークラウンドトリップによりレスポンス速度が低下する
  • マイクロサービス側の変更がクライアント側に影響しやすい
  • マイクロサービスを外部公開することになる
  • トレーサビリティが低下する

API Gatewayを導入することで上記の問題を解決できます。

参考:API ゲートウェイ パターンと、クライアントからマイクロサービスへの直接通信との比較

API Gateway導入による問題

一方で、API Gateway導入による問題もあります。

  • 可用性低下
    • 単一障害点の増加
  • 性能低下
    • API Gateway通過時のルーティング処理コスト
    • 通信回数の増加の可能性(1APIリクエストのみの場合)
    • スケーリングが間に合わない場合はAPI Gatewayがボトルネックになる可能性
  • コスト増加
    • 開発する場合は開発コスト
    • 既存サービスを利用する場合はそのサービスの学習コスト
    • 運用コスト
    • インフラコスト

参考:API ゲートウェイ パターンの欠点

API Gatewayの自社開発

ZOZOTOWNの開発では、API Gatewayを自社開発しています。

自社開発をする理由

「API Gateway」といえば、Amazon API GatewayやOSSのKongが有名です。しかしながら、今回はマイクロサービス化の開発中に発生する、様々な要求に柔軟に素早く対応するため、自分たちで開発することにしました。

例えば、自分たちが必要としているリトライやタイムアウトの細かい制御機能は、少なくとも導入検討時においては既存のものでは実現が難しそうでした。カスタムのプラグインなどを開発すれば要件を満たせますが、Lua/C++/Lambdaなどを駆使してスピード感を持って開発できるエンジニアがチームにいませんでした。

また、API Gateway導入時点ではID基盤側の要件が定まりきっておらず、多くの変更が発生しても柔軟に対応できる必要がありました。ID基盤は認証マイクロサービスで、ID基盤が発行したトークンの検証などをAPI Gatewayで処理しています。

加えて、開発当初、API Gatewayは全てのAPIリクエスト(マイクロサービス間も含む)がAPI Gatewayを経由することを想定していました。したがって、リクエスト量に応じた従量課金のサービスは避けたかったという理由もありました。

技術スタック

Go/Docker/AWS(EKSなど)/GitHub Actionsなどを使っています。

言語は実行速度や学習コストの低さなどから、Goを選択しました。 そして、API Gatewayはマイクロサービスと同様に、コンテナとしてEKS上に構築しています。

また、GitHub Actionsでは、以下のような処理を自動化しています。

  • テスト
  • Dockerイメージの脆弱性診断
  • ECRへのイメージプッシュ
  • デプロイ
  • コンフィグ関連のドキュメント作成

データストア

現状、API Gatewayにデータストアは持たせていません。例えば、認証用に使用している公開鍵はPod上のオンメモリに存在しています。これは可用性や性能を意識しているためなのですが、今後どうなるかは未定です。

追加機能として、スロットリング機能の実装を検討しています。その際に、レートリミットを管理する必要があります。複数Podを考慮すると何かしらのデータストア(ElastiCacheなど)は必要になる可能性があります。

API Gatewayの機能と設定

API Gatewayに実装した機能とその設定方法について説明します。

リバースプロキシ

API Gatewayの最も基本的な機能の1つとして、クライアントから来たHTTPリクエストをマイクロサービスへ転送する、リバースプロキシ機能があります。

大まかな処理の流れは以下です。

  1. HTTPサーバを起動し、リクエストを受け付ける
    • Goの標準パッケージ net/httpServer 型の ListenAndServe メソッドを使用
  2. 受け付けたリクエスト内容から転送先を確定する
    • 同時に、リクエスト内容(パス、ヘッダなど)を一部加工
  3. 転送先のマイクロサービスにHTTPリクエストする
    • Goの標準パッケージ net/httpClient 型の Do メソッドを使用
  4. HTTPレスポンスをAPIクライアントへ返す

個人的な話ですが、最初は「リバースプロキシ機能」の開発と言われてもピンと来なかったです。しかしながら、開発していくうちに、「そうか。実体は単なるHTTPサーバとHTTPクライアントなんだな」と理解して、スッキリしました。当たり前と言えば当たり前なのですが。

ルーティング

ターゲットとターゲットグループ

ターゲットとターゲットグループはルーティングにおいて重要な概念です。

ターゲットは転送先の接続情報(ホストとポート)です。

ターゲットグループは、転送先であるターゲットをまとめた単位です。ターゲットグループ内ではレガシなモノリスシステムと新規のマイクロサービスを混在させるようなこともできます。

ターゲットとターゲットグループの設定には、 target_groups.yml という名前のYAMLファイルを用意します。YAMLファイル上で設定値が指定されていないものに関しては、ハードコーディングされたdefault値が適用されます。

以下は具体例です。TargetGroupAというターゲットグループの中に、target1.example.comとtarget2.example.comの2つのターゲットを指定しています。

TargetGroupA:
  targets:
    - host: target1.example.com
      port: 8080
    - host: target2.example.com
      port: 8081

ルーティングの設定

routes.yml という名前のYAMLファイルを用意します。

ルーティングする送信元と送信先の情報を定義します。もしリクエスト情報が、定義された送信元情報に一致しなければ404を返します。

以下は具体例です。HTTPリクエストのパスが正規表現で ^/sample/(.+)$ に一致した場合、転送先のパスをGoの regexp.ReplaceAllString を使って、 /$1 に置き換えます。正規表現マッチした部分がURLのリライトの対象となるため、例えば /sample/hoge というパスでリクエストがきていた場合は、 /hoge に置き換えられます。

TargetGroupAに指定されたターゲットに対してラウンドロビンで転送先のターゲットを決定します。

- from:
    path: ^/sample/(.+)$
  to:
    destinations:
      - target_group: TargetGroupA
        path: /$1

加重ルーティング

target_groups.yml および routes.yml にて重み(weight)を設定できます。target_groups.yml で指定する重みはターゲットに対する重みで、 routes.yml で指定する重みはターゲットグループに対する重みです。転送先の比重をコントロールすることで、加重ルーティングおよびカナリアリリースを実現できます。

例えば、target_groups.yml でTargetGroupA内のtarget1.example.comに80%で、target2.example.comに20%の比重で振り分ける、重み付きラウンドロビンの指定が可能です。

TargetGroupA:
  targets:
    - host: target1.example.com
      port: 8080
      weight: 4
    - host: target2.example.com
      port: 8081
      weight: 1 

weight_80_20

もし、重みを指定しない場合あるいは全てに同じ重みを指定した場合は、一般的なラウンドロビンになります。つまり、各ターゲットへ順に振り分けられます。

TargetGroupA:
  targets:
    - host: target1.example.com
      port: 8080
      weight: 1
    - host: target2.example.com
      port: 8081
      weight: 1 

weight_50_50

APIクライアントトークン認証

APIクライアントトークンは、クライアントタイプ毎に用意したトークンです。以下のように、 api_client_tokens.yml という名前のYAMLファイルに、クライアントタイプとトークンの組み合わせを設定します。

APIクライアントトークン認証では、そのYAMLファイルの値とAPIクライアントトークン用の独自ヘッダに格納された値を比較します。

SampleClient:
  - abcde12345

また、 api_client_tokens.yml で定義したクライアントタイプを routes.ymlclients に指定します。

- from:
    path: ^/sample/(.+)$
    clients:
      - SampleClient

単体での認証は弱い

Don't rely on API keys as your only means of authentication and authorization for your APIs.

Creating and using usage plans with API keys

とあるように、これ単体では強い認証を提供することはありません。しかし、無いよりもあった方がセキュリティは強いです。

また、APIキー本来の目的は、トークンごとにアクセス量を制限することです。今後は、このトークンを利用して、クライアントタイプ毎のスロットリング機能の実装を考えています。

トークンの管理

実際のトークンの値は、YAMLファイルでなく、AWS Secrets Managerで管理しています。これにより、GitHub上に秘匿情報を載せないようにしています。

AWS Secrets Managerは、SREチームのみが管理可能な状態です。管理人数をできるだけ限定することで、可能な限りセキュリティを向上しています。

IP許可レンジ

ip_range_groups.yml という名前のYAMLファイルを用意します。ルーティングの送信元として許可するIP情報を指定します。

以下は具体例です。

SampleIPRange:
  - 127.0.0.1/32

ip_range_groups.yml で定義したIPレンジグループ名を routes.ymlip_range_groups に指定することで、ルーティング毎に許可するAPIクライアントを指定できます。

- from:
    path: ^/sample/(.+)$
    ip_range_groups:
      - SampleIPRange

リトライ

どのようなシステムであっても、なんらかの原因でリクエストが失敗する可能性はあります。例えば、転送先マイクロサービスの一時的なエラー、通信問題、タイムアウトなどです。その失敗をAPIクライアントへそのまま返さずに、API Gatewayとマイクロサービス間でリトライする機能です。最大の試行回数を3に設定していた場合に、1回目と2回目のAPIリクエストに失敗しても、3回目で成功すればAPIクライアントには200 OKが返ります。

リトライ条件

とはいえ、全てのAPIリクエストの失敗をリトライさせると非効率です。例えば、リクエストパラメータに不備がある場合には何度リトライさせたところで失敗になるため、待ち時間やコンピュータリソースの無駄になってしまいます。

そこで、 target_groups.yml でターゲットグループ毎にリトライ条件 retry_cases を設定できるようにしています。

HTTPレスポンスの条件がリトライ条件に一致した場合は、合計の試行回数が max_try_count の値を超えない範囲でリトライする作りにしています。 max_try_count の設定を省略した場合は、 targets で指定したターゲットの数になります。

加えて、 retry_non_idempotent により、冪等でないHTTPメソッド(POST, PATCH)に対してもリトライするかどうかを指定できます。設定を省略した場合は、falseです。

TargetGroupA:
  targets:
    - host: target1.example.com
      port: 8080
    - host: target2.example.com
      port: 8081
  max_try_count: 3
  retry_cases: ["server_error", "timeout"]
  retry_non_idempotent: true

リトライ先

どのターゲットにリトライするかは target_groups.ymlretry_to で指定します。省略した場合は、 target_groups.yml のリストにしたがって次のターゲットにリトライします。最後のターゲットの場合は最初のターゲットになります。

TargetGroupAB:
  targets:
    - host: target-a-1.example.com
      port: 8080
      retry_to: target-b-2.example.com
    - host: target-a-2.example.com
      port: 8080
      retry_to: target-b-1.example.com
    - host: target-b-1.example.com
      port: 8080
      retry_to: target-a-2.example.com
    - host: target-b-2.example.com
      port: 8080
      retry_to: target-a-1.example.com

Exponential Backoff And Jitter

リトライする場合に、全て即時リトライとしてしまうと、リクエストの多重度が高くなってパフォーマンスの劣化に繋がります。

そこで、Exponential Backoff And JitterのFull Jitterというアルゴリズムを採用しています。これは、即時リトライせずに、ランダム性のある待ち時間を経てリトライする方法です。リトライする前に 0 ~ ベースインターバル * 2^試行回数 ミリ秒スリープします。つまり、リトライ回数が増えるたびにスリープ時間は長くなる可能性が高まります。また、スリープの上限(最大インターバル)を設定することもできます。上限のデフォルトはベースインターバルの10倍としています。

target_groups.yml でベースインターバル retry_base_interval と最大インターバル retry_max_interval の指定が可能です。

TargetGroupA:
  targets:
    - host: target1.example.com
      port: 8080
    - host: target2.example.com
      port: 8081
  retry_base_interval: 50 
  retry_max_interval: 500   

実装の話でいうと、このようなスリープ関数を用意して、リトライ直前でこの関数を呼び出しています。

func SleepExponentialBackoffAndJitter(tryCount int, baseInterval time.Duration, maxInterval time.Duration) {
    interval := baseInterval * time.Duration(math.Pow(2, float64(tryCount)))
    if interval > maxInterval {
        interval = maxInterval
    }

    interval = time.Duration(mathRand.Float64() * float64(interval))
    time.Sleep(interval)
}

タイムアウト

API Gatewayのタイムアウトに関しては target_groups.yml で設定します。

TargetGroupA:
  targets:
    - host: target1.example.com
      port: 8080
      connect_timeout: 50
      read_timeout: 3000
      idle_conn_timeout: 90000
    - host: target2.example.com
      port: 8081
      connect_timeout: 40
      read_timeout: 2000
      idle_conn_timeout: 80000  
  connect_timeout: 50
  read_timeout: 3000
  idle_conn_timeout: 90000
  max_idle_conns_per_host: 2
  • connect_timeout は、1リクエストあたりのTCPコネクション確立までの間のタイムアウト値(ミリ秒単位)です。
  • read_timeout は、1リクエストあたりのリクエスト開始からレスポンスボディを読み込み終わるまでの間のタイムアウト値(ミリ秒単位)です。
  • idle_conn_timeout は、データが送受信されなかった場合にコネクションを維持する時間(ミリ秒単位)です。指定された時間内にデータが送受信されなかった場合、コネクションを閉じます。
  • max_idle_conns_per_host は、1ホストあたりに保持するアイドル状態のコネクションの最大数です。

Goのnet/httpのClientを生成する時に、これらの値を利用して設定します。

また、これらの値は、 ターゲットの設定>ターゲットグループの設定>デフォルト設定 の順で優先付けされています。

メンバー認証

メンバー認証は、以下の図の流れのような、ID基盤が発行したIDトークンを利用したBearer認証です。

api_gateway_auth

セキュリティ面の考慮から、本記事では詳細については割愛します。

トレースIDの付与

分散トレーシングを実現するために、API GatewayではトレースIDを発行し、リクエストヘッダに付与しています。

分散トレーシングとは、その名の通り、分散システムにおけるリクエストを追跡することです。マイクロサービスのように複数のサービスで構成される場合に、APIリクエストが複数のマイクロサービスにまたがると、トレーサビリティーの低下が懸念されます。分散トレーシングは、障害や遅延が発生した際に、どこに原因があるのかを速く正確に確認するのに役立ちます。

開発で工夫したこと

コンフィグファイルのスキーマ検証と仕様書作成

以下の4項目をYAMLファイルから設定できるようにしています。API Gatewayの起動時には、これらの設定が必要です。

  • ターゲットとターゲットグループ
  • ルーティング
  • IP許可レンジ
  • APIクライアントトークン

各YAMLファイルのスキーマ検証や仕様書作成は自動化されています。別の記事で詳細をまとめていますので、よろしければご覧ください。

リクエスト中断時の処理

特に、ネイティブアプリからのリクエストに関しては、ネットワークトラブルなどによる予期せぬリクエスト中断が起こり得ます。

もし、API Gatewayの処理中にクライアント側からの接続が切れた場合は、HTTPのステータスコードが460のエラーを返すように実装しています。なぜ460かというと、ALBの仕様に合わせているためです。

当然ながら、このエラー自体はクライアントまで返ることはないので、事実上ログ用途になっています。

テスト

開発用パラメータの導入

リクエストの時刻を改変して、動作を検証するために開発用パラメータを用意しました。RFC 3339の形式でヘッダに格納して使用します。例えば、IDトークンの有効期限切れの時刻を待たずに有効期限切れの動作を検証する目的として使用されます。

アプリケーションコード側では、Goのtime.Nowを都度呼び出す代わりに、引数で渡されるcontextを活用してリクエスト時刻を扱っています。

開発用パラメータが指定された場合のみ、リクエスト時刻は任意の時刻に上書きされます。本番環境やステージング環境でこのヘッダが格納された場合は、無視されます。

ローカル動作検証でのマイクロサービスのモック

Prism を使用して、マイクロサービスのモックを動作させています。

ローカルでAPI Gatewayの動作検証をするのに便利です。

例えば、下記のようなOpenAPIのYAMLファイルを用意します。

openapi: "3.0.0"
info:
  version: 0.0.1
  title: search service mock
servers:
  - url: http://localhost:4011
    description: local api mock server.
paths:
  /api/v1/goods:
    get:
      responses:
        200:
          description: return some response.
          content: 
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: success

docker-compose.yml では volumes で上記のYAMLファイルを指定して、 mock コマンドを指定します。

services:
  search:
    image: stoplight/prism:3
    command: mock -h 0.0.0.0 /search-mock.yml
    volumes:
      - ./search-mock.yml:/search-mock.yml

テストコード中のマイクロサービスのモック

テストコード内におけるマイクロサービスのモックにはnginxを利用しています。

シンプルなモック

test.conf にモックの定義をします。例えば、 search:4010/api/v1/goods にリクエストが来たら200を返します。

server {
    listen 4010;
    server_name search;
    location = /api/v1/goods {
        add_header Content-Type application/json;
        return 200 '{"status":"success"}';
    }
}

docker-compose.test.ymltest.conf をnginxコンテナの /etc/nginx/conf.d/default.conf に配置します。

services:
  mock:
    image: nginx:mainline-alpine
    volumes:
      - ./docker/nginx/nginx.conf:/etc/nginx/nginx.conf
      - ./docker/nginx/test.conf:/etc/nginx/conf.d/default.conf
    networks:
      backend:
        aliases:
          - search
networks:
  backend:

Goのテストコード側ではこのような target_groups.yml を定義します。

test:
  targets:
    - host: search
      port: 4010

上記の通り、以下を一致させる必要があります。

  • test.confserver_name の値
  • docker-compose.test.ymlaliases の値
  • target_groups.yml のホスト(ターゲットID)

以上より、マイクロサービスのレスポンスをモック化して、テストコードを実装しています。

タイムアウトのモック

さらに、njsを利用して、スリープ処理するJavaScriptファイルを配置し、マイクロサービスへのリクエストタイムアウトに関する異常系テストをしています。njsは、nginxの内部で使用可能なJavaScriptのサブセットです。

下記のような sleep.js を用意しています。

function sleep(r) {
  setTimeout(function() {
    r.return(200, "");
  }, 10000)
}

export default {sleep};

下記の test.conf では js_import して、slow:80/ にリクエストがきたら sleep を実行するにしています。

js_import js/sleep.js;

server {
    listen 80;
    server_name slow;
    location / {
        js_content sleep.sleep;
    }
}

docker-compose.test.yml はこちらです。volumessleep.js を指定しています。 aliasesslow を指定しています。

services:
  mock:
    image: nginx:mainline-alpine
    volumes:
      - ./docker/nginx/nginx.conf:/etc/nginx/nginx.conf
      - ./docker/nginx/test.conf:/etc/nginx/conf.d/default.conf
      - ./docker/nginx/sleep.js:/etc/nginx/js/sleep.js
    networks:
      backend:
        aliases:
          - slow
networks:
  backend:

分析・監視

API Gatewayの分析と監視について、ツール毎に説明します。

Athena

ログの分析にはAthenaを使用しています。

ALBとアプリケーションのログはS3に保管しており、そのデータをクエリでSQL検索できます。例えば、マイクロサービスで付与されているトレースIDを検索条件に、エラー状況を確認できます。

Athenaはクエリでスキャンされるデータ量によって課金されます(2020年12月現在の東京リージョンで1GBあたり約0.5円)。したがって、必ずクエリのWHERE句で日付などを指定するように、社内でルール化されています。念の為、各Workgroupには「スキャンできるデータ量の上限」が設定されています。

CloudWatch Alarm

通知条件の判定にはCloudWatch Alarmを使用しています。監視対象のメトリクスが閾値を超した場合に、SlackやPagerDutyで通知します。

Datadog APM

分散トレーシングの監視ツールには、Datadog APMを使用しています。

APMはApplication Performance Monitoringの略称です。従来のアプリケーション監視では、プロセス監視や外形監視などが一般的でしたが、APMではパフォーマンスも監視対象にしています。

API Gatewayだけでなく、Datadog APM側でもトレースIDを発行しています。Datadog APMで発行したトレースIDにより、下図のように、コンソール上でそれぞれの処理が紐づいて表示されます。コンソールではAPI Gatewayとマイクロサービスのレイテンシーやエラー情報、インフラ情報、メトリクスなどを確認できます。加えて、SQLはクエリ単位までドリルダウンして確認できます。

datadog apm

スパンの開始

スパンは、計測単位です。スパン同士は相互にネストできるため、親子関係にできます。スパンを設定することで、ドリルダウンでの可視化を可能にしています。

リバースプロキシ処理内において、マイクロサービスへHTTPリクエストする直前にスパンを開始しています。

opts := []ddtrace.StartSpanOption{
    tracer.SpanType(ext.SpanTypeHTTP),
    tracer.ResourceName("url"),
    tracer.Tag(ext.HTTPMethod, "method"),
    tracer.Tag(ext.HTTPURL, "url"),
}
span, _ := tracer.StartSpanFromContext(r.Context(), "transfer-request", opts...)
defer func() {
    span.Finish(tracer.WithError(e))
}()

スパンタグの付与

スパンタグは、スパンに付与するkey-value形式のタグです。以下のスパンタグを設定しています。

  • トレースID
  • クライアントタイプ
  • マイクロサービスのHTTPレスポンスコード

例えば、転送したマイクロサービスからのHTTPステータスを、スパンタグにセットする実装は次の通りです。

span.SetTag(ext.HTTPCode, response.StatusCode)

Sentry

アプリケーションのエラー監視にはSentryを使っています。

エラー情報だけでなくクライアント情報も詳しく表示され、Slackと連携したエラー通知も可能です。

Sentryへのエラー情報送信

このようなSentryにエラー情報を送信する関数を用意しています。

func SendSentry(r *http.Request, e error) {
    hub := sentry.CurrentHub()
    if sentry.HasHubOnContext(r.Context()) {
        hub = sentry.GetHubFromContext(r.Context())
    }
    hub.CaptureException(e)
}

エラー処理ではこの関数を呼び出しています。

if e != nil {
    lib.SendSentry(r, e)
}

秘匿情報を取り除く

ヘッダやボディなどのエラー送信内容には、トークンなどの秘匿情報が含まれます。Sentry側で、それらの秘匿情報を取り除けます。

Using before-send in the SDKs to scrub any data before it is sent is the recommended scrubbing approach

Scrubbing Sensitive Data for Go | Sentry Documentation

しかしながら、上記の通り、秘匿情報はSentryへの送信時点でアプリケーション側にて取り除いておくことが推奨されています。秘匿情報が送信前に取り除かれていることが管理画面上でわかるように、 XXXXX の値でマスクしています。

PagerDuty

オンコールシステムにはPagerDutyを使用しています。Slack通知しているものの中でより緊急度が高いものに関しては、PagerDutyからコールが来ます。

今のところサービスの監視担当は週次の交代制になっていて、SREチームとバックエンドチームから1人ずつ当番が割り当てられています。

API Gatewayの現状とこれから

現状、ある程度の機能を有したAPI Gatewayをリリースしています。これにより、ストラングラーパターンで進める準備(土台)ができました。

現在は、一日に約数億回のAPIリクエストがAPI Gatewayを経由しています。しかしながら、まだZOZOTOWNのAPIが全てAPI Gateway経由に置き換わったわけではありません。今後、さらにAPI Gatewayを通るリクエストが増えていくでしょう。例えば、ZOZOSUITZOZOMATなどの計測サービス、ZOZOUSEDFulfillment by ZOZOのAPIなどです。

ただし、マイクロサービス間のAPIリクエストに関しては、サービスメッシュ(Envoy)の導入も検討しています。理由は、「API Gatewayの負荷軽減」とAPIクライアント毎に配布している「クライアントトークン管理の手間を削減する」ためです。

We are hiring

ZOZOTOWNのマイクロサービス化はまだ始まったばかりです。今後は、API GatewayやID基盤の追加開発に加えて、新たなマイクロサービスの開発も目白押しです。そのためのエンジニアが足りていない状況です。

ご興味のある方は、以下のリンクからぜひご応募ください。お待ちしております。

hrmos.co

カテゴリー