Heroku の本番環境用に Spring Boot アプリを準備する
最終更新日 2023年06月14日(水)
Table of Contents
アプリを本番環境に移行する前に、アプリのセキュリティ、スケーラビリティ、および弾力性を確保することが重要です。このガイドでは、Spring Boot アプリを Heroku の本番環境で使用できるようにするために必要な、多くの一般的かつ重要なステップの概要を示します。
HTTPS の使用を強制する
特別なニーズがない限り、アプリではすべてのリクエストについて HTTPS を使用する必要があります。Heroku ではすべてのアプリに対して HTTPS URL (https://<app-name>-<random-identifier>.herokuapp.com
の形式) が提供されているほか、独自のドメインおよび証明書を追加するためのツールも提供されています。
Spring Boot アプリに次の設定を追加することによって、Heroku 上でアプリを実行するときに HTTPS の使用を強制することができます。
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.requiresChannel()
.requestMatchers(r -> r.getHeader("X-Forwarded-Proto") != null)
.requiresSecure();
}
}
WebSecurityConfigurerAdapter
の実装が既にある場合は、これに上記の設定を追加します。
この設定は Spring に対し、X-Forwarded-Proto
ヘッダーがある場合は、非暗号化のすべての HTTP リクエストを HTTPS を使用して同じ URL にリダイレクトするよう指示します。Heroku では X-Forwarded-Proto
ヘッダーを設定します。つまり、リクエストは SSL が終了する Heroku ルーター経由でリダイレクトされることを意味します。localhost
環境では、非暗号化の HTTP を引き続き使用できます。
詳細については、正式な Spring Boot ドキュメントの「Enable HTTPS When Running behind a Proxy Server」(プロキシサーバーの背後で実行するときの HTTPS の有効化) の方法に関する記事を参照してください。
速度制限 API 呼び出し
速度制限は、クライアント IP、ブロックされた IP、地理位置情報あるいはその他の要素に基づきサーバーへのトラフィックを制御するプロセスです。Java 用の速度制限ライブラリとして最も有名なものの 1 つが Bucket4j で、これは Spring Boot Starter for Bucket4j を介して Spring Boot と一緒に使用できます。
依存関係を追加してバックエンドを設定すると、application.yml
内の最小設定は次のようになります。
bucket4j:
enabled: true
filters:
- cache-name: buckets
url: /*
rate-limits:
- bandwidths:
- capacity: 5
time: 10
unit: seconds
この設定は、個々のユーザーを 10 秒間に最大 5 回のリクエストに制限します。
構造化ロギングの使用
構造化ロギングによって、ロギングデータの分析操作の検索と実行が容易になります。Logback を使用すると、次の依存関係をアプリに追加することによって Spring Boot ログを JSON 形式にエンコードできます。
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
</dependency>
その後、次の内容を持つ logback.xml
設定ファイルを src/main/resources
ディレクトリに作成します。
<configuration>
<appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="net.logstash.logback.encoder.LogstashEncoder"/>
</appender>
<logger name="jsonLogger" additivity="false" level="DEBUG">
<appender-ref ref="consoleAppender"/>
</logger>
<root level="INFO">
<appender-ref ref="consoleAppender"/>
</root>
</configuration>
結果として、ログ行は次のようになります。
{"@timestamp":"2018-04-16T09:36:34.641-05:00","@version":"1","message":"Started Main in 4.154 seconds (JVM running for 4.832)","logger_name":"com.example.Main","thread_name":"restartedMain","level":"INFO","level_value":20000}
heroku logs
コマンドを使用した場合、JSON 形式のログは読みづらくなるかもしれませんが、実際の本番環境での検索がずっと容易になります。したがって、本番環境でない環境で、人間が判読可能なログ形式を保持することもできます。
すべての Postgres のプライマリキーでの UUID の使用
デフォルトでは、Spring Data は Postgres データベースのプライマリキーに整数を使用します。しかし、これでは攻撃者が次のレコード ID を推測できるため、アプリが攻撃に対して脆弱になります。推測や数え上げが不可能な UUID のプライマリキーを使用することをお勧めします。Spring Data モデルで UUID のプライマリキーを使用するように設定するには、次のように @GeneratedValue
アノテーションと併せて java.util.UUID
型を使用します。
@Entity
class MyModel {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private UUID id;
}
この方法により、モデルが作成されるときにランダムの UUID が割り当てられます。その後、次のように UUID AttributeConverter
をアプリに追加します。
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
import java.util.UUID;
@Converter(autoApply = true)
public class UUIDConverter implements AttributeConverter<UUID, UUID> {
@Override
public UUID convertToDatabaseColumn(UUID attribute) {
return attribute;
}
@Override
public UUID convertToEntityAttribute(UUID dbData) {
return dbData;
}
}
UUID は、機密情報を開示することなく自由に公開できます。また、予想不能でパフォーマンスが良く、整数のロールオーバーで苦労することもありません。
ソースコードからの秘密情報の削除
機密情報をソースコードに保持する (つまり Git にチェックインする) ことは安全ではありません。ソースコードは平文で保存され、サーバー上で暗号化されません。さらに、秘密情報をソースコードに保存することは、ステージングサービスまたはテストサービスから本番環境のサービスに誤って接続するリスクを高めます。
すべての機密データ (パスワード、API トークン、プライベートキーなど) は heroku config
コマンドを使用して設定変数として保存する必要があります。その後、次のような application.yml
内の環境変数置換を使用してアクセスできます。
admin:
password: ${ADMIN_PASSWORD}
Spring で使用する場合、ほとんどのリレーショナルデータベースの URL は Heroku でのゼロ設定が必要です。
分散型セッションストアの使用
セッションをメモリ内に保存することは、dyno の廃棄可能性と水平方向のスケーラビリティの妨げとなります。このような理由で Redis などの分散型セッションストアを使用することが重要になります。Spring Boot には Redis 用の Spring Session モジュールなど、Redis にセッションを保存するための複数のメカニズムがあります。しかし、Heroku では Java Session Handling (Java セッション処理) の記事に記載されている Redisson を使用することをお勧めします。
JVM ランタイム指標の有効化
JVM ランタイム指標機能により、Java 仮想マシン (JVM) 内で実行するすべてのアプリのヒープメモリ、非ヒープメモリ、およびガベージコレクションアクティビティを表示できます。この機能は本番環境で安全に使用でき、パフォーマンス関連の問題を特定するのに役立てることができます。
アラートの設定
アプリケーションに問題が発生した場合、人にアラート通知する必要があります。最低でも、次のことを行う必要があります。
- 停止した場合に人にアラート通知する
- エラー率がしきい値を超えた場合に人にアラート通知する
- レイテンシーが高い場合に人にアラートで通知する
Professional dyno で実行中のアプリでは、Heroku のしきい値アラート機能を使用できます。ただし、Heroku のアドオンマーケットプレイスで多くのアラートおよび監視アドオンから選択することもできます。
エラーおよびメンテナンスページの設定
Heroku には、アプリケーションにエラー (クラッシュなど) が発生したり、メンテナンスのために停止したりするときに、ユーザーに表示される静的な HTML ページを設定するための仕組みが用意されています。詳細については、Dev Center ガイドのエラーページのカスタマイズに関する記事を参照してください。
ロギングアドオンのアタッチ
Heroku では、デフォルトで 1500 行のアプリケーションログが記録されますが、完全なログストリームもサービスとして提供しています。複数のアドオンプロバイダーがこのサービスを利用し、ログの永続化、検索、メールや SMS による通知などの機能を実現しています。
アプリで次のコマンドを実行することによって、これらのロギングアドオンの 1 つである Papertrail をプロビジョニングできます。
$ heroku addons:create papertrail
このアドオンが動作していることを確認するため、アプリケーションの Heroku URL に数回アクセスします。アクセスする度にログメッセージが生成され、Papertrail のアドオンに送られるようになります。
エラー追跡アドオンのアタッチ
アプリケーションログにはサーバープロセスの通常のアクティビティが取得されますが、エラー追跡アドオンは例外的な場面で詳細情報を取得できます。この機能はユーザーに発生したよくある問題を特定したり、パフォーマンスの低下について診断したりするのに役立ちます。アドオンマーケットプレイスにはいくつかのオプションがあり、その 1 つの Rollbar サービスは、次のコマンドを実行することによってアプリに追加できます。
$ heroku addons:create rollbar
Rollbar 統合の設定のガイドに従うことで、すべてのエラーの記録と、それに関連付けられたスタックトレースやアプリから得られたその他の詳細情報が表示できるようになります。
脆弱性検出アドオンのアタッチ
本番環境用のすべてのアプリで使用すべき最後のアドオンは、ソースコード内のセキュリティ脆弱性を検出するアドオンです。そのようなサービスの 1 つが Snyk で、これは次のコマンドを実行することによってアプリにアタッチできます。
$ heroku addons:create snyk
Snyk はソースコードをスキャンし、pom.xml
または build.gradle
内の依存関係を、自身が持つ既知のセキュリティ脆弱性のデータベースと比較します。脆弱性が検出されると、更新方法を伝える通知がお客様に送信されます。
参考情報
Spring Boot アプリを本番環境で実行することに関する詳細およびガイダンスについては、正式な Spring ドキュメントの「Moving to Production」(本番環境への移行)を参照してください。