Heroku の本番環境用に Java Web アプリを準備する
最終更新日 2023年06月14日(水)
Table of Contents
アプリを本番環境に移行する前に、アプリのセキュリティ、スケーラビリティ、および弾力性を確保することが重要です。このガイドでは、Java Web アプリケーションを Heroku の本番環境で使用できるようにするために必要な、重要な手順の概要を示します。Spring Boot を使用している場合は、本番環境用の Spring Boot アプリの準備に関する記事をご覧ください。
HTTPS の使用を強制する
特別なニーズがない限り、アプリではすべてのリクエストについて HTTPS を使用する必要があります。Heroku ではすべてのアプリに対して HTTPS URL (https://<app-name>-<random-identifier>.herokuapp.com
の形式) が提供されているほか、独自のドメインおよび証明書を追加するためのツールも提供されています。
次のコードで Servlet フィルターをアプリに作成することによって、Heroku 上でアプリを実行するときに HTTPS の使用を強制することができます。
public class HttpsEnforcer implements Filter {
public static final String X_FORWARDED_PROTO = "X-Forwarded-Proto";
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
if (request.getHeader(X_FORWARDED_PROTO) != null) {
if (request.getHeader(X_FORWARDED_PROTO).indexOf("https") != 0) {
String pathInfo = (request.getPathInfo() != null) ? request.getPathInfo() : "";
response.sendRedirect("https://" + request.getServerName() + pathInfo);
return;
}
}
filterChain.doFilter(request, response);
}
@Override
public void destroy() { }
}
この設定はサーブレットコンテナに対し、X-Forwarded-Proto
ヘッダーがある場合は、非暗号化のすべての HTTP リクエストを HTTPS を使用して同じ URL にリダイレクトするよう指示します。Heroku では X-Forwarded-Proto
ヘッダーを設定します。つまり、リクエストは SSL が終了する Heroku ルーター経由でリダイレクトされることを意味します。localhost
環境では、非暗号化の HTTP を引き続き使用できます。
速度制限 API 呼び出し
速度制限は、クライアント IP、ブロックされた IP、地理位置情報あるいはその他の要素に基づきサーバーへのトラフィックを制御するプロセスです。Java 用の速度制限ライブラリとして最も有名なものの 1 つが Bucket4j で、これは別の Servlet フィルターをアプリケーションに作成することによって使用できます。まず、次の依存関係を pom.xml
に追加します。
<dependency>
<groupId>com.github.vladimir-bukhtoyarov</groupId>
<artifactId>bucket4j-core</artifactId>
</dependency>
次のコードで Servlet フィルターを作成します。
public class ThrottlingFilter implements javax.servlet.Filter {
private Bucket createNewBucket() {
long overdraft = 50;
Refill refill = Refill.greedy(10, Duration.ofSeconds(1));
Bandwidth limit = Bandwidth.classic(overdraft, refill);
return Bucket4j.builder().addLimit(limit).build();
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
HttpSession session = httpRequest.getSession(true);
String appKey = SecurityUtils.getThirdPartyAppKey();
Bucket bucket = (Bucket) session.getAttribute("throttler-" + appKey);
if (bucket == null) {
Bucket bucket = createNewBucket();
session.setAttribute("throttler-" + appKey, bucket);
}
if (bucket.tryConsume(1)) {
filterChain.doFilter(servletRequest, servletResponse);
} else {
HttpServletResponse httpResponse = (HttpServletResponse) servletResponse;
httpResponse.setContentType("text/plain");
httpResponse.setStatus(429);
httpResponse.getWriter().append("Too many requests");
}
}
}
その他の例については、Bucket4j のドキュメントをご覧ください。
分散型セッションストアの使用
セッションをメモリ内に保存することは、dyno の廃棄可能性と水平方向のスケーラビリティの妨げとなります。したがって、代わりに Redis などの分散型セッションストアを使用することが重要になります。
サーバーやフレームワークに応じて、Java と Redis でセッションストレージを実装する多くの方法があります。Heroku では、「Java Session Handling」(Java セッション処理) の記事で説明しているように、ほとんどの一般的な選択肢と相性が良い Redisson の使用を推奨しています。
JVM ランタイム指標の有効化
JVM ランタイム指標機能により、Java 仮想マシン (JVM) 内で実行するすべてのアプリのヒープメモリ、非ヒープメモリ、およびガベージコレクションアクティビティを表示できます。この機能は本番環境で安全に使用でき、パフォーマンス関連の問題を特定するのに役立てることができます。
アラートの設定
アプリケーションに問題が発生した場合、人にアラート通知する必要があります。最低でも、アプリは次のことを行う必要があります。
- 停止した場合に人にアラート通知する
- エラー率がしきい値を超えた場合に人にアラート通知する
- レイテンシーが高い場合に人にアラートで通知する
Professional dyno で実行中のアプリでは、Heroku のしきい値アラート機能を使用できます。Heroku のアドオンマーケットプレイスで多くのアラートおよび監視アドオンから選択することもできます。
エラーおよびメンテナンスページの設定
Heroku では、アプリケーションにエラー (クラッシュなど) が発生したり、メンテナンスのために停止したりするときに、ユーザーに表示される静的な HTML ページを設定することができます。詳細は、「Customizing Error Pages」(エラーページのカスタマイズ) を参照してください。
ロギングアドオンのアタッチ
Heroku では、デフォルトで 1500 行のアプリケーションログが記録されますが、完全なログストリームもサービスとして提供しています。複数のアドオンプロバイダーがこのサービスを利用し、ログの永続化、検索、メールや SMS による通知などの機能を実現しています。
アプリで次のコマンドを実行することによって、これらのロギングアドオンの 1 つである Papertrail をプロビジョニングできます。
$ heroku addons:create papertrail
Papertrail が動作していることを確認するため、アプリケーションの Heroku URL に数回アクセスします。アクセスする度にログメッセージが生成され、アドオンに送られるようになります。
エラー追跡アドオンのアタッチ
アプリケーションログにはサーバープロセスの通常のアクティビティが取得されますが、エラー追跡アドオンは例外的な場面で詳細情報を取得できます。この機能はユーザーに発生したよくある問題を特定したり、パフォーマンスの低下について診断したりするのに役立ちます。アドオンマーケットプレイスにはいくつかのオプションがあり、その 1 つの Rollbar サービスは、次のコマンドを実行することによってアプリに追加できます。
$ heroku addons:create rollbar
Rollbar 統合の設定のガイドに従うことで、すべてのエラーの記録と、それに関連付けられたスタックトレースやアプリから得られたその他の詳細情報が表示できるようになります。
脆弱性検出アドオンのアタッチ
本番環境用のすべてのアプリで使用すべき最後のアドオンは、ソースコード内のセキュリティ脆弱性を検出するアドオンです。そのようなサービスの 1 つが Snyk で、これは次のコマンドを実行することによってアプリにアタッチできます。
$ heroku addons:create snyk
Snyk はソースコードをスキャンし、pom.xml
または build.gradle
ファイル内の依存関係を、自身が持つ既知のセキュリティ脆弱性のデータベースと比較します。脆弱性が検出されると、解決方法を説明する通知が送信されます。