TECH PLAY

PHP

イベント

マガジン

技術ブログ

ウェブサイトのパフォーマンス問題はよくあることですが、根本原因の特定は困難な作業となります。この投稿では、 Server-Timing ヘッダー の潜在能力を引き出すことで、パフォーマンスに関するトラブルシューティングのプロセスをシンプルにする方法を学びます。 Server-Timing ヘッダーは、バックエンドのコンポーネントがユーザーリクエストへのレスポンスにおいて、タイミングメトリクスやパフォーマンスモニタリングに関するインサイトを伝達できるようにします。 ウェブサイトのアクセスでは、画像変換などのコンテンツ最適化やデータベースからの動的なデータ取得を含んだ、複雑なサーバーサイドのプロセスが関与しています。遅いリクエストの原因となるサーバーやプロセスを特定するには、複数のログを突き合わせて分析する必要があり、時間がかかってしまいます。このプロセスをシンプルにすることで迅速に問題を解決できます。具体的には、ユーザー体験の品質シグナルとサーバーサイドのパフォーマンス指標とを直接関連付けて、単一のログ行内にカプセル化することで実現します。この方法は、広範なデータクエリや相関分析が不要であり、パフォーマンス問題を素早く特定し、原因となるサーバーコンポーネントまで追跡することを可能にします。このアプローチの実例として Common Media Client Data(CMCD) が挙げられます。 CMCD は動画ストリーミング業界で最近生まれた革新的な技術で、クライアントおよびサーバー両方のオブサーバビリティデータを単一のリクエストログ行にシームレスに統合するものです。ウェブサイトにおいては、 Server-Timing ヘッダーを実装することで同様の方式を採用できます。サーバーサイドのメトリクスとクライアントサイドで利用可能なメトリクスとを効果的に統合し、特定のリクエスト-レスポンスサイクルのパフォーマンスを包括的に把握するのです。 私たちが提案するソリューションは 2 つのパートで構成されます。第一に、エンドユーザーのレイテンシを測定してパフォーマンス問題を特定すること、第二に、そうした問題が発生した際にサーバーのインサイトに即座にアクセスすることです。 まず前者を取り上げてから、 Server-Timing の実装について掘り下げていきましょう。 パフォーマンス問題の検出 ウェブサイトのパフォーマンスはレイテンシに大きく依存します。レイテンシとは、ユーザーアクション(リンクのクリックやフォームの送信など)とサーバーからのレスポンスとの間の時間遅延を指します。ウェブサイトにおけるレイテンシは、通常 Time to First Byte ( TTFB )、別名 First Byte Latency ( FBL )の形式で測定されます。これは、ウェブサイトのコンテンツがユーザーの画面に描画され始めるまでの速さを測定したもので、 First Contentful Paint ( FCP )や Largest Contentful Paint ( LCP )などの Core Web Vitals シグナルに直接影響します。シームレスなユーザー体験を確保するには、 TTFB を 800 ミリ秒以下に維持することが 推奨されています 。このベンチマークは、遅いリクエストを特定するための有用な閾値として機能します。 Amazon CloudFront のようなサービスを活用することで、静的および 動的コンテンツ 両方の TTFB の改善に役立ちます。 クライアントサイドの視点で TTFB を測定する際は、ユーザーのリクエスト開始時点から、サーバーからのレスポンスの最初のバイト受信時点までの時間を対象範囲とします。この計算には、ネットワーク伝送時間やサーバーサイドでのすべての処理時間が含まれており、ウェブサイトのアーキテクチャに応じて、コンテンツ配信ネットワーク( CDN )の処理、オリジンサーバーの処理、データベースのクエリ、およびその他のリクエスト処理タスクなどが含まれます。サーバーサイドの視点で TTFB を測定する場合は、 サーバーがリクエストを受信してから、レスポンスの最初のバイトをネットワーク層に送出する時点までの時間を対象範囲とします。このとき、ネットワーク転送時間は含まれず、 TTFB はレスポンスを開始する前のサーバーの処理時間を本質的に示します。さらに、リクエストフローの途中にサーバーが位置するシナリオでは、サーバーは二重の役割を果たします。一つはダウンストリームからのリクエストを受信するサーバーとして、もう一つはアップストリームの他のサーバーにリクエストを転送するクライアントとして機能するのです。この動作モデルは、 Amazon CloudFront のような CDN 内のサーバーにおいて一般的であり、そのようなサーバーではクライアントサイドとサーバーサイドの両方の TTFB メトリクスが存在することになります。 CloudFront とエッジ関数、 Application Load Balancer 、ウェブサーバー、データベースなどのコンポーネントを含む典型的なウェブサイトアーキテクチャでは、リクエストからレスポンスまでのサイクルは図 1 に示すように進行します。 図 1. 典型的なウェブサイトアーキテクチャにおけるリクエスト-レスポンスサイクルのタイミング 図 1 では、リクエストとレスポンスの開始と終了のそれぞれのタイムスタンプを T を用いて表しています。これらのタイムスタンプを使用して、様々な TTFB を以下のように計算します: ユーザー TTFB は T1 から T18 までの時間間隔です。ユーザーエクスペリエンスをモニタリングし、推奨値を超えた時の問題特定をするために測定すべき指標です。ユーザー TTFB が短いほど、レスポンスが速く、良いユーザーエクスペリエンスであることを示しています。 CloudFront ダウンストリーム TTFB は T2 から T17 までの時間間隔です。キャッシュヒット、つまり、オリジンでの処理を必要とせず CloudFront キャッシュからリクエストが処理される場合には、 TTFB は CloudFront がリクエストを処理してレスポンスを準備するのにかかった時間のみを示します。エッジ関数を使用するのであれば、その実行時間も含まれます。ただし、キャッシュミスの場合には、オリジンがリクエストを処理してレスポンスを準備するまでにかかった時間と、オリジンから CloudFront へレスポンスを転送する時間が追加されます。 CloudFront アップストリーム TTFB は T3 から T14 までの時間間隔です。これは、CloudFront がリクエストをオリジンに送信し、レスポンスを受信するまでのキャッシュミスの場合を表しています。 CloudFront と同様に、オリジン側のシステム内のすべてのサーバーも独自の TTFB を持っています。たとえば、HTML ページを生成するためにデータベースクエリを実行する場合に、T7 から T10 までの時間間隔として、データベース処理時間と伝送時間の両方を測定します。 アップストリームのコンポーネントからダウンストリームへの伝送時間は、ダウンストリーム TTFB からアップストリーム TTFB を引いた値で推定できます。たとえば、CloudFront からユーザーへの最初のバイトの伝送時間は、ユーザー TTFB から CloudFront ダウンストリーム TTFB を引いて計算できます。伝送時間が短いほど、ネットワーク状態が良好で、距離が短いことを示します。 ブラウザ内の JavaScript を使用してユーザー TTFB を測定するには、 Resource Timing API が使用できます。この API では、リクエストの開始時刻、DNS 解決時間、TCP および TLS ハンドシェイク、レスポンスの最初のバイトの受信といったリソースの読み込みに関わるさまざまな段階のタイムスタンプを取得できます。これにより、TTFB の計算やリソースの読み込みに関連するその他の有用なタイミング情報の取得が容易になります。 const timings = {}; new PerformanceObserver((entryList) => { const entries = entryList.getEntries(); entries.forEach(entry => { if (entry.responseStart > 0) { timings.userDNS = (entry.domainLookupEnd - entry.domainLookupStart).toFixed(2); timings.userTCP = (entry.connectEnd - entry.connectStart).toFixed(2); timings.userTLS = (entry.requestStart - entry.secureConnectionStart).toFixed(2); timings.userTTFB = (entry.responseStart - entry.requestStart).toFixed(2); } }); }).observe({ type: 'resource', buffered: true }); このコードスニペットは、ウェブページから読み込まれた各リソースの DNS、TCP、TLS、および TTFB のタイミングを取得しています。同様に、 Navigation Timing API を使用して、ブラウザ内のナビゲーションリクエストに対してこれらのタイミングを取得できます。このデータを使用すると、レイテンシが許容範囲内かどうかを判断できるだけでなく、リクエストの DNS、TCP、TLS 各段階の所要時間を分析することもできます。これらのメトリクスは、パケットが移動することになるユーザーとフロントエンドサーバー間のネットワーク距離や、ネットワークの輻輳状態に影響を受けます。これらの値が大きく、800 ミリ秒のベンチマークに近づいている場合は、よりスムーズなユーザーエクスペリエンスのためにネットワーク状態を改善する必要があることを示しています。 CloudFront は、エッジロケーションでユーザーに近い場所でリクエストを終端することにより、ネットワークパフォーマンスを大幅に向上させることができます。 しかし、サーバーサイドが原因のパフォーマンス問題はこのデータでは可視化できません。そこで Server-Timing が役立ちます。 Server-Timing の実装 あらゆるウェブサーバーでは HTTP レスポンスに Server-Timing ヘッダーを含めることができ、サーバーメトリクスを提供します。このヘッダーはすべてのモダンブラウザでサポートされており、 PerformanceServerTiming インターフェースを使用してメトリクスを簡単に解析および取得できます。 CloudFront はすでに Server-Timing をサポートしており、処理に関連するメトリクスを伝達できます。たとえば、cdn-downstream-fbl メトリクスは前述した CloudFront ダウンストリーム TTFB であり、 cdn-upstream-fbl は CloudFront アップストリーム TTFB です。その他の利用可能なメトリクスとその説明については、 開発者ガイド で確認できます。 CloudFront で Server-Timing を有効化するには、 レスポンスヘッダーポリシー を作成します。 Server-Timing ヘッダーパネルの「有効」オプションを切り替え、サンプリングレートを指定します。他のレスポンスヘッダーの追加または削除も必要に応じて設定します。CloudFront の Server-Timing 機能により、CloudFront がリクエストを十分な速さで処理できているかを評価できます。キャッシュヒットの場合、 cdn-downstream-fbl メトリクスの値は小さくなり、これは CloudFront が迅速にレスポンスを開始したことを示します。逆に、このメトリクスの値が大きい場合は、処理が遅いことを示唆し、CloudFront 側に問題があることを示します。キャッシュミスの場合には、 cdn-upstream-connect と cdn-upstream-dns メトリクスを確認して CloudFront からオリジンへの接続時間の値も評価します。これらのメトリクスの値が小さい場合は、リクエストフローにおける次のサーバー(図 1 に示されている Application Load Balancer など)が正常に稼働しており、接続を素早く確立し、CloudFront のオリジン向けサーバーの近くに配置されていることを示唆しています。たいていの場合、 cdn-upstream-connect や cdn-upstream-dns の値は 0 になります。なぜならば、CloudFront の 持続的接続 機能が以前に確立された接続を再利用しているからです。 cdn-upstream-fbl メトリクスは、オリジンからのレスポンスの最初のバイトが CloudFront に到達する速さを示します。このメトリクスの値が大きく、 cdn-upstream-connect と cdn-upstream-dns の値が小さい場合は、 Application Load Balancer の後段にあるオリジン側のシステムに問題が発生し、速いレスポンスを提供できていないことを示します。理想的には、これらのメトリクスがユーザーの経験するレイテンシ(ユーザー TTFB )に大きく影響を与えてはいけません。 CloudFront の Server-Timing ヘッダーは、 CloudFront のダウンストリーム・アップストリーム両方のパフォーマンスに関するインサイトを提供しますが、リクエスト中にオリジンで何が起こったかを直接教えてはくれません。複数の異なるコンポーネントやテクノロジーで構成される現代のオリジンアーキテクチャの多様性を鑑みれば、包括的な理解のためには、それぞれのパフォーマンスタイミング情報を組み込むことが不可欠です。オリジンサーバーからインサイトを抽出するには、オリジンからの CloudFront へのレスポンスに Server-Timing ヘッダーを独自に実装して含めることができます。 CloudFront がこのヘッダーを置き換えることはありません。代わりに、オリジンから受信した Server-Timing ヘッダーに CloudFront が自身のメトリクスを追加します。独自で実装する Server-Timing ヘッダーに含めるタイミングメトリクスとしては、画像の最適化、 API 呼び出し、データベースのクエリ、エッジコンピューティングなどの重要なバックエンドプロセスの測定値が考えられます。たとえば、 PHP を使用してデータベースクエリを実行している場合、次のようにしてクエリの所要時間を測定できます。 $dbReadStartTime = hrtime(true); // Database query goes here $dbReadEndTime = hrtime(true); $dbReadTotalTime = ($dbReadEndTime - $dbReadStartTime) / 1000000; header('Server-Timing: my-query;dur=' . $dbReadTotalTime); こちらのコードスニペットでは、データベース操作の完了にかかった時間を取得し、 Server-Timing ヘッダー内で my-query メトリクスとして伝達しています。データベースは時に過負荷状態になり、パフォーマンスのボトルネックとなることがあるため、このデータはそのようなシナリオを明らかにするのに役立ちます。 Node.js を使用している場合は、 PerformanceServerTiming インターフェース仕様の 例 を参考にして、 Server-Timing ヘッダーを実装してください。 エッジ関数 によって追加になるレイテンシも Node.js を使用している場合と同様の実装で測定を行います。ネットワーク呼び出しを含んだ複雑な処理を行う Lambda@Edge 関数では特に有益です。以下の例では、オリジンレスポンスイベントにアタッチされた Lambda@Edge 関数で Server-Timing ヘッダーの実装をしています: import json import time # CF headers are available in request object for Lambda@Edge functions attached to origin response event only def lambda_handler(event, context): # Get function's start timestamp handler_start_time = time.time() response = event['Records'][0]['cf']['response'] request = event['Records'][0]['cf']['request'] server_timing_value = [] # List of CloudFront headers to include in server timing for additional inisghts cf_headers = ['cloudfront-viewer-country', 'cloudfront-viewer-city', 'cloudfront-viewer-asn'] # Iterate over each header name and construct the value for the Server-Timing header for header_name in cf_headers: if header_name in request['headers']: header_value = request['headers'][header_name][0]['value'] server_timing_value.append('{}; desc="{}"'.format(header_name, header_value)) # Function's logic goes here # Get function's stop timestamp handler_stop_time = time.time() handler_duration = round((handler_stop_time - handler_start_time) * 1000, 2) server_timing_value.append('{}; dur={}'.format("my-function", handler_duration)) if server_timing_value: # Construct the Server-Timing header server_timing = [{ "key": "Server-Timing", "value": ', '.join(server_timing_value) }] # Add or append the Server-Timing header if 'server-timing' in response['headers']: response['headers']['server-timing'][0]['value'] += ', ' + ', '.join(server_timing_value) else: response['headers']['server-timing'] = server_timing print("Server-Timing:", response['headers']['server-timing']) return response 注目すべき点として、このコードで追加されるメトリクスは、ハンドラーコードの実行時間のみであり、その他の Lambda のタイミングは除外されています。なお、オリジンアーキテクチャの情報が悪意のある攻撃者に悪用されるおそれがあるため、メトリクスには意図的に抽象的な名前を使用しています。 また、ユーザーの地理的位置情報と ASN 番号に関するインサイトを Server-Timing ヘッダーに追加したことにも注目すべき点です。 そして、 クライアントサイドでも Server Timing を取得できるように serverTiming プロパティを使用してコードを拡張します。以下が修正されたコードスニペットとなります。 // Creating a new PerformanceObserver to monitor performance entries new PerformanceObserver((entryList) => { const entries = entryList.getEntries(); for (const entry of entries) { // Object to store timings for various stages const timings = { userDNS: null, // User DNS resolution time userTCP: null, // User TCP handshake time userTLS: null, // User TLS handshake time CFDNS: null, // CDN DNS resolution time CFUpstreamHandshake: null, // CDN upstream TCP handshake time MyQuery: null, // Query time CFUpstreamTTFB: null, // CDN upstream Time To First Byte (TTFB) MyFunction: null, // Function execution time CFDownstreamTTFB: null, // CDN downstream TTFB userTTFB: null, // User Time To First Byte (TTFB) CFRID: null, // CDN Request ID CFCacheStatus: null, // CDN Cache status (Hit or Miss) UserASN: null // User Autonomous System Number (ASN) }; // Iterating through server timing entries for the current performance entry entry.serverTiming.forEach((serverEntry) => { switch (serverEntry.name) { case 'cdn-rid': timings.CFRID = serverEntry.description; break; case 'cdn-cache-miss': timings.CFCacheStatus = "Miss"; break; case 'cdn-cache-hit': timings.CFCacheStatus = "Hit"; break; case 'cdn-upstream-connect': timings.CFUpstreamHandshake = serverEntry.duration; break; case 'cdn-downstream-fbl': timings.CFDownstreamTTFB = serverEntry.duration; break; case 'cdn-upstream-dns': timings.CFDNS = serverEntry.duration; break; case 'cdn-upstream-fbl': timings.CFUpstreamTTFB = serverEntry.duration; break; case 'my-query': timings.MyQuery = serverEntry.duration; break; case 'my-function': timings.MyFunction = serverEntry.duration; break; case 'cloudfront-viewer-asn': timings.UserASN = serverEntry.description; break; } }); // Calculating user-specific timings if the response not served from the local cache if (entry.responseStart > 0) { timings.userDNS = (entry.domainLookupEnd - entry.domainLookupStart).toFixed(2); timings.userTCP = (entry.connectEnd - entry.connectStart).toFixed(2); timings.userTLS = (entry.requestStart - entry.secureConnectionStart).toFixed(2); timings.userTTFB = (entry.responseStart - entry.requestStart).toFixed(2); // Logging metrics for the current entry console.log("Metrics for:", entry.name); console.log("userDNS:", timings.userDNS); console.log("userTCP:", timings.userTCP); console.log("userTLS:", timings.userTLS); console.log("CFDNS:", timings.CFDNS); console.log("CFUpstreamHandshake:", timings.CFUpstreamHandshake); console.log("DBQuery:", timings.MyQuery); console.log("CFUpstreamTTFB:", timings.CFUpstreamTTFB); console.log("lambdaEdge:", timings.MyFunction); console.log("CFDownstreamTTFB:", timings.CFDownstreamTTFB); console.log("userTTFB:", timings.userTTFB); console.log("CFRID:", timings.CFRID); console.log("CFCacheStatus:", timings.CFCacheStatus); console.log("UserASN:", timings.UserASN); console.log("------------------------------------------------------"); } } }).observe({ type: 'resource', // Observing resource-related performance entries buffered: true }); この改良されたコードスニペットでは、Server Timing を取得したのちに、クライアントメトリクスと共に timings オブジェクトに統合しています。これにより、クライアントサイドとサーバーサイドの両方のリクエスト – レスポンスサイクルにおける包括的なパフォーマンスのインサイトを一箇所にまとめることができました。以下は console.log の出力例です。 Metrics for: https://d1234.cloudfront.net/script.php userDNS: 0.00 userTCP: 0.00 userTLS: 5.00 CFDNS: 0 CFUpstreamHandshake: 88 DBQuery: 0.538685 CFUpstreamTTFB: 178 lambdaEdge: 0.09 CFDownstreamTTFB: 229 userTTFB: 233.10 CFRID: mRq-Uvr__3OBDo0IX9ELV5Lrk3lF-bOp4eOIqTEXlFkFn0wIWPKgpA== CFCacheStatus: Miss UserASN: 1257 この例を見てみましょう。 CloudFront の Lambda@Edge 関数の実行と、オリジンサーバーのデータベースクエリの両方を合わせて、最初のバイトをネットワークに送信するまでに 229 ミリ秒かかりました( CFDownstreamTTFB )。最初のバイトは 233 ミリ秒後にクライアントデバイスに到達しているので( userTTFB )、伝送時間は 4 ミリ秒であることを示しています。クライアントデバイスは、以前に確立された TCP および TLS 接続を再利用しており( userTCP 、 userTLS )、 CloudFront の IP アドレスをキャッシュしていました( userDNS )。 CloudFront はオリジンに向けて新しい TCP 接続を確立する際( CFUpstreamHandshake )、 88 ミリ秒かかりました。オリジンはリクエストを 90 ミリ秒以内( CFUpstreamTTFB – CFUpstreamHandshake )で処理しており、素早くレスポンスの最初のバイトを返していることがわかります。結論として、エンドユーザーの全体的なレイテンシは推奨値の 800 ミリ秒を下回っており、満足のいくものであると言えます。 Server-Timing を他のデータで拡充する Server-Timing ヘッダーは、サーバーサイドの処理時間を伝達するために設計されたものですが、その構文は単にその用途に限定されたものではありません。たとえば、CloudFront ではキャッシュのステータスや内部のユニークなリクエスト ID をメトリクスに含んでいます。これらのデータは CloudFront の処理を正確に分析するために不可欠なものです。同様にして、リクエストの経路に関してインサイトを提供するメトリクスを加えて、 Server-Timing ヘッダーを独自に拡充することができます。たとえば、ログを見つけやすくするために、クラスター内のサーバーの内部 ID を追加することもできます。ユーザーの地理的位置情報やデバイスタイプも追加はできますが、 CloudFront のヘッダー の使用で実現できます。先述の Lambda@Edge 関数で使用方法を示した通りです。これらのヘッダーは、オリジンレスポンスイベントに関連付けられた Lambda@Edge 関数、もしくは、ビューワーレスポンスイベントやビューワーリクエストイベントに関連付けられた CloudFront 関数のリクエストオブジェクトで利用できます。オリジンリクエストポリシーでこれらのヘッダーを有効化すると、 オリジンウェブサーバーが CloudFront からのリクエストに含まれるこれらのヘッダーを取り扱えるようになります。こうして、拡充されたメトリクスを Server-Timing ヘッダーに統合するのです。 Amazon CloudWatch で結果を分析する Server-Timing ヘッダーは、パフォーマンス問題を特定し、根本原因を突き止めるのに有用ですが、ウェブサイトパフォーマンスの他の重要な側面に関するインサイトは提供しません。たとえば、JavaScript 実行に関連するエラーや、累積レイアウトシフト( Cumulative Layout Shift )などの特定の Web Vitals メトリクスは、このソリューションでは直接キャプチャされません。もしすでにリアルユーザーモニタリング( RUM ) ベースのウェブサイトモニタリングソリューションを利用しているのであれば、 Server-Timing ヘッダーを統合することで、 既存の手法を置き換えたり、パフォーマンスモニタリングを Server-Timing のみに限定したりするのではなく、既存の手法を補完できます。包括的なウェブサイトモニタリングソリューションの一例として Amazon CloudWatch RUM があります。 CloudWatch RUM のインサイトを前述の手法で拡張するには、 Server-Timing ヘッダーから抽出したメトリクスを取得する カスタムイベント を作成し、 CloudWatch RUM クライアント経由で CloudWatch に送信します。このアプローチにより、すべての CloudWatch RUM のインサイトと Server-Timing を同じサービス内に統合し、両方のデータセットをシームレスに分析できるようになります。 前述のコードスニペットについて、 Server-Timing ヘッダーから抽出したデータとクライアントサイドの測定値を使用して、CloudWatch RUM クライアントを介してカスタムイベントを記録する方法の例を以下に示します: // Sending performance data to a remote server cwr('recordEvent', { type: 'my-server-timing', data: { current_url: entry.name, ...timings // Spread operator to include all timings } }); この例では、 timings オブジェクトのすべてのプロパティと値を、 cwr 関数 に送信される data オブジェクトに含めています。これは、特定のエントリに対してキャプチャされたすべてのタイミングが、 current_url と共に送信されることを意味します。 カスタムイベントは CloudWatch Logs に記録され、CloudWatch Logs Insights を使用してクエリを実行できます。さらに、 メトリクスフィルター を使用して CloudWatch メトリクスを作成して、モニタリング目的のメトリクスアラームを設定することができます。 上記のコードで収集しているタイミングのカスタムメトリクス実装の例を以下に示します: { "event_timestamp": 1710929230000, "event_type": "my-server-timing", "event_id": "9ae82980-4bfb-47f5-8183-b241379e09e1", "event_version": "1.0.0", "log_stream": "2024-03-20T03", "application_id": "c27d1cef-e531-45ad-9bc4-8e03a716c775", "application_version": "1.0.0", "metadata": { "version": "1.0.0", "browserLanguage": "en", "browserName": "Chrome", "browserVersion": "123.0.0.0", "osName": "Mac OS", "osVersion": "10.15.7", "deviceType": "desktop", "platformType": "web", "pageId": "/", "interaction": 0, "title": "TTFB Demo", "domain": "d1234.cloudfront.net", "aws:client": "arw-script", "aws:clientVersion": "1.17.0", "countryCode": "SE", "subdivisionCode": "AB" }, "user_details": { "sessionId": "c9d2514a-8884-4b32-aec0-25203f213f84", "userId": "0f7f2bf3-c9b7-46ab-bc9e-2ff53864ea74" }, "event_details": { "current_url": "https://d1234.cloudfront.net/getmeal.php", "userDNS": "0.00", "userTCP": "0.00", "userTLS": "9.50", "CFDNS": 0, "CFUpstreamHandshake": 90, "MyQuery": 0.517874, "CFUpstreamTTFB": 180, "MyFunction": 0.12, "CFDownstreamTTFB": 233, "userTTFB": "239.30", "CFRID": "ujYncZYVJeIOk6fI7ApFuNt-mJoh8hfL3nZPgAj77z7RdtSzNMTcqQ==", "CFCacheStatus": "Miss", "UserASN": "1257" } } このメトリクスに基づいて、ユーザー TTFB のメトリクス用に以下のフィルターパターンを作成できます: { $.event_details.userTTFB= * && $.event_details.CFCacheStatus= * && $.event_details.UserASN= * && $.metadata.countryCode=*} これにより、国コード、 ASN 番号、 CloudFront キャッシュステータスなどのディメンションを持つユーザー TTFB のメトリクスを作成できます。その後、このメトリクスに対してアラートを作成し、推奨される 800 ミリ秒などの事前定義された静的な閾値を超えた場合に通知を受け取ることができます。また、 CloudWatch 異常検出 を利用することもできます。 最適化とコスト 重要なこととして、Server-Timing ヘッダーがレスポンスサイズを増加させる点を認識してください。これは、 CloudFront のデータ転送アウトに関するコストや、分析システム内でのデータの保存や処理に影響を与える可能性があります。たとえば、前述の Server-Timing ヘッダーの値は約 350 バイトに相当しますが、仮に 100 万リクエストを仮定した場合、追加で 0.325 ギガバイトのデータを転送することになります。ウェブサイトが受信するリクエスト数によっては、これが大きなコストになる場合とならない場合があります。ただし、 Server-Timing に必須情報、特にアクション可能なデータのみを含めることで、このコストを削減できます。たとえば、主にパフォーマンス低下の検出に Server-Timing が必要な場合は、推奨しきい値である 800 ミリ秒を超えるリクエストにのみ追加することを選択できます。さらに、インタラクティブフォームや API 呼び出しなど、ウェブサイト上の重要なリソースの読み込みにのみ適用することで、使用量を最小限に抑えることもできます。これには、クライアントサイドの JavaScript コードで該当するメトリクスに必要なフィルターを実装することで実現できます。 まとめ この投稿では、ウェブサイトパフォーマンスモニタリングにおける TTFB の重要性を探求し、リクエスト-レスポンスサイクルにおいて詳細なインサイトを提供する Server-Timing ヘッダーの活用方法を実証しました。レイテンシの測定とサーバーサイドメトリクス( CloudFront の処理時間、オリジンサーバーのレスポンス時間など)の取得により、ウェブサイトの所有者はパフォーマンス問題の根本原因を特定し、ウェブサイトの最適化に向けた積極的な対策を講じることができます。 本記事は「 How to identify website performance bottlenecks by measuring time to first byte latency and using Server-Timing header 」と題された記事の翻訳となります。 翻訳はプロフェッショナルサービスの 鈴木(隆) が担当しました。
こんにちは、SCSK株式会社の中野です。 本記事ではZabbix 7.0.xからZabbix 7.4.xの最新バージョンへアップグレードする手順について説明します。             新しい機能などをお試しいただきたい方など、最新バージョンへバージョンアップしてみてはいかがでしょうか。 今回の構成は以下とさせていただきます。 OS DISTRIBUTION: Red Hat Enterprise Linux OS VERSION: 9 DATABASE: MySQL WEB SERVER: Apache Zabbix7.4の新機能(抜粋) Zabbix 7.4における、主な主な新機能は以下となります。 ・TimescaleDB、MySQL、PostgreSQL、MariaDBの最大サポートバージョンが定期的に拡張 ・Zabbix agent MSIインストーラーに多数のコマンドラインパラメータオプションを追加 ・RedisプラグインにTLS対応およびTLS起動時検証機能を新規搭載 ・OracleプラグインでTNS名サポートおよび追加ロール対応を実装 ・SNMPv3認証情報の自動リロードとsmart.disk.discoveryアイテムにtypeパラメータ追加 Zabbix 7.0から 7.4へバージョンアップ まずはZabbixプロセスを停止させます。 systemctl stop zabbix-server エージェント、エージェント2もアップグレードする場合は、同様に停止します。 systemctl stop zabbix-agent2 ※バージョンアップ時の想定外の事象によるデータ損失を防ぐため、必要に応じてDBのバックアップを実施ください。 本記事ではZabbix設定ファイル、および関連ファイルのみバックアップを取得します。 mkdir /opt/zabbix-backup/ ※設定ファイル cp /etc/zabbix/zabbix_server.conf /opt/zabbix-backup/ cp /etc/httpd/conf.d/zabbix.conf /opt/zabbix-backup/ ※PHPファイルとZabbixバイナリ設定 cp -R /usr/share/zabbix/ /opt/zabbix-backup/ cp -R /usr/share/zabbix-* /opt/zabbix-backup/ リポジトリパッケージを最新バージョンに更新する。 rpm -Uvh https://repo.zabbix.com/zabbix/7.4/release/rhel/9/noarch/zabbix-release-latest.el9.noarch.rpm Zabbixコンポーネントのアップグレードする。 dnf clean all dnf install zabbix-server-mysql zabbix-web-mysql zabbix-agent2 zabbix-agent2-plugin-* ※Zabbixコンポーネントをアップグレード後にWebフロントエンドをアップグレードする dnf install zabbix-apache-conf systemctl restart httpd Zabbixプロセスを起動させる。 systemctl start zabbix-server systemctl start zabbix-agent2 ログを確認して、「database upgrade fully completed」が出力されていることを確認する。 tail /var/log/zabbix/zabbix_server.log -n 1000 15109:20260202:083721.036 completed 97% of database upgrade 15109:20260202:083721.041 completed 98% of database upgrade 15109:20260202:083721.046 completed 100% of database upgrade 15109:20260202:083721.050 database upgrade fully completed バージョンアップ作業は完了です。 最後に バージョンアップ作業は比較的簡単に実施することはできます。 ただ既存バージョンとの異なる点や、バージョンアップ時の注意点が上記の手順以外で環境ごとに発生する可能性がございます。    そのため、バージョンアップを実施する際は、検証環境等で十分に検証したうえで実施いただければと思います。 以上、最後まで読んでいただき、ありがとうございました!!
はじめに 2025年はAIエージェントによるコーディングがかなり浸透した1年だったのではないでしょうか。Claude Code、GitHub Copilot、Cursor をはじめとするAIエージェントによるコード生成ツールが普及する中、現場のエンジニアは実際にどのような効果を実感し、どのような課題に直面しているのでしょうか。 本レポートでは、2025年12月末に社内のエンジニア・マネージャー約100名を対象に実施したアンケート調査の結果をもとに、 満足度・生産性・コード品質・スキル向上 の4つの観点からAIツールの活用実態を分析します。 主な発見 観点 結果 満足度 約9割が満足、NPS +32と高い推奨意向 生産性 約65%が1日1時間以上を節約、バグ対応・コード理解で特に効果を実感 コード品質 「やや高い」が最多だが、約9割が何らかの修正を実施 スキル 新技術習得は加速する一方、「自力で書く力」への懸念も約4割 経験年数による傾向 若手〜中堅層(1〜5年) :満足度・生産性向上の実感が高い一方、スキル低下への懸念も強い ベテラン層(10年以上) :慎重な評価だが、確立されたスキルの上でAIを活用 以下、各セクションで詳細を見ていきます。 回答者の基本情報 社内エンジニア・マネージャー約100名から回答を得ました。 項目 概要 職種 バックエンド・フロントエンド中心、インフラ・マネジメント・モバイルも多数 経験年数 1〜10年がボリュームゾーン(詳細は下図) 使用言語 TypeScript/JavaScript最多、Ruby・PHP・Pythonが続く 利用ツール GitHub Copilot と Q Developer/Kiro で約8割 利用頻度 約7割が「ほぼ毎日」、週3〜4日含め9割超が日常的に活用 経験年数 使用言語 利用頻度 AIツールへの満足度 AIツールに対する満足度を「全体満足度」「期待との比較」「同僚への推奨度(NPS)」の3軸で調査しました。結果として、 全体的に高い満足度 が確認されましたが、一部に注意すべき声も見られました。 約9割が「満足」と回答 全体満足度では、 合計約90%が満足 と回答しました。「非常に不満」はゼロであり、AIツールが日常業務に定着していることを裏付けています。 期待を上回る効果を実感 導入前の期待と比較して、 約65%が期待以上の効果を実感 しています。「期待通り」を含めると96%以上がポジティブな評価であり、導入判断が正しかったと言えます。 NPS +32:高い推奨意向 同僚への推奨度を問うNPS調査では、平均スコアが 約8.0 、NPSは +32 となりました。これは「良好」とされる水準であり、多くのエンジニアが同僚にも利用を勧めたいと考えています。 推奨の声と懸念の声 推奨する理由(抜粋) 壁打ち相手として最適 AIツールがなかった時代にはもう戻れない 懸念・注意点(抜粋) 使い方を誤ると逆効果。リテラシーが必要 自分で考える時間が減り、成長が滞る可能性 PRが増えてレビュー負荷が高まっている これらの声から、AIツールは 「使いこなせば強力な武器」 である一方、 組織としての運用ガイドラインや教育 が求められていることが読み取れます。 経験年数による回答の傾向 経験年数 平均スコア 推奨者(9-10) 中立者(7-8) 批判者(0-6) 1年未満 8.4 50% 43% 7% 1〜3年 8.2 50% 35% 15% 3〜5年 8.3 45% 45% 10% 5〜10年 8.0 42% 46% 12% 10年以上 6.9 22% 45% 33% 経験年数別に観察しますと、 若手〜中堅層(1〜5年)は特に高い満足度 を示していました。「非常に満足」の割合は38〜43%、NPS平均も8.0〜8.4と高水準でした。学習効率の向上や開発スピードの改善を実感している声が多く見られました。 ベテラン層(10年以上)はやや慎重な評価 となりました。「非常に満足」は22%にとどまり、NPS平均も6.9と他層より低い結果です。自由記述では「AIの出力を見抜く力が必要」「本人ができないことをAIで補う使い方には懐疑的」といった声があり、 AIツールの限界を理解した上での活用 を重視していることがうかがえます。 生産性への影響 AIツールの導入により、エンジニアの日常業務はどのように変化したのでしょうか。時間節約効果、業務品質の変化、そして残された課題について詳しく見ていきます。 1日1〜2時間の節約が最多、「2時間以上」も4人に1人 「AIツールにより1日あたりどの程度の時間を節約できているか」を尋ねたところ、 最も多かったのは「1〜2時間」で約38% でした。さらに 「2時間以上」と回答した人も約24% に達し、AIツールが単なる補助ではなく、業務時間を大幅に短縮する「生産性を向上させるツール」として機能しています。 ただし「ほとんど節約できていない」という回答も存在し、ツールの活用度合いや業務内容によって効果に差があることも示唆されています。 5つの業務指標すべてで改善傾向 導入前後の変化を5段階評価で調査した結果、 すべての項目で「向上」「やや向上」が過半数 を占めました。 ただし「集中状態(フロー)への入りやすさ」は改善効果が限定的 で、「向上」は約45%にとどまり、「やや悪化」も約12%存在します。AIとの対話や提案の確認作業が集中を中断させること、また生成待ち時間に別作業を行うようになったことが要因と考えられます。 課題の中心は「AIの限界」と「検証コスト」 AIツール利用時に感じる課題を複数選択で尋ねたところ、 上位3つは以下の通り でした。 コンテキストの理解が不十分 (約45%) AIの出力を検証する負担が大きい (約40%) セキュリティ面での不安 (約38%) これらの課題は相互に関連しています。AIがプロジェクト固有の文脈を十分に理解できないと、ロジックの誤りや不適切なコードが生成されやすくなります。その結果、開発者はAI出力の検証に多くの時間を割く必要があり、見落としによるセキュリティリスクへの不安にもつながっていると考えられます。 AIが最も力を発揮するのは「バグ対応」と「コード理解」 「生産性向上に最も貢献している作業」を上位3つまで選択してもらった結果、 問題解決(バグ対応)や理解支援 といった「思考を補助する」用途で高く評価されています。エンジニアがAIを「コードを書かせる道具」ではなく、 「一緒に考えるパートナー」 として活用し始めていることを示しています。 コード品質への影響 生産性の向上が確認された一方で、「品質は犠牲になっていないか?」という懸念もあります。本セクションでは、AIツールが生成するコードの品質評価と、導入後の品質指標の変化を詳しく見ていきます。 品質評価は「やや高い」が最多——ただし検証は必須 AIが生成するコードの品質について、 「やや高い」が約50% で最多となりました。一方で「普通」も約40%を占め、 「非常に高い」はわずか約3% にとどまっています。 この評価を裏付けるように、出力の検証・修正状況では 約9割が何らかの修正を加えている と回答。「ほぼそのまま使用」はわずか2%でした。 注目すべきは、 「大幅な修正が必要」がゼロ だった点です。AIの出力は「使い物にならない」レベルではなく、 「手直しすれば十分使える」品質 といえます。エンジニアはAIを「完璧なコードを生成する」ものではなく、 「たたき台を素早く作るツール」 として活用しているようです。 経験年数による傾向:若手ほど「改善」を実感 経験年数別に品質評価を分析すると、 若手〜中堅層(1〜5年)は「やや高い」の割合が高く 、AIツールの品質に対して好意的な評価を示しています。一方、 ベテラン層(10年以上)は「普通」「やや低い」の割合が相対的に高く 、より厳しい目で品質を評価していることがうかがえます。 これは満足度セクションで確認された傾向と一致しており、ベテラン層が 「AIの出力を見抜く力が必要」 と考えていることの表れと考えております。 学習・スキルへの影響 AIツールの活用は、エンジニアのスキル向上にどのような影響を与えているのでしょうか。本セクションでは、スキルへの影響、依存への懸念、そして今後の利用意向について詳しく見ていきます。 新技術の習得速度が最も向上、一方で「自力で書く力」には懸念 スキル向上への影響を4つの観点から調査した結果、 項目によって明暗が分かれる 結果となりました。AIツールが「学習のハードルを下げる」役割を果たす一方で、頼りすぎによる基礎力低下のリスクが懸念されています。 自由記述では以下のような声が見られました。 学習効率向上を実感する声: 他チームメンバーに聞かなくても、壁打ち相手になってくれるので、自身の理解向上やチームの生産性向上に大きく貢献しているため スキル低下への懸念を示す声: 仕事自体は進むが、成長の側面で考えると良くないと感じる部分も多いため スピードや品質は向上するが、深く理解しないままAIで一定のコードや正解らしき情報が出力されるため、自分で深く考えたり調査したりする時間が減り、技術的な成長が滞るため 約4割が依存に「懸念あり」——ただし意見は二分 AIツールへの依存について尋ねたところ、 約45%が何らかの懸念を示しました 。一方で、 「あまり懸念なし」「全く懸念なし」も約39% と拮抗しており、エンジニアの間で意見が分かれています。 自由記述では、懸念の有無にかかわらず 「使い方次第」 という認識が共通して見られました。 利用できるのであれば、使わない理由はないと思っている。ただし、エンジニアとしての今後の成長を考えるのであれば、利用範囲、パターンは考えた方が良いとは思っている また、経験年数による活用の違いを指摘する声もありました。 エンジニア歴が非常に浅い人には鵜呑みにしてほしくないと思っています。理由は書いたコードの説明ができない程、自分の頭を使わないようになってコードに責任を持てない可能性がある為です。歴が長い人であれば生成されたコードが期待通りなのかそうではないのか判断がつくのでお勧めできます 一見万能に見えるが、正しい使い方を知らなければかえって効率が悪くなる。また、手作業の方が早いタスクもある。ただ使うだけで他メンバーに迷惑をかけるような人材には勧めない。リスクやデメリットもしっかりあるものだと理解している人だけに勧めたい 経験年数による傾向:若手ほど「自力で書く力」への悪影響を実感 経験年数 自力で書く力に「悪影響」「やや悪影響」と回答した割合 1年未満 約40% 1〜3年 約50% 3〜5年 約55% 5〜10年 約40% 10年以上 約35% 興味深いのは、 1〜5年目の若手〜中堅層で「自力で書く力への悪影響」を感じる割合が高い 点です。基礎スキルを習得・定着させる時期にAIツールに頼りすぎることへの自覚があると考えられます。 一方、ベテラン層(10年以上)は「悪影響」の割合が相対的に低く、 すでに確立されたスキルの上にAIを活用している ため、悪影響を感じにくい傾向がうかがえます。 継続利用意向は97%——「なくてはならないツール」へ 今後の継続利用意向を尋ねたところ、約97%が継続利用を希望しています。スキルへの懸念がありながらも継続利用を希望する声が圧倒的多数であることから、AIツールは 「課題を認識しつつも手放せない存在」 になっていることがわかります。 組織への要望:「ガイドライン整備」が最優先 AIツールの活用促進のために組織に取り組んでほしいことを尋ねた結果、 上位は以下の通り でした。 順位 取り組み内容 選択率 1位 利用ガイドライン/ベストプラクティスの整備 約55% 2位 セキュリティポリシーの明確化 約40% 3位 効果的な活用事例の共有 約38% 4位 社内勉強会・ナレッジ共有の機会 約32% 5位 ライセンスの追加付与 約30% 6位 新しいツールの試験導入 約28% 「利用ガイドライン/ベストプラクティスの整備」が過半数を超え、最も強く求められている ことがわかります。これは生産性セクションで確認された「社内ルール/ガイドラインとの整合性」への課題意識と一致しています。 自由記述でも、組織としての取り組みを求める声が見られました。 間違いなくAIを使った方がいいが、各個人単位ではなくPJや組織としてのAIを活用する前提での開発フローの共有が欲しい 今後に向けて AIツールの効果を最大化し、リスクを最小化するためには、以下の取り組みが求められます。 重点施策 1. 利用ガイドラインの整備 サイボウズ株式会社の新人研修資料 では、「裏取りは必ず行うこと」と明記し、AI出力を盲信しない姿勢を研修段階からエンジニアに伝えています。 弊社でも、セキュリティポリシーの明確化に加え、AI出力の検証プロセスをガイドラインとして整備していきます。 2. ナレッジ共有の促進 READYFOR株式会社のアンケート では、組織に求めるサポートとして「チーム内での経験・課題共有の場」が67%と高い割合となっています。 弊社のアンケートでも同様の声が見られました 。 また、 株式会社エブリー では、AIツールを用いた開発効率化の勉強会を開催し、知見を共有しています。 弊社でも、効果的な活用事例や失敗事例を共有する場を定期的に設けていければと考えております。 3. スキル育成との両立 本アンケートでは、1〜5年目の若手〜中堅層で「自力で書く力への悪影響」を感じる割合が高い結果となりました。この課題に対し、他社では以下のようなアプローチを取っています。 フリー株式会社 : 新卒研修を「前半6週間はAI禁止」「後半5週間はAI全力活用」の2段階構成とし、基礎スキル習得とAI活用のバランスを設計 クラウドエース株式会社 : AIの出力に対する「なぜこの実装なのか」を問う習慣化を徹底し、AIの言いなりにならない開発者を育成 弊社でも、特に若手層に対する基礎力強化の機会を確保しつつ、AI活用スキルを高める両輪のアプローチを検討していきます。 継続的な改善に向けて 定量的な効果測定 今後はアンケート結果だけでなく、ツールの使い方を定量的にメトリクスをもとに分析していきます。 フリー株式会社のAI駆動開発2025レポート では、トークン消費量やリクエスト数などの定量データを継続的に測定しています。 組織的な推進体制 組織戦略としては、 合同会社DMM.comのAX戦略 や フリー株式会社の組織設計 も参考に、組織全体での利活用を促進する仕組みを整えていきます。 おわりに AIは「完璧なコードを書く」ツールではなく、 「たたき台を素早く作り、一緒に考えるパートナー」 です。この認識のもと、組織として適切な活用体制を整えていきます。 最後までお読みいただき、ありがとうございました。

動画

書籍