メインコンテンツへスキップ
見出し画像

バックエンドエンジニアからプラットフォームエンジニアへの転換 - 視点の変化

    IVRy(アイブリー)のエンジニアの kshimadutsu です。

    この記事は 株式会社IVRy 白組 Advent Calendar 2024 の15日目の記事です。
    白組のAdvent Calendarは こちら です
    白組の昨日の記事は Saori Haga さんの『CSの私が社内失業すると思った話 ※良い話です』でした。白組の明日の記事は Yukichi Kawanami ( @ykch_ ) さんの予定です。どちらも合わせてご覧ください。

    紅組もやってます。
    紅組のAdvent Calendarは こちら です
    紅組の本日の記事は Hiroyuki Kuranishi さんの『急成長スタートアップIVRyでオペレーション設計に挑戦する醍醐味』です。

    プラットフォームエンジニアに転向しました

    2024年7月、Railsのバックエンドエンジニアからプラットフォームエンジニアへ転向しました。

    IVRyのプラットフォームエンジニアは主に以下のようなことに関心があり、日々より良いプロダクトを提供するために尽力しています。

    • サービスレベル

    • 開発者体験

    • セキュリティ

    転向を決意したきっかけは、サービス開発を行う中で利用者により一層安定したサービスを提供したいという気持ちが強くなり、アプリケーション開発だけでなくシステム全体の基盤を支えるインフラへの関心が大きく高まったことでした。

    この変化を通じて実感した「バックエンドエンジニア」と「プラットフォームエンジニア」の視点や意識の違いについて書いていきます。

    その前にバックエンドエンジニアとして経験してきたことを簡単にご紹介しておきます。
    2000年からエンジニアとして働いており、Perl, PHP, Railsを主として、Java, Pythonを少し触っていた程度。オンプレ時代にはミドルウェアのチューニングやログの調査などを行っており、AWSでWebサービスを提供するための最低限の知識を持っていました。
    インフラの知識が全くないわけではないものの、転向後に感じた視点やアプローチの違いということとなります。

    視点の違い

    バックエンドエンジニアとしては、主に機能要件に焦点を当て、機能の開発やテストを通じて新しい機能を提供することが中心でした。

    一方で、プラットフォームエンジニアに転向してからは、システム全体への影響やスケーラビリティ、パフォーマンスといった非機能要件に視点が移るようになりました。

    プラットフォームエンジニアとしてサービスを支える基盤が安定し、高負荷にも耐えられる設計になっているか、さらには障害発生時にも迅速に対応できる体制が整っているかといった、システムの「信頼性」を維持する責任が加わりました。

    この責任は非常に重く感じますが、システム全体を俯瞰し長期的な視点で運用と開発の両面から価値を提供するというやりがいも多く感じました。

    アプローチの違い

    バックエンドエンジニアの場合は、主にテストコードの作成や動作確認を通じて、機能が正しく動作することを確認するアプローチが中心になります。これにより、ユーザーが使う機能が期待どおりに動作し、機能・性能を担保します。

    一方で、プラットフォームエンジニアの場合は、システム全体の「安定性」を求めます。具体的には、エラー率やレイテンシといった定量的な指標に基づいてシステムの状態を評価します。さらに、それらの指標を常時モニタリングする仕組みを構築し、異常を早期に検知して対応できる環境を整えるようなアプローチを行います。
    IVRyではDatadogを活用してこれらの指標を観測し、リアルタイムで問題を把握できる環境を構築しています。

    大きな違いとして感じたのは、エラー率やレイテンシといった定量的な指標を通じて、システムの状態を「可視化」し、制御可能な形に変える点でした。
    特に印象深かったのは、秋頃に定義された CUJ(クリティカルユーザージャーニー) です。CUJは、ビジネス上重要とされるカスタマージャーニーにフォーカスし、サービスの根幹を明らかにするフレームワークです。この取り組みにより、最も守るべきシステムやプロセスが明確化され、エラーの影響が及ぶ範囲や優先的に対処すべきポイントをより深く理解できるようになりました。
    これらの体験は、システム全体の信頼性を維持するだけでなく、サービス提供の視点を技術からビジネスへと広げるきっかけにもなりました。

    通話サービスにおける特有の課題

    IVRyのサービスはクライアントのネットワーク状況だけでなくTwilioとAWS上に構築された弊社サービス間のネットワーク状況の影響を受けます。
    日に10万着信があるIVRyではごく稀に Twilio と IVRy(AWS) の間でエラーが発生することがあります。特にネットワークエラーの調査はログが十分に残っていないことも多く、Twilio側の環境や設定にも依存するため解決が難しい課題の一つです。

    ここでは2024年夏頃に発生したネットワークエラーへの対応例をご紹介します。
    課題発生と初期対応
    2024年夏頃、突然ネットワークエラーが数件発生しました。この種のエラーは以前から稀に発生しており短時間のうちに収束したため、その時はALBのアクセスログを確認し弊社サービスまでリクエストが届いていないことを確認し静観することになりました。 しかし翌週に再び同様のエラーが発生したため、問題を見過ごすことができず、本格的な調査と改善対応を開始しました。

    調査のアプローチ
    調査を進める中でTwilio側のサポート担当者とやりとりし、Twilio側の一部のエラーログに「TLS接続エラー」が記録されていることが分かりました。ハンドシェイクの失敗が示唆されており、接続が途中で遮断された可能性が考えられました。
    IVRy側のAWS環境でALBのログをDuckDB(※)を利用して調査しましたが、通常のログには接続エラーの記録は見つかりませんでした。さらに接続ログの調査を行いましたが、こちらにもエラーのログはなくネットワーク間の問題である可能性が高まりました。
    これらの調査結果をTwilioへ共有したところ、接続エラー発生時の一部で正しくリトライできていなかったことが判明し、リトライ時の挙動を改善してもらうことができました。

    改善のアプローチ
    ネットワーク通信においてエラーが発生することは避けられませんが、一般的にはリトライを行うことで緩和することができます。
    今回の改善ではリトライ設定を見直し、以下の要素を調整しました。

    • タイムアウト時間

    • リトライ回数

    • エッジロケーション

    このチューニングによりネットワークエラーの発生頻度を大幅に削減しエラーの半数以上を改善することができました。

    DuckDBについて

    話が逸れますが、現在IVRyのSREチーム内ではめちゃくちゃDuckdbが流行っています。

    DuckDBは軽量で使いやすいデータベースエンジンで、S3に保存されたALBのログを直接インポートすることが可能です。

    DuckDBは保存されたデータをインメモリに保持しプロセス終了時には破棄されるモードと、永続化モードがあります。そのため調査時に必要な分だけテーブルに保存してクエリすることができます。

    実際に利用した例がこちらになります。
    ※ duckdbのインストール方法、データベースへの接続方法は公式サイトを参照ください

    duckdbにAWSの認証情報保存
    ※ AWSの認証情報が環境変数やAWS 設定ファイルに設定されている前提です。

    INSTALL aws;
    LOAD aws;
    INSTALL httpfs;
    LOAD httpfs;
    CREATE SECRET (
        TYPE S3,
        PROVIDER CREDENTIAL_CHAIN
    );

    ALBの接続ログのインポート

    CREATE TABLE elb_connection_log_202412 AS
      SELECT *
      FROM read_csv(
          's3://[YOUR_S3_BUCKET_NAME]/[CUSTOM_PATH]/AWSLogs/[YOUR_ACCOUNT_ID]/elasticloadbalancing/[YOUR_REGION]/2024/12/15/*.log.gz',                                         
          columns={
              'timestamp': 'TIMESTAMP',
              'client_ip': 'VARCHAR',
              'client_port': 'INTEGER',
              'listener_port': 'INTEGER',
              'tls_protocol': 'VARCHAR',
              'tls_cipher': 'VARCHAR',
              'tls_handshake_latency': 'VARCHAR',
              'leaf_client_cert_subject': 'VARCHAR',
              'leaf_client_cert_validity': 'VARCHAR',
              'leaf_client_cert_serial_number': 'VARCHAR',
              'tls_verify_status': 'VARCHAR',
              'conn_trace_id': 'VARCHAR'
          },
          delim=' ',
          quote='"',
          escape='"',
          header=False,
          auto_detect=False
      );

    実際のクエリ例
    2024-12-03 01:00:00 〜 2024-12-03 01:10:00でTLSの接続が失敗したログをカラムを指定して10件抽出する条件

    SELECT
        timestamp, listener_port, tls_protocol, tls_cipher, tls_verify_status 
      FROM elb_connection_log_202412 
     WHERE tls_verify_status <> 'Success' 
       AND timestamp BETWEEN '2024-12-03 01:00:00' AND '2024-12-03 02:00:00' 
     ORDER BY timestamp 
     LIMIT 10;
    ┌────────────────────────────┬───────────────┬──────────────┬────────────────────────┬────────────────────────────────┐
    │         timestamp          │ listener_port │ tls_protocol │       tls_cipher       │       tls_verify_status        │
    │         timestamp          │     int32     │   varchar    │        varchar         │            varchar             │
    ├────────────────────────────┼───────────────┼──────────────┼────────────────────────┼────────────────────────────────┤
    │ 2024-12-03 01:00:05.533556 │           443 │ TLSv1.3      │ TLS_AES_128_GCM_SHA256 │ Failed:UnmappedConnectionError │
    │ 2024-12-03 01:00:34.153787 │           443 │ TLSv1.3      │ TLS_AES_128_GCM_SHA256 │ Failed:UnmappedConnectionError │
    │ 2024-12-03 01:01:16.348182 │           443 │ -            │ -                      │ -                              │
    │ 2024-12-03 01:01:17.597819 │           443 │ -            │ -                      │ -                              │
    │ 2024-12-03 01:02:08.12231  │           443 │ TLSv1.3      │ TLS_AES_128_GCM_SHA256 │ Failed:UnmappedConnectionError │
    │ 2024-12-03 01:02:22.555805 │           443 │ TLSv1.3      │ TLS_AES_128_GCM_SHA256 │ Failed:UnmappedConnectionError │
    │ 2024-12-03 01:03:10.967179 │           443 │ TLSv1.3      │ TLS_AES_128_GCM_SHA256 │ Failed:UnmappedConnectionError │
    │ 2024-12-03 01:03:35.350146 │           443 │ TLSv1.3      │ TLS_AES_128_GCM_SHA256 │ Failed:UnmappedConnectionError │
    │ 2024-12-03 01:04:08.661571 │           443 │ -            │ -                      │ -                              │
    │ 2024-12-03 01:04:09.900815 │           443 │ -            │ -                      │ -                              │
    ├────────────────────────────┴───────────────┴──────────────┴────────────────────────┴────────────────────────────────┤
    │ 10 rows                                                                                                   5 columns │
    └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

    Cloudwatch Log Insightを利用しても似たようなことは可能ですが、エンジニアとしてはSQLを実行できる環境というのは快適です。
    Cloudwatchと比較すると自由度が段違いで、調査がとても捗るのでオススメです。

    今後のチャレンジ

    プラットフォームエンジニアに転向し、IVRyの基盤を支えるプラットフォームエンジニアとして、ネットワークエラーの調査や改善を行い、信頼性向上に取り組んできました。
    しかし課題はまだまだ残っています。引き続きエラー発生時の調査と改善を行っていくとともに、モニタリング体制をさらに強化し、障害の「予測」し「予防」するアプローチを行なっていきたいと思います。

    2025年は、問題を未然に防ぐことを目指し、チーム全体で活用できる「可観測性」の高い環境を作り上げることで、予防的なインフラ運用の文化を醸成していくことを目指します。

    締めのメッセージ

    一緒にIVRyのプラットフォームをよくしていってくれる仲間を募集中です!


     
     
    株式会社IVRyで、バックエンドエンジニアしてます。 Rails好きです。 IVRy好きです。 お酒好きです。

    あなたへのおすすめ